summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Gray <jsg@cvs.openbsd.org>2010-08-03 16:39:35 +0000
committerJonathan Gray <jsg@cvs.openbsd.org>2010-08-03 16:39:35 +0000
commited57c6450ff65024153da7443b7c39b9bc3e0387 (patch)
treefe471dc4dd1da684aaaa08c1275d59fda1a87654
parentea7de5d651320b6c9fff1f9b26f41c8829c87ede (diff)
Add support for 82576 fiber adapters based on Intel code in FreeBSD.
Thanks to Frédéric URBAN for setting up a test network to develop this on. Tested by various people on copper adapters and fiber support also tested by mickey. ok deraadt@
-rw-r--r--sys/dev/pci/if_em_hw.c139
-rw-r--r--sys/dev/pci/if_em_hw.h24
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