e100: add support for 82552 10/100 adapter
authorBruce Allan <bruce.w.allan@intel.com>
Sat, 21 Mar 2009 20:25:25 +0000 (13:25 -0700)
committerDavid S. Miller <davem@davemloft.net>
Sat, 21 Mar 2009 20:25:25 +0000 (13:25 -0700)
This patch enables support for the new Intel 82552 adapter (new PHY paired
with the existing MAC in the ICH7 chipset).  No new features are added to
the driver, however there are minor changes due to updated registers and a
few workarounds for hardware errata.

Signed-off-by: Bruce Allan <bruce.w.allan@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/e100.c

index 861d2eeaa43c1bd34deaeefc956d5d3f62a1788e..0504db9ad6434c50bf1e9585b5a00b2090f7db26 100644 (file)
 
 #define DRV_NAME               "e100"
 #define DRV_EXT                        "-NAPI"
-#define DRV_VERSION            "3.5.23-k6"DRV_EXT
+#define DRV_VERSION            "3.5.24-k2"DRV_EXT
 #define DRV_DESCRIPTION                "Intel(R) PRO/100 Network Driver"
 #define DRV_COPYRIGHT          "Copyright(c) 1999-2006 Intel Corporation"
 #define PFX                    DRV_NAME ": "
@@ -240,6 +240,7 @@ static struct pci_device_id e100_id_table[] = {
        INTEL_8255X_ETHERNET_DEVICE(0x1093, 7),
        INTEL_8255X_ETHERNET_DEVICE(0x1094, 7),
        INTEL_8255X_ETHERNET_DEVICE(0x1095, 7),
+       INTEL_8255X_ETHERNET_DEVICE(0x10fe, 7),
        INTEL_8255X_ETHERNET_DEVICE(0x1209, 0),
        INTEL_8255X_ETHERNET_DEVICE(0x1229, 0),
        INTEL_8255X_ETHERNET_DEVICE(0x2449, 2),
@@ -275,6 +276,7 @@ enum phy {
        phy_82562_em = 0x032002A8,
        phy_82562_ek = 0x031002A8,
        phy_82562_eh = 0x017002A8,
+       phy_82552_v  = 0xd061004d,
        phy_unknown  = 0xFFFFFFFF,
 };
 
@@ -943,6 +945,22 @@ static int mdio_read(struct net_device *netdev, int addr, int reg)
 
 static void mdio_write(struct net_device *netdev, int addr, int reg, int data)
 {
+       struct nic *nic = netdev_priv(netdev);
+
+       if  ((nic->phy == phy_82552_v) && (reg == MII_BMCR) &&
+            (data & (BMCR_ANRESTART | BMCR_ANENABLE))) {
+               u16 advert = mdio_read(netdev, nic->mii.phy_id, MII_ADVERTISE);
+
+               /*
+                * Workaround Si issue where sometimes the part will not
+                * autoneg to 100Mbps even when advertised.
+                */
+               if (advert & ADVERTISE_100FULL)
+                       data |= BMCR_SPEED100 | BMCR_FULLDPLX;
+               else if (advert & ADVERTISE_100HALF)
+                       data |= BMCR_SPEED100;
+       }
+
        mdio_ctrl(netdev_priv(netdev), addr, mdi_write, reg, data);
 }
 
@@ -1276,16 +1294,12 @@ static int e100_phy_init(struct nic *nic)
        if (addr == 32)
                return -EAGAIN;
 
-       /* Selected the phy and isolate the rest */
-       for (addr = 0; addr < 32; addr++) {
-               if (addr != nic->mii.phy_id) {
-                       mdio_write(netdev, addr, MII_BMCR, BMCR_ISOLATE);
-               } else {
-                       bmcr = mdio_read(netdev, addr, MII_BMCR);
-                       mdio_write(netdev, addr, MII_BMCR,
-                               bmcr & ~BMCR_ISOLATE);
-               }
-       }
+       /* Isolate all the PHY ids */
+       for (addr = 0; addr < 32; addr++)
+               mdio_write(netdev, addr, MII_BMCR, BMCR_ISOLATE);
+       /* Select the discovered PHY */
+       bmcr &= ~BMCR_ISOLATE;
+       mdio_write(netdev, nic->mii.phy_id, MII_BMCR, bmcr);
 
        /* Get phy ID */
        id_lo = mdio_read(netdev, nic->mii.phy_id, MII_PHYSID1);
@@ -1303,7 +1317,18 @@ static int e100_phy_init(struct nic *nic)
                mdio_write(netdev, nic->mii.phy_id, MII_NSC_CONG, cong);
        }
 
-       if ((nic->mac >= mac_82550_D102) || ((nic->flags & ich) &&
+       if (nic->phy == phy_82552_v) {
+               u16 advert = mdio_read(netdev, nic->mii.phy_id, MII_ADVERTISE);
+
+               /* Workaround Si not advertising flow-control during autoneg */
+               advert |= ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM;
+               mdio_write(netdev, nic->mii.phy_id, MII_ADVERTISE, advert);
+
+               /* Reset for the above changes to take effect */
+               bmcr = mdio_read(netdev, nic->mii.phy_id, MII_BMCR);
+               bmcr |= BMCR_RESET;
+               mdio_write(netdev, nic->mii.phy_id, MII_BMCR, bmcr);
+       } else if ((nic->mac >= mac_82550_D102) || ((nic->flags & ich) &&
           (mdio_read(netdev, nic->mii.phy_id, MII_TPISTATUS) & 0x8000) &&
                !(nic->eeprom[eeprom_cnfg_mdix] & eeprom_mdix_enabled))) {
                /* enable/disable MDI/MDI-X auto-switching. */
@@ -2134,6 +2159,9 @@ err_clean_rx:
 }
 
 #define MII_LED_CONTROL        0x1B
+#define E100_82552_LED_OVERRIDE 0x19
+#define E100_82552_LED_ON       0x000F /* LEDTX and LED_RX both on */
+#define E100_82552_LED_OFF      0x000A /* LEDTX and LED_RX both off */
 static void e100_blink_led(unsigned long data)
 {
        struct nic *nic = (struct nic *)data;
@@ -2143,10 +2171,19 @@ static void e100_blink_led(unsigned long data)
                led_on_559 = 0x05,
                led_on_557 = 0x07,
        };
+       u16 led_reg = MII_LED_CONTROL;
+
+       if (nic->phy == phy_82552_v) {
+               led_reg = E100_82552_LED_OVERRIDE;
 
-       nic->leds = (nic->leds & led_on) ? led_off :
-               (nic->mac < mac_82559_D101M) ? led_on_557 : led_on_559;
-       mdio_write(nic->netdev, nic->mii.phy_id, MII_LED_CONTROL, nic->leds);
+               nic->leds = (nic->leds == E100_82552_LED_ON) ?
+                           E100_82552_LED_OFF : E100_82552_LED_ON;
+       } else {
+               nic->leds = (nic->leds & led_on) ? led_off :
+                           (nic->mac < mac_82559_D101M) ? led_on_557 :
+                           led_on_559;
+       }
+       mdio_write(nic->netdev, nic->mii.phy_id, led_reg, nic->leds);
        mod_timer(&nic->blink_timer, jiffies + HZ / 4);
 }
 
@@ -2375,13 +2412,15 @@ static void e100_diag_test(struct net_device *netdev,
 static int e100_phys_id(struct net_device *netdev, u32 data)
 {
        struct nic *nic = netdev_priv(netdev);
+       u16 led_reg = (nic->phy == phy_82552_v) ? E100_82552_LED_OVERRIDE :
+                     MII_LED_CONTROL;
 
        if (!data || data > (u32)(MAX_SCHEDULE_TIMEOUT / HZ))
                data = (u32)(MAX_SCHEDULE_TIMEOUT / HZ);
        mod_timer(&nic->blink_timer, jiffies);
        msleep_interruptible(data * 1000);
        del_timer_sync(&nic->blink_timer);
-       mdio_write(netdev, nic->mii.phy_id, MII_LED_CONTROL, 0);
+       mdio_write(netdev, nic->mii.phy_id, led_reg, 0);
 
        return 0;
 }
@@ -2686,6 +2725,9 @@ static void __devexit e100_remove(struct pci_dev *pdev)
        }
 }
 
+#define E100_82552_SMARTSPEED   0x14   /* SmartSpeed Ctrl register */
+#define E100_82552_REV_ANEG     0x0200 /* Reverse auto-negotiation */
+#define E100_82552_ANEG_NOW     0x0400 /* Auto-negotiate now */
 static int e100_suspend(struct pci_dev *pdev, pm_message_t state)
 {
        struct net_device *netdev = pci_get_drvdata(pdev);
@@ -2698,6 +2740,15 @@ static int e100_suspend(struct pci_dev *pdev, pm_message_t state)
        pci_save_state(pdev);
 
        if ((nic->flags & wol_magic) | e100_asf(nic)) {
+               /* enable reverse auto-negotiation */
+               if (nic->phy == phy_82552_v) {
+                       u16 smartspeed = mdio_read(netdev, nic->mii.phy_id,
+                                                  E100_82552_SMARTSPEED);
+
+                       mdio_write(netdev, nic->mii.phy_id,
+                                  E100_82552_SMARTSPEED, smartspeed |
+                                  E100_82552_REV_ANEG | E100_82552_ANEG_NOW);
+               }
                if (pci_enable_wake(pdev, PCI_D3cold, true))
                        pci_enable_wake(pdev, PCI_D3hot, true);
        } else {
@@ -2721,6 +2772,16 @@ static int e100_resume(struct pci_dev *pdev)
        /* ack any pending wake events, disable PME */
        pci_enable_wake(pdev, 0, 0);
 
+       /* disbale reverse auto-negotiation */
+       if (nic->phy == phy_82552_v) {
+               u16 smartspeed = mdio_read(netdev, nic->mii.phy_id,
+                                          E100_82552_SMARTSPEED);
+
+               mdio_write(netdev, nic->mii.phy_id,
+                          E100_82552_SMARTSPEED,
+                          smartspeed & ~(E100_82552_REV_ANEG));
+       }
+
        netif_device_attach(netdev);
        if (netif_running(netdev))
                e100_up(nic);