diff options
Diffstat (limited to 'usr/src/uts/common')
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, - ®_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, ®_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, + ®_data); + if (ret_val) + goto out; - reg_addr |= phy_page; + ret_val = hw->nvm.ops.read(hw, (word_addr + i * 2 + 1), + 1, ®_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, ®); /* 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, ®); - - 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 *); |