Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit aed93e0b authored by Michael Chan's avatar Michael Chan Committed by David S. Miller
Browse files

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: default avatarMatt Carlson <mcarlson@broadcom.com>
Signed-off-by: default avatarNithin Nayak Sujir <nsujir@broadcom.com>
Signed-off-by: default avatarMichael Chan <mchan@broadcom.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent cf8d55ae
Loading
Loading
Loading
Loading
+112 −0
Original line number Diff line number Diff line
@@ -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>
@@ -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; \
@@ -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);
@@ -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);
+38 −0
Original line number Diff line number Diff line
@@ -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.
@@ -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) */