From 2330eb53a7a9964f5d3383a2e58e32c94703e81f Mon Sep 17 00:00:00 2001 From: jsg Date: Tue, 3 Aug 2010 16:39:33 +0000 Subject: [PATCH] =?utf8?q?Add=20support=20for=2082576=20fiber=20adapters?= =?utf8?q?=20based=20on=20Intel=20code=20in=20FreeBSD.=20Thanks=20to=20Fr?= =?utf8?q?=C3=A9d=C3=A9ric=20URBAN=20for=20setting=20up=20a=20test=20netwo?= =?utf8?q?rk=20to=20develop=20this=20on.=20Tested=20by=20various=20people?= =?utf8?q?=20on=20copper=20adapters=20and=20fiber=20support=20also=20teste?= =?utf8?q?d=20by=20mickey.?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit ok deraadt@ --- sys/dev/pci/if_em_hw.c | 139 +++++++++++++++++++++++++++++++++++++++-- sys/dev/pci/if_em_hw.h | 24 ++++++- 2 files changed, 155 insertions(+), 8 deletions(-) diff --git a/sys/dev/pci/if_em_hw.c b/sys/dev/pci/if_em_hw.c index bc0287f7356..0c5d67de4d6 100644 --- a/sys/dev/pci/if_em_hw.c +++ b/sys/dev/pci/if_em_hw.c @@ -31,7 +31,7 @@ *******************************************************************************/ -/* $OpenBSD: if_em_hw.c,v 1.55 2010/07/13 21:55:52 jsg Exp $ */ +/* $OpenBSD: if_em_hw.c,v 1.56 2010/08/03 16:39:33 jsg Exp $ */ /* * if_em_hw.c Shared functions for accessing and configuring the MAC */ @@ -176,7 +176,9 @@ int32_t em_access_phy_debug_regs_hv(struct em_hw *, uint32_t, int32_t em_access_phy_reg_hv(struct em_hw *, uint32_t, uint16_t *, boolean_t); int32_t em_oem_bits_config_pchlan(struct em_hw *, boolean_t); - +void em_power_up_serdes_link_82575(struct em_hw *); +int32_t em_get_pcs_speed_and_duplex_82575(struct em_hw *, uint16_t *, + uint16_t *); /* IGP cable length table */ static const uint16_t @@ -590,13 +592,35 @@ em_set_mac_type(struct em_hw *hw) void em_set_media_type(struct em_hw *hw) { - uint32_t status; + uint32_t status, ctrl_ext; DEBUGFUNC("em_set_media_type"); if (hw->mac_type != em_82543) { /* tbi_compatibility is only valid on 82543 */ hw->tbi_compatibility_en = FALSE; } + + if (hw->mac_type == em_82575) { + hw->media_type = em_media_type_copper; + + ctrl_ext = E1000_READ_REG(hw, CTRL_EXT); + switch (ctrl_ext & E1000_CTRL_EXT_LINK_MODE_MASK) { + case E1000_CTRL_EXT_LINK_MODE_SGMII: + ctrl_ext |= E1000_CTRL_I2C_ENA; + break; + case E1000_CTRL_EXT_LINK_MODE_1000BASE_KX: + case E1000_CTRL_EXT_LINK_MODE_PCIE_SERDES: + hw->media_type = em_media_type_internal_serdes; + ctrl_ext |= E1000_CTRL_I2C_ENA; + break; + default: + ctrl_ext &= ~E1000_CTRL_I2C_ENA; + break; + } + E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext); + return; + } + switch (hw->device_id) { case E1000_DEV_ID_82545GM_SERDES: case E1000_DEV_ID_82546GB_SERDES: @@ -1308,7 +1332,8 @@ em_adjust_serdes_amplitude(struct em_hw *hw) int32_t ret_val; DEBUGFUNC("em_adjust_serdes_amplitude"); - if (hw->media_type != em_media_type_internal_serdes) + if (hw->media_type != em_media_type_internal_serdes || + hw->mac_type == em_82575) return E1000_SUCCESS; switch (hw->mac_type) { @@ -1512,6 +1537,26 @@ em_setup_link(struct em_hw *hw) return ret_val; } +void +em_power_up_serdes_link_82575(struct em_hw *hw) +{ + uint32_t reg; + + /* Enable PCS to turn on link */ + reg = E1000_READ_REG(hw, PCS_CFG0); + reg |= E1000_PCS_CFG_PCS_EN; + E1000_WRITE_REG(hw, PCS_CFG0, reg); + + /* Power up the laser */ + reg = E1000_READ_REG(hw, CTRL_EXT); + reg &= ~E1000_CTRL_EXT_SDP3_DATA; + E1000_WRITE_REG(hw, CTRL_EXT, reg); + + /* flush the write to verify completion */ + E1000_WRITE_FLUSH(hw); + delay(5); +} + /****************************************************************************** * Sets up link for a fiber based or serdes based adapter * @@ -1524,7 +1569,7 @@ em_setup_link(struct em_hw *hw) static int32_t em_setup_fiber_serdes_link(struct em_hw *hw) { - uint32_t ctrl; + uint32_t ctrl, reg; uint32_t status; uint32_t txcw = 0; uint32_t i; @@ -1538,8 +1583,13 @@ em_setup_fiber_serdes_link(struct em_hw *hw) * Therefore, we ensure loopback mode is disabled during * initialization. */ - if (hw->mac_type == em_82571 || hw->mac_type == em_82572) + if (hw->mac_type == em_82571 || hw->mac_type == em_82572 || + hw->mac_type == em_82575) E1000_WRITE_REG(hw, SCTL, E1000_DISABLE_SERDES_LOOPBACK); + + if (hw->mac_type == em_82575) + em_power_up_serdes_link_82575(hw); + /* * On adapters with a MAC newer than 82544, SWDP 1 will be set when * the optics detect a signal. On older adapters, it will be cleared @@ -1558,6 +1608,16 @@ em_setup_fiber_serdes_link(struct em_hw *hw) /* Take the link out of reset */ ctrl &= ~(E1000_CTRL_LRST); + if (hw->mac_type == em_82575) { + /* set both sw defined pins on 82575/82576*/ + ctrl |= E1000_CTRL_SWDPIN0 | E1000_CTRL_SWDPIN1; + + /* Set switch control to serdes energy detect */ + reg = E1000_READ_REG(hw, CONNSW); + reg |= E1000_CONNSW_ENRGSRC; + E1000_WRITE_REG(hw, CONNSW, reg); + } + /* Adjust VCO speed to improve BER performance */ ret_val = em_set_vco_speed(hw); if (ret_val) @@ -3380,6 +3440,16 @@ em_check_for_link(struct em_hw *hw) int32_t ret_val; uint16_t phy_data; DEBUGFUNC("em_check_for_link"); + uint16_t speed, duplex; + + if (hw->mac_type == em_82575 && + hw->media_type != em_media_type_copper) { + ret_val = em_get_pcs_speed_and_duplex_82575(hw, &speed, + &duplex); + hw->get_link_status = hw->serdes_link_down; + + return (ret_val); + } ctrl = E1000_READ_REG(hw, CTRL); status = E1000_READ_REG(hw, STATUS); @@ -3638,6 +3708,52 @@ em_check_for_link(struct em_hw *hw) return E1000_SUCCESS; } +int32_t +em_get_pcs_speed_and_duplex_82575(struct em_hw *hw, uint16_t *speed, + uint16_t *duplex) +{ + uint32_t pcs; + + hw->serdes_link_down = TRUE; + *speed = 0; + *duplex = 0; + + /* + * Read the PCS Status register for link state. For non-copper mode, + * the status register is not accurate. The PCS status register is + * used instead. + */ + pcs = E1000_READ_REG(hw, PCS_LSTAT); + + /* + * The link up bit determines when link is up on autoneg. The sync ok + * gets set once both sides sync up and agree upon link. Stable link + * can be determined by checking for both link up and link sync ok + */ + if ((pcs & E1000_PCS_LSTS_LINK_OK) && (pcs & E1000_PCS_LSTS_SYNK_OK)) { + hw->serdes_link_down = FALSE; + + /* Detect and store PCS speed */ + if (pcs & E1000_PCS_LSTS_SPEED_1000) { + *speed = SPEED_1000; + } else if (pcs & E1000_PCS_LSTS_SPEED_100) { + *speed = SPEED_100; + } else { + *speed = SPEED_10; + } + + /* Detect and store PCS duplex */ + if (pcs & E1000_PCS_LSTS_DUPLEX_FULL) { + *duplex = FULL_DUPLEX; + } else { + *duplex = HALF_DUPLEX; + } + } + + return (0); +} + + /****************************************************************************** * Detects the current speed and duplex settings of the hardware. * @@ -3653,6 +3769,9 @@ em_get_speed_and_duplex(struct em_hw *hw, uint16_t *speed, uint16_t *duplex) uint16_t phy_data; DEBUGFUNC("em_get_speed_and_duplex"); + if (hw->mac_type == em_82575 && hw->media_type != em_media_type_copper) + return em_get_pcs_speed_and_duplex_82575(hw, speed, duplex); + if (hw->mac_type >= em_82543) { status = E1000_READ_REG(hw, STATUS); if (status & E1000_STATUS_SPEED_1000) { @@ -4963,6 +5082,14 @@ em_detect_gig_phy(struct em_hw *hw) hw->phy_type = em_phy_undefined; return E1000_SUCCESS; } + + if ((hw->media_type == em_media_type_internal_serdes || + hw->media_type == em_media_type_fiber) && + hw->mac_type == em_82575) { + hw->phy_type = em_phy_undefined; + return E1000_SUCCESS; + } + /* * Up to 82543 (incl), we need reset the phy, or it might not get * detected diff --git a/sys/dev/pci/if_em_hw.h b/sys/dev/pci/if_em_hw.h index f78313a9932..1eef8edbdfc 100644 --- a/sys/dev/pci/if_em_hw.h +++ b/sys/dev/pci/if_em_hw.h @@ -31,7 +31,7 @@ *******************************************************************************/ -/* $OpenBSD: if_em_hw.h,v 1.44 2010/07/02 21:41:59 jsg Exp $ */ +/* $OpenBSD: if_em_hw.h,v 1.45 2010/08/03 16:39:34 jsg Exp $ */ /* $FreeBSD: if_em_hw.h,v 1.15 2005/05/26 23:32:02 tackerman Exp $ */ /* if_em_hw.h @@ -941,6 +941,7 @@ struct em_ffvt_entry { #define E1000_FCAL 0x00028 /* Flow Control Address Low - RW */ #define E1000_FCAH 0x0002C /* Flow Control Address High -RW */ #define E1000_FCT 0x00030 /* Flow Control Type - RW */ +#define E1000_CONNSW 0x00034 /* Copper/Fiber switch control - RW */ #define E1000_VET 0x00038 /* VLAN Ether Type - RW */ #define E1000_ICR 0x000C0 /* Interrupt Cause Read - R/clr */ #define E1000_ITR 0x000C4 /* Interrupt Throttling Rate - RW */ @@ -1092,6 +1093,9 @@ struct em_ffvt_entry { #define E1000_ICTXQMTC 0x0411C /* Interrupt Cause Tx Queue Minimum Threshold Count */ #define E1000_ICRXDMTC 0x04120 /* Interrupt Cause Rx Descriptor Minimum Threshold Count */ #define E1000_ICRXOC 0x04124 /* Interrupt Cause Receiver Overrun Count */ +#define E1000_PCS_CFG0 0x04200 /* PCS Configuration 0 - RW */ +#define E1000_PCS_LCTL 0x04208 /* PCS Link Control - RW */ +#define E1000_PCS_LSTAT 0x0420C /* PCS Link Status - RO */ #define E1000_RXCSUM 0x05000 /* RX Checksum Control - RW */ #define E1000_RFCTL 0x05008 /* Receive Filter Control*/ #define E1000_MTA 0x05200 /* Multicast Table Array - RW Array */ @@ -1155,6 +1159,7 @@ struct em_ffvt_entry { #define E1000_82542_FCAL E1000_FCAL #define E1000_82542_FCAH E1000_FCAH #define E1000_82542_FCT E1000_FCT +#define E1000_82542_CONNSW E1000_CONNSW #define E1000_82542_VET E1000_VET #define E1000_82542_RA 0x00040 #define E1000_82542_ICR E1000_ICR @@ -1347,6 +1352,9 @@ struct em_ffvt_entry { #define E1000_82542_ICRXDMTC E1000_ICRXDMTC #define E1000_82542_ICRXOC E1000_ICRXOC #define E1000_82542_HICR E1000_HICR +#define E1000_82542_PCS_CFG0 E1000_PCS_CFG0 +#define E1000_82542_PCS_LCTL E1000_PCS_LCTL +#define E1000_82542_PCS_LSTAT E1000_PCS_LSTAT #define E1000_82542_CPUVEC E1000_CPUVEC #define E1000_82542_MRQC E1000_MRQC @@ -1566,6 +1574,16 @@ struct em_hw { #define E1000_CTRL_VME 0x40000000 /* IEEE VLAN mode enable */ #define E1000_CTRL_PHY_RST 0x80000000 /* PHY Reset */ #define E1000_CTRL_SW2FW_INT 0x02000000 /* Initiate an interrupt to manageability engine */ +#define E1000_CTRL_I2C_ENA 0x02000000 /* I2C enable */ + +#define E1000_CONNSW_ENRGSRC 0x4 +#define E1000_PCS_CFG_PCS_EN 8 + +#define E1000_PCS_LSTS_LINK_OK 0x01 +#define E1000_PCS_LSTS_SPEED_100 0x02 +#define E1000_PCS_LSTS_SPEED_1000 0x04 +#define E1000_PCS_LSTS_DUPLEX_FULL 0x08 +#define E1000_PCS_LSTS_SYNK_OK 0x10 /* Device Status */ #define E1000_STATUS_FD 0x00000001 /* Full duplex.0=half,1=full */ @@ -1669,6 +1687,7 @@ struct em_hw { #define E1000_CTRL_EXT_PHY_INT E1000_CTRL_EXT_SDP5_DATA #define E1000_CTRL_EXT_SDP6_DATA 0x00000040 /* Value of SW Defineable Pin 6 */ #define E1000_CTRL_EXT_SDP7_DATA 0x00000080 /* Value of SW Defineable Pin 7 */ +#define E1000_CTRL_EXT_SDP3_DATA 0x00000080 /* Value of SW Defineable Pin 3 */ #define E1000_CTRL_EXT_SDP4_DIR 0x00000100 /* Direction of SDP4 0=in 1=out */ #define E1000_CTRL_EXT_SDP5_DIR 0x00000200 /* Direction of SDP5 0=in 1=out */ #define E1000_CTRL_EXT_SDP6_DIR 0x00000400 /* Direction of SDP6 0=in 1=out */ @@ -1682,7 +1701,8 @@ struct em_hw { #define E1000_CTRL_EXT_LINK_MODE_GMII 0x00000000 #define E1000_CTRL_EXT_LINK_MODE_TBI 0x00C00000 #define E1000_CTRL_EXT_LINK_MODE_KMRN 0x00000000 -#define E1000_CTRL_EXT_LINK_MODE_SERDES 0x00C00000 +#define E1000_CTRL_EXT_LINK_MODE_PCIE_SERDES 0x00C00000 +#define E1000_CTRL_EXT_LINK_MODE_1000BASE_KX 0x00400000 #define E1000_CTRL_EXT_LINK_MODE_SGMII 0x00800000 #define E1000_CTRL_EXT_WR_WMARK_MASK 0x03000000 #define E1000_CTRL_EXT_WR_WMARK_256 0x00000000 -- 2.20.1