[TG3]: Add 5722 and 5756 support.
[deliverable/linux.git] / drivers / net / tg3.c
index aaf45b907a786aaa9028e60fcf6d38668653ad6d..d443b7372325884212293cfc93f1a4dde599d4d1 100644 (file)
@@ -173,6 +173,7 @@ static struct pci_device_id tg3_pci_tbl[] = {
        {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5705F)},
        {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5720)},
        {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5721)},
+       {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5722)},
        {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5750)},
        {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5751)},
        {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5750M)},
@@ -187,6 +188,7 @@ static struct pci_device_id tg3_pci_tbl[] = {
        {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5754M)},
        {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5755)},
        {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5755M)},
+       {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5756)},
        {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5786)},
        {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5787)},
        {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5787M)},
@@ -1117,6 +1119,12 @@ static void tg3_nvram_unlock(struct tg3 *);
 
 static void tg3_power_down_phy(struct tg3 *tp)
 {
+       if (tp->tg3_flags2 & TG3_FLG2_PHY_SERDES)
+               return;
+
+       tg3_writephy(tp, MII_TG3_EXT_CTRL, MII_TG3_EXT_CTRL_FORCE_LED_OFF);
+       tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x01b2);
+
        /* The PHY should not be powered down on some chips because
         * of bugs.
         */
@@ -1223,7 +1231,10 @@ static int tg3_set_power_state(struct tg3 *tp, pci_power_t state)
                        tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x5a);
                        udelay(40);
 
-                       mac_mode = MAC_MODE_PORT_MODE_MII;
+                       if (tp->tg3_flags2 & TG3_FLG2_MII_SERDES)
+                               mac_mode = MAC_MODE_PORT_MODE_GMII;
+                       else
+                               mac_mode = MAC_MODE_PORT_MODE_MII;
 
                        if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5700 ||
                            !(tp->tg3_flags & TG3_FLAG_WOL_SPEED_100MB))
@@ -1301,15 +1312,8 @@ static int tg3_set_power_state(struct tg3 *tp, pci_power_t state)
        }
 
        if (!(tp->tg3_flags & TG3_FLAG_WOL_ENABLE) &&
-           !(tp->tg3_flags & TG3_FLAG_ENABLE_ASF)) {
-               /* Turn off the PHY */
-               if (!(tp->tg3_flags2 & TG3_FLG2_PHY_SERDES)) {
-                       tg3_writephy(tp, MII_TG3_EXT_CTRL,
-                                    MII_TG3_EXT_CTRL_FORCE_LED_OFF);
-                       tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x01b2);
-                       tg3_power_down_phy(tp);
-               }
-       }
+           !(tp->tg3_flags & TG3_FLAG_ENABLE_ASF))
+               tg3_power_down_phy(tp);
 
        tg3_frob_aux_power(tp);
 
@@ -2406,24 +2410,27 @@ static int tg3_setup_fiber_hw_autoneg(struct tg3 *tp, u32 mac_status)
        expected_sg_dig_ctrl |= (1 << 12);
 
        if (sg_dig_ctrl != expected_sg_dig_ctrl) {
+               if ((tp->tg3_flags2 & TG3_FLG2_PARALLEL_DETECT) &&
+                   tp->serdes_counter &&
+                   ((mac_status & (MAC_STATUS_PCS_SYNCED |
+                                   MAC_STATUS_RCVD_CFG)) ==
+                    MAC_STATUS_PCS_SYNCED)) {
+                       tp->serdes_counter--;
+                       current_link_up = 1;
+                       goto out;
+               }
+restart_autoneg:
                if (workaround)
                        tw32_f(MAC_SERDES_CFG, serdes_cfg | 0xc011000);
                tw32_f(SG_DIG_CTRL, expected_sg_dig_ctrl | (1 << 30));
                udelay(5);
                tw32_f(SG_DIG_CTRL, expected_sg_dig_ctrl);
 
-               tp->tg3_flags2 |= TG3_FLG2_PHY_JUST_INITTED;
+               tp->serdes_counter = SERDES_AN_TIMEOUT_5704S;
+               tp->tg3_flags2 &= ~TG3_FLG2_PARALLEL_DETECT;
        } else if (mac_status & (MAC_STATUS_PCS_SYNCED |
                                 MAC_STATUS_SIGNAL_DET)) {
-               int i;
-
-               /* Giver time to negotiate (~200ms) */
-               for (i = 0; i < 40000; i++) {
-                       sg_dig_status = tr32(SG_DIG_STATUS);
-                       if (sg_dig_status & (0x3))
-                               break;
-                       udelay(5);
-               }
+               sg_dig_status = tr32(SG_DIG_STATUS);
                mac_status = tr32(MAC_STATUS);
 
                if ((sg_dig_status & (1 << 1)) &&
@@ -2439,10 +2446,11 @@ static int tg3_setup_fiber_hw_autoneg(struct tg3 *tp, u32 mac_status)
 
                        tg3_setup_flow_control(tp, local_adv, remote_adv);
                        current_link_up = 1;
-                       tp->tg3_flags2 &= ~TG3_FLG2_PHY_JUST_INITTED;
+                       tp->serdes_counter = 0;
+                       tp->tg3_flags2 &= ~TG3_FLG2_PARALLEL_DETECT;
                } else if (!(sg_dig_status & (1 << 1))) {
-                       if (tp->tg3_flags2 & TG3_FLG2_PHY_JUST_INITTED)
-                               tp->tg3_flags2 &= ~TG3_FLG2_PHY_JUST_INITTED;
+                       if (tp->serdes_counter)
+                               tp->serdes_counter--;
                        else {
                                if (workaround) {
                                        u32 val = serdes_cfg;
@@ -2466,9 +2474,17 @@ static int tg3_setup_fiber_hw_autoneg(struct tg3 *tp, u32 mac_status)
                                    !(mac_status & MAC_STATUS_RCVD_CFG)) {
                                        tg3_setup_flow_control(tp, 0, 0);
                                        current_link_up = 1;
-                               }
+                                       tp->tg3_flags2 |=
+                                               TG3_FLG2_PARALLEL_DETECT;
+                                       tp->serdes_counter =
+                                               SERDES_PARALLEL_DET_TIMEOUT;
+                               } else
+                                       goto restart_autoneg;
                        }
                }
+       } else {
+               tp->serdes_counter = SERDES_AN_TIMEOUT_5704S;
+               tp->tg3_flags2 &= ~TG3_FLG2_PARALLEL_DETECT;
        }
 
 out:
@@ -2599,14 +2615,16 @@ static int tg3_setup_fiber_phy(struct tg3 *tp, int force_reset)
                                    MAC_STATUS_CFG_CHANGED));
                udelay(5);
                if ((tr32(MAC_STATUS) & (MAC_STATUS_SYNC_CHANGED |
-                                        MAC_STATUS_CFG_CHANGED)) == 0)
+                                        MAC_STATUS_CFG_CHANGED |
+                                        MAC_STATUS_LNKSTATE_CHANGED)) == 0)
                        break;
        }
 
        mac_status = tr32(MAC_STATUS);
        if ((mac_status & MAC_STATUS_PCS_SYNCED) == 0) {
                current_link_up = 0;
-               if (tp->link_config.autoneg == AUTONEG_ENABLE) {
+               if (tp->link_config.autoneg == AUTONEG_ENABLE &&
+                   tp->serdes_counter == 0) {
                        tw32_f(MAC_MODE, (tp->mac_mode |
                                          MAC_MODE_SEND_CONFIGS));
                        udelay(1);
@@ -2711,7 +2729,7 @@ static int tg3_setup_fiber_mii_phy(struct tg3 *tp, int force_reset)
                        tg3_writephy(tp, MII_BMCR, bmcr);
 
                        tw32_f(MAC_EVENT, MAC_EVENT_LNKSTATE_CHANGED);
-                       tp->tg3_flags2 |= TG3_FLG2_PHY_JUST_INITTED;
+                       tp->serdes_counter = SERDES_AN_TIMEOUT_5714S;
                        tp->tg3_flags2 &= ~TG3_FLG2_PARALLEL_DETECT;
 
                        return err;
@@ -2816,9 +2834,9 @@ static int tg3_setup_fiber_mii_phy(struct tg3 *tp, int force_reset)
 
 static void tg3_serdes_parallel_detect(struct tg3 *tp)
 {
-       if (tp->tg3_flags2 & TG3_FLG2_PHY_JUST_INITTED) {
+       if (tp->serdes_counter) {
                /* Give autoneg time to complete. */
-               tp->tg3_flags2 &= ~TG3_FLG2_PHY_JUST_INITTED;
+               tp->serdes_counter--;
                return;
        }
        if (!netif_carrier_ok(tp->dev) &&
@@ -6660,12 +6678,14 @@ static void tg3_timer(unsigned long __opaque)
                                need_setup = 1;
                        }
                        if (need_setup) {
-                               tw32_f(MAC_MODE,
-                                    (tp->mac_mode &
-                                     ~MAC_MODE_PORT_MODE_MASK));
-                               udelay(40);
-                               tw32_f(MAC_MODE, tp->mac_mode);
-                               udelay(40);
+                               if (!tp->serdes_counter) {
+                                       tw32_f(MAC_MODE,
+                                            (tp->mac_mode &
+                                             ~MAC_MODE_PORT_MODE_MASK));
+                                       udelay(40);
+                                       tw32_f(MAC_MODE, tp->mac_mode);
+                                       udelay(40);
+                               }
                                tg3_setup_phy(tp, 0);
                        }
                } else if (tp->tg3_flags2 & TG3_FLG2_MII_SERDES)
@@ -6674,13 +6694,29 @@ static void tg3_timer(unsigned long __opaque)
                tp->timer_counter = tp->timer_multiplier;
        }
 
-       /* Heartbeat is only sent once every 2 seconds.  */
+       /* Heartbeat is only sent once every 2 seconds.
+        *
+        * The heartbeat is to tell the ASF firmware that the host
+        * driver is still alive.  In the event that the OS crashes,
+        * ASF needs to reset the hardware to free up the FIFO space
+        * that may be filled with rx packets destined for the host.
+        * If the FIFO is full, ASF will no longer function properly.
+        *
+        * Unintended resets have been reported on real time kernels
+        * where the timer doesn't run on time.  Netpoll will also have
+        * same problem.
+        *
+        * The new FWCMD_NICDRV_ALIVE3 command tells the ASF firmware
+        * to check the ring condition when the heartbeat is expiring
+        * before doing the reset.  This will prevent most unintended
+        * resets.
+        */
        if (!--tp->asf_counter) {
                if (tp->tg3_flags & TG3_FLAG_ENABLE_ASF) {
                        u32 val;
 
                        tg3_write_mem(tp, NIC_SRAM_FW_CMD_MBOX,
-                                     FWCMD_NICDRV_ALIVE2);
+                                     FWCMD_NICDRV_ALIVE3);
                        tg3_write_mem(tp, NIC_SRAM_FW_CMD_LEN_MBOX, 4);
                        /* 5 seconds timeout */
                        tg3_write_mem(tp, NIC_SRAM_FW_CMD_DATA_MBOX, 5);
@@ -7857,7 +7893,7 @@ static int tg3_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
        if (wol->wolopts & ~WAKE_MAGIC)
                return -EINVAL;
        if ((wol->wolopts & WAKE_MAGIC) &&
-           tp->tg3_flags2 & TG3_FLG2_PHY_SERDES &&
+           tp->tg3_flags2 & TG3_FLG2_ANY_SERDES &&
            !(tp->tg3_flags & TG3_FLAG_SERDES_WOL_CAP))
                return -EINVAL;
 
@@ -8541,12 +8577,22 @@ static int tg3_run_loopback(struct tg3 *tp, int loopback_mode)
                        return 0;
 
                mac_mode = (tp->mac_mode & ~MAC_MODE_PORT_MODE_MASK) |
-                          MAC_MODE_PORT_INT_LPBACK | MAC_MODE_LINK_POLARITY |
-                          MAC_MODE_PORT_MODE_GMII;
+                          MAC_MODE_PORT_INT_LPBACK | MAC_MODE_LINK_POLARITY;
+               if (tp->tg3_flags & TG3_FLAG_10_100_ONLY)
+                       mac_mode |= MAC_MODE_PORT_MODE_MII;
+               else
+                       mac_mode |= MAC_MODE_PORT_MODE_GMII;
                tw32(MAC_MODE, mac_mode);
        } else if (loopback_mode == TG3_PHY_LOOPBACK) {
-               tg3_writephy(tp, MII_BMCR, BMCR_LOOPBACK | BMCR_FULLDPLX |
-                                          BMCR_SPEED1000);
+               u32 val;
+
+               val = BMCR_LOOPBACK | BMCR_FULLDPLX;
+               if (tp->tg3_flags & TG3_FLAG_10_100_ONLY)
+                       val |= BMCR_SPEED100;
+               else
+                       val |= BMCR_SPEED1000;
+
+               tg3_writephy(tp, MII_BMCR, val);
                udelay(40);
                /* reset to prevent losing 1st rx packet intermittently */
                if (tp->tg3_flags2 & TG3_FLG2_MII_SERDES) {
@@ -8555,7 +8601,11 @@ static int tg3_run_loopback(struct tg3 *tp, int loopback_mode)
                        tw32_f(MAC_RX_MODE, tp->rx_mode);
                }
                mac_mode = (tp->mac_mode & ~MAC_MODE_PORT_MODE_MASK) |
-                          MAC_MODE_LINK_POLARITY | MAC_MODE_PORT_MODE_GMII;
+                          MAC_MODE_LINK_POLARITY;
+               if (tp->tg3_flags & TG3_FLAG_10_100_ONLY)
+                       mac_mode |= MAC_MODE_PORT_MODE_MII;
+               else
+                       mac_mode |= MAC_MODE_PORT_MODE_GMII;
                if ((tp->phy_id & PHY_ID_MASK) == PHY_ID_BCM5401) {
                        mac_mode &= ~MAC_MODE_LINK_POLARITY;
                        tg3_writephy(tp, MII_TG3_EXT_CTRL,
@@ -8604,7 +8654,8 @@ static int tg3_run_loopback(struct tg3 *tp, int loopback_mode)
 
        udelay(10);
 
-       for (i = 0; i < 10; i++) {
+       /* 250 usec to allow enough time on some 10/100 Mbps devices.  */
+       for (i = 0; i < 25; i++) {
                tw32_f(HOSTCC_MODE, tp->coalesce_mode | HOSTCC_MODE_ENABLE |
                       HOSTCC_MODE_NOW);
 
@@ -11224,6 +11275,7 @@ static char * __devinit tg3_phy_string(struct tg3 *tp)
        case PHY_ID_BCM5780:    return "5780";
        case PHY_ID_BCM5755:    return "5755";
        case PHY_ID_BCM5787:    return "5787";
+       case PHY_ID_BCM5756:    return "5722/5756";
        case PHY_ID_BCM8002:    return "8002/serdes";
        case 0:                 return "serdes";
        default:                return "unknown";
This page took 0.031902 seconds and 5 git commands to generate.