summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile13
-rw-r--r--e1000_ethtool.c52
-rw-r--r--e1000_hw.c372
-rw-r--r--e1000_hw.h35
-rw-r--r--e1000_main.c225
-rwxr-xr-xe1000_oem_phy.c2497
-rwxr-xr-xe1000_oem_phy.h235
-rw-r--r--e1000_osdep.h8
-rw-r--r--e1000_param.c8
-rwxr-xr-xgcu.h105
-rwxr-xr-xgcu_if.c371
-rwxr-xr-xgcu_if.h52
-rwxr-xr-xgcu_main.c454
-rwxr-xr-xgcu_reg.h68
-rwxr-xr-xkcompat.h753
15 files changed, 5174 insertions, 74 deletions
diff --git a/Makefile b/Makefile
index 4a6ab15..7d693f2 100644
--- a/Makefile
+++ b/Makefile
@@ -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, &regs_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;
diff --git a/e1000_hw.c b/e1000_hw.c
index 8d7d87f..23787d5 100644
--- a/e1000_hw.c
+++ b/e1000_hw.c
@@ -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);
diff --git a/e1000_hw.h b/e1000_hw.h
index 9acfddb..bed5060 100644
--- a/e1000_hw.h
+++ b/e1000_hw.h
@@ -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();
}
diff --git a/gcu.h b/gcu.h
new file mode 100755
index 0000000..e0016cc
--- /dev/null
+++ b/gcu.h
@@ -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_ */
+
+