diff options
-rw-r--r-- | Makefile | 13 | ||||
-rw-r--r-- | e1000_ethtool.c | 52 | ||||
-rw-r--r-- | e1000_hw.c | 372 | ||||
-rw-r--r-- | e1000_hw.h | 35 | ||||
-rw-r--r-- | e1000_main.c | 225 | ||||
-rwxr-xr-x | e1000_oem_phy.c | 2497 | ||||
-rwxr-xr-x | e1000_oem_phy.h | 235 | ||||
-rw-r--r-- | e1000_osdep.h | 8 | ||||
-rw-r--r-- | e1000_param.c | 8 | ||||
-rwxr-xr-x | gcu.h | 105 | ||||
-rwxr-xr-x | gcu_if.c | 371 | ||||
-rwxr-xr-x | gcu_if.h | 52 | ||||
-rwxr-xr-x | gcu_main.c | 454 | ||||
-rwxr-xr-x | gcu_reg.h | 68 | ||||
-rwxr-xr-x | kcompat.h | 753 |
15 files changed, 5174 insertions, 74 deletions
@@ -30,6 +30,15 @@ # Makefile for the Intel(R) PRO/1000 ethernet driver # -obj-$(CONFIG_E1000) += e1000.o +EXTRA_CFLAGS += -DEXTERNAL_MDIO + +gcu-objs := gcu_main.o gcu_if.o + +ifeq ($(CONFIG_E1000_EP80579),y) + obj-$(CONFIG_E1000) += gcu.o +endif -e1000-objs := e1000_main.o e1000_hw.o e1000_ethtool.o e1000_param.o + +e1000-objs := e1000_main.o e1000_hw.o e1000_ethtool.o e1000_param.o e1000_oem_phy.o + +obj-$(CONFIG_E1000) += e1000.o diff --git a/e1000_ethtool.c b/e1000_ethtool.c index c67e931..042a476 100644 --- a/e1000_ethtool.c +++ b/e1000_ethtool.c @@ -29,6 +29,7 @@ /* ethtool support for e1000 */ #include "e1000.h" +#include "e1000_oem_phy.h" #include <asm/uaccess.h> enum {NETDEV_STATS, E1000_STATS}; @@ -112,7 +113,8 @@ static int e1000_get_settings(struct net_device *netdev, struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; - if (hw->media_type == e1000_media_type_copper) { + if((hw->media_type == e1000_media_type_copper) || + (hw->media_type == e1000_media_type_oem)) { ecmd->supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | @@ -281,8 +283,10 @@ static int e1000_set_pauseparam(struct net_device *netdev, } else e1000_reset(adapter); } else - retval = ((hw->media_type == e1000_media_type_fiber) ? - e1000_setup_link(hw) : e1000_force_mac_fc(hw)); + retval = ((hw->media_type == e1000_media_type_fiber) + || (hw->media_type == e1000_media_type_oem + && !e1000_oem_phy_is_copper(&adapter->hw)) ? + e1000_setup_link(hw) : e1000_force_mac_fc(hw)); clear_bit(__E1000_RESETTING, &adapter->flags); return retval; @@ -432,6 +436,8 @@ static void e1000_get_regs(struct net_device *netdev, struct ethtool_regs *regs, regs_buff[22] = 0; /* phy receive errors (unavailable) */ regs_buff[23] = regs_buff[18]; /* mdix mode */ e1000_write_phy_reg(hw, IGP01E1000_PHY_PAGE_SELECT, 0x0); + } else if (hw->phy_type == e1000_phy_oem) { + e1000_oem_get_phy_regs(adapter, ®s_buff[0xd], (0xb)); } else { e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data); regs_buff[13] = (u32)phy_data; /* cable length */ @@ -813,7 +819,9 @@ static int e1000_reg_test(struct e1000_adapter *adapter, u64 *data) REG_SET_AND_CHECK(RCTL, before, 0xFFFFFFFF); REG_PATTERN_TEST(RDBAL, 0xFFFFFFF0, 0xFFFFFFFF); - REG_PATTERN_TEST(TXCW, 0xC000FFFF, 0x0000FFFF); + if(adapter->hw.mac_type != e1000_icp_xxxx){ + REG_PATTERN_TEST(TXCW, 0xC000FFFF, 0x0000FFFF); + } REG_PATTERN_TEST(TDBAL, 0xFFFFFFF0, 0xFFFFFFFF); REG_PATTERN_TEST(TIDV, 0x0000FFFF, 0x0000FFFF); value = E1000_RAR_ENTRIES; @@ -1320,6 +1328,9 @@ static int e1000_set_phy_loopback(struct e1000_adapter *adapter) case e1000_82540: case e1000_82545: case e1000_82545_rev_3: + case e1000_icp_xxxx: + return e1000_oem_phy_loopback(adapter); + break; case e1000_82546: case e1000_82546_rev_3: case e1000_82541: @@ -1362,8 +1373,11 @@ static int e1000_setup_loopback_test(struct e1000_adapter *adapter) ew32(RCTL, rctl); return 0; } - } else if (hw->media_type == e1000_media_type_copper) + } else if(adapter->hw.media_type == e1000_media_type_copper + || (adapter->hw.media_type == e1000_media_type_oem + && e1000_oem_phy_is_copper(&adapter->hw))){ return e1000_set_phy_loopback(adapter); + } return 7; } @@ -1393,6 +1407,10 @@ static void e1000_loopback_cleanup(struct e1000_adapter *adapter) } break; } + + if(adapter->hw.media_type == e1000_media_type_oem){ + e1000_oem_loopback_cleanup(adapter); + } } static void e1000_create_lbtest_frame(struct sk_buff *skb, @@ -1523,9 +1541,29 @@ static int e1000_link_test(struct e1000_adapter *adapter, u64 *data) if (hw->autoneg) /* if auto_neg is set wait for it */ msleep(4000); + if(adapter->hw.mac_type == e1000_icp_xxxx) + { + int isUp = 0; + if(e1000_oem_phy_is_link_up(&adapter->hw, &isUp) != E1000_SUCCESS) + { + printk("unable to determine Link Status!\n"); + } + else + { + if(isUp){ + *data = 0; + } + else{ + *data = 1; + } + } + } + else + { if (!(er32(STATUS) & E1000_STATUS_LU)) { *data = 1; } + } } return *data; } @@ -1775,6 +1813,10 @@ static int e1000_phys_id(struct net_device *netdev, u32 data) struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; + if(adapter->hw.mac_type == e1000_icp_xxxx){ + /* No LED control on ICP family of gigE controllers */ + return 0; + } if (!data) data = INT_MAX; @@ -30,28 +30,25 @@ * Shared functions for accessing and configuring the MAC */ +#include "e1000_oem_phy.h" #include "e1000_hw.h" +#include "e1000.h" static s32 e1000_check_downshift(struct e1000_hw *hw); static s32 e1000_check_polarity(struct e1000_hw *hw, e1000_rev_polarity *polarity); static void e1000_clear_hw_cntrs(struct e1000_hw *hw); static void e1000_clear_vfta(struct e1000_hw *hw); -static s32 e1000_config_dsp_after_link_change(struct e1000_hw *hw, - bool link_up); static s32 e1000_config_fc_after_link_up(struct e1000_hw *hw); static s32 e1000_detect_gig_phy(struct e1000_hw *hw); -static s32 e1000_get_auto_rd_done(struct e1000_hw *hw); static s32 e1000_get_cable_length(struct e1000_hw *hw, u16 *min_length, u16 *max_length); -static s32 e1000_get_phy_cfg_done(struct e1000_hw *hw); static s32 e1000_id_led_init(struct e1000_hw *hw); static void e1000_init_rx_addrs(struct e1000_hw *hw); static s32 e1000_phy_igp_get_info(struct e1000_hw *hw, struct e1000_phy_info *phy_info); static s32 e1000_phy_m88_get_info(struct e1000_hw *hw, struct e1000_phy_info *phy_info); -static s32 e1000_set_d3_lplu_state(struct e1000_hw *hw, bool active); static s32 e1000_wait_autoneg(struct e1000_hw *hw); static void e1000_write_reg_io(struct e1000_hw *hw, u32 offset, u32 value); static s32 e1000_set_phy_type(struct e1000_hw *hw); @@ -59,7 +56,7 @@ static void e1000_phy_init_script(struct e1000_hw *hw); static s32 e1000_setup_copper_link(struct e1000_hw *hw); static s32 e1000_setup_fiber_serdes_link(struct e1000_hw *hw); static s32 e1000_adjust_serdes_amplitude(struct e1000_hw *hw); -static s32 e1000_phy_force_speed_duplex(struct e1000_hw *hw); +s32 e1000_phy_force_speed_duplex(struct e1000_hw *hw); static s32 e1000_config_mac_to_phy(struct e1000_hw *hw); static void e1000_raise_mdi_clk(struct e1000_hw *hw, u32 *ctrl); static void e1000_lower_mdi_clk(struct e1000_hw *hw, u32 *ctrl); @@ -318,6 +315,21 @@ s32 e1000_set_mac_type(struct e1000_hw *hw) case E1000_DEV_ID_82547GI: hw->mac_type = e1000_82547_rev_2; break; + case E1000_DEV_ID_ICP_5040: + case E1000_DEV_ID_ICP_5041: + case E1000_DEV_ID_ICP_5042: + case E1000_DEV_ID_ICP_5043: + case E1000_DEV_ID_ICP_5044: + case E1000_DEV_ID_ICP_5045: + case E1000_DEV_ID_ICP_5046: + case E1000_DEV_ID_ICP_5047: + case E1000_DEV_ID_ICP_5048: + case E1000_DEV_ID_ICP_5049: + case E1000_DEV_ID_ICP_504A: + case E1000_DEV_ID_ICP_504B: + hw->mac_type = e1000_icp_xxxx; + break; + default: /* Should never have loaded on this device */ return -E1000_ERR_MAC_TYPE; @@ -366,6 +378,20 @@ void e1000_set_media_type(struct e1000_hw *hw) case E1000_DEV_ID_82546GB_SERDES: hw->media_type = e1000_media_type_internal_serdes; break; + case E1000_DEV_ID_ICP_5040: + case E1000_DEV_ID_ICP_5041: + case E1000_DEV_ID_ICP_5042: + case E1000_DEV_ID_ICP_5043: + case E1000_DEV_ID_ICP_5044: + case E1000_DEV_ID_ICP_5045: + case E1000_DEV_ID_ICP_5046: + case E1000_DEV_ID_ICP_5047: + case E1000_DEV_ID_ICP_5048: + case E1000_DEV_ID_ICP_5049: + case E1000_DEV_ID_ICP_504A: + case E1000_DEV_ID_ICP_504B: + hw->media_type = e1000_media_type_oem; + break; default: switch (hw->mac_type) { case e1000_82542_rev2_0: @@ -437,6 +463,15 @@ s32 e1000_reset_hw(struct e1000_hw *hw) msleep(5); } + if(hw->phy_type == e1000_phy_oem + && e1000_oem_phy_needs_reset_with_mac(hw)) + { + ret_val = e1000_oem_phy_hw_reset(hw); + if(ret_val){ + return ret_val; + } + } + /* Issue a global reset to the MAC. This will reset the chip's * transmit, receive, DMA, and link units. It will not effect * the current PCI configuration. The global reset bit is self- @@ -499,7 +534,8 @@ s32 e1000_reset_hw(struct e1000_hw *hw) } /* Disable HW ARPs on ASF enabled adapters */ - if (hw->mac_type >= e1000_82540) { + if (hw->mac_type >= e1000_82540 && + hw->mac_type != e1000_icp_xxxx) { manc = er32(MANC); manc &= ~(E1000_MANC_ARP_EN); ew32(MANC, manc); @@ -515,6 +551,12 @@ s32 e1000_reset_hw(struct e1000_hw *hw) ew32(LEDCTL, led_ctrl); } + /* default configure the oem phy */ + if(hw->phy_type == e1000_phy_oem + && e1000_oem_phy_needs_reset_with_mac(hw)) { + e1000_oem_phy_init_script(hw); + } + /* Clear interrupt mask to stop board from generating interrupts */ DEBUGOUT("Masking off all interrupts\n"); ew32(IMC, 0xffffffff); @@ -650,6 +692,7 @@ s32 e1000_init_hw(struct e1000_hw *hw) ew32(CTRL_EXT, ctrl_ext); } + return ret_val; } @@ -708,9 +751,18 @@ s32 e1000_setup_link(struct e1000_hw *hw) u32 ctrl_ext; s32 ret_val; u16 eeprom_data; + u16 eeprom_control2_reg_offset = 0; DEBUGFUNC("e1000_setup_link"); + /* for icp_xxxx style controllers, the init control 2 and 3 are packed into + * a single word, with the top byte being occupied by control 2 + */ + eeprom_control2_reg_offset = + hw->mac_type != e1000_icp_xxxx + ? EEPROM_INIT_CONTROL2_REG + : EEPROM_INIT_CONTROL3_ICP_xxxx(e1000_oem_get_phy_dev_number(hw)); + /* Read and store word 0x0F of the EEPROM. This word contains bits * that determine the hardware's default PAUSE (flow control) mode, * a bit that determines whether the HW defaults to enabling or @@ -720,7 +772,7 @@ s32 e1000_setup_link(struct e1000_hw *hw) * be initialized based on a value in the EEPROM. */ if (hw->fc == E1000_FC_DEFAULT) { - ret_val = e1000_read_eeprom(hw, EEPROM_INIT_CONTROL2_REG, + ret_val = e1000_read_eeprom(hw, eeprom_control2_reg_offset, 1, &eeprom_data); if (ret_val) { DEBUGOUT("EEPROM Read Error\n"); @@ -769,8 +821,19 @@ s32 e1000_setup_link(struct e1000_hw *hw) } /* Call the necessary subroutine to configure the link. */ - ret_val = (hw->media_type == e1000_media_type_copper) ? - e1000_setup_copper_link(hw) : e1000_setup_fiber_serdes_link(hw); + switch(hw->media_type) { + case e1000_media_type_copper: + ret_val = e1000_setup_copper_link(hw); + break; + + case e1000_media_type_oem: + ret_val = e1000_oem_setup_link(hw); + break; + + default: + ret_val = e1000_setup_fiber_serdes_link(hw); + break; + } /* Initialize the flow control address, type, and PAUSE timer * registers to their default values. This is done even if flow @@ -924,9 +987,21 @@ static s32 e1000_setup_fiber_serdes_link(struct e1000_hw *hw) DEBUGOUT("Looking for Link\n"); for (i = 0; i < (LINK_UP_TIMEOUT / 10); i++) { msleep(10); - status = er32(STATUS); - if (status & E1000_STATUS_LU) + if(hw->mac_type != e1000_icp_xxxx) { + status = er32(STATUS); + if (status & E1000_STATUS_LU) break; + } + else + { + int isUp = 0; + if(e1000_oem_phy_is_link_up(hw, &isUp) != E1000_SUCCESS){ + isUp = 0; + } + if(isUp){ + break; + } + } } if (i == (LINK_UP_TIMEOUT / 10)) { DEBUGOUT("Never got a valid link from auto-neg!!!\n"); @@ -1269,7 +1344,7 @@ static s32 e1000_copper_link_mgp_setup(struct e1000_hw *hw) * Setup auto-negotiation and flow control advertisements, * and then perform auto-negotiation. */ -static s32 e1000_copper_link_autoneg(struct e1000_hw *hw) +s32 e1000_copper_link_autoneg(struct e1000_hw *hw) { s32 ret_val; u16 phy_data; @@ -1337,12 +1412,13 @@ static s32 e1000_copper_link_autoneg(struct e1000_hw *hw) * the link partner. * 3) Config DSP to improve Gigabit link quality for some PHY revisions. */ -static s32 e1000_copper_link_postconfig(struct e1000_hw *hw) +s32 e1000_copper_link_postconfig(struct e1000_hw *hw) { s32 ret_val; DEBUGFUNC("e1000_copper_link_postconfig"); - if (hw->mac_type >= e1000_82544) { + if (hw->mac_type >= e1000_82544 + && hw->mac_type != e1000_icp_xxxx) { e1000_config_collision_dist(hw); } else { ret_val = e1000_config_mac_to_phy(hw); @@ -1365,6 +1441,13 @@ static s32 e1000_copper_link_postconfig(struct e1000_hw *hw) return ret_val; } } + else if(hw->phy_type == e1000_phy_oem) { + ret_val = e1000_oem_config_dsp_after_link_change(hw, TRUE); + if(ret_val) { + DEBUGOUT("Error Configuring OEM PHY DSP after link up\n"); + return ret_val; + } + } return E1000_SUCCESS; } @@ -1591,7 +1674,7 @@ s32 e1000_phy_setup_autoneg(struct e1000_hw *hw) * * Force PHY speed and duplex settings to hw->forced_speed_duplex */ -static s32 e1000_phy_force_speed_duplex(struct e1000_hw *hw) +s32 e1000_phy_force_speed_duplex(struct e1000_hw *hw) { u32 ctrl; s32 ret_val; @@ -1599,6 +1682,7 @@ static s32 e1000_phy_force_speed_duplex(struct e1000_hw *hw) u16 mii_status_reg; u16 phy_data; u16 i; + bool resetPhy=FALSE; DEBUGFUNC("e1000_phy_force_speed_duplex"); @@ -1686,7 +1770,19 @@ static s32 e1000_phy_force_speed_duplex(struct e1000_hw *hw) mii_ctrl_reg |= MII_CR_RESET; /* Disable MDI-X support for 10/100 */ - } else { + } else if (hw->phy_type == e1000_phy_oem) { + ret_val = e1000_oem_force_mdi(hw, (int *) &resetPhy); + if(ret_val) { + return ret_val; + } + if(resetPhy) + { + ret_val = e1000_oem_phy_hw_reset(hw); + if(ret_val){ + return ret_val; + } + } + } else { /* Clear Auto-Crossover to force MDI manually. IGP requires MDI * forced whenever speed or duplex are forced. */ @@ -1811,6 +1907,14 @@ static s32 e1000_phy_force_speed_duplex(struct e1000_hw *hw) return ret_val; } } + + if(hw->phy_type == e1000_phy_oem && resetPhy) { + ret_val = e1000_oem_cleanup_after_phy_reset(hw); + if(ret_val){ + return ret_val; + } + } + return E1000_SUCCESS; } @@ -1856,12 +1960,16 @@ static s32 e1000_config_mac_to_phy(struct e1000_hw *hw) u32 ctrl; s32 ret_val; u16 phy_data; + int is_FullDuplex = FALSE; + int is_1000MBS = FALSE; + int is_100MBS = FALSE; DEBUGFUNC("e1000_config_mac_to_phy"); /* 82544 or newer MAC, Auto Speed Detection takes care of * MAC speed/duplex configuration.*/ - if (hw->mac_type >= e1000_82544) + if (hw->mac_type >= e1000_82544 + && hw->mac_type != e1000_icp_xxxx) return E1000_SUCCESS; /* Read the Device Control Register and set the bits to Force Speed @@ -1871,14 +1979,45 @@ static s32 e1000_config_mac_to_phy(struct e1000_hw *hw) ctrl |= (E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); ctrl &= ~(E1000_CTRL_SPD_SEL | E1000_CTRL_ILOS); + if(hw->phy_type == e1000_phy_oem) + { + ret_val = e1000_oem_set_trans_gasket(hw); + if(ret_val){ + return ret_val; + } + ret_val = e1000_oem_phy_is_full_duplex( + hw, (int *) &is_FullDuplex); + if(ret_val){ + return ret_val; + } + ret_val = e1000_oem_phy_is_speed_1000( + hw, (int *) &is_1000MBS); + if(ret_val) { + return ret_val; + } + ret_val = e1000_oem_phy_is_speed_100( + hw, (int *) &is_100MBS); + if(ret_val) { + return ret_val; + } + } + else + { + ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data); + if (ret_val) + { + return ret_val; + } + is_FullDuplex = phy_data & M88E1000_PSSR_DPLX; + is_1000MBS = (phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_1000MBS; + is_100MBS = (phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_100MBS; + } + /* Set up duplex in the Device Control and Transmit Control * registers depending on negotiated values. */ - ret_val = e1000_read_phy_reg(hw, M88E1000_PHY_SPEC_STATUS, &phy_data); - if (ret_val) - return ret_val; - if (phy_data & M88E1000_PSSR_DPLX) + if (is_FullDuplex) ctrl |= E1000_CTRL_FD; else ctrl &= ~E1000_CTRL_FD; @@ -1888,13 +2027,14 @@ static s32 e1000_config_mac_to_phy(struct e1000_hw *hw) /* Set up speed in the Device Control register depending on * negotiated values. */ - if ((phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_1000MBS) + if (is_1000MBS) ctrl |= E1000_CTRL_SPD_1000; - else if ((phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_100MBS) + else if (is_100MBS) ctrl |= E1000_CTRL_SPD_100; /* Write the configured values back to the Device Control Reg. */ ew32(CTRL, ctrl); + return E1000_SUCCESS; } @@ -1993,9 +2133,16 @@ static s32 e1000_config_fc_after_link_up(struct e1000_hw *hw) if (((hw->media_type == e1000_media_type_fiber) && (hw->autoneg_failed)) || ((hw->media_type == e1000_media_type_internal_serdes) && (hw->autoneg_failed)) + || ((hw->media_type == e1000_media_type_oem) + && !e1000_oem_phy_is_copper(hw) + && (hw->autoneg_failed)) || ((hw->media_type == e1000_media_type_copper) - && (!hw->autoneg))) { - ret_val = e1000_force_mac_fc(hw); + && (!hw->autoneg)) + || ((hw->media_type == e1000_media_type_oem) + && e1000_oem_phy_is_copper(hw) + && (!hw->autoneg))) { + + ret_val = e1000_force_mac_fc(hw); if (ret_val) { DEBUGOUT("Error forcing flow control settings\n"); return ret_val; @@ -2007,7 +2154,10 @@ static s32 e1000_config_fc_after_link_up(struct e1000_hw *hw) * has completed, and if so, how the PHY and link partner has * flow control configured. */ - if ((hw->media_type == e1000_media_type_copper) && hw->autoneg) { + if((hw->media_type == e1000_media_type_copper + || (hw->media_type == e1000_media_type_oem + && e1000_oem_phy_is_copper(hw))) + && hw->autoneg) { /* Read the MII Status Register and check to see if AutoNeg * has completed. We read this twice because this reg has * some "sticky" (latched) bits. @@ -2203,6 +2353,23 @@ static s32 e1000_check_for_serdes_link_generic(struct e1000_hw *hw) status = er32(STATUS); rxcw = er32(RXCW); + int isUp = 0; + + // SDG - this is potential issue. Should not cause problems on E1000 though but may + // cause a problem for ETH1 + if (hw->mac_type == e1000_icp_xxxx) + { + ret_val = e1000_oem_phy_is_link_up(hw, &isUp); + if (ret_val) + { + return ret_val; + } + } + else + { + isUp = status & E1000_STATUS_LU; + } + /* * If we don't have link (auto-negotiation failed or link partner * cannot auto-negotiate), and our link partner is not trying to @@ -2211,7 +2378,7 @@ static s32 e1000_check_for_serdes_link_generic(struct e1000_hw *hw) * time to complete. */ /* (ctrl & E1000_CTRL_SWDPIN1) == 1 == have signal */ - if ((!(status & E1000_STATUS_LU)) && (!(rxcw & E1000_RXCW_C))) { + if ((!(isUp)) && (!(rxcw & E1000_RXCW_C))) { if (hw->autoneg_failed == 0) { hw->autoneg_failed = 1; goto out; @@ -2265,8 +2432,7 @@ static s32 e1000_check_for_serdes_link_generic(struct e1000_hw *hw) } if (E1000_TXCW_ANE & er32(TXCW)) { - status = er32(STATUS); - if (status & E1000_STATUS_LU) { + if (isUp) { // SDG scary change here as well /* SYNCH bit and IV bit are sticky, so reread rxcw. */ udelay(10); rxcw = er32(RXCW); @@ -2321,16 +2487,21 @@ s32 e1000_check_for_link(struct e1000_hw *hw) * set when the optics detect a signal. On older adapters, it will be * cleared when there is a signal. This applies to fiber media only. */ - if ((hw->media_type == e1000_media_type_fiber) || - (hw->media_type == e1000_media_type_internal_serdes)) { + if ((hw->media_type == e1000_media_type_fiber) + || (hw->media_type == e1000_media_type_internal_serdes) + || (hw->media_type == e1000_media_type_oem + && !e1000_oem_phy_is_copper(hw))) { rxcw = er32(RXCW); if (hw->media_type == e1000_media_type_fiber) { signal = (hw->mac_type > e1000_82544) ? E1000_CTRL_SWDPIN1 : 0; - if (status & E1000_STATUS_LU) + if (hw->mac_type != e1000_icp_xxxx) + { + if (status & E1000_STATUS_LU) hw->get_link_status = false; + } } } @@ -2340,7 +2511,10 @@ s32 e1000_check_for_link(struct e1000_hw *hw) * receive a Link Status Change interrupt or we have Rx Sequence * Errors. */ - if ((hw->media_type == e1000_media_type_copper) && hw->get_link_status) { + if((hw->media_type == e1000_media_type_copper + || (hw->media_type == e1000_media_type_oem + && e1000_oem_phy_is_copper(hw))) + && hw->get_link_status) { /* First we want to see if the MII Status Register reports * link. If so, then we want to get the current speed/duplex * of the PHY. @@ -2352,6 +2526,7 @@ s32 e1000_check_for_link(struct e1000_hw *hw) ret_val = e1000_read_phy_reg(hw, PHY_STATUS, &phy_data); if (ret_val) return ret_val; + hw->icp_xxxx_is_link_up = (phy_data & MII_SR_LINK_STATUS) != 0; if (phy_data & MII_SR_LINK_STATUS) { hw->get_link_status = false; @@ -2382,6 +2557,12 @@ s32 e1000_check_for_link(struct e1000_hw *hw) } else { /* No link detected */ e1000_config_dsp_after_link_change(hw, false); + + + if(hw->phy_type == e1000_phy_oem) { + e1000_oem_config_dsp_after_link_change(hw, FALSE); + } + return 0; } @@ -2394,6 +2575,10 @@ s32 e1000_check_for_link(struct e1000_hw *hw) /* optimize the dsp settings for the igp phy */ e1000_config_dsp_after_link_change(hw, true); + if(hw->phy_type == e1000_phy_oem) { + e1000_oem_config_dsp_after_link_change(hw, TRUE); + } + /* We have a M88E1000 PHY and Auto-Neg is enabled. If we * have Si on board that is 82544 or newer, Auto * Speed Detection takes care of MAC speed/duplex @@ -2402,7 +2587,8 @@ s32 e1000_check_for_link(struct e1000_hw *hw) * speed/duplex on the MAC to the current PHY speed/duplex * settings. */ - if (hw->mac_type >= e1000_82544) + if (hw->mac_type >= e1000_82544 + && hw->mac_type != e1000_icp_xxxx) e1000_config_collision_dist(hw); else { ret_val = e1000_config_mac_to_phy(hw); @@ -2467,7 +2653,8 @@ s32 e1000_check_for_link(struct e1000_hw *hw) } if ((hw->media_type == e1000_media_type_fiber) || - (hw->media_type == e1000_media_type_internal_serdes)) + (hw->media_type == e1000_media_type_internal_serdes) || + (hw->media_type == e1000_media_type_oem && !e1000_oem_phy_is_copper(hw))) // SDG - this was a dodgy port from iegbe e1000_check_for_serdes_link_generic(hw); return E1000_SUCCESS; @@ -2748,7 +2935,8 @@ static s32 e1000_read_phy_reg_ex(struct e1000_hw *hw, u32 reg_addr, return -E1000_ERR_PARAM; } - if (hw->mac_type > e1000_82543) { + if (hw->mac_type > e1000_82543 + && hw->mac_type != e1000_icp_xxxx) { /* Set up Op-code, Phy Address, and register address in the MDI * Control register. The MAC will take care of interfacing with the * PHY to retrieve the desired data. @@ -2775,7 +2963,8 @@ static s32 e1000_read_phy_reg_ex(struct e1000_hw *hw, u32 reg_addr, return -E1000_ERR_PHY; } *phy_data = (u16) mdic; - } else { + } else if (hw->mac_type != e1000_icp_xxxx + && hw->mac_type != e1000_icp_xxxx) { /* We must first send a preamble through the MDIO pin to signal the * beginning of an MII instruction. This is done by sending 32 * consecutive "1" bits. @@ -2803,7 +2992,13 @@ static s32 e1000_read_phy_reg_ex(struct e1000_hw *hw, u32 reg_addr, * register address. */ *phy_data = e1000_shift_in_mdi_bits(hw); - } + } else { + int32_t ret_val = e1000_oem_read_phy_reg_ex(hw, reg_addr, phy_data); + if(ret_val) { + return ret_val; + } + } + return E1000_SUCCESS; } @@ -2850,7 +3045,8 @@ static s32 e1000_write_phy_reg_ex(struct e1000_hw *hw, u32 reg_addr, return -E1000_ERR_PARAM; } - if (hw->mac_type > e1000_82543) { + if (hw->mac_type > e1000_82543 && + hw->mac_type != e1000_icp_xxxx) { /* Set up Op-code, Phy Address, register address, and data intended * for the PHY register in the MDI Control register. The MAC will take * care of interfacing with the PHY to send the desired data. @@ -2873,7 +3069,7 @@ static s32 e1000_write_phy_reg_ex(struct e1000_hw *hw, u32 reg_addr, DEBUGOUT("MDI Write did not complete\n"); return -E1000_ERR_PHY; } - } else { + } else if (hw->mac_type != e1000_icp_xxxx) { /* We'll need to use the SW defined pins to shift the write command * out to the PHY. We first send a preamble to the PHY to signal the * beginning of the MII instruction. This is done by sending 32 @@ -2893,7 +3089,12 @@ static s32 e1000_write_phy_reg_ex(struct e1000_hw *hw, u32 reg_addr, mdic |= (u32) phy_data; e1000_shift_out_mdi_bits(hw, mdic, 32); - } + } else { + int32_t ret_val = e1000_oem_write_phy_reg_ex(hw, reg_addr, phy_data); + if(ret_val){ + return ret_val; + } + } return E1000_SUCCESS; } @@ -2913,6 +3114,16 @@ s32 e1000_phy_hw_reset(struct e1000_hw *hw) DEBUGFUNC("e1000_phy_hw_reset"); DEBUGOUT("Resetting Phy...\n"); + + if(hw->mac_type == e1000_icp_xxxx) + { + ret_val = e1000_oem_phy_hw_reset(hw); + if(ret_val){ + return ret_val; + } + e1000_oem_phy_init_script(hw); + return ret_val; + } if (hw->mac_type > e1000_82543) { /* Read the device control register and assert the E1000_CTRL_PHY_RST @@ -2953,6 +3164,10 @@ s32 e1000_phy_hw_reset(struct e1000_hw *hw) ew32(LEDCTL, led_ctrl); } + if(hw->phy_type == e1000_phy_oem){ + e1000_oem_phy_init_script(hw); + } + /* Wait for FW to finish PHY configuration. */ ret_val = e1000_get_phy_cfg_done(hw); if (ret_val != E1000_SUCCESS) @@ -2977,6 +3192,7 @@ s32 e1000_phy_reset(struct e1000_hw *hw) switch (hw->phy_type) { case e1000_phy_igp: + case e1000_icp_xxxx: ret_val = e1000_phy_hw_reset(hw); if (ret_val) return ret_val; @@ -3272,7 +3488,9 @@ s32 e1000_phy_get_info(struct e1000_hw *hw, struct e1000_phy_info *phy_info) phy_info->local_rx = e1000_1000t_rx_status_undefined; phy_info->remote_rx = e1000_1000t_rx_status_undefined; - if (hw->media_type != e1000_media_type_copper) { + if(hw->media_type != e1000_media_type_copper + && (hw->media_type == e1000_media_type_oem + && !e1000_oem_phy_is_copper(hw))) { DEBUGOUT("PHY info is only valid for copper media\n"); return -E1000_ERR_CONFIG; } @@ -3292,7 +3510,9 @@ s32 e1000_phy_get_info(struct e1000_hw *hw, struct e1000_phy_info *phy_info) if (hw->phy_type == e1000_phy_igp) return e1000_phy_igp_get_info(hw, phy_info); - else + else if (hw->phy_type == e1000_phy_oem) + return e1000_oem_phy_get_info(hw, phy_info); + else return e1000_phy_m88_get_info(hw, phy_info); } @@ -3340,6 +3560,7 @@ s32 e1000_init_eeprom_params(struct e1000_hw *hw) case e1000_82545_rev_3: case e1000_82546: case e1000_82546_rev_3: + case e1000_icp_xxxx: eeprom->type = e1000_eeprom_microwire; eeprom->opcode_bits = 3; eeprom->delay_usec = 50; @@ -4091,12 +4312,22 @@ s32 e1000_read_mac_addr(struct e1000_hw *hw) { u16 offset; u16 eeprom_data, i; + uint16_t ia_base_addr=0; DEBUGFUNC("e1000_read_mac_addr"); + if(hw->mac_type == e1000_icp_xxxx) { + struct e1000_adapter *adapter; + uint32_t device_number; + + adapter = (struct e1000_adapter *) hw->back; + device_number = PCI_SLOT(adapter->pdev->devfn); + ia_base_addr = (uint16_t) EEPROM_IA_START_ICP_xxxx(device_number); + } + for (i = 0; i < NODE_ADDRESS_SIZE; i += 2) { offset = i >> 1; - if (e1000_read_eeprom(hw, offset, 1, &eeprom_data) < 0) { + if (e1000_read_eeprom(hw, offset+ia_base_addr, 1, &eeprom_data) < 0) { DEBUGOUT("EEPROM Read Error\n"); return -E1000_ERR_EEPROM; } @@ -4292,7 +4523,8 @@ static s32 e1000_id_led_init(struct e1000_hw *hw) DEBUGFUNC("e1000_id_led_init"); - if (hw->mac_type < e1000_82540) { + if (hw->mac_type < e1000_82540 + || hw->mac_type == e1000_icp_xxxx) { /* Nothing to do */ return E1000_SUCCESS; } @@ -4370,6 +4602,7 @@ s32 e1000_setup_led(struct e1000_hw *hw) case e1000_82542_rev2_1: case e1000_82543: case e1000_82544: + case e1000_icp_xxxx: /* No setup necessary */ break; case e1000_82541: @@ -4422,6 +4655,7 @@ s32 e1000_cleanup_led(struct e1000_hw *hw) case e1000_82542_rev2_1: case e1000_82543: case e1000_82544: + case e1000_icp_xxxx: /* No cleanup necessary */ break; case e1000_82541: @@ -4454,6 +4688,9 @@ s32 e1000_led_on(struct e1000_hw *hw) DEBUGFUNC("e1000_led_on"); switch (hw->mac_type) { + case e1000_icp_xxxx: + /* No LED control on ICP family of gigE controllers */ + return E1000_SUCCESS; case e1000_82542_rev2_0: case e1000_82542_rev2_1: case e1000_82543: @@ -4500,6 +4737,9 @@ s32 e1000_led_off(struct e1000_hw *hw) DEBUGFUNC("e1000_led_off"); switch (hw->mac_type) { + case e1000_icp_xxxx: + /* No LED control on ICP family of gigE controllers */ + return E1000_SUCCESS; case e1000_82542_rev2_0: case e1000_82542_rev2_1: case e1000_82543: @@ -4607,8 +4847,8 @@ static void e1000_clear_hw_cntrs(struct e1000_hw *hw) temp = er32(TSCTC); temp = er32(TSCTFC); - if (hw->mac_type <= e1000_82544) - return; + if (hw->mac_type <= e1000_82544 + || hw->mac_type == e1000_icp_xxxx) { return;} temp = er32(MGTPRC); temp = er32(MGTPDC); @@ -4778,6 +5018,11 @@ void e1000_get_bus_info(struct e1000_hw *hw) hw->bus_speed = e1000_bus_speed_unknown; hw->bus_width = e1000_bus_width_unknown; break; + case e1000_icp_xxxx: + hw->bus_type = e1000_bus_type_cpp; + hw->bus_speed = e1000_bus_speed_unknown; + hw->bus_width = e1000_bus_width_unknown; + break; default: status = er32(STATUS); hw->bus_type = (status & E1000_STATUS_PCIX_MODE) ? @@ -4942,7 +5187,14 @@ static s32 e1000_get_cable_length(struct e1000_hw *hw, u16 *min_length, IGP01E1000_AGC_RANGE) : 0; *max_length = e1000_igp_cable_length_table[agc_value] + IGP01E1000_AGC_RANGE; - } + } else if(hw->phy_type == e1000_phy_oem) { + ret_val = e1000_oem_get_cable_length(hw, + min_length, + max_length); + if(ret_val){ + return ret_val; + } + } return E1000_SUCCESS; } @@ -5011,7 +5263,10 @@ static s32 e1000_check_polarity(struct e1000_hw *hw, e1000_rev_polarity_reversed : e1000_rev_polarity_normal; } - } + } else if (hw->phy_type == e1000_phy_oem) { + return e1000_oem_check_polarity(hw, (uint16_t*)polarity); + } + return E1000_SUCCESS; } @@ -5052,7 +5307,14 @@ static s32 e1000_check_downshift(struct e1000_hw *hw) hw->speed_downgraded = (phy_data & M88E1000_PSSR_DOWNSHIFT) >> M88E1000_PSSR_DOWNSHIFT_SHIFT; - } + } else if (hw->phy_type == e1000_phy_oem) { + ret_val = e1000_oem_phy_speed_downgraded(hw, &phy_data); + + if(ret_val) + return ret_val; + hw->speed_downgraded = phy_data>0; + } + return E1000_SUCCESS; } @@ -5069,7 +5331,7 @@ static s32 e1000_check_downshift(struct e1000_hw *hw) * gigabit link is achieved to improve link quality. */ -static s32 e1000_config_dsp_after_link_change(struct e1000_hw *hw, bool link_up) +s32 e1000_config_dsp_after_link_change(struct e1000_hw *hw, bool link_up) { s32 ret_val; u16 phy_data, phy_saved_data, speed, duplex, i; @@ -5333,7 +5595,7 @@ static s32 e1000_set_phy_mode(struct e1000_hw *hw) * returns: - E1000_ERR_PHY if fail to read/write the PHY * E1000_SUCCESS at any other case. */ -static s32 e1000_set_d3_lplu_state(struct e1000_hw *hw, bool active) +s32 e1000_set_d3_lplu_state(struct e1000_hw *hw, bool active) { s32 ret_val; u16 phy_data; @@ -5611,7 +5873,7 @@ static s32 e1000_polarity_reversal_workaround(struct e1000_hw *hw) * returns: - E1000_ERR_RESET if fail to reset MAC * E1000_SUCCESS at any other case. */ -static s32 e1000_get_auto_rd_done(struct e1000_hw *hw) +s32 e1000_get_auto_rd_done(struct e1000_hw *hw) { DEBUGFUNC("e1000_get_auto_rd_done"); msleep(5); @@ -5626,7 +5888,7 @@ static s32 e1000_get_auto_rd_done(struct e1000_hw *hw) * returns: - E1000_ERR_RESET if fail to reset MAC * E1000_SUCCESS at any other case. */ -static s32 e1000_get_phy_cfg_done(struct e1000_hw *hw) +s32 e1000_get_phy_cfg_done(struct e1000_hw *hw) { DEBUGFUNC("e1000_get_phy_cfg_done"); mdelay(10); @@ -50,6 +50,7 @@ typedef enum { e1000_82540, e1000_82545, e1000_82545_rev_3, + e1000_icp_xxxx, e1000_82546, e1000_82546_rev_3, e1000_82541, @@ -73,6 +74,7 @@ typedef enum { e1000_media_type_copper = 0, e1000_media_type_fiber = 1, e1000_media_type_internal_serdes = 2, + e1000_media_type_oem = 3, e1000_num_media_types } e1000_media_type; @@ -102,6 +104,8 @@ typedef enum { e1000_bus_type_unknown = 0, e1000_bus_type_pci, e1000_bus_type_pcix, + e1000_bus_type_pci_express, + e1000_bus_type_cpp, e1000_bus_type_reserved } e1000_bus_type; @@ -210,6 +214,7 @@ typedef enum { typedef enum { e1000_phy_m88 = 0, e1000_phy_igp, + e1000_phy_oem, e1000_phy_undefined = 0xFF } e1000_phy_type; @@ -399,11 +404,28 @@ int e1000_pcix_get_mmrbc(struct e1000_hw *hw); /* Port I/O is only supported on 82544 and newer */ void e1000_io_write(struct e1000_hw *hw, unsigned long port, u32 value); + +int32_t e1000_config_dsp_after_link_change(struct e1000_hw *hw, bool link_up); +int32_t e1000_get_auto_rd_done(struct e1000_hw *hw); +int32_t e1000_get_phy_cfg_done(struct e1000_hw *hw); +int32_t e1000_set_d3_lplu_state(struct e1000_hw *hw, bool active); + #define E1000_READ_REG_IO(a, reg) \ e1000_read_reg_io((a), E1000_##reg) #define E1000_WRITE_REG_IO(a, reg, val) \ e1000_write_reg_io((a), E1000_##reg, val) +/* ICP xxxx Signal Target Capability */ +#define PCI_CAP_ID_ST 0x09 +#define PCI_ST_SCID_MASK 0x000000FF +#define PCI_ST_SCP_MASK 0x0000FF00 +#define PCI_ST_SBC_MASK 0x00FF0000 +#define PCI_ST_STYP_MASK 0xFF000000 +#define PCI_ST_SMIA_MASK 0x000000FF +#define PCI_ST_SMACC_MASK 0x0000FF00 +#define PCI_ST_SDATA_MASK 0xFFFF0000 +#define PCI_ST_SMIA_OFFSET 0x00000004 + /* PCI Device IDs */ #define E1000_DEV_ID_82542 0x1000 #define E1000_DEV_ID_82543GC_FIBER 0x1001 @@ -834,6 +856,8 @@ struct e1000_ffvt_entry { #define E1000_TIPG 0x00410 /* TX Inter-packet gap -RW */ #define E1000_TBT 0x00448 /* TX Burst Timer - RW */ #define E1000_AIT 0x00458 /* Adaptive Interframe Spacing Throttle - RW */ +#define E1000_IMC1 0x008D8 /* Interrupt Mask Clear 1 - RW */ +#define E1000_IMC2 0x008F8 /* Interrupt Mask 2 Clear - WO */ #define E1000_LEDCTL 0x00E00 /* LED Control - RW */ #define E1000_EXTCNF_CTRL 0x00F00 /* Extended Configuration Control */ #define E1000_EXTCNF_SIZE 0x00F08 /* Extended Configuration Size */ @@ -1030,6 +1054,8 @@ struct e1000_ffvt_entry { #define E1000_82542_ICS E1000_ICS #define E1000_82542_IMS E1000_IMS #define E1000_82542_IMC E1000_IMC +#define E1000_82542_IMC1 E1000_IMC1 +#define E1000_82542_IMC2 E1000_IMC2 #define E1000_82542_RCTL E1000_RCTL #define E1000_82542_RDTR 0x00108 #define E1000_82542_RDBAL 0x00110 @@ -1380,6 +1406,7 @@ struct e1000_hw { bool leave_av_bit_off; bool bad_tx_carr_stats_fd; bool has_smbus; + bool icp_xxxx_is_link_up; }; #define E1000_EEPROM_SWDPIN0 0x0001 /* SWDPIN 0 EEPROM Value */ @@ -2194,6 +2221,14 @@ struct e1000_host_command_info { #define EEPROM_FLASH_VERSION 0x0032 #define EEPROM_CHECKSUM_REG 0x003F +/* ICP PCI Dev ID xxxx macros to calculate word offsets for IA, IPv4 and IPv6 */ +#define EEPROM_CTRL3_APME_ICP_xxxx 0x0004 +#define EEPROM_MGMT_CONTROL_ICP_xxxx(device_num) (((device_num) + 1) << 4) +#define EEPROM_INIT_CONTROL3_ICP_xxxx(device_num) ((((device_num) + 1) << 4) + 1) +#define EEPROM_IA_START_ICP_xxxx(device_num) ((((device_num) + 1) << 4) + 2) +#define EEPROM_IPV4_START_ICP_xxxx(device_num) ((((device_num) + 1) << 4) + 5) +#define EEPROM_IPV6_START_ICP_xxxx(device_num) ((((device_num) + 1) << 4) + 7) + #define E1000_EEPROM_CFG_DONE 0x00040000 /* MNG config cycle done */ #define E1000_EEPROM_CFG_DONE_PORT_1 0x00080000 /* ...for second port */ diff --git a/e1000_main.c b/e1000_main.c index b15ece2..1c87c14 100644 --- a/e1000_main.c +++ b/e1000_main.c @@ -27,6 +27,11 @@ *******************************************************************************/ #include "e1000.h" +#include "e1000_oem_phy.h" +#ifdef CONFIG_E1000_EP80579 +#include "gcu_if.h" +#endif + #include <net/ip6_checksum.h> char e1000_driver_name[] = "e1000"; @@ -35,6 +40,8 @@ static char e1000_driver_string[] = "Intel(R) PRO/1000 Network Driver"; const char e1000_driver_version[] = DRV_VERSION; static const char e1000_copyright[] = "Copyright (c) 1999-2006 Intel Corporation."; +extern bool e1000_loaded; + /* e1000_pci_tbl - PCI Device ID Table * * Last entry must be all 0s @@ -79,6 +86,18 @@ static DEFINE_PCI_DEVICE_TABLE(e1000_pci_tbl) = { INTEL_E1000_ETHERNET_DEVICE(0x108A), INTEL_E1000_ETHERNET_DEVICE(0x1099), INTEL_E1000_ETHERNET_DEVICE(0x10B5), + INTEL_E1000_ETHERNET_DEVICE(0x5040), + INTEL_E1000_ETHERNET_DEVICE(0x5041), + INTEL_E1000_ETHERNET_DEVICE(0x5042), + INTEL_E1000_ETHERNET_DEVICE(0x5043), + INTEL_E1000_ETHERNET_DEVICE(0x5044), + INTEL_E1000_ETHERNET_DEVICE(0x5045), + INTEL_E1000_ETHERNET_DEVICE(0x5046), + INTEL_E1000_ETHERNET_DEVICE(0x5047), + INTEL_E1000_ETHERNET_DEVICE(0x5048), + INTEL_E1000_ETHERNET_DEVICE(0x5049), + INTEL_E1000_ETHERNET_DEVICE(0x504A), + INTEL_E1000_ETHERNET_DEVICE(0x504B), /* required last entry */ {0,} }; @@ -213,6 +232,13 @@ static int debug = NETIF_MSG_DRV | NETIF_MSG_PROBE; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); +#ifdef CONFIG_E1000_EP80579 +static uint8_t gcu_suspend = 0x0; +static uint8_t gcu_resume = 0x0; +struct pci_dev *gcu = NULL; +#endif + + /** * e1000_init_module - Driver Registration Routine * @@ -236,10 +262,15 @@ static int __init e1000_init_module(void) printk(KERN_INFO "e1000: copybreak enabled for " "packets <= %u bytes\n", copybreak); } + return ret; } +#ifdef CONFIG_E1000_EP80579 +late_initcall(e1000_init_module); +#else module_init(e1000_init_module); +#endif /** * e1000_exit_module - Driver Exit Cleanup Routine @@ -539,6 +570,7 @@ void e1000_reset(struct e1000_adapter *adapter) case e1000_82540: case e1000_82541: case e1000_82541_rev_2: + case e1000_icp_xxxx: legacy_pba_adjust = true; pba = E1000_PBA_48K; break; @@ -989,6 +1021,12 @@ static int __devinit e1000_probe(struct pci_dev *pdev, EEPROM_INIT_CONTROL2_REG, 1, &eeprom_data); eeprom_apme_mask = E1000_EEPROM_82544_APM; break; + case e1000_icp_xxxx: + e1000_read_eeprom(&adapter->hw, + EEPROM_INIT_CONTROL3_ICP_xxxx(adapter->bd_number), + 1, &eeprom_data); + eeprom_apme_mask = EEPROM_CTRL3_APME_ICP_xxxx; + break; case e1000_82546: case e1000_82546_rev_3: if (er32(STATUS) & E1000_STATUS_FUNC_1){ @@ -1046,10 +1084,41 @@ static int __devinit e1000_probe(struct pci_dev *pdev, printk("%pM\n", netdev->dev_addr); + /* The ICP_xxxx device has multiple, duplicate interrupt + * registers, so disable all but the first one + */ + if(adapter->hw.mac_type == e1000_icp_xxxx) { + int offset = pci_find_capability(adapter->pdev, PCI_CAP_ID_ST) + + PCI_ST_SMIA_OFFSET; + pci_write_config_dword(adapter->pdev, offset, 0x00000006); + E1000_WRITE_REG(&adapter->hw, IMC1, ~0UL); + E1000_WRITE_REG(&adapter->hw, IMC2, ~0UL); + } + /* reset the hardware with the new settings */ e1000_reset(adapter); - strcpy(netdev->name, "eth%d"); + if(adapter->hw.mac_type != e1000_icp_xxxx) { + strcpy(netdev->name, "eth%d"); + } + else + { + uint32_t dev_num; + dev_num = PCI_SLOT(adapter->pdev->devfn); + + switch ( dev_num ) { + case ICP_XXXX_MAC_0: + strcpy(netdev->name, CONFIG_E1000_EP80579_MAC0_BASE_NAME "%d"); + break; + case ICP_XXXX_MAC_1: + strcpy(netdev->name, CONFIG_E1000_EP80579_MAC1_BASE_NAME "%d"); + break; + case ICP_XXXX_MAC_2: + strcpy(netdev->name, CONFIG_E1000_EP80579_MAC2_BASE_NAME "%d"); + break; + } + } + err = register_netdev(netdev); if (err) goto err_register; @@ -1180,7 +1249,9 @@ static int __devinit e1000_sw_init(struct e1000_adapter *adapter) /* Copper options */ - if (hw->media_type == e1000_media_type_copper) { + if (hw->media_type == e1000_media_type_copper + || (hw->media_type == e1000_media_type_oem + && e1000_oem_phy_is_copper(&adapter->hw))) { hw->mdix = AUTO_ALL_MODES; hw->disable_polarity_correction = false; hw->master_slave = E1000_MASTER_SLAVE; @@ -1197,6 +1268,14 @@ static int __devinit e1000_sw_init(struct e1000_adapter *adapter) /* Explicitly disable IRQ since the NIC can be in any state. */ e1000_irq_disable(adapter); + /* + * for ICP_XXXX style controllers, it is necessary to keep + * track of the last known state of the link to determine if + * the link experienced a change in state when e1000_watchdog + * fires + */ + adapter->hw.icp_xxxx_is_link_up = false; + spin_lock_init(&adapter->stats_lock); set_bit(__E1000_DOWN, &adapter->flags); @@ -1504,8 +1583,12 @@ static void e1000_configure_tx(struct e1000_adapter *adapter) if ((hw->media_type == e1000_media_type_fiber || hw->media_type == e1000_media_type_internal_serdes)) tipg = DEFAULT_82543_TIPG_IPGT_FIBER; - else + else if (hw->media_type != e1000_media_type_oem) tipg = DEFAULT_82543_TIPG_IPGT_COPPER; + else + tipg = (0xFFFFFFFFUL >> (sizeof(tipg)*0x8 - + E1000_TIPG_IPGR1_SHIFT)) + & e1000_oem_get_tipg(&adapter->hw); switch (hw->mac_type) { case e1000_82542_rev2_0: @@ -2247,6 +2330,24 @@ bool e1000_has_link(struct e1000_adapter *adapter) struct e1000_hw *hw = &adapter->hw; bool link_active = false; + /* + * Test the PHY for link status on icp_xxxx MACs. + * If the link status is different than the last link status stored + * in the adapter->hw structure, then set hw->get_link_status = 1 + */ + if(adapter->hw.mac_type == e1000_icp_xxxx) { + int isUp = 0x0; + int32_t ret_val; + + ret_val = e1000_oem_phy_is_link_up(&adapter->hw, &isUp); + if(ret_val != E1000_SUCCESS) { + isUp = 0x0; + } + if(isUp != adapter->hw.icp_xxxx_is_link_up) { + adapter->hw.get_link_status = 0x1; + } + } + /* get_link_status is set on LSC (link status) interrupt or * rx sequence error interrupt. get_link_status will stay * false until the e1000_check_for_link establishes link @@ -2269,10 +2370,26 @@ bool e1000_has_link(struct e1000_adapter *adapter) e1000_check_for_link(hw); link_active = hw->serdes_has_link; break; + case e1000_media_type_oem: + e1000_check_for_link(hw); + break; default: break; } + + if(adapter->hw.mac_type == e1000_icp_xxxx) + { + int isUp = 0x0; + + if(e1000_oem_phy_is_link_up(&adapter->hw, &isUp) != E1000_SUCCESS) + { + isUp = 0x0; + } + link_active = isUp; + } + + return link_active; } @@ -3317,7 +3434,9 @@ void e1000_update_stats(struct e1000_adapter *adapter) /* Tx Dropped needs to be maintained elsewhere */ /* Phy Stats */ - if (hw->media_type == e1000_media_type_copper) { + if (hw->media_type == e1000_media_type_copper + || (hw->media_type == e1000_media_type_oem + && e1000_oem_phy_is_copper(&adapter->hw))) { if ((adapter->link_speed == SPEED_1000) && (!e1000_read_phy_reg(hw, PHY_1000T_STATUS, &phy_tmp))) { phy_tmp &= PHY_IDLE_ERROR_COUNT_MASK; @@ -4249,7 +4368,10 @@ static int e1000_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, u16 spddplx; unsigned long flags; - if (hw->media_type != e1000_media_type_copper) + if((adapter->hw.media_type == e1000_media_type_oem + && !e1000_oem_phy_is_copper(&adapter->hw)) + || (adapter->hw.media_type != e1000_media_type_copper + && adapter->hw.media_type != e1000_media_type_oem)) return -EOPNOTSUPP; switch (cmd) { @@ -4276,7 +4398,14 @@ static int e1000_mii_ioctl(struct net_device *netdev, struct ifreq *ifr, return -EIO; } spin_unlock_irqrestore(&adapter->stats_lock, flags); - if (hw->media_type == e1000_media_type_copper) { + if (adapter->hw.phy_type == e1000_phy_oem) + { + retval = e1000_oem_mii_ioctl(adapter, flags, ifr, cmd); + if(retval) { + return retval; + } + } + else if (hw->media_type == e1000_media_type_copper) { switch (data->reg_num) { case PHY_CTRL: if (mii_reg & MII_CR_POWER_DOWN) @@ -4464,8 +4593,10 @@ int e1000_set_spd_dplx(struct e1000_adapter *adapter, u16 spddplx) hw->autoneg = 0; /* Fiber NICs only allow 1000 gbps Full duplex */ - if ((hw->media_type == e1000_media_type_fiber) && - spddplx != (SPEED_1000 + DUPLEX_FULL)) { + if((adapter->hw.media_type == e1000_media_type_fiber + || (adapter->hw.media_type == e1000_media_type_oem + && !e1000_oem_phy_is_copper(&adapter->hw))) + && spddplx != (SPEED_1000 + DUPLEX_FULL)) { DPRINTK(PROBE, ERR, "Unsupported Speed/Duplex configuration\n"); return -EINVAL; } @@ -4519,9 +4650,24 @@ static int __e1000_shutdown(struct pci_dev *pdev, bool *enable_wake) return retval; #endif - status = er32(STATUS); - if (status & E1000_STATUS_LU) - wufc &= ~E1000_WUFC_LNKC; + /* + * ICP_XXXX style MACs do not have a link up bit in + * the STATUS register, query the PHY directly + */ + if(adapter->hw.mac_type != e1000_icp_xxxx) { + status = er32(STATUS); + if(status & E1000_STATUS_LU) { + wufc &= ~E1000_WUFC_LNKC; + } + } else { + int isUp = 0x0; + if(e1000_oem_phy_is_link_up(&adapter->hw, &isUp) != E1000_SUCCESS) { + isUp = 0x0; + } + if(isUp) { + wufc &= ~E1000_WUFC_LNKC; + } + } if (wufc) { e1000_setup_rctl(adapter); @@ -4541,7 +4687,8 @@ static int __e1000_shutdown(struct pci_dev *pdev, bool *enable_wake) /* phy power management enable */ #define E1000_CTRL_EN_PHY_PWR_MGMT 0x00200000 ctrl |= E1000_CTRL_ADVD3WUC | - E1000_CTRL_EN_PHY_PWR_MGMT; + (adapter->hw.mac_type != e1000_icp_xxxx + ? E1000_CTRL_EN_PHY_PWR_MGMT : 0x0); ew32(CTRL, ctrl); } @@ -4579,8 +4726,11 @@ static int __e1000_shutdown(struct pci_dev *pdev, bool *enable_wake) #ifdef CONFIG_PM static int e1000_suspend(struct pci_dev *pdev, pm_message_t state) { + struct net_device *netdev = pci_get_drvdata(pdev); + struct e1000_adapter *adapter = netdev_priv(netdev); int retval; bool wake; + u16 cmd_word; retval = __e1000_shutdown(pdev, &wake); if (retval) @@ -4593,6 +4743,38 @@ static int e1000_suspend(struct pci_dev *pdev, pm_message_t state) pci_set_power_state(pdev, PCI_D3hot); } + if(adapter->hw.mac_type == e1000_icp_xxxx) { + /* + * ICP xxxx devices are not true PCI devices, in the context + * of power management, disabling the bus mastership is not + * sufficient to disable the device, it is also necessary to + * disable IO, Memory, and Interrupts if they are enabled. + */ + pci_read_config_word(pdev, PCI_COMMAND, &cmd_word); + if(cmd_word & PCI_COMMAND_IO) { + cmd_word &= ~PCI_COMMAND_IO; + } + if(cmd_word & PCI_COMMAND_MEMORY) { + cmd_word &= ~PCI_COMMAND_MEMORY; + } + if(cmd_word & PCI_COMMAND_INTX_DISABLE) { + cmd_word &= ~PCI_COMMAND_INTX_DISABLE; + } + pci_write_config_word(pdev, PCI_COMMAND, cmd_word); + } + +#ifdef CONFIG_E1000_EP80579 + if(gcu_suspend == 0x0) + { + if(gcu == NULL) { + gcu = pci_get_device(PCI_VENDOR_ID_INTEL, GCU_DEVID, NULL); + } + gcu_e1000_suspend(gcu, 0x3); + gcu_suspend = 0x1; + gcu_resume = 0x0; + } +#endif + return 0; } @@ -4602,6 +4784,25 @@ static int e1000_resume(struct pci_dev *pdev) struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; u32 err; + u32 vdid; + +#ifdef CONFIG_E1000_EP80579 + if(gcu_resume == 0x0) + { + if(gcu == NULL) { + gcu = pci_get_device(PCI_VENDOR_ID_INTEL, GCU_DEVID, NULL); + pci_read_config_dword(gcu, 0x00, &vdid); + } + + if(gcu) { + gcu_e1000_resume(gcu); + gcu_resume = 0x1; + gcu_suspend = 0x0; + } else { + printk("Unable to resume GCU!\n"); + } + } +#endif pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); diff --git a/e1000_oem_phy.c b/e1000_oem_phy.c new file mode 100755 index 0000000..544098f --- /dev/null +++ b/e1000_oem_phy.c @@ -0,0 +1,2497 @@ +/***************************************************************************** + +GPL LICENSE SUMMARY + + Copyright(c) 2007,2008,2009 Intel Corporation. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + The full GNU General Public License is included in this distribution + in the file called LICENSE.GPL. + + Contact Information: + Intel Corporation + + version: Embedded.Release.Patch.L.1.0.7-5 + + Contact Information: + + Intel Corporation, 5000 W Chandler Blvd, Chandler, AZ 85226 + +*****************************************************************************/ +/************************************************************************** + * @ingroup OEM_PHY_GENERAL + * + * @file oem_phy.c + * + * @description + * This module contains oem functions. + * + **************************************************************************/ + +#include "e1000_oem_phy.h" +#include "e1000.h" +#include "e1000_hw.h" +#include "gcu_if.h" +/* + * List of functions leveraged from the base e1000 driver. + * + * Ideally, it would have been nice to keep e1000_oem_phy.c + * minimally dependent on the e1000. Any function taking + * a struct e1000_hw as a parameter can be implemented in + * this file. It was chosen to reuse as much code as possible + * to save time (isn't that always the case ;) + */ +extern int e1000_up(struct e1000_adapter *adapter); +extern void e1000_down(struct e1000_adapter *adapter); +extern void e1000_reset(struct e1000_adapter *adapter); +extern int e1000_set_spd_dplx(struct e1000_adapter *adapter, uint16_t spddplx); +extern int32_t e1000_copper_link_autoneg(struct e1000_hw *hw); +extern int32_t e1000_phy_force_speed_duplex(struct e1000_hw *hw); +extern int32_t e1000_copper_link_postconfig(struct e1000_hw *hw); +extern int32_t e1000_oem_set_trans_gasket(struct e1000_hw *hw); + +/* forward declarations for static support functions */ +static int32_t e1000_oem_link_m88_setup(struct e1000_hw *hw); +static int32_t e1000_oem_link_v8211_setup(struct e1000_hw *hw); +static int32_t e1000_oem_link_v8601_setup(struct e1000_hw *hw); +static int32_t e1000_oem_set_phy_mode(struct e1000_hw *hw); +static int32_t e1000_oem_detect_phy(struct e1000_hw *hw); + +#define NON_PHY_PORT 0xFFFFFFFF +//#define KINGS_BEACH +#define SILVERTIP_BC5860 + +#define DEBUGFUNC1(F, B...) DEBUGOUT1(F, B) + +/** + * e1000_oem_setup_link + * @hw: e1000_hw struct containing device specific information + * + * Returns E1000_SUCCESS, negative E1000 error code on failure + * + * Performs OEM Transceiver specific link setup as part of the + * global e1000_setup_link() function. + **/ +int32_t +e1000_oem_setup_link(struct e1000_hw *hw) +{ +#ifdef EXTERNAL_MDIO + + /* + * see e1000_setup_copper_link() as the primary example. Look at both + * the M88 and IGP functions that are called for ideas, possibly for + * power management. + */ + + int32_t ret_val; + uint32_t ctrl; + uint16_t i; + uint16_t phy_data; + + DEBUGFUNC1("%s",__func__); + + if(!hw) { + return -1; + } + /* AFU: add test to exit out if improper phy type + */ + /* relevent parts of e1000_copper_link_preconfig */ + ctrl = E1000_READ_REG(hw, CTRL); + ctrl |= E1000_CTRL_SLU; + ctrl &= ~(E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); + E1000_WRITE_REG(hw, CTRL, ctrl); + + /* this is required for *hw init */ + ret_val = e1000_oem_detect_phy(hw); + if(ret_val) { + return ret_val; + } + ret_val = e1000_oem_set_phy_mode(hw); + if(ret_val) { + return ret_val; + } + + + switch (hw->phy_id) { + case M88E1000_I_PHY_ID: + case M88E1141_E_PHY_ID: + ret_val = e1000_oem_link_m88_setup(hw); + if(ret_val) { + return ret_val; + } + break; + case VSC8211_E_PHY_ID: + ret_val = e1000_oem_link_v8211_setup(hw); + if(ret_val) { + return ret_val; + } + break; + case VSC8601_E_PHY_ID: + ret_val = e1000_oem_link_v8601_setup(hw); + if(ret_val) { + return ret_val; + } + break; + + case NON_PHY_PORT: + hw->icp_xxxx_is_link_up = TRUE; + /* Reture for skipping the latter blocks about autoneg and + link status checking */ + return E1000_SUCCESS; + default: + DEBUGOUT("Invalid PHY ID\n"); + return -E1000_ERR_PHY_TYPE; + } + + if(hw->autoneg) { + ret_val = e1000_copper_link_autoneg(hw); + if(ret_val) { + return ret_val; + } + } + else { + DEBUGOUT("Forcing speed and duplex\n"); + ret_val = e1000_phy_force_speed_duplex(hw); + } + + /* + * Check link status. Wait up to 100 microseconds for link to become + * valid. + */ + for(i = 0; i < 0xa; i++) { + ret_val = e1000_oem_read_phy_reg_ex(hw, PHY_STATUS, &phy_data); + if(ret_val) { + DEBUGOUT("Unable to read register PHY_STATUS\n"); + return ret_val; + } + + ret_val = e1000_oem_read_phy_reg_ex(hw, PHY_STATUS, &phy_data); + if(ret_val) { + DEBUGOUT("Unable to read register PHY_STATUS\n"); + return ret_val; + } + + hw->icp_xxxx_is_link_up = (phy_data & MII_SR_LINK_STATUS) != 0; + + if(phy_data & MII_SR_LINK_STATUS) { + /* Config the MAC and PHY after link is up */ + ret_val = e1000_copper_link_postconfig(hw); + if(ret_val) { + return ret_val; + } + DEBUGOUT("Valid link established!!!\n"); + return E1000_SUCCESS; + } + udelay(0xa); + } + + DEBUGOUT("Unable to establish link!!!\n"); + return E1000_SUCCESS; + +#else /* ifdef EXTERNAL_MDIO */ + + DEBUGOUT("Invalid value for hw->media_type"); + return -E1000_ERR_PHY_TYPE; + +#endif /* ifdef EXTERNAL_MDIO */ +} + + +/** + * e1000_oem_link_m88_setup + * @hw: e1000_hw struct containing device specific information + * + * Returns E1000_SUCCESS, negative E1000 error code on failure + * + * lifted from e1000_copper_link_mgp_setup, pretty much + * copied verbatim except replace e1000_phy_reset with e1000_phy_hw_reset + **/ +static int32_t +e1000_oem_link_m88_setup(struct e1000_hw *hw) +{ + int32_t ret_val; + uint16_t phy_data = 0; + + DEBUGFUNC1("%s",__func__); + + if(!hw) { + return -1; + } + + ret_val = e1000_oem_read_phy_reg_ex(hw, M88E1000_PHY_SPEC_CTRL, + &phy_data); + phy_data |= 0x00000008; + ret_val = e1000_oem_write_phy_reg_ex(hw, M88E1000_PHY_SPEC_CTRL, phy_data); + + /* phy_reset_disable is set in e1000_oem_set_phy_mode */ + if(hw->phy_reset_disable) { + return E1000_SUCCESS; + } + /* Enable CRS on TX. This must be set for half-duplex operation. */ + ret_val = e1000_oem_read_phy_reg_ex(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); + if(ret_val) { + DEBUGOUT("Unable to read M88E1000_PHY_SPEC_CTRL register\n"); + return ret_val; + } + + phy_data &= ~M88E1000_PSCR_ASSERT_CRS_ON_TX; + + /* + * Options: + * MDI/MDI-X = 0 (default) + * 0 - Auto for all speeds + * 1 - MDI mode + * 2 - MDI-X mode + * 3 - Auto for 1000Base-T only (MDI-X for 10/100Base-T modes) + */ + phy_data &= ~M88E1000_PSCR_AUTO_X_MODE; + + switch (hw->mdix) { + case 0x1: + phy_data |= M88E1000_PSCR_MDI_MANUAL_MODE; + break; + case 0x2: + phy_data |= M88E1000_PSCR_MDIX_MANUAL_MODE; + break; + case 0x3: + phy_data |= M88E1000_PSCR_AUTO_X_1000T; + break; + case 0: + default: + phy_data |= M88E1000_PSCR_AUTO_X_MODE; + break; + } + + /* + * Options: + * disable_polarity_correction = 0 (default) + * Automatic Correction for Reversed Cable Polarity + * 0 - Disabled + * 1 - Enabled + */ + phy_data &= ~M88E1000_PSCR_POLARITY_REVERSAL; + + if(hw->disable_polarity_correction == 1) { + phy_data |= M88E1000_PSCR_POLARITY_REVERSAL; + } + ret_val = e1000_oem_write_phy_reg_ex(hw, M88E1000_PHY_SPEC_CTRL, phy_data); + if(ret_val) { + DEBUGOUT("Unable to write M88E1000_PHY_SPEC_CTRL register\n"); + return ret_val; + } + + /* + * Force TX_CLK in the Extended PHY Specific Control Register + * to 25MHz clock. + */ + ret_val = e1000_oem_read_phy_reg_ex(hw, M88E1000_EXT_PHY_SPEC_CTRL, + &phy_data); + if(ret_val) { + DEBUGOUT("Unable to read M88E1000_EXT_PHY_SPEC_CTRL register\n"); + return ret_val; + } + + /* + * For Truxton, it is necessary to add RGMII tx and rx + * timing delay though the EXT_PHY_SPEC_CTRL register + */ + phy_data |= M88E1000_EPSCR_TX_TIME_CTRL; + phy_data |= M88E1000_EPSCR_RX_TIME_CTRL; + + if (hw->phy_revision < M88E1011_I_REV_4) { + + phy_data |= M88E1000_EPSCR_TX_CLK_25; + /* Configure Master and Slave downshift values */ + phy_data &= ~(M88E1000_EPSCR_MASTER_DOWNSHIFT_MASK | + M88E1000_EPSCR_SLAVE_DOWNSHIFT_MASK); + phy_data |= (M88E1000_EPSCR_MASTER_DOWNSHIFT_1X | + M88E1000_EPSCR_SLAVE_DOWNSHIFT_1X); + } + ret_val = e1000_oem_write_phy_reg_ex(hw, M88E1000_EXT_PHY_SPEC_CTRL, + phy_data); + if(ret_val) { + DEBUGOUT("Unable to read M88E1000_EXT_PHY_SPEC_CTRL register\n"); + return ret_val; + } + + + /* SW Reset the PHY so all changes take effect */ + ret_val = e1000_phy_hw_reset(hw); + if(ret_val) { + DEBUGOUT("Error Resetting the PHY\n"); + return ret_val; + } + + return E1000_SUCCESS; +} + +/** + * e1000_oem_link_v8211_setup + * @hw: e1000_hw struct containing device specific information + * + * Returns E1000_SUCCESS, negative E1000 error code on failure + * + * lifted from e1000_copper_link_mgp_setup, pretty much + * copied verbatim except replace e1000_phy_reset with e1000_phy_hw_reset + **/ +static int32_t +e1000_oem_link_v8211_setup(struct e1000_hw *hw) +{ + int32_t ret_val; + uint16_t phy_data; + + DEBUGFUNC1("%s",__func__); + + if(!hw) { + return -1; + } + + /* phy_reset_disable is set in e1000_oem_set_phy_mode */ + if(hw->phy_reset_disable) { + return E1000_SUCCESS; + } + + /* + * Options: + * disable_polarity_correction = 0 (default) + * Automatic Correction for Reversed Cable Polarity + * 0 - Disabled + * 1 - Enabled + */ + ret_val = e1000_oem_read_phy_reg_ex(hw, VSC8211_BYPASS_CTRL, &phy_data); + if (ret_val) { + DEBUGOUT("Unable to read VSC8211_BYPASS_CTRL register\n"); + return ret_val; + } + if ( hw->disable_polarity_correction ) + phy_data |= VSC8211_BYPASS_POLAR_INVERS_DISABLE; + else + phy_data &= ~VSC8211_BYPASS_POLAR_INVERS_DISABLE; + + e1000_oem_write_phy_reg_ex(hw, VSC8211_BYPASS_CTRL, phy_data); + + if (ret_val) { + DEBUGOUT("Unable to write VSC8211_BYPASS_CTRL register\n"); + return ret_val; + } + + /* Options: + * MDI/MDI-X = 0 (default) + * 0 - Auto for all speeds + * 1 - MDI mode + * 2 - MDI-X mode + * 3 - Auto for 1000Base-T only (MDI-X for 10/100Base-T modes) + */ + switch ( hw->mdix ) { + default: + break; + } + + /* SW Reset the PHY so all changes take effect */ + ret_val = e1000_phy_hw_reset(hw); + if ( ret_val ) { + DEBUGOUT("Error Resetting the PHY\n"); + return ret_val; + } + + return E1000_SUCCESS; +} + +/** + * e1000_oem_link_v8601_setup + * @hw: e1000_hw struct containing device specific information + * + * Returns E1000_SUCCESS, negative E1000 error code on failure + * + * lifted from e1000_copper_link_mgp_setup, pretty much + * copied verbatim except replace e1000_phy_reset with e1000_phy_hw_reset + **/ +static int32_t +e1000_oem_link_v8601_setup(struct e1000_hw *hw) +{ + int32_t ret_val; + uint16_t phy_data; + + DEBUGFUNC1("%s",__func__); + + + if(!hw) { + return -1; + } + + /* phy_reset_disable is set in e1000_oem_set_phy_mode */ + if(hw->phy_reset_disable) { + return E1000_SUCCESS; + } + + /* + * Options: + * disable_polarity_correction = 0 (default) + * Automatic Correction for Reversed Cable Polarity + * 0 - Disabled + * 1 - Enabled + */ + ret_val = e1000_oem_read_phy_reg_ex(hw, VSC8601_BYPASS_CTRL, &phy_data); + if (ret_val) { + DEBUGOUT("Unable to read VSC8601_BYPASS_CTRL register\n"); + return ret_val; + } + if ( hw->disable_polarity_correction ) + phy_data |= VSC8601_BYPASS_POLAR_INVERS_DISABLE; + else + phy_data &= ~VSC8601_BYPASS_POLAR_INVERS_DISABLE; + + e1000_oem_write_phy_reg_ex(hw, VSC8601_BYPASS_CTRL, phy_data); + + if (ret_val) { + DEBUGOUT("Unable to write VSC8601_BYPASS_CTRL register\n"); + return ret_val; + } + + /* Options: + * MDI/MDI-X = 0 (default) + * 0 - Auto for all speeds + * 1 - MDI mode + * 2 - MDI-X mode + * 3 - Auto for 1000Base-T only (MDI-X for 10/100Base-T modes) + */ + switch ( hw->mdix ) { + default: + break; + } + + /* SW Reset the PHY so all changes take effect */ + ret_val = e1000_phy_hw_reset(hw); + if ( ret_val ) { + DEBUGOUT("Error Resetting the PHY\n"); + return ret_val; + } + + // SDG - this is done to setup the LED mode correctly but should really + // be fixed in the hardware + phy_data = 0x0001; + e1000_oem_write_phy_reg_ex(hw, 31, phy_data); // switch to extended page registers + // phy_data = 0x0C17; // just link + phy_data = 0x0410; // blink mode + e1000_oem_write_phy_reg_ex(hw, 17, phy_data); // enhanced led mode, + // no activity (done by turning off combination feature) + phy_data = 0x0021; + e1000_oem_write_phy_reg_ex(hw, 16, phy_data); // link100 and link1000 + phy_data = 0x0000; + e1000_oem_write_phy_reg_ex(hw, 31, phy_data); // switch back to standard page registers + + return E1000_SUCCESS; +} + + +/** + * e1000_oem_force_mdi + * @hw: e1000_hw struct containing device specific information + * @resetPhy: returns true if after calling this function the + * PHY requires a reset + * + * Returns E1000_SUCCESS, negative E1000 error code on failure + * + * This is called from e1000_phy_force_speed_duplex, which is + * called from e1000_oem_setup_link. + **/ +int32_t +e1000_oem_force_mdi(struct e1000_hw *hw, int *resetPhy) +{ +#ifdef EXTERNAL_MDIO + + uint16_t phy_data; + int32_t ret_val; + + DEBUGFUNC1("%s",__func__); + + if(!hw || !resetPhy) { + return -1; + } + + /* + * a boolean to indicate if the phy needs to be reset + * + * Make note that the M88 phy is what'll be used on Truxton + * see e1000_phy_force_speed_duplex, which does the following for M88 + */ + switch (hw->phy_id) { + case M88E1000_I_PHY_ID: + case M88E1141_E_PHY_ID: + ret_val = e1000_oem_read_phy_reg_ex(hw, + M88E1000_PHY_SPEC_CTRL, + &phy_data); + if(ret_val) { + DEBUGOUT("Unable to read M88E1000_PHY_SPEC_CTRL register\n"); + return ret_val; + } + + /* + * Clear Auto-Crossover to force MDI manually. M88E1000 requires + * MDI forced whenever speed are duplex are forced. + */ + + phy_data &= ~M88E1000_PSCR_AUTO_X_MODE; + ret_val = e1000_oem_write_phy_reg_ex(hw, M88E1000_PHY_SPEC_CTRL, + phy_data); + if(ret_val) { + DEBUGOUT("Unable to write M88E1000_PHY_SPEC_CTRL register\n"); + return ret_val; + } + *resetPhy = TRUE; + break; + case VSC8211_E_PHY_ID: + ret_val = e1000_oem_read_phy_reg_ex(hw, VSC8211_BYPASS_CTRL, &phy_data); + if (ret_val) { + DEBUGOUT("Unable to read VSC8211_BYPASS_CTRL register\n"); + return ret_val; + } + /* disable automatic MDI and MDI-X */ + phy_data |= VSC8211_BYPASS_AUTO_MDI_DISABLE; + e1000_oem_write_phy_reg_ex(hw, VSC8211_BYPASS_CTRL, phy_data); + if (ret_val) { + DEBUGOUT("Unable to write VSC8211_BYPASS_CTRL register\n"); + return ret_val; + } + *resetPhy = TRUE; + break; + case VSC8601_E_PHY_ID: + ret_val = e1000_oem_read_phy_reg_ex(hw, VSC8601_BYPASS_CTRL, &phy_data); + if (ret_val) { + DEBUGOUT("Unable to read VSC8601_BYPASS_CTRL register\n"); + return ret_val; + } + /* disable automatic MDI and MDI-X */ + phy_data |= VSC8601_BYPASS_AUTO_MDI_DISABLE; + e1000_oem_write_phy_reg_ex(hw, VSC8601_BYPASS_CTRL, phy_data); + if (ret_val) { + DEBUGOUT("Unable to write VSC8601_BYPASS_CTRL register\n"); + return ret_val; + } + *resetPhy = TRUE; + break; + + case NON_PHY_PORT: + *resetPhy = FALSE; + break; + default: + DEBUGOUT("Invalid PHY ID\n"); + return -E1000_ERR_PHY_TYPE; + } + + return E1000_SUCCESS; + +#else /* ifdef EXTERNAL_MDIO */ + + if(!hw || !resetPhy) { + return -1; + } + + *resetPhy = FALSE; + return -E1000_ERR_PHY_TYPE; + +#endif /* ifdef EXTERNAL_MDIO */ +} + + +/** + * e1000_oem_phy_reset_dsp + * @hw: e1000_hw struct containing device specific information + * + * Returns E1000_SUCCESS, negative E1000 error code on failure + * + * This is called from e1000_phy_force_speed_duplex, which is + * called from e1000_oem_setup_link. + **/ +int32_t +e1000_oem_phy_reset_dsp(struct e1000_hw *hw) +{ +#ifdef EXTERNAL_MDIO + + DEBUGFUNC1("%s",__func__); + + if(!hw) { + return -1; + } + + /* + * Make note that the M88 phy is what'll be used on Truxton. + * + * See e1000_phy_force_speed_duplex, which calls e1000_phy_reset_dsp + * for the M88 PHY. The code as written references registers 29 and 30, + * which are reserved for the M88 used on Truxton, so this will be a + * no-op. + */ + switch (hw->phy_id) { + case M88E1000_I_PHY_ID: + case M88E1141_E_PHY_ID: + case VSC8211_E_PHY_ID: + case VSC8601_E_PHY_ID: + case NON_PHY_PORT: + DEBUGOUT("No DSP to reset on OEM PHY\n"); + break; + default: + DEBUGOUT("Invalid PHY ID\n"); + return -E1000_ERR_PHY_TYPE; + } + + return E1000_SUCCESS; + +#else /* ifdef EXTERNAL_MDIO */ + + return -E1000_ERR_PHY_TYPE; + +#endif /* ifdef EXTERNAL_MDIO */ +} + + +/** + * e1000_oem_cleanup_after_phy_reset + * @hw: e1000_hw struct containing device specific information + * + * Returns E1000_SUCCESS, negative E1000 error code on failure + * + * This is called from e1000_phy_force_speed_duplex, which is + * called from e1000_oem_setup_link. + **/ +int32_t +e1000_oem_cleanup_after_phy_reset(struct e1000_hw *hw) +{ +#ifdef EXTERNAL_MDIO + + uint16_t phy_data; + int32_t ret_val; + + DEBUGFUNC1("%s",__func__); + + if(!hw) { + return -1; + } + + /* + * Make note that the M88 phy is what'll be used on Truxton. + * see e1000_phy_force_speed_duplex, which does the following for M88 + */ + switch (hw->phy_id) { + case M88E1000_I_PHY_ID: + case M88E1141_E_PHY_ID: + /* + * Because we reset the PHY above, we need to re-force + * TX_CLK in the Extended PHY Specific Control Register to + * 25MHz clock. This value defaults back to a 2.5MHz clock + * when the PHY is reset. + */ + + ret_val = e1000_oem_read_phy_reg_ex(hw, + M88E1000_EXT_PHY_SPEC_CTRL, + &phy_data); + if(ret_val) { + DEBUGOUT("Unable to read M88E1000_EXT_SPEC_CTRL register\n"); + return ret_val; + } + + phy_data |= M88E1000_EPSCR_TX_CLK_25; + ret_val = e1000_oem_write_phy_reg_ex(hw, + M88E1000_EXT_PHY_SPEC_CTRL, + phy_data); + if(ret_val) { + DEBUGOUT("Unable to write M88E1000_EXT_PHY_SPEC_CTRL " + "register\n"); + return ret_val; + } + + /* + * In addition, because of the s/w reset above, we need to enable + * CRX on TX. This must be set for both full and half duplex + * operation. + */ + + ret_val = e1000_oem_read_phy_reg_ex(hw, + M88E1000_PHY_SPEC_CTRL, + &phy_data); + if(ret_val) { + DEBUGOUT("Unable to read M88E1000_PHY_SPEC_CTRL register\n"); + return ret_val; + } + + phy_data &= ~M88E1000_PSCR_ASSERT_CRS_ON_TX; + ret_val = e1000_oem_write_phy_reg_ex(hw, M88E1000_PHY_SPEC_CTRL, + phy_data); + if(ret_val) { + DEBUGOUT("Unable to write M88E1000_PHY_SPEC_CTRL register\n"); + return ret_val; + } + break; + case VSC8211_E_PHY_ID: + case VSC8601_E_PHY_ID: + case NON_PHY_PORT: + /* do nothing */ + break; + + default: + DEBUGOUT("Invalid PHY ID\n"); + return -E1000_ERR_PHY_TYPE; + } + + return E1000_SUCCESS; + +#else /* ifdef EXTERNAL_MDIO */ + + return -E1000_ERR_PHY_TYPE; + +#endif /* ifdef EXTERNAL_MDIO */ +} + + +/** + * e1000_oem_set_phy_mode + * @hw: e1000_hw struct containing device specific information + * + * Returns E1000_SUCCESS, negative E1000 error code on failure + * + * This is called from e1000_oem_setup_link which is + * called from e1000_setup_link. + **/ +static int32_t +e1000_oem_set_phy_mode(struct e1000_hw *hw) +{ + /* + * it is unclear if it is necessary to set the phy mode. Right now only + * one MAC 82545 Rev 3 does it, but the other MACs like tola do not. + * Leave the functionality off for now until it is determined that Tolapai + * needs it as well. + */ +#ifdef skip_set_mode +#undef skip_set_mode +#endif + +#ifdef skip_set_mode + int32_t ret_val; + uint16_t eeprom_data; +#endif + DEBUGFUNC1("%s",__func__); + + if(!hw) { + return -1; + } + + /* + * e1000_set_phy_mode specifically works for 82545 Rev 3 only, + * since it is a 'loner' compared to the 82545, 82546, and + * 82546 Rev 3, assume for now it is anomaly and don't repeat + * for Truxton/Haxton. + * Note that this is the approach taken in both the Windows and + * FreeBSD drivers + */ + + switch (hw->phy_id) { + case VSC8211_E_PHY_ID: + { + int32_t ret_val; + int16_t phy_data; + /* Set CMODE to the RGMII-CAT5 combination */ + phy_data = + VSC8211_PHY_CTRL1_INTF_MODE1_RGMII | + VSC8211_PHY_CTRL1_TXC_SKEW_2NS | + VSC8211_PHY_CTRL1_RXC_SKEW_2NS | + VSC8211_PHY_CTRL1_RX_IDLE_CLK_ENABLE | + VSC8211_PHY_CTRL1_INTF_MODE2_CAT5; + ret_val = e1000_oem_write_phy_reg_ex(hw, VSC8211_PHY_CTRL_1, + phy_data); + if ( ret_val ) { + DEBUGOUT("Unable to write VSC8211_PHY_CTRL_1 register\n"); + return ret_val; + } + break; + } + } + + +#ifndef skip_set_mode + DEBUGOUT("No need to call oem_set_phy_mode on Truxton\n"); +#else + /* + * Make note that the M88 phy is what'll be used on Truxton. + * + * use e1000_set_phy_mode as example + */ + switch (hw->phy_id) { + case M88E1000_I_PHY_ID: + case M88E1141_E_PHY_ID: + ret_val = e1000_read_eeprom(hw, + EEPROM_PHY_CLASS_WORD, + 1, + &eeprom_data); + if(ret_val) { + return ret_val; + } + + if((eeprom_data != EEPROM_RESERVED_WORD) && + (eeprom_data & EEPROM_PHY_CLASS_A)) + { + ret_val = e1000_oem_write_phy_reg_ex(hw, + M88E1000_PHY_PAGE_SELECT, + 0x000B); + if(ret_val) { + DEBUGOUT("Unable to write to M88E1000_PHY_PAGE_SELECT " + "register on PHY\n"); + return ret_val; + } + + ret_val = e1000_oem_write_phy_reg_ex(hw, + M88E1000_PHY_GEN_CONTROL, + 0x8104); + if(ret_val) { + DEBUGOUT("Unable to write to M88E1000_PHY_GEN_CONTROL" + "register on PHY\n"); + return ret_val; + } + + hw->phy_reset_disable = FALSE; + } + break; + default: + DEBUGOUT("Invalid PHY ID\n"); + return -E1000_ERR_PHY_TYPE; + } +#endif + + return E1000_SUCCESS; + +} + + +/** + * e1000_oem_detect_phy + * @hw: e1000_hw struct containing device specific information + * + * Fills hw->phy_type, hw->phy_id and hw->phy_revision fields as well + * as verifies that the PHY identified is one that is comprehended + * by the driver. + * + * This borrows heavily from e1000_detect_gig_phy + **/ +static int32_t +e1000_oem_detect_phy(struct e1000_hw *hw) +{ + int32_t ret_val; + uint16_t phy_id_high, phy_id_low; + + DEBUGFUNC1("%s",__func__); + + if(!hw) { + return -1; + } + hw->phy_type = e1000_phy_oem; + + { + struct e1000_adapter *adapter; + uint32_t dev_num; + adapter = (struct e1000_adapter *) hw->back; + dev_num = PCI_SLOT(adapter->pdev->devfn); + switch ( dev_num ) { +#ifndef CONFIG_E1000_EP80579_PHY0 + case ICP_XXXX_MAC_0: + hw->phy_id = NON_PHY_PORT; + return E1000_SUCCESS; +#endif +#ifndef CONFIG_E1000_EP80579_PHY1 + case ICP_XXXX_MAC_1: + hw->phy_id = NON_PHY_PORT; + return E1000_SUCCESS; +#endif +#ifndef CONFIG_E1000_EP80579_PHY2 + case ICP_XXXX_MAC_2: + hw->phy_id = NON_PHY_PORT; + return E1000_SUCCESS; +#endif + } + } + + + ret_val = e1000_oem_read_phy_reg_ex(hw, PHY_ID1, &phy_id_high); + if(ret_val) { + DEBUGOUT("Unable to read PHY register PHY_ID1\n"); + return ret_val; + } + + udelay(0x14); + ret_val = e1000_oem_read_phy_reg_ex(hw, PHY_ID2, &phy_id_low); + if(ret_val) { + DEBUGOUT("Unable to read PHY register PHY_ID2\n"); + return ret_val; + } + hw->phy_id = (uint32_t) ((phy_id_high << 0x10) + + (phy_id_low & PHY_REVISION_MASK)); + hw->phy_revision = (uint32_t) phy_id_low & ~PHY_REVISION_MASK; + + return E1000_SUCCESS; +} + + +/** + * e1000_oem_get_tipg + * @hw: e1000_hw struct containing device specific information + * + * Returns the value of the Inter Packet Gap (IPG) Transmit Time (IPGT) in the + * Transmit IPG register appropriate for the given PHY. This field is only 10 + * bits wide. + * + * In the original e1000 code, only the IPGT field varied between media types. + * If the OEM phy requires setting IPG Receive Time 1 & 2 Registers, it would + * be required to modify the e1000_config_tx() function to accomdate the change + * + **/ +uint32_t +e1000_oem_get_tipg(struct e1000_hw *hw) +{ +#ifdef EXTERNAL_MDIO + + uint32_t phy_num; + + DEBUGFUNC1("%s",__func__); + + if(!hw) { + return DEFAULT_ICP_XXXX_TIPG_IPGT; + } + + switch (hw->phy_id) { + case M88E1000_I_PHY_ID: + case M88E1141_E_PHY_ID: + case VSC8211_E_PHY_ID: + case VSC8601_E_PHY_ID: + case NON_PHY_PORT: + phy_num = DEFAULT_ICP_XXXX_TIPG_IPGT; + break; + default: + DEBUGOUT("Invalid PHY ID\n"); + return DEFAULT_ICP_XXXX_TIPG_IPGT; + } + + return phy_num; + +#else /* ifdef EXTERNAL_MDIO */ + + /* return the default value required by ICP_xxxx style MACS */ + DEBUGOUT("Invalid value for transceiver type, return default" + " TIPG.IPGT value\n"); + return DEFAULT_ICP_XXXX_TIPG_IPGT; + +#endif /* ifdef EXTERNAL_MDIO */ +} + + +/** + * e1000_oem_phy_is_copper + * @hw: e1000_hw struct containing device specific information + * + * Test for media type within the e1000 driver is common, so this is a simple + * test for copper PHYs. The ICP_XXXX family of controllers initially only + * supported copper interconnects (no TBI (ten bit interface) for Fiber + * existed). If future revs support either Fiber or an internal SERDES, it + * may become necessary to evaluate where this function is used to go beyond + * determining whether or not media type is just copper. + * + **/ +int +e1000_oem_phy_is_copper(struct e1000_hw *hw) +{ +#ifdef EXTERNAL_MDIO + + int isCopper = TRUE; + + DEBUGFUNC1("%s",__func__); + + if(!hw) { + return isCopper; + } + + switch (hw->phy_id) { + case M88E1000_I_PHY_ID: + case M88E1141_E_PHY_ID: + case VSC8211_E_PHY_ID: + case VSC8601_E_PHY_ID: + isCopper = TRUE; + break; + case NON_PHY_PORT: + isCopper = FALSE; + break; + default: + DEBUGOUT("Invalid PHY ID\n"); + return -E1000_ERR_PHY_TYPE; + } + + return isCopper; + +#else /* ifdef EXTERNAL_MDIO */ + + /* + * caught between returning true or false. True allows it to + * be entered into && statements w/o ill effect, but false + * would make more sense + */ + DEBUGOUT("Invalid value for transceiver type, return FALSE\n"); + return FALSE; + +#endif /* ifdef EXTERNAL_MDIO */ +} + + +/** + * e1000_oem_get_phy_dev_number + * @hw: e1000_hw struct containing device specific information + * + * For ICP_XXXX family of devices, there are 3 MACs, each of which may + * have a different PHY (and indeed a different media interface). This + * function is used to indicate which of the MAC/PHY pairs we are interested + * in. + * + **/ +uint32_t +e1000_oem_get_phy_dev_number(struct e1000_hw *hw) +{ +#ifdef EXTERNAL_MDIO + + /* + * for ICP_XXXX family of devices, the three network interfaces are + * differentiated by their PCI device number, where the three share + * the same PCI bus + */ + struct e1000_adapter *adapter; + uint32_t device_number; + + DEBUGFUNC1("%s",__func__); + + if(!hw) { + return 0; + } + + adapter = (struct e1000_adapter *) hw->back; + device_number = PCI_SLOT(adapter->pdev->devfn); + + switch(device_number) + { + case ICP_XXXX_MAC_0: + hw->phy_addr = 0x00; + break; + case ICP_XXXX_MAC_1: + hw->phy_addr = 0x01; + break; + case ICP_XXXX_MAC_2: + hw->phy_addr = 0x02; + break; + default: hw->phy_addr = 0x00; + } + return hw->phy_addr; + +#else /* ifdef EXTERNAL_MDIO */ + DEBUGOUT("Invalid value for transceiver type, return 0\n"); + return 0; + +#endif /* ifdef EXTERNAL_MDIO */ +} + + +/** + * e1000_oem_mii_ioctl + * @adapter: e1000_hw struct containing device specific information + * @flags: The saved adapter->stats_lock flags from the initiating spinlock + * @ifr: interface request structure for socket ioctls + * @cmd: the original IOCTL command that instigated the call chain. + * + * This function abstracts out the code necessary to service the + * SIOCSMIIREG case within the e1000_mii_ioctl() for oem PHYs. + * e1000_mii_ioctl() was implemented for copper phy's only and this + * function will only be called if e1000_oem_phy_is_copper() returns true for + * a given MAC. Note that e1000_mii_ioctl() has a compile flag + * and exists only if SIOCGMIIPHY is defined. + * + * NOTE: a spinlock is in effect for the duration of this call. It is + * imperative that a negative value be returned on any error, so + * the spinlock can be released properly. + * + **/ +int +e1000_oem_mii_ioctl(struct e1000_adapter *adapter, unsigned long flags, + struct ifreq *ifr, int cmd) +{ +#ifdef EXTERNAL_MDIO + + struct mii_ioctl_data *data = if_mii(ifr); + uint16_t mii_reg = data->val_in; + uint16_t spddplx; + int retval; + + DEBUGFUNC1("%s",__func__); + + if(!adapter || !ifr) { + return -1; + } + switch (data->reg_num) { + case PHY_CTRL: + if(mii_reg & MII_CR_POWER_DOWN) { + break; + } + if(mii_reg & MII_CR_AUTO_NEG_EN) { + adapter->hw.autoneg = 1; + adapter->hw.autoneg_advertised = ICP_XXXX_AUTONEG_ADV_DEFAULT; + } + else { + if(mii_reg & 0x40) { + spddplx = SPEED_1000; + } + else if(mii_reg & 0x2000) { + spddplx = SPEED_100; + } + else { + spddplx = SPEED_10; + } + spddplx += (mii_reg & 0x100) ? FULL_DUPLEX : HALF_DUPLEX; + retval = e1000_set_spd_dplx(adapter, spddplx); + if(retval) { + return retval; + } + } + if(netif_running(adapter->netdev)) { + e1000_down(adapter); + e1000_up(adapter); + } + else { + e1000_reset(adapter); + } + break; + case M88E1000_PHY_SPEC_CTRL: + case M88E1000_EXT_PHY_SPEC_CTRL: + retval = e1000_phy_reset(&adapter->hw); + if(retval) { + DEBUGOUT("Error resetting the PHY\n"); + return -EIO; + } + break; + } + + return E1000_SUCCESS; + +#else /* ifdef EXTERNAL_MDIO */ + + return -EOPNOTSUPP; + +#endif /* ifdef EXTERNAL_MDIO */ +} + + +/** + * e1000_oem_fiber_live_in_suspend + * @hw: e1000_hw struct containing device specific information + * + * This is called within e1000_suspend() to allow an action to be performed + * on an oem phy before before the MAC goes into suspend. This is only called + * if the STATUS.LU (link up) bit has been previous set. + * + * For ICP_XXXX, this is a no op + **/ +void e1000_oem_fiber_live_in_suspend(struct e1000_hw *hw) +{ +#ifdef EXTERNAL_MDIO + + DEBUGFUNC1("%s",__func__); + + if(!hw) { + return; + } + return; + +#else /* ifdef EXTERNAL_MDIO */ + + return; + +#endif /* ifdef EXTERNAL_MDIO */ +} + + +/** + * e1000_oem_get_phy_regs + * @adapter e1000_adapter struct containing device specific information + * @data unsigned integer array of size data_len + * @data_len number of elements in data + * + * This is called by e1000_get_regs() in response to an ethtool request + * to return the data of the controller. Most of the data returned is from + * the MAC, but some data comes from the PHY, thus from this f(). + * + * Note: The call to e1000_get_regs() assumed an array of 24 elements + * where the last 11 are passed to this function. If the array + * that is passed to the calling function has its size or element + * defintions changed, this function becomes broken. + * + **/ +void e1000_oem_get_phy_regs(struct e1000_adapter *adapter, uint32_t *data, + uint32_t data_len) +{ +#define EXPECTED_ARRAY_LEN 11 + uint32_t corrected_len; + + DEBUGFUNC1("%s",__func__); + + if(!adapter || !data) { + return; + } + + /* This f(n) expects to have EXPECTED_ARRAY_LEN elements to initialize. + * Use the corrected_length variable to make sure we don't exceed that + * length + */ + corrected_len = data_len>EXPECTED_ARRAY_LEN + ? EXPECTED_ARRAY_LEN : data_len; + memset(data, 0, corrected_len*sizeof(uint32_t)); + +#ifdef EXTERNAL_MDIO + + /* + * Fill data[] with... + * + * [0] = cable length + * [1] = cable length + * [2] = cable length + * [3] = cable length + * [4] = extended 10bt distance + * [5] = cable polarity + * [6] = cable polarity + * [7] = polarity correction enabled + * [8] = undefined + * [9] = phy receive errors + * [10] = mdix mode + */ + switch (adapter->hw.phy_id) { + case M88E1000_I_PHY_ID: + case M88E1141_E_PHY_ID: + if(corrected_len > 0) { + e1000_oem_read_phy_reg_ex(&adapter->hw, + M88E1000_PHY_SPEC_STATUS, + (uint16_t *) &data[0]); + } + if(corrected_len > 0x1){ + data[0x1] = 0x0; /* Dummy (to align w/ IGP phy reg dump) */ + } + if(corrected_len > 0x2) { + data[0x2] = 0x0; /* Dummy (to align w/ IGP phy reg dump) */ + } + if(corrected_len > 0x3) { + data[0x3] = 0x0; /* Dummy (to align w/ IGP phy reg dump) */ + } + if(corrected_len > 0x4) { + e1000_oem_read_phy_reg_ex(&adapter->hw, M88E1000_PHY_SPEC_CTRL, + (uint16_t *) &data[0x4]); + } + if(corrected_len > 0x5) { + data[0x5] = data[0x0]; + } + if(corrected_len > 0x6) { + data[0x6] = 0x0; /* Dummy (to align w/ IGP phy reg dump) */ + } + if(corrected_len > 0x7) { + data[0x7] = data[0x4]; + } + /* phy receive errors */ + if(corrected_len > 0x9) { + data[0x9] = adapter->phy_stats.receive_errors; + } + if(corrected_len > 0xa) { + data[0xa] = data[0x0]; + } + break; + default: + DEBUGOUT("Invalid PHY ID\n"); + return; + } +#endif /* ifdef EXTERNAL_MDIO */ + +#undef EXPECTED_ARRAY_LEN + return; +} + + +/** + * e1000_oem_phy_loopback + * @adapter e1000_adapter struct containing device specific information + * + * This is called from e1000_set_phy_loopback in response from call from + * ethtool to place the PHY into loopback mode. + **/ +int +e1000_oem_phy_loopback(struct e1000_adapter *adapter) +{ +#ifdef EXTERNAL_MDIO + + int ret_val; + uint32_t ctrl_reg = 0; + + DEBUGFUNC1("%s",__func__); + + if(!adapter) { + return -1; + } + + /* + * This borrows liberally from e1000_integrated_phy_loopback(). + * e1000_nonintegrated_phy_loopback() was also a point of reference + * since it was similar. The biggest difference between the two + * was that nonintegrated called e1000_phy_reset_clk_and_crs(), + * hopefully this won't matter as CRS required for half-duplex + * operation and this is set to full duplex. + * + * Make note that the M88 phy is what'll be used on Truxton + * Loopback configuration is the same for each of the supported PHYs. + */ + switch (adapter->hw.phy_id) { + case M88E1000_I_PHY_ID: + case M88E1141_E_PHY_ID: + + adapter->hw.autoneg = FALSE; + + /* turn off Auto-MDI/MDIX */ + /*ret_val = e1000_oem_write_phy_reg_ex(&adapter->hw, + M88E1000_PHY_SPEC_CTRL, 0x0808); + if(ret_val) + { + DEBUGOUT("Unable to write to register M88E1000_PHY_SPEC_CTRL\n"); + return ret_val; + } + */ + /* reset to update Auto-MDI/MDIX */ + /* ret_val = e1000_oem_write_phy_reg_ex(&adapter->hw, + PHY_CTRL, 0x9140); + if(ret_val) + { + DEBUGOUT("Unable to write to register PHY__CTRL\n"); + return ret_val; + } + */ + /* autoneg off */ + /*ret_val = e1000_oem_write_phy_reg_ex(&adapter->hw, + PHY_CTRL, 0x8140); */ + ret_val = e1000_oem_write_phy_reg_ex(&adapter->hw, PHY_CTRL, 0xa100); + if(ret_val) { + DEBUGOUT("Unable to write to register PHY_CTRL\n"); + return ret_val; + } + + + /* force 1000, set loopback */ + /*ret_val = + e1000_oem_write_phy_reg_ex(&adapter->hw, PHY_CTRL, 0x4140); */ + ret_val = e1000_oem_write_phy_reg_ex(&adapter->hw, PHY_CTRL, 0x6100); + if(ret_val) { + DEBUGOUT("Unable to write to register PHY_CTRL\n"); + return ret_val; + } + + ctrl_reg = E1000_READ_REG(&adapter->hw, CTRL); + ctrl_reg &= ~E1000_CTRL_SPD_SEL; /* Clear the speed sel bits */ + ctrl_reg |= (E1000_CTRL_FRCSPD /* Set the Force Speed Bit */ + | E1000_CTRL_FRCDPX /* Set the Force Duplex Bit */ + | E1000_CTRL_SPD_100 /* Force Speed to 1000 */ + | E1000_CTRL_FD); /* Force Duplex to FULL */ + /* | E1000_CTRL_ILOS); */ /* Invert Loss of Signal */ + + E1000_WRITE_REG(&adapter->hw, CTRL, ctrl_reg); + + /* + * Write out to PHY registers 29 and 30 to disable the Receiver. + * This directly lifted from e1000_phy_disable_receiver(). + * + * The code is currently commented out as for the M88 used in + * Truxton, registers 29 and 30 are unutilized. Leave in, just + * in case we are on the receiving end of an 'undocumented' + * feature + */ + /* + * e1000_oem_write_phy_reg_ex(&adapter->hw, 29, 0x001F); + * e1000_oem_write_phy_reg_ex(&adapter->hw, 30, 0x8FFC); + * e1000_oem_write_phy_reg_ex(&adapter->hw, 29, 0x001A); + * e1000_oem_write_phy_reg_ex(&adapter->hw, 30, 0x8FF0); + */ + + break; + default: + DEBUGOUT("Invalid PHY ID\n"); + return -E1000_ERR_PHY_TYPE; + } + + return 0; + +#else /* ifdef EXTERNAL_MDIO */ + + return -E1000_ERR_PHY_TYPE; + +#endif /* ifdef EXTERNAL_MDIO */ + +} + + +/** + * e1000_oem_loopback_cleanup + * @adapter e1000_adapter struct containing device specific information + * + * This is called from e1000_loopback_cleanup in response from call from + * ethtool to place the PHY out of loopback mode. This handles the OEM + * specific part of loopback cleanup. + **/ +void +e1000_oem_loopback_cleanup(struct e1000_adapter *adapter) +{ +#ifdef EXTERNAL_MDIO + + /* + * This borrows liberally from e1000_loopback_cleanup(). + * making note that the M88 phy is what'll be used on Truxton + * + * Loopback cleanup is the same for all supported PHYs. + */ + int32_t ret_val; + uint16_t phy_reg; + + DEBUGFUNC1("%s",__func__); + + if(!adapter) { + return ; + } + + switch (adapter->hw.phy_id) { + case M88E1000_I_PHY_ID: + case M88E1141_E_PHY_ID: + default: + adapter->hw.autoneg = TRUE; + + ret_val = e1000_oem_read_phy_reg_ex(&adapter->hw, PHY_CTRL, + &phy_reg); + if(ret_val) { + DEBUGOUT("Unable to read to register PHY_CTRL\n"); + return; + } + + if(phy_reg & MII_CR_LOOPBACK) { + phy_reg &= ~MII_CR_LOOPBACK; + + ret_val = e1000_oem_write_phy_reg_ex(&adapter->hw, PHY_CTRL, + phy_reg); + if(ret_val) { + DEBUGOUT("Unable to write to register PHY_CTRL\n"); + return; + } + + e1000_phy_reset(&adapter->hw); + } + } + +#endif /* ifdef EXTERNAL_MDIO */ + return; + +} + + +/** + * e1000_oem_phy_speed_downgraded + * @hw e1000_hw struct containing device specific information + * @isDowngraded returns with value > 0 if the link belonging to hw + * has been downshifted + * + * Called by e1000_check_downshift(), checks the PHY to see if it running + * at as speed slower than its maximum. + **/ +uint32_t +e1000_oem_phy_speed_downgraded(struct e1000_hw *hw, uint16_t *isDowngraded) +{ +#ifdef EXTERNAL_MDIO + + uint32_t ret_val; + uint16_t phy_data; + + DEBUGFUNC1("%s",__func__); + + if(!hw || !isDowngraded) { + return 1; + } + + /* + * borrow liberally from E1000_check_downshift e1000_phy_m88 case. + * Make note that the M88 phy is what'll be used on Truxton + */ + + switch (hw->phy_id) { + case M88E1000_I_PHY_ID: + case M88E1141_E_PHY_ID: + ret_val = e1000_oem_read_phy_reg_ex(hw, M88E1000_PHY_SPEC_STATUS, + &phy_data); + if(ret_val) { + DEBUGOUT("Unable to read register M88E1000_PHY_SPEC_STATUS\n"); + return ret_val; + } + + *isDowngraded = (phy_data & M88E1000_PSSR_DOWNSHIFT) + >> M88E1000_PSSR_DOWNSHIFT_SHIFT; + + break; + + case VSC8211_E_PHY_ID: + ret_val = e1000_oem_read_phy_reg_ex(hw, VSC8211_AUX_CTRL_STS, + &phy_data); + if ( ret_val ) { + DEBUGOUT("Unable to read register VSC8211_AUX_CTRL_STS\n"); + return ret_val; + } + *isDowngraded = (phy_data & VSC8211_AUX_SPEED_MASK) != + VSC8211_AUX_SPEED_IS_1000; + break; + + case VSC8601_E_PHY_ID: + ret_val = e1000_oem_read_phy_reg_ex(hw, VSC8601_AUX_CTRL_STS, + &phy_data); + if ( ret_val ) { + DEBUGOUT("Unable to read register VSC8601_AUX_CTRL_STS\n"); + return ret_val; + } + *isDowngraded = (phy_data & VSC8601_AUX_SPEED_MASK) != + VSC8601_AUX_SPEED_IS_1000; + break; + + + default: + DEBUGOUT("Invalid PHY ID\n"); + return 1; + } + + return 0; + +#else /* ifdef EXTERNAL_MDIO */ + + if(!hw || !isDowngraded) { + return 1; + } + + *isDowngraded = 0; + return 0; + +#endif /* ifdef EXTERNAL_MDIO */ +} + + +/** + * e1000_oem_check_polarity + * @hw e1000_hw struct containing device specific information + * @isDowngraded returns with value > 0 if the link belonging to hw + * has its polarity shifted. + * + * Called by e1000_check_downshift(), checks the PHY to see if it running + * at as speed slower than its maximum. + **/ +int32_t +e1000_oem_check_polarity(struct e1000_hw *hw, uint16_t *polarity) +{ +#ifdef EXTERNAL_MDIO + + int32_t ret_val; + uint16_t phy_data; + + DEBUGFUNC1("%s",__func__); + + if(!hw || !polarity) { + return -1; + } + + /* + * borrow liberally from e1000_check_polarity. + * Make note that the M88 phy is what'll be used on Truxton + */ + + switch (hw->phy_id) { + case M88E1000_I_PHY_ID: + case M88E1141_E_PHY_ID: + /* return the Polarity bit in the Status register. */ + ret_val = e1000_oem_read_phy_reg_ex(hw, + M88E1000_PHY_SPEC_STATUS, + &phy_data); + if(ret_val) { + DEBUGOUT("Unable to read register M88E1000_PHY_SPEC_STATUS\n"); + return ret_val; + } + + *polarity = (phy_data & M88E1000_PSSR_REV_POLARITY) + >> M88E1000_PSSR_REV_POLARITY_SHIFT; + + break; + case VSC8211_E_PHY_ID: + case VSC8601_E_PHY_ID: + DEBUGOUT("check polarity is not supported by VSC8XXX\n"); + return -E1000_ERR_PHY_TYPE; + default: + DEBUGOUT("Invalid PHY ID\n"); + return -E1000_ERR_PHY_TYPE; + } + return 0; + +#else /* ifdef EXTERNAL_MDIO */ + + if(!hw || !polarity) { + return -1; + } + + *polarity = 0; + return -1; + +#endif /* ifdef EXTERNAL_MDIO */ +} + + +/** + * e1000_oem_phy_is_full_duplex + * @hw e1000_hw struct containing device specific information + * @isFD a boolean returning true if phy is full duplex + * + * This is called as part of e1000_config_mac_to_phy() to align + * the MAC with the PHY. It turns out on ICP_XXXX, this is not + * done automagically. + **/ +int32_t +e1000_oem_phy_is_full_duplex(struct e1000_hw *hw, int *isFD) +{ +#ifdef EXTERNAL_MDIO + + uint16_t phy_data; + int32_t ret_val; + + DEBUGFUNC1("%s",__func__); + + if(!hw || !isFD) { + return -1; + } + /* + * Make note that the M88 phy is what'll be used on Truxton + * see e1000_config_mac_to_phy + */ + + switch (hw->phy_id) { + case M88E1000_I_PHY_ID: + case M88E1141_E_PHY_ID: + ret_val = e1000_oem_read_phy_reg_ex(hw, M88E1000_PHY_SPEC_STATUS, + &phy_data); + if(ret_val) { + DEBUGOUT("Unable to read register M88E1000_PHY_SPEC_STATUS\n"); + return ret_val; + } + *isFD = (phy_data & M88E1000_PSSR_DPLX) != 0; + + break; + case VSC8211_E_PHY_ID: + ret_val = e1000_oem_read_phy_reg_ex(hw, VSC8211_AUX_CTRL_STS, &phy_data); + if ( ret_val ) { + DEBUGOUT("Unable to read register VSC8211_AUX_CTRL_STS\n"); + return ret_val; + } + *isFD = (phy_data & VSC8211_AUX_FDX_MASK)==VSC8211_AUX_FDX_IS_FULL; + break; + case VSC8601_E_PHY_ID: + ret_val = e1000_oem_read_phy_reg_ex(hw, VSC8601_AUX_CTRL_STS, &phy_data); + if ( ret_val ) { + DEBUGOUT("Unable to read register VSC8601_AUX_CTRL_STS\n"); + return ret_val; + } + *isFD = (phy_data & VSC8601_AUX_FDX_MASK)==VSC8601_AUX_FDX_IS_FULL; + break; + + case NON_PHY_PORT: + *isFD = TRUE; + break; + default: + DEBUGOUT("Invalid PHY ID\n"); + return -E1000_ERR_PHY_TYPE; + } + + return E1000_SUCCESS; + +#else /* ifdef EXTERNAL_MDIO */ + + if(!hw || !isFD) { + return -1; + } + *isFD = FALSE; + return -E1000_ERR_PHY_TYPE; + +#endif /* ifdef EXTERNAL_MDIO */ +} +/** + * e1000_oem_phy_is_speed_1000 + * @hw e1000_hw struct containing device specific information + * @is1000 a boolean returning true if phy is running at 1000 + * + * This is called as part of e1000_config_mac_to_phy() to align + * the MAC with the PHY. It turns out on ICP_XXXX, this is not + * done automagically. + **/ +int32_t +e1000_oem_phy_is_speed_1000(struct e1000_hw *hw, int *is1000) +{ +#ifdef EXTERNAL_MDIO + + uint16_t phy_data; + int32_t ret_val; + + DEBUGFUNC1("%s",__func__); + + if(!hw || !is1000) { + return -1; + } + /* + * Make note that the M88 phy is what'll be used on Truxton. + * see e1000_config_mac_to_phy + */ + + switch (hw->phy_id) { + case M88E1000_I_PHY_ID: + case M88E1141_E_PHY_ID: + ret_val = e1000_oem_read_phy_reg_ex(hw, M88E1000_PHY_SPEC_STATUS, + &phy_data); + if(ret_val) { + DEBUGOUT("Unable to read register M88E1000_PHY_SPEC_STATUS\n"); + return ret_val; + } + *is1000 = (phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_1000MBS; + break; + case VSC8211_E_PHY_ID: + ret_val = e1000_oem_read_phy_reg_ex(hw, VSC8211_AUX_CTRL_STS, &phy_data); + if ( ret_val ) { + DEBUGOUT("Unable to read register VSC8211_AUX_CTRL_STS\n"); + return ret_val; + } + *is1000 = (phy_data & VSC8211_AUX_SPEED_MASK) == + VSC8211_AUX_SPEED_IS_1000; + break; + case VSC8601_E_PHY_ID: + ret_val = e1000_oem_read_phy_reg_ex(hw, VSC8601_AUX_CTRL_STS, &phy_data); + if ( ret_val ) { + DEBUGOUT("Unable to read register VSC8601_AUX_CTRL_STS\n"); + return ret_val; + } + *is1000 = (phy_data & VSC8601_AUX_SPEED_MASK) == + VSC8601_AUX_SPEED_IS_1000; + break; + + case NON_PHY_PORT: + *is1000 = TRUE; + break; + default: + DEBUGOUT("Invalid PHY ID\n"); + return -E1000_ERR_PHY_TYPE; + } + + return E1000_SUCCESS; + +#else /* ifdef EXTERNAL_MDIO */ + + if(!hw || !is1000) { + return -1; + } + *is1000 = FALSE; + return -E1000_ERR_PHY_TYPE; + +#endif /* ifdef EXTERNAL_MDIO */ +} + +/** + * e1000_oem_phy_is_speed_100 + * @hw e1000_hw struct containing device specific information + * @is100 a boolean returning true if phy is running at 100 + * + * This is called as part of e1000_config_mac_to_phy() to align + * the MAC with the PHY. It turns out on ICP_XXXX, this is not + * done automagically. + **/ +int32_t +e1000_oem_phy_is_speed_100(struct e1000_hw *hw, int *is100) +{ +#ifdef EXTERNAL_MDIO + + uint16_t phy_data; + int32_t ret_val; + + DEBUGFUNC1("%s",__func__); + + if(!hw || !is100) { + return -1; + } + /* + * Make note that the M88 phy is what'll be used on Truxton + * see e1000_config_mac_to_phy + */ + switch (hw->phy_id) { + case M88E1000_I_PHY_ID: + case M88E1141_E_PHY_ID: + ret_val = e1000_oem_read_phy_reg_ex(hw, + M88E1000_PHY_SPEC_STATUS, + &phy_data); + if(ret_val) { + DEBUGOUT("Unable to read register M88E1000_PHY_SPEC_STATUS\n"); + return ret_val; + } + *is100 = (phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_100MBS; + break; + case VSC8211_E_PHY_ID: + ret_val = e1000_oem_read_phy_reg_ex(hw, VSC8211_AUX_CTRL_STS, &phy_data); + if ( ret_val ) { + DEBUGOUT("Unable to read register VSC8211_AUX_CTRL_STS\n"); + return ret_val; + } + *is100 = (phy_data & VSC8211_AUX_SPEED_MASK) == + VSC8211_AUX_SPEED_IS_100; + break; + case VSC8601_E_PHY_ID: + ret_val = e1000_oem_read_phy_reg_ex(hw, VSC8601_AUX_CTRL_STS, &phy_data); + if ( ret_val ) { + DEBUGOUT("Unable to read register VSC8601_AUX_CTRL_STS\n"); + return ret_val; + } + *is100 = (phy_data & VSC8601_AUX_SPEED_MASK) == + VSC8601_AUX_SPEED_IS_100; + break; + case NON_PHY_PORT: + *is100 = FALSE; + break; + default: + DEBUGOUT("Invalid PHY ID\n"); + return -E1000_ERR_PHY_TYPE; + } + + return E1000_SUCCESS; + +#else /* ifdef EXTERNAL_MDIO */ + + if(!hw || !is100) { + return -1; + } + *is100 = FALSE; + return -E1000_ERR_PHY_TYPE; + +#endif /* ifdef EXTERNAL_MDIO */ +} + +/** + * e1000_oem_phy_get_info + * @hw struct e1000_hw containing hardware specific data + * @phy_info struct e1000_phy_info that returned + * + * This is called by e1000_phy_get_info to gather PHY specific + * data. This is called for copper media based phys. + **/ +int32_t +e1000_oem_phy_get_info(struct e1000_hw *hw, + struct e1000_phy_info *phy_info) +{ +#ifdef EXTERNAL_MDIO + + int32_t ret_val; + uint16_t phy_data, polarity; + + DEBUGFUNC1("%s",__func__); + + if(!hw || !phy_info) { + return -1; + } + + /* + * Make note that the M88 phy is what'll be used on Truxton + * see e1000_phy_m88_get_info + */ + switch (hw->phy_id) { + case M88E1000_I_PHY_ID: + case M88E1141_E_PHY_ID: + /* The downshift status is checked only once, after link is + * established and it stored in the hw->speed_downgraded parameter.*/ + phy_info->downshift = (e1000_downshift)hw->speed_downgraded; + + ret_val = e1000_oem_read_phy_reg_ex(hw, M88E1000_PHY_SPEC_CTRL, + &phy_data); + if(ret_val) { + DEBUGOUT("Unable to read register M88E1000_PHY_SPEC_CTRL\n"); + return ret_val; + } + + phy_info->extended_10bt_distance = + (phy_data & M88E1000_PSCR_10BT_EXT_DIST_ENABLE) + >> M88E1000_PSCR_10BT_EXT_DIST_ENABLE_SHIFT; + phy_info->polarity_correction = + (phy_data & M88E1000_PSCR_POLARITY_REVERSAL) + >> M88E1000_PSCR_POLARITY_REVERSAL_SHIFT; + + /* Check polarity status */ + ret_val = e1000_oem_check_polarity(hw, &polarity); + if(ret_val) { + return ret_val; + } + + phy_info->cable_polarity = polarity; + + ret_val = e1000_oem_read_phy_reg_ex(hw, M88E1000_PHY_SPEC_STATUS, + &phy_data); + if(ret_val) { + DEBUGOUT("Unable to read register M88E1000_PHY_SPEC_STATUS\n"); + return ret_val; + } + + phy_info->mdix_mode = (phy_data & M88E1000_PSSR_MDIX) + >> M88E1000_PSSR_MDIX_SHIFT; + + if ((phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_1000MBS) { + /* Cable Length Estimation and Local/Remote Receiver Information + * are only valid at 1000 Mbps. + */ + phy_info->cable_length = + (phy_data & M88E1000_PSSR_CABLE_LENGTH) + >> M88E1000_PSSR_CABLE_LENGTH_SHIFT; + + ret_val = e1000_oem_read_phy_reg_ex(hw, PHY_1000T_STATUS, + &phy_data); + if(ret_val) { + DEBUGOUT("Unable to read register PHY_1000T_STATUS\n"); + return ret_val; + } + + phy_info->local_rx = (phy_data & SR_1000T_LOCAL_RX_STATUS) + >> SR_1000T_LOCAL_RX_STATUS_SHIFT; + + phy_info->remote_rx = (phy_data & SR_1000T_REMOTE_RX_STATUS) + >> SR_1000T_REMOTE_RX_STATUS_SHIFT; + } + + break; + default: + DEBUGOUT("Invalid PHY ID\n"); + return -E1000_ERR_PHY_TYPE; + } + + return E1000_SUCCESS; + +#else /* ifdef EXTERNAL_MDIO */ + + return -E1000_ERR_PHY_TYPE; + +#endif /* ifdef EXTERNAL_MDIO */ +} + +/** + * e1000_oem_phy_hw_reset + * @hw struct e1000_hw containing hardware specific data + * + * This function will perform a software initiated reset of + * the PHY + **/ +int32_t +e1000_oem_phy_hw_reset(struct e1000_hw *hw) +{ +#ifdef EXTERNAL_MDIO + + int32_t ret_val; + uint16_t phy_data; + + DEBUGFUNC1("%s",__func__); + + if(!hw) { + return -1; + } + /* + * This code pretty much copies the default case from + * e1000_phy_reset() as that is what is appropriate for + * the M88 used in truxton. + */ + switch (hw->phy_id) { + case M88E1000_I_PHY_ID: + case M88E1141_E_PHY_ID: + case VSC8211_E_PHY_ID: + case VSC8601_E_PHY_ID: + ret_val = e1000_oem_read_phy_reg_ex(hw, PHY_CTRL, &phy_data); + if(ret_val) { + DEBUGOUT("Unable to read register PHY_CTRL\n"); + return ret_val; + } + + phy_data |= MII_CR_RESET; + ret_val = e1000_oem_write_phy_reg_ex(hw, PHY_CTRL, phy_data); + if(ret_val) { + DEBUGOUT("Unable to write register PHY_CTRL\n"); + return ret_val; + } + + udelay(1); + break; + case NON_PHY_PORT: + /* do nothing */ + break; + default: + DEBUGOUT("Invalid PHY ID\n"); + return -E1000_ERR_PHY_TYPE; + } + + return E1000_SUCCESS; + +#else /* ifdef EXTERNAL_MDIO */ + + return -E1000_ERR_PHY_TYPE; + +#endif /* ifdef EXTERNAL_MDIO */ +} + +/** + * e1000_oem_phy_init_script + * @hw struct e1000_hw containing hardware specific data + * + * This gets called in three places, after egbe_oem_phy_hw_reset() + * to perform and post reset initialiation. Not all PHYs require + * this, which is why it was split off as a seperate function. + **/ +void +e1000_oem_phy_init_script(struct e1000_hw *hw) +{ +#ifdef EXTERNAL_MDIO + + DEBUGFUNC1("%s",__func__); + + if(!hw) { + return; + } + + /* call the GCU func that can do any phy specific init + * functions after a reset + * + * Make note that the M88 phy is what'll be used on Truxton + * + * The closest thing is in e1000_phy_init_script, however this is + * for the IGP style of phy. This is probably a no-op for truxton + * but may be needed by OEM's later on + * + */ + switch (hw->phy_id) { + case M88E1000_I_PHY_ID: + case M88E1141_E_PHY_ID: + case VSC8211_E_PHY_ID: + case VSC8601_E_PHY_ID: + case NON_PHY_PORT: + DEBUGOUT("Nothing to do for OEM PHY Init"); + break; + default: + DEBUGOUT("Invalid PHY ID\n"); + return; + } + +#endif /* ifdef EXTERNAL_MDIO */ + return; + +} + +/** + * e1000_oem_read_phy_reg_ex + * @hw struct e1000_hw containing hardware specific data + * @reg_addr address location within the PHY register set + * @phy_data returns the data read from reg_addr + * + * This encapsulates the interface call to the GCU for access + * to the MDIO for the PHY. + **/ +int32_t +e1000_oem_read_phy_reg_ex(struct e1000_hw *hw, + uint32_t reg_addr, + uint16_t *phy_data) +{ +#ifdef EXTERNAL_MDIO + + int32_t ret_val; + + DEBUGFUNC1("%s",__func__); + + if(!hw || !phy_data) { + return -1; + } + + /* call the GCU func that will read the phy + * + * Make note that the M88 phy is what'll be used on Truxton. + * + * The closest thing is in e1000_read_phy_reg_ex. + * + * NOTE: this is 1 (of 2) functions that is truly dependant on the + * gcu module + */ + + ret_val = gcu_read_eth_phy(e1000_oem_get_phy_dev_number(hw), + reg_addr, phy_data); + if(ret_val) { + DEBUGOUT("Error reading GCU"); + return ret_val; + } + + return E1000_SUCCESS; + +#else /* ifdef EXTERNAL_MDIO */ + + return -E1000_ERR_PHY_TYPE; + +#endif /* ifdef EXTERNAL_MDIO */ +} +/** + * e1000_oem_set_trans_gasket + * @hw: e1000_hw struct containing device specific information + * + * Returns E1000_SUCCESS, negative E1000 error code on failure + * + * This is called from e1000_config_mac_to_phy. Various supported + * Phys may require the RGMII/RMII Translation gasket be set to RMII. + **/ +int32_t +e1000_oem_set_trans_gasket(struct e1000_hw *hw) +{ +#ifdef EXTERNAL_MDIO + uint32_t ctrl_aux_reg = 0; + + DEBUGFUNC1("%s",__func__); + + if(!hw) { + return -1; + } + + switch (hw->phy_id) { + case M88E1000_I_PHY_ID: + case M88E1141_E_PHY_ID: + /* Gasket set correctly for Marvell Phys, so nothing to do */ + break; + /* Add your PHY_ID here if your device requires an RMII interface + case YOUR_PHY_ID: + ctrl_aux_reg = E1000_READ_REG(hw, CTRL_AUX); + ctrl_aux_reg |= E1000_CTRL_AUX_ICP_xxxx_MII_TGS; // Set the RGMII_RMII bit + */ + E1000_WRITE_REG(hw, CTRL_AUX, ctrl_aux_reg); + break; + case VSC8211_E_PHY_ID: + case VSC8601_E_PHY_ID: + break; + default: + DEBUGOUT("Invalid PHY ID\n"); + return -E1000_ERR_PHY_TYPE; + } + + return E1000_SUCCESS; + +#else /* ifdef EXTERNAL_MDIO */ + + return -E1000_ERR_PHY_TYPE; + +#endif /* ifdef EXTERNAL_MDIO */ +} + +/** + * e1000_oem_write_phy_reg_ex + * @hw struct e1000_hw containing hardware specific data + * @reg_addr address location within the PHY register set + * @phy_data data to be written to reg_addr + * + * This encapsulates the interface call to the GCU for access + * to the MDIO for the PHY. + **/ +int32_t +e1000_oem_write_phy_reg_ex(struct e1000_hw *hw, + uint32_t reg_addr, + uint16_t phy_data) +{ +#ifdef EXTERNAL_MDIO + + int32_t ret_val; + + DEBUGFUNC1("%s",__func__); + + if(!hw) { + return -1; + } + /* call the GCU func that will write to the phy + * + * Make note that the M88 phy is what'll be used on Truxton. + * + * The closest thing is in e1000_write_phy_reg_ex + * + * NOTE: this is 2 (of 2) functions that is truly dependant on the + * gcu module + */ + ret_val = gcu_write_eth_phy(e1000_oem_get_phy_dev_number(hw), + reg_addr, phy_data); + if(ret_val) { + DEBUGOUT("Error writing to GCU"); + return ret_val; + } + + return E1000_SUCCESS; + +#else /* ifdef EXTERNAL_MDIO */ + + return -E1000_ERR_PHY_TYPE; + +#endif /* ifdef EXTERNAL_MDIO */ +} + + +/** + * e1000_oem_phy_needs_reset_with_mac + * @hw struct e1000_hw hardware specific data + * + * e1000_reset_hw is called to reset the MAC. If, for + * some reason the PHY needs to be reset as well, this + * should return TRUE and then e1000_oem_phy_hw_reset() + * will be called. + **/ +int +e1000_oem_phy_needs_reset_with_mac(struct e1000_hw *hw) +{ +#ifdef EXTERNAL_MDIO + + int ret_val; + + DEBUGFUNC1("%s",__func__); + + if(!hw) { + return FALSE; + } + + /* + * From the original e1000 driver, the M88 + * PHYs did not seem to need this reset, + * so returning FALSE. + */ + switch (hw->phy_id) { + case M88E1000_I_PHY_ID: + case M88E1141_E_PHY_ID: + case VSC8211_E_PHY_ID: + case VSC8601_E_PHY_ID: + case NON_PHY_PORT: + ret_val = FALSE; + break; + default: + DEBUGOUT("Invalid PHY ID\n"); + return FALSE; + } + + return ret_val; + +#else /* ifdef EXTERNAL_MDIO */ + + return FALSE; + +#endif /* ifdef EXTERNAL_MDIO */ +} + + +/** + * e1000_oem_config_dsp_after_link_change + * @hw struct e1000_hw containing hardware specific data + * @link_up allows different configurations based on whether + * not the link was up. + * + * This is called from e1000_check_for_link, and allows for + * tweaking of the PHY, for PHYs that support a DSP. + * + **/ +int32_t +e1000_oem_config_dsp_after_link_change(struct e1000_hw *hw, + int link_up) +{ +#ifdef EXTERNAL_MDIO + + DEBUGFUNC1("%s",__func__); + + if(!hw) { + return -1; + } + + /* + * Make note that the M88 phy is what'll be used on Truxton, + * but in the e1000 driver, it had no such func. This is a no-op + * for M88, but may be useful for other phys + * + * use e1000_config_dsp_after_link_change as example + */ + switch (hw->phy_id) { + case M88E1000_I_PHY_ID: + case M88E1141_E_PHY_ID: + case VSC8211_E_PHY_ID: + case VSC8601_E_PHY_ID: + DEBUGOUT("No DSP to configure on OEM PHY"); + break; + default: + DEBUGOUT("Invalid PHY ID\n"); + return -E1000_ERR_PHY_TYPE; + } + + return E1000_SUCCESS; + +#else /* ifdef EXTERNAL_MDIO */ + + return -E1000_ERR_PHY_TYPE; + +#endif /* ifdef EXTERNAL_MDIO */ +} + + +/** + * e1000_oem_get_cable_length + * @hw struct e1000_hw containing hardware specific data + * @min_length pointer to return the approx minimum length + * @max_length pointer to return the approx maximum length + * + * + **/ +int32_t +e1000_oem_get_cable_length(struct e1000_hw *hw, + uint16_t *min_length, + uint16_t *max_length) +{ +#ifdef EXTERNAL_MDIO + + int32_t ret_val; + uint16_t cable_length; + uint16_t phy_data; + + DEBUGFUNC1("%s",__func__); + + if(!hw || !min_length || !max_length) { + return -1; + } + + switch (hw->phy_id) { + case M88E1000_I_PHY_ID: + case M88E1141_E_PHY_ID: + ret_val = e1000_oem_read_phy_reg_ex(hw, + M88E1000_PHY_SPEC_STATUS, + &phy_data); + if(ret_val) { + return ret_val; + } + + cable_length = (phy_data & M88E1000_PSSR_CABLE_LENGTH) + >> M88E1000_PSSR_CABLE_LENGTH_SHIFT; + + /* Convert the enum value to ranged values */ + switch (cable_length) { + case e1000_cable_length_50: + *min_length = 0; + *max_length = e1000_igp_cable_length_50; + break; + case e1000_cable_length_50_80: + *min_length = e1000_igp_cable_length_50; + *max_length = e1000_igp_cable_length_80; + break; + case e1000_cable_length_80_110: + *min_length = e1000_igp_cable_length_80; + *max_length = e1000_igp_cable_length_110; + break; + case e1000_cable_length_110_140: + *min_length = e1000_igp_cable_length_110; + *max_length = e1000_igp_cable_length_140; + break; + case e1000_cable_length_140: + *min_length = e1000_igp_cable_length_140; + *max_length = e1000_igp_cable_length_170; + break; + default: + return -E1000_ERR_PHY; + break; + } + break; + default: + DEBUGOUT("Invalid PHY ID\n"); + return -E1000_ERR_PHY_TYPE; + } + + return E1000_SUCCESS; + +#else /* ifdef EXTERNAL_MDIO */ + + return -E1000_ERR_PHY_TYPE; + +#endif /* ifdef EXTERNAL_MDIO */ +} + + +/** + * e1000_oem_phy_is_link_up + * @hw e1000_hw struct containing device specific information + * @isUp a boolean returning true if link is up + * + * This is called as part of e1000_config_mac_to_phy() to align + * the MAC with the PHY. It turns out on ICP_XXXX, this is not + * done automagically. + **/ +int32_t +e1000_oem_phy_is_link_up(struct e1000_hw *hw, int *isUp) +{ +#ifdef EXTERNAL_MDIO + + uint16_t phy_data; + uint16_t statusMask; + int32_t ret_val; + + DEBUGFUNC1("%s",__func__); + + if(!hw || !isUp) { + return -1; + } + /* + * Make note that the M88 phy is what'll be used on Truxton + * see e1000_config_mac_to_phy + */ + + switch (hw->phy_id) { + case M88E1000_I_PHY_ID: + case M88E1141_E_PHY_ID: + e1000_oem_read_phy_reg_ex(hw, M88E1000_PHY_SPEC_STATUS, &phy_data); + ret_val = e1000_oem_read_phy_reg_ex(hw, M88E1000_PHY_SPEC_STATUS, + &phy_data); + statusMask = M88E1000_PSSR_LINK; + break; + case VSC8211_E_PHY_ID: + case VSC8601_E_PHY_ID: + ret_val = e1000_oem_read_phy_reg_ex(hw, PHY_STATUS, &phy_data); + statusMask = MII_SR_LINK_STATUS; + break; + case NON_PHY_PORT: + ret_val = E1000_SUCCESS; + phy_data = statusMask = 1; /* to make the condition always true */ + break; + default: + DEBUGOUT("Invalid PHY ID\n"); + return -E1000_ERR_PHY_TYPE; + } + if(ret_val) { + DEBUGOUT("Unable to read PHY register\n"); + return ret_val; + } + + *isUp = (phy_data & statusMask) != 0; + + return E1000_SUCCESS; + +#else /* ifdef EXTERNAL_MDIO */ + + if(!hw || !isFD) { + return -1; + } + *isUp = FALSE; + return -E1000_ERR_PHY_TYPE; + +#endif /* ifdef EXTERNAL_MDIO */ +} + diff --git a/e1000_oem_phy.h b/e1000_oem_phy.h new file mode 100755 index 0000000..d0afe0e --- /dev/null +++ b/e1000_oem_phy.h @@ -0,0 +1,235 @@ +/******************************************************************************* + +GPL LICENSE SUMMARY + + Copyright(c) 2007,2008,2009 Intel Corporation. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + The full GNU General Public License is included in this distribution + in the file called LICENSE.GPL. + + Contact Information: + Intel Corporation + + version: Embedded.Release.Patch.L.1.0.7-5 + + Contact Information: + + Intel Corporation, 5000 W Chandler Blvd, Chandler, AZ 85226 + +*******************************************************************************/ +#ifndef _E1000_OEM_PHY_H_ +#define _E1000_OEM_PHY_H_ + +#include <linux/types.h> + +#define E1000_CTRL_AUX 0x000E0 /* Aux Control -RW */ +#define E1000_82542_CTRL_AUX E1000_CTRL_AUX + +/* Bit definitions for valid PHY IDs. */ +/* I = Integrated + * E = External + */ +#define M88E1000_E_PHY_ID 0x01410C50 +#define M88E1000_I_PHY_ID 0x01410C30 +#define M88E1011_I_PHY_ID 0x01410C20 +#define IGP01E1000_I_PHY_ID 0x02A80380 +#define M88E1000_12_PHY_ID M88E1000_E_PHY_ID +#define M88E1000_14_PHY_ID M88E1000_E_PHY_ID +#define M88E1011_I_REV_4 0x04 +#define M88E1141_E_PHY_ID 0x01410CD0 +#define L1LXT971A_PHY_ID 0x001378E0 +#define VSC8211_E_PHY_ID 0x000FC4B1 +#define VSC8601_E_PHY_ID 0x00070420 + +/* RGMII TX and RX Timing Control*/ +#define M88E1000_EPSCR_TX_TIME_CTRL 0x0002 /* Add Delay */ +#define M88E1000_EPSCR_RX_TIME_CTRL 0x0080 /* Add Delay */ + +/* M88E1000 Specific Registers */ +#define M88E1000_PHY_SPEC_CTRL 0x10 /* PHY Specific Control Register */ +#define M88E1000_PHY_SPEC_STATUS 0x11 /* PHY Specific Status Register */ +#define M88E1000_INT_ENABLE 0x12 /* Interrupt Enable Register */ +#define M88E1000_INT_STATUS 0x13 /* Interrupt Status Register */ +#define M88E1000_EXT_PHY_SPEC_CTRL 0x14 /* Extended PHY Specific Control */ +#define M88E1000_RX_ERR_CNTR 0x15 /* Receive Error Counter */ + +#define M88E1000_PHY_EXT_CTRL 0x1A /* PHY extend control register */ +#define M88E1000_PHY_PAGE_SELECT 0x1D /* Reg 29 for page number setting */ +#define M88E1000_PHY_GEN_CONTROL 0x1E /* Its meaning depends on reg 29 */ +#define M88E1000_PHY_VCO_REG_BIT8 0x100 /* Bits 8 & 11 are adjusted for */ +#define M88E1000_PHY_VCO_REG_BIT11 0x800 /* improved BER performance */ + +#define IGP01E1000_IEEE_REGS_PAGE 0x0000 +#define IGP01E1000_IEEE_RESTART_AUTONEG 0x3300 +#define IGP01E1000_IEEE_FORCE_GIGA 0x0140 + +/* IGP01E1000 Specific Registers */ +#define IGP01E1000_PHY_PORT_CONFIG 0x10 /* PHY Specific Port Config Register */ +#define IGP01E1000_PHY_PORT_STATUS 0x11 /* PHY Specific Status Register */ +#define IGP01E1000_PHY_PORT_CTRL 0x12 /* PHY Specific Control Register */ +#define IGP01E1000_PHY_LINK_HEALTH 0x13 /* PHY Link Health Register */ +#define IGP01E1000_GMII_FIFO 0x14 /* GMII FIFO Register */ +#define IGP01E1000_PHY_CHANNEL_QUALITY 0x15 /* PHY Channel Quality Register */ +#define IGP02E1000_PHY_POWER_MGMT 0x19 +#define IGP01E1000_PHY_PAGE_SELECT 0x1F /* PHY Page Select Core Register */ + +/* VSC8211 Specific Registers */ +#define VSC8211_BYPASS_CTRL 0x12 +#define VSC8211_PHY_CTRL_1 0x17 +#define VSC8211_AUX_CTRL_STS 0x1C +#define VSC8211_EXT_PAGE_ACCESS 0x1F + +/* VSC8601 Specific Registers */ +#define VSC8601_BYPASS_CTRL 0x12 +#define VSC8601_PHY_CTRL_1 0x17 +#define VSC8601_AUX_CTRL_STS 0x1C + +/* VSC8211 BYPASS Control Register */ +#define VSC8211_BYPASS_POLAR_INVERS_DISABLE 0x0010 +#define VSC8211_BYPASS_AUTO_MDI_DISABLE 0x0020 + +/* VSC8211 Auxiliary Control & Status Register */ +#define VSC8211_AUX_SPEED_MASK 0x0018 +#define VSC8211_AUX_SPEED_IS_1000 0x0010 +#define VSC8211_AUX_SPEED_IS_100 0x0008 +#define VSC8211_AUX_SPEED_IS_10 0x0000 + +#define VSC8211_AUX_FDX_MASK 0x0020 +#define VSC8211_AUX_FDX_IS_FULL 0x0020 +#define VSC8211_AUX_FDX_IS_HALF 0x0000 + +/* VSC8211 PHY Control Register #1 */ +#define VSC8211_PHY_CTRL1_INTF_MODE1_RGMII 0x1000 +#define VSC8211_PHY_CTRL1_INTF_MODE1_GMII 0x3000 + +#define VSC8211_PHY_CTRL1_TXC_SKEW_2NS 0x0800 +#define VSC8211_PHY_CTRL1_RXC_SKEW_2NS 0x0200 + +#define VSC8211_PHY_CTRL1_RX_IDLE_CLK_ENABLE 0x0020 +#define VSC8211_PHY_CTRL1_RX_IDLE_CLK_DISABLE 0x0000 + +#define VSC8211_PHY_CTRL1_INTF_MODE2_CAT5 0x0004 +#define VSC8211_PHY_CTRL1_INTF_MODE2_FIBER 0x0002 + +/* VSC8601 BYPASS Control Register */ +#define VSC8601_BYPASS_POLAR_INVERS_DISABLE 0x0010 +#define VSC8601_BYPASS_AUTO_MDI_DISABLE 0x0020 + +/* VSC8601 Auxiliary Control & Status Register */ +#define VSC8601_AUX_SPEED_MASK 0x0018 +#define VSC8601_AUX_SPEED_IS_1000 0x0010 +#define VSC8601_AUX_SPEED_IS_100 0x0008 +#define VSC8601_AUX_SPEED_IS_10 0x0000 + +#define VSC8601_AUX_FDX_MASK 0x0020 +#define VSC8601_AUX_FDX_IS_FULL 0x0020 +#define VSC8601_AUX_FDX_IS_HALF 0x0000 + +/* + * ICP GbE devices are not assigned Intel part numbers yet so just + * identify them via their device id's + */ +#define E1000_DEV_ID_ICP_5040 0x5040 +#define E1000_DEV_ID_ICP_5041 0x5041 +#define E1000_DEV_ID_ICP_5042 0x5042 +#define E1000_DEV_ID_ICP_5043 0x5043 +#define E1000_DEV_ID_ICP_5044 0x5044 +#define E1000_DEV_ID_ICP_5045 0x5045 +#define E1000_DEV_ID_ICP_5046 0x5046 +#define E1000_DEV_ID_ICP_5047 0x5047 +#define E1000_DEV_ID_ICP_5048 0x5048 +#define E1000_DEV_ID_ICP_5049 0x5049 +#define E1000_DEV_ID_ICP_504A 0x504A +#define E1000_DEV_ID_ICP_504B 0x504B + +struct e1000_hw; +struct e1000_adapter; +struct ifreq; +struct e1000_phy_info; + + +typedef enum { +#undef FALSE + FALSE = 0, +#undef TRUE + TRUE = 1 +} boolean_t; + +int32_t e1000_oem_setup_link(struct e1000_hw *hw); +int32_t e1000_oem_set_trans_gasket(struct e1000_hw *hw); + +uint32_t e1000_oem_get_tipg(struct e1000_hw *hw); +int e1000_oem_phy_is_copper(struct e1000_hw *hw); +uint32_t e1000_oem_get_phy_dev_number(struct e1000_hw *hw); +int e1000_oem_mii_ioctl(struct e1000_adapter *adapter, unsigned long flags, + struct ifreq *ifr, int cmd); +void e1000_oem_fiber_live_in_suspend(struct e1000_hw *hw); +void e1000_oem_get_phy_regs(struct e1000_adapter *adapter, uint32_t *data, + uint32_t data_length); +int e1000_oem_phy_loopback(struct e1000_adapter *adapter); +void e1000_oem_loopback_cleanup(struct e1000_adapter *adapter); +uint32_t e1000_oem_phy_speed_downgraded(struct e1000_hw *hw, uint16_t *isDowngraded); +int32_t e1000_oem_check_polarity(struct e1000_hw *hw, uint16_t *polarity); + +int32_t e1000_oem_phy_is_full_duplex(struct e1000_hw *hw, int *isFD); +int32_t e1000_oem_phy_is_speed_1000(struct e1000_hw *hw, int *is1000); +int32_t e1000_oem_phy_is_speed_100(struct e1000_hw *hw, int *is100); + +int32_t e1000_oem_force_mdi(struct e1000_hw *hw, int *resetPhy); +int32_t e1000_oem_phy_reset_dsp(struct e1000_hw *hw); +int32_t e1000_oem_cleanup_after_phy_reset(struct e1000_hw *hw); + +int32_t e1000_oem_phy_get_info(struct e1000_hw *hw, + struct e1000_phy_info *phy_info); + +int32_t e1000_oem_phy_hw_reset(struct e1000_hw *hw); +void e1000_oem_phy_init_script(struct e1000_hw *hw); + +int32_t e1000_oem_read_phy_reg_ex(struct e1000_hw *hw, + uint32_t reg_addr, + uint16_t *phy_data); +int32_t e1000_oem_write_phy_reg_ex(struct e1000_hw *hw, + uint32_t reg_addr, + uint16_t phy_data); + +int e1000_oem_phy_needs_reset_with_mac(struct e1000_hw *hw); + +int32_t e1000_oem_config_dsp_after_link_change(struct e1000_hw *hw, + int link_up); + +int32_t e1000_oem_get_cable_length(struct e1000_hw *hw, + uint16_t *min_length, + uint16_t *max_length); + +int32_t e1000_oem_phy_is_link_up(struct e1000_hw *hw, int *isUp); + +/* Default Register Macros */ + +#define ICP_XXXX_MAC_0 0 /* PCI Device numbers associated with MACs on */ +#define ICP_XXXX_MAC_1 1 /* ICP_XXXX family of controllers */ +#define ICP_XXXX_MAC_2 2 + +#define DEFAULT_ICP_XXXX_TIPG_IPGT 8 /* Inter Packet Gap Transmit Time */ +#define ICP_XXXX_TIPG_IPGT_MASK 0x000003FFUL + +/* Miscellaneous defines */ +#ifdef IEGBE_10_100_ONLY + #define ICP_XXXX_AUTONEG_ADV_DEFAULT 0x0F +#else + #define ICP_XXXX_AUTONEG_ADV_DEFAULT 0x2F +#endif + +#endif /* ifndef _IEGBE_OEM_PHY_H_ */ + diff --git a/e1000_osdep.h b/e1000_osdep.h index d929852..db244a6 100644 --- a/e1000_osdep.h +++ b/e1000_osdep.h @@ -63,6 +63,14 @@ (writel((value), (hw->hw_addr + ((hw->mac_type >= e1000_82543) \ ? E1000_##reg : E1000_82542_##reg)))) +#define E1000_WRITE_REG(a, reg, value) ( \ + writel((value), ((a)->hw_addr + \ + (((a)->mac_type >= e1000_82543) ? E1000_##reg : E1000_82542_##reg)))) + +#define E1000_READ_REG(a, reg) ( \ + readl((a)->hw_addr + \ + (((a)->mac_type >= e1000_82543) ? E1000_##reg : E1000_82542_##reg))) + #define E1000_WRITE_REG_ARRAY(a, reg, offset, value) ( \ writel((value), ((a)->hw_addr + \ (((a)->mac_type >= e1000_82543) ? E1000_##reg : E1000_82542_##reg) + \ diff --git a/e1000_param.c b/e1000_param.c index 38d2741..330a785 100644 --- a/e1000_param.c +++ b/e1000_param.c @@ -27,6 +27,7 @@ *******************************************************************************/ #include "e1000.h" +#include "e1000_oem_phy.h" /* This is the only thing that needs to be changed to adjust the * maximum number of ports that the driver can manage. @@ -527,6 +528,13 @@ void __devinit e1000_check_options(struct e1000_adapter *adapter) case e1000_media_type_copper: e1000_check_copper_options(adapter); break; + case e1000_media_type_oem: + if(e1000_oem_phy_is_copper(&adapter->hw)) { + e1000_check_copper_options(adapter); + }else { + e1000_check_fiber_options(adapter); + } + break; default: BUG(); } @@ -0,0 +1,105 @@ +/***************************************************************************** + +GPL LICENSE SUMMARY + + Copyright(c) 2007,2008,2009 Intel Corporation. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + The full GNU General Public License is included in this distribution + in the file called LICENSE.GPL. + + Contact Information: + Intel Corporation + + version: Embedded.Release.Patch.L.1.0.7-5 + + Contact Information: + + Intel Corporation, 5000 W Chandler Blvd, Chandler, AZ 85226 + +*****************************************************************************/ + + +/* Linux GCU Driver main header file */ + +#ifndef _GCU_H_ +#define _GCU_H_ + +#include <linux/stddef.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/spinlock.h> +#include <asm/bitops.h> +#include <linux/slab.h> +#include <linux/reboot.h> +#include <asm/delay.h> + +#define BAR_0 0 + +#include "kcompat.h" + +#define INTEL_GCU_DEVICE(device_id) {\ + PCI_DEVICE(PCI_VENDOR_ID_INTEL, device_id)} + +#define GCU_DEV_NAME_SIZE 16 + +#ifdef DBG +#define GCU_DBG(args...) printk(KERN_DEBUG "gcu: " args) +#else +#define GCU_DBG(args...) +#endif + +#define GCU_ERR(args...) printk(KERN_ERR "gcu: " args) + +#define PFX "gcu: " +#define DPRINTK(nlevel, klevel, fmt, args...) \ + (void)((NETIF_MSG_##nlevel & adapter->msg_enable) && \ + printk(KERN_##klevel PFX "%s: %s: " fmt, adapter->name, \ + __FUNCTION__ , ## args)) + +struct gcu_adapter { + struct pci_dev *pdev; + uint32_t mem_start; + uint32_t mem_end; + uint32_t base_addr; + uint8_t *hw_addr; + char name[GCU_DEV_NAME_SIZE]; + uint32_t pci_state[16]; + int32_t msg_enable; + uint16_t device_id; + uint16_t vendor_id; + uint16_t subsystem_id; + uint16_t subsystem_vendor_id; + uint16_t pci_cmd_word; + uint8_t revision_id; + /* open/release and usage marking */ + struct module *owner; + +}; + +/* + * Exported interface functions need access to the modules + * gcu_adapter struct + */ +const struct gcu_adapter *gcu_get_adapter(void); +void gcu_release_adapter(const struct gcu_adapter **adapter); + +#endif /* _GCU_H_ */ + diff --git a/gcu_if.c b/gcu_if.c new file mode 100755 index 0000000..33b573f --- /dev/null +++ b/gcu_if.c @@ -0,0 +1,371 @@ +/***************************************************************************** + +GPL LICENSE SUMMARY + + Copyright(c) 2007,2008,2009 Intel Corporation. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + The full GNU General Public License is included in this distribution + in the file called LICENSE.GPL. + + Contact Information: + Intel Corporation + + version: Embedded.Release.Patch.L.1.0.7-5 + + Contact Information: + + Intel Corporation, 5000 W Chandler Blvd, Chandler, AZ 85226 + +*****************************************************************************/ + +/************************************************************************** + * @ingroup GCU_INTERFACE + * + * @file gcu_if.c + * + * @description + * This module contains shared functions for accessing and configuring + * the GCU. + * + **************************************************************************/ + +#include "gcu.h" +#include "gcu_reg.h" +#include "gcu_if.h" + +/* forward declaration for write verify used in gcu_write_eth_phy */ +int32_t gcu_write_verify(uint32_t phy_num, + uint32_t reg_addr, + uint16_t written_data, + const struct gcu_adapter *adapter); + +/** + * gcu_write_eth_phy + * @phy_num: phy we want to write to, either 0, 1, or 2 + * @reg_addr: address in PHY's register space to write to + * @phy_data: data to be written + * + * interface function for other modules to access the GCU + **/ +int32_t +gcu_write_eth_phy(uint32_t phy_num, uint32_t reg_addr, uint16_t phy_data) +{ + const struct gcu_adapter *adapter; + uint32_t data = 0; + uint32_t timeoutCounter = 0; + const uint32_t timeoutCounterMax = GCU_MAX_ATTEMPTS; + uint32_t pending; + + GCU_DBG("%s\n", __func__); + + if(phy_num > MDIO_COMMAND_PHY_ADDR_MAX) + { + GCU_ERR("phy_num = %d, which is greater than " + "MDIO_COMMAND_PHY_ADDR_MAX\n", phy_num); + + return -1; + } + + if(reg_addr > MDIO_COMMAND_PHY_REG_MAX) + { + GCU_ERR("reg_addr = %d, which is greater than " + "MDIO_COMMAND_PHY_REG_MAX\n", phy_num); + + return -1; + } + + /* format the data to be written to the MDIO_COMMAND_REG */ + data = phy_data; + data |= (reg_addr << MDIO_COMMAND_PHY_REG_OFFSET); + data |= (phy_num << MDIO_COMMAND_PHY_ADDR_OFFSET); + data |= MDIO_COMMAND_OPER_MASK | MDIO_COMMAND_GO_MASK; + + /* + * get_gcu_adapter contains a spinlock, this may pause for a bit + */ + adapter = gcu_get_adapter(); + if(!adapter) + { + GCU_ERR("gcu_adapter not available, cannot access MMIO\n"); + return -1; + } + + /* + * We write to MDIO_COMMAND_REG initially, then read that + * same register until its MDIO_GO bit is cleared. When cleared, + * the transaction is complete + */ + iowrite32(data, adapter->hw_addr + MDIO_COMMAND_REG); + do { + timeoutCounter++; + udelay(0x32); /* 50 microsecond delay */ + data = ioread32(adapter->hw_addr + MDIO_COMMAND_REG); + pending = (data & MDIO_COMMAND_GO_MASK) >> MDIO_COMMAND_GO_OFFSET; + } while(pending && timeoutCounter < timeoutCounterMax); + + if(timeoutCounter == timeoutCounterMax && pending) + { + GCU_ERR("Reached maximum number of retries" + " accessing MDIO_COMMAND_REG\n"); + + gcu_release_adapter(&adapter); + + return -1; + } + + /* validate the write during debug */ +#ifdef DBG2 + if(!gcu_write_verify(phy_num, reg_addr, phy_data, adapter)) + { + GCU_ERR("Write verification failed for PHY=%d and addr=%d\n", + phy_num, reg_addr); + + gcu_release_adapter(&adapter); + + return -1; + } +#endif + + gcu_release_adapter(&adapter); + + return 0; +} +EXPORT_SYMBOL(gcu_write_eth_phy); + + +/** + * gcu_read_eth_phy + * @phy_num: phy we want to write to, either 0, 1, or 2 + * @reg_addr: address in PHY's register space to write to + * @phy_data: data to be written + * + * interface function for other modules to access the GCU + **/ +int32_t +gcu_read_eth_phy(uint32_t phy_num, uint32_t reg_addr, uint16_t *phy_data) +{ + const struct gcu_adapter *adapter; + uint32_t data = 0; + uint32_t timeoutCounter = 0; + const uint32_t timeoutCounterMax = GCU_MAX_ATTEMPTS; + uint32_t pending = 0; + + GCU_DBG("%s\n", __func__); + + if(phy_num > MDIO_COMMAND_PHY_ADDR_MAX) + { + GCU_ERR("phy_num = %d, which is greater than " + "MDIO_COMMAND_PHY_ADDR_MAX\n", phy_num); + + return -1; + } + + if(reg_addr > MDIO_COMMAND_PHY_REG_MAX) + { + GCU_ERR("reg_addr = %d, which is greater than " + "MDIO_COMMAND_PHY_REG_MAX\n", phy_num); + + return -1; + } + + /* format the data to be written to MDIO_COMMAND_REG */ + data |= (reg_addr << MDIO_COMMAND_PHY_REG_OFFSET); + data |= (phy_num << MDIO_COMMAND_PHY_ADDR_OFFSET); + data |= MDIO_COMMAND_GO_MASK; + + /* + * this call contains a spinlock, so this may pause for a bit + */ + adapter = gcu_get_adapter(); + if(!adapter) + { + GCU_ERR("gcu_adapter not available, cannot access MMIO\n"); + return -1; + } + + /* + * We write to MDIO_COMMAND_REG initially, then read that + * same register until its MDIO_GO bit is cleared. When cleared, + * the transaction is complete + */ + iowrite32(data, adapter->hw_addr + MDIO_COMMAND_REG); + do { + timeoutCounter++; + udelay(0x32); /* 50 microsecond delay */ + data = ioread32(adapter->hw_addr + MDIO_COMMAND_REG); + pending = (data & MDIO_COMMAND_GO_MASK) >> MDIO_COMMAND_GO_OFFSET; + } while(pending && timeoutCounter < timeoutCounterMax); + + if(timeoutCounter == timeoutCounterMax && pending) + { + GCU_ERR("Reached maximum number of retries" + " accessing MDIO_COMMAND_REG\n"); + + gcu_release_adapter(&adapter); + + return -1; + } + + /* we retrieve the data from the MDIO_STATUS_REGISTER */ + data = ioread32(adapter->hw_addr + MDIO_STATUS_REG); + if((data & MDIO_STATUS_STATUS_MASK) != 0) + { + GCU_ERR("Unable to retrieve data from MDIO_STATUS_REG\n"); + + gcu_release_adapter(&adapter); + + return -1; + } + + *phy_data = (uint16_t) (data & MDIO_STATUS_READ_DATA_MASK); + + gcu_release_adapter(&adapter); + + return 0; +} +EXPORT_SYMBOL(gcu_read_eth_phy); + + +/** + * gcu_write_verify + * @phy_num: phy we want to write to, either 0, 1, or 2 + * @reg_addr: address in PHY's register space to write to + * @phy_data: data to be checked + * @adapter: pointer to global adapter struct + * + * This f(n) assumes that the spinlock acquired for adapter is + * still in force. + **/ +int32_t +gcu_write_verify(uint32_t phy_num, uint32_t reg_addr, uint16_t written_data, + const struct gcu_adapter *adapter) +{ + uint32_t data = 0; + uint32_t timeoutCounter = 0; + const uint32_t timeoutCounterMax = GCU_MAX_ATTEMPTS; + uint32_t pending = 0; + + GCU_DBG("%s\n", __func__); + + if(!adapter) + { + GCU_ERR("Invalid adapter pointer\n"); + return 0; + } + + if(phy_num > MDIO_COMMAND_PHY_ADDR_MAX) + { + GCU_ERR("phy_num = %d, which is greater than " + "MDIO_COMMAND_PHY_ADDR_MAX\n", phy_num); + + return 0; + } + + if(reg_addr > MDIO_COMMAND_PHY_REG_MAX) + { + GCU_ERR("reg_addr = %d, which is greater than " + "MDIO_COMMAND_PHY_REG_MAX\n", phy_num); + + return 0; + } + + /* format the data to be written to MDIO_COMMAND_REG */ + data |= (reg_addr << MDIO_COMMAND_PHY_REG_OFFSET); + data |= (phy_num << MDIO_COMMAND_PHY_ADDR_OFFSET); + data |= MDIO_COMMAND_GO_MASK; + + /* + * We write to MDIO_COMMAND_REG initially, then read that + * same register until its MDIO_GO bit is cleared. When cleared, + * the transaction is complete + */ + iowrite32(data, adapter->hw_addr + MDIO_COMMAND_REG); + do { + timeoutCounter++; + udelay(0x32); /* 50 microsecond delay */ + data = ioread32(adapter->hw_addr + MDIO_COMMAND_REG); + pending = (data & MDIO_COMMAND_GO_MASK) >> MDIO_COMMAND_GO_OFFSET; + } while(pending && timeoutCounter < timeoutCounterMax); + + + if(timeoutCounter == timeoutCounterMax && pending) + { + GCU_ERR("Reached maximum number of retries" + " accessing MDIO_COMMAND_REG\n"); + + return 0; + } + + /* we retrieve the data from the MDIO_STATUS_REGISTER */ + data = ioread32(adapter->hw_addr + MDIO_STATUS_REG); + if((data & MDIO_STATUS_STATUS_MASK) != 0) + { + GCU_ERR("Unable to retrieve data from MDIO_STATUS_REG\n"); + + return 0; + } + + return written_data == (uint16_t) (data & MDIO_STATUS_READ_DATA_MASK); +} + +/* + * gcu_e1000_resume + * @pdev: gcu pci_dev + * purpose - exported PM resume function used by iegbe + * driver to enable the GCU device. + */ +void gcu_e1000_resume(struct pci_dev *pdev) +{ +#if ( ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,6) ) && \ + ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) ) ) + struct net_device *netdev = pci_get_drvdata(pdev); + struct gcu_adapter *adapter = netdev_priv(netdev); +#endif + + GCU_DBG("%s\n", __func__); + + pci_restore_state(pdev); + if(!pci_enable_device(pdev)) + GCU_DBG("pci_enable_device failed!\n"); + + return; +} +EXPORT_SYMBOL(gcu_e1000_resume); + +/* + * gcu_e1000_suspend + * @pdev: gcu pci_dev + * @state: PM state + * purpose - exported PM suspend function used by iegbe + * driver to disable the GCU device. + */ +int gcu_e1000_suspend(struct pci_dev *pdev, uint32_t state) +{ +#if ( ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,6) ) && \ + ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) ) ) + struct net_device *netdev = pci_get_drvdata(pdev); + struct gcu_adapter *adapter = netdev_priv(netdev); +#endif + + GCU_DBG("%s\n", __func__); + + pci_save_state(pdev); + pci_disable_device(pdev); + state = (state > 0) ? 0 : 0; + + return state; +} + +EXPORT_SYMBOL(gcu_e1000_suspend); diff --git a/gcu_if.h b/gcu_if.h new file mode 100755 index 0000000..38c1a89 --- /dev/null +++ b/gcu_if.h @@ -0,0 +1,52 @@ +/***************************************************************************** + +GPL LICENSE SUMMARY + + Copyright(c) 2007,2008,2009 Intel Corporation. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + The full GNU General Public License is included in this distribution + in the file called LICENSE.GPL. + + Contact Information: + Intel Corporation + + version: Embedded.Release.Patch.L.1.0.7-5 + + Contact Information: + + Intel Corporation, 5000 W Chandler Blvd, Chandler, AZ 85226 + +*****************************************************************************/ + +/* + * gcu_if.h + * Shared functions for accessing and configuring the GCU + */ + +#ifndef GCU_IF_H +#define GCU_IF_H +#define GCU_DEVID 0x503E +#define GCU_MAX_ATTEMPTS 64 + +int32_t gcu_write_eth_phy(uint32_t phy_num, uint32_t reg_addr, + uint16_t phy_data); + +int32_t gcu_read_eth_phy(uint32_t phy_num, uint32_t reg_addr, + uint16_t *phy_data); + +int gcu_e1000_suspend(struct pci_dev *pdev, uint32_t state); +void gcu_e1000_resume(struct pci_dev *pdev); +#endif /* ifndef GCU_IF_H */ + diff --git a/gcu_main.c b/gcu_main.c new file mode 100755 index 0000000..d43c094 --- /dev/null +++ b/gcu_main.c @@ -0,0 +1,454 @@ +/****************************************************************************** + +GPL LICENSE SUMMARY + + Copyright(c) 2007,2008,2009 Intel Corporation. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + The full GNU General Public License is included in this distribution + in the file called LICENSE.GPL. + + Contact Information: + Intel Corporation + + version: Embedded.Release.Patch.L.1.0.7-5 + + Contact Information: + + Intel Corporation, 5000 W Chandler Blvd, Chandler, AZ 85226 + +******************************************************************************/ +/************************************************************************** + * @ingroup GCU_GENERAL + * + * @file gcu_main.c + * + * @description + * This module contains the upper-edge routines of the driver + * interface that handle initialization, resets, and shutdowns + * of the GCU. + * + **************************************************************************/ + +#include "gcu.h" + +char gcu_driver_name[] = "GCU"; +char gcu_driver_string[] = "Global Configuration Unit Driver"; +#define DRV_VERSION "1.0.0" +char gcu_driver_version[] = DRV_VERSION; +char gcu_copyright[] = "Copyright (c) 1999-2007 Intel Corporation."; + +/* gcu_pci_tbl - PCI Device ID Table + * + * Last entry must be all 0s + * + * Macro expands to... + * {PCI_DEVICE(PCI_VENDOR_ID_INTEL, device_id)} + */ +static struct pci_device_id gcu_pci_tbl[] = { + INTEL_GCU_DEVICE(0x503E), + /* required last entry */ + {0,} +}; + +MODULE_DEVICE_TABLE(pci, gcu_pci_tbl); + +enum gcu_err_type {err_ioremap, err_alloc_gcu_adapter}; + +static int gcu_init_module(void); +static void gcu_exit_module(void); +static int gcu_probe(struct pci_dev *pdev, const struct pci_device_id *ent); +static void gcu_probe_err(enum gcu_err_type err, struct pci_dev *pdev, + struct gcu_adapter *adapter); +static void __devexit gcu_remove(struct pci_dev *pdev); +static int gcu_notify_reboot(struct notifier_block *, unsigned long event, + void *ptr); +static int gcu_suspend(struct pci_dev *pdev, uint32_t state); +static struct gcu_adapter *alloc_gcu_adapter(void); +static void free_gcu_adapter(struct gcu_adapter *adapter); + + +struct notifier_block gcu_notifier_reboot = { + .notifier_call = gcu_notify_reboot, + .next = NULL, + .priority = 0 +}; + +static struct pci_driver gcu_driver = { + .name = gcu_driver_name, + .id_table = gcu_pci_tbl, + .probe = gcu_probe, + .remove = __devexit_p(gcu_remove), +}; + +static struct gcu_adapter *global_adapter = 0; +static spinlock_t global_adapter_spinlock = SPIN_LOCK_UNLOCKED; +static unsigned long g_intflags = 0; + +MODULE_AUTHOR("Intel(R) Corporation"); +MODULE_DESCRIPTION("Global Configuration Unit Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1.0.0"); + +/* + * AFU: debug values are pulled originally from netdevice.h + * where in the orig iegbe driver, the enums were used. + * Probably want to come up with our own enum set + */ +static int debug = 0x1UL | 0x2UL; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); + +/** + * gcu_init_module - Driver Registration Routine + * + * gcu_init_module is the first routine called when the driver is + * loaded. All it does is register with the PCI subsystem. + **/ +static int __init +gcu_init_module(void) +{ + int ret; + printk(KERN_INFO "%s - version %s\n", + gcu_driver_string, gcu_driver_version); + + printk(KERN_INFO "%s\n", gcu_copyright); + + ret = pci_register_driver(&gcu_driver); + if(ret >= 0) { + register_reboot_notifier(&gcu_notifier_reboot); + } + return ret; +} + +module_init(gcu_init_module); + +/** + * gcu_exit_module - Driver Exit Cleanup Routine + * + * gcu_exit_module is called just before the driver is removed + * from memory. + **/ +static void __exit +gcu_exit_module(void) +{ + GCU_DBG("%s\n", __func__); + + unregister_reboot_notifier(&gcu_notifier_reboot); + pci_unregister_driver(&gcu_driver); +} + +module_exit(gcu_exit_module); + + +/** + * gcu_probe - Device Initialization Routine + * @pdev: PCI device information struct + * @ent: entry in gcu_pci_tbl + * + * Returns 0 on success, negative on failure + * + * gcu_probe initializes an adapter identified by a pci_dev structure. + * The OS initialization, configuring of the adapter private structure, + * and a hardware reset occur. + **/ +static int __devinit +gcu_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct gcu_adapter *adapter=0; + uint32_t mmio_start, mmio_len; + int err; + + GCU_DBG("%s\n", __func__); + + /** If we already have an adapter then we must already have been probed. */ + if (global_adapter != 0) + { + return 0; + } + + if((err = pci_enable_device(pdev))) + { + GCU_DBG("Unable to enable PCI Device\n"); + return err; + } + + if((err = pci_request_regions(pdev, gcu_driver_name))) + { + GCU_DBG("Unable to acquire requested memory regions\n"); + return err; + } + + /* + * acquire the adapter spinlock. Once the module is loaded, it is possible for + * someone to access the adapter struct via the interface functions exported + * in gcu_if.c + */ + spin_lock(&global_adapter_spinlock); + + adapter = alloc_gcu_adapter(); + if(!adapter) + { + gcu_probe_err(err_alloc_gcu_adapter, pdev, adapter); + spin_unlock(&global_adapter_spinlock); + return -ENOMEM; + } + + pci_set_drvdata(pdev, adapter); + + adapter->pdev = pdev; + adapter->msg_enable = (1 << debug) - 1; + + mmio_start = pci_resource_start(pdev, BAR_0); + mmio_len = pci_resource_len(pdev, BAR_0); + + adapter->hw_addr = ioremap(mmio_start, mmio_len); + if(!adapter->hw_addr) { + GCU_DBG("Unable to map mmio\n"); + gcu_probe_err(err_ioremap, pdev, adapter); + spin_unlock(&global_adapter_spinlock); + return -EIO; + } + + strncpy(adapter->name, pci_name(pdev), sizeof(adapter->name)-1); + adapter->mem_start = mmio_start; + adapter->mem_end = mmio_start + mmio_len; + + adapter->vendor_id = pdev->vendor; + adapter->device_id = pdev->device; + adapter->subsystem_vendor_id = pdev->subsystem_vendor; + adapter->subsystem_id = pdev->subsystem_device; + + pci_read_config_byte(pdev, PCI_REVISION_ID, &adapter->revision_id); + + pci_read_config_word(pdev, PCI_COMMAND, &adapter->pci_cmd_word); + + global_adapter = adapter; + spin_unlock(&global_adapter_spinlock); + + DPRINTK(PROBE, INFO, "Intel(R) GCU Initialized\n"); + + return 0; +} + +/** + * gcu_probe_err - gcu_probe error handler + * @err: gcu_err_type + * + * encapsulated error handling for gcu_probe + **/ +static void +gcu_probe_err(enum gcu_err_type err, struct pci_dev *pdev, + struct gcu_adapter *adapter) +{ + + switch(err) { + case err_ioremap: + iounmap(adapter->hw_addr); + pci_release_regions(pdev); + case err_alloc_gcu_adapter: + default: + free_gcu_adapter(adapter); + break; + } +} + + +/** + * gcu_remove - Device Removal Routine + * @pdev: PCI device information struct + * + * gcu_remove is called by the PCI subsystem to alert the driver + * that it should release a PCI device. The could be caused by a + * Hot-Plug event, or because the driver is going to be removed from + * memory. + **/ +static void __devexit +gcu_remove(struct pci_dev *pdev) +{ + struct gcu_adapter *adapter = pci_get_drvdata(pdev); + + GCU_DBG("%s\n", __func__); + + iounmap(adapter->hw_addr); + pci_release_regions(pdev); + free_gcu_adapter(adapter); + pci_set_drvdata(pdev, NULL); +} + +static int +gcu_notify_reboot(struct notifier_block *nb, unsigned long event, void *p) +{ + struct pci_dev *pdev = NULL; + + GCU_DBG("%s\n", __func__); + + switch(event) { + case SYS_DOWN: + case SYS_HALT: + case SYS_POWER_OFF: + while((pdev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pdev))) { + if(pci_dev_driver(pdev) == &gcu_driver){ + gcu_suspend(pdev, 0x3); + } + } + } + return NOTIFY_DONE; +} + + +/** + * gcu_suspend - device sleep function + * @pdev: PCI device information struct + * + * gcu_supend is generally called to place a device in sleep mode, + * however the GCU doesn't support power mangement. For this case, + * it is part of the gcu_notify_reboot() call chain to quiese the + * device before a reboot. + **/ +static int +gcu_suspend(struct pci_dev *pdev, uint32_t state) +{ + /*struct gcu_adapter *adapter = pci_get_drvdata(pdev); */ +#if ( ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,6) ) && \ + ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) ) ) + struct net_device *netdev = pci_get_drvdata(pdev); + struct gcu_adapter *adapter = netdev_priv(netdev); +#endif + + GCU_DBG("%s\n", __func__); + + pci_save_state(pdev); + + pci_disable_device(pdev); + + state = (state > 0) ? 0 : 0; + + /* + * GCU doesn't support power management, but want to + * leave a hook incase that situation changes in the future + * + * pci_set_power_state(pdev, state); + * + */ + + return state; +} + +/** + * alloc_gcu_adapter + * + * alloc_gcu_adapter is a wrapper for the kmalloc call for the + * device specific data block plus inits the global_adapter variable. + * + * Note that this function assumes that the spinlock for the global + * gcu_adapter struct as been acquired. + **/ +static struct gcu_adapter * +alloc_gcu_adapter() +{ + struct gcu_adapter *adapter; + + GCU_DBG("%s\n", __func__); + + adapter = (struct gcu_adapter*) kmalloc(sizeof(*adapter), GFP_KERNEL); + + global_adapter = adapter; + + if(!adapter) + { + GCU_DBG("Unable to allocate space for global gcu_adapter"); + return 0; + } + + memset(adapter, 0, sizeof(*adapter)); + + return adapter; +} + + +/** + * free_gcu_adapter + * @adapter: gcu_adapter struct to be free'd + * + * free_gcu_adapter is a wrapper for the kfree call for the + * device specific data block plus clears the global_adapter variable + * + * Note that this function assumes that the spinlock for the global + * gcu_adapter struct as been acquired. + **/ +static void +free_gcu_adapter(struct gcu_adapter *adapter) +{ + GCU_DBG("%s\n", __func__); + + global_adapter = 0; + + if(adapter){ + kfree(adapter); + } +} + + +/** + * gcu_get_adapter + * + * gcu_get_adapter is used by the functions exported in gcu_if.c to get + * access to the memory addresses needed to access the MMIO registers + * of the GCU + **/ +const struct gcu_adapter * +gcu_get_adapter(void) +{ + GCU_DBG("%s\n", __func__); + + if(global_adapter == NULL) + { + GCU_DBG("global gcu_adapter is not available\n"); + return NULL; + } + + spin_lock_irqsave(&global_adapter_spinlock, g_intflags); + + return global_adapter; +} + +/** + * gcu_release_adapter + * + * gcu_release_adapter is used by the functions exported in gcu_if.c to get + * release the adapter spinlock and the handle to the adapter + **/ +void +gcu_release_adapter(const struct gcu_adapter **adapter) +{ + GCU_DBG("%s\n", __func__); + + if(adapter == NULL) + { + GCU_ERR("global gcu_adapter handle is invalid\n"); + } + else + { + *adapter = 0; + } + + spin_unlock_irqrestore(&global_adapter_spinlock, g_intflags); + + return; +} + +/* gcu_main.c */ + diff --git a/gcu_reg.h b/gcu_reg.h new file mode 100755 index 0000000..5011b67 --- /dev/null +++ b/gcu_reg.h @@ -0,0 +1,68 @@ +/****************************************************************************** + +GPL LICENSE SUMMARY + + Copyright(c) 2007,2008,2009 Intel Corporation. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + The full GNU General Public License is included in this distribution + in the file called LICENSE.GPL. + + Contact Information: + Intel Corporation + + version: Embedded.Release.Patch.L.1.0.7-5 + + Contact Information: + + Intel Corporation, 5000 W Chandler Blvd, Chandler, AZ 85226 + +******************************************************************************/ + +/* + * gcu_reg.h + * Macros and constants related to the registers available on the GCU + */ + +#ifndef GCU_REG_H +#define GCU_REG_H + +/* Register Offsets within memory map register space */ +#define MDIO_STATUS_REG 0x00000010UL +#define MDIO_COMMAND_REG 0x00000014UL + +/* MDIO_STATUS_REG fields */ +#define MDIO_STATUS_STATUS_MASK 0x80000000UL /* bit 31 = 1 on error */ +#define MDIO_STATUS_READ_DATA_MASK 0x0000FFFFUL + +/* MDIO_COMMAND_REG fields */ +#define MDIO_COMMAND_GO_MASK 0x80000000UL /* bit 31 = 1 during read or + * write, 0 on completion */ +#define MDIO_COMMAND_OPER_MASK 0x04000000UL /* bit = 1 is a write */ +#define MDIO_COMMAND_PHY_ADDR_MASK 0x03E00000UL +#define MDIO_COMMAND_PHY_REG_MASK 0x001F0000UL +#define MDIO_COMMAND_WRITE_DATA_MASK 0x0000FFFFUL + +#define MDIO_COMMAND_GO_OFFSET 31 +#define MDIO_COMMAND_OPER_OFFSET 26 +#define MDIO_COMMAND_PHY_ADDR_OFFSET 21 +#define MDIO_COMMAND_PHY_REG_OFFSET 16 +#define MDIO_COMMAND_WRITE_DATA_OFFSET 0 + +#define MDIO_COMMAND_PHY_ADDR_MAX 2 /* total phys supported by GCU */ +#define MDIO_COMMAND_PHY_REG_MAX 31 /* total registers available on + * the M88 Phy used on truxton */ + +#endif /* ifndef GCU_REG_H */ + diff --git a/kcompat.h b/kcompat.h new file mode 100755 index 0000000..0a04ae7 --- /dev/null +++ b/kcompat.h @@ -0,0 +1,753 @@ +/******************************************************************************* + +GPL LICENSE SUMMARY + + Copyright(c) 2007,2008,2009 Intel Corporation. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + The full GNU General Public License is included in this distribution + in the file called LICENSE.GPL. + + Contact Information: + Intel Corporation + + version: Embedded.Release.Patch.L.1.0.7-5 + + Contact Information: + + Intel Corporation, 5000 W Chandler Blvd, Chandler, AZ 85226 + +*******************************************************************************/ + +#ifndef _KCOMPAT_H_ +#define _KCOMPAT_H_ + +#include <linux/version.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/netdevice.h> +#include <linux/ioport.h> +#include <linux/slab.h> +#include <linux/pagemap.h> +#include <linux/list.h> +#include <linux/sched.h> +#include <asm/io.h> + +#ifndef IRQ_HANDLED +#define irqreturn_t void +#define IRQ_HANDLED +#define IRQ_NONE +#endif + +#ifndef SET_NETDEV_DEV +#define SET_NETDEV_DEV(net, pdev) +#endif + +#ifndef HAVE_FREE_NETDEV +#define free_netdev(x) kfree(x) +#endif + +#ifndef lock_cpu_hotplug +#define lock_cpu_hotplug() do { } while (0) +#define unlock_cpu_hotplug() do { } while (0) +#endif + +#ifdef HAVE_POLL_CONTROLLER +#define CONFIG_NET_POLL_CONTROLLER +#endif + +#ifndef module_param +#define module_param(v,t,p) MODULE_PARM(v, "i"); +#endif + +/*****************************************************************************/ +#ifndef unlikely +#define unlikely(_x) _x +#define likely(_x) _x +#endif +/*****************************************************************************/ + +#ifndef PCI_DEVICE +#define PCI_DEVICE(vend,dev) \ + .vendor = (vend), .device = (dev), \ + .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID +#endif + +/*****************************************************************************/ +/* Installations with ethtool version without eeprom, adapter id, or statistics + * support */ +#ifndef ETHTOOL_GSTATS +#define ETHTOOL_GSTATS 0x1d +#undef ethtool_drvinfo +#define ethtool_drvinfo k_ethtool_drvinfo +struct k_ethtool_drvinfo { + uint32_t cmd; + char driver[32]; + char version[32]; + char fw_version[32]; + char bus_info[32]; + char reserved1[32]; + char reserved2[16]; + uint32_t n_stats; + uint32_t testinfo_len; + uint32_t eedump_len; + uint32_t regdump_len; +}; + +struct ethtool_stats { + uint32_t cmd; + uint32_t n_stats; + uint64_t data[0]; +}; + +#ifndef ETHTOOL_PHYS_ID +#define ETHTOOL_PHYS_ID 0x1c +#ifndef ETHTOOL_GSTRINGS +#define ETHTOOL_GSTRINGS 0x1b +enum ethtool_stringset { + ETH_SS_TEST = 0, + ETH_SS_STATS, +}; +struct ethtool_gstrings { + u32 cmd; /* ETHTOOL_GSTRINGS */ + u32 string_set; /* string set id e.c. ETH_SS_TEST, etc*/ + u32 len; /* number of strings in the string set */ + u8 data[0]; +}; +#ifndef ETHTOOL_TEST +#define ETHTOOL_TEST 0x1a +enum ethtool_test_flags { + ETH_TEST_FL_OFFLINE = (1 << 0), + ETH_TEST_FL_FAILED = (1 << 1), +}; +struct ethtool_test { + uint32_t cmd; + uint32_t flags; + uint32_t reserved; + uint32_t len; + uint64_t data[0]; +}; +#ifndef ETHTOOL_GEEPROM +#define ETHTOOL_GEEPROM 0xb +#undef ETHTOOL_GREGS +struct ethtool_eeprom { + uint32_t cmd; + uint32_t magic; + uint32_t offset; + uint32_t len; + uint8_t data[0]; +}; + +struct ethtool_value { + uint32_t cmd; + uint32_t data; +}; + +#ifndef ETHTOOL_GLINK +#define ETHTOOL_GLINK 0xa +#endif /* Ethtool version without link support */ +#endif /* Ethtool version without eeprom support */ +#endif /* Ethtool version without test support */ +#endif /* Ethtool version without strings support */ +#endif /* Ethtool version wihtout adapter id support */ +#endif /* Ethtool version without statistics support */ + +#ifndef ETHTOOL_GREGS +#define ETHTOOL_GREGS 0x00000004 /* Get NIC registers */ +#define ethtool_regs _kc_ethtool_regs +/* for passing big chunks of data */ +struct _kc_ethtool_regs { + u32 cmd; + u32 version; /* driver-specific, indicates different chips/revs */ + u32 len; /* bytes */ + u8 data[0]; +}; +#endif +#ifndef ETHTOOL_GMSGLVL +#define ETHTOOL_GMSGLVL 0x00000007 /* Get driver message level */ +#endif +#ifndef ETHTOOL_SMSGLVL +#define ETHTOOL_SMSGLVL 0x00000008 /* Set driver msg level, priv. */ +#endif +#ifndef ETHTOOL_NWAY_RST +#define ETHTOOL_NWAY_RST 0x00000009 /* Restart autonegotiation, priv */ +#endif +#ifndef ETHTOOL_GLINK +#define ETHTOOL_GLINK 0x0000000a /* Get link status */ +#endif +#ifndef ETHTOOL_GEEPROM +#define ETHTOOL_GEEPROM 0x0000000b /* Get EEPROM data */ +#endif +#ifndef ETHTOOL_SEEPROM +#define ETHTOOL_SEEPROM 0x0000000c /* Set EEPROM data */ +#endif +#ifndef ETHTOOL_GCOALESCE +#define ETHTOOL_GCOALESCE 0x0000000e /* Get coalesce config */ +/* for configuring coalescing parameters of chip */ +#define ethtool_coalesce _kc_ethtool_coalesce +struct _kc_ethtool_coalesce { + u32 cmd; /* ETHTOOL_{G,S}COALESCE */ + + /* How many usecs to delay an RX interrupt after + * a packet arrives. If 0, only rx_max_coalesced_frames + * is used. + */ + u32 rx_coalesce_usecs; + + /* How many packets to delay an RX interrupt after + * a packet arrives. If 0, only rx_coalesce_usecs is + * used. It is illegal to set both usecs and max frames + * to zero as this would cause RX interrupts to never be + * generated. + */ + u32 rx_max_coalesced_frames; + + /* Same as above two parameters, except that these values + * apply while an IRQ is being serviced by the host. Not + * all cards support this feature and the values are ignored + * in that case. + */ + u32 rx_coalesce_usecs_irq; + u32 rx_max_coalesced_frames_irq; + + /* How many usecs to delay a TX interrupt after + * a packet is sent. If 0, only tx_max_coalesced_frames + * is used. + */ + u32 tx_coalesce_usecs; + + /* How many packets to delay a TX interrupt after + * a packet is sent. If 0, only tx_coalesce_usecs is + * used. It is illegal to set both usecs and max frames + * to zero as this would cause TX interrupts to never be + * generated. + */ + u32 tx_max_coalesced_frames; + + /* Same as above two parameters, except that these values + * apply while an IRQ is being serviced by the host. Not + * all cards support this feature and the values are ignored + * in that case. + */ + u32 tx_coalesce_usecs_irq; + u32 tx_max_coalesced_frames_irq; + + /* How many usecs to delay in-memory statistics + * block updates. Some drivers do not have an in-memory + * statistic block, and in such cases this value is ignored. + * This value must not be zero. + */ + u32 stats_block_coalesce_usecs; + + /* Adaptive RX/TX coalescing is an algorithm implemented by + * some drivers to improve latency under low packet rates and + * improve throughput under high packet rates. Some drivers + * only implement one of RX or TX adaptive coalescing. Anything + * not implemented by the driver causes these values to be + * silently ignored. + */ + u32 use_adaptive_rx_coalesce; + u32 use_adaptive_tx_coalesce; + + /* When the packet rate (measured in packets per second) + * is below pkt_rate_low, the {rx,tx}_*_low parameters are + * used. + */ + u32 pkt_rate_low; + u32 rx_coalesce_usecs_low; + u32 rx_max_coalesced_frames_low; + u32 tx_coalesce_usecs_low; + u32 tx_max_coalesced_frames_low; + + /* When the packet rate is below pkt_rate_high but above + * pkt_rate_low (both measured in packets per second) the + * normal {rx,tx}_* coalescing parameters are used. + */ + + /* When the packet rate is (measured in packets per second) + * is above pkt_rate_high, the {rx,tx}_*_high parameters are + * used. + */ + u32 pkt_rate_high; + u32 rx_coalesce_usecs_high; + u32 rx_max_coalesced_frames_high; + u32 tx_coalesce_usecs_high; + u32 tx_max_coalesced_frames_high; + + /* How often to do adaptive coalescing packet rate sampling, + * measured in seconds. Must not be zero. + */ + u32 rate_sample_interval; +}; +#endif +#ifndef ETHTOOL_SCOALESCE +#define ETHTOOL_SCOALESCE 0x0000000f /* Set coalesce config. */ +#endif +#ifndef ETHTOOL_GRINGPARAM +#define ETHTOOL_GRINGPARAM 0x00000010 /* Get ring parameters */ +/* for configuring RX/TX ring parameters */ +#define ethtool_ringparam _kc_ethtool_ringparam +struct _kc_ethtool_ringparam { + u32 cmd; /* ETHTOOL_{G,S}RINGPARAM */ + + /* Read only attributes. These indicate the maximum number + * of pending RX/TX ring entries the driver will allow the + * user to set. + */ + u32 rx_max_pending; + u32 rx_mini_max_pending; + u32 rx_jumbo_max_pending; + u32 tx_max_pending; + + /* Values changeable by the user. The valid values are + * in the range 1 to the "*_max_pending" counterpart above. + */ + u32 rx_pending; + u32 rx_mini_pending; + u32 rx_jumbo_pending; + u32 tx_pending; +}; +#endif +#ifndef ETHTOOL_SRINGPARAM +#define ETHTOOL_SRINGPARAM 0x00000011 /* Set ring parameters, priv. */ +#endif +#ifndef ETHTOOL_GPAUSEPARAM +#define ETHTOOL_GPAUSEPARAM 0x00000012 /* Get pause parameters */ +/* for configuring link flow control parameters */ +#define ethtool_pauseparam _kc_ethtool_pauseparam +struct _kc_ethtool_pauseparam { + u32 cmd; /* ETHTOOL_{G,S}PAUSEPARAM */ + + /* If the link is being auto-negotiated (via ethtool_cmd.autoneg + * being true) the user may set 'autonet' here non-zero to have the + * pause parameters be auto-negotiated too. In such a case, the + * {rx,tx}_pause values below determine what capabilities are + * advertised. + * + * If 'autoneg' is zero or the link is not being auto-negotiated, + * then {rx,tx}_pause force the driver to use/not-use pause + * flow control. + */ + u32 autoneg; + u32 rx_pause; + u32 tx_pause; +}; +#endif +#ifndef ETHTOOL_SPAUSEPARAM +#define ETHTOOL_SPAUSEPARAM 0x00000013 /* Set pause parameters. */ +#endif +#ifndef ETHTOOL_GRXCSUM +#define ETHTOOL_GRXCSUM 0x00000014 /* Get RX hw csum enable (ethtool_value) */ +#endif +#ifndef ETHTOOL_SRXCSUM +#define ETHTOOL_SRXCSUM 0x00000015 /* Set RX hw csum enable (ethtool_value) */ +#endif +#ifndef ETHTOOL_GTXCSUM +#define ETHTOOL_GTXCSUM 0x00000016 /* Get TX hw csum enable (ethtool_value) */ +#endif +#ifndef ETHTOOL_STXCSUM +#define ETHTOOL_STXCSUM 0x00000017 /* Set TX hw csum enable (ethtool_value) */ +#endif +#ifndef ETHTOOL_GSG +#define ETHTOOL_GSG 0x00000018 /* Get scatter-gather enable + * (ethtool_value) */ +#endif +#ifndef ETHTOOL_SSG +#define ETHTOOL_SSG 0x00000019 /* Set scatter-gather enable + * (ethtool_value). */ +#endif +#ifndef ETHTOOL_TEST +#define ETHTOOL_TEST 0x0000001a /* execute NIC self-test, priv. */ +#endif +#ifndef ETHTOOL_GSTRINGS +#define ETHTOOL_GSTRINGS 0x0000001b /* get specified string set */ +#endif +#ifndef ETHTOOL_PHYS_ID +#define ETHTOOL_PHYS_ID 0x0000001c /* identify the NIC */ +#endif +#ifndef ETHTOOL_GSTATS +#define ETHTOOL_GSTATS 0x0000001d /* get NIC-specific statistics */ +#endif +#ifndef ETHTOOL_GTSO +#define ETHTOOL_GTSO 0x0000001e /* Get TSO enable (ethtool_value) */ +#endif +#ifndef ETHTOOL_STSO +#define ETHTOOL_STSO 0x0000001f /* Set TSO enable (ethtool_value) */ +#endif + +/*****************************************************************************/ +/* 2.4.3 => 2.4.0 */ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,3) ) + +/**************************************/ +/* PCI DRIVER API */ + +#ifndef pci_set_dma_mask +#define pci_set_dma_mask _kc_pci_set_dma_mask +extern int _kc_pci_set_dma_mask(struct pci_dev *dev, dma_addr_t mask); +#endif + +#ifndef pci_request_regions +#define pci_request_regions _kc_pci_request_regions +extern int _kc_pci_request_regions(struct pci_dev *pdev, char *res_name); +#endif + +#ifndef pci_release_regions +#define pci_release_regions _kc_pci_release_regions +extern void _kc_pci_release_regions(struct pci_dev *pdev); +#endif + +/**************************************/ +/* NETWORK DRIVER API */ + +#ifndef alloc_etherdev +#define alloc_etherdev _kc_alloc_etherdev +extern struct net_device * _kc_alloc_etherdev(int sizeof_priv); +#endif + +#ifndef is_valid_ether_addr +#define is_valid_ether_addr _kc_is_valid_ether_addr +extern int _kc_is_valid_ether_addr(u8 *addr); +#endif + +/**************************************/ +/* MISCELLANEOUS */ + +#ifndef INIT_TQUEUE +#define INIT_TQUEUE(_tq, _routine, _data) \ + do { \ + INIT_LIST_HEAD(&(_tq)->list); \ + (_tq)->sync = 0; \ + (_tq)->routine = _routine; \ + (_tq)->data = _data; \ + } while(0) +#endif + +#endif /* 2.4.3 => 2.4.0 */ + +/*****************************************************************************/ +/* 2.4.6 => 2.4.3 */ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,6) ) + +#ifndef pci_set_power_state +#define pci_set_power_state _kc_pci_set_power_state +extern int _kc_pci_set_power_state(struct pci_dev *dev, int state); +#endif + +#ifndef pci_save_state +#define pci_save_state _kc_pci_save_state +extern int _kc_pci_save_state(struct pci_dev *dev, u32 *buffer); +#endif + +#ifndef pci_restore_state +#define pci_restore_state _kc_pci_restore_state +extern int _kc_pci_restore_state(struct pci_dev *pdev, u32 *buffer); +#endif + +#ifndef pci_enable_wake +#define pci_enable_wake _kc_pci_enable_wake +extern int _kc_pci_enable_wake(struct pci_dev *pdev, u32 state, int enable); +#endif + +/* PCI PM entry point syntax changed, so don't support suspend/resume */ +#undef CONFIG_PM + +#endif /* 2.4.6 => 2.4.3 */ + +/*****************************************************************************/ +/* 2.4.10 => 2.4.6 */ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10) ) + +/**************************************/ +/* MODULE API */ + +#ifndef MODULE_LICENSE + #define MODULE_LICENSE(X) +#endif + +/**************************************/ +/* OTHER */ + +#undef min +#define min(x,y) ({ \ + const typeof(x) _x = (x); \ + const typeof(y) _y = (y); \ + (void) (&_x == &_y); \ + _x < _y ? _x : _y; }) + +#undef max +#define max(x,y) ({ \ + const typeof(x) _x = (x); \ + const typeof(y) _y = (y); \ + (void) (&_x == &_y); \ + _x > _y ? _x : _y; }) + +#endif /* 2.4.10 -> 2.4.6 */ + + +/*****************************************************************************/ +/* 2.4.13 => 2.4.10 */ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,13) ) + +/**************************************/ +/* PCI DMA MAPPING */ + +#ifndef virt_to_page + #define virt_to_page(v) (mem_map + (virt_to_phys(v) >> PAGE_SHIFT)) +#endif + +#ifndef pci_map_page +#define pci_map_page _kc_pci_map_page +extern u64 _kc_pci_map_page(struct pci_dev *dev, struct page *page, unsigned long offset, size_t size, int direction); +#endif + +#ifndef pci_unmap_page +#define pci_unmap_page _kc_pci_unmap_page +extern void _kc_pci_unmap_page(struct pci_dev *dev, u64 dma_addr, size_t size, int direction); +#endif + +/* pci_set_dma_mask takes dma_addr_t, which is only 32-bits prior to 2.4.13 */ + +#undef PCI_DMA_32BIT +#define PCI_DMA_32BIT 0xffffffff +#undef PCI_DMA_64BIT +#define PCI_DMA_64BIT 0xffffffff + +#endif /* 2.4.13 => 2.4.10 */ + +/*****************************************************************************/ +/* 2.4.17 => 2.4.12 */ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,17) ) + +#ifndef __devexit_p + #define __devexit_p(x) &(x) +#endif + +#endif /* 2.4.17 => 2.4.13 */ + +/*****************************************************************************/ +/* 2.4.22 => 2.4.17 */ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,22) ) +#define pci_name(x) ((x)->slot_name) +#endif + +/*****************************************************************************/ +/* 2.5.28 => 2.4.23 */ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,5,28) ) + +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,23) ) +static inline void _kc_synchronize_irq(void) { barrier(); } +#else +static inline void _kc_synchronize_irq() { synchronize_irq(); } +#endif /* 2.4.23 */ +#undef synchronize_irq +#define synchronize_irq(X) _kc_synchronize_irq() + +#include <linux/tqueue.h> +#define work_struct tq_struct +#define INIT_WORK INIT_TQUEUE +#define schedule_work schedule_task +#define flush_scheduled_work flush_scheduled_tasks + +#endif /* 2.5.28 => 2.4.17 */ + +/*****************************************************************************/ +/* 2.6.0 => 2.5.28 */ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ) +#define MODULE_INFO(version, _version) +#ifdef CONFIG_E1000_PACKET_SPLIT +#undef CONFIG_E1000_PACKET_SPLIT +#endif +#else +#ifndef CONFIG_E1000_PACKET_SPLIT +#define CONFIG_E1000_PACKET_SPLIT 1 +#endif +#endif /* 2.6.0 => 2.5.28 */ + +/*****************************************************************************/ +/* 2.6.4 => 2.6.0 */ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,4) ) +#define MODULE_VERSION(_version) MODULE_INFO(version, _version) +#endif /* 2.6.4 => 2.6.0 */ + +/*****************************************************************************/ +/* 2.6.5 => 2.6.0 */ +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,5) ) +#define pci_dma_sync_single_for_cpu pci_dma_sync_single +#define pci_dma_sync_single_for_device pci_dma_sync_single_for_cpu +#endif /* 2.6.5 => 2.6.0 */ + +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,25) || \ + ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) && \ + LINUX_VERSION_CODE < KERNEL_VERSION(2,6,4) ) ) +#define ETHTOOL_OPS_COMPAT +#endif + +#ifndef HAVE_NETIF_MSG +#define HAVE_NETIF_MSG 1 +enum { + NETIF_MSG_DRV = 0x0001, + NETIF_MSG_PROBE = 0x0002, + NETIF_MSG_LINK = 0x0004, + NETIF_MSG_TIMER = 0x0008, + NETIF_MSG_IFDOWN = 0x0010, + NETIF_MSG_IFUP = 0x0020, + NETIF_MSG_RX_ERR = 0x0040, + NETIF_MSG_TX_ERR = 0x0080, + NETIF_MSG_TX_QUEUED = 0x0100, + NETIF_MSG_INTR = 0x0200, + NETIF_MSG_TX_DONE = 0x0400, + NETIF_MSG_RX_STATUS = 0x0800, + NETIF_MSG_PKTDATA = 0x1000, + NETIF_MSG_HW = 0x2000, + NETIF_MSG_WOL = 0x4000, +}; +#endif /* ! HAVE_NETIF_MSG */ + +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,7) ) +#undef if_mii +#define if_mii _kc_if_mii +static inline struct mii_ioctl_data *_kc_if_mii(struct ifreq *rq) +{ + return (struct mii_ioctl_data *) &rq->ifr_ifru; +} +#endif /* < 2.6.7 */ + +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,8) ) +#define msleep(x) do { set_current_state(TASK_UNINTERRUPTIBLE); \ + schedule_timeout((x * HZ)/1000 + 2); \ + } while(0) +#endif + +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,9) ) +#define msleep_interruptible(x) do {set_current_state(TASK_INTERRUPTIBLE); \ + schedule_timeout((x * HZ)/1000); \ + } while(0) +#endif + +#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,6) && \ + LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) ) +#ifdef pci_save_state +#undef pci_save_state +#endif +#define pci_save_state(X) { \ + int i; \ + if (adapter->pci_state) { \ + for (i = 0; i < 16; i++) { \ + pci_read_config_dword((X), \ + i * 4, \ + &adapter->pci_state[i]); \ + } \ + } \ +} + +#ifdef pci_restore_state +#undef pci_restore_state +#endif +#define pci_restore_state(X) { \ + int i; \ + if (adapter->pci_state) { \ + for (i = 0; i < 16; i++) { \ + pci_write_config_dword((X), \ + i * 4, \ + adapter->pci_state[i]); \ + } \ + } else { \ + for (i = 0; i < 6; i++) { \ + pci_write_config_dword((X), \ + PCI_BASE_ADDRESS_0 + (i * 4), \ + (X)->resource[i].start); \ + } \ + pci_write_config_byte((X), PCI_INTERRUPT_LINE, (X)->irq); \ + } \ +} +#endif /* 2.4.6 <= x < 2.6.10 */ + +#if (( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,27) ) || \ + (( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ) && \ + ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,3) ))) +#define netdev_priv(x) x->priv +#endif + +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) ) +#ifdef module_param_array_named +#undef module_param_array_named +#define module_param_array_named(name, array, type, nump, perm) \ + static struct kparam_array __param_arr_##name \ + = { ARRAY_SIZE(array), nump, param_set_##type, param_get_##type, \ + sizeof(array[0]), array }; \ + module_param_call(name, param_array_set, param_array_get, \ + &__param_arr_##name, perm) +#endif /* module_param_array_named */ + +#endif /* < 2.6.10 */ + +#ifndef NET_IP_ALIGN +#define NET_IP_ALIGN 2 +#endif + +#ifndef NETDEV_TX_OK +#define NETDEV_TX_OK 0 /* driver took care of the packet */ +#endif + +#ifndef NETDEV_TX_BUSY +#define NETDEV_TX_BUSY 1 /* driver tx path was busy */ +#endif + +#ifndef NETDEV_TX_LOCKED +#define NETDEV_TX_LOCKED -1 /* driver tx lock was already taken */ +#endif + +/* if we do not have the infrastructure to detect if skb_header is cloned * + * just return false in all cases */ +#ifndef SKB_DATAREF_SHIFT +#define skb_header_cloned(x) 0 +#endif /* SKB_DATAREF_SHIFT not defined */ + +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10) ) + +#define ioread32(addr) readl(addr) +#define iowrite32(val,addr) writel(val,addr) + +#endif /* 2.6.10 */ + +#ifndef DEFINE_SPINLOCK +#define DEFINE_SPINLOCK(s) spinlock_t s = SPIN_LOCK_UNLOCKED +#endif /* DEFINE_SPINLOCK */ + +#ifndef PCI_COMMAND_INTX_DISABLE +#define PCI_COMMAND_INTX_DISABLE 0x400 /* INTx Emulation Disable */ +#endif /* PCI_COMMAND_INTX_DISABLE */ + +#ifndef ETH_GSTRING_LEN +#define ETH_GSTRING_LEN 32 +#endif /* ETH_GSTRING_LEN */ + +#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,4,24) ) + +extern void dump_stack(void); + +#undef register_reboot_notifier +#define register_reboot_notifier(a) + +#undef unregister_reboot_notifier +#define unregister_reboot_notifier(a) + +#endif /* 2.4.24 */ + +#endif /* _KCOMPAT_H_ */ + + |