summaryrefslogtreecommitdiff
path: root/usr/src/uts/common
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common')
-rw-r--r--usr/src/uts/common/Makefile.files2
-rw-r--r--usr/src/uts/common/Makefile.rules7
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_init.c3
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_opipe.c8
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_print.c9
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_server.c49
-rw-r--r--usr/src/uts/common/fs/smbsrv/smb_tree.c7
-rw-r--r--usr/src/uts/common/fs/zfs/dmu_tx.c24
-rw-r--r--usr/src/uts/common/io/e1000g/e1000_api.c34
-rw-r--r--usr/src/uts/common/io/e1000g/e1000_defines.h34
-rw-r--r--usr/src/uts/common/io/e1000g/e1000_hw.h38
-rw-r--r--usr/src/uts/common/io/e1000g/e1000_ich8lan.c1080
-rw-r--r--usr/src/uts/common/io/e1000g/e1000_ich8lan.h69
-rw-r--r--usr/src/uts/common/io/e1000g/e1000_phy.c149
-rw-r--r--usr/src/uts/common/io/e1000g/e1000_phy.h34
-rw-r--r--usr/src/uts/common/io/e1000g/e1000_regs.h33
-rw-r--r--usr/src/uts/common/io/e1000g/e1000g_main.c15
-rw-r--r--usr/src/uts/common/io/e1000g/e1000g_rx.c14
-rw-r--r--usr/src/uts/common/io/ksocket/ksocket.c142
-rw-r--r--usr/src/uts/common/io/pcn/THIRDPARTYLICENSE31
-rw-r--r--usr/src/uts/common/io/pcn/THIRDPARTYLICENSE.descrip1
-rw-r--r--usr/src/uts/common/io/pcn/pcn.c1827
-rw-r--r--usr/src/uts/common/io/pcn/pcn.h450
-rw-r--r--usr/src/uts/common/io/pcn/pcnimpl.h258
-rw-r--r--usr/src/uts/common/krtld/kobj.c18
-rw-r--r--usr/src/uts/common/os/cred.c7
-rw-r--r--usr/src/uts/common/sys/cred_impl.h10
-rw-r--r--usr/src/uts/common/sys/ksocket.h2
28 files changed, 4024 insertions, 331 deletions
diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files
index d465ec4a8d..047a6b5e83 100644
--- a/usr/src/uts/common/Makefile.files
+++ b/usr/src/uts/common/Makefile.files
@@ -1780,6 +1780,8 @@ IXGB_OBJS += ixgb.o ixgb_atomic.o ixgb_chip.o ixgb_gld.o ixgb_kstats.o \
NGE_OBJS += nge_main.o nge_atomic.o nge_chip.o nge_ndd.o nge_kstats.o \
nge_log.o nge_rx.o nge_tx.o nge_xmii.o
+PCN_OBJS += pcn.o
+
RGE_OBJS += rge_main.o rge_chip.o rge_ndd.o rge_kstats.o rge_log.o rge_rxtx.o
URTW_OBJS += urtw.o
diff --git a/usr/src/uts/common/Makefile.rules b/usr/src/uts/common/Makefile.rules
index 9924914c59..2b379caa7b 100644
--- a/usr/src/uts/common/Makefile.rules
+++ b/usr/src/uts/common/Makefile.rules
@@ -975,6 +975,10 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/io/pcan/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
+$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/pcn/%.c
+ $(COMPILE.c) -o $@ $<
+ $(CTFCONVERT_O)
+
$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/pcwl/%.c
$(COMPILE.c) -o $@ $<
$(CTFCONVERT_O)
@@ -2245,6 +2249,9 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/pcmcia/%.c
$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/pcan/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
+$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/pcn/%.c
+ @($(LHEAD) $(LINT.c) $< $(LTAIL))
+
$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/pcwl/%.c
@($(LHEAD) $(LINT.c) $< $(LTAIL))
diff --git a/usr/src/uts/common/fs/smbsrv/smb_init.c b/usr/src/uts/common/fs/smbsrv/smb_init.c
index 27c00bde70..15ceb53dce 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_init.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_init.c
@@ -19,6 +19,7 @@
* CDDL HEADER END
*/
/*
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
*/
@@ -64,7 +65,7 @@ int smb_oplock_min_timeout = OPLOCK_MIN_TIMEOUT;
int smb_flush_required = 1;
int smb_dirsymlink_enable = 1;
int smb_sign_debug = 0;
-int smb_raw_mode = 1;
+int smb_raw_mode = 0;
int smb_shortnames = 1;
uint_t smb_audit_flags =
#ifdef DEBUG
diff --git a/usr/src/uts/common/fs/smbsrv/smb_opipe.c b/usr/src/uts/common/fs/smbsrv/smb_opipe.c
index e7673de96a..bb178f3952 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_opipe.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_opipe.c
@@ -19,6 +19,7 @@
* CDDL HEADER END
*/
/*
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
*/
@@ -120,6 +121,13 @@ smb_opipe_open(smb_request_t *sr)
if ((pipe_name = smb_opipe_lookup(op->fqi.fq_path.pn_path)) == NULL)
return (NT_STATUS_OBJECT_NAME_NOT_FOUND);
+ /*
+ * If printing is disabled, pretend spoolss does not exist.
+ */
+ if (sr->sr_server->sv_cfg.skc_print_enable == 0 &&
+ strcmp(pipe_name, "SPOOLSS") == 0)
+ return (NT_STATUS_OBJECT_NAME_NOT_FOUND);
+
op->create_options = 0;
of = smb_ofile_open(sr->tid_tree, NULL, sr->smb_pid, op,
diff --git a/usr/src/uts/common/fs/smbsrv/smb_print.c b/usr/src/uts/common/fs/smbsrv/smb_print.c
index 759c5d6e10..b3bbedfd1b 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_print.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_print.c
@@ -109,7 +109,8 @@ smb_com_open_print_file(smb_request_t *sr)
smb_kshare_t *si;
struct open_param *op = &sr->arg.open;
- if (!STYPE_ISPRN(sr->tid_tree->t_res_type)) {
+ if (sr->sr_server->sv_cfg.skc_print_enable == 0 ||
+ !STYPE_ISPRN(sr->tid_tree->t_res_type)) {
cmn_err(CE_WARN, "smb_com_open_print_file: bad device");
smbsr_error(sr, NT_STATUS_BAD_DEVICE_TYPE,
ERRDOS, ERROR_BAD_DEV_TYPE);
@@ -182,7 +183,8 @@ smb_com_close_print_file(smb_request_t *sr)
{
smb_sdrc_t rc;
- if (!STYPE_ISPRN(sr->tid_tree->t_res_type)) {
+ if (sr->sr_server->sv_cfg.skc_print_enable == 0 ||
+ !STYPE_ISPRN(sr->tid_tree->t_res_type)) {
smbsr_error(sr, NT_STATUS_BAD_DEVICE_TYPE,
ERRDOS, ERROR_BAD_DEV_TYPE);
cmn_err(CE_WARN, "smb_com_close_print_file: SDRC_ERROR");
@@ -269,7 +271,8 @@ smb_com_write_print_file(smb_request_t *sr)
smb_attr_t attr;
int rc;
- if (!STYPE_ISPRN(sr->tid_tree->t_res_type)) {
+ if (sr->sr_server->sv_cfg.skc_print_enable == 0 ||
+ !STYPE_ISPRN(sr->tid_tree->t_res_type)) {
smbsr_error(sr, NT_STATUS_BAD_DEVICE_TYPE,
ERRDOS, ERROR_BAD_DEV_TYPE);
return (SDRC_ERROR);
diff --git a/usr/src/uts/common/fs/smbsrv/smb_server.c b/usr/src/uts/common/fs/smbsrv/smb_server.c
index 52915cbd44..bef039cfea 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_server.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_server.c
@@ -754,6 +754,11 @@ smb_server_spooldoc(smb_ioc_spooldoc_t *ioc)
if ((rc = smb_server_lookup(&sv)) != 0)
return (rc);
+ if (sv->sv_cfg.skc_print_enable == 0) {
+ rc = ENOTTY;
+ goto out;
+ }
+
mutex_enter(&sv->sv_mutex);
for (;;) {
if (sv->sv_state != SMB_SERVER_STATE_RUNNING) {
@@ -770,22 +775,24 @@ smb_server_spooldoc(smb_ioc_spooldoc_t *ioc)
}
}
mutex_exit(&sv->sv_mutex);
- if (rc == 0) {
- spdoc = kmem_zalloc(sizeof (*spdoc), KM_SLEEP);
- if (smb_spool_lookup_doc_byfid(sv, fid, spdoc)) {
- ioc->spool_num = spdoc->sd_spool_num;
- ioc->ipaddr = spdoc->sd_ipaddr;
- (void) strlcpy(ioc->path, spdoc->sd_path,
- MAXPATHLEN);
- (void) strlcpy(ioc->username,
- spdoc->sd_username, MAXNAMELEN);
- } else {
- /* Did not find that print job. */
- rc = EAGAIN;
- }
- kmem_free(spdoc, sizeof (*spdoc));
+ if (rc != 0)
+ goto out;
+
+ spdoc = kmem_zalloc(sizeof (*spdoc), KM_SLEEP);
+ if (smb_spool_lookup_doc_byfid(sv, fid, spdoc)) {
+ ioc->spool_num = spdoc->sd_spool_num;
+ ioc->ipaddr = spdoc->sd_ipaddr;
+ (void) strlcpy(ioc->path, spdoc->sd_path,
+ MAXPATHLEN);
+ (void) strlcpy(ioc->username,
+ spdoc->sd_username, MAXNAMELEN);
+ } else {
+ /* Did not find that print job. */
+ rc = EAGAIN;
}
+ kmem_free(spdoc, sizeof (*spdoc));
+out:
smb_server_release(sv);
return (rc);
}
@@ -1858,6 +1865,20 @@ smb_server_store_cfg(smb_server_t *sv, smb_ioc_cfg_t *ioc)
sizeof (sv->sv_cfg.skc_hostname));
(void) strlcpy(sv->sv_cfg.skc_system_comment, ioc->system_comment,
sizeof (sv->sv_cfg.skc_system_comment));
+
+ if (sv->sv_cfg.skc_oplock_enable && smb_raw_mode) {
+ /*
+ * Note that these two optional protocol features
+ * (oplocks, raw_mode) have unfortunate interactions.
+ * Since raw_mode is only wanted by ancient clients,
+ * we just turn it off (that's what MS recommends).
+ * Leave some evidence in the log if someone has
+ * patched smb_raw_mode to enable it.
+ */
+ cmn_err(CE_NOTE,
+ "Raw mode enabled: Disabling opportunistic locks");
+ sv->sv_cfg.skc_oplock_enable = 0;
+ }
}
static int
diff --git a/usr/src/uts/common/fs/smbsrv/smb_tree.c b/usr/src/uts/common/fs/smbsrv/smb_tree.c
index 03a4e856c1..24dd27cdc5 100644
--- a/usr/src/uts/common/fs/smbsrv/smb_tree.c
+++ b/usr/src/uts/common/fs/smbsrv/smb_tree.c
@@ -20,6 +20,7 @@
*/
/*
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
*/
@@ -760,6 +761,12 @@ smb_tree_connect_printq(smb_request_t *sr, const char *sharename)
ASSERT(user);
ASSERT(user->u_cred);
+ if (sr->sr_server->sv_cfg.skc_print_enable == 0) {
+ smb_tree_log(sr, sharename, "printing disabled");
+ smbsr_error(sr, 0, ERRSRV, ERRinvnetname);
+ return (NULL);
+ }
+
if ((strcmp(service, any) != 0) &&
(strcasecmp(service, "LPT1:") != 0)) {
smb_tree_log(sr, sharename, "invalid service (%s)", service);
diff --git a/usr/src/uts/common/fs/zfs/dmu_tx.c b/usr/src/uts/common/fs/zfs/dmu_tx.c
index bd5c71a226..b4579e278c 100644
--- a/usr/src/uts/common/fs/zfs/dmu_tx.c
+++ b/usr/src/uts/common/fs/zfs/dmu_tx.c
@@ -21,6 +21,9 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
*/
+/*
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ */
#include <sys/dmu.h>
#include <sys/dmu_impl.h>
@@ -676,6 +679,8 @@ dmu_tx_hold_zap(dmu_tx_t *tx, uint64_t object, int add, const char *name)
ASSERT3P(dmu_ot[dn->dn_type].ot_byteswap, ==, zap_byteswap);
if (dn->dn_maxblkid == 0 && !add) {
+ blkptr_t *bp;
+
/*
* If there is only one block (i.e. this is a micro-zap)
* and we are not adding anything, the accounting is simple.
@@ -690,14 +695,13 @@ dmu_tx_hold_zap(dmu_tx_t *tx, uint64_t object, int add, const char *name)
* Use max block size here, since we don't know how much
* the size will change between now and the dbuf dirty call.
*/
+ bp = &dn->dn_phys->dn_blkptr[0];
if (dsl_dataset_block_freeable(dn->dn_objset->os_dsl_dataset,
- &dn->dn_phys->dn_blkptr[0],
- dn->dn_phys->dn_blkptr[0].blk_birth)) {
+ bp, bp->blk_birth))
txh->txh_space_tooverwrite += SPA_MAXBLOCKSIZE;
- } else {
+ else
txh->txh_space_towrite += SPA_MAXBLOCKSIZE;
- }
- if (dn->dn_phys->dn_blkptr[0].blk_birth)
+ if (!BP_IS_HOLE(bp))
txh->txh_space_tounref += SPA_MAXBLOCKSIZE;
return;
}
@@ -1273,7 +1277,6 @@ dmu_tx_hold_spill(dmu_tx_t *tx, uint64_t object)
{
dnode_t *dn;
dmu_tx_hold_t *txh;
- blkptr_t *bp;
txh = dmu_tx_hold_object_impl(tx, tx->tx_objset, object,
THT_SPILL, 0, 0);
@@ -1284,17 +1287,18 @@ dmu_tx_hold_spill(dmu_tx_t *tx, uint64_t object)
return;
/* If blkptr doesn't exist then add space to towrite */
- bp = &dn->dn_phys->dn_spill;
- if (BP_IS_HOLE(bp)) {
+ if (!(dn->dn_phys->dn_flags & DNODE_FLAG_SPILL_BLKPTR)) {
txh->txh_space_towrite += SPA_MAXBLOCKSIZE;
- txh->txh_space_tounref = 0;
} else {
+ blkptr_t *bp;
+
+ bp = &dn->dn_phys->dn_spill;
if (dsl_dataset_block_freeable(dn->dn_objset->os_dsl_dataset,
bp, bp->blk_birth))
txh->txh_space_tooverwrite += SPA_MAXBLOCKSIZE;
else
txh->txh_space_towrite += SPA_MAXBLOCKSIZE;
- if (bp->blk_birth)
+ if (!BP_IS_HOLE(bp))
txh->txh_space_tounref += SPA_MAXBLOCKSIZE;
}
}
diff --git a/usr/src/uts/common/io/e1000g/e1000_api.c b/usr/src/uts/common/io/e1000g/e1000_api.c
index 44cd74d164..b2f960e685 100644
--- a/usr/src/uts/common/io/e1000g/e1000_api.c
+++ b/usr/src/uts/common/io/e1000g/e1000_api.c
@@ -24,7 +24,34 @@
*/
/*
- * IntelVersion: 1.125 v3-1-10-1_2009-9-18_Release14-6
+ * Copyright (c) 2001-2010, Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
*/
#include "e1000_api.h"
@@ -257,6 +284,10 @@ e1000_set_mac_type(struct e1000_hw *hw)
case E1000_DEV_ID_PCH_M_HV_LC:
mac->type = e1000_pchlan;
break;
+ case E1000_DEV_ID_PCH2_LV_LM:
+ case E1000_DEV_ID_PCH2_LV_V:
+ mac->type = e1000_pch2lan;
+ break;
default:
/* Should never have loaded on this device */
ret_val = -E1000_ERR_MAC_INIT;
@@ -344,6 +375,7 @@ e1000_setup_init_funcs(struct e1000_hw *hw, bool init_device)
case e1000_ich9lan:
case e1000_ich10lan:
case e1000_pchlan:
+ case e1000_pch2lan:
e1000_init_function_pointers_ich8lan(hw);
break;
default:
diff --git a/usr/src/uts/common/io/e1000g/e1000_defines.h b/usr/src/uts/common/io/e1000g/e1000_defines.h
index 5927c6a263..e28ae88ce4 100644
--- a/usr/src/uts/common/io/e1000g/e1000_defines.h
+++ b/usr/src/uts/common/io/e1000g/e1000_defines.h
@@ -24,8 +24,36 @@
*/
/*
- * IntelVersion: 1.118 v3-1-10-1_2009-9-18_Release14-6
+ * Copyright (c) 2001-2010, Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
*/
+
#ifndef _E1000_DEFINES_H_
#define _E1000_DEFINES_H_
@@ -403,6 +431,8 @@
#define E1000_CTRL_FORCE_PHY_RESET 0x00008000
/* enable link status from external LINK_0 and LINK_1 pins */
#define E1000_CTRL_EXT_LINK_EN 0x00010000
+#define E1000_CTRL_LANPHYPC_OVERRIDE 0x00010000 /* SW control of LANPHYPC */
+#define E1000_CTRL_LANPHYPC_VALUE 0x00020000 /* SW value of LANPHYPC */
#define E1000_CTRL_SWDPIN0 0x00040000 /* SWDPIN 0 value */
#define E1000_CTRL_SWDPIN1 0x00080000 /* SWDPIN 1 value */
#define E1000_CTRL_SWDPIN2 0x00100000 /* SWDPIN 2 value */
@@ -693,6 +723,7 @@
#define E1000_EXTCNF_CTRL_LCD_WRITE_ENABLE 0x00000001
#define E1000_EXTCNF_CTRL_OEM_WRITE_ENABLE 0x00000008
#define E1000_EXTCNF_CTRL_SWFLAG 0x00000020
+#define E1000_EXTCNF_CTRL_GATE_PHY_CFG 0x00000080
#define E1000_EXTCNF_SIZE_EXT_PCIE_LENGTH_MASK 0x00FF0000
#define E1000_EXTCNF_SIZE_EXT_PCIE_LENGTH_SHIFT 16
#define E1000_EXTCNF_CTRL_EXT_CNF_POINTER_MASK 0x0FFF0000
@@ -1313,6 +1344,7 @@
#define BME1000_E_PHY_ID_R2 0x01410CB1
#define I82577_E_PHY_ID 0x01540050
#define I82578_E_PHY_ID 0x004DD040
+#define I82579_E_PHY_ID 0x01540090
#define M88_VENDOR 0x0141
/* M88E1000 Specific Registers */
diff --git a/usr/src/uts/common/io/e1000g/e1000_hw.h b/usr/src/uts/common/io/e1000g/e1000_hw.h
index 723ca8ee27..d2f779f86a 100644
--- a/usr/src/uts/common/io/e1000g/e1000_hw.h
+++ b/usr/src/uts/common/io/e1000g/e1000_hw.h
@@ -24,8 +24,36 @@
*/
/*
- * IntelVersion: 1.439 v3-1-10-1_2009-9-18_Release14-6
+ * Copyright (c) 2001-2010, Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
*/
+
#ifndef _E1000_HW_H_
#define _E1000_HW_H_
@@ -124,6 +152,8 @@ struct e1000_hw;
#define E1000_DEV_ID_PCH_M_HV_LC 0x10EB
#define E1000_DEV_ID_PCH_D_HV_DM 0x10EF
#define E1000_DEV_ID_PCH_D_HV_DC 0x10F0
+#define E1000_DEV_ID_PCH2_LV_LM 0x1502
+#define E1000_DEV_ID_PCH2_LV_V 0x1503
#define E1000_REVISION_0 0
#define E1000_REVISION_1 1
@@ -164,6 +194,7 @@ enum e1000_mac_type {
e1000_ich9lan,
e1000_ich10lan,
e1000_pchlan,
+ e1000_pch2lan,
e1000_num_macs /* List is 1-based, so subtract 1 for true count. */
};
@@ -204,6 +235,7 @@ enum e1000_phy_type {
e1000_phy_bm,
e1000_phy_82578,
e1000_phy_82577,
+ e1000_phy_82579
};
enum e1000_bus_type {
@@ -653,6 +685,7 @@ struct e1000_mac_info {
u8 forced_speed_duplex;
bool adaptive_ifs;
+ bool has_fwsm;
bool arc_subsystem_valid;
bool asf_firmware_present;
bool autoneg;
@@ -727,6 +760,7 @@ struct e1000_fc_info {
u32 high_water; /* Flow control high-water mark */
u32 low_water; /* Flow control low-water mark */
u16 pause_time; /* Flow control pause timer */
+ u16 refresh_time; /* Flow control refresh timer */
bool send_xon; /* Flow control send XON */
bool strict_ieee; /* Strict IEEE mode */
enum e1000_fc_mode current_mode; /* FC mode in effect */
@@ -777,7 +811,7 @@ struct e1000_dev_spec_ich8lan {
E1000_MUTEX nvm_mutex;
E1000_MUTEX swflag_mutex;
bool nvm_k1_enabled;
- bool nvm_lcd_config_enabled;
+ bool eee_disable;
};
struct e1000_hw {
diff --git a/usr/src/uts/common/io/e1000g/e1000_ich8lan.c b/usr/src/uts/common/io/e1000g/e1000_ich8lan.c
index 1c0120ed70..0cc9f26f27 100644
--- a/usr/src/uts/common/io/e1000g/e1000_ich8lan.c
+++ b/usr/src/uts/common/io/e1000g/e1000_ich8lan.c
@@ -24,7 +24,34 @@
*/
/*
- * IntelVersion: 1.186.2.1 v3-1-10-1_2009-9-18_Release14-6
+ * Copyright (c) 2001-2010, Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
*/
/*
@@ -53,6 +80,8 @@
* 82577LC Gigabit Network Connection
* 82578DM Gigabit Network Connection
* 82578DC Gigabit Network Connection
+ * 82579LM Gigabit Network Connection
+ * 82579V Gigabit Network Connection
*/
#include "e1000_api.h"
@@ -66,6 +95,11 @@ static void e1000_release_swflag_ich8lan(struct e1000_hw *hw);
static s32 e1000_acquire_nvm_ich8lan(struct e1000_hw *hw);
static void e1000_release_nvm_ich8lan(struct e1000_hw *hw);
static bool e1000_check_mng_mode_ich8lan(struct e1000_hw *hw);
+static bool e1000_check_mng_mode_pchlan(struct e1000_hw *hw);
+static void e1000_rar_set_pch2lan(struct e1000_hw *hw, u8 *addr, u32 index);
+static void e1000_update_mc_addr_list_pch2lan(struct e1000_hw *hw,
+ u8 *mc_addr_list,
+ u32 mc_addr_count);
static s32 e1000_check_reset_block_ich8lan(struct e1000_hw *hw);
static s32 e1000_phy_hw_reset_ich8lan(struct e1000_hw *hw);
static s32 e1000_get_phy_info_ich8lan(struct e1000_hw *hw);
@@ -122,6 +156,9 @@ static void e1000_power_down_phy_copper_ich8lan(struct e1000_hw *hw);
static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw);
static void e1000_lan_init_done_ich8lan(struct e1000_hw *hw);
static s32 e1000_sw_lcd_config_ich8lan(struct e1000_hw *hw);
+static s32 e1000_set_mdio_slow_mode_hv(struct e1000_hw *hw);
+static s32 e1000_k1_workaround_lv(struct e1000_hw *hw);
+static void e1000_gate_hw_phy_config_ich8lan(struct e1000_hw *hw, bool gate);
/* ICH GbE Flash Hardware Sequencing Flash Status Register bit breakdown */
/* Offset 04h HSFSTS */
@@ -174,6 +211,7 @@ static s32
e1000_init_phy_params_pchlan(struct e1000_hw *hw)
{
struct e1000_phy_info *phy = &hw->phy;
+ u32 ctrl, fwsm;
s32 ret_val = E1000_SUCCESS;
DEBUGFUNC("e1000_init_phy_params_pchlan");
@@ -182,12 +220,8 @@ e1000_init_phy_params_pchlan(struct e1000_hw *hw)
phy->reset_delay_us = 100;
phy->ops.acquire = e1000_acquire_swflag_ich8lan;
- phy->ops.check_polarity = e1000_check_polarity_ife;
phy->ops.check_reset_block = e1000_check_reset_block_ich8lan;
- phy->ops.force_speed_duplex = e1000_phy_force_speed_duplex_ife;
- phy->ops.get_cable_length = e1000_get_cable_length_igp_2;
phy->ops.get_cfg_done = e1000_get_cfg_done_ich8lan;
- phy->ops.get_info = e1000_get_phy_info_ich8lan;
phy->ops.read_reg = e1000_read_phy_reg_hv;
phy->ops.read_reg_locked = e1000_read_phy_reg_hv_locked;
phy->ops.release = e1000_release_swflag_ich8lan;
@@ -200,19 +234,95 @@ e1000_init_phy_params_pchlan(struct e1000_hw *hw)
phy->ops.power_down = e1000_power_down_phy_copper_ich8lan;
phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT;
+ /*
+ * The MAC-PHY interconnect may still be in SMBus mode
+ * after Sx->S0. If the manageability engine (ME) is
+ * disabled, then toggle the LANPHYPC Value bit to force
+ * the interconnect to PCIe mode.
+ */
+ fwsm = E1000_READ_REG(hw, E1000_FWSM);
+ if (!(fwsm & E1000_ICH_FWSM_FW_VALID) &&
+ !(hw->phy.ops.check_reset_block(hw))) {
+ ctrl = E1000_READ_REG(hw, E1000_CTRL);
+ ctrl |= E1000_CTRL_LANPHYPC_OVERRIDE;
+ ctrl &= ~E1000_CTRL_LANPHYPC_VALUE;
+ E1000_WRITE_REG(hw, E1000_CTRL, ctrl);
+ usec_delay(10);
+ ctrl &= ~E1000_CTRL_LANPHYPC_OVERRIDE;
+ E1000_WRITE_REG(hw, E1000_CTRL, ctrl);
+ msec_delay(50);
+
+ /*
+ * Gate automatic PHY configuration by hardware on
+ * non-managed 82579
+ */
+ if (hw->mac.type == e1000_pch2lan)
+ e1000_gate_hw_phy_config_ich8lan(hw, true);
+ }
+
+ /*
+ * Reset the PHY before any acccess to it. Doing so, ensures that
+ * the PHY is in a known good state before we read/write PHY registers.
+ * The generic reset is sufficient here, because we haven't determined
+ * the PHY type yet.
+ */
+ ret_val = e1000_phy_hw_reset_generic(hw);
+ if (ret_val)
+ goto out;
+
+ /* Ungate automatic PHY configuration on non-managed 82579 */
+ if ((hw->mac.type == e1000_pch2lan) &&
+ !(fwsm & E1000_ICH_FWSM_FW_VALID)) {
+ msec_delay(10);
+ e1000_gate_hw_phy_config_ich8lan(hw, false);
+ }
+
phy->id = e1000_phy_unknown;
- (void) e1000_get_phy_id(hw);
+ switch (hw->mac.type) {
+ default:
+ ret_val = e1000_get_phy_id(hw);
+ if (ret_val)
+ goto out;
+ if ((phy->id != 0) && (phy->id != PHY_REVISION_MASK))
+ break;
+ /* FALLTHROUGH */
+ case e1000_pch2lan:
+ /*
+ * In case the PHY needs to be in mdio slow mode,
+ * set slow mode and try to get the PHY id again.
+ */
+ ret_val = e1000_set_mdio_slow_mode_hv(hw);
+ if (ret_val)
+ goto out;
+ ret_val = e1000_get_phy_id(hw);
+ if (ret_val)
+ goto out;
+ break;
+ }
phy->type = e1000_get_phy_type_from_id(phy->id);
- if (phy->type == e1000_phy_82577) {
+ switch (phy->type) {
+ case e1000_phy_82577:
+ case e1000_phy_82579:
phy->ops.check_polarity = e1000_check_polarity_82577;
phy->ops.force_speed_duplex =
e1000_phy_force_speed_duplex_82577;
- phy->ops.get_cable_length = e1000_get_cable_length_82577;
+ phy->ops.get_cable_length = e1000_get_cable_length_82577;
phy->ops.get_info = e1000_get_phy_info_82577;
phy->ops.commit = e1000_phy_sw_reset_generic;
+ break;
+ case e1000_phy_82578:
+ phy->ops.check_polarity = e1000_check_polarity_m88;
+ phy->ops.force_speed_duplex = e1000_phy_force_speed_duplex_m88;
+ phy->ops.get_cable_length = e1000_get_cable_length_m88;
+ phy->ops.get_info = e1000_get_phy_info_m88;
+ break;
+ default:
+ ret_val = -E1000_ERR_PHY;
+ break;
}
+out:
return (ret_val);
}
@@ -388,6 +498,7 @@ static s32
e1000_init_mac_params_ich8lan(struct e1000_hw *hw)
{
struct e1000_mac_info *mac = &hw->mac;
+ u16 pci_cfg;
DEBUGFUNC("e1000_init_mac_params_ich8lan");
@@ -402,8 +513,12 @@ e1000_init_mac_params_ich8lan(struct e1000_hw *hw)
mac->rar_entry_count--;
/* Set if part includes ASF firmware */
mac->asf_firmware_present = true;
- /* Set if manageability features are enabled. */
- mac->arc_subsystem_valid = true;
+ /* FWSM register */
+ mac->has_fwsm = true;
+ /* ARC subsystem not supported */
+ mac->arc_subsystem_valid = false;
+ /* Adaptive IFS supported */
+ mac->adaptive_ifs = true;
/* Function pointers */
@@ -421,14 +536,10 @@ e1000_init_mac_params_ich8lan(struct e1000_hw *hw)
mac->ops.setup_physical_interface = e1000_setup_copper_link_ich8lan;
/* check for link */
mac->ops.check_for_link = e1000_check_for_copper_link_ich8lan;
- /* check management mode */
- mac->ops.check_mng_mode = e1000_check_mng_mode_ich8lan;
/* link info */
mac->ops.get_link_up_info = e1000_get_link_up_info_ich8lan;
/* multicast address update */
mac->ops.update_mc_addr_list = e1000_update_mc_addr_list_generic;
- /* setting MTA */
- mac->ops.mta_set = e1000_mta_set_generic;
/* clear hardware counters */
mac->ops.clear_hw_cntrs = e1000_clear_hw_cntrs_ich8lan;
@@ -437,6 +548,8 @@ e1000_init_mac_params_ich8lan(struct e1000_hw *hw)
case e1000_ich8lan:
case e1000_ich9lan:
case e1000_ich10lan:
+ /* check management mode */
+ mac->ops.check_mng_mode = e1000_check_mng_mode_ich8lan;
/* ID LED init */
mac->ops.id_led_init = e1000_id_led_init_generic;
/* blink LED */
@@ -449,7 +562,19 @@ e1000_init_mac_params_ich8lan(struct e1000_hw *hw)
mac->ops.led_on = e1000_led_on_ich8lan;
mac->ops.led_off = e1000_led_off_ich8lan;
break;
+ case e1000_pch2lan:
+ mac->rar_entry_count = E1000_PCH2_RAR_ENTRIES;
+ mac->ops.rar_set = e1000_rar_set_pch2lan;
+ /* multicast address update for pch2 */
+ mac->ops.update_mc_addr_list =
+ e1000_update_mc_addr_list_pch2lan;
+ /* FALLTHROUGH */
case e1000_pchlan:
+ /* save PCH revision_id */
+ e1000_read_pci_cfg(hw, 0x2, &pci_cfg);
+ hw->revision_id = (u8)(pci_cfg &= 0x000F);
+ /* check management mode */
+ mac->ops.check_mng_mode = e1000_check_mng_mode_pchlan;
/* ID LED init */
mac->ops.id_led_init = e1000_id_led_init_pchlan;
/* setup LED */
@@ -468,10 +593,47 @@ e1000_init_mac_params_ich8lan(struct e1000_hw *hw)
if (mac->type == e1000_ich8lan)
e1000_set_kmrn_lock_loss_workaround_ich8lan(hw, true);
+ /* Gate automatic PHY configuration by hardware on managed 82579 */
+ if ((mac->type == e1000_pch2lan) &&
+ (E1000_READ_REG(hw, E1000_FWSM) & E1000_ICH_FWSM_FW_VALID))
+ e1000_gate_hw_phy_config_ich8lan(hw, true);
+
return (E1000_SUCCESS);
}
/*
+ * e1000_set_eee_pchlan - Enable/disable EEE support
+ * @hw: pointer to the HW structure
+ *
+ * Enable/disable EEE based on setting in dev_spec structure. The bits in
+ * the LPI Control register will remain set only if/when link is up.
+ */
+static s32
+e1000_set_eee_pchlan(struct e1000_hw *hw)
+{
+ s32 ret_val = E1000_SUCCESS;
+ u16 phy_reg;
+
+ DEBUGFUNC("e1000_set_eee_pchlan");
+
+ if (hw->phy.type != e1000_phy_82579)
+ goto out;
+
+ ret_val = hw->phy.ops.read_reg(hw, I82579_LPI_CTRL, &phy_reg);
+ if (ret_val)
+ goto out;
+
+ if (hw->dev_spec.ich8lan.eee_disable)
+ phy_reg &= ~I82579_LPI_CTRL_ENABLE_MASK;
+ else
+ phy_reg |= I82579_LPI_CTRL_ENABLE_MASK;
+
+ ret_val = hw->phy.ops.write_reg(hw, I82579_LPI_CTRL, phy_reg);
+out:
+ return (ret_val);
+}
+
+/*
* e1000_check_for_copper_link_ich8lan - Check for link (Copper)
* @hw: pointer to the HW structure
*
@@ -525,12 +687,23 @@ e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw)
goto out;
}
+ if (hw->mac.type == e1000_pch2lan) {
+ ret_val = e1000_k1_workaround_lv(hw);
+ if (ret_val)
+ goto out;
+ }
+
/*
* Check if there was DownShift, must be checked
* immediately after link-up
*/
(void) e1000_check_downshift_generic(hw);
+ /* Enable/Disable EEE after link up */
+ ret_val = e1000_set_eee_pchlan(hw);
+ if (ret_val)
+ goto out;
+
/*
* If we are forcing speed/duplex, then we simply return since
* we have already determined whether we have link or not.
@@ -554,10 +727,8 @@ e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw)
* different link partner.
*/
ret_val = e1000_config_fc_after_link_up_generic(hw);
- if (ret_val) {
- /* EMPTY */
+ if (ret_val)
DEBUGOUT("Error configuring flow control\n");
- }
out:
return (ret_val);
@@ -583,6 +754,7 @@ e1000_init_function_pointers_ich8lan(struct e1000_hw *hw)
hw->phy.ops.init_params = e1000_init_phy_params_ich8lan;
break;
case e1000_pchlan:
+ case e1000_pch2lan:
hw->phy.ops.init_params = e1000_init_phy_params_pchlan;
break;
default:
@@ -725,6 +897,112 @@ e1000_check_mng_mode_ich8lan(struct e1000_hw *hw)
}
/*
+ * e1000_check_mng_mode_pchlan - Checks management mode
+ * @hw: pointer to the HW structure
+ *
+ * This checks if the adapter has iAMT enabled.
+ * This is a function pointer entry point only called by read/write
+ * routines for the PHY and NVM parts.
+ */
+static bool
+e1000_check_mng_mode_pchlan(struct e1000_hw *hw)
+{
+ u32 fwsm;
+
+ DEBUGFUNC("e1000_check_mng_mode_pchlan");
+
+ fwsm = E1000_READ_REG(hw, E1000_FWSM);
+
+ return ((fwsm & E1000_ICH_FWSM_FW_VALID) &&
+ (fwsm & (E1000_ICH_MNG_IAMT_MODE << E1000_FWSM_MODE_SHIFT)));
+}
+
+/*
+ * e1000_rar_set_pch2lan - Set receive address register
+ * @hw: pointer to the HW structure
+ * @addr: pointer to the receive address
+ * @index: receive address array register
+ *
+ * Sets the receive address array register at index to the address passed
+ * in by addr. For 82579, RAR[0] is the base address register that is to
+ * contain the MAC address but RAR[1-6] are reserved for manageability (ME).
+ * Use SHRA[0-3] in place of those reserved for ME.
+ */
+static void
+e1000_rar_set_pch2lan(struct e1000_hw *hw, u8 *addr, u32 index)
+{
+ u32 rar_low, rar_high;
+
+ DEBUGFUNC("e1000_rar_set_pch2lan");
+
+ /*
+ * HW expects these in little endian so we reverse the byte order
+ * from network order (big endian) to little endian
+ */
+ rar_low = ((u32) addr[0] | ((u32) addr[1] << 8) |
+ ((u32) addr[2] << 16) | ((u32) addr[3] << 24));
+
+ rar_high = ((u32) addr[4] | ((u32) addr[5] << 8));
+
+ /* If MAC address zero, no need to set the AV bit */
+ if (rar_low || rar_high)
+ rar_high |= E1000_RAH_AV;
+
+ if (index == 0) {
+ E1000_WRITE_REG(hw, E1000_RAL(index), rar_low);
+ E1000_WRITE_FLUSH(hw);
+ E1000_WRITE_REG(hw, E1000_RAH(index), rar_high);
+ E1000_WRITE_FLUSH(hw);
+ return;
+ }
+
+ if (index < hw->mac.rar_entry_count) {
+ E1000_WRITE_REG(hw, E1000_SHRAL(index - 1), rar_low);
+ E1000_WRITE_FLUSH(hw);
+ E1000_WRITE_REG(hw, E1000_SHRAH(index - 1), rar_high);
+ E1000_WRITE_FLUSH(hw);
+
+ /* verify the register updates */
+ if ((E1000_READ_REG(hw, E1000_SHRAL(index - 1)) == rar_low) &&
+ (E1000_READ_REG(hw, E1000_SHRAH(index - 1)) == rar_high))
+ return;
+
+ DEBUGOUT2("SHRA[%d] might be locked by ME - FWSM=0x%8.8x\n",
+ (index - 1), E1000_READ_REG(hw, E1000_FWSM));
+ }
+
+ DEBUGOUT1("Failed to write receive address at index %d\n", index);
+}
+
+/*
+ * e1000_update_mc_addr_list_pch2lan - Update Multicast addresses
+ * @hw: pointer to the HW structure
+ * @mc_addr_list: array of multicast addresses to program
+ * @mc_addr_count: number of multicast addresses to program
+ *
+ * Updates entire Multicast Table Array of the PCH2 MAC and PHY.
+ * The caller must have a packed mc_addr_list of multicast addresses.
+ */
+static void
+e1000_update_mc_addr_list_pch2lan(struct e1000_hw *hw, u8 *mc_addr_list,
+ u32 mc_addr_count)
+{
+ int i;
+
+ DEBUGFUNC("e1000_update_mc_addr_list_pch2lan");
+
+ e1000_update_mc_addr_list_generic(hw, mc_addr_list, mc_addr_count);
+
+ for (i = 0; i < hw->mac.mta_reg_count; i++) {
+ hw->phy.ops.write_reg(hw, BM_MTA(i),
+ (u16)(hw->mac.mta_shadow[i] & 0xFFFF));
+ hw->phy.ops.write_reg(hw, (BM_MTA(i) + 1),
+ (u16)((hw->mac.mta_shadow[i] >> 16) &
+ 0xFFFF));
+ }
+}
+
+/*
* e1000_check_reset_block_ich8lan - Check if PHY reset is blocked
* @hw: pointer to the HW structure
*
@@ -746,6 +1024,35 @@ e1000_check_reset_block_ich8lan(struct e1000_hw *hw)
}
/*
+ * e1000_write_smbus_addr - Write SMBus address to PHY needed during Sx states
+ * @hw: pointer to the HW structure
+ *
+ * Assumes semaphore already acquired.
+ *
+ */
+static s32
+e1000_write_smbus_addr(struct e1000_hw *hw)
+{
+ u16 phy_data;
+ u32 strap = E1000_READ_REG(hw, E1000_STRAP);
+ s32 ret_val = E1000_SUCCESS;
+
+ strap &= E1000_STRAP_SMBUS_ADDRESS_MASK;
+
+ ret_val = e1000_read_phy_reg_hv_locked(hw, HV_SMB_ADDR, &phy_data);
+ if (ret_val)
+ goto out;
+
+ phy_data &= ~HV_SMB_ADDR_MASK;
+ phy_data |= (strap >> E1000_STRAP_SMBUS_ADDRESS_SHIFT);
+ phy_data |= HV_SMB_ADDR_PEC_EN | HV_SMB_ADDR_VALID;
+ ret_val = e1000_write_phy_reg_hv_locked(hw, HV_SMB_ADDR, phy_data);
+
+out:
+ return (ret_val);
+}
+
+/*
* e1000_sw_lcd_config_ich8lan - SW-based LCD Configuration
* @hw: pointer to the HW structure
*
@@ -755,15 +1062,12 @@ e1000_check_reset_block_ich8lan(struct e1000_hw *hw)
static s32
e1000_sw_lcd_config_ich8lan(struct e1000_hw *hw)
{
- struct e1000_dev_spec_ich8lan *dev_spec = &hw->dev_spec.ich8lan;
struct e1000_phy_info *phy = &hw->phy;
u32 i, data, cnf_size, cnf_base_addr, sw_cfg_mask;
- s32 ret_val;
+ s32 ret_val = E1000_SUCCESS;
u16 word_addr, reg_data, reg_addr, phy_page = 0;
- ret_val = hw->phy.ops.acquire(hw);
- if (ret_val)
- return (ret_val);
+ DEBUGFUNC("e1000_sw_lcd_config_ich8lan");
/*
* Initialize the PHY from the NVM on ICH platforms. This
@@ -772,108 +1076,101 @@ e1000_sw_lcd_config_ich8lan(struct e1000_hw *hw)
* Therefore, after each PHY reset, we will load the
* configuration data out of the NVM manually.
*/
- if ((hw->mac.type == e1000_ich8lan && phy->type == e1000_phy_igp_3) ||
- (hw->mac.type == e1000_pchlan)) {
- /* Check if SW needs to configure the PHY */
- if ((hw->device_id == E1000_DEV_ID_ICH8_IGP_M_AMT) ||
- (hw->device_id == E1000_DEV_ID_ICH8_IGP_M) ||
- (hw->mac.type == e1000_pchlan))
- sw_cfg_mask = E1000_FEXTNVM_SW_CONFIG_ICH8M;
- else
+ switch (hw->mac.type) {
+ case e1000_ich8lan:
+ if (phy->type != e1000_phy_igp_3)
+ return (ret_val);
+
+ if ((hw->device_id == E1000_DEV_ID_ICH8_IGP_AMT) ||
+ (hw->device_id == E1000_DEV_ID_ICH8_IGP_C)) {
sw_cfg_mask = E1000_FEXTNVM_SW_CONFIG;
+ break;
+ }
+ /* FALLTHROUGH */
+ case e1000_pchlan:
+ case e1000_pch2lan:
+ sw_cfg_mask = E1000_FEXTNVM_SW_CONFIG_ICH8M;
+ break;
+ default:
+ return (ret_val);
+ }
- data = E1000_READ_REG(hw, E1000_FEXTNVM);
- if (!(data & sw_cfg_mask))
- goto out;
+ ret_val = hw->phy.ops.acquire(hw);
+ if (ret_val)
+ return (ret_val);
- /* Wait for basic configuration completes before proceeding */
- e1000_lan_init_done_ich8lan(hw);
+ data = E1000_READ_REG(hw, E1000_FEXTNVM);
+ if (!(data & sw_cfg_mask))
+ goto out;
- /*
- * Make sure HW does not configure LCD from PHY
- * extended configuration before SW configuration
- */
- data = E1000_READ_REG(hw, E1000_EXTCNF_CTRL);
+ /*
+ * Make sure HW does not configure LCD from PHY
+ * extended configuration before SW configuration
+ */
+ data = E1000_READ_REG(hw, E1000_EXTCNF_CTRL);
+ if (!(hw->mac.type == e1000_pch2lan)) {
if (data & E1000_EXTCNF_CTRL_LCD_WRITE_ENABLE)
goto out;
+ }
- cnf_size = E1000_READ_REG(hw, E1000_EXTCNF_SIZE);
- cnf_size &= E1000_EXTCNF_SIZE_EXT_PCIE_LENGTH_MASK;
- cnf_size >>= E1000_EXTCNF_SIZE_EXT_PCIE_LENGTH_SHIFT;
- if (!cnf_size)
- goto out;
+ cnf_size = E1000_READ_REG(hw, E1000_EXTCNF_SIZE);
+ cnf_size &= E1000_EXTCNF_SIZE_EXT_PCIE_LENGTH_MASK;
+ cnf_size >>= E1000_EXTCNF_SIZE_EXT_PCIE_LENGTH_SHIFT;
+ if (!cnf_size)
+ goto out;
- cnf_base_addr = data & E1000_EXTCNF_CTRL_EXT_CNF_POINTER_MASK;
- cnf_base_addr >>= E1000_EXTCNF_CTRL_EXT_CNF_POINTER_SHIFT;
+ cnf_base_addr = data & E1000_EXTCNF_CTRL_EXT_CNF_POINTER_MASK;
+ cnf_base_addr >>= E1000_EXTCNF_CTRL_EXT_CNF_POINTER_SHIFT;
- if (!(data & E1000_EXTCNF_CTRL_OEM_WRITE_ENABLE) &&
- hw->mac.type == e1000_pchlan) {
- /*
- * HW configures the SMBus address and LEDs when the
- * OEM and LCD Write Enable bits are set in the NVM.
- * When both NVM bits are cleared, SW will configure
- * them instead.
- */
- data = E1000_READ_REG(hw, E1000_STRAP);
- data &= E1000_STRAP_SMBUS_ADDRESS_MASK;
- reg_data = data >> E1000_STRAP_SMBUS_ADDRESS_SHIFT;
- reg_data |= HV_SMB_ADDR_PEC_EN | HV_SMB_ADDR_VALID;
- ret_val = e1000_write_phy_reg_hv_locked(hw, HV_SMB_ADDR,
- reg_data);
- if (ret_val)
- goto out;
-
- data = E1000_READ_REG(hw, E1000_LEDCTL);
- ret_val = e1000_write_phy_reg_hv_locked(hw,
- HV_LED_CONFIG, (u16)data);
- if (ret_val)
- goto out;
+ if ((!(data & E1000_EXTCNF_CTRL_OEM_WRITE_ENABLE) &&
+ (hw->mac.type == e1000_pchlan)) ||
+ (hw->mac.type == e1000_pch2lan)) {
+ /*
+ * HW configures the SMBus address and LEDs when the
+ * OEM and LCD Write Enable bits are set in the NVM.
+ * When both NVM bits are cleared, SW will configure
+ * them instead.
+ */
+ ret_val = e1000_write_smbus_addr(hw);
+ if (ret_val)
+ goto out;
- dev_spec->nvm_lcd_config_enabled = true;
- }
- /* Configure LCD from extended configuration region. */
+ data = E1000_READ_REG(hw, E1000_LEDCTL);
+ ret_val = e1000_write_phy_reg_hv_locked(hw, HV_LED_CONFIG,
+ (u16)data);
+ if (ret_val)
+ goto out;
+ }
- /* cnf_base_addr is in DWORD */
- word_addr = (u16)(cnf_base_addr << 1);
+ /* Configure LCD from extended configuration region. */
- for (i = 0; i < cnf_size; i++) {
- ret_val = hw->nvm.ops.read(hw, (word_addr + i * 2), 1,
- &reg_data);
- if (ret_val)
- goto out;
+ /* cnf_base_addr is in DWORD */
+ word_addr = (u16)(cnf_base_addr << 1);
- ret_val = hw->nvm.ops.read(hw, (word_addr + i * 2 + 1),
- 1, &reg_addr);
- if (ret_val)
- goto out;
-
- /* Save off the PHY page for future writes. */
- if (reg_addr == IGP01E1000_PHY_PAGE_SELECT) {
- phy_page = reg_data;
- continue;
- }
- /*
- * Bit 5 in the LCD config word contains the phy
- * address for PCH
- */
- if (hw->mac.type == e1000_pchlan) {
- phy->addr = 1;
- if (reg_addr & LCD_CFG_PHY_ADDR_BIT) {
- phy->addr = 2;
- reg_addr &= PHY_REG_MASK;
- }
- }
+ for (i = 0; i < cnf_size; i++) {
+ ret_val = hw->nvm.ops.read(hw, (word_addr + i * 2), 1,
+ &reg_data);
+ if (ret_val)
+ goto out;
- reg_addr |= phy_page;
+ ret_val = hw->nvm.ops.read(hw, (word_addr + i * 2 + 1),
+ 1, &reg_addr);
+ if (ret_val)
+ goto out;
- ret_val = phy->ops.write_reg_locked(hw, (u32)reg_addr,
- reg_data);
- if (ret_val)
- goto out;
+ /* Save off the PHY page for future writes. */
+ if (reg_addr == IGP01E1000_PHY_PAGE_SELECT) {
+ phy_page = reg_data;
+ continue;
}
- if (hw->mac.type == e1000_pchlan)
- dev_spec->nvm_lcd_config_enabled = false;
+ reg_addr &= PHY_REG_MASK;
+ reg_addr |= phy_page;
+
+ ret_val = phy->ops.write_reg_locked(hw, (u32)reg_addr,
+ reg_data);
+ if (ret_val)
+ goto out;
}
out:
@@ -881,6 +1178,7 @@ out:
return (ret_val);
}
+
/*
* e1000_k1_gig_workaround_hv - K1 Si workaround
* @hw: pointer to the HW structure
@@ -1036,16 +1334,20 @@ e1000_oem_bits_config_ich8lan(struct e1000_hw *hw, bool d0_state)
u32 mac_reg;
u16 oem_reg;
- if (hw->mac.type != e1000_pchlan)
+ DEBUGFUNC("e1000_oem_bits_config_ich8lan");
+
+ if ((hw->mac.type != e1000_pch2lan) && (hw->mac.type != e1000_pchlan))
return (ret_val);
ret_val = hw->phy.ops.acquire(hw);
if (ret_val)
return (ret_val);
- mac_reg = E1000_READ_REG(hw, E1000_EXTCNF_CTRL);
- if (mac_reg & E1000_EXTCNF_CTRL_OEM_WRITE_ENABLE)
- goto out;
+ if (!(hw->mac.type == e1000_pch2lan)) {
+ mac_reg = E1000_READ_REG(hw, E1000_EXTCNF_CTRL);
+ if (mac_reg & E1000_EXTCNF_CTRL_OEM_WRITE_ENABLE)
+ goto out;
+ }
mac_reg = E1000_READ_REG(hw, E1000_FEXTNVM);
if (!(mac_reg & E1000_FEXTNVM_SW_CONFIG_ICH8M))
@@ -1073,7 +1375,8 @@ e1000_oem_bits_config_ich8lan(struct e1000_hw *hw, bool d0_state)
oem_reg |= HV_OEM_BITS_LPLU;
}
/* Restart auto-neg to activate the bits */
- oem_reg |= HV_OEM_BITS_RESTART_AN;
+ if (!hw->phy.ops.check_reset_block(hw))
+ oem_reg |= HV_OEM_BITS_RESTART_AN;
ret_val = hw->phy.ops.write_reg_locked(hw, HV_OEM_BITS, oem_reg);
out:
@@ -1083,6 +1386,44 @@ out:
}
/*
+ * e1000_hv_phy_powerdown_workaround_ich8lan - Power down workaround on Sx
+ * @hw: pointer to the HW structure
+ */
+s32
+e1000_hv_phy_powerdown_workaround_ich8lan(struct e1000_hw *hw)
+{
+ DEBUGFUNC("e1000_hv_phy_powerdown_workaround_ich8lan");
+
+ if ((hw->phy.type != e1000_phy_82577) || (hw->revision_id > 2))
+ return (E1000_SUCCESS);
+
+ return (hw->phy.ops.write_reg(hw, PHY_REG(768, 25), 0x0444));
+}
+
+/*
+ * e1000_set_mdio_slow_mode_hv - Set slow MDIO access mode
+ * @hw: pointer to the HW structure
+ */
+static s32
+e1000_set_mdio_slow_mode_hv(struct e1000_hw *hw)
+{
+ s32 ret_val;
+ u16 data;
+
+ DEBUGFUNC("e1000_set_mdio_slow_mode_hv");
+
+ ret_val = hw->phy.ops.read_reg(hw, HV_KMRN_MODE_CTRL, &data);
+ if (ret_val)
+ return (ret_val);
+
+ data |= HV_KMRN_MDIO_SLOW;
+
+ ret_val = hw->phy.ops.write_reg(hw, HV_KMRN_MODE_CTRL, data);
+
+ return (ret_val);
+}
+
+/*
* e1000_hv_phy_workarounds_ich8lan - A series of Phy workarounds to be
* done after every PHY reset.
*/
@@ -1142,6 +1483,320 @@ out:
}
/*
+ * e1000_copy_rx_addrs_to_phy_ich8lan - Copy Rx addresses from MAC to PHY
+ * @hw: pointer to the HW structure
+ */
+void
+e1000_copy_rx_addrs_to_phy_ich8lan(struct e1000_hw *hw)
+{
+ u32 mac_reg;
+ u16 i;
+
+ DEBUGFUNC("e1000_copy_rx_addrs_to_phy_ich8lan");
+
+ /* Copy both RAL/H (rar_entry_count) and SHRAL/H (+4) to PHY */
+ for (i = 0; i < (hw->mac.rar_entry_count + 4); i++) {
+ mac_reg = E1000_READ_REG(hw, E1000_RAL(i));
+ hw->phy.ops.write_reg(hw, BM_RAR_L(i),
+ (u16)(mac_reg & 0xFFFF));
+ hw->phy.ops.write_reg(hw, BM_RAR_M(i),
+ (u16)((mac_reg >> 16) & 0xFFFF));
+ mac_reg = E1000_READ_REG(hw, E1000_RAH(i));
+ hw->phy.ops.write_reg(hw, BM_RAR_H(i),
+ (u16)(mac_reg & 0xFFFF));
+ hw->phy.ops.write_reg(hw, BM_RAR_CTRL(i),
+ (u16)((mac_reg >> 16) & 0x8000));
+ }
+}
+
+static u32
+e1000_calc_rx_da_crc(u8 mac[])
+{
+ u32 poly = 0xEDB88320; /* Polynomial for 802.3 CRC calculation */
+ u32 i, j, mask, crc;
+
+ DEBUGFUNC("e1000_calc_rx_da_crc");
+
+ crc = 0xffffffff;
+ for (i = 0; i < 6; i++) {
+ crc = crc ^ mac[i];
+ for (j = 8; j > 0; j--) {
+ mask = (crc & 1) * (-1);
+ crc = (crc >> 1) ^ (poly & mask);
+ }
+ }
+ return (~crc);
+}
+
+/*
+ * e1000_lv_jumbo_workaround_ich8lan - required for jumbo frame operation
+ * with 82579 PHY
+ * @hw: pointer to the HW structure
+ * @enable: flag to enable/disable workaround when enabling/disabling jumbos
+ */
+s32
+e1000_lv_jumbo_workaround_ich8lan(struct e1000_hw *hw, bool enable)
+{
+ s32 ret_val = E1000_SUCCESS;
+ u16 phy_reg, data;
+ u32 mac_reg;
+ u16 i;
+
+ DEBUGFUNC("e1000_lv_jumbo_workaround_ich8lan");
+
+ if (hw->mac.type != e1000_pch2lan)
+ goto out;
+
+ /* disable Rx path while enabling/disabling workaround */
+ hw->phy.ops.read_reg(hw, PHY_REG(769, 20), &phy_reg);
+ ret_val = hw->phy.ops.write_reg(hw, PHY_REG(769, 20),
+ phy_reg | (1 << 14));
+ if (ret_val)
+ goto out;
+
+ if (enable) {
+ /*
+ * Write Rx addresses (rar_entry_count for RAL/H, +4 for
+ * SHRAL/H) and initial CRC values to the MAC
+ */
+ for (i = 0; i < (hw->mac.rar_entry_count + 4); i++) {
+ u8 mac_addr[ETH_ADDR_LEN] = {0};
+ u32 addr_high, addr_low;
+
+ addr_high = E1000_READ_REG(hw, E1000_RAH(i));
+ if (!(addr_high & E1000_RAH_AV))
+ continue;
+ addr_low = E1000_READ_REG(hw, E1000_RAL(i));
+ mac_addr[0] = (addr_low & 0xFF);
+ mac_addr[1] = ((addr_low >> 8) & 0xFF);
+ mac_addr[2] = ((addr_low >> 16) & 0xFF);
+ mac_addr[3] = ((addr_low >> 24) & 0xFF);
+ mac_addr[4] = (addr_high & 0xFF);
+ mac_addr[5] = ((addr_high >> 8) & 0xFF);
+
+ E1000_WRITE_REG(hw, E1000_PCH_RAICC(i),
+ e1000_calc_rx_da_crc(mac_addr));
+ }
+
+ /* Write Rx addresses to the PHY */
+ e1000_copy_rx_addrs_to_phy_ich8lan(hw);
+
+ /* Enable jumbo frame workaround in the MAC */
+ mac_reg = E1000_READ_REG(hw, E1000_FFLT_DBG);
+ mac_reg &= ~(1 << 14);
+ mac_reg |= (7 << 15);
+ E1000_WRITE_REG(hw, E1000_FFLT_DBG, mac_reg);
+
+ mac_reg = E1000_READ_REG(hw, E1000_RCTL);
+ mac_reg |= E1000_RCTL_SECRC;
+ E1000_WRITE_REG(hw, E1000_RCTL, mac_reg);
+
+ ret_val = e1000_read_kmrn_reg_generic(hw,
+ E1000_KMRNCTRLSTA_CTRL_OFFSET,
+ &data);
+ if (ret_val)
+ goto out;
+ ret_val = e1000_write_kmrn_reg_generic(hw,
+ E1000_KMRNCTRLSTA_CTRL_OFFSET,
+ data | (1 << 0));
+ if (ret_val)
+ goto out;
+ ret_val = e1000_read_kmrn_reg_generic(hw,
+ E1000_KMRNCTRLSTA_HD_CTRL,
+ &data);
+ if (ret_val)
+ goto out;
+ data &= ~(0xF << 8);
+ data |= (0xB << 8);
+ ret_val = e1000_write_kmrn_reg_generic(hw,
+ E1000_KMRNCTRLSTA_HD_CTRL,
+ data);
+ if (ret_val)
+ goto out;
+
+ /* Enable jumbo frame workaround in the PHY */
+ hw->phy.ops.read_reg(hw, PHY_REG(769, 23), &data);
+ data &= ~(0x7F << 5);
+ data |= (0x37 << 5);
+ ret_val = hw->phy.ops.write_reg(hw, PHY_REG(769, 23), data);
+ if (ret_val)
+ goto out;
+ hw->phy.ops.read_reg(hw, PHY_REG(769, 16), &data);
+ data &= ~(1 << 13);
+ ret_val = hw->phy.ops.write_reg(hw, PHY_REG(769, 16), data);
+ if (ret_val)
+ goto out;
+ hw->phy.ops.read_reg(hw, PHY_REG(776, 20), &data);
+ data &= ~(0x3FF << 2);
+ data |= (0x1A << 2);
+ ret_val = hw->phy.ops.write_reg(hw, PHY_REG(776, 20), data);
+ if (ret_val)
+ goto out;
+ ret_val = hw->phy.ops.write_reg(hw, PHY_REG(776, 23), 0xFE00);
+ if (ret_val)
+ goto out;
+ hw->phy.ops.read_reg(hw, HV_PM_CTRL, &data);
+ ret_val = hw->phy.ops.write_reg(hw, HV_PM_CTRL,
+ data | (1 << 10));
+ if (ret_val)
+ goto out;
+ } else {
+ /* Write MAC register values back to h/w defaults */
+ mac_reg = E1000_READ_REG(hw, E1000_FFLT_DBG);
+ mac_reg &= ~(0xF << 14);
+ E1000_WRITE_REG(hw, E1000_FFLT_DBG, mac_reg);
+
+ mac_reg = E1000_READ_REG(hw, E1000_RCTL);
+ mac_reg &= ~E1000_RCTL_SECRC;
+ E1000_WRITE_REG(hw, E1000_RCTL, mac_reg);
+
+ ret_val = e1000_read_kmrn_reg_generic(hw,
+ E1000_KMRNCTRLSTA_CTRL_OFFSET,
+ &data);
+ if (ret_val)
+ goto out;
+ ret_val = e1000_write_kmrn_reg_generic(hw,
+ E1000_KMRNCTRLSTA_CTRL_OFFSET,
+ data & ~(1 << 0));
+ if (ret_val)
+ goto out;
+ ret_val = e1000_read_kmrn_reg_generic(hw,
+ E1000_KMRNCTRLSTA_HD_CTRL,
+ &data);
+ if (ret_val)
+ goto out;
+ data &= ~(0xF << 8);
+ data |= (0xB << 8);
+ ret_val = e1000_write_kmrn_reg_generic(hw,
+ E1000_KMRNCTRLSTA_HD_CTRL, data);
+ if (ret_val)
+ goto out;
+
+ /* Write PHY register values back to h/w defaults */
+ hw->phy.ops.read_reg(hw, PHY_REG(769, 23), &data);
+ data &= ~(0x7F << 5);
+ ret_val = hw->phy.ops.write_reg(hw, PHY_REG(769, 23), data);
+ if (ret_val)
+ goto out;
+ hw->phy.ops.read_reg(hw, PHY_REG(769, 16), &data);
+ data |= (1 << 13);
+ ret_val = hw->phy.ops.write_reg(hw, PHY_REG(769, 16), data);
+ if (ret_val)
+ goto out;
+ hw->phy.ops.read_reg(hw, PHY_REG(776, 20), &data);
+ data &= ~(0x3FF << 2);
+ data |= (0x8 << 2);
+ ret_val = hw->phy.ops.write_reg(hw, PHY_REG(776, 20), data);
+ if (ret_val)
+ goto out;
+ ret_val = hw->phy.ops.write_reg(hw, PHY_REG(776, 23), 0x7E00);
+ if (ret_val)
+ goto out;
+ hw->phy.ops.read_reg(hw, HV_PM_CTRL, &data);
+ ret_val = hw->phy.ops.write_reg(hw, HV_PM_CTRL,
+ data & ~(1 << 10));
+ if (ret_val)
+ goto out;
+ }
+
+ /* re-enable Rx path after enabling/disabling workaround */
+ ret_val = hw->phy.ops.write_reg(hw, PHY_REG(769, 20),
+ phy_reg & ~(1 << 14));
+
+out:
+ return (ret_val);
+}
+
+/*
+ * e1000_lv_phy_workarounds_ich8lan - A series of Phy workarounds to be
+ * done after every PHY reset.
+ */
+static s32
+e1000_lv_phy_workarounds_ich8lan(struct e1000_hw *hw)
+{
+ s32 ret_val = E1000_SUCCESS;
+
+ DEBUGFUNC("e1000_lv_phy_workarounds_ich8lan");
+
+ if (hw->mac.type != e1000_pch2lan)
+ goto out;
+
+ /* Set MDIO slow mode before any other MDIO access */
+ ret_val = e1000_set_mdio_slow_mode_hv(hw);
+
+out:
+ return (ret_val);
+}
+
+/*
+ * e1000_k1_gig_workaround_lv - K1 Si workaround
+ * @hw: pointer to the HW structure
+ *
+ * Workaround to set the K1 beacon duration for 82579 parts
+ */
+static s32
+e1000_k1_workaround_lv(struct e1000_hw *hw)
+{
+ s32 ret_val = E1000_SUCCESS;
+ u16 status_reg = 0;
+ u32 mac_reg;
+
+ DEBUGFUNC("e1000_k1_workaround_lv");
+
+ if (hw->mac.type != e1000_pch2lan)
+ goto out;
+
+ /* Set K1 beacon duration based on 1Gbps speed or otherwise */
+ ret_val = hw->phy.ops.read_reg(hw, HV_M_STATUS, &status_reg);
+ if (ret_val)
+ goto out;
+
+ if ((status_reg & (HV_M_STATUS_LINK_UP | HV_M_STATUS_AUTONEG_COMPLETE))
+ == (HV_M_STATUS_LINK_UP | HV_M_STATUS_AUTONEG_COMPLETE)) {
+ mac_reg = E1000_READ_REG(hw, E1000_FEXTNVM4);
+ mac_reg &= ~E1000_FEXTNVM4_BEACON_DURATION_MASK;
+
+ if (status_reg & HV_M_STATUS_SPEED_1000)
+ mac_reg |= E1000_FEXTNVM4_BEACON_DURATION_8USEC;
+ else
+ mac_reg |= E1000_FEXTNVM4_BEACON_DURATION_16USEC;
+
+ E1000_WRITE_REG(hw, E1000_FEXTNVM4, mac_reg);
+ }
+
+out:
+ return (ret_val);
+}
+
+/*
+ * e1000_gate_hw_phy_config_ich8lan - disable PHY config via hardware
+ * @hw: pointer to the HW structure
+ * @gate: boolean set to true to gate, false to ungate
+ *
+ * Gate/ungate the automatic PHY configuration via hardware; perform
+ * the configuration via software instead.
+ */
+static void
+e1000_gate_hw_phy_config_ich8lan(struct e1000_hw *hw, bool gate)
+{
+ u32 extcnf_ctrl;
+
+ DEBUGFUNC("e1000_gate_hw_phy_config_ich8lan");
+
+ if (hw->mac.type != e1000_pch2lan)
+ return;
+
+ extcnf_ctrl = E1000_READ_REG(hw, E1000_EXTCNF_CTRL);
+
+ if (gate)
+ extcnf_ctrl |= E1000_EXTCNF_CTRL_GATE_PHY_CFG;
+ else
+ extcnf_ctrl &= ~E1000_EXTCNF_CTRL_GATE_PHY_CFG;
+
+ E1000_WRITE_REG(hw, E1000_EXTCNF_CTRL, extcnf_ctrl);
+}
+
+/*
* e1000_hv_phy_tuning_workaround_ich8lan - This is a Phy tuning work around
* needed for Nahum3 + Hanksville testing, requested by HW team
*/
@@ -1206,32 +1861,37 @@ e1000_lan_init_done_ich8lan(struct e1000_hw *hw)
}
/*
- * e1000_phy_hw_reset_ich8lan - Performs a PHY reset
+ * e1000_post_phy_reset_ich8lan - Perform steps required after a PHY reset
* @hw: pointer to the HW structure
- *
- * Resets the PHY
- * This is a function pointer entry point called by drivers
- * or other shared routines.
*/
static s32
-e1000_phy_hw_reset_ich8lan(struct e1000_hw *hw)
+e1000_post_phy_reset_ich8lan(struct e1000_hw *hw)
{
s32 ret_val = E1000_SUCCESS;
u16 reg;
- DEBUGFUNC("e1000_phy_hw_reset_ich8lan");
+ DEBUGFUNC("e1000_post_phy_reset_ich8lan");
- ret_val = e1000_phy_hw_reset_generic(hw);
- if (ret_val)
+ if (hw->phy.ops.check_reset_block(hw))
goto out;
- /* Allow time for h/w to get to a quiescent state after reset */
+ /* Allow time for h/w to get to quiescent state after reset */
msec_delay(10);
- if (hw->mac.type == e1000_pchlan) {
+ /* Perform any necessary post-reset workarounds */
+ switch (hw->mac.type) {
+ case e1000_pchlan:
ret_val = e1000_hv_phy_workarounds_ich8lan(hw);
if (ret_val)
goto out;
+ break;
+ case e1000_pch2lan:
+ ret_val = e1000_lv_phy_workarounds_ich8lan(hw);
+ if (ret_val)
+ goto out;
+ break;
+ default:
+ break;
}
if (hw->device_id == E1000_DEV_ID_ICH10_HANKSVILLE) {
@@ -1241,7 +1901,7 @@ e1000_phy_hw_reset_ich8lan(struct e1000_hw *hw)
}
/* Dummy read to clear the phy wakeup bit after lcd reset */
- if (hw->mac.type == e1000_pchlan)
+ if (hw->mac.type >= e1000_pchlan)
hw->phy.ops.read_reg(hw, BM_WUC, &reg);
/* Configure the LCD with the extended configuration region in NVM */
@@ -1250,12 +1910,65 @@ e1000_phy_hw_reset_ich8lan(struct e1000_hw *hw)
goto out;
/* Configure the LCD with the OEM bits in NVM */
- if (hw->mac.type == e1000_pchlan)
- ret_val = e1000_oem_bits_config_ich8lan(hw, true);
+ ret_val = e1000_oem_bits_config_ich8lan(hw, true);
+
+ if (hw->mac.type == e1000_pch2lan) {
+ /* Ungate automatic PHY configuration on non-managed 82579 */
+ if (!(E1000_READ_REG(hw, E1000_FWSM) &
+ E1000_ICH_FWSM_FW_VALID)) {
+ msec_delay(10);
+ e1000_gate_hw_phy_config_ich8lan(hw, false);
+ }
+
+ /* Set EEE LPI Update Timer to 200usec */
+ ret_val = hw->phy.ops.acquire(hw);
+ if (ret_val)
+ goto out;
+ ret_val = hw->phy.ops.write_reg_locked(hw, I82579_EMI_ADDR,
+ I82579_LPI_UPDATE_TIMER);
+ if (ret_val)
+ goto release;
+ ret_val = hw->phy.ops.write_reg_locked(hw, I82579_EMI_DATA,
+ 0x1387);
+release:
+ hw->phy.ops.release(hw);
+ }
out:
return (ret_val);
}
+
+/*
+ * e1000_phy_hw_reset_ich8lan - Performs a PHY reset
+ * @hw: pointer to the HW structure
+ *
+ * Resets the PHY
+ * This is a function pointer entry point called by drivers
+ * or other shared routines.
+ */
+static s32
+e1000_phy_hw_reset_ich8lan(struct e1000_hw *hw)
+{
+ s32 ret_val = E1000_SUCCESS;
+
+ DEBUGFUNC("e1000_phy_hw_reset_ich8lan");
+
+ /* Gate automatic PHY configuration by hardware on non-managed 82579 */
+ if ((hw->mac.type == e1000_pch2lan) &&
+ !(E1000_READ_REG(hw, E1000_FWSM) & E1000_ICH_FWSM_FW_VALID))
+ e1000_gate_hw_phy_config_ich8lan(hw, true);
+
+ ret_val = e1000_phy_hw_reset_generic(hw);
+ if (ret_val)
+ goto out;
+
+ ret_val = e1000_post_phy_reset_ich8lan(hw);
+
+out:
+ return (ret_val);
+}
+
+
/*
* e1000_get_phy_info_ich8lan - Calls appropriate PHY type get_phy_info
* @hw: pointer to the HW structure
@@ -2327,8 +3040,7 @@ e1000_write_flash_byte_ich8lan(struct e1000_hw *hw, u32 offset, u8 data)
* Goes through a retry algorithm before giving up.
*/
static s32
-e1000_retry_write_flash_byte_ich8lan(struct e1000_hw *hw,
- u32 offset, u8 byte)
+e1000_retry_write_flash_byte_ich8lan(struct e1000_hw *hw, u32 offset, u8 byte)
{
s32 ret_val;
u16 program_retries;
@@ -2628,10 +3340,8 @@ e1000_reset_hw_ich8lan(struct e1000_hw *hw)
* on the last TLP read/write transaction when MAC is reset.
*/
ret_val = e1000_disable_pcie_master_generic(hw);
- if (ret_val) {
- /* EMPTY */
+ if (ret_val)
DEBUGOUT("PCI-E Master disable polling has failed.\n");
- }
DEBUGOUT("Masking off all interrupts\n");
E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff);
@@ -2669,24 +3379,23 @@ e1000_reset_hw_ich8lan(struct e1000_hw *hw)
ctrl = E1000_READ_REG(hw, E1000_CTRL);
- if (!hw->phy.ops.check_reset_block(hw) && !hw->phy.reset_disable) {
- /* Clear PHY Reset Asserted bit */
- if (hw->mac.type >= e1000_pchlan) {
- u32 status = E1000_READ_REG(hw, E1000_STATUS);
- E1000_WRITE_REG(hw, E1000_STATUS, status &
- ~E1000_STATUS_PHYRA);
- }
-
+ if (!hw->phy.ops.check_reset_block(hw)) {
/*
- * PHY HW reset requires MAC CORE reset at the same time to
- * make sure the interface between MAC and the external PHY is
- * reset.
+ * Full-chip reset requires MAC and PHY reset at the same
+ * time to make sure the interface between MAC and the
+ * external PHY is reset.
*/
ctrl |= E1000_CTRL_PHY_RST;
- }
+ /*
+ * Gate automatic PHY configuration by hardware on
+ * non-managed 82579
+ */
+ if ((hw->mac.type == e1000_pch2lan) &&
+ !(E1000_READ_REG(hw, E1000_FWSM) & E1000_ICH_FWSM_FW_VALID))
+ e1000_gate_hw_phy_config_ich8lan(hw, true);
+ }
ret_val = e1000_acquire_swflag_ich8lan(hw);
-
DEBUGOUT("Issuing a global reset to ich8lan\n");
E1000_WRITE_REG(hw, E1000_CTRL, (ctrl | E1000_CTRL_RST));
msec_delay(20);
@@ -2694,34 +3403,12 @@ e1000_reset_hw_ich8lan(struct e1000_hw *hw)
if (!ret_val)
e1000_release_swflag_ich8lan(hw);
- if (ctrl & E1000_CTRL_PHY_RST)
+ if (ctrl & E1000_CTRL_PHY_RST) {
ret_val = hw->phy.ops.get_cfg_done(hw);
+ if (ret_val)
+ goto out;
- if (hw->mac.type >= e1000_ich10lan) {
- e1000_lan_init_done_ich8lan(hw);
- } else {
- ret_val = e1000_get_auto_rd_done_generic(hw);
- if (ret_val) {
- /* EMPTY */
- /*
- * When auto config read does not complete, do not
- * return with an error. This can happen in situations
- * where there is no eeprom and prevents getting link.
- */
- DEBUGOUT("Auto Read Done did not complete\n");
- }
- }
-
- /* Dummy read to clear the phy wakeup bit after lcd reset */
- if (hw->mac.type == e1000_pchlan)
- hw->phy.ops.read_reg(hw, BM_WUC, &reg);
-
- ret_val = e1000_sw_lcd_config_ich8lan(hw);
- if (ret_val)
- goto out;
-
- if (hw->mac.type == e1000_pchlan) {
- ret_val = e1000_oem_bits_config_ich8lan(hw, true);
+ ret_val = e1000_post_phy_reset_ich8lan(hw);
if (ret_val)
goto out;
}
@@ -2741,19 +3428,11 @@ e1000_reset_hw_ich8lan(struct e1000_hw *hw)
kab |= E1000_KABGTXD_BGSQLBIAS;
E1000_WRITE_REG(hw, E1000_KABGTXD, kab);
- if (hw->mac.type == e1000_pchlan)
- ret_val = e1000_hv_phy_workarounds_ich8lan(hw);
-
- if (ret_val)
- goto out;
-
- if (hw->device_id == E1000_DEV_ID_ICH10_HANKSVILLE)
- ret_val = e1000_hv_phy_tuning_workaround_ich8lan(hw);
-
out:
return (ret_val);
}
+
/*
* e1000_init_hw_ich8lan - Initialize the hardware
* @hw: pointer to the HW structure
@@ -2946,7 +3625,11 @@ e1000_setup_link_ich8lan(struct e1000_hw *hw)
E1000_WRITE_REG(hw, E1000_FCTTV, hw->fc.pause_time);
if ((hw->phy.type == e1000_phy_82578) ||
+ (hw->phy.type == e1000_phy_82579) ||
(hw->phy.type == e1000_phy_82577)) {
+ /* added from freebsd */
+ E1000_WRITE_REG(hw, E1000_FCRTV_PCH, hw->fc.refresh_time);
+
ret_val = hw->phy.ops.write_reg(hw,
PHY_REG(BM_PORT_CTRL_PAGE, 27),
hw->fc.pause_time);
@@ -3492,26 +4175,41 @@ e1000_get_cfg_done_ich8lan(struct e1000_hw *hw)
{
s32 ret_val = E1000_SUCCESS;
u32 bank = 0;
+ u32 status;
- if (hw->mac.type >= e1000_pchlan) {
- u32 status = E1000_READ_REG(hw, E1000_STATUS);
+ DEBUGFUNC("e1000_get_cfg_done_ich8lan");
- if (status & E1000_STATUS_PHYRA) {
- E1000_WRITE_REG(hw, E1000_STATUS, status &
- ~E1000_STATUS_PHYRA);
- } else
- /* EMPTY */
- DEBUGOUT("PHY Reset Asserted not set - needs delay\n");
+ (void) e1000_get_cfg_done_generic(hw);
+
+ /* Wait for indication from h/w that it has completed basic config */
+ if (hw->mac.type >= e1000_ich10lan) {
+ e1000_lan_init_done_ich8lan(hw);
+ } else {
+ ret_val = e1000_get_auto_rd_done_generic(hw);
+ if (ret_val) {
+ /*
+ * When auto config read does not complete, do not
+ * return with an error. This can happen in situations
+ * where there is no eeprom and prevents getting link.
+ */
+ DEBUGOUT("Auto Read Done did not complete\n");
+ ret_val = E1000_SUCCESS;
+ }
}
- (void) e1000_get_cfg_done_generic(hw);
+ /* Clear PHY Reset Asserted bit */
+ status = E1000_READ_REG(hw, E1000_STATUS);
+ if (status & E1000_STATUS_PHYRA) {
+ E1000_WRITE_REG(hw, E1000_STATUS, status & ~E1000_STATUS_PHYRA);
+ } else {
+ DEBUGOUT("PHY Reset Asserted not set - needs delay\n");
+ }
/* If EEPROM is not marked present, init the IGP 3 PHY manually */
- if ((hw->mac.type != e1000_ich10lan) &&
- (hw->mac.type != e1000_pchlan)) {
+ if (hw->mac.type <= e1000_ich9lan) {
if (((E1000_READ_REG(hw, E1000_EECD) & E1000_EECD_PRES) == 0) &&
(hw->phy.type == e1000_phy_igp_3)) {
- (void) e1000_phy_init_script_igp3(hw);
+ ret_val = e1000_phy_init_script_igp3(hw);
}
} else {
if (e1000_valid_nvm_bank_detect_ich8lan(hw, &bank)) {
diff --git a/usr/src/uts/common/io/e1000g/e1000_ich8lan.h b/usr/src/uts/common/io/e1000g/e1000_ich8lan.h
index 48d0f95c12..3dad4b6ed2 100644
--- a/usr/src/uts/common/io/e1000g/e1000_ich8lan.h
+++ b/usr/src/uts/common/io/e1000g/e1000_ich8lan.h
@@ -24,8 +24,36 @@
*/
/*
- * IntelVersion: 1.41 v3-1-10-1_2009-9-18_Release14-6
+ * Copyright (c) 2001-2010, Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
*/
+
#ifndef _E1000_ICH8LAN_H_
#define _E1000_ICH8LAN_H_
@@ -83,9 +111,14 @@ extern "C" {
#define E1000_FEXTNVM_SW_CONFIG 1
#define E1000_FEXTNVM_SW_CONFIG_ICH8M (1 << 27) /* Bit redefined for ICH8M */
+#define E1000_FEXTNVM4_BEACON_DURATION_MASK 0x7
+#define E1000_FEXTNVM4_BEACON_DURATION_8USEC 0x7
+#define E1000_FEXTNVM4_BEACON_DURATION_16USEC 0x3
+
#define PCIE_ICH8_SNOOP_ALL PCIE_NO_SNOOP_ALL
#define E1000_ICH_RAR_ENTRIES 7
+#define E1000_PCH2_RAR_ENTRIES 5 /* RAR[0], SHRA[0-3] */
#define PHY_PAGE_SHIFT 5
#define PHY_REG(page, reg) (((page) << PHY_PAGE_SHIFT) | \
@@ -146,6 +179,7 @@ extern "C" {
/* SMBus Address Phy Register */
#define HV_SMB_ADDR PHY_REG(768, 26)
+#define HV_SMB_ADDR_MASK 0x007F
#define HV_SMB_ADDR_PEC_EN 0x0200
#define HV_SMB_ADDR_VALID 0x0080
@@ -162,9 +196,39 @@ extern "C" {
/* Phy address bit from LCD Config word */
#define LCD_CFG_PHY_ADDR_BIT 0x0020
+/* KMRN Mode Control */
+#define HV_KMRN_MODE_CTRL PHY_REG(769, 16)
+#define HV_KMRN_MDIO_SLOW 0x0400
+
+/* KMRN FIFO Control and Status */
+#define HV_KMRN_FIFO_CTRLSTA PHY_REG(770, 16)
+#define HV_KMRN_FIFO_CTRLSTA_PREAMBLE_MASK 0x7000
+#define HV_KMRN_FIFO_CTRLSTA_PREAMBLE_SHIFT 12
+
+/* PHY Power Management Control */
+#define HV_PM_CTRL PHY_REG(770, 17)
+
/* SW Semaphore flag timeout in milliseconds */
#define SW_FLAG_TIMEOUT 400
+/* PHY Low Power Idle Control */
+#define I82579_LPI_CTRL PHY_REG(772, 20)
+#define I82579_LPI_CTRL_ENABLE_MASK 0x6000
+
+/* EMI Registers */
+#define I82579_EMI_ADDR 0x10
+#define I82579_EMI_DATA 0x11
+#define I82579_LPI_UPDATE_TIMER 0x4805 /* in 40ns units + 40 ns base value */
+
+/* PHY Low Power Idle Control */
+#define I82579_LPI_CTRL PHY_REG(772, 20)
+#define I82579_LPI_CTRL_ENABLE_MASK 0x6000
+
+/* EMI Registers */
+#define I82579_EMI_ADDR 0x10
+#define I82579_EMI_DATA 0x11
+#define I82579_LPI_UPDATE_TIMER 0x4805 /* in 40ns units + 40 ns base value */
+
/*
* Additional interrupts need to be handled for ICH family:
* DSW = The FW changed the status of the DISSW bit in FWSM
@@ -188,6 +252,8 @@ extern "C" {
#define E1000_RXDEXT_LINKSEC_ERROR_REPLAY_ERROR 0x40000000
#define E1000_RXDEXT_LINKSEC_ERROR_BAD_SIG 0x60000000
+/* Receive Address Initial CRC Calculation */
+#define E1000_PCH_RAICC(_n) (0x05F50 + ((_n) * 4))
void e1000_set_kmrn_lock_loss_workaround_ich8lan(struct e1000_hw *hw,
bool state);
@@ -196,6 +262,7 @@ void e1000_gig_downshift_workaround_ich8lan(struct e1000_hw *hw);
void e1000_disable_gig_wol_ich8lan(struct e1000_hw *hw);
s32 e1000_configure_k1_ich8lan(struct e1000_hw *hw, bool k1_enable);
s32 e1000_oem_bits_config_ich8lan(struct e1000_hw *hw, bool d0_config);
+s32 e1000_lv_jumbo_workaround_ich8lan(struct e1000_hw *hw, bool enable);
#ifdef __cplusplus
}
diff --git a/usr/src/uts/common/io/e1000g/e1000_phy.c b/usr/src/uts/common/io/e1000g/e1000_phy.c
index 21a7253099..e8e0698fb8 100644
--- a/usr/src/uts/common/io/e1000g/e1000_phy.c
+++ b/usr/src/uts/common/io/e1000g/e1000_phy.c
@@ -24,8 +24,36 @@
*/
/*
- * IntelVersion: 1.151 v3-1-10-1_2009-9-18_Release14-6
+ * Copyright (c) 2001-2010, Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
*/
+
#include "e1000_api.h"
static u32 e1000_get_phy_addr_for_bm_page(u32 page, u32 reg);
@@ -198,32 +226,9 @@ e1000_get_phy_id(struct e1000_hw *hw)
if (phy->id != 0 && phy->id != PHY_REVISION_MASK)
goto out;
- /*
- * If the PHY ID is still unknown, we may have an 82577
- * without link. We will try again after setting Slow MDIC
- * mode. No harm in trying again in this case since the PHY
- * ID is unknown at this point anyway.
- */
- ret_val = phy->ops.acquire(hw);
- if (ret_val)
- goto out;
- ret_val = e1000_set_mdio_slow_mode_hv(hw, true);
- if (ret_val)
- goto out;
- phy->ops.release(hw);
-
retry_count++;
}
out:
- /* Revert to MDIO fast mode, if applicable */
- if (retry_count) {
- ret_val = phy->ops.acquire(hw);
- if (ret_val)
- return (ret_val);
- ret_val = e1000_set_mdio_slow_mode_hv(hw, false);
- phy->ops.release(hw);
- }
-
return (ret_val);
}
@@ -305,6 +310,13 @@ e1000_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data)
}
*data = (u16)mdic;
+ /*
+ * Allow some time after each MDIC transaction to avoid
+ * reading duplicate data in the next MDIC transaction.
+ */
+ if (hw->mac.type == e1000_pch2lan)
+ usec_delay(100);
+
out:
return (ret_val);
}
@@ -360,6 +372,13 @@ e1000_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data)
goto out;
}
+ /*
+ * Allow some time after each MDIC transaction to avoid
+ * reading duplicate data in the next MDIC transaction.
+ */
+ if (hw->mac.type == e1000_pch2lan)
+ usec_delay(100);
+
out:
return (ret_val);
}
@@ -2527,6 +2546,9 @@ e1000_get_phy_type_from_id(u32 phy_id)
case I82577_E_PHY_ID:
phy_type = e1000_phy_82577;
break;
+ case I82579_E_PHY_ID:
+ phy_type = e1000_phy_82579;
+ break;
default:
phy_type = e1000_phy_unknown;
break;
@@ -2965,40 +2987,6 @@ e1000_power_down_phy_copper(struct e1000_hw *hw)
}
/*
- * e1000_set_mdio_slow_mode_hv - Set slow MDIO access mode
- * @hw: pointer to the HW structure
- * @slow: true for slow mode, false for normal mode
- *
- * Assumes semaphore already acquired.
- */
-s32
-e1000_set_mdio_slow_mode_hv(struct e1000_hw *hw, bool slow)
-{
- s32 ret_val = E1000_SUCCESS;
- u16 data = 0;
-
- /* Set MDIO mode - page 769, register 16: 0x2580==slow, 0x2180==fast */
- hw->phy.addr = 1;
- ret_val = e1000_write_phy_reg_mdic(hw, IGP01E1000_PHY_PAGE_SELECT,
- (BM_PORT_CTRL_PAGE << IGP_PAGE_SHIFT));
- if (ret_val)
- goto out;
-
- ret_val = e1000_write_phy_reg_mdic(hw, BM_CS_CTRL1,
- (0x2180 | (slow << 10)));
-
- if (ret_val)
- goto out;
-
- /* dummy read when reverting to fast mode - throw away result */
- if (!slow)
- ret_val = e1000_read_phy_reg_mdic(hw, BM_CS_CTRL1, &data);
-
-out:
- return (ret_val);
-}
-
-/*
* __e1000_read_phy_reg_hv - Read HV PHY register
* @hw: pointer to the HW structure
* @offset: register offset to be read
@@ -3016,9 +3004,8 @@ __e1000_read_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 *data,
s32 ret_val;
u16 page = BM_PHY_REG_PAGE(offset);
u16 reg = BM_PHY_REG_NUM(offset);
- bool in_slow_mode = false;
- DEBUGFUNC("e1000_read_phy_reg_hv");
+ DEBUGFUNC("__e1000_read_phy_reg_hv");
if (!locked) {
ret_val = hw->phy.ops.acquire(hw);
@@ -3026,16 +3013,6 @@ __e1000_read_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 *data,
return (ret_val);
}
- /* Workaround failure in MDIO access while cable is disconnected */
- if ((hw->phy.type == e1000_phy_82577) &&
- !(E1000_READ_REG(hw, E1000_STATUS) & E1000_STATUS_LU)) {
- ret_val = e1000_set_mdio_slow_mode_hv(hw, true);
- if (ret_val)
- goto out;
-
- in_slow_mode = true;
- }
-
/* Page 800 works differently than the rest so it has its own func */
if (page == BM_WUC_PAGE) {
ret_val = e1000_access_phy_wakeup_reg_bm(hw, offset,
@@ -3063,14 +3040,13 @@ __e1000_read_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 *data,
ret_val = e1000_write_phy_reg_mdic(hw,
IGP01E1000_PHY_PAGE_SELECT, (page << IGP_PAGE_SHIFT));
hw->phy.addr = phy_addr;
+
+ if (ret_val)
+ goto out;
}
ret_val = e1000_read_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & reg, data);
out:
- /* Revert to MDIO fast mode, if applicable */
- if ((hw->phy.type == e1000_phy_82577) && in_slow_mode)
- ret_val = e1000_set_mdio_slow_mode_hv(hw, false);
-
if (!locked)
hw->phy.ops.release(hw);
@@ -3125,10 +3101,8 @@ __e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data,
s32 ret_val;
u16 page = BM_PHY_REG_PAGE(offset);
u16 reg = BM_PHY_REG_NUM(offset);
- bool in_slow_mode = false;
- struct e1000_dev_spec_ich8lan *dev_spec = &hw->dev_spec.ich8lan;
- DEBUGFUNC("e1000_write_phy_reg_hv");
+ DEBUGFUNC("__e1000_write_phy_reg_hv");
if (!locked) {
ret_val = hw->phy.ops.acquire(hw);
@@ -3136,16 +3110,6 @@ __e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data,
return (ret_val);
}
- /* Workaround failure in MDIO access while cable is disconnected */
- if ((hw->phy.type == e1000_phy_82577) &&
- !(E1000_READ_REG(hw, E1000_STATUS) & E1000_STATUS_LU)) {
- ret_val = e1000_set_mdio_slow_mode_hv(hw, true);
- if (ret_val)
- goto out;
-
- in_slow_mode = true;
- }
-
/* Page 800 works differently than the rest so it has its own func */
if (page == BM_WUC_PAGE) {
ret_val = e1000_access_phy_wakeup_reg_bm(hw, offset,
@@ -3159,9 +3123,7 @@ __e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data,
goto out;
}
- /* The LCD Config workaround provides the phy address to use */
- if (dev_spec->nvm_lcd_config_enabled == false)
- hw->phy.addr = e1000_get_phy_addr_for_hv_page(page);
+ hw->phy.addr = e1000_get_phy_addr_for_hv_page(page);
if (page == HV_INTC_FC_PAGE_START)
page = 0;
@@ -3191,16 +3153,15 @@ __e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data,
ret_val = e1000_write_phy_reg_mdic(hw,
IGP01E1000_PHY_PAGE_SELECT, (page << IGP_PAGE_SHIFT));
hw->phy.addr = phy_addr;
+
+ if (ret_val)
+ goto out;
}
ret_val = e1000_write_phy_reg_mdic(hw, MAX_PHY_REG_ADDRESS & reg,
data);
out:
- /* Revert to MDIO fast mode, if applicable */
- if ((hw->phy.type == e1000_phy_82577) && in_slow_mode)
- ret_val = e1000_set_mdio_slow_mode_hv(hw, false);
-
if (!locked)
hw->phy.ops.release(hw);
diff --git a/usr/src/uts/common/io/e1000g/e1000_phy.h b/usr/src/uts/common/io/e1000g/e1000_phy.h
index e052cabeb1..905ed96241 100644
--- a/usr/src/uts/common/io/e1000g/e1000_phy.h
+++ b/usr/src/uts/common/io/e1000g/e1000_phy.h
@@ -24,8 +24,36 @@
*/
/*
- * IntelVersion: 1.74 v3-1-10-1_2009-9-18_Release14-6
+ * Copyright (c) 2001-2010, Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
*/
+
#ifndef _E1000_PHY_H_
#define _E1000_PHY_H_
@@ -91,7 +119,6 @@ s32 e1000_read_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 *data);
s32 e1000_read_phy_reg_hv_locked(struct e1000_hw *hw, u32 offset, u16 *data);
s32 e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data);
s32 e1000_write_phy_reg_hv_locked(struct e1000_hw *hw, u32 offset, u16 data);
-s32 e1000_set_mdio_slow_mode_hv(struct e1000_hw *hw, bool slow);
s32 e1000_link_stall_workaround_hv(struct e1000_hw *hw);
s32 e1000_copper_link_setup_82577(struct e1000_hw *hw);
s32 e1000_check_polarity_82577(struct e1000_hw *hw);
@@ -227,12 +254,15 @@ s32 e1000_get_cable_length_82577(struct e1000_hw *hw);
#define E1000_KMRNCTRLSTA_OFFSET 0x001F0000
#define E1000_KMRNCTRLSTA_OFFSET_SHIFT 16
#define E1000_KMRNCTRLSTA_REN 0x00200000
+#define E1000_KMRNCTRLSTA_CTRL_OFFSET 0x1 /* Kumeran Control */
#define E1000_KMRNCTRLSTA_DIAG_OFFSET 0x3 /* Kumeran Diagnostic */
#define E1000_KMRNCTRLSTA_TIMEOUTS 0x4 /* Kumeran Timeouts */
#define E1000_KMRNCTRLSTA_INBAND_PARAM 0x9 /* Kumeran InBand Parameters */
+#define E1000_KMRNCTRLSTA_IBIST_DISABLE 0x0200 /* Kumeran IBIST Disable */
#define E1000_KMRNCTRLSTA_DIAG_NELPBK 0x1000 /* Nearend Loopback mode */
#define E1000_KMRNCTRLSTA_K1_CONFIG 0x7
#define E1000_KMRNCTRLSTA_K1_ENABLE 0x0002
+#define E1000_KMRNCTRLSTA_HD_CTRL 0x10 /* Kumeran HD Control */
#define IFE_PHY_EXTENDED_STATUS_CONTROL 0x10
#define IFE_PHY_SPECIAL_CONTROL 0x11 /* 100BaseTx PHY Special Control */
diff --git a/usr/src/uts/common/io/e1000g/e1000_regs.h b/usr/src/uts/common/io/e1000g/e1000_regs.h
index c7a350a619..8e6353921e 100644
--- a/usr/src/uts/common/io/e1000g/e1000_regs.h
+++ b/usr/src/uts/common/io/e1000g/e1000_regs.h
@@ -24,8 +24,36 @@
*/
/*
- * IntelVersion: 1.76 v3-1-10-1_2009-9-18_Release14-6
+ * Copyright (c) 2001-2010, Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
*/
+
#ifndef _E1000_REGS_H_
#define _E1000_REGS_H_
@@ -45,6 +73,7 @@ extern "C" {
#define E1000_FCAL 0x00028 /* Flow Control Address Low - RW */
#define E1000_FCAH 0x0002C /* Flow Control Address High -RW */
#define E1000_FEXT 0x0002C /* Future Extended - RW */
+#define E1000_FEXTNVM4 0x00024 /* Future Extended NVM 4 - RW */
#define E1000_FEXTNVM 0x00028 /* Future Extended NVM - RW */
#define E1000_FCT 0x00030 /* Flow Control Type - RW */
#define E1000_CONNSW 0x00034 /* Copper/Fiber switch control - RW */
@@ -154,6 +183,8 @@ extern "C" {
(((_i) <= 15) ? (0x05400 + ((_i) * 8)) : (0x054E0 + ((_i - 16) * 8)))
#define E1000_RAH(_i) \
(((_i) <= 15) ? (0x05404 + ((_i) * 8)) : (0x054E4 + ((_i - 16) * 8)))
+#define E1000_SHRAL(_i) (0x05438 + ((_i) * 8))
+#define E1000_SHRAH(_i) (0x0543C + ((_i) * 8))
#define E1000_IP4AT_REG(_i) (0x05840 + ((_i) * 8))
#define E1000_IP6AT_REG(_i) (0x05880 + ((_i) * 4))
#define E1000_WUPM_REG(_i) (0x05A00 + ((_i) * 4))
diff --git a/usr/src/uts/common/io/e1000g/e1000g_main.c b/usr/src/uts/common/io/e1000g/e1000g_main.c
index ca074a5cea..d30f83044d 100644
--- a/usr/src/uts/common/io/e1000g/e1000g_main.c
+++ b/usr/src/uts/common/io/e1000g/e1000g_main.c
@@ -23,6 +23,10 @@
*/
/*
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
* **********************************************************************
* *
* Module Name: *
@@ -677,6 +681,7 @@ e1000g_regs_map(struct e1000g *Adapter)
case e1000_ich9lan:
case e1000_ich10lan:
case e1000_pchlan:
+ case e1000_pch2lan:
rnumber = ICH_FLASH_REG_SET;
/* get flash size */
@@ -881,6 +886,10 @@ e1000g_setup_max_mtu(struct e1000g *Adapter)
case e1000_pchlan:
Adapter->max_mtu = MAXIMUM_MTU_4K;
break;
+ /* pch2 can do jumbo frames up to 9K */
+ case e1000_pch2lan:
+ Adapter->max_mtu = MAXIMUM_MTU_9K;
+ break;
/* types with a special limit */
case e1000_82571:
case e1000_82572:
@@ -1439,6 +1448,8 @@ e1000g_init(struct e1000g *Adapter)
pba = E1000_PBA_10K;
} else if (hw->mac.type == e1000_pchlan) {
pba = E1000_PBA_26K;
+ } else if (hw->mac.type == e1000_pch2lan) {
+ pba = E1000_PBA_26K;
} else {
/*
* Total FIFO is 40K
@@ -4547,7 +4558,7 @@ e1000g_pch_limits(struct e1000g *Adapter)
struct e1000_hw *hw = &Adapter->shared;
/* only applies to PCH silicon type */
- if (hw->mac.type != e1000_pchlan)
+ if (hw->mac.type != e1000_pchlan && hw->mac.type != e1000_pch2lan)
return;
/* only applies to frames larger than ethernet default */
@@ -6400,6 +6411,7 @@ e1000g_get_driver_control(struct e1000_hw *hw)
case e1000_ich9lan:
case e1000_ich10lan:
case e1000_pchlan:
+ case e1000_pch2lan:
ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT);
E1000_WRITE_REG(hw, E1000_CTRL_EXT,
ctrl_ext | E1000_CTRL_EXT_DRV_LOAD);
@@ -6434,6 +6446,7 @@ e1000g_release_driver_control(struct e1000_hw *hw)
case e1000_ich9lan:
case e1000_ich10lan:
case e1000_pchlan:
+ case e1000_pch2lan:
ctrl_ext = E1000_READ_REG(hw, E1000_CTRL_EXT);
E1000_WRITE_REG(hw, E1000_CTRL_EXT,
ctrl_ext & ~E1000_CTRL_EXT_DRV_LOAD);
diff --git a/usr/src/uts/common/io/e1000g/e1000g_rx.c b/usr/src/uts/common/io/e1000g/e1000g_rx.c
index 09ea513b2a..57c1d0dc94 100644
--- a/usr/src/uts/common/io/e1000g/e1000g_rx.c
+++ b/usr/src/uts/common/io/e1000g/e1000g_rx.c
@@ -23,6 +23,10 @@
*/
/*
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ */
+
+/*
* **********************************************************************
* *
* Module Name: *
@@ -341,6 +345,16 @@ e1000g_rx_setup(struct e1000g *Adapter)
}
}
+ /* Workaround errata on 82579 adapters with large frames */
+ if (hw->mac.type == e1000_pch2lan) {
+ boolean_t enable_jumbo = (Adapter->default_mtu > ETHERMTU ?
+ B_TRUE : B_FALSE);
+
+ if (e1000_lv_jumbo_workaround_ich8lan(hw, enable_jumbo) != 0)
+ E1000G_DEBUGLOG_0(Adapter, E1000G_INFO_LEVEL,
+ "failed to enable jumbo frame workaround mode\n");
+ }
+
reg_val =
E1000_RXCSUM_TUOFL | /* TCP/UDP checksum offload Enable */
E1000_RXCSUM_IPOFL; /* IP checksum offload Enable */
diff --git a/usr/src/uts/common/io/ksocket/ksocket.c b/usr/src/uts/common/io/ksocket/ksocket.c
index 4100f049d7..49ca6f0475 100644
--- a/usr/src/uts/common/io/ksocket/ksocket.c
+++ b/usr/src/uts/common/io/ksocket/ksocket.c
@@ -20,6 +20,7 @@
*/
/*
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
*/
@@ -30,6 +31,7 @@
#include <sys/sysmacros.h>
#include <sys/filio.h> /* FIO* ioctls */
#include <sys/sockio.h> /* SIOC* ioctls */
+#include <sys/poll_impl.h>
#include <sys/cmn_err.h>
#include <sys/ksocket.h>
#include <io/ksocket/ksocket_impl.h>
@@ -54,7 +56,7 @@ ksocket_socket(ksocket_t *ksp, int domain, int type, int protocol, int flags,
/* All Solaris components should pass a cred for this operation. */
ASSERT(cr != NULL);
- if (domain == AF_NCA || domain == AF_UNIX)
+ if (domain == AF_NCA)
return (EAFNOSUPPORT);
ASSERT(flags == KSOCKET_SLEEP || flags == KSOCKET_NOSLEEP);
@@ -717,6 +719,144 @@ ksocket_ioctl(ksocket_t ks, int cmd, intptr_t arg, int *rvalp, struct cred *cr)
return (rval);
}
+/*
+ * Wait for an input event, similar to t_kspoll().
+ * Ideas and code borrowed from ../devpoll.c
+ * Basically, setup just enough poll data structures so
+ * we can block on a CV until timeout or pollwakeup().
+ */
+int
+ksocket_spoll(ksocket_t ks, int timo, short events, short *revents,
+ struct cred *cr)
+{
+ struct sonode *so;
+ pollhead_t *php, *php2;
+ polldat_t *pdp;
+ pollcache_t *pcp;
+ int error;
+ clock_t expires = 0;
+ clock_t rval;
+
+ /* All Solaris components should pass a cred for this operation. */
+ ASSERT(cr != NULL);
+ ASSERT(curthread->t_pollcache == NULL);
+
+ if (revents == NULL)
+ return (EINVAL);
+ if (!KSOCKET_VALID(ks))
+ return (ENOTSOCK);
+ so = KSTOSO(ks);
+
+ /*
+ * Check if there are any events already pending.
+ * If we're not willing to block, (timo == 0) then
+ * pass "anyyet">0 to socket_poll so it can skip
+ * some work. Othewise pass "anyyet"=0 and if
+ * there are no events pending, it will fill in
+ * the pollhead pointer we need for pollwakeup().
+ *
+ * XXX - pollrelock() logic needs to know which
+ * which pollcache lock to grab. It'd be a
+ * cleaner solution if we could pass pcp as
+ * an arguement in VOP_POLL interface instead
+ * of implicitly passing it using thread_t
+ * struct. On the other hand, changing VOP_POLL
+ * interface will require all driver/file system
+ * poll routine to change. May want to revisit
+ * the tradeoff later.
+ */
+ php = NULL;
+ *revents = 0;
+ pcp = pcache_alloc();
+ pcache_create(pcp, 1);
+
+ mutex_enter(&pcp->pc_lock);
+ curthread->t_pollcache = pcp;
+ error = socket_poll(so, (short)events, (timo == 0),
+ revents, &php);
+ curthread->t_pollcache = NULL;
+ mutex_exit(&pcp->pc_lock);
+
+ if (error != 0 || *revents != 0 || timo == 0)
+ goto out;
+
+ /*
+ * Need to block. Did not get *revents, so the
+ * php should be non-NULL, but let's verify.
+ * Also compute when our sleep expires.
+ */
+ if (php == NULL) {
+ error = EIO;
+ goto out;
+ }
+ if (timo > 0)
+ expires = ddi_get_lbolt() +
+ MSEC_TO_TICK_ROUNDUP(timo);
+
+ /*
+ * Setup: pollhead -> polldat -> pollcache
+ * needed for pollwakeup()
+ * pdp should be freed by pcache_destroy
+ */
+ pdp = kmem_zalloc(sizeof (*pdp), KM_SLEEP);
+ pdp->pd_fd = 0;
+ pdp->pd_events = events;
+ pdp->pd_pcache = pcp;
+ pcache_insert_fd(pcp, pdp, 1);
+ pollhead_insert(php, pdp);
+ pdp->pd_php = php;
+
+ mutex_enter(&pcp->pc_lock);
+ while (!(so->so_state & SS_CLOSING)) {
+ pcp->pc_flag = 0;
+
+ /* Ditto pcp comment above. */
+ curthread->t_pollcache = pcp;
+ error = socket_poll(so, (short)events, 0,
+ revents, &php2);
+ curthread->t_pollcache = NULL;
+ ASSERT(php2 == php);
+
+ if (error != 0 || *revents != 0)
+ break;
+
+ if (pcp->pc_flag & T_POLLWAKE)
+ continue;
+
+ if (timo == -1) {
+ rval = cv_wait_sig(&pcp->pc_cv, &pcp->pc_lock);
+ } else {
+ rval = cv_timedwait_sig(&pcp->pc_cv, &pcp->pc_lock,
+ expires);
+ }
+ if (rval <= 0) {
+ if (rval == 0)
+ error = EINTR;
+ break;
+ }
+ }
+ mutex_exit(&pcp->pc_lock);
+
+ if (pdp->pd_php != NULL) {
+ pollhead_delete(pdp->pd_php, pdp);
+ pdp->pd_php = NULL;
+ pdp->pd_fd = NULL;
+ }
+
+ /*
+ * pollwakeup() may still interact with this pollcache. Wait until
+ * it is done.
+ */
+ mutex_enter(&pcp->pc_no_exit);
+ ASSERT(pcp->pc_busy >= 0);
+ while (pcp->pc_busy > 0)
+ cv_wait(&pcp->pc_busy_cv, &pcp->pc_no_exit);
+ mutex_exit(&pcp->pc_no_exit);
+out:
+ pcache_destroy(pcp);
+ return (error);
+}
+
int
ksocket_sendmblk(ksocket_t ks, struct nmsghdr *msg, int flags,
mblk_t **mpp, cred_t *cr)
diff --git a/usr/src/uts/common/io/pcn/THIRDPARTYLICENSE b/usr/src/uts/common/io/pcn/THIRDPARTYLICENSE
new file mode 100644
index 0000000000..dfbdad4c0a
--- /dev/null
+++ b/usr/src/uts/common/io/pcn/THIRDPARTYLICENSE
@@ -0,0 +1,31 @@
+Copyright (c) 2000 Berkeley Software Design, Inc.
+Copyright (c) 1997, 1998, 1999, 2000
+ Bill Paul <wpaul@osd.bsdi.com>. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+ This product includes software developed by Bill Paul.
+4. Neither the name of the author nor the names of any co-contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/usr/src/uts/common/io/pcn/THIRDPARTYLICENSE.descrip b/usr/src/uts/common/io/pcn/THIRDPARTYLICENSE.descrip
new file mode 100644
index 0000000000..474eb11062
--- /dev/null
+++ b/usr/src/uts/common/io/pcn/THIRDPARTYLICENSE.descrip
@@ -0,0 +1 @@
+PCNET FAST ETHERNET DRIVER
diff --git a/usr/src/uts/common/io/pcn/pcn.c b/usr/src/uts/common/io/pcn/pcn.c
new file mode 100644
index 0000000000..1697787f7a
--- /dev/null
+++ b/usr/src/uts/common/io/pcn/pcn.c
@@ -0,0 +1,1827 @@
+/*
+ * Copyright (c) 2011 Jason King.
+ * Copyright (c) 2000 Berkeley Software Design, Inc.
+ * Copyright (c) 1997, 1998, 1999, 2000
+ * Bill Paul <wpaul@osd.bsdi.com>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/varargs.h>
+#include <sys/types.h>
+#include <sys/modctl.h>
+#include <sys/devops.h>
+#include <sys/stream.h>
+#include <sys/strsun.h>
+#include <sys/cmn_err.h>
+#include <sys/ethernet.h>
+#include <sys/kmem.h>
+#include <sys/crc32.h>
+#include <sys/mii.h>
+#include <sys/miiregs.h>
+#include <sys/mac.h>
+#include <sys/mac_ether.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/vlan.h>
+#include <sys/pci.h>
+#include <sys/conf.h>
+
+#include "pcn.h"
+#include "pcnimpl.h"
+
+#define ETHERVLANMTU (ETHERMAX + 4)
+
+#define CSR_WRITE_4(pcnp, reg, val) \
+ ddi_put32(pcnp->pcn_regshandle, (uint32_t *)(pcnp->pcn_regs + reg), val)
+
+#define CSR_WRITE_2(pcnp, reg, val) \
+ ddi_put16(pcnp->pcn_regshandle, (uint16_t *)(pcnp->pcn_regs + reg), val)
+
+#define CSR_READ_4(pcnp, reg) \
+ ddi_get32(pcnp->pcn_regshandle, (uint32_t *)(pcnp->pcn_regs + reg))
+
+#define CSR_READ_2(pcnp, reg) \
+ ddi_get16(pcnp->pcn_regshandle, (uint16_t *)(pcnp->pcn_regs + reg))
+
+#define PCN_CSR_SETBIT(pcnp, reg, x) \
+ pcn_csr_write(pcnp, reg, pcn_csr_read(pcnp, reg) | (x))
+
+#define PCN_CSR_CLRBIT(pcnp, reg, x) \
+ pcn_csr_write(pcnp, reg, pcn_csr_read(pcnp, reg) & ~(x))
+
+#define PCN_BCR_SETBIT(pncp, reg, x) \
+ pcn_bcr_write(pcnp, reg, pcn_bcr_read(pcnp, reg) | (x))
+
+#define PCN_BCR_CLRBIT(pcnp, reg, x) \
+ pcn_bcr_write(pcnp, reg, pcn_bcr_read(pcnp, reg) & ~(x))
+
+static int pcn_attach(dev_info_t *, ddi_attach_cmd_t);
+static int pcn_detach(dev_info_t *, ddi_detach_cmd_t);
+static int pcn_ddi_resume(dev_info_t *);
+static int pcn_quiesce(dev_info_t *);
+
+static void pcn_teardown(pcn_t *);
+
+static int pcn_m_unicast(void *, const uint8_t *);
+static int pcn_m_multicast(void *, boolean_t, const uint8_t *);
+static int pcn_m_promisc(void *, boolean_t);
+static mblk_t *pcn_m_tx(void *, mblk_t *);
+static void pcn_m_ioctl(void *, queue_t *, mblk_t *);
+static int pcn_m_stat(void *, uint_t, uint64_t *);
+static int pcn_m_start(void *);
+static void pcn_m_stop(void *);
+static int pcn_m_getprop(void *, const char *, mac_prop_id_t, uint_t,
+ void *);
+static int pcn_m_setprop(void *, const char *, mac_prop_id_t, uint_t,
+ const void *);
+static void pcn_m_propinfo(void *, const char *, mac_prop_id_t,
+ mac_prop_info_handle_t);
+static int pcn_watchdog(pcn_t *);
+
+static unsigned pcn_intr(caddr_t);
+
+static uint16_t pcn_mii_read(void *, uint8_t, uint8_t);
+static void pcn_mii_write(void *, uint8_t, uint8_t, uint16_t);
+static void pcn_mii_notify(void *, link_state_t);
+
+static uint32_t pcn_csr_read(pcn_t *, uint32_t);
+static uint16_t pcn_csr_read16(pcn_t *, uint32_t);
+static void pcn_csr_write(pcn_t *, uint32_t, uint32_t);
+
+static uint32_t pcn_bcr_read(pcn_t *, uint32_t);
+static uint16_t pcn_bcr_read16(pcn_t *, uint32_t);
+static void pcn_bcr_write(pcn_t *, uint32_t, uint32_t);
+
+static boolean_t pcn_send(pcn_t *, mblk_t *);
+
+static pcn_buf_t *pcn_allocbuf(pcn_t *);
+static void pcn_destroybuf(pcn_buf_t *);
+static int pcn_allocrxring(pcn_t *);
+static int pcn_alloctxring(pcn_t *);
+static void pcn_freetxring(pcn_t *);
+static void pcn_freerxring(pcn_t *);
+static void pcn_resetrings(pcn_t *);
+static int pcn_initialize(pcn_t *, boolean_t);
+static mblk_t *pcn_receive(pcn_t *);
+static void pcn_resetall(pcn_t *);
+static void pcn_startall(pcn_t *);
+static void pcn_stopall(pcn_t *);
+static void pcn_reclaim(pcn_t *);
+static void pcn_getfactaddr(pcn_t *);
+static int pcn_set_chipid(pcn_t *, uint32_t);
+static const pcn_type_t *pcn_match(uint16_t, uint16_t);
+static void pcn_start_timer(pcn_t *);
+static void pcn_stop_timer(pcn_t *);
+
+static void pcn_error(dev_info_t *, char *, ...);
+
+void *pcn_ssp = NULL;
+
+static uchar_t pcn_broadcast[ETHERADDRL] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+static const pcn_type_t pcn_devs[] = {
+ { PCN_VENDORID, PCN_DEVICEID_PCNET, "AMD PCnet/PCI 10/100BaseTX" },
+ { PCN_VENDORID, PCN_DEVICEID_HOME, "AMD PCnet/Home HomePNA" },
+ { 0, 0, NULL }
+};
+
+static mii_ops_t pcn_mii_ops = {
+ MII_OPS_VERSION,
+ pcn_mii_read,
+ pcn_mii_write,
+ pcn_mii_notify,
+ NULL
+};
+
+static mac_callbacks_t pcn_m_callbacks = {
+ MC_IOCTL | MC_SETPROP | MC_GETPROP | MC_PROPINFO,
+ pcn_m_stat,
+ pcn_m_start,
+ pcn_m_stop,
+ pcn_m_promisc,
+ pcn_m_multicast,
+ pcn_m_unicast,
+ pcn_m_tx,
+ NULL,
+ pcn_m_ioctl,
+ NULL, /* mc_getcapab */
+ NULL, /* mc_open */
+ NULL, /* mc_close */
+ pcn_m_setprop,
+ pcn_m_getprop,
+ pcn_m_propinfo
+};
+
+DDI_DEFINE_STREAM_OPS(pcn_devops, nulldev, nulldev, pcn_attach, pcn_detach,
+ nodev, NULL, D_MP, NULL, pcn_quiesce);
+
+static struct modldrv pcn_modldrv = {
+ &mod_driverops,
+ "AMD PCnet",
+ &pcn_devops
+};
+
+static struct modlinkage pcn_modlinkage = {
+ MODREV_1,
+ { &pcn_modldrv, NULL }
+};
+
+static ddi_device_acc_attr_t pcn_devattr = {
+ DDI_DEVICE_ATTR_V0,
+ DDI_STRUCTURE_LE_ACC,
+ DDI_STRICTORDER_ACC
+};
+
+static ddi_device_acc_attr_t pcn_bufattr = {
+ DDI_DEVICE_ATTR_V0,
+ DDI_NEVERSWAP_ACC,
+ DDI_STRICTORDER_ACC
+};
+
+static ddi_dma_attr_t pcn_dma_attr = {
+ DMA_ATTR_V0, /* dm_attr_version */
+ 0, /* dma_attr_addr_lo */
+ 0xFFFFFFFFU, /* dma_attr_addr_hi */
+ 0x7FFFFFFFU, /* dma_attr_count_max */
+ 4, /* dma_attr_align */
+ 0x3F, /* dma_attr_burstsizes */
+ 1, /* dma_attr_minxfer */
+ 0xFFFFFFFFU, /* dma_attr_maxxfer */
+ 0xFFFFFFFFU, /* dma_attr_seg */
+ 1, /* dma_attr_sgllen */
+ 1, /* dma_attr_granular */
+ 0 /* dma_attr_flags */
+};
+
+static ddi_dma_attr_t pcn_dmadesc_attr = {
+ DMA_ATTR_V0, /* dm_attr_version */
+ 0, /* dma_attr_addr_lo */
+ 0xFFFFFFFFU, /* dma_attr_addr_hi */
+ 0x7FFFFFFFU, /* dma_attr_count_max */
+ 16, /* dma_attr_align */
+ 0x3F, /* dma_attr_burstsizes */
+ 1, /* dma_attr_minxfer */
+ 0xFFFFFFFFU, /* dma_attr_maxxfer */
+ 0xFFFFFFFFU, /* dma_attr_seg */
+ 1, /* dma_attr_sgllen */
+ 1, /* dma_attr_granular */
+ 0 /* dma_attr_flags */
+};
+
+/*
+ * DDI entry points
+ */
+int
+_init(void)
+{
+ int rc;
+
+ if ((rc = ddi_soft_state_init(&pcn_ssp, sizeof (pcn_t), 1)) != 0)
+ return (rc);
+
+ mac_init_ops(&pcn_devops, "pcn");
+ if ((rc = mod_install(&pcn_modlinkage)) != DDI_SUCCESS) {
+ mac_fini_ops(&pcn_devops);
+ ddi_soft_state_fini(&pcn_ssp);
+ }
+ return (rc);
+}
+
+int
+_fini(void)
+{
+ int rc;
+
+ if ((rc = mod_remove(&pcn_modlinkage)) == DDI_SUCCESS) {
+ mac_fini_ops(&pcn_devops);
+ ddi_soft_state_fini(&pcn_ssp);
+ }
+ return (rc);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&pcn_modlinkage, modinfop));
+}
+
+int
+pcn_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ pcn_t *pcnp;
+ mac_register_t *macp;
+ const pcn_type_t *pcn_type;
+ int instance = ddi_get_instance(dip);
+ int rc;
+ ddi_acc_handle_t pci;
+ uint16_t venid;
+ uint16_t devid;
+ uint16_t svid;
+ uint16_t ssid;
+
+ switch (cmd) {
+ case DDI_RESUME:
+ return (pcn_ddi_resume(dip));
+
+ case DDI_ATTACH:
+ break;
+
+ default:
+ return (DDI_FAILURE);
+ }
+
+ if (ddi_slaveonly(dip) == DDI_SUCCESS) {
+ pcn_error(dip, "slot does not support PCI bus-master");
+ return (DDI_FAILURE);
+ }
+
+ if (ddi_intr_hilevel(dip, 0) != 0) {
+ pcn_error(dip, "hilevel interrupts not supported");
+ return (DDI_FAILURE);
+ }
+
+ if (pci_config_setup(dip, &pci) != DDI_SUCCESS) {
+ pcn_error(dip, "unable to setup PCI config handle");
+ return (DDI_FAILURE);
+ }
+
+ venid = pci_config_get16(pci, PCI_CONF_VENID);
+ devid = pci_config_get16(pci, PCI_CONF_DEVID);
+ svid = pci_config_get16(pci, PCI_CONF_SUBVENID);
+ ssid = pci_config_get16(pci, PCI_CONF_SUBSYSID);
+
+ if ((pcn_type = pcn_match(venid, devid)) == NULL) {
+ pci_config_teardown(&pci);
+ pcn_error(dip, "Unable to identify PCI card");
+ return (DDI_FAILURE);
+ }
+
+ if (ddi_prop_update_string(DDI_DEV_T_NONE, dip, "model",
+ pcn_type->pcn_name) != DDI_PROP_SUCCESS) {
+ pci_config_teardown(&pci);
+ pcn_error(dip, "Unable to create model property");
+ return (DDI_FAILURE);
+ }
+
+ if (ddi_soft_state_zalloc(pcn_ssp, instance) != DDI_SUCCESS) {
+ pcn_error(dip, "Unable to allocate soft state");
+ pci_config_teardown(&pci);
+ return (DDI_FAILURE);
+ }
+
+ pcnp = ddi_get_soft_state(pcn_ssp, instance);
+ pcnp->pcn_dip = dip;
+ pcnp->pcn_instance = instance;
+ pcnp->pcn_extphyaddr = -1;
+
+ if (ddi_get_iblock_cookie(dip, 0, &pcnp->pcn_icookie) != DDI_SUCCESS) {
+ pcn_error(pcnp->pcn_dip, "ddi_get_iblock_cookie failed");
+ ddi_soft_state_free(pcn_ssp, instance);
+ pci_config_teardown(&pci);
+ return (DDI_FAILURE);
+ }
+
+
+ mutex_init(&pcnp->pcn_xmtlock, NULL, MUTEX_DRIVER, pcnp->pcn_icookie);
+ mutex_init(&pcnp->pcn_intrlock, NULL, MUTEX_DRIVER, pcnp->pcn_icookie);
+ mutex_init(&pcnp->pcn_reglock, NULL, MUTEX_DRIVER, pcnp->pcn_icookie);
+
+ /*
+ * Enable bus master, IO space, and memory space accesses
+ */
+ pci_config_put16(pci, PCI_CONF_COMM,
+ pci_config_get16(pci, PCI_CONF_COMM) | PCI_COMM_ME | PCI_COMM_MAE);
+
+ pci_config_teardown(&pci);
+
+ if (ddi_regs_map_setup(dip, 1, (caddr_t *)&pcnp->pcn_regs, 0, 0,
+ &pcn_devattr, &pcnp->pcn_regshandle)) {
+ pcn_error(dip, "ddi_regs_map_setup failed");
+ goto fail;
+ }
+
+ if (pcn_set_chipid(pcnp, (uint32_t)ssid << 16 | (uint32_t)svid) !=
+ DDI_SUCCESS) {
+ goto fail;
+ }
+
+ if ((pcnp->pcn_mii = mii_alloc(pcnp, dip, &pcn_mii_ops)) == NULL)
+ goto fail;
+
+ /* XXX: need to set based on device */
+ mii_set_pauseable(pcnp->pcn_mii, B_FALSE, B_FALSE);
+
+ if ((pcn_allocrxring(pcnp) != DDI_SUCCESS) ||
+ (pcn_alloctxring(pcnp) != DDI_SUCCESS)) {
+ pcn_error(dip, "unable to allocate DMA resources");
+ goto fail;
+ }
+
+ pcnp->pcn_promisc = B_FALSE;
+
+ mutex_enter(&pcnp->pcn_intrlock);
+ mutex_enter(&pcnp->pcn_xmtlock);
+ rc = pcn_initialize(pcnp, B_TRUE);
+ mutex_exit(&pcnp->pcn_xmtlock);
+ mutex_exit(&pcnp->pcn_intrlock);
+ if (rc != DDI_SUCCESS)
+ goto fail;
+
+ if (ddi_add_intr(dip, 0, NULL, NULL, pcn_intr, (caddr_t)pcnp) !=
+ DDI_SUCCESS) {
+ pcn_error(dip, "unable to add interrupt");
+ goto fail;
+ }
+
+ pcnp->pcn_flags |= PCN_INTR_ENABLED;
+
+ if ((macp = mac_alloc(MAC_VERSION)) == NULL) {
+ pcn_error(pcnp->pcn_dip, "mac_alloc failed");
+ goto fail;
+ }
+
+ macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
+ macp->m_driver = pcnp;
+ macp->m_dip = dip;
+ macp->m_src_addr = pcnp->pcn_addr;
+ macp->m_callbacks = &pcn_m_callbacks;
+ macp->m_min_sdu = 0;
+ macp->m_max_sdu = ETHERMTU;
+ macp->m_margin = VLAN_TAGSZ;
+
+ if (mac_register(macp, &pcnp->pcn_mh) == DDI_SUCCESS) {
+ mac_free(macp);
+ return (DDI_SUCCESS);
+ }
+
+ mac_free(macp);
+
+ return (DDI_SUCCESS);
+
+fail:
+ pcn_teardown(pcnp);
+ return (DDI_FAILURE);
+}
+
+int
+pcn_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ pcn_t *pcnp;
+
+ pcnp = ddi_get_soft_state(pcn_ssp, ddi_get_instance(dip));
+
+ if (pcnp == NULL) {
+ pcn_error(dip, "no soft state in detach!");
+ return (DDI_FAILURE);
+ }
+
+ switch (cmd) {
+ case DDI_DETACH:
+ if (mac_unregister(pcnp->pcn_mh) != 0)
+ return (DDI_FAILURE);
+
+ mutex_enter(&pcnp->pcn_intrlock);
+ mutex_enter(&pcnp->pcn_xmtlock);
+ pcnp->pcn_flags &= ~PCN_RUNNING;
+ pcn_stopall(pcnp);
+ mutex_exit(&pcnp->pcn_xmtlock);
+ mutex_exit(&pcnp->pcn_intrlock);
+
+ pcn_teardown(pcnp);
+ return (DDI_SUCCESS);
+
+ case DDI_SUSPEND:
+ mii_suspend(pcnp->pcn_mii);
+
+ mutex_enter(&pcnp->pcn_intrlock);
+ mutex_enter(&pcnp->pcn_xmtlock);
+ pcnp->pcn_flags |= PCN_SUSPENDED;
+ pcn_stopall(pcnp);
+ mutex_exit(&pcnp->pcn_xmtlock);
+ mutex_exit(&pcnp->pcn_intrlock);
+ return (DDI_SUCCESS);
+
+ default:
+ return (DDI_FAILURE);
+ }
+}
+
+int
+pcn_ddi_resume(dev_info_t *dip)
+{
+ pcn_t *pcnp;
+
+ if ((pcnp = ddi_get_soft_state(pcn_ssp, ddi_get_instance(dip))) == NULL)
+ return (DDI_FAILURE);
+
+ mutex_enter(&pcnp->pcn_intrlock);
+ mutex_enter(&pcnp->pcn_xmtlock);
+
+ pcnp->pcn_flags &= ~PCN_SUSPENDED;
+
+ if (!pcn_initialize(pcnp, B_FALSE)) {
+ pcn_error(pcnp->pcn_dip, "unable to resume chip");
+ pcnp->pcn_flags |= PCN_SUSPENDED;
+ mutex_exit(&pcnp->pcn_intrlock);
+ mutex_exit(&pcnp->pcn_xmtlock);
+ return (DDI_SUCCESS);
+ }
+
+ if (IS_RUNNING(pcnp))
+ pcn_startall(pcnp);
+
+ mutex_exit(&pcnp->pcn_xmtlock);
+ mutex_exit(&pcnp->pcn_intrlock);
+
+ mii_resume(pcnp->pcn_mii);
+
+ return (DDI_SUCCESS);
+}
+
+int
+pcn_quiesce(dev_info_t *dip)
+{
+ pcn_t *pcnp;
+
+ if ((pcnp = ddi_get_soft_state(pcn_ssp, ddi_get_instance(dip))) == NULL)
+ return (DDI_FAILURE);
+
+ /* don't want to take the chance of blocking */
+ CSR_WRITE_4(pcnp, PCN_IO32_RAP, PCN_CSR_EXTCTL1);
+ CSR_WRITE_4(pcnp, PCN_IO32_RDP, CSR_READ_4(pcnp, PCN_IO32_RDP) &
+ ~(PCN_EXTCTL1_SINTEN));
+
+ CSR_WRITE_4(pcnp, PCN_IO32_RAP, PCN_CSR_CSR);
+ CSR_WRITE_4(pcnp, PCN_IO32_RDP,
+ (CSR_READ_4(pcnp, PCN_IO32_RDP) & ~(PCN_CSR_INTEN)) |
+ PCN_CSR_STOP);
+
+ return (DDI_SUCCESS);
+}
+
+static void
+pcn_teardown(pcn_t *pcnp)
+{
+ ASSERT(!(pcnp->pcn_flags & PCN_RUNNING));
+
+ if (pcnp->pcn_mii != NULL) {
+ mii_free(pcnp->pcn_mii);
+ pcnp->pcn_mii = NULL;
+ }
+
+ if (pcnp->pcn_flags & PCN_INTR_ENABLED)
+ ddi_remove_intr(pcnp->pcn_dip, 0, pcnp->pcn_icookie);
+
+ /* These will exit gracefully if not yet allocated */
+ pcn_freerxring(pcnp);
+ pcn_freetxring(pcnp);
+
+ if (pcnp->pcn_regshandle != NULL)
+ ddi_regs_map_free(&pcnp->pcn_regshandle);
+
+
+ mutex_destroy(&pcnp->pcn_xmtlock);
+ mutex_destroy(&pcnp->pcn_intrlock);
+ mutex_destroy(&pcnp->pcn_reglock);
+
+ ddi_soft_state_free(pcn_ssp, ddi_get_instance(pcnp->pcn_dip));
+}
+
+/*
+ * Drains any FIFOs in the card, then pauses it
+ */
+static void
+pcn_suspend(pcn_t *pcnp)
+{
+ uint32_t val;
+ int i;
+
+ PCN_CSR_SETBIT(pcnp, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SPND);
+ for (i = 0; i < 5000; i++) {
+ if ((val = pcn_csr_read(pcnp, PCN_CSR_EXTCTL1)) &
+ PCN_EXTCTL1_SPND)
+ return;
+ drv_usecwait(1000);
+ }
+
+ pcn_error(pcnp->pcn_dip, "Unable to suspend, EXTCTL1 was 0x%b", val,
+ PCN_EXTCTL1_STR);
+}
+
+static void
+pcn_resume(pcn_t *pcnp)
+{
+ PCN_CSR_CLRBIT(pcnp, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SPND);
+}
+
+static int
+pcn_m_multicast(void *arg, boolean_t add, const uint8_t *macaddr)
+{
+ pcn_t *pcnp = (pcn_t *)arg;
+ int index;
+ uint32_t crc;
+ uint16_t bit;
+ uint16_t newval, oldval;
+
+ /*
+ * PCNet uses the upper 6 bits of the CRC of the macaddr
+ * to index into a 64bit mask
+ */
+ CRC32(crc, macaddr, ETHERADDRL, -1U, crc32_table);
+ crc >>= 26;
+ index = crc / 16;
+ bit = (1U << (crc % 16));
+
+ mutex_enter(&pcnp->pcn_intrlock);
+ mutex_enter(&pcnp->pcn_xmtlock);
+ newval = oldval = pcnp->pcn_mctab[index];
+
+ if (add) {
+ pcnp->pcn_mccount[crc]++;
+ if (pcnp->pcn_mccount[crc] == 1)
+ newval |= bit;
+ } else {
+ pcnp->pcn_mccount[crc]--;
+ if (pcnp->pcn_mccount[crc] == 0)
+ newval &= ~bit;
+ }
+ if (newval != oldval) {
+ pcnp->pcn_mctab[index] = newval;
+ pcn_suspend(pcnp);
+ pcn_csr_write(pcnp, PCN_CSR_MAR0 + index, newval);
+ pcn_resume(pcnp);
+ }
+
+ mutex_exit(&pcnp->pcn_xmtlock);
+ mutex_exit(&pcnp->pcn_intrlock);
+
+ return (0);
+}
+
+static int
+pcn_m_promisc(void *arg, boolean_t on)
+{
+ pcn_t *pcnp = (pcn_t *)arg;
+
+ mutex_enter(&pcnp->pcn_intrlock);
+ mutex_enter(&pcnp->pcn_xmtlock);
+
+ pcnp->pcn_promisc = on;
+
+ if (IS_RUNNING(pcnp))
+ pcn_suspend(pcnp);
+
+ /* set promiscuous mode */
+ if (pcnp->pcn_promisc)
+ PCN_CSR_SETBIT(pcnp, PCN_CSR_MODE, PCN_MODE_PROMISC);
+ else
+ PCN_CSR_CLRBIT(pcnp, PCN_CSR_MODE, PCN_MODE_PROMISC);
+
+ if (IS_RUNNING(pcnp))
+ pcn_resume(pcnp);
+
+ mutex_exit(&pcnp->pcn_xmtlock);
+ mutex_exit(&pcnp->pcn_intrlock);
+
+ return (0);
+}
+
+static int
+pcn_m_unicast(void *arg, const uint8_t *macaddr)
+{
+ pcn_t *pcnp = (pcn_t *)arg;
+ int i;
+ uint16_t addr[3];
+
+ bcopy(macaddr, addr, sizeof (addr));
+
+ mutex_enter(&pcnp->pcn_intrlock);
+ mutex_enter(&pcnp->pcn_xmtlock);
+
+ if (IS_RUNNING(pcnp))
+ pcn_suspend(pcnp);
+
+ for (i = 0; i < 3; i++)
+ pcn_csr_write(pcnp, PCN_CSR_PAR0 + i, addr[i]);
+
+ bcopy(macaddr, pcnp->pcn_addr, ETHERADDRL);
+
+ if (IS_RUNNING(pcnp))
+ pcn_resume(pcnp);
+
+ mutex_exit(&pcnp->pcn_xmtlock);
+ mutex_exit(&pcnp->pcn_intrlock);
+
+ return (0);
+}
+
+static mblk_t *
+pcn_m_tx(void *arg, mblk_t *mp)
+{
+ pcn_t *pcnp = (pcn_t *)arg;
+ mblk_t *nmp;
+
+ mutex_enter(&pcnp->pcn_xmtlock);
+
+ if (pcnp->pcn_flags & PCN_SUSPENDED) {
+ while ((nmp = mp) != NULL) {
+ pcnp->pcn_carrier_errors++;
+ mp = mp->b_next;
+ freemsg(nmp);
+ }
+ mutex_exit(&pcnp->pcn_xmtlock);
+ return (NULL);
+ }
+
+ while (mp != NULL) {
+ nmp = mp->b_next;
+ mp->b_next = NULL;
+
+ if (!pcn_send(pcnp, mp)) {
+ mp->b_next = nmp;
+ break;
+ }
+ mp = nmp;
+ }
+ mutex_exit(&pcnp->pcn_xmtlock);
+
+ return (mp);
+}
+
+static boolean_t
+pcn_send(pcn_t *pcnp, mblk_t *mp)
+{
+ size_t len;
+ pcn_buf_t *txb;
+ pcn_tx_desc_t *tmd;
+ int txsend;
+
+ ASSERT(mutex_owned(&pcnp->pcn_xmtlock));
+ ASSERT(mp != NULL);
+
+ len = msgsize(mp);
+ if (len > ETHERVLANMTU) {
+ pcnp->pcn_macxmt_errors++;
+ freemsg(mp);
+ return (B_TRUE);
+ }
+
+ if (pcnp->pcn_txavail < PCN_TXRECLAIM)
+ pcn_reclaim(pcnp);
+
+ if (pcnp->pcn_txavail == 0) {
+ pcnp->pcn_wantw = B_TRUE;
+
+ /* enable tx interrupt */
+ PCN_CSR_SETBIT(pcnp, PCN_CSR_EXTCTL1, PCN_EXTCTL1_LTINTEN);
+ return (B_FALSE);
+ }
+
+ txsend = pcnp->pcn_txsend;
+
+ /*
+ * We copy the packet to a single buffer. NetBSD sources suggest
+ * that if multiple segements are ever used, VMware has a bug that will
+ * only allow 8 segments to be used, while the physical chips allow 16
+ */
+ txb = pcnp->pcn_txbufs[txsend];
+ mcopymsg(mp, txb->pb_buf); /* frees mp! */
+
+ pcnp->pcn_opackets++;
+ pcnp->pcn_obytes += len;
+ if (txb->pb_buf[0] & 0x1) {
+ if (bcmp(txb->pb_buf, pcn_broadcast, ETHERADDRL) != 0)
+ pcnp->pcn_multixmt++;
+ else
+ pcnp->pcn_brdcstxmt++;
+ }
+
+ tmd = &pcnp->pcn_txdescp[txsend];
+
+ SYNCBUF(txb, len, DDI_DMA_SYNC_FORDEV);
+ tmd->pcn_txstat = 0;
+ tmd->pcn_tbaddr = txb->pb_paddr;
+
+ /* PCNet wants the 2's complement of the length of the buffer */
+ tmd->pcn_txctl = (~(len) + 1) & PCN_TXCTL_BUFSZ;
+ tmd->pcn_txctl |= PCN_TXCTL_MBO;
+ tmd->pcn_txctl |= PCN_TXCTL_STP | PCN_TXCTL_ENP | PCN_TXCTL_ADD_FCS |
+ PCN_TXCTL_OWN | PCN_TXCTL_MORE_LTINT;
+
+ SYNCTXDESC(pcnp, txsend, DDI_DMA_SYNC_FORDEV);
+
+ pcnp->pcn_txavail--;
+ pcnp->pcn_txsend = (txsend + 1) % PCN_TXRING;
+ pcnp->pcn_txstall_time = gethrtime() + (5 * 1000000000ULL);
+
+ pcn_csr_write(pcnp, PCN_CSR_CSR, PCN_CSR_TX|PCN_CSR_INTEN);
+
+ return (B_TRUE);
+}
+
+static void
+pcn_reclaim(pcn_t *pcnp)
+{
+ pcn_tx_desc_t *tmdp;
+
+ while (pcnp->pcn_txavail != PCN_TXRING) {
+ int index = pcnp->pcn_txreclaim;
+
+ tmdp = &pcnp->pcn_txdescp[index];
+
+ /* sync before reading */
+ SYNCTXDESC(pcnp, index, DDI_DMA_SYNC_FORKERNEL);
+
+ /* check if chip is still working on it */
+ if (tmdp->pcn_txctl & PCN_TXCTL_OWN)
+ break;
+
+ pcnp->pcn_txavail++;
+ pcnp->pcn_txreclaim = (index + 1) % PCN_TXRING;
+ }
+
+ if (pcnp->pcn_txavail >= PCN_TXRESCHED) {
+ if (pcnp->pcn_wantw) {
+ pcnp->pcn_wantw = B_FALSE;
+
+ /* Disable TX interrupt */
+ PCN_CSR_CLRBIT(pcnp, PCN_CSR_EXTCTL1,
+ PCN_EXTCTL1_LTINTEN);
+
+ mac_tx_update(pcnp->pcn_mh);
+ }
+ }
+}
+
+static unsigned
+pcn_intr(caddr_t arg1)
+{
+ pcn_t *pcnp = (void *)arg1;
+ mblk_t *mp = NULL;
+ uint32_t status, status2;
+ boolean_t do_reset = B_FALSE;
+
+ mutex_enter(&pcnp->pcn_intrlock);
+
+ if (IS_SUSPENDED(pcnp)) {
+ mutex_exit(&pcnp->pcn_intrlock);
+ return (DDI_INTR_UNCLAIMED);
+ }
+
+ while ((status = pcn_csr_read(pcnp, PCN_CSR_CSR)) & PCN_CSR_INTR) {
+ pcn_csr_write(pcnp, PCN_CSR_CSR, status);
+
+ status2 = pcn_csr_read(pcnp, PCN_CSR_EXTCTL2);
+
+ if (status & PCN_CSR_TINT) {
+ mutex_enter(&pcnp->pcn_xmtlock);
+ pcn_reclaim(pcnp);
+ mutex_exit(&pcnp->pcn_xmtlock);
+ }
+
+ if (status & PCN_CSR_RINT)
+ mp = pcn_receive(pcnp);
+
+ if (status & PCN_CSR_ERR) {
+ do_reset = B_TRUE;
+ break;
+ }
+
+ /* timer interrupt */
+ if (status2 & PCN_EXTCTL2_STINT) {
+ /* ack it */
+ PCN_CSR_SETBIT(pcnp, PCN_CSR_EXTCTL2,
+ PCN_EXTCTL2_STINT);
+
+ if (pcn_watchdog(pcnp) != DDI_SUCCESS) {
+ do_reset = B_TRUE;
+ break;
+ }
+ }
+ }
+
+ if (do_reset) {
+ mutex_enter(&pcnp->pcn_xmtlock);
+ pcn_resetall(pcnp);
+ mutex_exit(&pcnp->pcn_xmtlock);
+ mutex_exit(&pcnp->pcn_intrlock);
+
+ mii_reset(pcnp->pcn_mii);
+ } else {
+ mutex_exit(&pcnp->pcn_intrlock);
+ }
+
+ if (mp)
+ mac_rx(pcnp->pcn_mh, NULL, mp);
+
+ return (DDI_INTR_CLAIMED);
+}
+
+static mblk_t *
+pcn_receive(pcn_t *pcnp)
+{
+ uint32_t len;
+ pcn_buf_t *rxb;
+ pcn_rx_desc_t *rmd;
+ mblk_t *mpchain, **mpp, *mp;
+ int head, cnt;
+
+ mpchain = NULL;
+ mpp = &mpchain;
+ head = pcnp->pcn_rxhead;
+
+ for (cnt = 0; cnt < PCN_RXRING; cnt++) {
+ rmd = &pcnp->pcn_rxdescp[head];
+ rxb = pcnp->pcn_rxbufs[head];
+
+ SYNCRXDESC(pcnp, head, DDI_DMA_SYNC_FORKERNEL);
+ if (rmd->pcn_rxstat & PCN_RXSTAT_OWN)
+ break;
+
+ len = rmd->pcn_rxlen - ETHERFCSL;
+
+ if (rmd->pcn_rxstat & PCN_RXSTAT_ERR) {
+ pcnp->pcn_errrcv++;
+
+ if (rmd->pcn_rxstat & PCN_RXSTAT_FRAM)
+ pcnp->pcn_align_errors++;
+ if (rmd->pcn_rxstat & PCN_RXSTAT_OFLOW)
+ pcnp->pcn_overflow++;
+ if (rmd->pcn_rxstat & PCN_RXSTAT_CRC)
+ pcnp->pcn_fcs_errors++;
+ } else if (len > ETHERVLANMTU) {
+ pcnp->pcn_errrcv++;
+ pcnp->pcn_toolong_errors++;
+ } else {
+ mp = allocb(len + PCN_HEADROOM, 0);
+ if (mp == NULL) {
+ pcnp->pcn_errrcv++;
+ pcnp->pcn_norcvbuf++;
+ goto skip;
+ }
+
+ SYNCBUF(rxb, len, DDI_DMA_SYNC_FORKERNEL);
+ mp->b_rptr += PCN_HEADROOM;
+ mp->b_wptr = mp->b_rptr + len;
+ bcopy((char *)rxb->pb_buf, mp->b_rptr, len);
+
+ pcnp->pcn_ipackets++;
+ pcnp->pcn_rbytes++;
+
+ if (rmd->pcn_rxstat & PCN_RXSTAT_LAFM|PCN_RXSTAT_BAM) {
+ if (rmd->pcn_rxstat & PCN_RXSTAT_BAM)
+ pcnp->pcn_brdcstrcv++;
+ else
+ pcnp->pcn_multircv++;
+ }
+ *mpp = mp;
+ mpp = &mp->b_next;
+ }
+
+skip:
+ rmd->pcn_rxstat = PCN_RXSTAT_OWN;
+ SYNCRXDESC(pcnp, head, DDI_DMA_SYNC_FORDEV);
+
+ head = (head + 1) % PCN_RXRING;
+ }
+
+ pcnp->pcn_rxhead = head;
+ return (mpchain);
+}
+
+static void
+pcn_m_ioctl(void *arg, queue_t *wq, mblk_t *mp)
+{
+ pcn_t *pcnp = (pcn_t *)arg;
+
+ if (mii_m_loop_ioctl(pcnp->pcn_mii, wq, mp))
+ return;
+
+ miocnak(wq, mp, 0, EINVAL);
+}
+
+static int
+pcn_m_start(void *arg)
+{
+ pcn_t *pcnp = (pcn_t *)arg;
+
+ mutex_enter(&pcnp->pcn_intrlock);
+ mutex_enter(&pcnp->pcn_xmtlock);
+
+ pcn_startall(pcnp);
+ pcnp->pcn_flags |= PCN_RUNNING;
+
+ mutex_exit(&pcnp->pcn_xmtlock);
+ mutex_exit(&pcnp->pcn_intrlock);
+
+ mii_start(pcnp->pcn_mii);
+
+ return (0);
+}
+
+static void
+pcn_m_stop(void *arg)
+{
+ pcn_t *pcnp = (pcn_t *)arg;
+
+ mii_stop(pcnp->pcn_mii);
+
+ mutex_enter(&pcnp->pcn_intrlock);
+ mutex_enter(&pcnp->pcn_xmtlock);
+
+ pcn_stopall(pcnp);
+ pcnp->pcn_flags &= ~PCN_RUNNING;
+
+ mutex_exit(&pcnp->pcn_xmtlock);
+ mutex_exit(&pcnp->pcn_intrlock);
+}
+
+static int
+pcn_initialize(pcn_t *pcnp, boolean_t getfact)
+{
+ int i;
+ uint16_t addr[3];
+
+ bcopy(pcnp->pcn_addr, addr, sizeof (addr));
+
+ /*
+ * Issue a reset by reading from the RESET register.
+ * Note that we don't know if the chip is operating in
+ * 16-bit or 32-bit mode at this point, so we attempt
+ * to reset the chip both ways. If one fails, the other
+ * will succeed.
+ */
+ (void) CSR_READ_2(pcnp, PCN_IO16_RESET);
+ (void) CSR_READ_4(pcnp, PCN_IO32_RESET);
+
+ drv_usecwait(1000);
+
+ /* Select 32-bit (DWIO) mode */
+ CSR_WRITE_4(pcnp, PCN_IO32_RDP, 0);
+
+ /* The timer is not affected by a reset, so explicitly disable */
+ pcn_stop_timer(pcnp);
+
+ /* Enable fast suspend */
+ pcn_csr_write(pcnp, PCN_CSR_EXTCTL2, PCN_EXTCTL2_FASTSPNDE);
+
+ /* Select Style 3 descriptors */
+ pcn_bcr_write(pcnp, PCN_BCR_SSTYLE, PCN_SWSTYLE_PCNETPCI);
+
+ /* Set MAC address */
+ if (getfact)
+ pcn_getfactaddr(pcnp);
+
+ pcn_csr_write(pcnp, PCN_CSR_PAR0, addr[0]);
+ pcn_csr_write(pcnp, PCN_CSR_PAR1, addr[1]);
+ pcn_csr_write(pcnp, PCN_CSR_PAR2, addr[2]);
+
+ /* Clear PCN_MISC_ASEL so we can set the port via PCN_CSR_MODE. */
+ PCN_BCR_CLRBIT(pcnp, PCN_BCR_MISCCFG, PCN_MISC_ASEL);
+
+ /*
+ * XXX: need to find a way to determine when 10bt media is
+ * selected for non Am79C978, and set to PCN_PORT_10BASET
+ * instead of PCN_PORT_MII
+ */
+ pcn_csr_write(pcnp, PCN_CSR_MODE, PCN_PORT_MII);
+
+ /* Reenable auto negotiation for external phy */
+ PCN_BCR_SETBIT(pcnp, PCN_BCR_MIICTL, PCN_MIICTL_XPHYANE);
+
+ if (pcnp->pcn_promisc)
+ PCN_CSR_SETBIT(pcnp, PCN_CSR_MODE, PCN_MODE_PROMISC);
+
+ /* Initalize mcast addr filter */
+ for (i = 0; i < 4; i++)
+ pcn_csr_write(pcnp, PCN_CSR_MAR0 + i, pcnp->pcn_mctab[i]);
+
+ pcn_resetrings(pcnp);
+
+ /* We're not using the initialization block. */
+ pcn_csr_write(pcnp, PCN_CSR_IAB1, 0);
+
+ /*
+ * Enable burst read and write. Also set the no underflow
+ * bit. This will avoid transmit underruns in ceratin
+ * conditions while still providing decent performance.
+ */
+ PCN_BCR_SETBIT(pcnp, PCN_BCR_BUSCTL, PCN_BUSCTL_NOUFLOW |
+ PCN_BUSCTL_BREAD | PCN_BUSCTL_BWRITE);
+
+ /* Enable graceful recovery from underflow. */
+ PCN_CSR_SETBIT(pcnp, PCN_CSR_IMR, PCN_IMR_DXSUFLO);
+
+ /* Enable auto-padding of short TX frames. */
+ PCN_CSR_SETBIT(pcnp, PCN_CSR_TFEAT, PCN_TFEAT_PAD_TX);
+
+ if (pcnp->pcn_type == Am79C978)
+ pcn_bcr_write(pcnp, PCN_BCR_PHYSEL,
+ PCN_PHYSEL_PCNET|PCN_PHY_HOMEPNA);
+
+ return (DDI_SUCCESS);
+}
+
+static void
+pcn_resetall(pcn_t *pcnp)
+{
+ pcn_stopall(pcnp);
+ pcn_startall(pcnp);
+}
+
+static void
+pcn_startall(pcn_t *pcnp)
+{
+ ASSERT(mutex_owned(&pcnp->pcn_intrlock));
+ ASSERT(mutex_owned(&pcnp->pcn_xmtlock));
+
+ (void) pcn_initialize(pcnp, B_FALSE);
+
+ /* Start chip and enable interrupts */
+ PCN_CSR_SETBIT(pcnp, PCN_CSR_CSR, PCN_CSR_START|PCN_CSR_INTEN);
+
+ pcn_start_timer(pcnp);
+
+ if (IS_RUNNING(pcnp))
+ mac_tx_update(pcnp->pcn_mh);
+}
+
+static void
+pcn_stopall(pcn_t *pcnp)
+{
+ ASSERT(mutex_owned(&pcnp->pcn_intrlock));
+ ASSERT(mutex_owned(&pcnp->pcn_xmtlock));
+
+ pcn_stop_timer(pcnp);
+ PCN_CSR_SETBIT(pcnp, PCN_CSR_CSR, PCN_CSR_STOP);
+}
+
+/*
+ * The soft timer is not affected by a soft reset (according to the datasheet)
+ * so it must always be explicitly enabled and disabled
+ */
+static void
+pcn_start_timer(pcn_t *pcnp)
+{
+ PCN_CSR_SETBIT(pcnp, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SINTEN);
+
+ /*
+ * The frequency this fires varies based on the particular
+ * model, this value is largely arbitrary. It just needs to
+ * fire often enough to detect a stall
+ */
+ pcn_bcr_write(pcnp, PCN_BCR_TIMER, 0xa000);
+}
+
+
+static void
+pcn_stop_timer(pcn_t *pcnp)
+{
+ PCN_CSR_CLRBIT(pcnp, PCN_CSR_EXTCTL1, PCN_EXTCTL1_SINTEN);
+}
+
+static int
+pcn_m_stat(void *arg, uint_t stat, uint64_t *val)
+{
+ pcn_t *pcnp = (pcn_t *)arg;
+
+ if (mii_m_getstat(pcnp->pcn_mii, stat, val) == 0)
+ return (0);
+
+ switch (stat) {
+ case MAC_STAT_MULTIRCV:
+ *val = pcnp->pcn_multircv;
+ break;
+
+ case MAC_STAT_BRDCSTRCV:
+ *val = pcnp->pcn_brdcstrcv;
+ break;
+
+ case MAC_STAT_MULTIXMT:
+ *val = pcnp->pcn_multixmt;
+ break;
+
+ case MAC_STAT_BRDCSTXMT:
+ *val = pcnp->pcn_brdcstxmt;
+ break;
+
+ case MAC_STAT_IPACKETS:
+ *val = pcnp->pcn_ipackets;
+ break;
+
+ case MAC_STAT_RBYTES:
+ *val = pcnp->pcn_rbytes;
+ break;
+
+ case MAC_STAT_OPACKETS:
+ *val = pcnp->pcn_opackets;
+ break;
+
+ case MAC_STAT_OBYTES:
+ *val = pcnp->pcn_obytes;
+ break;
+
+ case MAC_STAT_NORCVBUF:
+ *val = pcnp->pcn_norcvbuf;
+ break;
+
+ case MAC_STAT_NOXMTBUF:
+ *val = 0;
+ break;
+
+ case MAC_STAT_COLLISIONS:
+ *val = pcnp->pcn_collisions;
+ break;
+
+ case MAC_STAT_IERRORS:
+ *val = pcnp->pcn_errrcv;
+ break;
+
+ case MAC_STAT_OERRORS:
+ *val = pcnp->pcn_errxmt;
+ break;
+
+ case ETHER_STAT_ALIGN_ERRORS:
+ *val = pcnp->pcn_align_errors;
+ break;
+
+ case ETHER_STAT_FCS_ERRORS:
+ *val = pcnp->pcn_fcs_errors;
+ break;
+
+ case ETHER_STAT_SQE_ERRORS:
+ *val = pcnp->pcn_sqe_errors;
+ break;
+
+ case ETHER_STAT_DEFER_XMTS:
+ *val = pcnp->pcn_defer_xmts;
+ break;
+
+ case ETHER_STAT_FIRST_COLLISIONS:
+ *val = pcnp->pcn_first_collisions;
+ break;
+
+ case ETHER_STAT_MULTI_COLLISIONS:
+ *val = pcnp->pcn_multi_collisions;
+ break;
+
+ case ETHER_STAT_TX_LATE_COLLISIONS:
+ *val = pcnp->pcn_tx_late_collisions;
+ break;
+
+ case ETHER_STAT_EX_COLLISIONS:
+ *val = pcnp->pcn_ex_collisions;
+ break;
+
+ case ETHER_STAT_MACXMT_ERRORS:
+ *val = pcnp->pcn_macxmt_errors;
+ break;
+
+ case ETHER_STAT_CARRIER_ERRORS:
+ *val = pcnp->pcn_carrier_errors;
+ break;
+
+ case ETHER_STAT_TOOLONG_ERRORS:
+ *val = pcnp->pcn_toolong_errors;
+ break;
+
+ case ETHER_STAT_MACRCV_ERRORS:
+ *val = pcnp->pcn_macrcv_errors;
+ break;
+
+ case MAC_STAT_OVERFLOWS:
+ *val = pcnp->pcn_overflow;
+ break;
+
+ case MAC_STAT_UNDERFLOWS:
+ *val = pcnp->pcn_underflow;
+ break;
+
+ case ETHER_STAT_TOOSHORT_ERRORS:
+ *val = pcnp->pcn_runt;
+ break;
+
+ case ETHER_STAT_JABBER_ERRORS:
+ *val = pcnp->pcn_jabber;
+ break;
+
+ default:
+ return (ENOTSUP);
+ }
+ return (0);
+}
+
+static int
+pcn_m_getprop(void *arg, const char *name, mac_prop_id_t num, uint_t sz,
+ void *val)
+{
+ pcn_t *pcnp = (pcn_t *)arg;
+
+ return (mii_m_getprop(pcnp->pcn_mii, name, num, sz, val));
+}
+
+static int
+pcn_m_setprop(void *arg, const char *name, mac_prop_id_t num, uint_t sz,
+ const void *val)
+{
+ pcn_t *pcnp = (pcn_t *)arg;
+
+ return (mii_m_setprop(pcnp->pcn_mii, name, num, sz, val));
+}
+
+static void
+pcn_m_propinfo(void *arg, const char *name, mac_prop_id_t num,
+ mac_prop_info_handle_t prh)
+{
+ pcn_t *pcnp = arg;
+
+ mii_m_propinfo(pcnp->pcn_mii, name, num, prh);
+}
+
+static int
+pcn_watchdog(pcn_t *pcnp)
+{
+ if ((pcnp->pcn_txstall_time != 0) &&
+ (gethrtime() > pcnp->pcn_txstall_time) &&
+ (pcnp->pcn_txavail != PCN_TXRING)) {
+ pcnp->pcn_txstall_time = 0;
+ pcn_error(pcnp->pcn_dip, "TX stall detected!");
+ return (DDI_FAILURE);
+ } else {
+ return (DDI_SUCCESS);
+ }
+}
+
+static uint16_t
+pcn_mii_read(void *arg, uint8_t phy, uint8_t reg)
+{
+ pcn_t *pcnp = (pcn_t *)arg;
+ uint16_t val;
+
+ /*
+ * At least Am79C971 with DP83840A wedge when isolating the
+ * external PHY so we can't allow multiple external PHYs.
+ * There are cards that use Am79C971 with both the internal
+ * and an external PHY though.
+ * For internal PHYs it doesn't really matter whether we can
+ * isolate the remaining internal and the external ones in
+ * the PHY drivers as the internal PHYs have to be enabled
+ * individually in PCN_BCR_PHYSEL, PCN_CSR_MODE, etc.
+ * With Am79C97{3,5,8} we don't support switching beetween
+ * the internal and external PHYs, yet, so we can't allow
+ * multiple PHYs with these either.
+ * Am79C97{2,6} actually only support external PHYs (not
+ * connectable internal ones respond at the usual addresses,
+ * which don't hurt if we let them show up on the bus) and
+ * isolating them works.
+ */
+ if (((pcnp->pcn_type == Am79C971 && phy != PCN_PHYAD_10BT) ||
+ pcnp->pcn_type == Am79C973 || pcnp->pcn_type == Am79C975 ||
+ pcnp->pcn_type == Am79C978) && pcnp->pcn_extphyaddr != -1 &&
+ phy != pcnp->pcn_extphyaddr) {
+ return (0);
+ }
+
+ val = ((uint16_t)phy << 5) | reg;
+ pcn_bcr_write(pcnp, PCN_BCR_MIIADDR, phy << 5 | reg);
+ val = pcn_bcr_read(pcnp, PCN_BCR_MIIDATA) & 0xFFFF;
+ if (val == 0xFFFF) {
+ return (0);
+ }
+
+ if (((pcnp->pcn_type == Am79C971 && phy != PCN_PHYAD_10BT) ||
+ pcnp->pcn_type == Am79C973 || pcnp->pcn_type == Am79C975 ||
+ pcnp->pcn_type == Am79C978) && pcnp->pcn_extphyaddr == -1)
+ pcnp->pcn_extphyaddr = phy;
+
+ return (val);
+}
+
+static void
+pcn_mii_write(void *arg, uint8_t phy, uint8_t reg, uint16_t val)
+{
+ pcn_t *pcnp = (pcn_t *)arg;
+
+ pcn_bcr_write(pcnp, PCN_BCR_MIIADDR, reg | (phy << 5));
+ pcn_bcr_write(pcnp, PCN_BCR_MIIDATA, val);
+}
+
+static void
+pcn_mii_notify(void *arg, link_state_t link)
+{
+ pcn_t *pcnp = (pcn_t *)arg;
+
+ mac_link_update(pcnp->pcn_mh, link);
+}
+
+static const pcn_type_t *
+pcn_match(uint16_t vid, uint16_t did)
+{
+ const pcn_type_t *t;
+
+ t = pcn_devs;
+ while (t->pcn_name != NULL) {
+ if ((vid == t->pcn_vid) && (did == t->pcn_did))
+ return (t);
+ t++;
+ }
+ return (NULL);
+}
+
+static void
+pcn_getfactaddr(pcn_t *pcnp)
+{
+ uint32_t addr[2];
+
+ addr[0] = CSR_READ_4(pcnp, PCN_IO32_APROM00);
+ addr[1] = CSR_READ_4(pcnp, PCN_IO32_APROM01);
+
+ bcopy(&addr[0], &pcnp->pcn_addr[0], sizeof (pcnp->pcn_addr));
+}
+
+static uint32_t
+pcn_csr_read(pcn_t *pcnp, uint32_t reg)
+{
+ uint32_t val;
+
+ mutex_enter(&pcnp->pcn_reglock);
+ CSR_WRITE_4(pcnp, PCN_IO32_RAP, reg);
+ val = CSR_READ_4(pcnp, PCN_IO32_RDP);
+ mutex_exit(&pcnp->pcn_reglock);
+ return (val);
+}
+
+static uint16_t
+pcn_csr_read16(pcn_t *pcnp, uint32_t reg)
+{
+ uint16_t val;
+
+ mutex_enter(&pcnp->pcn_reglock);
+ CSR_WRITE_2(pcnp, PCN_IO16_RAP, reg);
+ val = CSR_READ_2(pcnp, PCN_IO16_RDP);
+ mutex_exit(&pcnp->pcn_reglock);
+ return (val);
+}
+
+static void
+pcn_csr_write(pcn_t *pcnp, uint32_t reg, uint32_t val)
+{
+ mutex_enter(&pcnp->pcn_reglock);
+ CSR_WRITE_4(pcnp, PCN_IO32_RAP, reg);
+ CSR_WRITE_4(pcnp, PCN_IO32_RDP, val);
+ mutex_exit(&pcnp->pcn_reglock);
+}
+
+static uint32_t
+pcn_bcr_read(pcn_t *pcnp, uint32_t reg)
+{
+ uint32_t val;
+
+ mutex_enter(&pcnp->pcn_reglock);
+ CSR_WRITE_4(pcnp, PCN_IO32_RAP, reg);
+ val = CSR_READ_4(pcnp, PCN_IO32_BDP);
+ mutex_exit(&pcnp->pcn_reglock);
+ return (val);
+}
+
+static uint16_t
+pcn_bcr_read16(pcn_t *pcnp, uint32_t reg)
+{
+ uint16_t val;
+
+ mutex_enter(&pcnp->pcn_reglock);
+ CSR_WRITE_2(pcnp, PCN_IO16_RAP, reg);
+ val = CSR_READ_2(pcnp, PCN_IO16_BDP);
+ mutex_exit(&pcnp->pcn_reglock);
+ return (val);
+}
+
+static void
+pcn_bcr_write(pcn_t *pcnp, uint32_t reg, uint32_t val)
+{
+ mutex_enter(&pcnp->pcn_reglock);
+ CSR_WRITE_4(pcnp, PCN_IO32_RAP, reg);
+ CSR_WRITE_4(pcnp, PCN_IO32_BDP, val);
+ mutex_exit(&pcnp->pcn_reglock);
+}
+
+static void
+pcn_resetrings(pcn_t *pcnp)
+{
+ int i;
+ uint16_t bufsz = ((~(PCN_BUFSZ) + 1) & PCN_RXLEN_BUFSZ) | PCN_RXLEN_MBO;
+
+ pcnp->pcn_rxhead = 0;
+ pcnp->pcn_txreclaim = 0;
+ pcnp->pcn_txsend = 0;
+ pcnp->pcn_txavail = PCN_TXRING;
+
+ /* reset rx descriptor values */
+ for (i = 0; i < PCN_RXRING; i++) {
+ pcn_rx_desc_t *rmd = &pcnp->pcn_rxdescp[i];
+ pcn_buf_t *rxb = pcnp->pcn_rxbufs[i];
+
+ rmd->pcn_rxlen = rmd->pcn_rsvd0 = 0;
+ rmd->pcn_rbaddr = rxb->pb_paddr;
+ rmd->pcn_bufsz = bufsz;
+ rmd->pcn_rxstat = PCN_RXSTAT_OWN;
+ }
+ (void) ddi_dma_sync(pcnp->pcn_rxdesc_dmah, 0,
+ PCN_RXRING * sizeof (pcn_rx_desc_t), DDI_DMA_SYNC_FORDEV);
+
+ /* reset tx descriptor values */
+ for (i = 0; i < PCN_TXRING; i++) {
+ pcn_tx_desc_t *txd = &pcnp->pcn_txdescp[i];
+ pcn_buf_t *txb = pcnp->pcn_txbufs[i];
+
+ txd->pcn_txstat = txd->pcn_txctl = txd->pcn_uspace = 0;
+ txd->pcn_tbaddr = txb->pb_paddr;
+ }
+ (void) ddi_dma_sync(pcnp->pcn_txdesc_dmah, 0,
+ PCN_TXRING * sizeof (pcn_tx_desc_t), DDI_DMA_SYNC_FORDEV);
+
+ /* set addresses of decriptors */
+ pcn_csr_write(pcnp, PCN_CSR_RXADDR0, pcnp->pcn_rxdesc_paddr & 0xFFFF);
+ pcn_csr_write(pcnp, PCN_CSR_RXADDR1,
+ (pcnp->pcn_rxdesc_paddr >> 16) & 0xFFFF);
+
+ pcn_csr_write(pcnp, PCN_CSR_TXADDR0, pcnp->pcn_txdesc_paddr & 0xFFFF);
+ pcn_csr_write(pcnp, PCN_CSR_TXADDR1,
+ (pcnp->pcn_txdesc_paddr >> 16) & 0xFFFF);
+
+ /* set the ring sizes */
+ pcn_csr_write(pcnp, PCN_CSR_RXRINGLEN, (~PCN_RXRING) + 1);
+ pcn_csr_write(pcnp, PCN_CSR_TXRINGLEN, (~PCN_TXRING) + 1);
+}
+
+static void
+pcn_destroybuf(pcn_buf_t *buf)
+{
+ if (buf == NULL)
+ return;
+
+ if (buf->pb_paddr)
+ (void) ddi_dma_unbind_handle(buf->pb_dmah);
+ if (buf->pb_acch)
+ ddi_dma_mem_free(&buf->pb_acch);
+ if (buf->pb_dmah)
+ ddi_dma_free_handle(&buf->pb_dmah);
+ kmem_free(buf, sizeof (*buf));
+}
+
+static pcn_buf_t *
+pcn_allocbuf(pcn_t *pcnp)
+{
+ pcn_buf_t *buf;
+ size_t len;
+ unsigned ccnt;
+ ddi_dma_cookie_t dmac;
+
+ buf = kmem_zalloc(sizeof (*buf), KM_SLEEP);
+
+ if (ddi_dma_alloc_handle(pcnp->pcn_dip, &pcn_dma_attr, DDI_DMA_SLEEP,
+ NULL, &buf->pb_dmah) != DDI_SUCCESS) {
+ kmem_free(buf, sizeof (*buf));
+ return (NULL);
+ }
+
+ if (ddi_dma_mem_alloc(buf->pb_dmah, PCN_BUFSZ, &pcn_bufattr,
+ DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL, &buf->pb_buf, &len,
+ &buf->pb_acch) != DDI_SUCCESS) {
+ pcn_destroybuf(buf);
+ return (NULL);
+ }
+
+ if (ddi_dma_addr_bind_handle(buf->pb_dmah, NULL, buf->pb_buf, len,
+ DDI_DMA_READ | DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL, &dmac,
+ &ccnt) != DDI_DMA_MAPPED) {
+ pcn_destroybuf(buf);
+ return (NULL);
+ }
+ buf->pb_paddr = dmac.dmac_address;
+
+ return (buf);
+}
+
+static int
+pcn_alloctxring(pcn_t *pcnp)
+{
+ int rval;
+ int i;
+ size_t size;
+ size_t len;
+ ddi_dma_cookie_t dmac;
+ unsigned ncookies;
+ caddr_t kaddr;
+
+ size = PCN_TXRING * sizeof (pcn_tx_desc_t);
+
+ rval = ddi_dma_alloc_handle(pcnp->pcn_dip, &pcn_dma_attr, DDI_DMA_SLEEP,
+ NULL, &pcnp->pcn_txdesc_dmah);
+ if (rval != DDI_SUCCESS) {
+ pcn_error(pcnp->pcn_dip, "unable to allocate DMA handle for tx "
+ "descriptors");
+ return (DDI_FAILURE);
+ }
+
+ rval = ddi_dma_mem_alloc(pcnp->pcn_txdesc_dmah, size, &pcn_devattr,
+ DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &kaddr, &len,
+ &pcnp->pcn_txdesc_acch);
+ if (rval != DDI_SUCCESS) {
+ pcn_error(pcnp->pcn_dip, "unable to allocate DMA memory for tx "
+ "descriptors");
+ return (DDI_FAILURE);
+ }
+
+ rval = ddi_dma_addr_bind_handle(pcnp->pcn_txdesc_dmah, NULL, kaddr,
+ size, DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &dmac,
+ &ncookies);
+ if (rval != DDI_DMA_MAPPED) {
+ pcn_error(pcnp->pcn_dip, "unable to bind DMA for tx "
+ "descriptors");
+ return (DDI_FAILURE);
+ }
+
+ ASSERT(ncookies == 1);
+
+ pcnp->pcn_txdesc_paddr = dmac.dmac_address;
+ pcnp->pcn_txdescp = (void *)kaddr;
+
+ pcnp->pcn_txbufs = kmem_zalloc(PCN_TXRING * sizeof (pcn_buf_t *),
+ KM_SLEEP);
+
+ for (i = 0; i < PCN_TXRING; i++) {
+ pcn_buf_t *txb = pcn_allocbuf(pcnp);
+ if (txb == NULL)
+ return (DDI_FAILURE);
+ pcnp->pcn_txbufs[i] = txb;
+ }
+
+ return (DDI_SUCCESS);
+}
+
+static int
+pcn_allocrxring(pcn_t *pcnp)
+{
+ int rval;
+ int i;
+ size_t len;
+ size_t size;
+ ddi_dma_cookie_t dmac;
+ unsigned ncookies;
+ caddr_t kaddr;
+
+ size = PCN_RXRING * sizeof (pcn_rx_desc_t);
+
+ rval = ddi_dma_alloc_handle(pcnp->pcn_dip, &pcn_dmadesc_attr,
+ DDI_DMA_SLEEP, NULL, &pcnp->pcn_rxdesc_dmah);
+ if (rval != DDI_SUCCESS) {
+ pcn_error(pcnp->pcn_dip, "unable to allocate DMA handle for rx "
+ "descriptors");
+ return (DDI_FAILURE);
+ }
+
+ rval = ddi_dma_mem_alloc(pcnp->pcn_rxdesc_dmah, size, &pcn_devattr,
+ DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &kaddr, &len,
+ &pcnp->pcn_rxdesc_acch);
+ if (rval != DDI_SUCCESS) {
+ pcn_error(pcnp->pcn_dip, "unable to allocate DMA memory for rx "
+ "descriptors");
+ return (DDI_FAILURE);
+ }
+
+ rval = ddi_dma_addr_bind_handle(pcnp->pcn_rxdesc_dmah, NULL, kaddr,
+ size, DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &dmac,
+ &ncookies);
+ if (rval != DDI_DMA_MAPPED) {
+ pcn_error(pcnp->pcn_dip, "unable to bind DMA for rx "
+ "descriptors");
+ return (DDI_FAILURE);
+ }
+
+ ASSERT(ncookies == 1);
+
+ pcnp->pcn_rxdesc_paddr = dmac.dmac_address;
+ pcnp->pcn_rxdescp = (void *)kaddr;
+
+ pcnp->pcn_rxbufs = kmem_zalloc(PCN_RXRING * sizeof (pcn_buf_t *),
+ KM_SLEEP);
+
+ for (i = 0; i < PCN_RXRING; i++) {
+ pcn_buf_t *rxb = pcn_allocbuf(pcnp);
+ if (rxb == NULL)
+ return (DDI_FAILURE);
+ pcnp->pcn_rxbufs[i] = rxb;
+ }
+
+ return (DDI_SUCCESS);
+}
+
+static void
+pcn_freetxring(pcn_t *pcnp)
+{
+ int i;
+
+ if (pcnp->pcn_txbufs) {
+ for (i = 0; i < PCN_TXRING; i++)
+ pcn_destroybuf(pcnp->pcn_txbufs[i]);
+
+ kmem_free(pcnp->pcn_txbufs, PCN_TXRING * sizeof (pcn_buf_t *));
+ }
+
+ if (pcnp->pcn_txdesc_paddr)
+ (void) ddi_dma_unbind_handle(pcnp->pcn_txdesc_dmah);
+ if (pcnp->pcn_txdesc_acch)
+ ddi_dma_mem_free(&pcnp->pcn_txdesc_acch);
+ if (pcnp->pcn_txdesc_dmah)
+ ddi_dma_free_handle(&pcnp->pcn_txdesc_dmah);
+}
+
+static void
+pcn_freerxring(pcn_t *pcnp)
+{
+ int i;
+
+ if (pcnp->pcn_rxbufs) {
+ for (i = 0; i < PCN_RXRING; i++)
+ pcn_destroybuf(pcnp->pcn_rxbufs[i]);
+
+ kmem_free(pcnp->pcn_rxbufs, PCN_RXRING * sizeof (pcn_buf_t *));
+ }
+
+ if (pcnp->pcn_rxdesc_paddr)
+ (void) ddi_dma_unbind_handle(pcnp->pcn_rxdesc_dmah);
+ if (pcnp->pcn_rxdesc_acch)
+ ddi_dma_mem_free(&pcnp->pcn_rxdesc_acch);
+ if (pcnp->pcn_rxdesc_dmah)
+ ddi_dma_free_handle(&pcnp->pcn_rxdesc_dmah);
+}
+
+static int
+pcn_set_chipid(pcn_t *pcnp, uint32_t conf_id)
+{
+ char *name = NULL;
+ uint32_t chipid;
+
+ /*
+ * Note: we can *NOT* put the chip into 32-bit mode yet. If a
+ * lance ethernet device is present and pcn tries to attach, it can
+ * hang the device (requiring a hardware reset), since they only work
+ * in 16-bit mode.
+ *
+ * The solution is check using 16-bit operations first, and determine
+ * if 32-bit mode operations are supported.
+ *
+ * The safest way to do this is to read the PCI subsystem ID from
+ * BCR23/24 and compare that with the value read from PCI config
+ * space.
+ */
+ chipid = pcn_bcr_read16(pcnp, PCN_BCR_PCISUBSYSID);
+ chipid <<= 16;
+ chipid |= pcn_bcr_read16(pcnp, PCN_BCR_PCISUBVENID);
+
+ /*
+ * The test for 0x10001000 is a hack to pacify VMware, who's
+ * pseudo-PCnet interface is broken. Reading the subsystem register
+ * from PCI config space yields 0x00000000 while reading the same value
+ * from I/O space yields 0x10001000. It's not supposed to be that way.
+ */
+ if (chipid == conf_id || chipid == 0x10001000) {
+ /* We're in 16-bit mode. */
+ chipid = pcn_csr_read16(pcnp, PCN_CSR_CHIPID1);
+ chipid <<= 16;
+ chipid |= pcn_csr_read16(pcnp, PCN_CSR_CHIPID0);
+ } else {
+ chipid = pcn_csr_read(pcnp, PCN_CSR_CHIPID1);
+ chipid <<= 16;
+ chipid |= pcn_csr_read(pcnp, PCN_CSR_CHIPID0);
+ }
+
+ chipid = CHIPID_PARTID(chipid);
+
+ /* Set default value and override as needed */
+ switch (chipid) {
+ case Am79C970:
+ name = "Am79C970 PCnet-PCI";
+ pcn_error(pcnp->pcn_dip, "Unsupported chip: %s", name);
+ return (DDI_FAILURE);
+ case Am79C970A:
+ name = "Am79C970A PCnet-PCI II";
+ pcn_error(pcnp->pcn_dip, "Unsupported chip: %s", name);
+ return (DDI_FAILURE);
+ case Am79C971:
+ name = "Am79C971 PCnet-FAST";
+ break;
+ case Am79C972:
+ name = "Am79C972 PCnet-FAST+";
+ break;
+ case Am79C973:
+ name = "Am79C973 PCnet-FAST III";
+ break;
+ case Am79C975:
+ name = "Am79C975 PCnet-FAST III";
+ break;
+ case Am79C976:
+ name = "Am79C976";
+ break;
+ case Am79C978:
+ name = "Am79C978";
+ break;
+ default:
+ name = "Unknown";
+ pcn_error(pcnp->pcn_dip, "Unknown chip id 0x%x", chipid);
+ }
+
+ if (ddi_prop_update_string(DDI_DEV_T_NONE, pcnp->pcn_dip, "chipid",
+ name) != DDI_SUCCESS) {
+ pcn_error(pcnp->pcn_dip, "Unable to set chipid property");
+ return (DDI_FAILURE);
+ }
+
+ return (DDI_SUCCESS);
+}
+
+static void
+pcn_error(dev_info_t *dip, char *fmt, ...)
+{
+ va_list ap;
+ char buf[256];
+
+ va_start(ap, fmt);
+ (void) vsnprintf(buf, sizeof (buf), fmt, ap);
+ va_end(ap);
+
+ if (dip)
+ cmn_err(CE_WARN, "%s%d: %s", ddi_driver_name(dip),
+ ddi_get_instance(dip), buf);
+ else
+ cmn_err(CE_WARN, "pcn: %s", buf);
+}
diff --git a/usr/src/uts/common/io/pcn/pcn.h b/usr/src/uts/common/io/pcn/pcn.h
new file mode 100644
index 0000000000..7a0dcd52fe
--- /dev/null
+++ b/usr/src/uts/common/io/pcn/pcn.h
@@ -0,0 +1,450 @@
+/*
+ * Copyright 2011 Jason King.
+ * Copyright (c) 2000 Berkeley Software Design, Inc.
+ * Copyright (c) 1997, 1998, 1999, 2000
+ * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef _PCN_H
+#define _PCN_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * 16-bit I/O map
+ * To switch to 32-bit mode, write to RDP.
+ */
+#define PCN_IO16_APROM00 0x00
+#define PCN_IO16_APROM01 0x02
+#define PCN_IO16_APROM02 0x04
+#define PCN_IO16_APROM03 0x06
+#define PCN_IO16_APROM04 0x08
+#define PCN_IO16_APROM05 0x0A
+#define PCN_IO16_APROM06 0x0C
+#define PCN_IO16_APROM07 0x0E
+#define PCN_IO16_RDP 0x10
+#define PCN_IO16_RAP 0x12
+#define PCN_IO16_RESET 0x14
+#define PCN_IO16_BDP 0x16
+
+/*
+ * 32-bit I/O map
+ */
+#define PCN_IO32_APROM00 0x00
+#define PCN_IO32_APROM01 0x04
+#define PCN_IO32_APROM02 0x08
+#define PCN_IO32_APROM03 0x0C
+#define PCN_IO32_RDP 0x10
+#define PCN_IO32_RAP 0x14
+#define PCN_IO32_RESET 0x18
+#define PCN_IO32_BDP 0x1C
+
+/*
+ * CSR registers
+ */
+#define PCN_CSR_CSR 0x00
+#define PCN_CSR_IAB0 0x01
+#define PCN_CSR_IAB1 0x02
+#define PCN_CSR_IMR 0x03
+#define PCN_CSR_TFEAT 0x04
+#define PCN_CSR_EXTCTL1 0x05
+#define PCN_CSR_DTBLLEN 0x06
+#define PCN_CSR_EXTCTL2 0x07
+#define PCN_CSR_MAR0 0x08
+#define PCN_CSR_MAR1 0x09
+#define PCN_CSR_MAR2 0x0A
+#define PCN_CSR_MAR3 0x0B
+#define PCN_CSR_PAR0 0x0C
+#define PCN_CSR_PAR1 0x0D
+#define PCN_CSR_PAR2 0x0E
+#define PCN_CSR_MODE 0x0F
+#define PCN_CSR_RXADDR0 0x18
+#define PCN_CSR_RXADDR1 0x19
+#define PCN_CSR_TXADDR0 0x1E
+#define PCN_CSR_TXADDR1 0x1F
+#define PCN_CSR_TXPOLL 0x2F
+#define PCN_CSR_RXPOLL 0x31
+#define PCN_CSR_RXRINGLEN 0x4C
+#define PCN_CSR_TXRINGLEN 0x4E
+#define PCN_CSR_DMACTL 0x50
+#define PCN_CSR_BUSTIMER 0x52
+#define PCN_CSR_MEMERRTIMEO 0x64
+#define PCN_CSR_ONNOWMISC 0x74
+#define PCN_CSR_ADVFEAT 0x7A
+#define PCN_CSR_MACCFG 0x7D
+#define PCN_CSR_CHIPID0 0x58
+#define PCN_CSR_CHIPID1 0x59
+
+
+#define PCN_CSR_INIT 0x0001
+#define PCN_CSR_START 0x0002
+#define PCN_CSR_STOP 0x0004
+#define PCN_CSR_TX 0x0008
+#define PCN_CSR_TXON 0x0010
+#define PCN_CSR_RXON 0x0020
+#define PCN_CSR_INTEN 0x0040
+#define PCN_CSR_INTR 0x0080
+#define PCN_CSR_IDONE 0x0100
+#define PCN_CSR_TINT 0x0200
+#define PCN_CSR_RINT 0x0400
+#define PCN_CSR_MERR 0x0800
+#define PCN_CSR_MISS 0x1000
+#define PCN_CSR_CERR 0x2000
+#define PCN_CSR_ERR 0x8000
+#define PCN_CSR_STR \
+ "\020" \
+ "\001INIT" \
+ "\002START" \
+ "\003STOP" \
+ "\004TX" \
+ "\005TXON" \
+ "\006RXON" \
+ "\007INTEN" \
+ "\010INTR" \
+ "\011IDONE" \
+ "\012TINT" \
+ "\013RINT" \
+ "\014MERR" \
+ "\015MISS" \
+ "\016CERR" \
+ "\017ERR"
+
+/*
+ * Interrupt masks and deferral control (CSR3)
+ */
+#define PCN_IMR_BSWAP 0x0004
+#define PCN_IMR_ENMBA 0x0008 /* enable modified backoff alg */
+#define PCN_IMR_DXMT2PD 0x0010
+#define PCN_IMR_LAPPEN 0x0020 /* lookahead packet processing enb */
+#define PCN_IMR_DXSUFLO 0x0040 /* disable TX stop on underflow */
+#define PCN_IMR_IDONE 0x0100
+#define PCN_IMR_TINT 0x0200
+#define PCN_IMR_RINT 0x0400
+#define PCN_IMR_MERR 0x0800
+#define PCN_IMR_MISS 0x1000
+#define PCN_IMR_STR \
+ "\020" \
+ "\003BSWAP" \
+ "\004ENMBA" \
+ "\005DXMT2PD" \
+ "\006LAPPEN" \
+ "\007DXSUFLO" \
+ "\010IDONE" \
+ "\011TINT" \
+ "\012RINT" \
+ "\013MERR" \
+ "\014MISS"
+
+/*
+ * Test and features control (CSR4)
+ */
+#define PCN_TFEAT_TXSTRTMASK 0x0004
+#define PCN_TFEAT_TXSTRT 0x0008
+#define PCN_TFEAT_RXCCOFLOWM 0x0010 /* Rx collision counter oflow */
+#define PCN_TFEAT_RXCCOFLOW 0x0020
+#define PCN_TFEAT_UINT 0x0040
+#define PCN_TFEAT_UINTREQ 0x0080
+#define PCN_TFEAT_MISSOFLOWM 0x0100
+#define PCN_TFEAT_MISSOFLOW 0x0200
+#define PCN_TFEAT_STRIP_FCS 0x0400
+#define PCN_TFEAT_PAD_TX 0x0800
+#define PCN_TFEAT_TXDPOLL 0x1000
+#define PCN_TFEAT_DMAPLUS 0x4000
+
+/*
+ * Extended control and interrupt 1 (CSR5)
+ */
+#define PCN_EXTCTL1_SPND 0x0001 /* suspend */
+#define PCN_EXTCTL1_MPMODE 0x0002 /* magic packet mode */
+#define PCN_EXTCTL1_MPENB 0x0004 /* magic packet enable */
+#define PCN_EXTCTL1_MPINTEN 0x0008 /* magic packet interrupt enable */
+#define PCN_EXTCTL1_MPINT 0x0010 /* magic packet interrupt */
+#define PCN_EXTCTL1_MPPLBA 0x0020 /* magic packet phys. logical bcast */
+#define PCN_EXTCTL1_EXDEFEN 0x0040 /* excessive deferral interrupt enb. */
+#define PCN_EXTCTL1_EXDEF 0x0080 /* excessive deferral interrupt */
+#define PCN_EXTCTL1_SINTEN 0x0400 /* system interrupt enable */
+#define PCN_EXTCTL1_SINT 0x0800 /* system interrupt */
+#define PCN_EXTCTL1_LTINTEN 0x4000 /* last TX interrupt enb */
+#define PCN_EXTCTL1_TXOKINTD 0x8000 /* TX OK interrupt disable */
+#define PCN_EXTCTL1_STR \
+ "\020" \
+ "\001SPND" \
+ "\002MPMODE" \
+ "\003MPENB" \
+ "\004MPINTEN" \
+ "\005MPINT" \
+ "\006MPPLB" \
+ "\007EXDEFEN" \
+ "\010EXDEF" \
+ "\013SINTEN" \
+ "\014SINT" \
+ "\017LTINTEN" \
+ "\020TXOKINTD"
+
+/*
+ * RX/TX descriptor len (CSR6)
+ */
+#define PCN_DTBLLEN_RLEN 0x0F00
+#define PCN_DTBLLEN_TLEN 0xF000
+
+/*
+ * Extended control and interrupt 2 (CSR7)
+ */
+#define PCN_EXTCTL2_MIIPDTINTE 0x0001
+#define PCN_EXTCTL2_MIIPDTINT 0x0002
+#define PCN_EXTCTL2_MCCIINTE 0x0004
+#define PCN_EXTCTL2_MCCIINT 0x0008
+#define PCN_EXTCTL2_MCCINTE 0x0010
+#define PCN_EXTCTL2_MCCINT 0x0020
+#define PCN_EXTCTL2_MAPINTE 0x0040
+#define PCN_EXTCTL2_MAPINT 0x0080
+#define PCN_EXTCTL2_MREINTE 0x0100
+#define PCN_EXTCTL2_MREINT 0x0200
+#define PCN_EXTCTL2_STINTE 0x0400
+#define PCN_EXTCTL2_STINT 0x0800
+#define PCN_EXTCTL2_RXDPOLL 0x1000
+#define PCN_EXTCTL2_RDMD 0x2000
+#define PCN_EXTCTL2_RXFRTG 0x4000
+#define PCN_EXTCTL2_FASTSPNDE 0x8000
+#define PCN_EXTCTL2_STR \
+ "\020" \
+ "\001MIIPDTINTE" \
+ "\002MIIPDTINT" \
+ "\003MCCIINTTE" \
+ "\004MCCIINT" \
+ "\005MCCINTE" \
+ "\006MCCINT" \
+ "\007MAPINTE" \
+ "\010MAPINT" \
+ "\011MRTINTE" \
+ "\012MREINT" \
+ "\013STINTE" \
+ "\014STINT" \
+ "\015RXDPOLL" \
+ "\016RDMD" \
+ "\017RXFRTG" \
+ "\020FASTSPNDE"
+
+/*
+ * Mode (CSR15)
+ */
+#define PCN_MODE_RXD 0x0001 /* RX disable */
+#define PCN_MODE_TXD 0x0002 /* TX disable */
+#define PCN_MODE_LOOP 0x0004 /* loopback enable */
+#define PCN_MODE_TXCRCD 0x0008
+#define PCN_MODE_FORCECOLL 0x0010
+#define PCN_MODE_RETRYD 0x0020
+#define PCN_MODE_INTLOOP 0x0040
+#define PCN_MODE_PORTSEL 0x0180
+#define PCN_MODE_RXVPAD 0x2000
+#define PCN_MODE_RXNOBROAD 0x4000
+#define PCN_MODE_PROMISC 0x8000
+#define PCN_MODE_STR \
+ "\020" \
+ "\001RXD" \
+ "\002TXD" \
+ "\003LOOP" \
+ "\004TXCRCD" \
+ "\005FORCECOLL" \
+ "\006RETRYD" \
+ "\007INTLOOP" \
+ "\016RXVPAD" \
+ "\017RXNOBROAD" \
+ "\020PROMISC"
+
+/* Settings for PCN_MODE_PORTSEL when ASEL (BCR2[1]) is 0 */
+#define PCN_PORT_AUI 0x0000
+#define PCN_PORT_10BASET 0x0080
+#define PCN_PORT_GPSI 0x0100
+#define PCN_PORT_MII 0x0180
+
+/*
+ * Chip ID values.
+ */
+
+#define CHIPID_MANFID(x) (((x) >> 1) & 0x3ff)
+#define CHIPID_PARTID(x) (((x) >> 12) & 0xffff)
+#define CHIPID_VER(x) (((x) >> 28) & 0x7)
+
+/* CSR88-89: Chip ID masks */
+#define Am79C970 0x0003
+#define Am79C970A 0x2621
+#define Am79C971 0x2623
+#define Am79C972 0x2624
+#define Am79C973 0x2625
+#define Am79C978 0x2626
+#define Am79C975 0x2627
+#define Am79C976 0x2628
+
+/*
+ * Advanced feature control (CSR122)
+ */
+#define PCN_AFC_RXALIGN 0x0001
+
+/*
+ * BCR (bus control) registers
+ */
+#define PCN_BCR_MMRA 0x00 /* Master Mode Read Active */
+#define PCN_BCR_MMW 0x01 /* Master Mode Write Active */
+#define PCN_BCR_MISCCFG 0x02
+#define PCN_BCR_LED0 0x04
+#define PCN_BCR_LED1 0x05
+#define PCN_BCR_LED2 0x06
+#define PCN_BCR_LED3 0x07
+#define PCN_BCR_DUPLEX 0x09
+#define PCN_BCR_BUSCTL 0x12
+#define PCN_BCR_EECTL 0x13
+#define PCN_BCR_SSTYLE 0x14
+#define PCN_BCR_PCILAT 0x16
+#define PCN_BCR_PCISUBVENID 0x17
+#define PCN_BCR_PCISUBSYSID 0x18
+#define PCN_BCR_SRAMSIZE 0x19
+#define PCN_BCR_SRAMBOUND 0x1A
+#define PCN_BCR_SRAMCTL 0x1B
+#define PCN_BCR_TIMER 0x1F
+#define PCN_BCR_MIICTL 0x20
+#define PCN_BCR_MIIADDR 0x21
+#define PCN_BCR_MIIDATA 0x22
+#define PCN_BCR_PCIVENID 0x23
+#define PCN_BCR_PCIPCAP 0x24
+#define PCN_BCR_DATA0 0x25
+#define PCN_BCR_DATA1 0x26
+#define PCN_BCR_DATA2 0x27
+#define PCN_BCR_DATA3 0x28
+#define PCN_BCR_DATA4 0x29
+#define PCN_BCR_DATA5 0x2A
+#define PCN_BCR_DATA6 0x2B
+#define PCN_BCR_DATA7 0x2C
+#define PCN_BCR_ONNOWPAT0 0x2D
+#define PCN_BCR_ONNOWPAT1 0x2E
+#define PCN_BCR_ONNOWPAT2 0x2F
+#define PCN_BCR_PHYSEL 0x31
+
+/*
+ * Miscellaneous Configuration (BCR2)
+ */
+#define PCN_MISC_TMAULOOP 1<<14 /* T-MAU Loopback packet enable. */
+#define PCN_MISC_LEDPE 1<<12 /* LED Program Enable */
+#define PCN_MISC_APROMWE 1<<8 /* Address PROM Write Enable */
+#define PCN_MISC_INTLEVEL 1<<7 /* Interrupt level */
+#define PCN_MISC_EADISEL 1<<3 /* EADI Select */
+#define PCN_MISC_AWAKE 1<<2 /* Power saving mode select */
+#define PCN_MISC_ASEL 1<<1 /* Auto Select */
+#define PCN_MISC_XMAUSEL 1<<0 /* Reserved. */
+
+/*
+ * Full duplex control (BCR9)
+ */
+#define PCN_DUPLEX_FDEN 0x0001 /* Full-duplex enable */
+#define PCN_DUPLEX_AUI 0x0002 /* AUI full-duplex */
+#define PCN_DUPLEX_FDRPAD 0x0004 /* Full-duplex runt pkt accept dis. */
+
+/*
+ * Burst and bus control register (BCR18)
+ */
+#define PCN_BUSCTL_BWRITE 0x0020
+#define PCN_BUSCTL_BREAD 0x0040
+#define PCN_BUSCTL_DWIO 0x0080
+#define PCN_BUSCTL_EXTREQ 0x0100
+#define PCN_BUSCTL_MEMCMD 0x0200
+#define PCN_BUSCTL_NOUFLOW 0x0800
+#define PCN_BUSCTL_ROMTMG 0xF000
+
+/*
+ * EEPROM control (BCR19)
+ */
+#define PCN_EECTL_EDATA 0x0001
+#define PCN_EECTL_ECLK 0x0002
+#define PCN_EECTL_EECS 0x0004
+#define PCN_EECTL_EEN 0x0100
+#define PCN_EECTL_EEDET 0x2000
+#define PCN_EECTL_PREAD 0x4000
+#define PCN_EECTL_PVALID 0x8000
+
+/*
+ * Software style (BCR20)
+ */
+#define PCN_SSTYLE_APERREN 0x0400 /* advanced parity error checking */
+#define PCN_SSTYLE_SSIZE32 0x0100
+#define PCN_SSTYLE_SWSTYLE 0x00FF
+
+#define PCN_SWSTYLE_LANCE 0x0000
+#define PCN_SWSTYLE_PCNETPCI 0x0102
+#define PCN_SWSTYLE_PCNETPCI_BURST 0x0103
+
+/*
+ * MII control and status (BCR32)
+ */
+#define PCN_MIICTL_MIILP 0x0002 /* MII internal loopback */
+#define PCN_MIICTL_XPHYSP 0x0008 /* external PHY speed */
+#define PCN_MIICTL_XPHYFD 0x0010 /* external PHY full duplex */
+#define PCN_MIICTL_XPHYANE 0x0020 /* external phy auto-neg enable */
+#define PCN_MIICTL_XPHYRST 0x0040 /* external PHY reset */
+#define PCN_MIICTL_DANAS 0x0080 /* disable auto-neg auto-setup */
+#define PCN_MIICTL_APDW 0x0700 /* auto-poll dwell time */
+#define PCN_MIICTL_APEP 0x0100 /* auto-poll external PHY */
+#define PCN_MIICTL_FMDC 0x3000 /* data clock speed */
+#define PCN_MIICTL_MIIPD 0x4000 /* PHY detect */
+#define PCN_MIICTL_ANTST 0x8000 /* Manufacturing test */
+
+/*
+ * MII address register (BCR33)
+ */
+#define PCN_MIIADDR_REGAD 0x001F
+#define PCN_MIIADDR_PHYAD 0x03E0
+
+/* addresses of internal PHYs */
+#define PCN_PHYAD_100BTX 30
+#define PCN_PHYAD_10BT 31
+
+/*
+ * MII data register (BCR34)
+ */
+#define PCN_MIIDATA_MIIMD 0xFFFF
+
+/*
+ * PHY selection (BCR49) (HomePNA NIC only)
+ */
+#define PCN_PHYSEL_PHYSEL 0x0003
+#define PCN_PHYSEL_DEFAULT 0x0300
+#define PCN_PHYSEL_PCNET 0x8000
+
+#define PCN_PHY_10BT 0x0000
+#define PCN_PHY_HOMEPNA 0x0001
+#define PCN_PHY_EXTERNAL 0x0002
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PCN_H */
diff --git a/usr/src/uts/common/io/pcn/pcnimpl.h b/usr/src/uts/common/io/pcn/pcnimpl.h
new file mode 100644
index 0000000000..49220e02a2
--- /dev/null
+++ b/usr/src/uts/common/io/pcn/pcnimpl.h
@@ -0,0 +1,258 @@
+/*
+ * Copyright (c) 2011 Jason King.
+ * Copyright (c) 2000 Berkeley Software Design, Inc.
+ * Copyright (c) 1997, 1998, 1999, 2000
+ * Bill Paul <wpaul@ee.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+#ifndef _PCNIMPL_H
+#define _PCNIMPL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _KERNEL
+
+#define PCN_VENDORID 0x1022
+#define PCN_DEVICEID_PCNET 0x2000
+#define PCN_DEVICEID_HOME 0x2001
+
+typedef struct pcn_type {
+ uint16_t pcn_vid;
+ uint16_t pcn_did;
+ char *pcn_name; /* ddi_set_prop takes char * */
+} pcn_type_t;
+
+#define PCN_TXRECLAIM 8
+#define PCN_HEADROOM 34
+#define PCN_TXRESCHED 120
+
+#define PCN_RXSTAT_BAM 0x0008 /* broadcast address match */
+#define PCN_RXSTAT_LAFM 0x0010 /* logical address filter match */
+#define PCN_RXSTAT_PAM 0x0020 /* physical address match */
+#define PCN_RXSTAT_BPE 0x0080 /* bus parity error */
+#define PCN_RXSTAT_ENP 0x0100 /* end of packet */
+#define PCN_RXSTAT_STP 0x0200 /* start of packet */
+#define PCN_RXSTAT_BUFF 0x0400 /* buffer error */
+#define PCN_RXSTAT_CRC 0x0800 /* CRC error */
+#define PCN_RXSTAT_OFLOW 0x1000 /* rx overrun */
+#define PCN_RXSTAT_FRAM 0x2000 /* framing error */
+#define PCN_RXSTAT_ERR 0x4000 /* error summary */
+#define PCN_RXSTAT_OWN 0x8000
+#define PCN_RXSTAT_STR \
+ "\020" \
+ "\004BAM" \
+ "\005LAFM" \
+ "\006PAM" \
+ "\010BPE" \
+ "\011ENP" \
+ "\012STP" \
+ "\013BUFF" \
+ "\014CRC" \
+ "\015OFLOW" \
+ "\016FRAM" \
+ "\017ERR" \
+ "\020OWN"
+
+#define PCN_RXLEN_MBO 0xF000
+#define PCN_RXLEN_BUFSZ 0x0FFF
+
+typedef struct pcn_rx_desc {
+ uint16_t pcn_rxlen;
+ uint16_t pcn_rsvd0;
+ uint16_t pcn_bufsz;
+ uint16_t pcn_rxstat;
+ uint32_t pcn_rbaddr;
+ uint32_t pcn_uspace;
+} pcn_rx_desc_t;
+
+typedef struct pcn_tx_desc {
+ uint32_t pcn_txstat;
+ uint32_t pcn_txctl;
+ uint32_t pcn_tbaddr;
+ uint32_t pcn_uspace;
+} pcn_tx_desc_t;
+
+#define PCN_TXCTL_OWN 0x80000000
+#define PCN_TXCTL_ERR 0x40000000 /* error summary */
+#define PCN_TXCTL_ADD_FCS 0x20000000 /* add FCS to pkt */
+#define PCN_TXCTL_MORE_LTINT 0x10000000
+#define PCN_TXCTL_ONE 0x08000000
+#define PCN_TXCTL_DEF 0x04000000
+#define PCN_TXCTL_STP 0x02000000
+#define PCN_TXCTL_ENP 0x01000000
+#define PCN_TXCTL_BPE 0x00800000
+#define PCN_TXCTL_MBO 0x0000F000
+#define PCN_TXCTL_BUFSZ 0x00000FFF
+#define PCN_TXCTL_STR \
+ "\020" \
+ "\040OWN" \
+ "\037ERR" \
+ "\036ADD_FCS" \
+ "\035MORE_LTINT" \
+ "\034ONE" \
+ "\033DEF" \
+ "\032STP" \
+ "\031ENP" \
+ "\030BPE"
+
+typedef struct pcn_buf {
+ caddr_t pb_buf;
+ ddi_dma_handle_t pb_dmah;
+ ddi_acc_handle_t pb_acch;
+ uint32_t pb_paddr;
+} pcn_buf_t;
+
+/* Constants, do not change */
+#define PCN_BUFSZ (1664)
+#define PCN_MCHASH (64)
+
+/* Number of descriptor entries */
+#define PCN_RXRING 64
+#define PCN_TXRING 256
+
+typedef struct pcn {
+ dev_info_t *pcn_dip;
+ mac_handle_t pcn_mh;
+ mii_handle_t pcn_mii;
+ uint16_t pcn_cachesize;
+ int pcn_flags;
+ int pcn_instance;
+ kmutex_t pcn_xmtlock;
+ kmutex_t pcn_intrlock;
+ kmutex_t pcn_reglock;
+ ddi_iblock_cookie_t pcn_icookie;
+ uint_t pcn_int_pri;
+ int pcn_type;
+ int8_t pcn_extphyaddr;
+
+ /*
+ * Register and DMA access
+ */
+ uintptr_t pcn_regs;
+ ddi_acc_handle_t pcn_regshandle;
+
+ /*
+ * Receive descriptors.
+ */
+ int pcn_rxhead;
+ pcn_rx_desc_t *pcn_rxdescp;
+ ddi_dma_handle_t pcn_rxdesc_dmah;
+ ddi_acc_handle_t pcn_rxdesc_acch;
+ uint32_t pcn_rxdesc_paddr;
+ pcn_buf_t **pcn_rxbufs;
+
+ /*
+ * Transmit descriptors.
+ */
+ int pcn_txreclaim;
+ int pcn_txsend;
+ int pcn_txavail;
+ pcn_tx_desc_t *pcn_txdescp;
+ ddi_dma_handle_t pcn_txdesc_dmah;
+ ddi_acc_handle_t pcn_txdesc_acch;
+ uint32_t pcn_txdesc_paddr;
+ pcn_buf_t **pcn_txbufs;
+ hrtime_t pcn_txstall_time;
+ boolean_t pcn_wantw;
+
+ /*
+ * Address management.
+ */
+ uchar_t pcn_addr[ETHERADDRL];
+ boolean_t pcn_promisc;
+ uint16_t pcn_mccount[PCN_MCHASH];
+ uint16_t pcn_mctab[PCN_MCHASH / 16];
+
+ /*
+ * stats
+ */
+ uint64_t pcn_ipackets;
+ uint64_t pcn_opackets;
+ uint64_t pcn_rbytes;
+ uint64_t pcn_obytes;
+ uint64_t pcn_brdcstxmt;
+ uint64_t pcn_multixmt;
+ uint64_t pcn_brdcstrcv;
+ uint64_t pcn_multircv;
+ uint64_t pcn_norcvbuf;
+ uint64_t pcn_errrcv;
+ uint64_t pcn_errxmt;
+ uint64_t pcn_missed;
+ uint64_t pcn_underflow;
+ uint64_t pcn_overflow;
+ uint64_t pcn_align_errors;
+ uint64_t pcn_fcs_errors;
+ uint64_t pcn_carrier_errors;
+ uint64_t pcn_collisions;
+ uint64_t pcn_ex_collisions;
+ uint64_t pcn_tx_late_collisions;
+ uint64_t pcn_defer_xmts;
+ uint64_t pcn_first_collisions;
+ uint64_t pcn_multi_collisions;
+ uint64_t pcn_sqe_errors;
+ uint64_t pcn_macxmt_errors;
+ uint64_t pcn_macrcv_errors;
+ uint64_t pcn_toolong_errors;
+ uint64_t pcn_runt;
+ uint64_t pcn_jabber;
+} pcn_t;
+
+/* Flags */
+#define PCN_RUNNING (1L << 0)
+#define PCN_SUSPENDED (1L << 1)
+#define PCN_INTR_ENABLED (1L << 2)
+#define PCN_FLAGSTR \
+ "\020" \
+ "\001RUNNING" \
+ "\002SUSPENDED" \
+ "\003INTR_ENABLED"
+#define IS_RUNNING(p) ((p)->pcn_flags & PCN_RUNNING)
+#define IS_SUSPENDED(p) ((p)->pcn_flags & PCN_SUSPENDED)
+
+#define SYNCTXDESC(pcnp, index, who) \
+ (void) ddi_dma_sync(pcnp->pcn_txdesc_dmah, \
+ (index * sizeof (pcn_tx_desc_t)), sizeof (pcn_tx_desc_t), who)
+
+#define SYNCRXDESC(pcnp, index, who) \
+ (void) ddi_dma_sync(pcnp->pcn_rxdesc_dmah, \
+ (index * sizeof (pcn_rx_desc_t)), sizeof (pcn_rx_desc_t), who)
+
+#define SYNCBUF(pb, len, who) \
+ (void) ddi_dma_sync(pb->pb_dmah, 0, len, who)
+
+#endif /* _KERNEL */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PCNIMPL_H */
diff --git a/usr/src/uts/common/krtld/kobj.c b/usr/src/uts/common/krtld/kobj.c
index b5e1297751..17e27b223a 100644
--- a/usr/src/uts/common/krtld/kobj.c
+++ b/usr/src/uts/common/krtld/kobj.c
@@ -22,6 +22,10 @@
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
+/*
+ * Copyright 2011 Bayard G. Bell <buffer.g.overflow@gmail.com>.
+ * All rights reserved. Use is subject to license terms.
+ */
/*
* Kernel's linker/loader
@@ -1444,6 +1448,20 @@ depends_on(struct module *mp)
q = (char *)sp->st_value;
+#ifdef KOBJ_DEBUG
+ /*
+ * _depends_on is a deprecated interface, so we warn about its use
+ * irrespective of subsequent processing errors. How else are we going
+ * to be able to deco this interface completely?
+ * Changes initially limited to DEBUG because third-party modules
+ * should be flagged to developers before general use base.
+ */
+ _kobj_printf(ops,
+ "Warning: %s uses deprecated _depends_on interface.\n",
+ mp->filename);
+ _kobj_printf(ops, "Please notify module developer or vendor.\n");
+#endif
+
/*
* Idiot checks. Make sure it's
* in-bounds and NULL terminated.
diff --git a/usr/src/uts/common/os/cred.c b/usr/src/uts/common/os/cred.c
index 93a274f2d1..1ec63249ab 100644
--- a/usr/src/uts/common/os/cred.c
+++ b/usr/src/uts/common/os/cred.c
@@ -75,13 +75,6 @@ typedef struct ephemeral_zsd {
cred_t *eph_nobody;
} ephemeral_zsd_t;
-/* Supplemental groups list. */
-typedef struct credgrp {
- uint_t crg_ref;
- uint_t crg_ngroups;
- gid_t crg_groups[1];
-} credgrp_t;
-
static void crgrphold(credgrp_t *);
#define CREDGRPSZ(ngrp) (sizeof (credgrp_t) + ((ngrp - 1) * sizeof (gid_t)))
diff --git a/usr/src/uts/common/sys/cred_impl.h b/usr/src/uts/common/sys/cred_impl.h
index fc2fa34e43..e102f60e7a 100644
--- a/usr/src/uts/common/sys/cred_impl.h
+++ b/usr/src/uts/common/sys/cred_impl.h
@@ -19,6 +19,7 @@
* CDDL HEADER END
*/
/*
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -64,6 +65,13 @@ struct zone; /* forward reference */
struct ts_label_s; /* forward reference */
struct credklpd; /* forward reference */
+/* Supplemental groups list. */
+typedef struct credgrp {
+ uint_t crg_ref;
+ uint_t crg_ngroups;
+ gid_t crg_groups[1];
+} credgrp_t;
+
struct cred {
uint_t cr_ref; /* reference count */
uid_t cr_uid; /* effective user id */
@@ -78,7 +86,7 @@ struct cred {
struct ts_label_s *cr_label; /* pointer to the effective label */
struct credklpd *cr_klpd; /* pointer to the cred's klpd */
credsid_t *cr_ksid; /* pointer to SIDs */
- struct credgrp *cr_grps; /* supplemental groups */
+ credgrp_t *cr_grps; /* supplemental groups */
/* audit info is defined dynamically */
/* and valid only when audit enabled */
/* auditinfo_addr_t cr_auinfo; audit info */
diff --git a/usr/src/uts/common/sys/ksocket.h b/usr/src/uts/common/sys/ksocket.h
index df15b12c08..dfe25eec76 100644
--- a/usr/src/uts/common/sys/ksocket.h
+++ b/usr/src/uts/common/sys/ksocket.h
@@ -19,6 +19,7 @@
* CDDL HEADER END
*/
/*
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
*/
@@ -113,6 +114,7 @@ extern int ksocket_getpeername(ksocket_t, struct sockaddr *, socklen_t *,
extern int ksocket_getsockname(ksocket_t, struct sockaddr *, socklen_t *,
struct cred *);
extern int ksocket_ioctl(ksocket_t, int, intptr_t, int *, struct cred *);
+extern int ksocket_spoll(ksocket_t, int, short, short *, struct cred *);
extern int ksocket_setcallbacks(ksocket_t, ksocket_callbacks_t *, void *,
struct cred *);
extern int ksocket_close(ksocket_t, struct cred *);