summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan Stadler <istadler@sutus.com>2011-10-04 15:48:30 -0700
committerNoe Rubinstein <nrubinstein@avencall.com>2011-12-23 14:51:01 +0100
commita3e9ebba269b63037abf614337fc17c152fcb976 (patch)
tree829a6af1d7f6548a6000fc06b4015c4e16dafda7
parent53bb00e2440db9d03318e4aa3c8b1b9d8b992da4 (diff)
net: e1000: Added support for Intel EP80579 "Tolapai" GCU and some OEM phys
The Global Configuration Unit (GCU) manages the 3 gigabit MACs that are baked onto the Intel EP80579 SoC. The GCU is registered as a device on the PCI bus and the driver handles the init, probe, and removal of the device as well as entering into ACPI power sleep states. Any number of PHYs can be attached to the MACs in order to connect to a network. Support for the following PHYs is addded via the e1000_oem_phy portion of the addition: Intel M88E1000 Intel M88E1011 Intel IGP01E1000 Intel M88E1141 Vitesse VSC8211 Vitesse VSC8601
-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_ */
+
+