summaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/intel/e1000/e1000_main.c
diff options
context:
space:
mode:
authorNoe Rubinstein <nrubinstein@avencall.com>2012-02-09 12:04:05 +0100
committerNoe Rubinstein <nrubinstein@avencall.com>2012-02-16 10:54:51 +0100
commit9be5a1c601171fd77aca2c978d1895f669a846f3 (patch)
tree33d83dcb1828941671330cca0168d82f916419bd /drivers/net/ethernet/intel/e1000/e1000_main.c
parentaaf6dd2e34de5db8b8f40297ef4660c7196e8407 (diff)
net: e1000: Add support for Intel EP80579 and some OEM phys
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 Signed-off-by: Noe Rubinstein <nrubinstein@avencall.com>
Diffstat (limited to 'drivers/net/ethernet/intel/e1000/e1000_main.c')
-rw-r--r--drivers/net/ethernet/intel/e1000/e1000_main.c212
1 files changed, 200 insertions, 12 deletions
diff --git a/drivers/net/ethernet/intel/e1000/e1000_main.c b/drivers/net/ethernet/intel/e1000/e1000_main.c
index 1f86a70b88a..ea12cc65646 100644
--- a/drivers/net/ethernet/intel/e1000/e1000_main.c
+++ b/drivers/net/ethernet/intel/e1000/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>
#include <linux/io.h>
#include <linux/prefetch.h>
@@ -83,6 +88,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),
INTEL_E1000_ETHERNET_DEVICE(0x2E6E),
/* required last entry */
{0,}
@@ -219,6 +236,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;
+static struct pci_dev *gcu;
+#endif
+
+
/**
* e1000_get_hw_dev - return device
* used by hardware layer to print debugging information
@@ -252,10 +276,15 @@ static int __init e1000_init_module(void)
pr_info("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
@@ -580,6 +609,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;
@@ -912,12 +942,13 @@ static int e1000_init_hw_struct(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;
}
-
return 0;
}
@@ -1143,6 +1174,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){
@@ -1189,6 +1226,17 @@ static int __devinit e1000_probe(struct pci_dev *pdev,
adapter->wol = adapter->eeprom_wol;
device_set_wakeup_enable(&adapter->pdev->dev, adapter->wol);
+ /* 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);
+ }
+
/* Auto detect PHY address */
if (hw->mac_type == e1000_ce4100) {
for (i = 0; i < 32; i++) {
@@ -1206,7 +1254,28 @@ static int __devinit e1000_probe(struct pci_dev *pdev,
/* 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;
@@ -1314,6 +1383,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);
mutex_init(&adapter->mutex);
@@ -1624,8 +1701,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:
@@ -2381,6 +2462,23 @@ 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 = 0;
+ int32_t ret_val;
+
+ ret_val = e1000_oem_phy_is_link_up(&adapter->hw, &isUp);
+ if (ret_val != E1000_SUCCESS)
+ isUp = 0;
+
+ if (isUp != adapter->hw.icp_xxxx_is_link_up)
+ adapter->hw.get_link_status = 1;
+ }
+
/* get_link_status is set on LSC (link status) interrupt or rx
* sequence error interrupt (except on intel ce4100).
* get_link_status will stay false until the
@@ -2406,10 +2504,23 @@ 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;
}
@@ -3708,7 +3819,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;
@@ -4648,7 +4761,10 @@ static int e1000_mii_ioctl(struct net_device *netdev, struct ifreq *ifr,
u16 mii_reg;
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) {
@@ -4675,7 +4791,11 @@ 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)
@@ -4897,11 +5017,14 @@ int e1000_set_spd_dplx(struct e1000_adapter *adapter, u32 spd, u8 dplx)
goto err_inval;
/* Fiber NICs only allow 1000 gbps Full duplex */
- if ((hw->media_type == e1000_media_type_fiber) &&
+ if ((hw->media_type == e1000_media_type_fiber ||
+ (adapter->hw.media_type == e1000_media_type_oem &&
+ !e1000_oem_phy_is_copper(&adapter->hw))) &&
spd != SPEED_1000 &&
dplx != DUPLEX_FULL)
goto err_inval;
+
switch (spd + dplx) {
case SPEED_10 + DUPLEX_HALF:
hw->forced_speed_duplex = e1000_10_half;
@@ -4954,9 +5077,22 @@ 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 = 0;
+ if (e1000_oem_phy_is_link_up(&adapter->hw, &isUp) !=
+ E1000_SUCCESS)
+ isUp = 0;
+ if (isUp)
+ wufc &= ~E1000_WUFC_LNKC;
+ }
if (wufc) {
e1000_setup_rctl(adapter);
@@ -4976,7 +5112,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);
}
@@ -5014,8 +5151,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)
@@ -5028,6 +5168,34 @@ 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);
+
+ cmd_word &= ~PCI_COMMAND_IO;
+ cmd_word &= ~PCI_COMMAND_MEMORY;
+ 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;
}
@@ -5037,6 +5205,26 @@ 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(KERN_ERR "Unable to resume GCU!\n");
+ }
+ }
+#endif
pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);