Skip to content

Commit

Permalink
tg3: Add hwmon support for temperature
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Michael Chan authored and David S. Miller committed Jul 17, 2012
1 parent cf8d55a commit aed93e0
Show file tree
Hide file tree
Showing 2 changed files with 150 additions and 0 deletions.
112 changes: 112 additions & 0 deletions drivers/net/ethernet/broadcom/tg3.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@
#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>
Expand Down Expand Up @@ -9481,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; \
Expand Down Expand Up @@ -10189,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);
Expand Down Expand Up @@ -10238,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);
Expand Down
38 changes: 38 additions & 0 deletions drivers/net/ethernet/broadcom/tg3.h
Original file line number Diff line number Diff line change
Expand Up @@ -2676,6 +2676,40 @@ struct tg3_hw_stats {
u8 __reserved4[0xb00-0x9c8];
};

#define TG3_SD_NUM_RECS 3
#define TG3_OCIR_LEN (sizeof(struct tg3_ocir))
#define TG3_OCIR_SIG_MAGIC 0x5253434f
#define TG3_OCIR_FLAG_ACTIVE 0x00000001

#define TG3_TEMP_CAUTION_OFFSET 0xc8
#define TG3_TEMP_MAX_OFFSET 0xcc
#define TG3_TEMP_SENSOR_OFFSET 0xd4


struct tg3_ocir {
u32 signature;
u16 version_flags;
u16 refresh_int;
u32 refresh_tmr;
u32 update_tmr;
u32 dst_base_addr;
u16 src_hdr_offset;
u16 src_hdr_length;
u16 src_data_offset;
u16 src_data_length;
u16 dst_hdr_offset;
u16 dst_data_offset;
u16 dst_reg_upd_offset;
u16 dst_sem_offset;
u32 reserved1[2];
u32 port0_flags;
u32 port1_flags;
u32 port2_flags;
u32 port3_flags;
u32 reserved2[1];
};


/* 'mapping' is superfluous as the chip does not write into
* the tx/rx post rings so we could just fetch it from there.
* But the cache behavior is better how we are doing it now.
Expand Down Expand Up @@ -3211,6 +3245,10 @@ struct tg3 {
const char *fw_needed;
const struct firmware *fw;
u32 fw_len; /* includes BSS */

#if IS_ENABLED(CONFIG_HWMON)
struct device *hwmon_dev;
#endif
};

#endif /* !(_T3_H) */

0 comments on commit aed93e0

Please sign in to comment.