diff options
author | Michael Chan <mchan@broadcom.com> | 2012-07-16 12:24:02 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-07-17 02:10:30 -0400 |
commit | aed93e0bf493535c25c27270001226bb1dd379b2 (patch) | |
tree | ac90051c0b2f86ad78b67a6c33e8620f2c845d30 /drivers/net/ethernet/broadcom | |
parent | cf8d55ae08459d6412779559fb1e88252db86c9d (diff) |
tg3: Add hwmon support for temperature
Some tg3 devices have management firmware that can export sensor data.
Export temperature sensor reading via hwmon sysfs.
[hwmon interface suggested by Ben Hutchings <bhutchings@solarflare.com>]
Signed-off-by: Matt Carlson <mcarlson@broadcom.com>
Signed-off-by: Nithin Nayak Sujir <nsujir@broadcom.com>
Signed-off-by: Michael Chan <mchan@broadcom.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ethernet/broadcom')
-rw-r--r-- | drivers/net/ethernet/broadcom/tg3.c | 112 | ||||
-rw-r--r-- | drivers/net/ethernet/broadcom/tg3.h | 38 |
2 files changed, 150 insertions, 0 deletions
diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 26ca36810e72..fce4c1e4dd3f 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c | |||
@@ -44,6 +44,10 @@ | |||
44 | #include <linux/prefetch.h> | 44 | #include <linux/prefetch.h> |
45 | #include <linux/dma-mapping.h> | 45 | #include <linux/dma-mapping.h> |
46 | #include <linux/firmware.h> | 46 | #include <linux/firmware.h> |
47 | #if IS_ENABLED(CONFIG_HWMON) | ||
48 | #include <linux/hwmon.h> | ||
49 | #include <linux/hwmon-sysfs.h> | ||
50 | #endif | ||
47 | 51 | ||
48 | #include <net/checksum.h> | 52 | #include <net/checksum.h> |
49 | #include <net/ip.h> | 53 | #include <net/ip.h> |
@@ -9481,6 +9485,110 @@ static int tg3_init_hw(struct tg3 *tp, int reset_phy) | |||
9481 | return tg3_reset_hw(tp, reset_phy); | 9485 | return tg3_reset_hw(tp, reset_phy); |
9482 | } | 9486 | } |
9483 | 9487 | ||
9488 | #if IS_ENABLED(CONFIG_HWMON) | ||
9489 | static void tg3_sd_scan_scratchpad(struct tg3 *tp, struct tg3_ocir *ocir) | ||
9490 | { | ||
9491 | int i; | ||
9492 | |||
9493 | for (i = 0; i < TG3_SD_NUM_RECS; i++, ocir++) { | ||
9494 | u32 off = i * TG3_OCIR_LEN, len = TG3_OCIR_LEN; | ||
9495 | |||
9496 | tg3_ape_scratchpad_read(tp, (u32 *) ocir, off, len); | ||
9497 | off += len; | ||
9498 | |||
9499 | if (ocir->signature != TG3_OCIR_SIG_MAGIC || | ||
9500 | !(ocir->version_flags & TG3_OCIR_FLAG_ACTIVE)) | ||
9501 | memset(ocir, 0, TG3_OCIR_LEN); | ||
9502 | } | ||
9503 | } | ||
9504 | |||
9505 | /* sysfs attributes for hwmon */ | ||
9506 | static ssize_t tg3_show_temp(struct device *dev, | ||
9507 | struct device_attribute *devattr, char *buf) | ||
9508 | { | ||
9509 | struct pci_dev *pdev = to_pci_dev(dev); | ||
9510 | struct net_device *netdev = pci_get_drvdata(pdev); | ||
9511 | struct tg3 *tp = netdev_priv(netdev); | ||
9512 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||
9513 | u32 temperature; | ||
9514 | |||
9515 | spin_lock_bh(&tp->lock); | ||
9516 | tg3_ape_scratchpad_read(tp, &temperature, attr->index, | ||
9517 | sizeof(temperature)); | ||
9518 | spin_unlock_bh(&tp->lock); | ||
9519 | return sprintf(buf, "%u\n", temperature); | ||
9520 | } | ||
9521 | |||
9522 | |||
9523 | static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, tg3_show_temp, NULL, | ||
9524 | TG3_TEMP_SENSOR_OFFSET); | ||
9525 | static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, tg3_show_temp, NULL, | ||
9526 | TG3_TEMP_CAUTION_OFFSET); | ||
9527 | static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, tg3_show_temp, NULL, | ||
9528 | TG3_TEMP_MAX_OFFSET); | ||
9529 | |||
9530 | static struct attribute *tg3_attributes[] = { | ||
9531 | &sensor_dev_attr_temp1_input.dev_attr.attr, | ||
9532 | &sensor_dev_attr_temp1_crit.dev_attr.attr, | ||
9533 | &sensor_dev_attr_temp1_max.dev_attr.attr, | ||
9534 | NULL | ||
9535 | }; | ||
9536 | |||
9537 | static const struct attribute_group tg3_group = { | ||
9538 | .attrs = tg3_attributes, | ||
9539 | }; | ||
9540 | |||
9541 | #endif | ||
9542 | |||
9543 | static void tg3_hwmon_close(struct tg3 *tp) | ||
9544 | { | ||
9545 | #if IS_ENABLED(CONFIG_HWMON) | ||
9546 | if (tp->hwmon_dev) { | ||
9547 | hwmon_device_unregister(tp->hwmon_dev); | ||
9548 | tp->hwmon_dev = NULL; | ||
9549 | sysfs_remove_group(&tp->pdev->dev.kobj, &tg3_group); | ||
9550 | } | ||
9551 | #endif | ||
9552 | } | ||
9553 | |||
9554 | static void tg3_hwmon_open(struct tg3 *tp) | ||
9555 | { | ||
9556 | #if IS_ENABLED(CONFIG_HWMON) | ||
9557 | int i, err; | ||
9558 | u32 size = 0; | ||
9559 | struct pci_dev *pdev = tp->pdev; | ||
9560 | struct tg3_ocir ocirs[TG3_SD_NUM_RECS]; | ||
9561 | |||
9562 | tg3_sd_scan_scratchpad(tp, ocirs); | ||
9563 | |||
9564 | for (i = 0; i < TG3_SD_NUM_RECS; i++) { | ||
9565 | if (!ocirs[i].src_data_length) | ||
9566 | continue; | ||
9567 | |||
9568 | size += ocirs[i].src_hdr_length; | ||
9569 | size += ocirs[i].src_data_length; | ||
9570 | } | ||
9571 | |||
9572 | if (!size) | ||
9573 | return; | ||
9574 | |||
9575 | /* Register hwmon sysfs hooks */ | ||
9576 | err = sysfs_create_group(&pdev->dev.kobj, &tg3_group); | ||
9577 | if (err) { | ||
9578 | dev_err(&pdev->dev, "Cannot create sysfs group, aborting\n"); | ||
9579 | return; | ||
9580 | } | ||
9581 | |||
9582 | tp->hwmon_dev = hwmon_device_register(&pdev->dev); | ||
9583 | if (IS_ERR(tp->hwmon_dev)) { | ||
9584 | tp->hwmon_dev = NULL; | ||
9585 | dev_err(&pdev->dev, "Cannot register hwmon device, aborting\n"); | ||
9586 | sysfs_remove_group(&pdev->dev.kobj, &tg3_group); | ||
9587 | } | ||
9588 | #endif | ||
9589 | } | ||
9590 | |||
9591 | |||
9484 | #define TG3_STAT_ADD32(PSTAT, REG) \ | 9592 | #define TG3_STAT_ADD32(PSTAT, REG) \ |
9485 | do { u32 __val = tr32(REG); \ | 9593 | do { u32 __val = tr32(REG); \ |
9486 | (PSTAT)->low += __val; \ | 9594 | (PSTAT)->low += __val; \ |
@@ -10189,6 +10297,8 @@ static int tg3_open(struct net_device *dev) | |||
10189 | 10297 | ||
10190 | tg3_phy_start(tp); | 10298 | tg3_phy_start(tp); |
10191 | 10299 | ||
10300 | tg3_hwmon_open(tp); | ||
10301 | |||
10192 | tg3_full_lock(tp, 0); | 10302 | tg3_full_lock(tp, 0); |
10193 | 10303 | ||
10194 | tg3_timer_start(tp); | 10304 | tg3_timer_start(tp); |
@@ -10238,6 +10348,8 @@ static int tg3_close(struct net_device *dev) | |||
10238 | 10348 | ||
10239 | tg3_timer_stop(tp); | 10349 | tg3_timer_stop(tp); |
10240 | 10350 | ||
10351 | tg3_hwmon_close(tp); | ||
10352 | |||
10241 | tg3_phy_stop(tp); | 10353 | tg3_phy_stop(tp); |
10242 | 10354 | ||
10243 | tg3_full_lock(tp, 1); | 10355 | tg3_full_lock(tp, 1); |
diff --git a/drivers/net/ethernet/broadcom/tg3.h b/drivers/net/ethernet/broadcom/tg3.h index f8a0d9c0e990..a1b75cd67b9d 100644 --- a/drivers/net/ethernet/broadcom/tg3.h +++ b/drivers/net/ethernet/broadcom/tg3.h | |||
@@ -2676,6 +2676,40 @@ struct tg3_hw_stats { | |||
2676 | u8 __reserved4[0xb00-0x9c8]; | 2676 | u8 __reserved4[0xb00-0x9c8]; |
2677 | }; | 2677 | }; |
2678 | 2678 | ||
2679 | #define TG3_SD_NUM_RECS 3 | ||
2680 | #define TG3_OCIR_LEN (sizeof(struct tg3_ocir)) | ||
2681 | #define TG3_OCIR_SIG_MAGIC 0x5253434f | ||
2682 | #define TG3_OCIR_FLAG_ACTIVE 0x00000001 | ||
2683 | |||
2684 | #define TG3_TEMP_CAUTION_OFFSET 0xc8 | ||
2685 | #define TG3_TEMP_MAX_OFFSET 0xcc | ||
2686 | #define TG3_TEMP_SENSOR_OFFSET 0xd4 | ||
2687 | |||
2688 | |||
2689 | struct tg3_ocir { | ||
2690 | u32 signature; | ||
2691 | u16 version_flags; | ||
2692 | u16 refresh_int; | ||
2693 | u32 refresh_tmr; | ||
2694 | u32 update_tmr; | ||
2695 | u32 dst_base_addr; | ||
2696 | u16 src_hdr_offset; | ||
2697 | u16 src_hdr_length; | ||
2698 | u16 src_data_offset; | ||
2699 | u16 src_data_length; | ||
2700 | u16 dst_hdr_offset; | ||
2701 | u16 dst_data_offset; | ||
2702 | u16 dst_reg_upd_offset; | ||
2703 | u16 dst_sem_offset; | ||
2704 | u32 reserved1[2]; | ||
2705 | u32 port0_flags; | ||
2706 | u32 port1_flags; | ||
2707 | u32 port2_flags; | ||
2708 | u32 port3_flags; | ||
2709 | u32 reserved2[1]; | ||
2710 | }; | ||
2711 | |||
2712 | |||
2679 | /* 'mapping' is superfluous as the chip does not write into | 2713 | /* 'mapping' is superfluous as the chip does not write into |
2680 | * the tx/rx post rings so we could just fetch it from there. | 2714 | * the tx/rx post rings so we could just fetch it from there. |
2681 | * But the cache behavior is better how we are doing it now. | 2715 | * But the cache behavior is better how we are doing it now. |
@@ -3211,6 +3245,10 @@ struct tg3 { | |||
3211 | const char *fw_needed; | 3245 | const char *fw_needed; |
3212 | const struct firmware *fw; | 3246 | const struct firmware *fw; |
3213 | u32 fw_len; /* includes BSS */ | 3247 | u32 fw_len; /* includes BSS */ |
3248 | |||
3249 | #if IS_ENABLED(CONFIG_HWMON) | ||
3250 | struct device *hwmon_dev; | ||
3251 | #endif | ||
3214 | }; | 3252 | }; |
3215 | 3253 | ||
3216 | #endif /* !(_T3_H) */ | 3254 | #endif /* !(_T3_H) */ |