Merge tag 'for-3.6' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci
[linux-2.6-block.git] / drivers / net / ethernet / broadcom / tg3.c
index 09fa3c687a1ff57e3b32e269cf5f23c1551e4cb0..9a009fd6ea1b716418d3ad85fd72b4c72254be2e 100644 (file)
 #include <linux/prefetch.h>
 #include <linux/dma-mapping.h>
 #include <linux/firmware.h>
+#if IS_ENABLED(CONFIG_HWMON)
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#endif
 
 #include <net/checksum.h>
 #include <net/ip.h>
@@ -298,6 +302,7 @@ static DEFINE_PCI_DEVICE_TABLE(tg3_pci_tbl) = {
        {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_57795)},
        {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_5719)},
        {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_5720)},
+       {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, TG3PCI_DEVICE_TIGON3_57762)},
        {PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_9DXX)},
        {PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_9MXX)},
        {PCI_DEVICE(PCI_VENDOR_ID_ALTIMA, PCI_DEVICE_ID_ALTIMA_AC1000)},
@@ -730,44 +735,131 @@ static void tg3_ape_unlock(struct tg3 *tp, int locknum)
        tg3_ape_write32(tp, gnt + 4 * locknum, bit);
 }
 
-static void tg3_ape_send_event(struct tg3 *tp, u32 event)
+static int tg3_ape_event_lock(struct tg3 *tp, u32 timeout_us)
 {
-       int i;
        u32 apedata;
 
-       /* NCSI does not support APE events */
-       if (tg3_flag(tp, APE_HAS_NCSI))
-               return;
+       while (timeout_us) {
+               if (tg3_ape_lock(tp, TG3_APE_LOCK_MEM))
+                       return -EBUSY;
+
+               apedata = tg3_ape_read32(tp, TG3_APE_EVENT_STATUS);
+               if (!(apedata & APE_EVENT_STATUS_EVENT_PENDING))
+                       break;
+
+               tg3_ape_unlock(tp, TG3_APE_LOCK_MEM);
+
+               udelay(10);
+               timeout_us -= (timeout_us > 10) ? 10 : timeout_us;
+       }
+
+       return timeout_us ? 0 : -EBUSY;
+}
+
+static int tg3_ape_wait_for_event(struct tg3 *tp, u32 timeout_us)
+{
+       u32 i, apedata;
+
+       for (i = 0; i < timeout_us / 10; i++) {
+               apedata = tg3_ape_read32(tp, TG3_APE_EVENT_STATUS);
+
+               if (!(apedata & APE_EVENT_STATUS_EVENT_PENDING))
+                       break;
+
+               udelay(10);
+       }
+
+       return i == timeout_us / 10;
+}
+
+int tg3_ape_scratchpad_read(struct tg3 *tp, u32 *data, u32 base_off, u32 len)
+{
+       int err;
+       u32 i, bufoff, msgoff, maxlen, apedata;
+
+       if (!tg3_flag(tp, APE_HAS_NCSI))
+               return 0;
 
        apedata = tg3_ape_read32(tp, TG3_APE_SEG_SIG);
        if (apedata != APE_SEG_SIG_MAGIC)
-               return;
+               return -ENODEV;
 
        apedata = tg3_ape_read32(tp, TG3_APE_FW_STATUS);
        if (!(apedata & APE_FW_STATUS_READY))
-               return;
+               return -EAGAIN;
 
-       /* Wait for up to 1 millisecond for APE to service previous event. */
-       for (i = 0; i < 10; i++) {
-               if (tg3_ape_lock(tp, TG3_APE_LOCK_MEM))
-                       return;
+       bufoff = tg3_ape_read32(tp, TG3_APE_SEG_MSG_BUF_OFF) +
+                TG3_APE_SHMEM_BASE;
+       msgoff = bufoff + 2 * sizeof(u32);
+       maxlen = tg3_ape_read32(tp, TG3_APE_SEG_MSG_BUF_LEN);
 
-               apedata = tg3_ape_read32(tp, TG3_APE_EVENT_STATUS);
+       while (len) {
+               u32 length;
 
-               if (!(apedata & APE_EVENT_STATUS_EVENT_PENDING))
-                       tg3_ape_write32(tp, TG3_APE_EVENT_STATUS,
-                                       event | APE_EVENT_STATUS_EVENT_PENDING);
+               /* Cap xfer sizes to scratchpad limits. */
+               length = (len > maxlen) ? maxlen : len;
+               len -= length;
+
+               apedata = tg3_ape_read32(tp, TG3_APE_FW_STATUS);
+               if (!(apedata & APE_FW_STATUS_READY))
+                       return -EAGAIN;
+
+               /* Wait for up to 1 msec for APE to service previous event. */
+               err = tg3_ape_event_lock(tp, 1000);
+               if (err)
+                       return err;
+
+               apedata = APE_EVENT_STATUS_DRIVER_EVNT |
+                         APE_EVENT_STATUS_SCRTCHPD_READ |
+                         APE_EVENT_STATUS_EVENT_PENDING;
+               tg3_ape_write32(tp, TG3_APE_EVENT_STATUS, apedata);
+
+               tg3_ape_write32(tp, bufoff, base_off);
+               tg3_ape_write32(tp, bufoff + sizeof(u32), length);
 
                tg3_ape_unlock(tp, TG3_APE_LOCK_MEM);
+               tg3_ape_write32(tp, TG3_APE_EVENT, APE_EVENT_1);
 
-               if (!(apedata & APE_EVENT_STATUS_EVENT_PENDING))
-                       break;
+               base_off += length;
 
-               udelay(100);
+               if (tg3_ape_wait_for_event(tp, 30000))
+                       return -EAGAIN;
+
+               for (i = 0; length; i += 4, length -= 4) {
+                       u32 val = tg3_ape_read32(tp, msgoff + i);
+                       memcpy(data, &val, sizeof(u32));
+                       data++;
+               }
        }
 
-       if (!(apedata & APE_EVENT_STATUS_EVENT_PENDING))
-               tg3_ape_write32(tp, TG3_APE_EVENT, APE_EVENT_1);
+       return 0;
+}
+
+static int tg3_ape_send_event(struct tg3 *tp, u32 event)
+{
+       int err;
+       u32 apedata;
+
+       apedata = tg3_ape_read32(tp, TG3_APE_SEG_SIG);
+       if (apedata != APE_SEG_SIG_MAGIC)
+               return -EAGAIN;
+
+       apedata = tg3_ape_read32(tp, TG3_APE_FW_STATUS);
+       if (!(apedata & APE_FW_STATUS_READY))
+               return -EAGAIN;
+
+       /* Wait for up to 1 millisecond for APE to service previous event. */
+       err = tg3_ape_event_lock(tp, 1000);
+       if (err)
+               return err;
+
+       tg3_ape_write32(tp, TG3_APE_EVENT_STATUS,
+                       event | APE_EVENT_STATUS_EVENT_PENDING);
+
+       tg3_ape_unlock(tp, TG3_APE_LOCK_MEM);
+       tg3_ape_write32(tp, TG3_APE_EVENT, APE_EVENT_1);
+
+       return 0;
 }
 
 static void tg3_ape_driver_state_change(struct tg3 *tp, int kind)
@@ -9393,6 +9485,110 @@ static int tg3_init_hw(struct tg3 *tp, int reset_phy)
        return tg3_reset_hw(tp, reset_phy);
 }
 
+#if IS_ENABLED(CONFIG_HWMON)
+static void tg3_sd_scan_scratchpad(struct tg3 *tp, struct tg3_ocir *ocir)
+{
+       int i;
+
+       for (i = 0; i < TG3_SD_NUM_RECS; i++, ocir++) {
+               u32 off = i * TG3_OCIR_LEN, len = TG3_OCIR_LEN;
+
+               tg3_ape_scratchpad_read(tp, (u32 *) ocir, off, len);
+               off += len;
+
+               if (ocir->signature != TG3_OCIR_SIG_MAGIC ||
+                   !(ocir->version_flags & TG3_OCIR_FLAG_ACTIVE))
+                       memset(ocir, 0, TG3_OCIR_LEN);
+       }
+}
+
+/* sysfs attributes for hwmon */
+static ssize_t tg3_show_temp(struct device *dev,
+                            struct device_attribute *devattr, char *buf)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+       struct net_device *netdev = pci_get_drvdata(pdev);
+       struct tg3 *tp = netdev_priv(netdev);
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       u32 temperature;
+
+       spin_lock_bh(&tp->lock);
+       tg3_ape_scratchpad_read(tp, &temperature, attr->index,
+                               sizeof(temperature));
+       spin_unlock_bh(&tp->lock);
+       return sprintf(buf, "%u\n", temperature);
+}
+
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, tg3_show_temp, NULL,
+                         TG3_TEMP_SENSOR_OFFSET);
+static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, tg3_show_temp, NULL,
+                         TG3_TEMP_CAUTION_OFFSET);
+static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, tg3_show_temp, NULL,
+                         TG3_TEMP_MAX_OFFSET);
+
+static struct attribute *tg3_attributes[] = {
+       &sensor_dev_attr_temp1_input.dev_attr.attr,
+       &sensor_dev_attr_temp1_crit.dev_attr.attr,
+       &sensor_dev_attr_temp1_max.dev_attr.attr,
+       NULL
+};
+
+static const struct attribute_group tg3_group = {
+       .attrs = tg3_attributes,
+};
+
+#endif
+
+static void tg3_hwmon_close(struct tg3 *tp)
+{
+#if IS_ENABLED(CONFIG_HWMON)
+       if (tp->hwmon_dev) {
+               hwmon_device_unregister(tp->hwmon_dev);
+               tp->hwmon_dev = NULL;
+               sysfs_remove_group(&tp->pdev->dev.kobj, &tg3_group);
+       }
+#endif
+}
+
+static void tg3_hwmon_open(struct tg3 *tp)
+{
+#if IS_ENABLED(CONFIG_HWMON)
+       int i, err;
+       u32 size = 0;
+       struct pci_dev *pdev = tp->pdev;
+       struct tg3_ocir ocirs[TG3_SD_NUM_RECS];
+
+       tg3_sd_scan_scratchpad(tp, ocirs);
+
+       for (i = 0; i < TG3_SD_NUM_RECS; i++) {
+               if (!ocirs[i].src_data_length)
+                       continue;
+
+               size += ocirs[i].src_hdr_length;
+               size += ocirs[i].src_data_length;
+       }
+
+       if (!size)
+               return;
+
+       /* Register hwmon sysfs hooks */
+       err = sysfs_create_group(&pdev->dev.kobj, &tg3_group);
+       if (err) {
+               dev_err(&pdev->dev, "Cannot create sysfs group, aborting\n");
+               return;
+       }
+
+       tp->hwmon_dev = hwmon_device_register(&pdev->dev);
+       if (IS_ERR(tp->hwmon_dev)) {
+               tp->hwmon_dev = NULL;
+               dev_err(&pdev->dev, "Cannot register hwmon device, aborting\n");
+               sysfs_remove_group(&pdev->dev.kobj, &tg3_group);
+       }
+#endif
+}
+
+
 #define TG3_STAT_ADD32(PSTAT, REG) \
 do {   u32 __val = tr32(REG); \
        (PSTAT)->low += __val; \
@@ -9908,7 +10104,7 @@ static bool tg3_enable_msix(struct tg3 *tp)
        int i, rc;
        struct msix_entry msix_ent[tp->irq_max];
 
-       tp->irq_cnt = num_online_cpus();
+       tp->irq_cnt = netif_get_num_default_rss_queues();
        if (tp->irq_cnt > 1) {
                /* We want as many rx rings enabled as there are cpus.
                 * In multiqueue MSI-X mode, the first MSI-X vector
@@ -10101,6 +10297,8 @@ static int tg3_open(struct net_device *dev)
 
        tg3_phy_start(tp);
 
+       tg3_hwmon_open(tp);
+
        tg3_full_lock(tp, 0);
 
        tg3_timer_start(tp);
@@ -10150,6 +10348,8 @@ static int tg3_close(struct net_device *dev)
 
        tg3_timer_stop(tp);
 
+       tg3_hwmon_close(tp);
+
        tg3_phy_stop(tp);
 
        tg3_full_lock(tp, 1);
@@ -13857,14 +14057,9 @@ static void __devinit tg3_read_mgmtfw_ver(struct tg3 *tp)
        }
 }
 
-static void __devinit tg3_read_dash_ver(struct tg3 *tp)
+static void __devinit tg3_probe_ncsi(struct tg3 *tp)
 {
-       int vlen;
        u32 apedata;
-       char *fwtype;
-
-       if (!tg3_flag(tp, ENABLE_APE) || !tg3_flag(tp, ENABLE_ASF))
-               return;
 
        apedata = tg3_ape_read32(tp, TG3_APE_SEG_SIG);
        if (apedata != APE_SEG_SIG_MAGIC)
@@ -13874,14 +14069,22 @@ static void __devinit tg3_read_dash_ver(struct tg3 *tp)
        if (!(apedata & APE_FW_STATUS_READY))
                return;
 
+       if (tg3_ape_read32(tp, TG3_APE_FW_FEATURES) & TG3_APE_FW_FEATURE_NCSI)
+               tg3_flag_set(tp, APE_HAS_NCSI);
+}
+
+static void __devinit tg3_read_dash_ver(struct tg3 *tp)
+{
+       int vlen;
+       u32 apedata;
+       char *fwtype;
+
        apedata = tg3_ape_read32(tp, TG3_APE_FW_VERSION);
 
-       if (tg3_ape_read32(tp, TG3_APE_FW_FEATURES) & TG3_APE_FW_FEATURE_NCSI) {
-               tg3_flag_set(tp, APE_HAS_NCSI);
+       if (tg3_flag(tp, APE_HAS_NCSI))
                fwtype = "NCSI";
-       } else {
+       else
                fwtype = "DASH";
-       }
 
        vlen = strlen(tp->fw_ver);
 
@@ -13915,20 +14118,17 @@ static void __devinit tg3_read_fw_ver(struct tg3 *tp)
                tg3_read_sb_ver(tp, val);
        else if ((val & TG3_EEPROM_MAGIC_HW_MSK) == TG3_EEPROM_MAGIC_HW)
                tg3_read_hwsb_ver(tp);
-       else
-               return;
-
-       if (vpd_vers)
-               goto done;
 
-       if (tg3_flag(tp, ENABLE_APE)) {
-               if (tg3_flag(tp, ENABLE_ASF))
-                       tg3_read_dash_ver(tp);
-       } else if (tg3_flag(tp, ENABLE_ASF)) {
-               tg3_read_mgmtfw_ver(tp);
+       if (tg3_flag(tp, ENABLE_ASF)) {
+               if (tg3_flag(tp, ENABLE_APE)) {
+                       tg3_probe_ncsi(tp);
+                       if (!vpd_vers)
+                               tg3_read_dash_ver(tp);
+               } else if (!vpd_vers) {
+                       tg3_read_mgmtfw_ver(tp);
+               }
        }
 
-done:
        tp->fw_ver[TG3_VER_SIZE - 1] = 0;
 }
 
@@ -14275,7 +14475,8 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
                }
        }
 
-       if (tg3_flag(tp, 5755_PLUS))
+       if (tg3_flag(tp, 5755_PLUS) ||
+           GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906)
                tg3_flag_set(tp, SHORT_DMA_BUG);
 
        if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5719)