*******************************************************************************/
-/* $OpenBSD: if_em_hw.c,v 1.110 2021/01/24 10:21:43 jsg Exp $ */
+/* $OpenBSD: if_em_hw.c,v 1.111 2021/12/14 10:48:10 patrick Exp $ */
/*
* if_em_hw.c Shared functions for accessing and configuring the MAC
*/
return E1000_SUCCESS;
}
+/**
+ * em_set_sfp_media_type_82575 - derives SFP module media type.
+ * @hw: pointer to the HW structure
+ *
+ * The media type is chosen based on SFP module.
+ * compatibility flags retrieved from SFP ID EEPROM.
+ **/
+STATIC int32_t em_set_sfp_media_type_82575(struct em_hw *hw)
+{
+ struct sfp_e1000_flags eth_flags;
+ int32_t ret_val = E1000_ERR_CONFIG;
+ uint32_t ctrl_ext = 0;
+ uint8_t tranceiver_type = 0;
+ int32_t timeout = 3;
+
+ /* Turn I2C interface ON and power on sfp cage */
+ ctrl_ext = E1000_READ_REG(hw, CTRL_EXT);
+ ctrl_ext &= ~E1000_CTRL_EXT_SDP3_DATA;
+ E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext | E1000_CTRL_I2C_ENA);
+
+ E1000_WRITE_FLUSH(hw);
+
+ /* Read SFP module data */
+ while (timeout) {
+ ret_val = em_read_sfp_data_byte(hw,
+ E1000_I2CCMD_SFP_DATA_ADDR(E1000_SFF_IDENTIFIER_OFFSET),
+ &tranceiver_type);
+ if (ret_val == E1000_SUCCESS)
+ break;
+ msec_delay(100);
+ timeout--;
+ }
+ if (ret_val != E1000_SUCCESS)
+ goto out;
+
+ ret_val = em_read_sfp_data_byte(hw,
+ E1000_I2CCMD_SFP_DATA_ADDR(E1000_SFF_ETH_FLAGS_OFFSET),
+ (uint8_t *)ð_flags);
+ if (ret_val != E1000_SUCCESS)
+ goto out;
+
+ /* Check if there is some SFP module plugged and powered */
+ if ((tranceiver_type == E1000_SFF_IDENTIFIER_SFP) ||
+ (tranceiver_type == E1000_SFF_IDENTIFIER_SFF)) {
+ if (eth_flags.e1000_base_lx || eth_flags.e1000_base_sx) {
+ hw->media_type = em_media_type_internal_serdes;
+ } else if (eth_flags.e100_base_fx || eth_flags.e100_base_lx) {
+ hw->media_type = em_media_type_internal_serdes;
+ hw->sgmii_active = TRUE;
+ } else if (eth_flags.e1000_base_t) {
+ hw->media_type = em_media_type_copper;
+ hw->sgmii_active = TRUE;
+ } else {
+ DEBUGOUT("PHY module has not been recognized\n");
+ ret_val = E1000_ERR_CONFIG;
+ goto out;
+ }
+ } else {
+ ret_val = E1000_ERR_CONFIG;
+ goto out;
+ }
+ ret_val = E1000_SUCCESS;
+out:
+ /* Restore I2C interface setting */
+ E1000_WRITE_REG(hw, CTRL_EXT, ctrl_ext);
+ return ret_val;
+}
+
+
/*****************************************************************************
* Set media type and TBI compatibility.
*
void
em_set_media_type(struct em_hw *hw)
{
- uint32_t status, ctrl_ext;
+ uint32_t status, ctrl_ext, mdic;
DEBUGFUNC("em_set_media_type");
if (hw->mac_type != em_82543) {
hw->mac_type == em_82576 ||
hw->mac_type == em_i210 || hw->mac_type == em_i350) {
hw->media_type = em_media_type_copper;
-
+ hw->sgmii_active = FALSE;
+
ctrl_ext = E1000_READ_REG(hw, CTRL_EXT);
switch (ctrl_ext & E1000_CTRL_EXT_LINK_MODE_MASK) {
- case E1000_CTRL_EXT_LINK_MODE_SGMII:
+ case E1000_CTRL_EXT_LINK_MODE_1000BASE_KX:
+ hw->media_type = em_media_type_internal_serdes;
ctrl_ext |= E1000_CTRL_I2C_ENA;
break;
- case E1000_CTRL_EXT_LINK_MODE_1000BASE_KX:
+ case E1000_CTRL_EXT_LINK_MODE_SGMII:
+ mdic = EM_READ_REG(hw, E1000_MDICNFG);
+ ctrl_ext |= E1000_CTRL_I2C_ENA;
+ if (mdic & E1000_MDICNFG_EXT_MDIO) {
+ hw->media_type = em_media_type_copper;
+ hw->sgmii_active = TRUE;
+ break;
+ }
+ /* FALLTHROUGH */
case E1000_CTRL_EXT_LINK_MODE_PCIE_SERDES:
- hw->media_type = em_media_type_internal_serdes;
ctrl_ext |= E1000_CTRL_I2C_ENA;
+ if (em_set_sfp_media_type_82575(hw) != 0) {
+ hw->media_type = em_media_type_internal_serdes;
+ if ((ctrl_ext & E1000_CTRL_EXT_LINK_MODE_MASK) ==
+ E1000_CTRL_EXT_LINK_MODE_SGMII) {
+ hw->media_type = em_media_type_copper;
+ hw->sgmii_active = TRUE;
+ }
+ }
+
+ ctrl_ext &= ~E1000_CTRL_EXT_LINK_MODE_MASK;
+ if (hw->sgmii_active)
+ ctrl_ext |= E1000_CTRL_EXT_LINK_MODE_SGMII;
+ else
+ ctrl_ext |= E1000_CTRL_EXT_LINK_MODE_PCIE_SERDES;
break;
default:
ctrl_ext &= ~E1000_CTRL_I2C_ENA;
{
uint32_t reg;
+ if (hw->media_type != em_media_type_internal_serdes &&
+ hw->sgmii_active == FALSE)
+ return;
+
/* Enable PCS to turn on link */
reg = E1000_READ_REG(hw, PCS_CFG0);
reg |= E1000_PCS_CFG_PCS_EN;
uint32_t signal = 0;
int32_t ret_val;
DEBUGFUNC("em_setup_fiber_serdes_link");
+
+ if (hw->media_type != em_media_type_internal_serdes &&
+ hw->sgmii_active == FALSE)
+ return -E1000_ERR_CONFIG;
+
/*
* On 82571 and 82572 Fiber connections, SerDes loopback mode
* persists until explicitly turned off or a power cycle is
uint32_t mdic = 0;
DEBUGFUNC("em_read_phy_reg_ex");
+ /* SGMII active is only set on some specific chips */
+ if (hw->sgmii_active && !em_sgmii_uses_mdio_82575(hw)) {
+ if (reg_addr > E1000_MAX_SGMII_PHY_REG_ADDR) {
+ DEBUGOUT1("PHY Address %d is out of range\n", reg_addr);
+ return -E1000_ERR_PARAM;
+ }
+ return em_read_phy_reg_i2c(hw, reg_addr, phy_data);
+ }
if (reg_addr > MAX_PHY_REG_ADDRESS) {
DEBUGOUT1("PHY Address %d is out of range\n", reg_addr);
return -E1000_ERR_PARAM;
uint32_t mdic = 0;
DEBUGFUNC("em_write_phy_reg_ex");
+ /* SGMII active is only set on some specific chips */
+ if (hw->sgmii_active && !em_sgmii_uses_mdio_82575(hw)) {
+ if (reg_addr > E1000_MAX_SGMII_PHY_REG_ADDR) {
+ DEBUGOUT1("PHY Address %d is out of range\n", reg_addr);
+ return -E1000_ERR_PARAM;
+ }
+ return em_write_phy_reg_i2c(hw, reg_addr, phy_data);
+ }
if (reg_addr > MAX_PHY_REG_ADDRESS) {
DEBUGOUT1("PHY Address %d is out of range\n", reg_addr);
return -E1000_ERR_PARAM;
return E1000_SUCCESS;
}
+/**
+ * em_sgmii_uses_mdio_82575 - Determine if I2C pins are for external MDIO
+ * @hw: pointer to the HW structure
+ *
+ * Called to determine if the I2C pins are being used for I2C or as an
+ * external MDIO interface since the two options are mutually exclusive.
+ **/
+int em_sgmii_uses_mdio_82575(struct em_hw *hw)
+{
+ uint32_t reg = 0;
+ int ext_mdio = 0;
+
+ DEBUGFUNC("em_sgmii_uses_mdio_82575");
+
+ switch (hw->mac_type) {
+ case em_82575:
+ case em_82576:
+ reg = E1000_READ_REG(hw, MDIC);
+ ext_mdio = !!(reg & E1000_MDIC_DEST);
+ break;
+ case em_82580:
+ case em_i350:
+ case em_i210:
+ reg = E1000_READ_REG(hw, MDICNFG);
+ ext_mdio = !!(reg & E1000_MDICNFG_EXT_MDIO);
+ break;
+ default:
+ break;
+ }
+ return ext_mdio;
+}
+
+/**
+ * em_read_phy_reg_i2c - Read PHY register using i2c
+ * @hw: pointer to the HW structure
+ * @offset: register offset to be read
+ * @data: pointer to the read data
+ *
+ * Reads the PHY register at offset using the i2c interface and stores the
+ * retrieved information in data.
+ **/
+int32_t em_read_phy_reg_i2c(struct em_hw *hw, uint32_t offset, uint16_t *data)
+{
+ uint32_t i, i2ccmd = 0;
+
+ DEBUGFUNC("em_read_phy_reg_i2c");
+
+ /* Set up Op-code, Phy Address, and register address in the I2CCMD
+ * register. The MAC will take care of interfacing with the
+ * PHY to retrieve the desired data.
+ */
+ i2ccmd = ((offset << E1000_I2CCMD_REG_ADDR_SHIFT) |
+ (hw->phy_addr << E1000_I2CCMD_PHY_ADDR_SHIFT) |
+ (E1000_I2CCMD_OPCODE_READ));
+
+ E1000_WRITE_REG(hw, I2CCMD, i2ccmd);
+
+ /* Poll the ready bit to see if the I2C read completed */
+ for (i = 0; i < E1000_I2CCMD_PHY_TIMEOUT; i++) {
+ usec_delay(50);
+ i2ccmd = E1000_READ_REG(hw, I2CCMD);
+ if (i2ccmd & E1000_I2CCMD_READY)
+ break;
+ }
+ if (!(i2ccmd & E1000_I2CCMD_READY)) {
+ DEBUGOUT("I2CCMD Read did not complete\n");
+ return -E1000_ERR_PHY;
+ }
+ if (i2ccmd & E1000_I2CCMD_ERROR) {
+ DEBUGOUT("I2CCMD Error bit set\n");
+ return -E1000_ERR_PHY;
+ }
+
+ /* Need to byte-swap the 16-bit value. */
+ *data = ((i2ccmd >> 8) & 0x00FF) | ((i2ccmd << 8) & 0xFF00);
+
+ return E1000_SUCCESS;
+}
+
+/**
+ * em_write_phy_reg_i2c - Write PHY register using i2c
+ * @hw: pointer to the HW structure
+ * @offset: register offset to write to
+ * @data: data to write at register offset
+ *
+ * Writes the data to PHY register at the offset using the i2c interface.
+ **/
+int32_t em_write_phy_reg_i2c(struct em_hw *hw, uint32_t offset, uint16_t data)
+{
+ uint32_t i, i2ccmd = 0;
+ uint16_t phy_data_swapped;
+
+ DEBUGFUNC("em_write_phy_reg_i2c");
+
+ /* Prevent overwritting SFP I2C EEPROM which is at A0 address.*/
+ if ((hw->phy_addr == 0) || (hw->phy_addr > 7)) {
+ DEBUGOUT1("PHY I2C Address %d is out of range.\n",
+ hw->phy_addr);
+ return -E1000_ERR_CONFIG;
+ }
+
+ /* Swap the data bytes for the I2C interface */
+ phy_data_swapped = ((data >> 8) & 0x00FF) | ((data << 8) & 0xFF00);
+
+ /* Set up Op-code, Phy Address, and register address in the I2CCMD
+ * register. The MAC will take care of interfacing with the
+ * PHY to retrieve the desired data.
+ */
+ i2ccmd = ((offset << E1000_I2CCMD_REG_ADDR_SHIFT) |
+ (hw->phy_addr << E1000_I2CCMD_PHY_ADDR_SHIFT) |
+ E1000_I2CCMD_OPCODE_WRITE |
+ phy_data_swapped);
+
+ E1000_WRITE_REG(hw, I2CCMD, i2ccmd);
+
+ /* Poll the ready bit to see if the I2C read completed */
+ for (i = 0; i < E1000_I2CCMD_PHY_TIMEOUT; i++) {
+ usec_delay(50);
+ i2ccmd = E1000_READ_REG(hw, I2CCMD);
+ if (i2ccmd & E1000_I2CCMD_READY)
+ break;
+ }
+ if (!(i2ccmd & E1000_I2CCMD_READY)) {
+ DEBUGOUT("I2CCMD Write did not complete\n");
+ return -E1000_ERR_PHY;
+ }
+ if (i2ccmd & E1000_I2CCMD_ERROR) {
+ DEBUGOUT("I2CCMD Error bit set\n");
+ return -E1000_ERR_PHY;
+ }
+
+ return E1000_SUCCESS;
+}
+
+/**
+ * em_read_sfp_data_byte - Reads SFP module data.
+ * @hw: pointer to the HW structure
+ * @offset: byte location offset to be read
+ * @data: read data buffer pointer
+ *
+ * Reads one byte from SFP module data stored
+ * in SFP resided EEPROM memory or SFP diagnostic area.
+ * Function should be called with
+ * E1000_I2CCMD_SFP_DATA_ADDR(<byte offset>) for SFP module database access
+ * E1000_I2CCMD_SFP_DIAG_ADDR(<byte offset>) for SFP diagnostics parameters
+ * access
+ **/
+int32_t em_read_sfp_data_byte(struct em_hw *hw, uint16_t offset, uint8_t *data)
+{
+ uint32_t i = 0;
+ uint32_t i2ccmd = 0;
+ uint32_t data_local = 0;
+
+ DEBUGFUNC("em_read_sfp_data_byte");
+
+ if (offset > E1000_I2CCMD_SFP_DIAG_ADDR(255)) {
+ DEBUGOUT("I2CCMD command address exceeds upper limit\n");
+ return -E1000_ERR_PHY;
+ }
+
+ /* Set up Op-code, EEPROM Address,in the I2CCMD
+ * register. The MAC will take care of interfacing with the
+ * EEPROM to retrieve the desired data.
+ */
+ i2ccmd = ((offset << E1000_I2CCMD_REG_ADDR_SHIFT) |
+ E1000_I2CCMD_OPCODE_READ);
+
+ E1000_WRITE_REG(hw, I2CCMD, i2ccmd);
+
+ /* Poll the ready bit to see if the I2C read completed */
+ for (i = 0; i < E1000_I2CCMD_PHY_TIMEOUT; i++) {
+ usec_delay(50);
+ data_local = E1000_READ_REG(hw, I2CCMD);
+ if (data_local & E1000_I2CCMD_READY)
+ break;
+ }
+ if (!(data_local & E1000_I2CCMD_READY)) {
+ DEBUGOUT("I2CCMD Read did not complete\n");
+ return -E1000_ERR_PHY;
+ }
+ if (data_local & E1000_I2CCMD_ERROR) {
+ DEBUGOUT("I2CCMD Error bit set\n");
+ return -E1000_ERR_PHY;
+ }
+ *data = (uint8_t) data_local & 0xFF;
+
+ return E1000_SUCCESS;
+}
+
/******************************************************************************
* Returns the PHY to the power-on reset state
*
hw->phy_id == I210_I_PHY_ID ||
hw->phy_id == I347AT4_E_PHY_ID ||
hw->phy_id == I350_I_PHY_ID ||
+ hw->phy_id == M88E1111_I_PHY_ID ||
hw->phy_id == M88E1112_E_PHY_ID ||
hw->phy_id == M88E1543_E_PHY_ID ||
hw->phy_id == M88E1512_E_PHY_ID) {
uint32_t mdic;
mdic = EM_READ_REG(hw, E1000_MDICNFG);
- mdic &= E1000_MDICNFG_PHY_MASK;
- hw->phy_addr = mdic >> E1000_MDICNFG_PHY_SHIFT;
- DEBUGOUT1("MDICNFG PHY ADDR %d",
- mdic >> E1000_MDICNFG_PHY_SHIFT);
+ if (mdic & E1000_MDICNFG_EXT_MDIO) {
+ mdic &= E1000_MDICNFG_PHY_MASK;
+ hw->phy_addr = mdic >> E1000_MDICNFG_PHY_SHIFT;
+ DEBUGOUT1("MDICNFG PHY ADDR %d",
+ mdic >> E1000_MDICNFG_PHY_SHIFT);
+ }
match = TRUE;
}
break;
uint32_t ctrl_ext = EM_READ_REG(hw, E1000_CTRL_EXT);
EM_WRITE_REG(hw, E1000_CTRL_EXT,
ctrl_ext & ~E1000_CTRL_EXT_SDP3_DATA);
- delay(300);
+ E1000_WRITE_FLUSH(hw);
+ msec_delay(300);
}
/* Read the PHY ID Registers to identify which PHY is onboard. */
- for (i = 1; i < 4; i++) {
+ for (i = 1; i < 8; i++) {
/*
* hw->phy_addr may be modified down in the call stack,
* we can't use it as loop variable.
*******************************************************************************/
-/* $OpenBSD: if_em_hw.h,v 1.84 2021/01/24 10:21:43 jsg Exp $ */
+/* $OpenBSD: if_em_hw.h,v 1.85 2021/12/14 10:48:10 patrick Exp $ */
/* $FreeBSD: if_em_hw.h,v 1.15 2005/05/26 23:32:02 tackerman Exp $ */
/* if_em_hw.h
int32_t em_phy_get_info(struct em_hw *hw, struct em_phy_info *phy_info);
int32_t em_validate_mdi_setting(struct em_hw *hw);
void em_phy_powerdown_workaround(struct em_hw *hw);
+int em_sgmii_uses_mdio_82575(struct em_hw *);
+int32_t em_read_phy_reg_i2c(struct em_hw *, uint32_t, uint16_t *);
+int32_t em_write_phy_reg_i2c(struct em_hw *, uint32_t, uint16_t);
+int32_t em_read_sfp_data_byte(struct em_hw *, uint16_t, uint8_t *);
/* EEPROM Functions */
int32_t em_init_eeprom_params(struct em_hw *hw);
#define E1000_FLSWDATA 0x01034 /* FLASH data register */
#define E1000_FLSWCNT 0x01038 /* FLASH Access Counter */
#define E1000_FLOP 0x0103C /* FLASH Opcode Register */
+#define E1000_I2CCMD 0x01028 /* SFPI2C Command Register - RW */
#define E1000_ERT 0x02008 /* Early Rx Threshold - RW */
#define E1000_FCRTL 0x02160 /* Flow Control Receive Threshold Low - RW */
#define E1000_FCRTH 0x02168 /* Flow Control Receive Threshold High - RW */
uint16_t swfw;
boolean_t eee_enable;
int sw_flag;
+ boolean_t sgmii_active;
};
#define E1000_EEPROM_SWDPIN0 0x0001 /* SWDPIN 0 EEPROM Value */
#define E1000_CTRL_LANPHYPC_VALUE 0x00020000 /* SW value of LANPHYPC */
#define E1000_CTRL_EXT_FORCE_SMBUS 0x00000800 /* Force SMBus mode */
#define E1000_CTRL_EXT_PHYPDEN 0x00100000
+#define E1000_I2CCMD_REG_ADDR_SHIFT 16
+#define E1000_I2CCMD_PHY_ADDR_SHIFT 24
+#define E1000_I2CCMD_OPCODE_READ 0x08000000
+#define E1000_I2CCMD_OPCODE_WRITE 0x00000000
+#define E1000_I2CCMD_READY 0x20000000
+#define E1000_I2CCMD_ERROR 0x80000000
+#define E1000_I2CCMD_SFP_DATA_ADDR(a) (0x0000 + (a))
+#define E1000_I2CCMD_SFP_DIAG_ADDR(a) (0x0100 + (a))
+#define E1000_MAX_SGMII_PHY_REG_ADDR 255
+#define E1000_I2CCMD_PHY_TIMEOUT 200
#define E1000_CTRL_SWDPIN0 0x00040000 /* SWDPIN 0 value */
#define E1000_CTRL_SWDPIN1 0x00080000 /* SWDPIN 1 value */
(((offset) >> (PHY_UPPER_SHIFT - PHY_PAGE_SHIFT)) &\
~MAX_PHY_REG_ADDRESS)))
+/* SFP modules ID memory locations */
+#define E1000_SFF_IDENTIFIER_OFFSET 0x00
+#define E1000_SFF_IDENTIFIER_SFF 0x02
+#define E1000_SFF_IDENTIFIER_SFP 0x03
+
+#define E1000_SFF_ETH_FLAGS_OFFSET 0x06
+/* Flags for SFP modules compatible with ETH up to 1Gb */
+struct sfp_e1000_flags {
+ uint8_t e1000_base_sx:1;
+ uint8_t e1000_base_lx:1;
+ uint8_t e1000_base_cx:1;
+ uint8_t e1000_base_t:1;
+ uint8_t e100_base_lx:1;
+ uint8_t e100_base_fx:1;
+ uint8_t e10_base_bx10:1;
+ uint8_t e10_base_px:1;
+};
+
+/* Vendor OUIs: format of OUI is 0x[byte0][byte1][byte2][00] */
+#define E1000_SFF_VENDOR_OUI_TYCO 0x00407600
+#define E1000_SFF_VENDOR_OUI_FTL 0x00906500
+#define E1000_SFF_VENDOR_OUI_AVAGO 0x00176A00
+#define E1000_SFF_VENDOR_OUI_INTEL 0x001B2100
+
#endif /* _EM_HW_H_ */