summaryrefslogtreecommitdiff
path: root/drivers/hwmon
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2016-10-04 20:56:14 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2016-10-04 20:56:14 +0300
commit77b0a4aa0732f1856aef85b8db085864e5971a14 (patch)
tree3b55b24d79afbc1fb358bc63e03469ec770d2150 /drivers/hwmon
parentf80fa1822d6ccca369578108dc70576cff6c67a0 (diff)
parent7ce4190c4ca466ccd609845b97dce665317aee2a (diff)
downloadlinux-77b0a4aa0732f1856aef85b8db085864e5971a14.tar.xz
Merge tag 'hwmon-for-linus-v4.9' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging
Pull hwmon updates from Guenter Roeck: - New hwmon registration API, including ports of several drivers to the new API - New hwmon driver for APM X-Gene SoC - Added support for UCD90160, DPS-460, DPS-800, and SGD009 PMBUs chips - Various cleanups, minor improvements, and fixes in several drivers * tag 'hwmon-for-linus-v4.9' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (54 commits) hwmon: (nct6775) Add support for multiple virtual temperature sources hwmon: (adt7470) No need for additional synchronization on kthread_stop() hwmon: (lm95241) Update module description to include LM95231 hwmon: (lm95245) Select REGMAP_I2C hwmon: (ibmpowernv) Fix label for cores numbers not threads hwmon: (adt7470) Allow faster removal hwmon: (adt7470) Add write support to alarm_mask hwmon: (xgene) access mailbox as RAM hwmon: (lm95245) Use new hwmon registration API hwmon: (lm95241) Convert to use new hwmon registration API hwmon: (jc42) Convert to use new hwmon registration API hwmon: (max31790) Convert to use new hwmon registration API hwmon: (nct7904) Convert to use new hwmon registration API hwmon: (ltc4245) Convert to use new hwmon registration API hwmon: (tmp421) Convert to use new hwmon registration API hwmon: (tmp102) Convert to use new hwmon registration API hwmon: (lm90) Convert to use new hwmon registration API hwmon: (lm75) Convert to use new hwmon registration API hwmon: (xgene) Fix crash when alarm occurs before driver probe hwmon: (iio_hwmon) defer probe when no channel is found ...
Diffstat (limited to 'drivers/hwmon')
-rw-r--r--drivers/hwmon/Kconfig10
-rw-r--r--drivers/hwmon/Makefile1
-rw-r--r--drivers/hwmon/adt7411.c51
-rw-r--r--drivers/hwmon/adt7470.c108
-rw-r--r--drivers/hwmon/ftsteutates.c45
-rw-r--r--drivers/hwmon/hwmon.c616
-rw-r--r--drivers/hwmon/ibmpowernv.c8
-rw-r--r--drivers/hwmon/iio_hwmon.c5
-rw-r--r--drivers/hwmon/it87.c8
-rw-r--r--drivers/hwmon/jc42.c293
-rw-r--r--drivers/hwmon/lm75.c185
-rw-r--r--drivers/hwmon/lm90.c811
-rw-r--r--drivers/hwmon/lm95241.c469
-rw-r--r--drivers/hwmon/lm95245.c638
-rw-r--r--drivers/hwmon/ltc4151.c22
-rw-r--r--drivers/hwmon/ltc4245.c362
-rw-r--r--drivers/hwmon/max31790.c521
-rw-r--r--drivers/hwmon/max6650.c155
-rw-r--r--drivers/hwmon/nct6775.c23
-rw-r--r--drivers/hwmon/nct7904.c555
-rw-r--r--drivers/hwmon/ntc_thermistor.c107
-rw-r--r--drivers/hwmon/pmbus/Kconfig6
-rw-r--r--drivers/hwmon/pmbus/pmbus.c20
-rw-r--r--drivers/hwmon/pmbus/ucd9000.c3
-rw-r--r--drivers/hwmon/scpi-hwmon.c1
-rw-r--r--drivers/hwmon/tmp102.c154
-rw-r--r--drivers/hwmon/tmp421.c133
-rw-r--r--drivers/hwmon/xgene-hwmon.c787
28 files changed, 3945 insertions, 2152 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index eaf2f916d48c..45cef3d2c75c 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -969,7 +969,6 @@ config SENSORS_LM73
config SENSORS_LM75
tristate "National Semiconductor LM75 and compatibles"
depends on I2C
- depends on THERMAL || !THERMAL_OF
select REGMAP_I2C
help
If you say yes here you get support for one common type of
@@ -1119,6 +1118,7 @@ config SENSORS_LM95241
config SENSORS_LM95245
tristate "National Semiconductor LM95245 and compatibles"
depends on I2C
+ select REGMAP_I2C
help
If you say yes here you get support for LM95235 and LM95245
temperature sensor chips.
@@ -1572,7 +1572,6 @@ config SENSORS_THMC50
config SENSORS_TMP102
tristate "Texas Instruments TMP102"
depends on I2C
- depends on THERMAL || !THERMAL_OF
select REGMAP_I2C
help
If you say yes here you get support for Texas Instruments TMP102
@@ -1823,6 +1822,13 @@ config SENSORS_ULTRA45
This driver provides support for the Ultra45 workstation environmental
sensors.
+config SENSORS_XGENE
+ tristate "APM X-Gene SoC hardware monitoring driver"
+ depends on XGENE_SLIMPRO_MBOX || PCC
+ help
+ If you say yes here you get support for the temperature
+ and power sensors for APM X-Gene SoC.
+
if ACPI
comment "ACPI drivers"
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index fe87d2895a97..aecf4ba17460 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -165,6 +165,7 @@ obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o
obj-$(CONFIG_SENSORS_W83L786NG) += w83l786ng.o
obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o
obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o
+obj-$(CONFIG_SENSORS_XGENE) += xgene-hwmon.o
obj-$(CONFIG_PMBUS) += pmbus/
diff --git a/drivers/hwmon/adt7411.c b/drivers/hwmon/adt7411.c
index fc1e65a263a4..812fbc00f693 100644
--- a/drivers/hwmon/adt7411.c
+++ b/drivers/hwmon/adt7411.c
@@ -7,8 +7,7 @@
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
- * TODO: SPI, support for external temperature sensor
- * use power-down mode for suspend?, interrupt handling?
+ * TODO: SPI, use power-down mode for suspend?, interrupt handling?
*/
#include <linux/kernel.h>
@@ -31,6 +30,7 @@
#define ADT7411_REG_CFG1 0x18
#define ADT7411_CFG1_START_MONITOR (1 << 0)
#define ADT7411_CFG1_RESERVED_BIT1 (1 << 1)
+#define ADT7411_CFG1_EXT_TDM (1 << 2)
#define ADT7411_CFG1_RESERVED_BIT3 (1 << 3)
#define ADT7411_REG_CFG2 0x19
@@ -57,6 +57,7 @@ struct adt7411_data {
unsigned long next_update;
int vref_cached;
struct i2c_client *client;
+ bool use_ext_temp;
};
/*
@@ -127,11 +128,20 @@ static ssize_t adt7411_show_vdd(struct device *dev,
static ssize_t adt7411_show_temp(struct device *dev,
struct device_attribute *attr, char *buf)
{
+ int nr = to_sensor_dev_attr(attr)->index;
struct adt7411_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
- int val = adt7411_read_10_bit(client, ADT7411_REG_INT_TEMP_VDD_LSB,
- ADT7411_REG_INT_TEMP_MSB, 0);
-
+ int val;
+ struct {
+ u8 low;
+ u8 high;
+ } reg[2] = {
+ { ADT7411_REG_INT_TEMP_VDD_LSB, ADT7411_REG_INT_TEMP_MSB },
+ { ADT7411_REG_EXT_TEMP_AIN14_LSB,
+ ADT7411_REG_EXT_TEMP_AIN1_MSB },
+ };
+
+ val = adt7411_read_10_bit(client, reg[nr].low, reg[nr].high, 0);
if (val < 0)
return val;
@@ -218,11 +228,13 @@ static ssize_t adt7411_set_bit(struct device *dev,
return ret < 0 ? ret : count;
}
+
#define ADT7411_BIT_ATTR(__name, __reg, __bit) \
SENSOR_DEVICE_ATTR_2(__name, S_IRUGO | S_IWUSR, adt7411_show_bit, \
adt7411_set_bit, __bit, __reg)
-static DEVICE_ATTR(temp1_input, S_IRUGO, adt7411_show_temp, NULL);
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, adt7411_show_temp, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, adt7411_show_temp, NULL, 1);
static DEVICE_ATTR(in0_input, S_IRUGO, adt7411_show_vdd, NULL);
static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, adt7411_show_input, NULL, 0);
static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, adt7411_show_input, NULL, 1);
@@ -237,7 +249,8 @@ static ADT7411_BIT_ATTR(fast_sampling, ADT7411_REG_CFG3, ADT7411_CFG3_ADC_CLK_22
static ADT7411_BIT_ATTR(adc_ref_vdd, ADT7411_REG_CFG3, ADT7411_CFG3_REF_VDD);
static struct attribute *adt7411_attrs[] = {
- &dev_attr_temp1_input.attr,
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
&dev_attr_in0_input.attr,
&sensor_dev_attr_in1_input.dev_attr.attr,
&sensor_dev_attr_in2_input.dev_attr.attr,
@@ -253,7 +266,27 @@ static struct attribute *adt7411_attrs[] = {
NULL
};
-ATTRIBUTE_GROUPS(adt7411);
+static umode_t adt7411_attrs_visible(struct kobject *kobj,
+ struct attribute *attr, int index)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct adt7411_data *data = dev_get_drvdata(dev);
+ bool visible = true;
+
+ if (attr == &sensor_dev_attr_temp2_input.dev_attr.attr)
+ visible = data->use_ext_temp;
+ else if (attr == &sensor_dev_attr_in1_input.dev_attr.attr ||
+ attr == &sensor_dev_attr_in2_input.dev_attr.attr)
+ visible = !data->use_ext_temp;
+
+ return visible ? attr->mode : 0;
+}
+
+static const struct attribute_group adt7411_group = {
+ .attrs = adt7411_attrs,
+ .is_visible = adt7411_attrs_visible,
+};
+__ATTRIBUTE_GROUPS(adt7411);
static int adt7411_detect(struct i2c_client *client,
struct i2c_board_info *info)
@@ -309,6 +342,8 @@ static int adt7411_init_device(struct adt7411_data *data)
if (ret < 0)
return ret;
+ data->use_ext_temp = ret & ADT7411_CFG1_EXT_TDM;
+
/*
* We must only write zero to bit 1 and only one to bit 3 according to
* the datasheet.
diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
index f5da39a68929..6e60ca53406e 100644
--- a/drivers/hwmon/adt7470.c
+++ b/drivers/hwmon/adt7470.c
@@ -32,6 +32,7 @@
#include <linux/log2.h>
#include <linux/kthread.h>
#include <linux/slab.h>
+#include <linux/util_macros.h>
/* Addresses to scan */
static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
@@ -83,6 +84,7 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
#define ADT7470_REG_PWM_MIN_MAX_ADDR 0x6D
#define ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR 0x6E
#define ADT7470_REG_PWM_TEMP_MIN_MAX_ADDR 0x71
+#define ADT7470_REG_CFG_2 0x74
#define ADT7470_REG_ACOUSTICS12 0x75
#define ADT7470_REG_ACOUSTICS34 0x76
#define ADT7470_REG_DEVICE 0x3D
@@ -142,6 +144,11 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END };
#define FAN_PERIOD_INVALID 65535
#define FAN_DATA_VALID(x) ((x) && (x) != FAN_PERIOD_INVALID)
+/* Config registers 1 and 2 include fields for selecting the PWM frequency */
+#define ADT7470_CFG_LF 0x40
+#define ADT7470_FREQ_MASK 0x70
+#define ADT7470_FREQ_SHIFT 4
+
struct adt7470_data {
struct i2c_client *client;
struct mutex lock;
@@ -170,7 +177,6 @@ struct adt7470_data {
u8 pwm_auto_temp[ADT7470_PWM_COUNT];
struct task_struct *auto_update;
- struct completion auto_update_stop;
unsigned int auto_update_interval;
};
@@ -266,12 +272,14 @@ static int adt7470_update_thread(void *p)
mutex_lock(&data->lock);
adt7470_read_temperatures(client, data);
mutex_unlock(&data->lock);
+
+ set_current_state(TASK_INTERRUPTIBLE);
if (kthread_should_stop())
break;
- msleep_interruptible(data->auto_update_interval);
+
+ schedule_timeout(msecs_to_jiffies(data->auto_update_interval));
}
- complete_all(&data->auto_update_stop);
return 0;
}
@@ -538,6 +546,28 @@ static ssize_t show_alarm_mask(struct device *dev,
return sprintf(buf, "%x\n", data->alarms_mask);
}
+static ssize_t set_alarm_mask(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf,
+ size_t count)
+{
+ struct adt7470_data *data = dev_get_drvdata(dev);
+ long mask;
+
+ if (kstrtoul(buf, 0, &mask))
+ return -EINVAL;
+
+ if (mask & ~0xffff)
+ return -EINVAL;
+
+ mutex_lock(&data->lock);
+ data->alarms_mask = mask;
+ adt7470_write_word_data(data->client, ADT7470_REG_ALARM1_MASK, mask);
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
static ssize_t show_fan_max(struct device *dev,
struct device_attribute *devattr,
char *buf)
@@ -688,6 +718,70 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr,
return count;
}
+/* These are the valid PWM frequencies to the nearest Hz */
+static const int adt7470_freq_map[] = {
+ 11, 15, 22, 29, 35, 44, 59, 88, 1400, 22500
+};
+
+static ssize_t show_pwm_freq(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct adt7470_data *data = adt7470_update_device(dev);
+ unsigned char cfg_reg_1;
+ unsigned char cfg_reg_2;
+ int index;
+
+ mutex_lock(&data->lock);
+ cfg_reg_1 = i2c_smbus_read_byte_data(data->client, ADT7470_REG_CFG);
+ cfg_reg_2 = i2c_smbus_read_byte_data(data->client, ADT7470_REG_CFG_2);
+ mutex_unlock(&data->lock);
+
+ index = (cfg_reg_2 & ADT7470_FREQ_MASK) >> ADT7470_FREQ_SHIFT;
+ if (!(cfg_reg_1 & ADT7470_CFG_LF))
+ index += 8;
+ if (index >= ARRAY_SIZE(adt7470_freq_map))
+ index = ARRAY_SIZE(adt7470_freq_map) - 1;
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n", adt7470_freq_map[index]);
+}
+
+static ssize_t set_pwm_freq(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct adt7470_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ long freq;
+ int index;
+ int low_freq = ADT7470_CFG_LF;
+ unsigned char val;
+
+ if (kstrtol(buf, 10, &freq))
+ return -EINVAL;
+
+ /* Round the user value given to the closest available frequency */
+ index = find_closest(freq, adt7470_freq_map,
+ ARRAY_SIZE(adt7470_freq_map));
+
+ if (index >= 8) {
+ index -= 8;
+ low_freq = 0;
+ }
+
+ mutex_lock(&data->lock);
+ /* Configuration Register 1 */
+ val = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG);
+ i2c_smbus_write_byte_data(client, ADT7470_REG_CFG,
+ (val & ~ADT7470_CFG_LF) | low_freq);
+ /* Configuration Register 2 */
+ val = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG_2);
+ i2c_smbus_write_byte_data(client, ADT7470_REG_CFG_2,
+ (val & ~ADT7470_FREQ_MASK) | (index << ADT7470_FREQ_SHIFT));
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
static ssize_t show_pwm_max(struct device *dev,
struct device_attribute *devattr,
char *buf)
@@ -918,7 +1012,8 @@ static ssize_t show_alarm(struct device *dev,
return sprintf(buf, "0\n");
}
-static DEVICE_ATTR(alarm_mask, S_IRUGO, show_alarm_mask, NULL);
+static DEVICE_ATTR(alarm_mask, S_IWUSR | S_IRUGO, show_alarm_mask,
+ set_alarm_mask);
static DEVICE_ATTR(num_temp_sensors, S_IWUSR | S_IRUGO, show_num_temp_sensors,
set_num_temp_sensors);
static DEVICE_ATTR(auto_update_interval, S_IWUSR | S_IRUGO,
@@ -1038,6 +1133,8 @@ static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 1);
static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 2);
static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 3);
+static DEVICE_ATTR(pwm1_freq, S_IWUSR | S_IRUGO, show_pwm_freq, set_pwm_freq);
+
static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IWUSR | S_IRUGO,
show_pwm_min, set_pwm_min, 0);
static SENSOR_DEVICE_ATTR(pwm2_auto_point1_pwm, S_IWUSR | S_IRUGO,
@@ -1154,6 +1251,7 @@ static struct attribute *adt7470_attrs[] = {
&sensor_dev_attr_fan4_alarm.dev_attr.attr,
&sensor_dev_attr_force_pwm_max.dev_attr.attr,
&sensor_dev_attr_pwm1.dev_attr.attr,
+ &dev_attr_pwm1_freq.attr,
&sensor_dev_attr_pwm2.dev_attr.attr,
&sensor_dev_attr_pwm3.dev_attr.attr,
&sensor_dev_attr_pwm4.dev_attr.attr,
@@ -1256,7 +1354,6 @@ static int adt7470_probe(struct i2c_client *client,
if (IS_ERR(hwmon_dev))
return PTR_ERR(hwmon_dev);
- init_completion(&data->auto_update_stop);
data->auto_update = kthread_run(adt7470_update_thread, client, "%s",
dev_name(hwmon_dev));
if (IS_ERR(data->auto_update)) {
@@ -1271,7 +1368,6 @@ static int adt7470_remove(struct i2c_client *client)
struct adt7470_data *data = i2c_get_clientdata(client);
kthread_stop(data->auto_update);
- wait_for_completion(&data->auto_update_stop);
return 0;
}
diff --git a/drivers/hwmon/ftsteutates.c b/drivers/hwmon/ftsteutates.c
index 48633e541dc3..0f0277e7aae5 100644
--- a/drivers/hwmon/ftsteutates.c
+++ b/drivers/hwmon/ftsteutates.c
@@ -36,6 +36,10 @@
#define FTS_EVENT_STATUS_REG 0x0006
#define FTS_GLOBAL_CONTROL_REG 0x0007
+#define FTS_DEVICE_DETECT_REG_1 0x0C
+#define FTS_DEVICE_DETECT_REG_2 0x0D
+#define FTS_DEVICE_DETECT_REG_3 0x0E
+
#define FTS_SENSOR_EVENT_REG 0x0010
#define FTS_FAN_EVENT_REG 0x0014
@@ -54,6 +58,8 @@
#define FTS_NO_TEMP_SENSORS 0x10
#define FTS_NO_VOLT_SENSORS 0x04
+static const unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END };
+
static struct i2c_device_id fts_id[] = {
{ "ftsteutates", 0 },
{ }
@@ -734,6 +740,42 @@ static const struct attribute_group *fts_attr_groups[] = {
/*****************************************************************************/
/* Module initialization / remove functions */
/*****************************************************************************/
+static int fts_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ int val;
+
+ /* detection works with revsion greater or equal to 0x2b */
+ val = i2c_smbus_read_byte_data(client, FTS_DEVICE_REVISION_REG);
+ if (val < 0x2b)
+ return -ENODEV;
+
+ /* Device Detect Regs must have 0x17 0x34 and 0x54 */
+ val = i2c_smbus_read_byte_data(client, FTS_DEVICE_DETECT_REG_1);
+ if (val != 0x17)
+ return -ENODEV;
+
+ val = i2c_smbus_read_byte_data(client, FTS_DEVICE_DETECT_REG_2);
+ if (val != 0x34)
+ return -ENODEV;
+
+ val = i2c_smbus_read_byte_data(client, FTS_DEVICE_DETECT_REG_3);
+ if (val != 0x54)
+ return -ENODEV;
+
+ /*
+ * 0x10 == Baseboard Management Controller, 0x01 == Teutates
+ * Device ID Reg needs to be 0x11
+ */
+ val = i2c_smbus_read_byte_data(client, FTS_DEVICE_ID_REG);
+ if (val != 0x11)
+ return -ENODEV;
+
+ strlcpy(info->type, fts_id[0].name, I2C_NAME_SIZE);
+ info->flags = 0;
+ return 0;
+}
+
static int fts_remove(struct i2c_client *client)
{
struct fts_data *data = dev_get_drvdata(&client->dev);
@@ -804,12 +846,15 @@ static int fts_probe(struct i2c_client *client, const struct i2c_device_id *id)
/* Module Details */
/*****************************************************************************/
static struct i2c_driver fts_driver = {
+ .class = I2C_CLASS_HWMON,
.driver = {
.name = "ftsteutates",
},
.id_table = fts_id,
.probe = fts_probe,
.remove = fts_remove,
+ .detect = fts_detect,
+ .address_list = normal_i2c,
};
module_i2c_driver(fts_driver);
diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c
index a26c385a435b..adae6848ffb2 100644
--- a/drivers/hwmon/hwmon.c
+++ b/drivers/hwmon/hwmon.c
@@ -12,17 +12,17 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <linux/module.h>
+#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/err.h>
-#include <linux/slab.h>
-#include <linux/kdev_t.h>
-#include <linux/idr.h>
-#include <linux/hwmon.h>
#include <linux/gfp.h>
-#include <linux/spinlock.h>
+#include <linux/hwmon.h>
+#include <linux/idr.h>
+#include <linux/module.h>
#include <linux/pci.h>
+#include <linux/slab.h>
#include <linux/string.h>
+#include <linux/thermal.h>
#define HWMON_ID_PREFIX "hwmon"
#define HWMON_ID_FORMAT HWMON_ID_PREFIX "%d"
@@ -30,9 +30,35 @@
struct hwmon_device {
const char *name;
struct device dev;
+ const struct hwmon_chip_info *chip;
+
+ struct attribute_group group;
+ const struct attribute_group **groups;
};
+
#define to_hwmon_device(d) container_of(d, struct hwmon_device, dev)
+struct hwmon_device_attribute {
+ struct device_attribute dev_attr;
+ const struct hwmon_ops *ops;
+ enum hwmon_sensor_types type;
+ u32 attr;
+ int index;
+};
+
+#define to_hwmon_attr(d) \
+ container_of(d, struct hwmon_device_attribute, dev_attr)
+
+/*
+ * Thermal zone information
+ * In addition to the reference to the hwmon device,
+ * also provides the sensor index.
+ */
+struct hwmon_thermal_data {
+ struct hwmon_device *hwdev; /* Reference to hwmon device */
+ int index; /* sensor index */
+};
+
static ssize_t
show_name(struct device *dev, struct device_attribute *attr, char *buf)
{
@@ -80,25 +106,409 @@ static struct class hwmon_class = {
static DEFINE_IDA(hwmon_ida);
-/**
- * hwmon_device_register_with_groups - register w/ hwmon
- * @dev: the parent device
- * @name: hwmon name attribute
- * @drvdata: driver data to attach to created device
- * @groups: List of attribute groups to create
- *
- * hwmon_device_unregister() must be called when the device is no
- * longer needed.
- *
- * Returns the pointer to the new device.
+/* Thermal zone handling */
+
+/*
+ * The complex conditional is necessary to avoid a cyclic dependency
+ * between hwmon and thermal_sys modules.
*/
-struct device *
-hwmon_device_register_with_groups(struct device *dev, const char *name,
- void *drvdata,
- const struct attribute_group **groups)
+#if IS_REACHABLE(CONFIG_THERMAL) && defined(CONFIG_THERMAL_OF) && \
+ (!defined(CONFIG_THERMAL_HWMON) || \
+ !(defined(MODULE) && IS_MODULE(CONFIG_THERMAL)))
+static int hwmon_thermal_get_temp(void *data, int *temp)
+{
+ struct hwmon_thermal_data *tdata = data;
+ struct hwmon_device *hwdev = tdata->hwdev;
+ int ret;
+ long t;
+
+ ret = hwdev->chip->ops->read(&hwdev->dev, hwmon_temp, hwmon_temp_input,
+ tdata->index, &t);
+ if (ret < 0)
+ return ret;
+
+ *temp = t;
+
+ return 0;
+}
+
+static struct thermal_zone_of_device_ops hwmon_thermal_ops = {
+ .get_temp = hwmon_thermal_get_temp,
+};
+
+static int hwmon_thermal_add_sensor(struct device *dev,
+ struct hwmon_device *hwdev, int index)
+{
+ struct hwmon_thermal_data *tdata;
+
+ tdata = devm_kzalloc(dev, sizeof(*tdata), GFP_KERNEL);
+ if (!tdata)
+ return -ENOMEM;
+
+ tdata->hwdev = hwdev;
+ tdata->index = index;
+
+ devm_thermal_zone_of_sensor_register(&hwdev->dev, index, tdata,
+ &hwmon_thermal_ops);
+
+ return 0;
+}
+#else
+static int hwmon_thermal_add_sensor(struct device *dev,
+ struct hwmon_device *hwdev, int index)
+{
+ return 0;
+}
+#endif /* IS_REACHABLE(CONFIG_THERMAL) && ... */
+
+/* sysfs attribute management */
+
+static ssize_t hwmon_attr_show(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct hwmon_device_attribute *hattr = to_hwmon_attr(devattr);
+ long val;
+ int ret;
+
+ ret = hattr->ops->read(dev, hattr->type, hattr->attr, hattr->index,
+ &val);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%ld\n", val);
+}
+
+static ssize_t hwmon_attr_store(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct hwmon_device_attribute *hattr = to_hwmon_attr(devattr);
+ long val;
+ int ret;
+
+ ret = kstrtol(buf, 10, &val);
+ if (ret < 0)
+ return ret;
+
+ ret = hattr->ops->write(dev, hattr->type, hattr->attr, hattr->index,
+ val);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static int hwmon_attr_base(enum hwmon_sensor_types type)
+{
+ if (type == hwmon_in)
+ return 0;
+ return 1;
+}
+
+static struct attribute *hwmon_genattr(struct device *dev,
+ const void *drvdata,
+ enum hwmon_sensor_types type,
+ u32 attr,
+ int index,
+ const char *template,
+ const struct hwmon_ops *ops)
+{
+ struct hwmon_device_attribute *hattr;
+ struct device_attribute *dattr;
+ struct attribute *a;
+ umode_t mode;
+ char *name;
+
+ /* The attribute is invisible if there is no template string */
+ if (!template)
+ return ERR_PTR(-ENOENT);
+
+ mode = ops->is_visible(drvdata, type, attr, index);
+ if (!mode)
+ return ERR_PTR(-ENOENT);
+
+ if ((mode & S_IRUGO) && !ops->read)
+ return ERR_PTR(-EINVAL);
+ if ((mode & S_IWUGO) && !ops->write)
+ return ERR_PTR(-EINVAL);
+
+ if (type == hwmon_chip) {
+ name = (char *)template;
+ } else {
+ name = devm_kzalloc(dev, strlen(template) + 16, GFP_KERNEL);
+ if (!name)
+ return ERR_PTR(-ENOMEM);
+ scnprintf(name, strlen(template) + 16, template,
+ index + hwmon_attr_base(type));
+ }
+
+ hattr = devm_kzalloc(dev, sizeof(*hattr), GFP_KERNEL);
+ if (!hattr)
+ return ERR_PTR(-ENOMEM);
+
+ hattr->type = type;
+ hattr->attr = attr;
+ hattr->index = index;
+ hattr->ops = ops;
+
+ dattr = &hattr->dev_attr;
+ dattr->show = hwmon_attr_show;
+ dattr->store = hwmon_attr_store;
+
+ a = &dattr->attr;
+ sysfs_attr_init(a);
+ a->name = name;
+ a->mode = mode;
+
+ return a;
+}
+
+static const char * const hwmon_chip_attr_templates[] = {
+ [hwmon_chip_temp_reset_history] = "temp_reset_history",
+ [hwmon_chip_in_reset_history] = "in_reset_history",
+ [hwmon_chip_curr_reset_history] = "curr_reset_history",
+ [hwmon_chip_power_reset_history] = "power_reset_history",
+ [hwmon_chip_update_interval] = "update_interval",
+ [hwmon_chip_alarms] = "alarms",
+};
+
+static const char * const hwmon_temp_attr_templates[] = {
+ [hwmon_temp_input] = "temp%d_input",
+ [hwmon_temp_type] = "temp%d_type",
+ [hwmon_temp_lcrit] = "temp%d_lcrit",
+ [hwmon_temp_lcrit_hyst] = "temp%d_lcrit_hyst",
+ [hwmon_temp_min] = "temp%d_min",
+ [hwmon_temp_min_hyst] = "temp%d_min_hyst",
+ [hwmon_temp_max] = "temp%d_max",
+ [hwmon_temp_max_hyst] = "temp%d_max_hyst",
+ [hwmon_temp_crit] = "temp%d_crit",
+ [hwmon_temp_crit_hyst] = "temp%d_crit_hyst",
+ [hwmon_temp_emergency] = "temp%d_emergency",
+ [hwmon_temp_emergency_hyst] = "temp%d_emergency_hyst",
+ [hwmon_temp_alarm] = "temp%d_alarm",
+ [hwmon_temp_lcrit_alarm] = "temp%d_lcrit_alarm",
+ [hwmon_temp_min_alarm] = "temp%d_min_alarm",
+ [hwmon_temp_max_alarm] = "temp%d_max_alarm",
+ [hwmon_temp_crit_alarm] = "temp%d_crit_alarm",
+ [hwmon_temp_emergency_alarm] = "temp%d_emergency_alarm",
+ [hwmon_temp_fault] = "temp%d_fault",
+ [hwmon_temp_offset] = "temp%d_offset",
+ [hwmon_temp_label] = "temp%d_label",
+ [hwmon_temp_lowest] = "temp%d_lowest",
+ [hwmon_temp_highest] = "temp%d_highest",
+ [hwmon_temp_reset_history] = "temp%d_reset_history",
+};
+
+static const char * const hwmon_in_attr_templates[] = {
+ [hwmon_in_input] = "in%d_input",
+ [hwmon_in_min] = "in%d_min",
+ [hwmon_in_max] = "in%d_max",
+ [hwmon_in_lcrit] = "in%d_lcrit",
+ [hwmon_in_crit] = "in%d_crit",
+ [hwmon_in_average] = "in%d_average",
+ [hwmon_in_lowest] = "in%d_lowest",
+ [hwmon_in_highest] = "in%d_highest",
+ [hwmon_in_reset_history] = "in%d_reset_history",
+ [hwmon_in_label] = "in%d_label",
+ [hwmon_in_alarm] = "in%d_alarm",
+ [hwmon_in_min_alarm] = "in%d_min_alarm",
+ [hwmon_in_max_alarm] = "in%d_max_alarm",
+ [hwmon_in_lcrit_alarm] = "in%d_lcrit_alarm",
+ [hwmon_in_crit_alarm] = "in%d_crit_alarm",
+};
+
+static const char * const hwmon_curr_attr_templates[] = {
+ [hwmon_curr_input] = "curr%d_input",
+ [hwmon_curr_min] = "curr%d_min",
+ [hwmon_curr_max] = "curr%d_max",
+ [hwmon_curr_lcrit] = "curr%d_lcrit",
+ [hwmon_curr_crit] = "curr%d_crit",
+ [hwmon_curr_average] = "curr%d_average",
+ [hwmon_curr_lowest] = "curr%d_lowest",
+ [hwmon_curr_highest] = "curr%d_highest",
+ [hwmon_curr_reset_history] = "curr%d_reset_history",
+ [hwmon_curr_label] = "curr%d_label",
+ [hwmon_curr_alarm] = "curr%d_alarm",
+ [hwmon_curr_min_alarm] = "curr%d_min_alarm",
+ [hwmon_curr_max_alarm] = "curr%d_max_alarm",
+ [hwmon_curr_lcrit_alarm] = "curr%d_lcrit_alarm",
+ [hwmon_curr_crit_alarm] = "curr%d_crit_alarm",
+};
+
+static const char * const hwmon_power_attr_templates[] = {
+ [hwmon_power_average] = "power%d_average",
+ [hwmon_power_average_interval] = "power%d_average_interval",
+ [hwmon_power_average_interval_max] = "power%d_interval_max",
+ [hwmon_power_average_interval_min] = "power%d_interval_min",
+ [hwmon_power_average_highest] = "power%d_average_highest",
+ [hwmon_power_average_lowest] = "power%d_average_lowest",
+ [hwmon_power_average_max] = "power%d_average_max",
+ [hwmon_power_average_min] = "power%d_average_min",
+ [hwmon_power_input] = "power%d_input",
+ [hwmon_power_input_highest] = "power%d_input_highest",
+ [hwmon_power_input_lowest] = "power%d_input_lowest",
+ [hwmon_power_reset_history] = "power%d_reset_history",
+ [hwmon_power_accuracy] = "power%d_accuracy",
+ [hwmon_power_cap] = "power%d_cap",
+ [hwmon_power_cap_hyst] = "power%d_cap_hyst",
+ [hwmon_power_cap_max] = "power%d_cap_max",
+ [hwmon_power_cap_min] = "power%d_cap_min",
+ [hwmon_power_max] = "power%d_max",
+ [hwmon_power_crit] = "power%d_crit",
+ [hwmon_power_label] = "power%d_label",
+ [hwmon_power_alarm] = "power%d_alarm",
+ [hwmon_power_cap_alarm] = "power%d_cap_alarm",
+ [hwmon_power_max_alarm] = "power%d_max_alarm",
+ [hwmon_power_crit_alarm] = "power%d_crit_alarm",
+};
+
+static const char * const hwmon_energy_attr_templates[] = {
+ [hwmon_energy_input] = "energy%d_input",
+ [hwmon_energy_label] = "energy%d_label",
+};
+
+static const char * const hwmon_humidity_attr_templates[] = {
+ [hwmon_humidity_input] = "humidity%d_input",
+ [hwmon_humidity_label] = "humidity%d_label",
+ [hwmon_humidity_min] = "humidity%d_min",
+ [hwmon_humidity_min_hyst] = "humidity%d_min_hyst",
+ [hwmon_humidity_max] = "humidity%d_max",
+ [hwmon_humidity_max_hyst] = "humidity%d_max_hyst",
+ [hwmon_humidity_alarm] = "humidity%d_alarm",
+ [hwmon_humidity_fault] = "humidity%d_fault",
+};
+
+static const char * const hwmon_fan_attr_templates[] = {
+ [hwmon_fan_input] = "fan%d_input",
+ [hwmon_fan_label] = "fan%d_label",
+ [hwmon_fan_min] = "fan%d_min",
+ [hwmon_fan_max] = "fan%d_max",
+ [hwmon_fan_div] = "fan%d_div",
+ [hwmon_fan_pulses] = "fan%d_pulses",
+ [hwmon_fan_target] = "fan%d_target",
+ [hwmon_fan_alarm] = "fan%d_alarm",
+ [hwmon_fan_min_alarm] = "fan%d_min_alarm",
+ [hwmon_fan_max_alarm] = "fan%d_max_alarm",
+ [hwmon_fan_fault] = "fan%d_fault",
+};
+
+static const char * const hwmon_pwm_attr_templates[] = {
+ [hwmon_pwm_input] = "pwm%d",
+ [hwmon_pwm_enable] = "pwm%d_enable",
+ [hwmon_pwm_mode] = "pwm%d_mode",
+ [hwmon_pwm_freq] = "pwm%d_freq",
+};
+
+static const char * const *__templates[] = {
+ [hwmon_chip] = hwmon_chip_attr_templates,
+ [hwmon_temp] = hwmon_temp_attr_templates,
+ [hwmon_in] = hwmon_in_attr_templates,
+ [hwmon_curr] = hwmon_curr_attr_templates,
+ [hwmon_power] = hwmon_power_attr_templates,
+ [hwmon_energy] = hwmon_energy_attr_templates,
+ [hwmon_humidity] = hwmon_humidity_attr_templates,
+ [hwmon_fan] = hwmon_fan_attr_templates,
+ [hwmon_pwm] = hwmon_pwm_attr_templates,
+};
+
+static const int __templates_size[] = {
+ [hwmon_chip] = ARRAY_SIZE(hwmon_chip_attr_templates),
+ [hwmon_temp] = ARRAY_SIZE(hwmon_temp_attr_templates),
+ [hwmon_in] = ARRAY_SIZE(hwmon_in_attr_templates),
+ [hwmon_curr] = ARRAY_SIZE(hwmon_curr_attr_templates),
+ [hwmon_power] = ARRAY_SIZE(hwmon_power_attr_templates),
+ [hwmon_energy] = ARRAY_SIZE(hwmon_energy_attr_templates),
+ [hwmon_humidity] = ARRAY_SIZE(hwmon_humidity_attr_templates),
+ [hwmon_fan] = ARRAY_SIZE(hwmon_fan_attr_templates),
+ [hwmon_pwm] = ARRAY_SIZE(hwmon_pwm_attr_templates),
+};
+
+static int hwmon_num_channel_attrs(const struct hwmon_channel_info *info)
+{
+ int i, n;
+
+ for (i = n = 0; info->config[i]; i++)
+ n += hweight32(info->config[i]);
+
+ return n;
+}
+
+static int hwmon_genattrs(struct device *dev,
+ const void *drvdata,
+ struct attribute **attrs,
+ const struct hwmon_ops *ops,
+ const struct hwmon_channel_info *info)
+{
+ const char * const *templates;
+ int template_size;
+ int i, aindex = 0;
+
+ if (info->type >= ARRAY_SIZE(__templates))
+ return -EINVAL;
+
+ templates = __templates[info->type];
+ template_size = __templates_size[info->type];
+
+ for (i = 0; info->config[i]; i++) {
+ u32 attr_mask = info->config[i];
+ u32 attr;
+
+ while (attr_mask) {
+ struct attribute *a;
+
+ attr = __ffs(attr_mask);
+ attr_mask &= ~BIT(attr);
+ if (attr >= template_size)
+ return -EINVAL;
+ a = hwmon_genattr(dev, drvdata, info->type, attr, i,
+ templates[attr], ops);
+ if (IS_ERR(a)) {
+ if (PTR_ERR(a) != -ENOENT)
+ return PTR_ERR(a);
+ continue;
+ }
+ attrs[aindex++] = a;
+ }
+ }
+ return aindex;
+}
+
+static struct attribute **
+__hwmon_create_attrs(struct device *dev, const void *drvdata,
+ const struct hwmon_chip_info *chip)
+{
+ int ret, i, aindex = 0, nattrs = 0;
+ struct attribute **attrs;
+
+ for (i = 0; chip->info[i]; i++)
+ nattrs += hwmon_num_channel_attrs(chip->info[i]);
+
+ if (nattrs == 0)
+ return ERR_PTR(-EINVAL);
+
+ attrs = devm_kcalloc(dev, nattrs + 1, sizeof(*attrs), GFP_KERNEL);
+ if (!attrs)
+ return ERR_PTR(-ENOMEM);
+
+ for (i = 0; chip->info[i]; i++) {
+ ret = hwmon_genattrs(dev, drvdata, &attrs[aindex], chip->ops,
+ chip->info[i]);
+ if (ret < 0)
+ return ERR_PTR(ret);
+ aindex += ret;
+ }
+
+ return attrs;
+}
+
+static struct device *
+__hwmon_device_register(struct device *dev, const char *name, void *drvdata,
+ const struct hwmon_chip_info *chip,
+ const struct attribute_group **groups)
{
struct hwmon_device *hwdev;
- int err, id;
+ struct device *hdev;
+ int i, j, err, id;
/* Do not accept invalid characters in hwmon name attribute */
if (name && (!strlen(name) || strpbrk(name, "-* \t\n")))
@@ -114,28 +524,128 @@ hwmon_device_register_with_groups(struct device *dev, const char *name,
goto ida_remove;
}
+ hdev = &hwdev->dev;
+
+ if (chip && chip->ops->is_visible) {
+ struct attribute **attrs;
+ int ngroups = 2;
+
+ if (groups)
+ for (i = 0; groups[i]; i++)
+ ngroups++;
+
+ hwdev->groups = devm_kcalloc(dev, ngroups, sizeof(*groups),
+ GFP_KERNEL);
+ if (!hwdev->groups)
+ return ERR_PTR(-ENOMEM);
+
+ attrs = __hwmon_create_attrs(dev, drvdata, chip);
+ if (IS_ERR(attrs)) {
+ err = PTR_ERR(attrs);
+ goto free_hwmon;
+ }
+
+ hwdev->group.attrs = attrs;
+ ngroups = 0;
+ hwdev->groups[ngroups++] = &hwdev->group;
+
+ if (groups) {
+ for (i = 0; groups[i]; i++)
+ hwdev->groups[ngroups++] = groups[i];
+ }
+
+ hdev->groups = hwdev->groups;
+ } else {
+ hdev->groups = groups;
+ }
+
hwdev->name = name;
- hwdev->dev.class = &hwmon_class;
- hwdev->dev.parent = dev;
- hwdev->dev.groups = groups;
- hwdev->dev.of_node = dev ? dev->of_node : NULL;
- dev_set_drvdata(&hwdev->dev, drvdata);
- dev_set_name(&hwdev->dev, HWMON_ID_FORMAT, id);
- err = device_register(&hwdev->dev);
+ hdev->class = &hwmon_class;
+ hdev->parent = dev;
+ hdev->of_node = dev ? dev->of_node : NULL;
+ hwdev->chip = chip;
+ dev_set_drvdata(hdev, drvdata);
+ dev_set_name(hdev, HWMON_ID_FORMAT, id);
+ err = device_register(hdev);
if (err)
- goto free;
+ goto free_hwmon;
+
+ if (chip && chip->ops->is_visible && chip->ops->read &&
+ chip->info[0]->type == hwmon_chip &&
+ (chip->info[0]->config[0] & HWMON_C_REGISTER_TZ)) {
+ const struct hwmon_channel_info **info = chip->info;
+
+ for (i = 1; info[i]; i++) {
+ if (info[i]->type != hwmon_temp)
+ continue;
+
+ for (j = 0; info[i]->config[j]; j++) {
+ if (!chip->ops->is_visible(drvdata, hwmon_temp,
+ hwmon_temp_input, j))
+ continue;
+ if (info[i]->config[j] & HWMON_T_INPUT)
+ hwmon_thermal_add_sensor(dev, hwdev, j);
+ }
+ }
+ }
- return &hwdev->dev;
+ return hdev;
-free:
+free_hwmon:
kfree(hwdev);
ida_remove:
ida_simple_remove(&hwmon_ida, id);
return ERR_PTR(err);
}
+
+/**
+ * hwmon_device_register_with_groups - register w/ hwmon
+ * @dev: the parent device
+ * @name: hwmon name attribute
+ * @drvdata: driver data to attach to created device
+ * @groups: List of attribute groups to create
+ *
+ * hwmon_device_unregister() must be called when the device is no
+ * longer needed.
+ *
+ * Returns the pointer to the new device.
+ */
+struct device *
+hwmon_device_register_with_groups(struct device *dev, const char *name,
+ void *drvdata,
+ const struct attribute_group **groups)
+{
+ return __hwmon_device_register(dev, name, drvdata, NULL, groups);
+}
EXPORT_SYMBOL_GPL(hwmon_device_register_with_groups);
/**
+ * hwmon_device_register_with_info - register w/ hwmon
+ * @dev: the parent device
+ * @name: hwmon name attribute
+ * @drvdata: driver data to attach to created device
+ * @info: Pointer to hwmon chip information
+ * @groups - pointer to list of driver specific attribute groups
+ *
+ * hwmon_device_unregister() must be called when the device is no
+ * longer needed.
+ *
+ * Returns the pointer to the new device.
+ */
+struct device *
+hwmon_device_register_with_info(struct device *dev, const char *name,
+ void *drvdata,
+ const struct hwmon_chip_info *chip,
+ const struct attribute_group **groups)
+{
+ if (chip && (!chip->ops || !chip->info))
+ return ERR_PTR(-EINVAL);
+
+ return __hwmon_device_register(dev, name, drvdata, chip, groups);
+}
+EXPORT_SYMBOL_GPL(hwmon_device_register_with_info);
+
+/**
* hwmon_device_register - register w/ hwmon
* @dev: the device to register
*
@@ -213,6 +723,48 @@ error:
}
EXPORT_SYMBOL_GPL(devm_hwmon_device_register_with_groups);
+/**
+ * devm_hwmon_device_register_with_info - register w/ hwmon
+ * @dev: the parent device
+ * @name: hwmon name attribute
+ * @drvdata: driver data to attach to created device
+ * @info: Pointer to hwmon chip information
+ * @groups - pointer to list of driver specific attribute groups
+ *
+ * Returns the pointer to the new device. The new device is automatically
+ * unregistered with the parent device.
+ */
+struct device *
+devm_hwmon_device_register_with_info(struct device *dev, const char *name,
+ void *drvdata,
+ const struct hwmon_chip_info *chip,
+ const struct attribute_group **groups)
+{
+ struct device **ptr, *hwdev;
+
+ if (!dev)
+ return ERR_PTR(-EINVAL);
+
+ ptr = devres_alloc(devm_hwmon_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ hwdev = hwmon_device_register_with_info(dev, name, drvdata, chip,
+ groups);
+ if (IS_ERR(hwdev))
+ goto error;
+
+ *ptr = hwdev;
+ devres_add(dev, ptr);
+
+ return hwdev;
+
+error:
+ devres_free(ptr);
+ return hwdev;
+}
+EXPORT_SYMBOL_GPL(devm_hwmon_device_register_with_info);
+
static int devm_hwmon_match(struct device *dev, void *res, void *data)
{
struct device **hwdev = res;
diff --git a/drivers/hwmon/ibmpowernv.c b/drivers/hwmon/ibmpowernv.c
index 55b5a8ff1cfe..6d2e6605751c 100644
--- a/drivers/hwmon/ibmpowernv.c
+++ b/drivers/hwmon/ibmpowernv.c
@@ -143,13 +143,11 @@ static void __init make_sensor_label(struct device_node *np,
if (cpuid >= 0)
/*
* The digital thermal sensors are associated
- * with a core. Let's print out the range of
- * cpu ids corresponding to the hardware
- * threads of the core.
+ * with a core.
*/
n += snprintf(sdata->label + n,
- sizeof(sdata->label) - n, " %d-%d",
- cpuid, cpuid + threads_per_core - 1);
+ sizeof(sdata->label) - n, " %d",
+ cpuid);
else
n += snprintf(sdata->label + n,
sizeof(sdata->label) - n, " phy%d", id);
diff --git a/drivers/hwmon/iio_hwmon.c b/drivers/hwmon/iio_hwmon.c
index 89449871bca7..f6a76679c650 100644
--- a/drivers/hwmon/iio_hwmon.c
+++ b/drivers/hwmon/iio_hwmon.c
@@ -73,8 +73,11 @@ static int iio_hwmon_probe(struct platform_device *pdev)
name = dev->of_node->name;
channels = iio_channel_get_all(dev);
- if (IS_ERR(channels))
+ if (IS_ERR(channels)) {
+ if (PTR_ERR(channels) == -ENODEV)
+ return -EPROBE_DEFER;
return PTR_ERR(channels);
+ }
st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
if (st == NULL) {
diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c
index 4667012b46b7..ad82cb28d87a 100644
--- a/drivers/hwmon/it87.c
+++ b/drivers/hwmon/it87.c
@@ -2011,10 +2011,10 @@ static struct attribute *it87_attributes_in[] = {
&sensor_dev_attr_in7_beep.dev_attr.attr, /* 39 */
&sensor_dev_attr_in8_input.dev_attr.attr, /* 40 */
- &sensor_dev_attr_in9_input.dev_attr.attr, /* 41 */
- &sensor_dev_attr_in10_input.dev_attr.attr, /* 41 */
- &sensor_dev_attr_in11_input.dev_attr.attr, /* 41 */
- &sensor_dev_attr_in12_input.dev_attr.attr, /* 41 */
+ &sensor_dev_attr_in9_input.dev_attr.attr,
+ &sensor_dev_attr_in10_input.dev_attr.attr,
+ &sensor_dev_attr_in11_input.dev_attr.attr,
+ &sensor_dev_attr_in12_input.dev_attr.attr,
NULL
};
diff --git a/drivers/hwmon/jc42.c b/drivers/hwmon/jc42.c
index 9d5f85f3384f..1bf22eff0b08 100644
--- a/drivers/hwmon/jc42.c
+++ b/drivers/hwmon/jc42.c
@@ -28,7 +28,6 @@
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/of.h>
@@ -254,170 +253,148 @@ abort:
return ret;
}
-/* sysfs functions */
-
-static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
- char *buf)
-{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- struct jc42_data *data = jc42_update_device(dev);
- if (IS_ERR(data))
- return PTR_ERR(data);
- return sprintf(buf, "%d\n",
- jc42_temp_from_reg(data->temp[attr->index]));
-}
-
-static ssize_t show_temp_hyst(struct device *dev,
- struct device_attribute *devattr, char *buf)
+static int jc42_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct jc42_data *data = jc42_update_device(dev);
int temp, hyst;
if (IS_ERR(data))
return PTR_ERR(data);
- temp = jc42_temp_from_reg(data->temp[attr->index]);
- hyst = jc42_hysteresis[(data->config & JC42_CFG_HYST_MASK)
- >> JC42_CFG_HYST_SHIFT];
- return sprintf(buf, "%d\n", temp - hyst);
+ switch (attr) {
+ case hwmon_temp_input:
+ *val = jc42_temp_from_reg(data->temp[t_input]);
+ return 0;
+ case hwmon_temp_min:
+ *val = jc42_temp_from_reg(data->temp[t_min]);
+ return 0;
+ case hwmon_temp_max:
+ *val = jc42_temp_from_reg(data->temp[t_max]);
+ return 0;
+ case hwmon_temp_crit:
+ *val = jc42_temp_from_reg(data->temp[t_crit]);
+ return 0;
+ case hwmon_temp_max_hyst:
+ temp = jc42_temp_from_reg(data->temp[t_max]);
+ hyst = jc42_hysteresis[(data->config & JC42_CFG_HYST_MASK)
+ >> JC42_CFG_HYST_SHIFT];
+ *val = temp - hyst;
+ return 0;
+ case hwmon_temp_crit_hyst:
+ temp = jc42_temp_from_reg(data->temp[t_crit]);
+ hyst = jc42_hysteresis[(data->config & JC42_CFG_HYST_MASK)
+ >> JC42_CFG_HYST_SHIFT];
+ *val = temp - hyst;
+ return 0;
+ case hwmon_temp_min_alarm:
+ *val = (data->temp[t_input] >> JC42_ALARM_MIN_BIT) & 1;
+ return 0;
+ case hwmon_temp_max_alarm:
+ *val = (data->temp[t_input] >> JC42_ALARM_MAX_BIT) & 1;
+ return 0;
+ case hwmon_temp_crit_alarm:
+ *val = (data->temp[t_input] >> JC42_ALARM_CRIT_BIT) & 1;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static ssize_t set_temp(struct device *dev, struct device_attribute *devattr,
- const char *buf, size_t count)
+static int jc42_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct jc42_data *data = dev_get_drvdata(dev);
- int err, ret = count;
- int nr = attr->index;
- long val;
+ struct i2c_client *client = data->client;
+ int diff, hyst;
+ int ret;
- if (kstrtol(buf, 10, &val) < 0)
- return -EINVAL;
mutex_lock(&data->update_lock);
- data->temp[nr] = jc42_temp_to_reg(val, data->extended);
- err = i2c_smbus_write_word_swapped(data->client, temp_regs[nr],
- data->temp[nr]);
- if (err < 0)
- ret = err;
- mutex_unlock(&data->update_lock);
- return ret;
-}
-/*
- * JC42.4 compliant chips only support four hysteresis values.
- * Pick best choice and go from there.
- */
-static ssize_t set_temp_crit_hyst(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct jc42_data *data = dev_get_drvdata(dev);
- long val;
- int diff, hyst;
- int err;
- int ret = count;
-
- if (kstrtol(buf, 10, &val) < 0)
- return -EINVAL;
-
- val = clamp_val(val, (data->extended ? JC42_TEMP_MIN_EXTENDED :
- JC42_TEMP_MIN) - 6000, JC42_TEMP_MAX);
- diff = jc42_temp_from_reg(data->temp[t_crit]) - val;
-
- hyst = 0;
- if (diff > 0) {
- if (diff < 2250)
- hyst = 1; /* 1.5 degrees C */
- else if (diff < 4500)
- hyst = 2; /* 3.0 degrees C */
- else
- hyst = 3; /* 6.0 degrees C */
+ switch (attr) {
+ case hwmon_temp_min:
+ data->temp[t_min] = jc42_temp_to_reg(val, data->extended);
+ ret = i2c_smbus_write_word_swapped(client, temp_regs[t_min],
+ data->temp[t_min]);
+ break;
+ case hwmon_temp_max:
+ data->temp[t_max] = jc42_temp_to_reg(val, data->extended);
+ ret = i2c_smbus_write_word_swapped(client, temp_regs[t_max],
+ data->temp[t_max]);
+ break;
+ case hwmon_temp_crit:
+ data->temp[t_crit] = jc42_temp_to_reg(val, data->extended);
+ ret = i2c_smbus_write_word_swapped(client, temp_regs[t_crit],
+ data->temp[t_crit]);
+ break;
+ case hwmon_temp_crit_hyst:
+ /*
+ * JC42.4 compliant chips only support four hysteresis values.
+ * Pick best choice and go from there.
+ */
+ val = clamp_val(val, (data->extended ? JC42_TEMP_MIN_EXTENDED
+ : JC42_TEMP_MIN) - 6000,
+ JC42_TEMP_MAX);
+ diff = jc42_temp_from_reg(data->temp[t_crit]) - val;
+ hyst = 0;
+ if (diff > 0) {
+ if (diff < 2250)
+ hyst = 1; /* 1.5 degrees C */
+ else if (diff < 4500)
+ hyst = 2; /* 3.0 degrees C */
+ else
+ hyst = 3; /* 6.0 degrees C */
+ }
+ data->config = (data->config & ~JC42_CFG_HYST_MASK) |
+ (hyst << JC42_CFG_HYST_SHIFT);
+ ret = i2c_smbus_write_word_swapped(data->client,
+ JC42_REG_CONFIG,
+ data->config);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
}
- mutex_lock(&data->update_lock);
- data->config = (data->config & ~JC42_CFG_HYST_MASK)
- | (hyst << JC42_CFG_HYST_SHIFT);
- err = i2c_smbus_write_word_swapped(data->client, JC42_REG_CONFIG,
- data->config);
- if (err < 0)
- ret = err;
mutex_unlock(&data->update_lock);
- return ret;
-}
-static ssize_t show_alarm(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- u16 bit = to_sensor_dev_attr(attr)->index;
- struct jc42_data *data = jc42_update_device(dev);
- u16 val;
-
- if (IS_ERR(data))
- return PTR_ERR(data);
-
- val = data->temp[t_input];
- if (bit != JC42_ALARM_CRIT_BIT && (data->config & JC42_CFG_CRIT_ONLY))
- val = 0;
- return sprintf(buf, "%u\n", (val >> bit) & 1);
+ return ret;
}
-static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, t_input);
-static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp, set_temp, t_crit);
-static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO, show_temp, set_temp, t_min);
-static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp, set_temp, t_max);
-
-static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO, show_temp_hyst,
- set_temp_crit_hyst, t_crit);
-static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IRUGO, show_temp_hyst, NULL, t_max);
-
-static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL,
- JC42_ALARM_CRIT_BIT);
-static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL,
- JC42_ALARM_MIN_BIT);
-static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL,
- JC42_ALARM_MAX_BIT);
-
-static struct attribute *jc42_attributes[] = {
- &sensor_dev_attr_temp1_input.dev_attr.attr,
- &sensor_dev_attr_temp1_crit.dev_attr.attr,
- &sensor_dev_attr_temp1_min.dev_attr.attr,
- &sensor_dev_attr_temp1_max.dev_attr.attr,
- &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
- &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
- &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
- &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
- &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
- NULL
-};
-
-static umode_t jc42_attribute_mode(struct kobject *kobj,
- struct attribute *attr, int index)
+static umode_t jc42_is_visible(const void *_data, enum hwmon_sensor_types type,
+ u32 attr, int channel)
{
- struct device *dev = container_of(kobj, struct device, kobj);
- struct jc42_data *data = dev_get_drvdata(dev);
+ const struct jc42_data *data = _data;
unsigned int config = data->config;
- bool readonly;
-
- if (attr == &sensor_dev_attr_temp1_crit.dev_attr.attr)
- readonly = config & JC42_CFG_TCRIT_LOCK;
- else if (attr == &sensor_dev_attr_temp1_min.dev_attr.attr ||
- attr == &sensor_dev_attr_temp1_max.dev_attr.attr)
- readonly = config & JC42_CFG_EVENT_LOCK;
- else if (attr == &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr)
- readonly = config & (JC42_CFG_EVENT_LOCK | JC42_CFG_TCRIT_LOCK);
- else
- readonly = true;
-
- return S_IRUGO | (readonly ? 0 : S_IWUSR);
+ umode_t mode = S_IRUGO;
+
+ switch (attr) {
+ case hwmon_temp_min:
+ case hwmon_temp_max:
+ if (!(config & JC42_CFG_EVENT_LOCK))
+ mode |= S_IWUSR;
+ break;
+ case hwmon_temp_crit:
+ if (!(config & JC42_CFG_TCRIT_LOCK))
+ mode |= S_IWUSR;
+ break;
+ case hwmon_temp_crit_hyst:
+ if (!(config & (JC42_CFG_EVENT_LOCK | JC42_CFG_TCRIT_LOCK)))
+ mode |= S_IWUSR;
+ break;
+ case hwmon_temp_input:
+ case hwmon_temp_max_hyst:
+ case hwmon_temp_min_alarm:
+ case hwmon_temp_max_alarm:
+ case hwmon_temp_crit_alarm:
+ break;
+ default:
+ mode = 0;
+ break;
+ }
+ return mode;
}
-static const struct attribute_group jc42_group = {
- .attrs = jc42_attributes,
- .is_visible = jc42_attribute_mode,
-};
-__ATTRIBUTE_GROUPS(jc42);
-
/* Return 0 if detection is successful, -ENODEV otherwise */
static int jc42_detect(struct i2c_client *client, struct i2c_board_info *info)
{
@@ -450,6 +427,34 @@ static int jc42_detect(struct i2c_client *client, struct i2c_board_info *info)
return -ENODEV;
}
+static const u32 jc42_temp_config[] = {
+ HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_CRIT |
+ HWMON_T_MAX_HYST | HWMON_T_CRIT_HYST |
+ HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM,
+ 0
+};
+
+static const struct hwmon_channel_info jc42_temp = {
+ .type = hwmon_temp,
+ .config = jc42_temp_config,
+};
+
+static const struct hwmon_channel_info *jc42_info[] = {
+ &jc42_temp,
+ NULL
+};
+
+static const struct hwmon_ops jc42_hwmon_ops = {
+ .is_visible = jc42_is_visible,
+ .read = jc42_read,
+ .write = jc42_write,
+};
+
+static const struct hwmon_chip_info jc42_chip_info = {
+ .ops = &jc42_hwmon_ops,
+ .info = jc42_info,
+};
+
static int jc42_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
@@ -482,9 +487,9 @@ static int jc42_probe(struct i2c_client *client, const struct i2c_device_id *id)
}
data->config = config;
- hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
- data,
- jc42_groups);
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
+ data, &jc42_chip_info,
+ NULL);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c
index 92f9d4bbf597..eff3b24d8473 100644
--- a/drivers/hwmon/lm75.c
+++ b/drivers/hwmon/lm75.c
@@ -28,7 +28,6 @@
#include <linux/err.h>
#include <linux/of.h>
#include <linux/regmap.h>
-#include <linux/thermal.h>
#include "lm75.h"
@@ -88,56 +87,75 @@ static inline long lm75_reg_to_mc(s16 temp, u8 resolution)
return ((temp >> (16 - resolution)) * 1000) >> (resolution - 8);
}
-/* sysfs attributes for hwmon */
-
-static int lm75_read_temp(void *dev, int *temp)
+static int lm75_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
{
struct lm75_data *data = dev_get_drvdata(dev);
- unsigned int _temp;
- int err;
-
- err = regmap_read(data->regmap, LM75_REG_TEMP, &_temp);
- if (err < 0)
- return err;
-
- *temp = lm75_reg_to_mc(_temp, data->resolution);
-
+ unsigned int regval;
+ int err, reg;
+
+ switch (type) {
+ case hwmon_chip:
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ *val = data->sample_time;
+ break;;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case hwmon_temp:
+ switch (attr) {
+ case hwmon_temp_input:
+ reg = LM75_REG_TEMP;
+ break;
+ case hwmon_temp_max:
+ reg = LM75_REG_MAX;
+ break;
+ case hwmon_temp_max_hyst:
+ reg = LM75_REG_HYST;
+ break;
+ default:
+ return -EINVAL;
+ }
+ err = regmap_read(data->regmap, reg, &regval);
+ if (err < 0)
+ return err;
+
+ *val = lm75_reg_to_mc(regval, data->resolution);
+ break;
+ default:
+ return -EINVAL;
+ }
return 0;
}
-static ssize_t show_temp(struct device *dev, struct device_attribute *da,
- char *buf)
+static int lm75_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long temp)
{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
struct lm75_data *data = dev_get_drvdata(dev);
- unsigned int temp = 0;
- int err;
-
- err = regmap_read(data->regmap, attr->index, &temp);
- if (err < 0)
- return err;
-
- return sprintf(buf, "%ld\n", lm75_reg_to_mc(temp, data->resolution));
-}
-
-static ssize_t set_temp(struct device *dev, struct device_attribute *da,
- const char *buf, size_t count)
-{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
- struct lm75_data *data = dev_get_drvdata(dev);
- long temp;
- int error;
u8 resolution;
+ int reg;
+
+ if (type != hwmon_temp)
+ return -EINVAL;
- error = kstrtol(buf, 10, &temp);
- if (error)
- return error;
+ switch (attr) {
+ case hwmon_temp_max:
+ reg = LM75_REG_MAX;
+ break;
+ case hwmon_temp_max_hyst:
+ reg = LM75_REG_HYST;
+ break;
+ default:
+ return -EINVAL;
+ }
/*
* Resolution of limit registers is assumed to be the same as the
* temperature input register resolution unless given explicitly.
*/
- if (attr->index && data->resolution_limits)
+ if (data->resolution_limits)
resolution = data->resolution_limits;
else
resolution = data->resolution;
@@ -145,45 +163,77 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *da,
temp = clamp_val(temp, LM75_TEMP_MIN, LM75_TEMP_MAX);
temp = DIV_ROUND_CLOSEST(temp << (resolution - 8),
1000) << (16 - resolution);
- error = regmap_write(data->regmap, attr->index, temp);
- if (error < 0)
- return error;
- return count;
+ return regmap_write(data->regmap, reg, temp);
}
-static ssize_t show_update_interval(struct device *dev,
- struct device_attribute *da, char *buf)
+static umode_t lm75_is_visible(const void *data, enum hwmon_sensor_types type,
+ u32 attr, int channel)
{
- struct lm75_data *data = dev_get_drvdata(dev);
-
- return sprintf(buf, "%u\n", data->sample_time);
+ switch (type) {
+ case hwmon_chip:
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ return S_IRUGO;
+ }
+ break;
+ case hwmon_temp:
+ switch (attr) {
+ case hwmon_temp_input:
+ return S_IRUGO;
+ case hwmon_temp_max:
+ case hwmon_temp_max_hyst:
+ return S_IRUGO | S_IWUSR;
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
}
-static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO,
- show_temp, set_temp, LM75_REG_MAX);
-static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO,
- show_temp, set_temp, LM75_REG_HYST);
-static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, LM75_REG_TEMP);
-static DEVICE_ATTR(update_interval, S_IRUGO, show_update_interval, NULL);
+/*-----------------------------------------------------------------------*/
-static struct attribute *lm75_attrs[] = {
- &sensor_dev_attr_temp1_input.dev_attr.attr,
- &sensor_dev_attr_temp1_max.dev_attr.attr,
- &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
- &dev_attr_update_interval.attr,
+/* device probe and removal */
- NULL
+/* chip configuration */
+
+static const u32 lm75_chip_config[] = {
+ HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL,
+ 0
};
-ATTRIBUTE_GROUPS(lm75);
-static const struct thermal_zone_of_device_ops lm75_of_thermal_ops = {
- .get_temp = lm75_read_temp,
+static const struct hwmon_channel_info lm75_chip = {
+ .type = hwmon_chip,
+ .config = lm75_chip_config,
};
-/*-----------------------------------------------------------------------*/
+static const u32 lm75_temp_config[] = {
+ HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST,
+ 0
+};
-/* device probe and removal */
+static const struct hwmon_channel_info lm75_temp = {
+ .type = hwmon_temp,
+ .config = lm75_temp_config,
+};
+
+static const struct hwmon_channel_info *lm75_info[] = {
+ &lm75_chip,
+ &lm75_temp,
+ NULL
+};
+
+static const struct hwmon_ops lm75_hwmon_ops = {
+ .is_visible = lm75_is_visible,
+ .read = lm75_read,
+ .write = lm75_write,
+};
+
+static const struct hwmon_chip_info lm75_chip_info = {
+ .ops = &lm75_hwmon_ops,
+ .info = lm75_info,
+};
static bool lm75_is_writeable_reg(struct device *dev, unsigned int reg)
{
@@ -337,15 +387,12 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id)
dev_dbg(dev, "Config %02x\n", new);
- hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
- data, lm75_groups);
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
+ data, &lm75_chip_info,
+ NULL);
if (IS_ERR(hwmon_dev))
return PTR_ERR(hwmon_dev);
- devm_thermal_zone_of_sensor_register(hwmon_dev, 0,
- hwmon_dev,
- &lm75_of_thermal_ops);
-
dev_info(dev, "%s: sensor '%s'\n", dev_name(hwmon_dev), client->name);
return 0;
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index 496e771b363f..322ed9272811 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -89,7 +89,6 @@
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
-#include <linux/hwmon-sysfs.h>
#include <linux/hwmon.h>
#include <linux/err.h>
#include <linux/mutex.h>
@@ -326,7 +325,7 @@ static const struct lm90_params lm90_params[] = {
.alert_alarms = 0x7c,
.max_convrate = 9,
.reg_local_ext = TMP451_REG_R_LOCAL_TEMPL,
- }
+ },
};
/*
@@ -365,7 +364,10 @@ enum lm90_temp11_reg_index {
struct lm90_data {
struct i2c_client *client;
- const struct attribute_group *groups[6];
+ u32 channel_config[4];
+ struct hwmon_channel_info temp_info;
+ const struct hwmon_channel_info *info[3];
+ struct hwmon_chip_info chip;
struct mutex update_lock;
bool valid; /* true if register values are valid */
unsigned long last_updated; /* in jiffies */
@@ -489,11 +491,11 @@ static inline int lm90_select_remote_channel(struct i2c_client *client,
* client->update_lock must be held when calling this function (unless we are
* in detection or initialization steps).
*/
-static void lm90_set_convrate(struct i2c_client *client, struct lm90_data *data,
- unsigned int interval)
+static int lm90_set_convrate(struct i2c_client *client, struct lm90_data *data,
+ unsigned int interval)
{
- int i;
unsigned int update_interval;
+ int i, err;
/* Shift calculations to avoid rounding errors */
interval <<= 6;
@@ -504,8 +506,9 @@ static void lm90_set_convrate(struct i2c_client *client, struct lm90_data *data,
if (interval >= update_interval * 3 / 4)
break;
- i2c_smbus_write_byte_data(client, LM90_REG_W_CONVRATE, i);
+ err = i2c_smbus_write_byte_data(client, LM90_REG_W_CONVRATE, i);
data->update_interval = DIV_ROUND_CLOSEST(update_interval, 64);
+ return err;
}
static int lm90_update_limits(struct device *dev)
@@ -604,19 +607,17 @@ static int lm90_update_limits(struct device *dev)
return 0;
}
-static struct lm90_data *lm90_update_device(struct device *dev)
+static int lm90_update_device(struct device *dev)
{
struct lm90_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
unsigned long next_update;
- int val = 0;
-
- mutex_lock(&data->update_lock);
+ int val;
if (!data->valid) {
val = lm90_update_limits(dev);
if (val < 0)
- goto error;
+ return val;
}
next_update = data->last_updated +
@@ -628,53 +629,55 @@ static struct lm90_data *lm90_update_device(struct device *dev)
val = lm90_read_reg(client, LM90_REG_R_LOCAL_LOW);
if (val < 0)
- goto error;
+ return val;
data->temp8[LOCAL_LOW] = val;
val = lm90_read_reg(client, LM90_REG_R_LOCAL_HIGH);
if (val < 0)
- goto error;
+ return val;
data->temp8[LOCAL_HIGH] = val;
if (data->reg_local_ext) {
val = lm90_read16(client, LM90_REG_R_LOCAL_TEMP,
data->reg_local_ext);
if (val < 0)
- goto error;
+ return val;
data->temp11[LOCAL_TEMP] = val;
} else {
val = lm90_read_reg(client, LM90_REG_R_LOCAL_TEMP);
if (val < 0)
- goto error;
+ return val;
data->temp11[LOCAL_TEMP] = val << 8;
}
val = lm90_read16(client, LM90_REG_R_REMOTE_TEMPH,
LM90_REG_R_REMOTE_TEMPL);
if (val < 0)
- goto error;
+ return val;
data->temp11[REMOTE_TEMP] = val;
val = lm90_read_reg(client, LM90_REG_R_STATUS);
if (val < 0)
- goto error;
+ return val;
data->alarms = val; /* lower 8 bit of alarms */
if (data->kind == max6696) {
val = lm90_select_remote_channel(client, data, 1);
if (val < 0)
- goto error;
+ return val;
val = lm90_read16(client, LM90_REG_R_REMOTE_TEMPH,
LM90_REG_R_REMOTE_TEMPL);
- if (val < 0)
- goto error;
+ if (val < 0) {
+ lm90_select_remote_channel(client, data, 0);
+ return val;
+ }
data->temp11[REMOTE2_TEMP] = val;
lm90_select_remote_channel(client, data, 0);
val = lm90_read_reg(client, MAX6696_REG_R_STATUS2);
if (val < 0)
- goto error;
+ return val;
data->alarms |= val << 8;
}
@@ -686,7 +689,7 @@ static struct lm90_data *lm90_update_device(struct device *dev)
!(data->alarms & data->alert_alarms)) {
val = lm90_read_reg(client, LM90_REG_R_CONFIG1);
if (val < 0)
- goto error;
+ return val;
if (val & 0x80) {
dev_dbg(&client->dev, "Re-enabling ALERT#\n");
@@ -700,13 +703,7 @@ static struct lm90_data *lm90_update_device(struct device *dev)
data->valid = true;
}
-error:
- mutex_unlock(&data->update_lock);
-
- if (val < 0)
- return ERR_PTR(val);
-
- return data;
+ return 0;
}
/*
@@ -832,52 +829,19 @@ static u16 temp_to_u16_adt7461(struct lm90_data *data, long val)
return (val + 125) / 250 * 64;
}
-/*
- * Sysfs stuff
- */
-
-static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr,
- char *buf)
+/* pec used for ADM1032 only */
+static ssize_t show_pec(struct device *dev, struct device_attribute *dummy,
+ char *buf)
{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- struct lm90_data *data = lm90_update_device(dev);
- int temp;
-
- if (IS_ERR(data))
- return PTR_ERR(data);
-
- if (data->kind == adt7461 || data->kind == tmp451)
- temp = temp_from_u8_adt7461(data, data->temp8[attr->index]);
- else if (data->kind == max6646)
- temp = temp_from_u8(data->temp8[attr->index]);
- else
- temp = temp_from_s8(data->temp8[attr->index]);
-
- /* +16 degrees offset for temp2 for the LM99 */
- if (data->kind == lm99 && attr->index == 3)
- temp += 16000;
+ struct i2c_client *client = to_i2c_client(dev);
- return sprintf(buf, "%d\n", temp);
+ return sprintf(buf, "%d\n", !!(client->flags & I2C_CLIENT_PEC));
}
-static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr,
- const char *buf, size_t count)
+static ssize_t set_pec(struct device *dev, struct device_attribute *dummy,
+ const char *buf, size_t count)
{
- static const u8 reg[TEMP8_REG_NUM] = {
- LM90_REG_W_LOCAL_LOW,
- LM90_REG_W_LOCAL_HIGH,
- LM90_REG_W_LOCAL_CRIT,
- LM90_REG_W_REMOTE_CRIT,
- MAX6659_REG_W_LOCAL_EMERG,
- MAX6659_REG_W_REMOTE_EMERG,
- LM90_REG_W_REMOTE_CRIT,
- MAX6659_REG_W_REMOTE_EMERG,
- };
-
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- struct lm90_data *data = dev_get_drvdata(dev);
- struct i2c_client *client = data->client;
- int nr = attr->index;
+ struct i2c_client *client = to_i2c_client(dev);
long val;
int err;
@@ -885,82 +849,61 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr,
if (err < 0)
return err;
- /* +16 degrees offset for temp2 for the LM99 */
- if (data->kind == lm99 && attr->index == 3)
- val -= 16000;
-
- mutex_lock(&data->update_lock);
- if (data->kind == adt7461 || data->kind == tmp451)
- data->temp8[nr] = temp_to_u8_adt7461(data, val);
- else if (data->kind == max6646)
- data->temp8[nr] = temp_to_u8(val);
- else
- data->temp8[nr] = temp_to_s8(val);
-
- lm90_select_remote_channel(client, data, nr >= 6);
- i2c_smbus_write_byte_data(client, reg[nr], data->temp8[nr]);
- lm90_select_remote_channel(client, data, 0);
+ switch (val) {
+ case 0:
+ client->flags &= ~I2C_CLIENT_PEC;
+ break;
+ case 1:
+ client->flags |= I2C_CLIENT_PEC;
+ break;
+ default:
+ return -EINVAL;
+ }
- mutex_unlock(&data->update_lock);
return count;
}
-static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr,
- char *buf)
+static DEVICE_ATTR(pec, S_IWUSR | S_IRUGO, show_pec, set_pec);
+
+static int lm90_get_temp11(struct lm90_data *data, int index)
{
- struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
- struct lm90_data *data = lm90_update_device(dev);
+ s16 temp11 = data->temp11[index];
int temp;
- if (IS_ERR(data))
- return PTR_ERR(data);
-
if (data->kind == adt7461 || data->kind == tmp451)
- temp = temp_from_u16_adt7461(data, data->temp11[attr->index]);
+ temp = temp_from_u16_adt7461(data, temp11);
else if (data->kind == max6646)
- temp = temp_from_u16(data->temp11[attr->index]);
+ temp = temp_from_u16(temp11);
else
- temp = temp_from_s16(data->temp11[attr->index]);
+ temp = temp_from_s16(temp11);
/* +16 degrees offset for temp2 for the LM99 */
- if (data->kind == lm99 && attr->index <= 2)
+ if (data->kind == lm99 && index <= 2)
temp += 16000;
- return sprintf(buf, "%d\n", temp);
+ return temp;
}
-static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr,
- const char *buf, size_t count)
+static int lm90_set_temp11(struct lm90_data *data, int index, long val)
{
- struct {
+ static struct reg {
u8 high;
u8 low;
- int channel;
- } reg[5] = {
- { LM90_REG_W_REMOTE_LOWH, LM90_REG_W_REMOTE_LOWL, 0 },
- { LM90_REG_W_REMOTE_HIGHH, LM90_REG_W_REMOTE_HIGHL, 0 },
- { LM90_REG_W_REMOTE_OFFSH, LM90_REG_W_REMOTE_OFFSL, 0 },
- { LM90_REG_W_REMOTE_LOWH, LM90_REG_W_REMOTE_LOWL, 1 },
- { LM90_REG_W_REMOTE_HIGHH, LM90_REG_W_REMOTE_HIGHL, 1 }
+ } reg[] = {
+ [REMOTE_LOW] = { LM90_REG_W_REMOTE_LOWH, LM90_REG_W_REMOTE_LOWL },
+ [REMOTE_HIGH] = { LM90_REG_W_REMOTE_HIGHH, LM90_REG_W_REMOTE_HIGHL },
+ [REMOTE_OFFSET] = { LM90_REG_W_REMOTE_OFFSH, LM90_REG_W_REMOTE_OFFSL },
+ [REMOTE2_LOW] = { LM90_REG_W_REMOTE_LOWH, LM90_REG_W_REMOTE_LOWL },
+ [REMOTE2_HIGH] = { LM90_REG_W_REMOTE_HIGHH, LM90_REG_W_REMOTE_HIGHL }
};
-
- struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
- struct lm90_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
- int nr = attr->nr;
- int index = attr->index;
- long val;
+ struct reg *regp = &reg[index];
int err;
- err = kstrtol(buf, 10, &val);
- if (err < 0)
- return err;
-
/* +16 degrees offset for temp2 for the LM99 */
if (data->kind == lm99 && index <= 2)
val -= 16000;
- mutex_lock(&data->update_lock);
if (data->kind == adt7461 || data->kind == tmp451)
data->temp11[index] = temp_to_u16_adt7461(data, val);
else if (data->kind == max6646)
@@ -970,317 +913,383 @@ static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr,
else
data->temp11[index] = temp_to_s8(val) << 8;
- lm90_select_remote_channel(client, data, reg[nr].channel);
- i2c_smbus_write_byte_data(client, reg[nr].high,
+ lm90_select_remote_channel(client, data, index >= 3);
+ err = i2c_smbus_write_byte_data(client, regp->high,
data->temp11[index] >> 8);
+ if (err < 0)
+ return err;
if (data->flags & LM90_HAVE_REM_LIMIT_EXT)
- i2c_smbus_write_byte_data(client, reg[nr].low,
- data->temp11[index] & 0xff);
- lm90_select_remote_channel(client, data, 0);
+ err = i2c_smbus_write_byte_data(client, regp->low,
+ data->temp11[index] & 0xff);
- mutex_unlock(&data->update_lock);
- return count;
+ lm90_select_remote_channel(client, data, 0);
+ return err;
}
-static ssize_t show_temphyst(struct device *dev,
- struct device_attribute *devattr,
- char *buf)
+static int lm90_get_temp8(struct lm90_data *data, int index)
{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- struct lm90_data *data = lm90_update_device(dev);
+ s8 temp8 = data->temp8[index];
int temp;
- if (IS_ERR(data))
- return PTR_ERR(data);
-
if (data->kind == adt7461 || data->kind == tmp451)
- temp = temp_from_u8_adt7461(data, data->temp8[attr->index]);
+ temp = temp_from_u8_adt7461(data, temp8);
else if (data->kind == max6646)
- temp = temp_from_u8(data->temp8[attr->index]);
+ temp = temp_from_u8(temp8);
else
- temp = temp_from_s8(data->temp8[attr->index]);
+ temp = temp_from_s8(temp8);
/* +16 degrees offset for temp2 for the LM99 */
- if (data->kind == lm99 && attr->index == 3)
+ if (data->kind == lm99 && index == 3)
temp += 16000;
- return sprintf(buf, "%d\n", temp - temp_from_s8(data->temp_hyst));
+ return temp;
}
-static ssize_t set_temphyst(struct device *dev, struct device_attribute *dummy,
- const char *buf, size_t count)
+static int lm90_set_temp8(struct lm90_data *data, int index, long val)
{
- struct lm90_data *data = dev_get_drvdata(dev);
+ static const u8 reg[TEMP8_REG_NUM] = {
+ LM90_REG_W_LOCAL_LOW,
+ LM90_REG_W_LOCAL_HIGH,
+ LM90_REG_W_LOCAL_CRIT,
+ LM90_REG_W_REMOTE_CRIT,
+ MAX6659_REG_W_LOCAL_EMERG,
+ MAX6659_REG_W_REMOTE_EMERG,
+ LM90_REG_W_REMOTE_CRIT,
+ MAX6659_REG_W_REMOTE_EMERG,
+ };
struct i2c_client *client = data->client;
- long val;
int err;
- int temp;
- err = kstrtol(buf, 10, &val);
- if (err < 0)
- return err;
+ /* +16 degrees offset for temp2 for the LM99 */
+ if (data->kind == lm99 && index == 3)
+ val -= 16000;
- mutex_lock(&data->update_lock);
if (data->kind == adt7461 || data->kind == tmp451)
- temp = temp_from_u8_adt7461(data, data->temp8[LOCAL_CRIT]);
+ data->temp8[index] = temp_to_u8_adt7461(data, val);
else if (data->kind == max6646)
- temp = temp_from_u8(data->temp8[LOCAL_CRIT]);
+ data->temp8[index] = temp_to_u8(val);
else
- temp = temp_from_s8(data->temp8[LOCAL_CRIT]);
+ data->temp8[index] = temp_to_s8(val);
- data->temp_hyst = hyst_to_reg(temp - val);
- i2c_smbus_write_byte_data(client, LM90_REG_W_TCRIT_HYST,
- data->temp_hyst);
- mutex_unlock(&data->update_lock);
- return count;
-}
-
-static ssize_t show_alarms(struct device *dev, struct device_attribute *dummy,
- char *buf)
-{
- struct lm90_data *data = lm90_update_device(dev);
-
- if (IS_ERR(data))
- return PTR_ERR(data);
+ lm90_select_remote_channel(client, data, index >= 6);
+ err = i2c_smbus_write_byte_data(client, reg[index], data->temp8[index]);
+ lm90_select_remote_channel(client, data, 0);
- return sprintf(buf, "%d\n", data->alarms);
+ return err;
}
-static ssize_t show_alarm(struct device *dev, struct device_attribute
- *devattr, char *buf)
+static int lm90_get_temphyst(struct lm90_data *data, int index)
{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- struct lm90_data *data = lm90_update_device(dev);
- int bitnr = attr->index;
-
- if (IS_ERR(data))
- return PTR_ERR(data);
+ int temp;
- return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1);
-}
+ if (data->kind == adt7461 || data->kind == tmp451)
+ temp = temp_from_u8_adt7461(data, data->temp8[index]);
+ else if (data->kind == max6646)
+ temp = temp_from_u8(data->temp8[index]);
+ else
+ temp = temp_from_s8(data->temp8[index]);
-static ssize_t show_update_interval(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct lm90_data *data = dev_get_drvdata(dev);
+ /* +16 degrees offset for temp2 for the LM99 */
+ if (data->kind == lm99 && index == 3)
+ temp += 16000;
- return sprintf(buf, "%u\n", data->update_interval);
+ return temp - temp_from_s8(data->temp_hyst);
}
-static ssize_t set_update_interval(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static int lm90_set_temphyst(struct lm90_data *data, long val)
{
- struct lm90_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
- unsigned long val;
+ int temp;
int err;
- err = kstrtoul(buf, 10, &val);
- if (err)
- return err;
-
- mutex_lock(&data->update_lock);
- lm90_set_convrate(client, data, clamp_val(val, 0, 100000));
- mutex_unlock(&data->update_lock);
+ if (data->kind == adt7461 || data->kind == tmp451)
+ temp = temp_from_u8_adt7461(data, data->temp8[LOCAL_CRIT]);
+ else if (data->kind == max6646)
+ temp = temp_from_u8(data->temp8[LOCAL_CRIT]);
+ else
+ temp = temp_from_s8(data->temp8[LOCAL_CRIT]);
- return count;
+ data->temp_hyst = hyst_to_reg(temp - val);
+ err = i2c_smbus_write_byte_data(client, LM90_REG_W_TCRIT_HYST,
+ data->temp_hyst);
+ return err;
}
-static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp11, NULL,
- 0, LOCAL_TEMP);
-static SENSOR_DEVICE_ATTR_2(temp2_input, S_IRUGO, show_temp11, NULL,
- 0, REMOTE_TEMP);
-static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp8,
- set_temp8, LOCAL_LOW);
-static SENSOR_DEVICE_ATTR_2(temp2_min, S_IWUSR | S_IRUGO, show_temp11,
- set_temp11, 0, REMOTE_LOW);
-static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp8,
- set_temp8, LOCAL_HIGH);
-static SENSOR_DEVICE_ATTR_2(temp2_max, S_IWUSR | S_IRUGO, show_temp11,
- set_temp11, 1, REMOTE_HIGH);
-static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp8,
- set_temp8, LOCAL_CRIT);
-static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp8,
- set_temp8, REMOTE_CRIT);
-static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temphyst,
- set_temphyst, LOCAL_CRIT);
-static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_temphyst, NULL,
- REMOTE_CRIT);
-static SENSOR_DEVICE_ATTR_2(temp2_offset, S_IWUSR | S_IRUGO, show_temp11,
- set_temp11, 2, REMOTE_OFFSET);
-
-/* Individual alarm files */
-static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, 0);
-static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL, 2);
-static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_alarm, NULL, 3);
-static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 4);
-static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL, 5);
-static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 6);
-/* Raw alarm file for compatibility */
-static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
-
-static DEVICE_ATTR(update_interval, S_IRUGO | S_IWUSR, show_update_interval,
- set_update_interval);
-
-static struct attribute *lm90_attributes[] = {
- &sensor_dev_attr_temp1_input.dev_attr.attr,
- &sensor_dev_attr_temp2_input.dev_attr.attr,
- &sensor_dev_attr_temp1_min.dev_attr.attr,
- &sensor_dev_attr_temp2_min.dev_attr.attr,
- &sensor_dev_attr_temp1_max.dev_attr.attr,
- &sensor_dev_attr_temp2_max.dev_attr.attr,
- &sensor_dev_attr_temp1_crit.dev_attr.attr,
- &sensor_dev_attr_temp2_crit.dev_attr.attr,
- &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
- &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr,
-
- &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
- &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
- &sensor_dev_attr_temp2_fault.dev_attr.attr,
- &sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
- &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
- &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
- &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
- &dev_attr_alarms.attr,
- &dev_attr_update_interval.attr,
- NULL
+static const u8 lm90_temp_index[3] = {
+ LOCAL_TEMP, REMOTE_TEMP, REMOTE2_TEMP
};
-static const struct attribute_group lm90_group = {
- .attrs = lm90_attributes,
+static const u8 lm90_temp_min_index[3] = {
+ LOCAL_LOW, REMOTE_LOW, REMOTE2_LOW
};
-static struct attribute *lm90_temp2_offset_attributes[] = {
- &sensor_dev_attr_temp2_offset.dev_attr.attr,
- NULL
+static const u8 lm90_temp_max_index[3] = {
+ LOCAL_HIGH, REMOTE_HIGH, REMOTE2_HIGH
};
-static const struct attribute_group lm90_temp2_offset_group = {
- .attrs = lm90_temp2_offset_attributes,
+static const u8 lm90_temp_crit_index[3] = {
+ LOCAL_CRIT, REMOTE_CRIT, REMOTE2_CRIT
};
-/*
- * Additional attributes for devices with emergency sensors
- */
-static SENSOR_DEVICE_ATTR(temp1_emergency, S_IWUSR | S_IRUGO, show_temp8,
- set_temp8, LOCAL_EMERG);
-static SENSOR_DEVICE_ATTR(temp2_emergency, S_IWUSR | S_IRUGO, show_temp8,
- set_temp8, REMOTE_EMERG);
-static SENSOR_DEVICE_ATTR(temp1_emergency_hyst, S_IRUGO, show_temphyst,
- NULL, LOCAL_EMERG);
-static SENSOR_DEVICE_ATTR(temp2_emergency_hyst, S_IRUGO, show_temphyst,
- NULL, REMOTE_EMERG);
-
-static struct attribute *lm90_emergency_attributes[] = {
- &sensor_dev_attr_temp1_emergency.dev_attr.attr,
- &sensor_dev_attr_temp2_emergency.dev_attr.attr,
- &sensor_dev_attr_temp1_emergency_hyst.dev_attr.attr,
- &sensor_dev_attr_temp2_emergency_hyst.dev_attr.attr,
- NULL
+static const u8 lm90_temp_emerg_index[3] = {
+ LOCAL_EMERG, REMOTE_EMERG, REMOTE2_EMERG
};
-static const struct attribute_group lm90_emergency_group = {
- .attrs = lm90_emergency_attributes,
-};
+static const u8 lm90_min_alarm_bits[3] = { 5, 3, 11 };
+static const u8 lm90_max_alarm_bits[3] = { 0, 4, 12 };
+static const u8 lm90_crit_alarm_bits[3] = { 0, 1, 9 };
+static const u8 lm90_emergency_alarm_bits[3] = { 15, 13, 14 };
+static const u8 lm90_fault_bits[3] = { 0, 2, 10 };
-static SENSOR_DEVICE_ATTR(temp1_emergency_alarm, S_IRUGO, show_alarm, NULL, 15);
-static SENSOR_DEVICE_ATTR(temp2_emergency_alarm, S_IRUGO, show_alarm, NULL, 13);
+static int lm90_temp_read(struct device *dev, u32 attr, int channel, long *val)
+{
+ struct lm90_data *data = dev_get_drvdata(dev);
+ int err;
-static struct attribute *lm90_emergency_alarm_attributes[] = {
- &sensor_dev_attr_temp1_emergency_alarm.dev_attr.attr,
- &sensor_dev_attr_temp2_emergency_alarm.dev_attr.attr,
- NULL
-};
+ mutex_lock(&data->update_lock);
+ err = lm90_update_device(dev);
+ mutex_unlock(&data->update_lock);
+ if (err)
+ return err;
-static const struct attribute_group lm90_emergency_alarm_group = {
- .attrs = lm90_emergency_alarm_attributes,
-};
+ switch (attr) {
+ case hwmon_temp_input:
+ *val = lm90_get_temp11(data, lm90_temp_index[channel]);
+ break;
+ case hwmon_temp_min_alarm:
+ *val = (data->alarms >> lm90_min_alarm_bits[channel]) & 1;
+ break;
+ case hwmon_temp_max_alarm:
+ *val = (data->alarms >> lm90_max_alarm_bits[channel]) & 1;
+ break;
+ case hwmon_temp_crit_alarm:
+ *val = (data->alarms >> lm90_crit_alarm_bits[channel]) & 1;
+ break;
+ case hwmon_temp_emergency_alarm:
+ *val = (data->alarms >> lm90_emergency_alarm_bits[channel]) & 1;
+ break;
+ case hwmon_temp_fault:
+ *val = (data->alarms >> lm90_fault_bits[channel]) & 1;
+ break;
+ case hwmon_temp_min:
+ if (channel == 0)
+ *val = lm90_get_temp8(data,
+ lm90_temp_min_index[channel]);
+ else
+ *val = lm90_get_temp11(data,
+ lm90_temp_min_index[channel]);
+ break;
+ case hwmon_temp_max:
+ if (channel == 0)
+ *val = lm90_get_temp8(data,
+ lm90_temp_max_index[channel]);
+ else
+ *val = lm90_get_temp11(data,
+ lm90_temp_max_index[channel]);
+ break;
+ case hwmon_temp_crit:
+ *val = lm90_get_temp8(data, lm90_temp_crit_index[channel]);
+ break;
+ case hwmon_temp_crit_hyst:
+ *val = lm90_get_temphyst(data, lm90_temp_crit_index[channel]);
+ break;
+ case hwmon_temp_emergency:
+ *val = lm90_get_temp8(data, lm90_temp_emerg_index[channel]);
+ break;
+ case hwmon_temp_emergency_hyst:
+ *val = lm90_get_temphyst(data, lm90_temp_emerg_index[channel]);
+ break;
+ case hwmon_temp_offset:
+ *val = lm90_get_temp11(data, REMOTE_OFFSET);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
-/*
- * Additional attributes for devices with 3 temperature sensors
- */
-static SENSOR_DEVICE_ATTR_2(temp3_input, S_IRUGO, show_temp11, NULL,
- 0, REMOTE2_TEMP);
-static SENSOR_DEVICE_ATTR_2(temp3_min, S_IWUSR | S_IRUGO, show_temp11,
- set_temp11, 3, REMOTE2_LOW);
-static SENSOR_DEVICE_ATTR_2(temp3_max, S_IWUSR | S_IRUGO, show_temp11,
- set_temp11, 4, REMOTE2_HIGH);
-static SENSOR_DEVICE_ATTR(temp3_crit, S_IWUSR | S_IRUGO, show_temp8,
- set_temp8, REMOTE2_CRIT);
-static SENSOR_DEVICE_ATTR(temp3_crit_hyst, S_IRUGO, show_temphyst, NULL,
- REMOTE2_CRIT);
-static SENSOR_DEVICE_ATTR(temp3_emergency, S_IWUSR | S_IRUGO, show_temp8,
- set_temp8, REMOTE2_EMERG);
-static SENSOR_DEVICE_ATTR(temp3_emergency_hyst, S_IRUGO, show_temphyst,
- NULL, REMOTE2_EMERG);
-
-static SENSOR_DEVICE_ATTR(temp3_crit_alarm, S_IRUGO, show_alarm, NULL, 9);
-static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_alarm, NULL, 10);
-static SENSOR_DEVICE_ATTR(temp3_min_alarm, S_IRUGO, show_alarm, NULL, 11);
-static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, show_alarm, NULL, 12);
-static SENSOR_DEVICE_ATTR(temp3_emergency_alarm, S_IRUGO, show_alarm, NULL, 14);
-
-static struct attribute *lm90_temp3_attributes[] = {
- &sensor_dev_attr_temp3_input.dev_attr.attr,
- &sensor_dev_attr_temp3_min.dev_attr.attr,
- &sensor_dev_attr_temp3_max.dev_attr.attr,
- &sensor_dev_attr_temp3_crit.dev_attr.attr,
- &sensor_dev_attr_temp3_crit_hyst.dev_attr.attr,
- &sensor_dev_attr_temp3_emergency.dev_attr.attr,
- &sensor_dev_attr_temp3_emergency_hyst.dev_attr.attr,
-
- &sensor_dev_attr_temp3_fault.dev_attr.attr,
- &sensor_dev_attr_temp3_min_alarm.dev_attr.attr,
- &sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
- &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr,
- &sensor_dev_attr_temp3_emergency_alarm.dev_attr.attr,
- NULL
-};
+static int lm90_temp_write(struct device *dev, u32 attr, int channel, long val)
+{
+ struct lm90_data *data = dev_get_drvdata(dev);
+ int err;
-static const struct attribute_group lm90_temp3_group = {
- .attrs = lm90_temp3_attributes,
-};
+ mutex_lock(&data->update_lock);
-/* pec used for ADM1032 only */
-static ssize_t show_pec(struct device *dev, struct device_attribute *dummy,
- char *buf)
+ err = lm90_update_device(dev);
+ if (err)
+ goto error;
+
+ switch (attr) {
+ case hwmon_temp_min:
+ if (channel == 0)
+ err = lm90_set_temp8(data,
+ lm90_temp_min_index[channel],
+ val);
+ else
+ err = lm90_set_temp11(data,
+ lm90_temp_min_index[channel],
+ val);
+ break;
+ case hwmon_temp_max:
+ if (channel == 0)
+ err = lm90_set_temp8(data,
+ lm90_temp_max_index[channel],
+ val);
+ else
+ err = lm90_set_temp11(data,
+ lm90_temp_max_index[channel],
+ val);
+ break;
+ case hwmon_temp_crit:
+ err = lm90_set_temp8(data, lm90_temp_crit_index[channel], val);
+ break;
+ case hwmon_temp_crit_hyst:
+ err = lm90_set_temphyst(data, val);
+ break;
+ case hwmon_temp_emergency:
+ err = lm90_set_temp8(data, lm90_temp_emerg_index[channel], val);
+ break;
+ case hwmon_temp_offset:
+ err = lm90_set_temp11(data, REMOTE_OFFSET, val);
+ break;
+ default:
+ err = -EOPNOTSUPP;
+ break;
+ }
+error:
+ mutex_unlock(&data->update_lock);
+
+ return err;
+}
+
+static umode_t lm90_temp_is_visible(const void *data, u32 attr, int channel)
{
- struct i2c_client *client = to_i2c_client(dev);
- return sprintf(buf, "%d\n", !!(client->flags & I2C_CLIENT_PEC));
+ switch (attr) {
+ case hwmon_temp_input:
+ case hwmon_temp_min_alarm:
+ case hwmon_temp_max_alarm:
+ case hwmon_temp_crit_alarm:
+ case hwmon_temp_emergency_alarm:
+ case hwmon_temp_emergency_hyst:
+ case hwmon_temp_fault:
+ return S_IRUGO;
+ case hwmon_temp_min:
+ case hwmon_temp_max:
+ case hwmon_temp_crit:
+ case hwmon_temp_emergency:
+ case hwmon_temp_offset:
+ return S_IRUGO | S_IWUSR;
+ case hwmon_temp_crit_hyst:
+ if (channel == 0)
+ return S_IRUGO | S_IWUSR;
+ return S_IRUGO;
+ default:
+ return 0;
+ }
}
-static ssize_t set_pec(struct device *dev, struct device_attribute *dummy,
- const char *buf, size_t count)
+static int lm90_chip_read(struct device *dev, u32 attr, int channel, long *val)
{
- struct i2c_client *client = to_i2c_client(dev);
- long val;
+ struct lm90_data *data = dev_get_drvdata(dev);
int err;
- err = kstrtol(buf, 10, &val);
- if (err < 0)
+ mutex_lock(&data->update_lock);
+ err = lm90_update_device(dev);
+ mutex_unlock(&data->update_lock);
+ if (err)
return err;
- switch (val) {
- case 0:
- client->flags &= ~I2C_CLIENT_PEC;
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ *val = data->update_interval;
break;
- case 1:
- client->flags |= I2C_CLIENT_PEC;
+ case hwmon_chip_alarms:
+ *val = data->alarms;
break;
default:
- return -EINVAL;
+ return -EOPNOTSUPP;
}
- return count;
+ return 0;
}
-static DEVICE_ATTR(pec, S_IWUSR | S_IRUGO, show_pec, set_pec);
+static int lm90_chip_write(struct device *dev, u32 attr, int channel, long val)
+{
+ struct lm90_data *data = dev_get_drvdata(dev);
+ struct i2c_client *client = data->client;
+ int err;
-/*
- * Real code
- */
+ mutex_lock(&data->update_lock);
+
+ err = lm90_update_device(dev);
+ if (err)
+ goto error;
+
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ err = lm90_set_convrate(client, data,
+ clamp_val(val, 0, 100000));
+ break;
+ default:
+ err = -EOPNOTSUPP;
+ break;
+ }
+error:
+ mutex_unlock(&data->update_lock);
+
+ return err;
+}
+
+static umode_t lm90_chip_is_visible(const void *data, u32 attr, int channel)
+{
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ return S_IRUGO | S_IWUSR;
+ case hwmon_chip_alarms:
+ return S_IRUGO;
+ default:
+ return 0;
+ }
+}
+
+static int lm90_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ switch (type) {
+ case hwmon_chip:
+ return lm90_chip_read(dev, attr, channel, val);
+ case hwmon_temp:
+ return lm90_temp_read(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int lm90_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ switch (type) {
+ case hwmon_chip:
+ return lm90_chip_write(dev, attr, channel, val);
+ case hwmon_temp:
+ return lm90_temp_write(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static umode_t lm90_is_visible(const void *data, enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ switch (type) {
+ case hwmon_chip:
+ return lm90_chip_is_visible(data, attr, channel);
+ case hwmon_temp:
+ return lm90_temp_is_visible(data, attr, channel);
+ default:
+ return 0;
+ }
+}
/* Return 0 if detection is successful, -ENODEV otherwise */
static int lm90_detect(struct i2c_client *client,
@@ -1617,15 +1626,32 @@ static void lm90_regulator_disable(void *regulator)
regulator_disable(regulator);
}
+static const u32 lm90_chip_config[] = {
+ HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL | HWMON_C_ALARMS,
+ 0
+};
+
+static const struct hwmon_channel_info lm90_chip_info = {
+ .type = hwmon_chip,
+ .config = lm90_chip_config,
+};
+
+
+static const struct hwmon_ops lm90_ops = {
+ .is_visible = lm90_is_visible,
+ .read = lm90_read,
+ .write = lm90_write,
+};
+
static int lm90_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct i2c_adapter *adapter = to_i2c_adapter(dev->parent);
- struct lm90_data *data;
+ struct hwmon_channel_info *info;
struct regulator *regulator;
struct device *hwmon_dev;
- int groups = 0;
+ struct lm90_data *data;
int err;
regulator = devm_regulator_get(dev, "vcc");
@@ -1665,6 +1691,49 @@ static int lm90_probe(struct i2c_client *client,
/* Set chip capabilities */
data->flags = lm90_params[data->kind].flags;
+
+ data->chip.ops = &lm90_ops;
+ data->chip.info = data->info;
+
+ data->info[0] = &lm90_chip_info;
+ data->info[1] = &data->temp_info;
+
+ info = &data->temp_info;
+ info->type = hwmon_temp;
+ info->config = data->channel_config;
+
+ data->channel_config[0] = HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
+ HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_MIN_ALARM |
+ HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM;
+ data->channel_config[1] = HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
+ HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_MIN_ALARM |
+ HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | HWMON_T_FAULT;
+
+ if (data->flags & LM90_HAVE_OFFSET)
+ data->channel_config[1] |= HWMON_T_OFFSET;
+
+ if (data->flags & LM90_HAVE_EMERGENCY) {
+ data->channel_config[0] |= HWMON_T_EMERGENCY |
+ HWMON_T_EMERGENCY_HYST;
+ data->channel_config[1] |= HWMON_T_EMERGENCY |
+ HWMON_T_EMERGENCY_HYST;
+ }
+
+ if (data->flags & LM90_HAVE_EMERGENCY_ALARM) {
+ data->channel_config[0] |= HWMON_T_EMERGENCY_ALARM;
+ data->channel_config[1] |= HWMON_T_EMERGENCY_ALARM;
+ }
+
+ if (data->flags & LM90_HAVE_TEMP3) {
+ data->channel_config[2] = HWMON_T_INPUT |
+ HWMON_T_MIN | HWMON_T_MAX |
+ HWMON_T_CRIT | HWMON_T_CRIT_HYST |
+ HWMON_T_EMERGENCY | HWMON_T_EMERGENCY_HYST |
+ HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM |
+ HWMON_T_CRIT_ALARM | HWMON_T_EMERGENCY_ALARM |
+ HWMON_T_FAULT;
+ }
+
data->reg_local_ext = lm90_params[data->kind].reg_local_ext;
/* Set maximum conversion rate */
@@ -1677,21 +1746,10 @@ static int lm90_probe(struct i2c_client *client,
return err;
}
- /* Register sysfs hooks */
- data->groups[groups++] = &lm90_group;
-
- if (data->flags & LM90_HAVE_OFFSET)
- data->groups[groups++] = &lm90_temp2_offset_group;
-
- if (data->flags & LM90_HAVE_EMERGENCY)
- data->groups[groups++] = &lm90_emergency_group;
-
- if (data->flags & LM90_HAVE_EMERGENCY_ALARM)
- data->groups[groups++] = &lm90_emergency_alarm_group;
-
- if (data->flags & LM90_HAVE_TEMP3)
- data->groups[groups++] = &lm90_temp3_group;
-
+ /*
+ * The 'pec' attribute is attached to the i2c device and thus created
+ * separately.
+ */
if (client->flags & I2C_CLIENT_PEC) {
err = device_create_file(dev, &dev_attr_pec);
if (err)
@@ -1701,8 +1759,9 @@ static int lm90_probe(struct i2c_client *client,
return err;
}
- hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
- data, data->groups);
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
+ data, &data->chip,
+ NULL);
if (IS_ERR(hwmon_dev))
return PTR_ERR(hwmon_dev);
diff --git a/drivers/hwmon/lm95241.c b/drivers/hwmon/lm95241.c
index cdf19adaec79..8c573e6e9726 100644
--- a/drivers/hwmon/lm95241.c
+++ b/drivers/hwmon/lm95241.c
@@ -15,22 +15,17 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include <linux/module.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
#include <linux/init.h>
-#include <linux/slab.h>
#include <linux/jiffies.h>
-#include <linux/i2c.h>
#include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
-#include <linux/err.h>
+#include <linux/module.h>
#include <linux/mutex.h>
-#include <linux/sysfs.h>
+#include <linux/slab.h>
#define DEVNAME "lm95241"
@@ -54,26 +49,25 @@ static const unsigned short normal_i2c[] = {
#define LM95241_REG_RW_REMOTE_MODEL 0x30
/* LM95241 specific bitfields */
-#define CFG_STOP 0x40
-#define CFG_CR0076 0x00
-#define CFG_CR0182 0x10
-#define CFG_CR1000 0x20
-#define CFG_CR2700 0x30
-#define R1MS_SHIFT 0
-#define R2MS_SHIFT 2
-#define R1MS_MASK (0x01 << (R1MS_SHIFT))
-#define R2MS_MASK (0x01 << (R2MS_SHIFT))
-#define R1DF_SHIFT 1
-#define R2DF_SHIFT 2
-#define R1DF_MASK (0x01 << (R1DF_SHIFT))
-#define R2DF_MASK (0x01 << (R2DF_SHIFT))
-#define R1FE_MASK 0x01
-#define R2FE_MASK 0x05
-#define TT1_SHIFT 0
-#define TT2_SHIFT 4
-#define TT_OFF 0
-#define TT_ON 1
-#define TT_MASK 7
+#define CFG_STOP BIT(6)
+#define CFG_CR0076 0x00
+#define CFG_CR0182 BIT(4)
+#define CFG_CR1000 BIT(5)
+#define CFG_CR2700 (BIT(4) | BIT(5))
+#define CFG_CRMASK (BIT(4) | BIT(5))
+#define R1MS_MASK BIT(0)
+#define R2MS_MASK BIT(2)
+#define R1DF_MASK BIT(1)
+#define R2DF_MASK BIT(2)
+#define R1FE_MASK BIT(0)
+#define R2FE_MASK BIT(2)
+#define R1DM BIT(0)
+#define R2DM BIT(1)
+#define TT1_SHIFT 0
+#define TT2_SHIFT 4
+#define TT_OFF 0
+#define TT_ON 1
+#define TT_MASK 7
#define NATSEMI_MAN_ID 0x01
#define LM95231_CHIP_ID 0xA1
#define LM95241_CHIP_ID 0xA4
@@ -91,11 +85,12 @@ static const u8 lm95241_reg_address[] = {
struct lm95241_data {
struct i2c_client *client;
struct mutex update_lock;
- unsigned long last_updated, interval; /* in jiffies */
+ unsigned long last_updated; /* in jiffies */
+ unsigned long interval; /* in milli-seconds */
char valid; /* zero until following fields are valid */
/* registers values */
u8 temp[ARRAY_SIZE(lm95241_reg_address)];
- u8 config, model, trutherm;
+ u8 status, config, model, trutherm;
};
/* Conversions */
@@ -118,7 +113,8 @@ static struct lm95241_data *lm95241_update_device(struct device *dev)
mutex_lock(&data->update_lock);
- if (time_after(jiffies, data->last_updated + data->interval) ||
+ if (time_after(jiffies, data->last_updated
+ + msecs_to_jiffies(data->interval)) ||
!data->valid) {
int i;
@@ -127,6 +123,9 @@ static struct lm95241_data *lm95241_update_device(struct device *dev)
data->temp[i]
= i2c_smbus_read_byte_data(client,
lm95241_reg_address[i]);
+
+ data->status = i2c_smbus_read_byte_data(client,
+ LM95241_REG_R_STATUS);
data->last_updated = jiffies;
data->valid = 1;
}
@@ -136,197 +135,241 @@ static struct lm95241_data *lm95241_update_device(struct device *dev)
return data;
}
-/* Sysfs stuff */
-static ssize_t show_input(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct lm95241_data *data = lm95241_update_device(dev);
- int index = to_sensor_dev_attr(attr)->index;
-
- return snprintf(buf, PAGE_SIZE - 1, "%d\n",
- index == 0 || (data->config & (1 << (index / 2))) ?
- temp_from_reg_signed(data->temp[index], data->temp[index + 1]) :
- temp_from_reg_unsigned(data->temp[index],
- data->temp[index + 1]));
-}
-
-static ssize_t show_type(struct device *dev, struct device_attribute *attr,
- char *buf)
+static int lm95241_read_chip(struct device *dev, u32 attr, int channel,
+ long *val)
{
struct lm95241_data *data = dev_get_drvdata(dev);
- return snprintf(buf, PAGE_SIZE - 1,
- data->model & to_sensor_dev_attr(attr)->index ? "1\n" : "2\n");
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ *val = data->interval;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static ssize_t set_type(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static int lm95241_read_temp(struct device *dev, u32 attr, int channel,
+ long *val)
{
- struct lm95241_data *data = dev_get_drvdata(dev);
- struct i2c_client *client = data->client;
- unsigned long val;
- int shift;
- u8 mask = to_sensor_dev_attr(attr)->index;
-
- if (kstrtoul(buf, 10, &val) < 0)
- return -EINVAL;
- if (val != 1 && val != 2)
- return -EINVAL;
-
- shift = mask == R1MS_MASK ? TT1_SHIFT : TT2_SHIFT;
-
- mutex_lock(&data->update_lock);
+ struct lm95241_data *data = lm95241_update_device(dev);
- data->trutherm &= ~(TT_MASK << shift);
- if (val == 1) {
- data->model |= mask;
- data->trutherm |= (TT_ON << shift);
- } else {
- data->model &= ~mask;
- data->trutherm |= (TT_OFF << shift);
+ switch (attr) {
+ case hwmon_temp_input:
+ if (!channel || (data->config & BIT(channel - 1)))
+ *val = temp_from_reg_signed(data->temp[channel * 2],
+ data->temp[channel * 2 + 1]);
+ else
+ *val = temp_from_reg_unsigned(data->temp[channel * 2],
+ data->temp[channel * 2 + 1]);
+ return 0;
+ case hwmon_temp_min:
+ if (channel == 1)
+ *val = (data->config & R1DF_MASK) ? -128000 : 0;
+ else
+ *val = (data->config & R2DF_MASK) ? -128000 : 0;
+ return 0;
+ case hwmon_temp_max:
+ if (channel == 1)
+ *val = (data->config & R1DF_MASK) ? 127875 : 255875;
+ else
+ *val = (data->config & R2DF_MASK) ? 127875 : 255875;
+ return 0;
+ case hwmon_temp_type:
+ if (channel == 1)
+ *val = (data->model & R1MS_MASK) ? 1 : 2;
+ else
+ *val = (data->model & R2MS_MASK) ? 1 : 2;
+ return 0;
+ case hwmon_temp_fault:
+ if (channel == 1)
+ *val = !!(data->status & R1DM);
+ else
+ *val = !!(data->status & R2DM);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
}
- data->valid = 0;
-
- i2c_smbus_write_byte_data(client, LM95241_REG_RW_REMOTE_MODEL,
- data->model);
- i2c_smbus_write_byte_data(client, LM95241_REG_RW_TRUTHERM,
- data->trutherm);
-
- mutex_unlock(&data->update_lock);
-
- return count;
}
-static ssize_t show_min(struct device *dev, struct device_attribute *attr,
- char *buf)
+static int lm95241_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
{
- struct lm95241_data *data = dev_get_drvdata(dev);
-
- return snprintf(buf, PAGE_SIZE - 1,
- data->config & to_sensor_dev_attr(attr)->index ?
- "-127000\n" : "0\n");
+ switch (type) {
+ case hwmon_chip:
+ return lm95241_read_chip(dev, attr, channel, val);
+ case hwmon_temp:
+ return lm95241_read_temp(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static ssize_t set_min(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static int lm95241_write_chip(struct device *dev, u32 attr, int channel,
+ long val)
{
struct lm95241_data *data = dev_get_drvdata(dev);
- long val;
-
- if (kstrtol(buf, 10, &val) < 0)
- return -EINVAL;
- if (val < -128000)
- return -EINVAL;
+ int convrate;
+ u8 config;
+ int ret;
mutex_lock(&data->update_lock);
- if (val < 0)
- data->config |= to_sensor_dev_attr(attr)->index;
- else
- data->config &= ~to_sensor_dev_attr(attr)->index;
- data->valid = 0;
-
- i2c_smbus_write_byte_data(data->client, LM95241_REG_RW_CONFIG,
- data->config);
-
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ config = data->config & ~CFG_CRMASK;
+ if (val < 130) {
+ convrate = 76;
+ config |= CFG_CR0076;
+ } else if (val < 590) {
+ convrate = 182;
+ config |= CFG_CR0182;
+ } else if (val < 1850) {
+ convrate = 1000;
+ config |= CFG_CR1000;
+ } else {
+ convrate = 2700;
+ config |= CFG_CR2700;
+ }
+ data->interval = convrate;
+ data->config = config;
+ ret = i2c_smbus_write_byte_data(data->client,
+ LM95241_REG_RW_CONFIG, config);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
mutex_unlock(&data->update_lock);
-
- return count;
+ return ret;
}
-static ssize_t show_max(struct device *dev, struct device_attribute *attr,
- char *buf)
+static int lm95241_write_temp(struct device *dev, u32 attr, int channel,
+ long val)
{
struct lm95241_data *data = dev_get_drvdata(dev);
-
- return snprintf(buf, PAGE_SIZE - 1,
- data->config & to_sensor_dev_attr(attr)->index ?
- "127000\n" : "255000\n");
-}
-
-static ssize_t set_max(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct lm95241_data *data = dev_get_drvdata(dev);
- long val;
-
- if (kstrtol(buf, 10, &val) < 0)
- return -EINVAL;
- if (val >= 256000)
- return -EINVAL;
+ struct i2c_client *client = data->client;
+ int ret;
mutex_lock(&data->update_lock);
- if (val <= 127000)
- data->config |= to_sensor_dev_attr(attr)->index;
- else
- data->config &= ~to_sensor_dev_attr(attr)->index;
- data->valid = 0;
-
- i2c_smbus_write_byte_data(data->client, LM95241_REG_RW_CONFIG,
- data->config);
+ switch (attr) {
+ case hwmon_temp_min:
+ if (channel == 1) {
+ if (val < 0)
+ data->config |= R1DF_MASK;
+ else
+ data->config &= ~R1DF_MASK;
+ } else {
+ if (val < 0)
+ data->config |= R2DF_MASK;
+ else
+ data->config &= ~R2DF_MASK;
+ }
+ data->valid = 0;
+ ret = i2c_smbus_write_byte_data(client, LM95241_REG_RW_CONFIG,
+ data->config);
+ break;
+ case hwmon_temp_max:
+ if (channel == 1) {
+ if (val <= 127875)
+ data->config |= R1DF_MASK;
+ else
+ data->config &= ~R1DF_MASK;
+ } else {
+ if (val <= 127875)
+ data->config |= R2DF_MASK;
+ else
+ data->config &= ~R2DF_MASK;
+ }
+ data->valid = 0;
+ ret = i2c_smbus_write_byte_data(client, LM95241_REG_RW_CONFIG,
+ data->config);
+ break;
+ case hwmon_temp_type:
+ if (val != 1 && val != 2) {
+ ret = -EINVAL;
+ break;
+ }
+ if (channel == 1) {
+ data->trutherm &= ~(TT_MASK << TT1_SHIFT);
+ if (val == 1) {
+ data->model |= R1MS_MASK;
+ data->trutherm |= (TT_ON << TT1_SHIFT);
+ } else {
+ data->model &= ~R1MS_MASK;
+ data->trutherm |= (TT_OFF << TT1_SHIFT);
+ }
+ } else {
+ data->trutherm &= ~(TT_MASK << TT2_SHIFT);
+ if (val == 1) {
+ data->model |= R2MS_MASK;
+ data->trutherm |= (TT_ON << TT2_SHIFT);
+ } else {
+ data->model &= ~R2MS_MASK;
+ data->trutherm |= (TT_OFF << TT2_SHIFT);
+ }
+ }
+ ret = i2c_smbus_write_byte_data(client,
+ LM95241_REG_RW_REMOTE_MODEL,
+ data->model);
+ if (ret < 0)
+ break;
+ ret = i2c_smbus_write_byte_data(client, LM95241_REG_RW_TRUTHERM,
+ data->trutherm);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
mutex_unlock(&data->update_lock);
- return count;
+ return ret;
}
-static ssize_t show_interval(struct device *dev, struct device_attribute *attr,
- char *buf)
+static int lm95241_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
{
- struct lm95241_data *data = lm95241_update_device(dev);
-
- return snprintf(buf, PAGE_SIZE - 1, "%lu\n", 1000 * data->interval
- / HZ);
+ switch (type) {
+ case hwmon_chip:
+ return lm95241_write_chip(dev, attr, channel, val);
+ case hwmon_temp:
+ return lm95241_write_temp(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static ssize_t set_interval(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static umode_t lm95241_is_visible(const void *data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
{
- struct lm95241_data *data = dev_get_drvdata(dev);
- unsigned long val;
-
- if (kstrtoul(buf, 10, &val) < 0)
- return -EINVAL;
-
- data->interval = val * HZ / 1000;
-
- return count;
+ switch (type) {
+ case hwmon_chip:
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ return S_IRUGO | S_IWUSR;
+ }
+ break;
+ case hwmon_temp:
+ switch (attr) {
+ case hwmon_temp_input:
+ return S_IRUGO;
+ case hwmon_temp_fault:
+ return S_IRUGO;
+ case hwmon_temp_min:
+ case hwmon_temp_max:
+ case hwmon_temp_type:
+ return S_IRUGO | S_IWUSR;
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
}
-static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_input, NULL, 0);
-static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_input, NULL, 2);
-static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_input, NULL, 4);
-static SENSOR_DEVICE_ATTR(temp2_type, S_IWUSR | S_IRUGO, show_type, set_type,
- R1MS_MASK);
-static SENSOR_DEVICE_ATTR(temp3_type, S_IWUSR | S_IRUGO, show_type, set_type,
- R2MS_MASK);
-static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_min, set_min,
- R1DF_MASK);
-static SENSOR_DEVICE_ATTR(temp3_min, S_IWUSR | S_IRUGO, show_min, set_min,
- R2DF_MASK);
-static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_max, set_max,
- R1DF_MASK);
-static SENSOR_DEVICE_ATTR(temp3_max, S_IWUSR | S_IRUGO, show_max, set_max,
- R2DF_MASK);
-static DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, show_interval,
- set_interval);
-
-static struct attribute *lm95241_attrs[] = {
- &sensor_dev_attr_temp1_input.dev_attr.attr,
- &sensor_dev_attr_temp2_input.dev_attr.attr,
- &sensor_dev_attr_temp3_input.dev_attr.attr,
- &sensor_dev_attr_temp2_type.dev_attr.attr,
- &sensor_dev_attr_temp3_type.dev_attr.attr,
- &sensor_dev_attr_temp2_min.dev_attr.attr,
- &sensor_dev_attr_temp3_min.dev_attr.attr,
- &sensor_dev_attr_temp2_max.dev_attr.attr,
- &sensor_dev_attr_temp3_max.dev_attr.attr,
- &dev_attr_update_interval.attr,
- NULL
-};
-ATTRIBUTE_GROUPS(lm95241);
-
/* Return 0 if detection is successful, -ENODEV otherwise */
static int lm95241_detect(struct i2c_client *new_client,
struct i2c_board_info *info)
@@ -362,8 +405,8 @@ static int lm95241_detect(struct i2c_client *new_client,
static void lm95241_init_client(struct i2c_client *client,
struct lm95241_data *data)
{
- data->interval = HZ; /* 1 sec default */
- data->config = CFG_CR0076;
+ data->interval = 1000;
+ data->config = CFG_CR1000;
data->trutherm = (TT_OFF << TT1_SHIFT) | (TT_OFF << TT2_SHIFT);
i2c_smbus_write_byte_data(client, LM95241_REG_RW_CONFIG, data->config);
@@ -375,6 +418,47 @@ static void lm95241_init_client(struct i2c_client *client,
data->model);
}
+static const u32 lm95241_chip_config[] = {
+ HWMON_C_UPDATE_INTERVAL,
+ 0
+};
+
+static const struct hwmon_channel_info lm95241_chip = {
+ .type = hwmon_chip,
+ .config = lm95241_chip_config,
+};
+
+static const u32 lm95241_temp_config[] = {
+ HWMON_T_INPUT,
+ HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN | HWMON_T_TYPE |
+ HWMON_T_FAULT,
+ HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN | HWMON_T_TYPE |
+ HWMON_T_FAULT,
+ 0
+};
+
+static const struct hwmon_channel_info lm95241_temp = {
+ .type = hwmon_temp,
+ .config = lm95241_temp_config,
+};
+
+static const struct hwmon_channel_info *lm95241_info[] = {
+ &lm95241_chip,
+ &lm95241_temp,
+ NULL
+};
+
+static const struct hwmon_ops lm95241_hwmon_ops = {
+ .is_visible = lm95241_is_visible,
+ .read = lm95241_read,
+ .write = lm95241_write,
+};
+
+static const struct hwmon_chip_info lm95241_chip_info = {
+ .ops = &lm95241_hwmon_ops,
+ .info = lm95241_info,
+};
+
static int lm95241_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -392,9 +476,10 @@ static int lm95241_probe(struct i2c_client *client,
/* Initialize the LM95241 chip */
lm95241_init_client(client, data);
- hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
data,
- lm95241_groups);
+ &lm95241_chip_info,
+ NULL);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
@@ -420,5 +505,5 @@ static struct i2c_driver lm95241_driver = {
module_i2c_driver(lm95241_driver);
MODULE_AUTHOR("Davide Rizzo <elpa.rizzo@gmail.com>");
-MODULE_DESCRIPTION("LM95241 sensor driver");
+MODULE_DESCRIPTION("LM95231/LM95241 sensor driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/lm95245.c b/drivers/hwmon/lm95245.c
index e7aef4561c83..a3bfd88752ca 100644
--- a/drivers/hwmon/lm95245.c
+++ b/drivers/hwmon/lm95245.c
@@ -15,22 +15,16 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include <linux/module.h>
+#include <linux/err.h>
#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/jiffies.h>
-#include <linux/i2c.h>
#include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
-#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
#include <linux/mutex.h>
-#include <linux/sysfs.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
static const unsigned short normal_i2c[] = {
0x18, 0x19, 0x29, 0x4c, 0x4d, I2C_CLIENT_END };
@@ -89,6 +83,7 @@ static const unsigned short normal_i2c[] = {
#define RATE_CR1000 0x02
#define RATE_CR2500 0x03
+#define STATUS1_ROS 0x10
#define STATUS1_DIODE_FAULT 0x04
#define STATUS1_RTCRIT 0x02
#define STATUS1_LOC 0x01
@@ -112,14 +107,9 @@ static const u8 lm95245_reg_address[] = {
/* Client data (each client gets its own) */
struct lm95245_data {
- struct i2c_client *client;
+ struct regmap *regmap;
struct mutex update_lock;
- unsigned long last_updated; /* in jiffies */
- unsigned long interval; /* in msecs */
- bool valid; /* zero until following fields are valid */
- /* registers values */
- u8 regs[ARRAY_SIZE(lm95245_reg_address)];
- u8 config1, config2;
+ int interval; /* in msecs */
};
/* Conversions */
@@ -135,60 +125,36 @@ static int temp_from_reg_signed(u8 val_h, u8 val_l)
return temp_from_reg_unsigned(val_h, val_l);
}
-static struct lm95245_data *lm95245_update_device(struct device *dev)
+static int lm95245_read_conversion_rate(struct lm95245_data *data)
{
- struct lm95245_data *data = dev_get_drvdata(dev);
- struct i2c_client *client = data->client;
+ unsigned int rate;
+ int ret;
- mutex_lock(&data->update_lock);
-
- if (time_after(jiffies, data->last_updated
- + msecs_to_jiffies(data->interval)) || !data->valid) {
- int i;
-
- for (i = 0; i < ARRAY_SIZE(lm95245_reg_address); i++)
- data->regs[i]
- = i2c_smbus_read_byte_data(client,
- lm95245_reg_address[i]);
- data->last_updated = jiffies;
- data->valid = 1;
- }
-
- mutex_unlock(&data->update_lock);
-
- return data;
-}
-
-static unsigned long lm95245_read_conversion_rate(struct i2c_client *client)
-{
- int rate;
- unsigned long interval;
-
- rate = i2c_smbus_read_byte_data(client, LM95245_REG_RW_CONVERS_RATE);
+ ret = regmap_read(data->regmap, LM95245_REG_RW_CONVERS_RATE, &rate);
+ if (ret < 0)
+ return ret;
switch (rate) {
case RATE_CR0063:
- interval = 63;
+ data->interval = 63;
break;
case RATE_CR0364:
- interval = 364;
+ data->interval = 364;
break;
case RATE_CR1000:
- interval = 1000;
+ data->interval = 1000;
break;
case RATE_CR2500:
default:
- interval = 2500;
+ data->interval = 2500;
break;
}
-
- return interval;
+ return 0;
}
-static unsigned long lm95245_set_conversion_rate(struct i2c_client *client,
- unsigned long interval)
+static int lm95245_set_conversion_rate(struct lm95245_data *data, long interval)
{
- int rate;
+ int ret, rate;
if (interval <= 63) {
interval = 63;
@@ -204,221 +170,289 @@ static unsigned long lm95245_set_conversion_rate(struct i2c_client *client,
rate = RATE_CR2500;
}
- i2c_smbus_write_byte_data(client, LM95245_REG_RW_CONVERS_RATE, rate);
+ ret = regmap_write(data->regmap, LM95245_REG_RW_CONVERS_RATE, rate);
+ if (ret < 0)
+ return ret;
- return interval;
-}
-
-/* Sysfs stuff */
-static ssize_t show_input(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct lm95245_data *data = lm95245_update_device(dev);
- int temp;
- int index = to_sensor_dev_attr(attr)->index;
-
- /*
- * Index 0 (Local temp) is always signed
- * Index 2 (Remote temp) has both signed and unsigned data
- * use signed calculation for remote if signed bit is set
- */
- if (index == 0 || data->regs[index] & 0x80)
- temp = temp_from_reg_signed(data->regs[index],
- data->regs[index + 1]);
- else
- temp = temp_from_reg_unsigned(data->regs[index + 2],
- data->regs[index + 3]);
-
- return snprintf(buf, PAGE_SIZE - 1, "%d\n", temp);
-}
-
-static ssize_t show_limit(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- struct lm95245_data *data = lm95245_update_device(dev);
- int index = to_sensor_dev_attr(attr)->index;
-
- return snprintf(buf, PAGE_SIZE - 1, "%d\n",
- data->regs[index] * 1000);
+ data->interval = interval;
+ return 0;
}
-static ssize_t set_limit(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static int lm95245_read_temp(struct device *dev, u32 attr, int channel,
+ long *val)
{
struct lm95245_data *data = dev_get_drvdata(dev);
- int index = to_sensor_dev_attr(attr)->index;
- struct i2c_client *client = data->client;
- unsigned long val;
-
- if (kstrtoul(buf, 10, &val) < 0)
- return -EINVAL;
-
- val /= 1000;
-
- val = clamp_val(val, 0, (index == 6 ? 127 : 255));
-
- mutex_lock(&data->update_lock);
-
- data->valid = 0;
-
- i2c_smbus_write_byte_data(client, lm95245_reg_address[index], val);
-
- mutex_unlock(&data->update_lock);
-
- return count;
+ struct regmap *regmap = data->regmap;
+ int ret, regl, regh, regvall, regvalh;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ regl = channel ? LM95245_REG_R_REMOTE_TEMPL_S :
+ LM95245_REG_R_LOCAL_TEMPL_S;
+ regh = channel ? LM95245_REG_R_REMOTE_TEMPH_S :
+ LM95245_REG_R_LOCAL_TEMPH_S;
+ ret = regmap_read(regmap, regl, &regvall);
+ if (ret < 0)
+ return ret;
+ ret = regmap_read(regmap, regh, &regvalh);
+ if (ret < 0)
+ return ret;
+ /*
+ * Local temp is always signed.
+ * Remote temp has both signed and unsigned data.
+ * Use signed calculation for remote if signed bit is set
+ * or if reported temperature is below signed limit.
+ */
+ if (!channel || (regvalh & 0x80) || regvalh < 0x7f) {
+ *val = temp_from_reg_signed(regvalh, regvall);
+ return 0;
+ }
+ ret = regmap_read(regmap, LM95245_REG_R_REMOTE_TEMPL_U,
+ &regvall);
+ if (ret < 0)
+ return ret;
+ ret = regmap_read(regmap, LM95245_REG_R_REMOTE_TEMPH_U,
+ &regvalh);
+ if (ret < 0)
+ return ret;
+ *val = temp_from_reg_unsigned(regvalh, regvall);
+ return 0;
+ case hwmon_temp_max:
+ ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OS_LIMIT,
+ &regvalh);
+ if (ret < 0)
+ return ret;
+ *val = regvalh * 1000;
+ return 0;
+ case hwmon_temp_crit:
+ regh = channel ? LM95245_REG_RW_REMOTE_TCRIT_LIMIT :
+ LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT;
+ ret = regmap_read(regmap, regh, &regvalh);
+ if (ret < 0)
+ return ret;
+ *val = regvalh * 1000;
+ return 0;
+ case hwmon_temp_max_hyst:
+ ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OS_LIMIT,
+ &regvalh);
+ if (ret < 0)
+ return ret;
+ ret = regmap_read(regmap, LM95245_REG_RW_COMMON_HYSTERESIS,
+ &regvall);
+ if (ret < 0)
+ return ret;
+ *val = (regvalh - regvall) * 1000;
+ return 0;
+ case hwmon_temp_crit_hyst:
+ regh = channel ? LM95245_REG_RW_REMOTE_TCRIT_LIMIT :
+ LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT;
+ ret = regmap_read(regmap, regh, &regvalh);
+ if (ret < 0)
+ return ret;
+ ret = regmap_read(regmap, LM95245_REG_RW_COMMON_HYSTERESIS,
+ &regvall);
+ if (ret < 0)
+ return ret;
+ *val = (regvalh - regvall) * 1000;
+ return 0;
+ case hwmon_temp_type:
+ ret = regmap_read(regmap, LM95245_REG_RW_CONFIG2, &regvalh);
+ if (ret < 0)
+ return ret;
+ *val = (regvalh & CFG2_REMOTE_TT) ? 1 : 2;
+ return 0;
+ case hwmon_temp_offset:
+ ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OFFL,
+ &regvall);
+ if (ret < 0)
+ return ret;
+ ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OFFH,
+ &regvalh);
+ if (ret < 0)
+ return ret;
+ *val = temp_from_reg_signed(regvalh, regvall);
+ return 0;
+ case hwmon_temp_max_alarm:
+ ret = regmap_read(regmap, LM95245_REG_R_STATUS1, &regvalh);
+ if (ret < 0)
+ return ret;
+ *val = !!(regvalh & STATUS1_ROS);
+ return 0;
+ case hwmon_temp_crit_alarm:
+ ret = regmap_read(regmap, LM95245_REG_R_STATUS1, &regvalh);
+ if (ret < 0)
+ return ret;
+ *val = !!(regvalh & (channel ? STATUS1_RTCRIT : STATUS1_LOC));
+ return 0;
+ case hwmon_temp_fault:
+ ret = regmap_read(regmap, LM95245_REG_R_STATUS1, &regvalh);
+ if (ret < 0)
+ return ret;
+ *val = !!(regvalh & STATUS1_DIODE_FAULT);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static ssize_t show_crit_hyst(struct device *dev, struct device_attribute *attr,
- char *buf)
+static int lm95245_write_temp(struct device *dev, u32 attr, int channel,
+ long val)
{
- struct lm95245_data *data = lm95245_update_device(dev);
- int index = to_sensor_dev_attr(attr)->index;
- int hyst = data->regs[index] - data->regs[8];
-
- return snprintf(buf, PAGE_SIZE - 1, "%d\n", hyst * 1000);
+ struct lm95245_data *data = dev_get_drvdata(dev);
+ struct regmap *regmap = data->regmap;
+ unsigned int regval;
+ int ret, reg;
+
+ switch (attr) {
+ case hwmon_temp_max:
+ val = clamp_val(val / 1000, 0, 255);
+ ret = regmap_write(regmap, LM95245_REG_RW_REMOTE_OS_LIMIT, val);
+ return ret;
+ case hwmon_temp_crit:
+ reg = channel ? LM95245_REG_RW_REMOTE_TCRIT_LIMIT :
+ LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT;
+ val = clamp_val(val / 1000, 0, channel ? 255 : 127);
+ ret = regmap_write(regmap, reg, val);
+ return ret;
+ case hwmon_temp_crit_hyst:
+ mutex_lock(&data->update_lock);
+ ret = regmap_read(regmap, LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT,
+ &regval);
+ if (ret < 0) {
+ mutex_unlock(&data->update_lock);
+ return ret;
+ }
+ /* Clamp to reasonable range to prevent overflow */
+ val = clamp_val(val, -1000000, 1000000);
+ val = regval - val / 1000;
+ val = clamp_val(val, 0, 31);
+ ret = regmap_write(regmap, LM95245_REG_RW_COMMON_HYSTERESIS,
+ val);
+ mutex_unlock(&data->update_lock);
+ return ret;
+ case hwmon_temp_offset:
+ val = clamp_val(val, -128000, 127875);
+ val = val * 256 / 1000;
+ mutex_lock(&data->update_lock);
+ ret = regmap_write(regmap, LM95245_REG_RW_REMOTE_OFFL,
+ val & 0xe0);
+ if (ret < 0) {
+ mutex_unlock(&data->update_lock);
+ return ret;
+ }
+ ret = regmap_write(regmap, LM95245_REG_RW_REMOTE_OFFH,
+ (val >> 8) & 0xff);
+ mutex_unlock(&data->update_lock);
+ return ret;
+ case hwmon_temp_type:
+ if (val != 1 && val != 2)
+ return -EINVAL;
+ ret = regmap_update_bits(regmap, LM95245_REG_RW_CONFIG2,
+ CFG2_REMOTE_TT,
+ val == 1 ? CFG2_REMOTE_TT : 0);
+ return ret;
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static ssize_t set_crit_hyst(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static int lm95245_read_chip(struct device *dev, u32 attr, int channel,
+ long *val)
{
struct lm95245_data *data = dev_get_drvdata(dev);
- int index = to_sensor_dev_attr(attr)->index;
- struct i2c_client *client = data->client;
- unsigned long val;
- int hyst, limit;
-
- if (kstrtoul(buf, 10, &val) < 0)
- return -EINVAL;
-
- mutex_lock(&data->update_lock);
-
- limit = i2c_smbus_read_byte_data(client, lm95245_reg_address[index]);
- hyst = limit - val / 1000;
- hyst = clamp_val(hyst, 0, 31);
- data->regs[8] = hyst;
- /* shared crit hysteresis */
- i2c_smbus_write_byte_data(client, LM95245_REG_RW_COMMON_HYSTERESIS,
- hyst);
-
- mutex_unlock(&data->update_lock);
-
- return count;
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ *val = data->interval;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static ssize_t show_type(struct device *dev, struct device_attribute *attr,
- char *buf)
+static int lm95245_write_chip(struct device *dev, u32 attr, int channel,
+ long val)
{
struct lm95245_data *data = dev_get_drvdata(dev);
-
- return snprintf(buf, PAGE_SIZE - 1,
- data->config2 & CFG2_REMOTE_TT ? "1\n" : "2\n");
+ int ret;
+
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ mutex_lock(&data->update_lock);
+ ret = lm95245_set_conversion_rate(data, val);
+ mutex_unlock(&data->update_lock);
+ return ret;
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static ssize_t set_type(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static int lm95245_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
{
- struct lm95245_data *data = dev_get_drvdata(dev);
- struct i2c_client *client = data->client;
- unsigned long val;
-
- if (kstrtoul(buf, 10, &val) < 0)
- return -EINVAL;
- if (val != 1 && val != 2)
- return -EINVAL;
-
- mutex_lock(&data->update_lock);
-
- if (val == 1)
- data->config2 |= CFG2_REMOTE_TT;
- else
- data->config2 &= ~CFG2_REMOTE_TT;
-
- data->valid = 0;
-
- i2c_smbus_write_byte_data(client, LM95245_REG_RW_CONFIG2,
- data->config2);
-
- mutex_unlock(&data->update_lock);
-
- return count;
+ switch (type) {
+ case hwmon_chip:
+ return lm95245_read_chip(dev, attr, channel, val);
+ case hwmon_temp:
+ return lm95245_read_temp(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
- char *buf)
+static int lm95245_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
{
- struct lm95245_data *data = lm95245_update_device(dev);
- int index = to_sensor_dev_attr(attr)->index;
-
- return snprintf(buf, PAGE_SIZE - 1, "%d\n",
- !!(data->regs[9] & index));
+ switch (type) {
+ case hwmon_chip:
+ return lm95245_write_chip(dev, attr, channel, val);
+ case hwmon_temp:
+ return lm95245_write_temp(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static ssize_t show_interval(struct device *dev, struct device_attribute *attr,
- char *buf)
+static umode_t lm95245_temp_is_visible(const void *data, u32 attr, int channel)
{
- struct lm95245_data *data = lm95245_update_device(dev);
-
- return snprintf(buf, PAGE_SIZE - 1, "%lu\n", data->interval);
+ switch (attr) {
+ case hwmon_temp_input:
+ case hwmon_temp_max_alarm:
+ case hwmon_temp_max_hyst:
+ case hwmon_temp_crit_alarm:
+ case hwmon_temp_fault:
+ return S_IRUGO;
+ case hwmon_temp_type:
+ case hwmon_temp_max:
+ case hwmon_temp_crit:
+ case hwmon_temp_offset:
+ return S_IRUGO | S_IWUSR;
+ case hwmon_temp_crit_hyst:
+ return (channel == 0) ? S_IRUGO | S_IWUSR : S_IRUGO;
+ default:
+ return 0;
+ }
}
-static ssize_t set_interval(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
+static umode_t lm95245_is_visible(const void *data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
{
- struct lm95245_data *data = dev_get_drvdata(dev);
- struct i2c_client *client = data->client;
- unsigned long val;
-
- if (kstrtoul(buf, 10, &val) < 0)
- return -EINVAL;
-
- mutex_lock(&data->update_lock);
-
- data->interval = lm95245_set_conversion_rate(client, val);
-
- mutex_unlock(&data->update_lock);
-
- return count;
+ switch (type) {
+ case hwmon_chip:
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ return S_IRUGO | S_IWUSR;
+ default:
+ return 0;
+ }
+ case hwmon_temp:
+ return lm95245_temp_is_visible(data, attr, channel);
+ default:
+ return 0;
+ }
}
-static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_input, NULL, 0);
-static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_limit,
- set_limit, 6);
-static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_crit_hyst,
- set_crit_hyst, 6);
-static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL,
- STATUS1_LOC);
-
-static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_input, NULL, 2);
-static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_limit,
- set_limit, 7);
-static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_crit_hyst, NULL, 7);
-static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL,
- STATUS1_RTCRIT);
-static SENSOR_DEVICE_ATTR(temp2_type, S_IWUSR | S_IRUGO, show_type,
- set_type, 0);
-static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL,
- STATUS1_DIODE_FAULT);
-
-static DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, show_interval,
- set_interval);
-
-static struct attribute *lm95245_attrs[] = {
- &sensor_dev_attr_temp1_input.dev_attr.attr,
- &sensor_dev_attr_temp1_crit.dev_attr.attr,
- &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
- &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
- &sensor_dev_attr_temp2_input.dev_attr.attr,
- &sensor_dev_attr_temp2_crit.dev_attr.attr,
- &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr,
- &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
- &sensor_dev_attr_temp2_type.dev_attr.attr,
- &sensor_dev_attr_temp2_fault.dev_attr.attr,
- &dev_attr_update_interval.attr,
- NULL
-};
-ATTRIBUTE_GROUPS(lm95245);
-
/* Return 0 if detection is successful, -ENODEV otherwise */
static int lm95245_detect(struct i2c_client *new_client,
struct i2c_board_info *info)
@@ -453,44 +487,130 @@ static int lm95245_detect(struct i2c_client *new_client,
return 0;
}
-static void lm95245_init_client(struct i2c_client *client,
- struct lm95245_data *data)
+static int lm95245_init_client(struct lm95245_data *data)
{
- data->interval = lm95245_read_conversion_rate(client);
-
- data->config1 = i2c_smbus_read_byte_data(client,
- LM95245_REG_RW_CONFIG1);
- data->config2 = i2c_smbus_read_byte_data(client,
- LM95245_REG_RW_CONFIG2);
-
- if (data->config1 & CFG_STOP) {
- /* Clear the standby bit */
- data->config1 &= ~CFG_STOP;
- i2c_smbus_write_byte_data(client, LM95245_REG_RW_CONFIG1,
- data->config1);
+ int ret;
+
+ ret = lm95245_read_conversion_rate(data);
+ if (ret < 0)
+ return ret;
+
+ return regmap_update_bits(data->regmap, LM95245_REG_RW_CONFIG1,
+ CFG_STOP, 0);
+}
+
+static bool lm95245_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case LM95245_REG_RW_CONFIG1:
+ case LM95245_REG_RW_CONVERS_RATE:
+ case LM95245_REG_W_ONE_SHOT:
+ case LM95245_REG_RW_CONFIG2:
+ case LM95245_REG_RW_REMOTE_OFFH:
+ case LM95245_REG_RW_REMOTE_OFFL:
+ case LM95245_REG_RW_REMOTE_OS_LIMIT:
+ case LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT:
+ case LM95245_REG_RW_REMOTE_TCRIT_LIMIT:
+ case LM95245_REG_RW_COMMON_HYSTERESIS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool lm95245_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case LM95245_REG_R_STATUS1:
+ case LM95245_REG_R_STATUS2:
+ case LM95245_REG_R_LOCAL_TEMPH_S:
+ case LM95245_REG_R_LOCAL_TEMPL_S:
+ case LM95245_REG_R_REMOTE_TEMPH_S:
+ case LM95245_REG_R_REMOTE_TEMPL_S:
+ case LM95245_REG_R_REMOTE_TEMPH_U:
+ case LM95245_REG_R_REMOTE_TEMPL_U:
+ return true;
+ default:
+ return false;
}
}
+static const struct regmap_config lm95245_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .writeable_reg = lm95245_is_writeable_reg,
+ .volatile_reg = lm95245_is_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+ .use_single_rw = true,
+};
+
+static const u32 lm95245_chip_config[] = {
+ HWMON_C_UPDATE_INTERVAL,
+ 0
+};
+
+static const struct hwmon_channel_info lm95245_chip = {
+ .type = hwmon_chip,
+ .config = lm95245_chip_config,
+};
+
+static const u32 lm95245_temp_config[] = {
+ HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_CRIT_ALARM,
+ HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | HWMON_T_CRIT |
+ HWMON_T_CRIT_HYST | HWMON_T_FAULT | HWMON_T_MAX_ALARM |
+ HWMON_T_CRIT_ALARM | HWMON_T_TYPE | HWMON_T_OFFSET,
+ 0
+};
+
+static const struct hwmon_channel_info lm95245_temp = {
+ .type = hwmon_temp,
+ .config = lm95245_temp_config,
+};
+
+static const struct hwmon_channel_info *lm95245_info[] = {
+ &lm95245_chip,
+ &lm95245_temp,
+ NULL
+};
+
+static const struct hwmon_ops lm95245_hwmon_ops = {
+ .is_visible = lm95245_is_visible,
+ .read = lm95245_read,
+ .write = lm95245_write,
+};
+
+static const struct hwmon_chip_info lm95245_chip_info = {
+ .ops = &lm95245_hwmon_ops,
+ .info = lm95245_info,
+};
+
static int lm95245_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct lm95245_data *data;
struct device *hwmon_dev;
+ int ret;
data = devm_kzalloc(dev, sizeof(struct lm95245_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
- data->client = client;
+ data->regmap = devm_regmap_init_i2c(client, &lm95245_regmap_config);
+ if (IS_ERR(data->regmap))
+ return PTR_ERR(data->regmap);
+
mutex_init(&data->update_lock);
/* Initialize the LM95245 chip */
- lm95245_init_client(client, data);
-
- hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
- data,
- lm95245_groups);
+ ret = lm95245_init_client(data);
+ if (ret < 0)
+ return ret;
+
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
+ data,
+ &lm95245_chip_info,
+ NULL);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
diff --git a/drivers/hwmon/ltc4151.c b/drivers/hwmon/ltc4151.c
index c86a18402496..8445c9fd946b 100644
--- a/drivers/hwmon/ltc4151.c
+++ b/drivers/hwmon/ltc4151.c
@@ -30,6 +30,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/slab.h>
@@ -52,6 +53,7 @@ struct ltc4151_data {
struct mutex update_lock;
bool valid;
unsigned long last_updated; /* in jiffies */
+ unsigned int shunt; /* in micro ohms */
/* Registers */
u8 regs[6];
@@ -111,9 +113,9 @@ static int ltc4151_get_value(struct ltc4151_data *data, u8 reg)
case LTC4151_SENSE_H:
/*
* 20uV resolution. Convert to current as measured with
- * an 1 mOhm sense resistor, in mA.
+ * a given sense resistor, in mA.
*/
- val = val * 20;
+ val = val * 20 * 1000 / data->shunt;
break;
case LTC4151_VIN_H:
/* 25 mV per increment */
@@ -176,6 +178,7 @@ static int ltc4151_probe(struct i2c_client *client,
struct device *dev = &client->dev;
struct ltc4151_data *data;
struct device *hwmon_dev;
+ u32 shunt;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
@@ -184,6 +187,15 @@ static int ltc4151_probe(struct i2c_client *client,
if (!data)
return -ENOMEM;
+ if (of_property_read_u32(client->dev.of_node,
+ "shunt-resistor-micro-ohms", &shunt))
+ shunt = 1000; /* 1 mOhm if not set via DT */
+
+ if (shunt == 0)
+ return -EINVAL;
+
+ data->shunt = shunt;
+
data->client = client;
mutex_init(&data->update_lock);
@@ -199,10 +211,16 @@ static const struct i2c_device_id ltc4151_id[] = {
};
MODULE_DEVICE_TABLE(i2c, ltc4151_id);
+static const struct of_device_id ltc4151_match[] = {
+ { .compatible = "lltc,ltc4151" },
+ {},
+};
+
/* This is the driver that will be inserted */
static struct i2c_driver ltc4151_driver = {
.driver = {
.name = "ltc4151",
+ .of_match_table = of_match_ptr(ltc4151_match),
},
.probe = ltc4151_probe,
.id_table = ltc4151_id,
diff --git a/drivers/hwmon/ltc4245.c b/drivers/hwmon/ltc4245.c
index 681b5b7b3c3b..4680d89556ce 100644
--- a/drivers/hwmon/ltc4245.c
+++ b/drivers/hwmon/ltc4245.c
@@ -16,6 +16,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
+#include <linux/bitops.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/i2c.h>
@@ -53,8 +54,6 @@ enum ltc4245_cmd {
struct ltc4245_data {
struct i2c_client *client;
- const struct attribute_group *groups[3];
-
struct mutex update_lock;
bool valid;
unsigned long last_updated; /* in jiffies */
@@ -162,7 +161,7 @@ static struct ltc4245_data *ltc4245_update_device(struct device *dev)
ltc4245_update_gpios(dev);
data->last_updated = jiffies;
- data->valid = 1;
+ data->valid = true;
}
mutex_unlock(&data->update_lock);
@@ -256,213 +255,204 @@ static unsigned int ltc4245_get_current(struct device *dev, u8 reg)
return curr;
}
-static ssize_t ltc4245_show_voltage(struct device *dev,
- struct device_attribute *da,
- char *buf)
-{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
- const int voltage = ltc4245_get_voltage(dev, attr->index);
+/* Map from voltage channel index to voltage register */
- return snprintf(buf, PAGE_SIZE, "%d\n", voltage);
-}
+static const s8 ltc4245_in_regs[] = {
+ LTC4245_12VIN, LTC4245_5VIN, LTC4245_3VIN, LTC4245_VEEIN,
+ LTC4245_12VOUT, LTC4245_5VOUT, LTC4245_3VOUT, LTC4245_VEEOUT,
+};
+
+/* Map from current channel index to current register */
-static ssize_t ltc4245_show_current(struct device *dev,
- struct device_attribute *da,
- char *buf)
+static const s8 ltc4245_curr_regs[] = {
+ LTC4245_12VSENSE, LTC4245_5VSENSE, LTC4245_3VSENSE, LTC4245_VEESENSE,
+};
+
+static int ltc4245_read_curr(struct device *dev, u32 attr, int channel,
+ long *val)
{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
- const unsigned int curr = ltc4245_get_current(dev, attr->index);
+ struct ltc4245_data *data = ltc4245_update_device(dev);
- return snprintf(buf, PAGE_SIZE, "%u\n", curr);
+ switch (attr) {
+ case hwmon_curr_input:
+ *val = ltc4245_get_current(dev, ltc4245_curr_regs[channel]);
+ return 0;
+ case hwmon_curr_max_alarm:
+ *val = !!(data->cregs[LTC4245_FAULT1] & BIT(channel + 4));
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static ssize_t ltc4245_show_power(struct device *dev,
- struct device_attribute *da,
- char *buf)
+static int ltc4245_read_in(struct device *dev, u32 attr, int channel, long *val)
{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
- const unsigned int curr = ltc4245_get_current(dev, attr->index);
- const int output_voltage = ltc4245_get_voltage(dev, attr->index+1);
+ struct ltc4245_data *data = ltc4245_update_device(dev);
- /* current in mA * voltage in mV == power in uW */
- const unsigned int power = abs(output_voltage * curr);
+ switch (attr) {
+ case hwmon_in_input:
+ if (channel < 8) {
+ *val = ltc4245_get_voltage(dev,
+ ltc4245_in_regs[channel]);
+ } else {
+ int regval = data->gpios[channel - 8];
+
+ if (regval < 0)
+ return regval;
+ *val = regval * 10;
+ }
+ return 0;
+ case hwmon_in_min_alarm:
+ if (channel < 4)
+ *val = !!(data->cregs[LTC4245_FAULT1] & BIT(channel));
+ else
+ *val = !!(data->cregs[LTC4245_FAULT2] &
+ BIT(channel - 4));
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
+}
- return snprintf(buf, PAGE_SIZE, "%u\n", power);
+static int ltc4245_read_power(struct device *dev, u32 attr, int channel,
+ long *val)
+{
+ unsigned long curr;
+ long voltage;
+
+ switch (attr) {
+ case hwmon_power_input:
+ (void)ltc4245_update_device(dev);
+ curr = ltc4245_get_current(dev, ltc4245_curr_regs[channel]);
+ voltage = ltc4245_get_voltage(dev, ltc4245_in_regs[channel]);
+ *val = abs(curr * voltage);
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static ssize_t ltc4245_show_alarm(struct device *dev,
- struct device_attribute *da,
- char *buf)
+static int ltc4245_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
{
- struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(da);
- struct ltc4245_data *data = ltc4245_update_device(dev);
- const u8 reg = data->cregs[attr->index];
- const u32 mask = attr->nr;
- return snprintf(buf, PAGE_SIZE, "%u\n", (reg & mask) ? 1 : 0);
+ switch (type) {
+ case hwmon_curr:
+ return ltc4245_read_curr(dev, attr, channel, val);
+ case hwmon_power:
+ return ltc4245_read_power(dev, attr, channel, val);
+ case hwmon_in:
+ return ltc4245_read_in(dev, attr, channel - 1, val);
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static ssize_t ltc4245_show_gpio(struct device *dev,
- struct device_attribute *da,
- char *buf)
+static umode_t ltc4245_is_visible(const void *_data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
- struct ltc4245_data *data = ltc4245_update_device(dev);
- int val = data->gpios[attr->index];
+ const struct ltc4245_data *data = _data;
+
+ switch (type) {
+ case hwmon_in:
+ if (channel == 0)
+ return 0;
+ switch (attr) {
+ case hwmon_in_input:
+ if (channel > 9 && !data->use_extra_gpios)
+ return 0;
+ return S_IRUGO;
+ case hwmon_in_min_alarm:
+ if (channel > 8)
+ return 0;
+ return S_IRUGO;
+ default:
+ return 0;
+ }
+ case hwmon_curr:
+ switch (attr) {
+ case hwmon_curr_input:
+ case hwmon_curr_max_alarm:
+ return S_IRUGO;
+ default:
+ return 0;
+ }
+ case hwmon_power:
+ switch (attr) {
+ case hwmon_power_input:
+ return S_IRUGO;
+ default:
+ return 0;
+ }
+ default:
+ return 0;
+ }
+}
- /* handle stale GPIO's */
- if (val < 0)
- return val;
+static const u32 ltc4245_in_config[] = {
+ HWMON_I_INPUT, /* dummy, skipped in is_visible */
+ HWMON_I_INPUT | HWMON_I_MIN_ALARM,
+ HWMON_I_INPUT | HWMON_I_MIN_ALARM,
+ HWMON_I_INPUT | HWMON_I_MIN_ALARM,
+ HWMON_I_INPUT | HWMON_I_MIN_ALARM,
+ HWMON_I_INPUT | HWMON_I_MIN_ALARM,
+ HWMON_I_INPUT | HWMON_I_MIN_ALARM,
+ HWMON_I_INPUT | HWMON_I_MIN_ALARM,
+ HWMON_I_INPUT | HWMON_I_MIN_ALARM,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ 0
+};
- /* Convert to millivolts and print */
- return snprintf(buf, PAGE_SIZE, "%u\n", val * 10);
-}
+static const struct hwmon_channel_info ltc4245_in = {
+ .type = hwmon_in,
+ .config = ltc4245_in_config,
+};
-/* Construct a sensor_device_attribute structure for each register */
-
-/* Input voltages */
-static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ltc4245_show_voltage, NULL,
- LTC4245_12VIN);
-static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, ltc4245_show_voltage, NULL,
- LTC4245_5VIN);
-static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, ltc4245_show_voltage, NULL,
- LTC4245_3VIN);
-static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, ltc4245_show_voltage, NULL,
- LTC4245_VEEIN);
-
-/* Input undervoltage alarms */
-static SENSOR_DEVICE_ATTR_2(in1_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
- 1 << 0, LTC4245_FAULT1);
-static SENSOR_DEVICE_ATTR_2(in2_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
- 1 << 1, LTC4245_FAULT1);
-static SENSOR_DEVICE_ATTR_2(in3_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
- 1 << 2, LTC4245_FAULT1);
-static SENSOR_DEVICE_ATTR_2(in4_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
- 1 << 3, LTC4245_FAULT1);
-
-/* Currents (via sense resistor) */
-static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, ltc4245_show_current, NULL,
- LTC4245_12VSENSE);
-static SENSOR_DEVICE_ATTR(curr2_input, S_IRUGO, ltc4245_show_current, NULL,
- LTC4245_5VSENSE);
-static SENSOR_DEVICE_ATTR(curr3_input, S_IRUGO, ltc4245_show_current, NULL,
- LTC4245_3VSENSE);
-static SENSOR_DEVICE_ATTR(curr4_input, S_IRUGO, ltc4245_show_current, NULL,
- LTC4245_VEESENSE);
-
-/* Overcurrent alarms */
-static SENSOR_DEVICE_ATTR_2(curr1_max_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
- 1 << 4, LTC4245_FAULT1);
-static SENSOR_DEVICE_ATTR_2(curr2_max_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
- 1 << 5, LTC4245_FAULT1);
-static SENSOR_DEVICE_ATTR_2(curr3_max_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
- 1 << 6, LTC4245_FAULT1);
-static SENSOR_DEVICE_ATTR_2(curr4_max_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
- 1 << 7, LTC4245_FAULT1);
-
-/* Output voltages */
-static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, ltc4245_show_voltage, NULL,
- LTC4245_12VOUT);
-static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, ltc4245_show_voltage, NULL,
- LTC4245_5VOUT);
-static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, ltc4245_show_voltage, NULL,
- LTC4245_3VOUT);
-static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, ltc4245_show_voltage, NULL,
- LTC4245_VEEOUT);
-
-/* Power Bad alarms */
-static SENSOR_DEVICE_ATTR_2(in5_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
- 1 << 0, LTC4245_FAULT2);
-static SENSOR_DEVICE_ATTR_2(in6_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
- 1 << 1, LTC4245_FAULT2);
-static SENSOR_DEVICE_ATTR_2(in7_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
- 1 << 2, LTC4245_FAULT2);
-static SENSOR_DEVICE_ATTR_2(in8_min_alarm, S_IRUGO, ltc4245_show_alarm, NULL,
- 1 << 3, LTC4245_FAULT2);
-
-/* GPIO voltages */
-static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, ltc4245_show_gpio, NULL, 0);
-static SENSOR_DEVICE_ATTR(in10_input, S_IRUGO, ltc4245_show_gpio, NULL, 1);
-static SENSOR_DEVICE_ATTR(in11_input, S_IRUGO, ltc4245_show_gpio, NULL, 2);
-
-/* Power Consumption (virtual) */
-static SENSOR_DEVICE_ATTR(power1_input, S_IRUGO, ltc4245_show_power, NULL,
- LTC4245_12VSENSE);
-static SENSOR_DEVICE_ATTR(power2_input, S_IRUGO, ltc4245_show_power, NULL,
- LTC4245_5VSENSE);
-static SENSOR_DEVICE_ATTR(power3_input, S_IRUGO, ltc4245_show_power, NULL,
- LTC4245_3VSENSE);
-static SENSOR_DEVICE_ATTR(power4_input, S_IRUGO, ltc4245_show_power, NULL,
- LTC4245_VEESENSE);
+static const u32 ltc4245_curr_config[] = {
+ HWMON_C_INPUT | HWMON_C_MAX_ALARM,
+ HWMON_C_INPUT | HWMON_C_MAX_ALARM,
+ HWMON_C_INPUT | HWMON_C_MAX_ALARM,
+ HWMON_C_INPUT | HWMON_C_MAX_ALARM,
+ 0
+};
-/*
- * Finally, construct an array of pointers to members of the above objects,
- * as required for sysfs_create_group()
- */
-static struct attribute *ltc4245_std_attributes[] = {
- &sensor_dev_attr_in1_input.dev_attr.attr,
- &sensor_dev_attr_in2_input.dev_attr.attr,
- &sensor_dev_attr_in3_input.dev_attr.attr,
- &sensor_dev_attr_in4_input.dev_attr.attr,
-
- &sensor_dev_attr_in1_min_alarm.dev_attr.attr,
- &sensor_dev_attr_in2_min_alarm.dev_attr.attr,
- &sensor_dev_attr_in3_min_alarm.dev_attr.attr,
- &sensor_dev_attr_in4_min_alarm.dev_attr.attr,
-
- &sensor_dev_attr_curr1_input.dev_attr.attr,
- &sensor_dev_attr_curr2_input.dev_attr.attr,
- &sensor_dev_attr_curr3_input.dev_attr.attr,
- &sensor_dev_attr_curr4_input.dev_attr.attr,
-
- &sensor_dev_attr_curr1_max_alarm.dev_attr.attr,
- &sensor_dev_attr_curr2_max_alarm.dev_attr.attr,
- &sensor_dev_attr_curr3_max_alarm.dev_attr.attr,
- &sensor_dev_attr_curr4_max_alarm.dev_attr.attr,
-
- &sensor_dev_attr_in5_input.dev_attr.attr,
- &sensor_dev_attr_in6_input.dev_attr.attr,
- &sensor_dev_attr_in7_input.dev_attr.attr,
- &sensor_dev_attr_in8_input.dev_attr.attr,
-
- &sensor_dev_attr_in5_min_alarm.dev_attr.attr,
- &sensor_dev_attr_in6_min_alarm.dev_attr.attr,
- &sensor_dev_attr_in7_min_alarm.dev_attr.attr,
- &sensor_dev_attr_in8_min_alarm.dev_attr.attr,
-
- &sensor_dev_attr_in9_input.dev_attr.attr,
-
- &sensor_dev_attr_power1_input.dev_attr.attr,
- &sensor_dev_attr_power2_input.dev_attr.attr,
- &sensor_dev_attr_power3_input.dev_attr.attr,
- &sensor_dev_attr_power4_input.dev_attr.attr,
-
- NULL,
+static const struct hwmon_channel_info ltc4245_curr = {
+ .type = hwmon_curr,
+ .config = ltc4245_curr_config,
};
-static struct attribute *ltc4245_gpio_attributes[] = {
- &sensor_dev_attr_in10_input.dev_attr.attr,
- &sensor_dev_attr_in11_input.dev_attr.attr,
- NULL,
+static const u32 ltc4245_power_config[] = {
+ HWMON_P_INPUT,
+ HWMON_P_INPUT,
+ HWMON_P_INPUT,
+ HWMON_P_INPUT,
+ 0
};
-static const struct attribute_group ltc4245_std_group = {
- .attrs = ltc4245_std_attributes,
+static const struct hwmon_channel_info ltc4245_power = {
+ .type = hwmon_power,
+ .config = ltc4245_power_config,
};
-static const struct attribute_group ltc4245_gpio_group = {
- .attrs = ltc4245_gpio_attributes,
+static const struct hwmon_channel_info *ltc4245_info[] = {
+ &ltc4245_in,
+ &ltc4245_curr,
+ &ltc4245_power,
+ NULL
};
-static void ltc4245_sysfs_add_groups(struct ltc4245_data *data)
-{
- /* standard sysfs attributes */
- data->groups[0] = &ltc4245_std_group;
+static const struct hwmon_ops ltc4245_hwmon_ops = {
+ .is_visible = ltc4245_is_visible,
+ .read = ltc4245_read,
+};
- /* if we're using the extra gpio support, register it's attributes */
- if (data->use_extra_gpios)
- data->groups[1] = &ltc4245_gpio_group;
-}
+static const struct hwmon_chip_info ltc4245_chip_info = {
+ .ops = &ltc4245_hwmon_ops,
+ .info = ltc4245_info,
+};
static bool ltc4245_use_extra_gpios(struct i2c_client *client)
{
@@ -502,12 +492,10 @@ static int ltc4245_probe(struct i2c_client *client,
i2c_smbus_write_byte_data(client, LTC4245_FAULT1, 0x00);
i2c_smbus_write_byte_data(client, LTC4245_FAULT2, 0x00);
- /* Add sysfs hooks */
- ltc4245_sysfs_add_groups(data);
-
- hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev,
- client->name, data,
- data->groups);
+ hwmon_dev = devm_hwmon_device_register_with_info(&client->dev,
+ client->name, data,
+ &ltc4245_chip_info,
+ NULL);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
diff --git a/drivers/hwmon/max31790.c b/drivers/hwmon/max31790.c
index 69c0ac80a946..bef84e085973 100644
--- a/drivers/hwmon/max31790.c
+++ b/drivers/hwmon/max31790.c
@@ -17,7 +17,6 @@
#include <linux/err.h>
#include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/jiffies.h>
@@ -169,362 +168,288 @@ static u8 bits_for_tach_period(int rpm)
return bits;
}
-static ssize_t get_fan(struct device *dev,
- struct device_attribute *devattr, char *buf)
+static int max31790_read_fan(struct device *dev, u32 attr, int channel,
+ long *val)
{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct max31790_data *data = max31790_update_device(dev);
int sr, rpm;
if (IS_ERR(data))
return PTR_ERR(data);
- sr = get_tach_period(data->fan_dynamics[attr->index]);
- rpm = RPM_FROM_REG(data->tach[attr->index], sr);
-
- return sprintf(buf, "%d\n", rpm);
-}
-
-static ssize_t get_fan_target(struct device *dev,
- struct device_attribute *devattr, char *buf)
-{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- struct max31790_data *data = max31790_update_device(dev);
- int sr, rpm;
-
- if (IS_ERR(data))
- return PTR_ERR(data);
-
- sr = get_tach_period(data->fan_dynamics[attr->index]);
- rpm = RPM_FROM_REG(data->target_count[attr->index], sr);
-
- return sprintf(buf, "%d\n", rpm);
+ switch (attr) {
+ case hwmon_fan_input:
+ sr = get_tach_period(data->fan_dynamics[channel]);
+ rpm = RPM_FROM_REG(data->tach[channel], sr);
+ *val = rpm;
+ return 0;
+ case hwmon_fan_target:
+ sr = get_tach_period(data->fan_dynamics[channel]);
+ rpm = RPM_FROM_REG(data->target_count[channel], sr);
+ *val = rpm;
+ return 0;
+ case hwmon_fan_fault:
+ *val = !!(data->fault_status & (1 << channel));
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static ssize_t set_fan_target(struct device *dev,
- struct device_attribute *devattr,
- const char *buf, size_t count)
+static int max31790_write_fan(struct device *dev, u32 attr, int channel,
+ long val)
{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct max31790_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
+ int target_count;
+ int err = 0;
u8 bits;
int sr;
- int target_count;
- unsigned long rpm;
- int err;
-
- err = kstrtoul(buf, 10, &rpm);
- if (err)
- return err;
mutex_lock(&data->update_lock);
- rpm = clamp_val(rpm, FAN_RPM_MIN, FAN_RPM_MAX);
- bits = bits_for_tach_period(rpm);
- data->fan_dynamics[attr->index] =
- ((data->fan_dynamics[attr->index]
- & ~MAX31790_FAN_DYN_SR_MASK)
- | (bits << MAX31790_FAN_DYN_SR_SHIFT));
- err = i2c_smbus_write_byte_data(client,
- MAX31790_REG_FAN_DYNAMICS(attr->index),
- data->fan_dynamics[attr->index]);
-
- if (err < 0) {
- mutex_unlock(&data->update_lock);
- return err;
+ switch (attr) {
+ case hwmon_fan_target:
+ val = clamp_val(val, FAN_RPM_MIN, FAN_RPM_MAX);
+ bits = bits_for_tach_period(val);
+ data->fan_dynamics[channel] =
+ ((data->fan_dynamics[channel] &
+ ~MAX31790_FAN_DYN_SR_MASK) |
+ (bits << MAX31790_FAN_DYN_SR_SHIFT));
+ err = i2c_smbus_write_byte_data(client,
+ MAX31790_REG_FAN_DYNAMICS(channel),
+ data->fan_dynamics[channel]);
+ if (err < 0)
+ break;
+
+ sr = get_tach_period(data->fan_dynamics[channel]);
+ target_count = RPM_TO_REG(val, sr);
+ target_count = clamp_val(target_count, 0x1, 0x7FF);
+
+ data->target_count[channel] = target_count << 5;
+
+ err = i2c_smbus_write_word_swapped(client,
+ MAX31790_REG_TARGET_COUNT(channel),
+ data->target_count[channel]);
+ break;
+ default:
+ err = -EOPNOTSUPP;
+ break;
}
- sr = get_tach_period(data->fan_dynamics[attr->index]);
- target_count = RPM_TO_REG(rpm, sr);
- target_count = clamp_val(target_count, 0x1, 0x7FF);
-
- data->target_count[attr->index] = target_count << 5;
-
- err = i2c_smbus_write_word_swapped(client,
- MAX31790_REG_TARGET_COUNT(attr->index),
- data->target_count[attr->index]);
-
mutex_unlock(&data->update_lock);
- if (err < 0)
- return err;
+ return err;
+}
- return count;
+static umode_t max31790_fan_is_visible(const void *_data, u32 attr, int channel)
+{
+ const struct max31790_data *data = _data;
+ u8 fan_config = data->fan_config[channel % NR_CHANNEL];
+
+ switch (attr) {
+ case hwmon_fan_input:
+ case hwmon_fan_fault:
+ if (channel < NR_CHANNEL ||
+ (fan_config & MAX31790_FAN_CFG_TACH_INPUT))
+ return S_IRUGO;
+ return 0;
+ case hwmon_fan_target:
+ if (channel < NR_CHANNEL &&
+ !(fan_config & MAX31790_FAN_CFG_TACH_INPUT))
+ return S_IRUGO | S_IWUSR;
+ return 0;
+ default:
+ return 0;
+ }
}
-static ssize_t get_pwm(struct device *dev,
- struct device_attribute *devattr, char *buf)
+static int max31790_read_pwm(struct device *dev, u32 attr, int channel,
+ long *val)
{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct max31790_data *data = max31790_update_device(dev);
- int pwm;
+ u8 fan_config = data->fan_config[channel];
if (IS_ERR(data))
return PTR_ERR(data);
- pwm = data->pwm[attr->index] >> 8;
-
- return sprintf(buf, "%d\n", pwm);
+ switch (attr) {
+ case hwmon_pwm_input:
+ *val = data->pwm[channel] >> 8;
+ return 0;
+ case hwmon_pwm_enable:
+ if (fan_config & MAX31790_FAN_CFG_RPM_MODE)
+ *val = 2;
+ else if (fan_config & MAX31790_FAN_CFG_TACH_INPUT_EN)
+ *val = 1;
+ else
+ *val = 0;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static ssize_t set_pwm(struct device *dev,
- struct device_attribute *devattr,
- const char *buf, size_t count)
+static int max31790_write_pwm(struct device *dev, u32 attr, int channel,
+ long val)
{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct max31790_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
- unsigned long pwm;
- int err;
-
- err = kstrtoul(buf, 10, &pwm);
- if (err)
- return err;
-
- if (pwm > 255)
- return -EINVAL;
+ u8 fan_config;
+ int err = 0;
mutex_lock(&data->update_lock);
- data->pwm[attr->index] = pwm << 8;
- err = i2c_smbus_write_word_swapped(client,
- MAX31790_REG_PWMOUT(attr->index),
- data->pwm[attr->index]);
+ switch (attr) {
+ case hwmon_pwm_input:
+ if (val < 0 || val > 255) {
+ err = -EINVAL;
+ break;
+ }
+ data->pwm[channel] = val << 8;
+ err = i2c_smbus_write_word_swapped(client,
+ MAX31790_REG_PWMOUT(channel),
+ val);
+ break;
+ case hwmon_pwm_enable:
+ fan_config = data->fan_config[channel];
+ if (val == 0) {
+ fan_config &= ~(MAX31790_FAN_CFG_TACH_INPUT_EN |
+ MAX31790_FAN_CFG_RPM_MODE);
+ } else if (val == 1) {
+ fan_config = (fan_config |
+ MAX31790_FAN_CFG_TACH_INPUT_EN) &
+ ~MAX31790_FAN_CFG_RPM_MODE;
+ } else if (val == 2) {
+ fan_config |= MAX31790_FAN_CFG_TACH_INPUT_EN |
+ MAX31790_FAN_CFG_RPM_MODE;
+ } else {
+ err = -EINVAL;
+ break;
+ }
+ data->fan_config[channel] = fan_config;
+ err = i2c_smbus_write_byte_data(client,
+ MAX31790_REG_FAN_CONFIG(channel),
+ fan_config);
+ break;
+ default:
+ err = -EOPNOTSUPP;
+ break;
+ }
mutex_unlock(&data->update_lock);
- if (err < 0)
- return err;
-
- return count;
+ return err;
}
-static ssize_t get_pwm_enable(struct device *dev,
- struct device_attribute *devattr, char *buf)
+static umode_t max31790_pwm_is_visible(const void *_data, u32 attr, int channel)
{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- struct max31790_data *data = max31790_update_device(dev);
- int mode;
-
- if (IS_ERR(data))
- return PTR_ERR(data);
-
- if (data->fan_config[attr->index] & MAX31790_FAN_CFG_RPM_MODE)
- mode = 2;
- else if (data->fan_config[attr->index] & MAX31790_FAN_CFG_TACH_INPUT_EN)
- mode = 1;
- else
- mode = 0;
-
- return sprintf(buf, "%d\n", mode);
+ const struct max31790_data *data = _data;
+ u8 fan_config = data->fan_config[channel];
+
+ switch (attr) {
+ case hwmon_pwm_input:
+ case hwmon_pwm_enable:
+ if (!(fan_config & MAX31790_FAN_CFG_TACH_INPUT))
+ return S_IRUGO | S_IWUSR;
+ return 0;
+ default:
+ return 0;
+ }
}
-static ssize_t set_pwm_enable(struct device *dev,
- struct device_attribute *devattr,
- const char *buf, size_t count)
+static int max31790_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- struct max31790_data *data = dev_get_drvdata(dev);
- struct i2c_client *client = data->client;
- unsigned long mode;
- int err;
-
- err = kstrtoul(buf, 10, &mode);
- if (err)
- return err;
-
- switch (mode) {
- case 0:
- data->fan_config[attr->index] =
- data->fan_config[attr->index]
- & ~(MAX31790_FAN_CFG_TACH_INPUT_EN
- | MAX31790_FAN_CFG_RPM_MODE);
- break;
- case 1:
- data->fan_config[attr->index] =
- (data->fan_config[attr->index]
- | MAX31790_FAN_CFG_TACH_INPUT_EN)
- & ~MAX31790_FAN_CFG_RPM_MODE;
- break;
- case 2:
- data->fan_config[attr->index] =
- data->fan_config[attr->index]
- | MAX31790_FAN_CFG_TACH_INPUT_EN
- | MAX31790_FAN_CFG_RPM_MODE;
- break;
+ switch (type) {
+ case hwmon_fan:
+ return max31790_read_fan(dev, attr, channel, val);
+ case hwmon_pwm:
+ return max31790_read_pwm(dev, attr, channel, val);
default:
- return -EINVAL;
+ return -EOPNOTSUPP;
}
-
- mutex_lock(&data->update_lock);
-
- err = i2c_smbus_write_byte_data(client,
- MAX31790_REG_FAN_CONFIG(attr->index),
- data->fan_config[attr->index]);
-
- mutex_unlock(&data->update_lock);
-
- if (err < 0)
- return err;
-
- return count;
}
-static ssize_t get_fan_fault(struct device *dev,
- struct device_attribute *devattr, char *buf)
+static int max31790_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- struct max31790_data *data = max31790_update_device(dev);
- int fault;
-
- if (IS_ERR(data))
- return PTR_ERR(data);
-
- fault = !!(data->fault_status & (1 << attr->index));
+ switch (type) {
+ case hwmon_fan:
+ return max31790_write_fan(dev, attr, channel, val);
+ case hwmon_pwm:
+ return max31790_write_pwm(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
- return sprintf(buf, "%d\n", fault);
+static umode_t max31790_is_visible(const void *data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ switch (type) {
+ case hwmon_fan:
+ return max31790_fan_is_visible(data, attr, channel);
+ case hwmon_pwm:
+ return max31790_pwm_is_visible(data, attr, channel);
+ default:
+ return 0;
+ }
}
-static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, get_fan, NULL, 0);
-static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, get_fan, NULL, 1);
-static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, get_fan, NULL, 2);
-static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, get_fan, NULL, 3);
-static SENSOR_DEVICE_ATTR(fan5_input, S_IRUGO, get_fan, NULL, 4);
-static SENSOR_DEVICE_ATTR(fan6_input, S_IRUGO, get_fan, NULL, 5);
-
-static SENSOR_DEVICE_ATTR(fan1_fault, S_IRUGO, get_fan_fault, NULL, 0);
-static SENSOR_DEVICE_ATTR(fan2_fault, S_IRUGO, get_fan_fault, NULL, 1);
-static SENSOR_DEVICE_ATTR(fan3_fault, S_IRUGO, get_fan_fault, NULL, 2);
-static SENSOR_DEVICE_ATTR(fan4_fault, S_IRUGO, get_fan_fault, NULL, 3);
-static SENSOR_DEVICE_ATTR(fan5_fault, S_IRUGO, get_fan_fault, NULL, 4);
-static SENSOR_DEVICE_ATTR(fan6_fault, S_IRUGO, get_fan_fault, NULL, 5);
-
-static SENSOR_DEVICE_ATTR(fan7_input, S_IRUGO, get_fan, NULL, 6);
-static SENSOR_DEVICE_ATTR(fan8_input, S_IRUGO, get_fan, NULL, 7);
-static SENSOR_DEVICE_ATTR(fan9_input, S_IRUGO, get_fan, NULL, 8);
-static SENSOR_DEVICE_ATTR(fan10_input, S_IRUGO, get_fan, NULL, 9);
-static SENSOR_DEVICE_ATTR(fan11_input, S_IRUGO, get_fan, NULL, 10);
-static SENSOR_DEVICE_ATTR(fan12_input, S_IRUGO, get_fan, NULL, 11);
-
-static SENSOR_DEVICE_ATTR(fan7_fault, S_IRUGO, get_fan_fault, NULL, 6);
-static SENSOR_DEVICE_ATTR(fan8_fault, S_IRUGO, get_fan_fault, NULL, 7);
-static SENSOR_DEVICE_ATTR(fan9_fault, S_IRUGO, get_fan_fault, NULL, 8);
-static SENSOR_DEVICE_ATTR(fan10_fault, S_IRUGO, get_fan_fault, NULL, 9);
-static SENSOR_DEVICE_ATTR(fan11_fault, S_IRUGO, get_fan_fault, NULL, 10);
-static SENSOR_DEVICE_ATTR(fan12_fault, S_IRUGO, get_fan_fault, NULL, 11);
-
-static SENSOR_DEVICE_ATTR(fan1_target, S_IWUSR | S_IRUGO,
- get_fan_target, set_fan_target, 0);
-static SENSOR_DEVICE_ATTR(fan2_target, S_IWUSR | S_IRUGO,
- get_fan_target, set_fan_target, 1);
-static SENSOR_DEVICE_ATTR(fan3_target, S_IWUSR | S_IRUGO,
- get_fan_target, set_fan_target, 2);
-static SENSOR_DEVICE_ATTR(fan4_target, S_IWUSR | S_IRUGO,
- get_fan_target, set_fan_target, 3);
-static SENSOR_DEVICE_ATTR(fan5_target, S_IWUSR | S_IRUGO,
- get_fan_target, set_fan_target, 4);
-static SENSOR_DEVICE_ATTR(fan6_target, S_IWUSR | S_IRUGO,
- get_fan_target, set_fan_target, 5);
-
-static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 0);
-static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 1);
-static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 2);
-static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 3);
-static SENSOR_DEVICE_ATTR(pwm5, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 4);
-static SENSOR_DEVICE_ATTR(pwm6, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 5);
-
-static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO,
- get_pwm_enable, set_pwm_enable, 0);
-static SENSOR_DEVICE_ATTR(pwm2_enable, S_IWUSR | S_IRUGO,
- get_pwm_enable, set_pwm_enable, 1);
-static SENSOR_DEVICE_ATTR(pwm3_enable, S_IWUSR | S_IRUGO,
- get_pwm_enable, set_pwm_enable, 2);
-static SENSOR_DEVICE_ATTR(pwm4_enable, S_IWUSR | S_IRUGO,
- get_pwm_enable, set_pwm_enable, 3);
-static SENSOR_DEVICE_ATTR(pwm5_enable, S_IWUSR | S_IRUGO,
- get_pwm_enable, set_pwm_enable, 4);
-static SENSOR_DEVICE_ATTR(pwm6_enable, S_IWUSR | S_IRUGO,
- get_pwm_enable, set_pwm_enable, 5);
-
-static struct attribute *max31790_attrs[] = {
- &sensor_dev_attr_fan1_input.dev_attr.attr,
- &sensor_dev_attr_fan2_input.dev_attr.attr,
- &sensor_dev_attr_fan3_input.dev_attr.attr,
- &sensor_dev_attr_fan4_input.dev_attr.attr,
- &sensor_dev_attr_fan5_input.dev_attr.attr,
- &sensor_dev_attr_fan6_input.dev_attr.attr,
-
- &sensor_dev_attr_fan1_fault.dev_attr.attr,
- &sensor_dev_attr_fan2_fault.dev_attr.attr,
- &sensor_dev_attr_fan3_fault.dev_attr.attr,
- &sensor_dev_attr_fan4_fault.dev_attr.attr,
- &sensor_dev_attr_fan5_fault.dev_attr.attr,
- &sensor_dev_attr_fan6_fault.dev_attr.attr,
-
- &sensor_dev_attr_fan7_input.dev_attr.attr,
- &sensor_dev_attr_fan8_input.dev_attr.attr,
- &sensor_dev_attr_fan9_input.dev_attr.attr,
- &sensor_dev_attr_fan10_input.dev_attr.attr,
- &sensor_dev_attr_fan11_input.dev_attr.attr,
- &sensor_dev_attr_fan12_input.dev_attr.attr,
-
- &sensor_dev_attr_fan7_fault.dev_attr.attr,
- &sensor_dev_attr_fan8_fault.dev_attr.attr,
- &sensor_dev_attr_fan9_fault.dev_attr.attr,
- &sensor_dev_attr_fan10_fault.dev_attr.attr,
- &sensor_dev_attr_fan11_fault.dev_attr.attr,
- &sensor_dev_attr_fan12_fault.dev_attr.attr,
-
- &sensor_dev_attr_fan1_target.dev_attr.attr,
- &sensor_dev_attr_fan2_target.dev_attr.attr,
- &sensor_dev_attr_fan3_target.dev_attr.attr,
- &sensor_dev_attr_fan4_target.dev_attr.attr,
- &sensor_dev_attr_fan5_target.dev_attr.attr,
- &sensor_dev_attr_fan6_target.dev_attr.attr,
-
- &sensor_dev_attr_pwm1.dev_attr.attr,
- &sensor_dev_attr_pwm2.dev_attr.attr,
- &sensor_dev_attr_pwm3.dev_attr.attr,
- &sensor_dev_attr_pwm4.dev_attr.attr,
- &sensor_dev_attr_pwm5.dev_attr.attr,
- &sensor_dev_attr_pwm6.dev_attr.attr,
-
- &sensor_dev_attr_pwm1_enable.dev_attr.attr,
- &sensor_dev_attr_pwm2_enable.dev_attr.attr,
- &sensor_dev_attr_pwm3_enable.dev_attr.attr,
- &sensor_dev_attr_pwm4_enable.dev_attr.attr,
- &sensor_dev_attr_pwm5_enable.dev_attr.attr,
- &sensor_dev_attr_pwm6_enable.dev_attr.attr,
- NULL
+static const u32 max31790_fan_config[] = {
+ HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
+ HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
+ HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
+ HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
+ HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
+ HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
+ HWMON_F_INPUT | HWMON_F_FAULT,
+ HWMON_F_INPUT | HWMON_F_FAULT,
+ HWMON_F_INPUT | HWMON_F_FAULT,
+ HWMON_F_INPUT | HWMON_F_FAULT,
+ HWMON_F_INPUT | HWMON_F_FAULT,
+ HWMON_F_INPUT | HWMON_F_FAULT,
+ 0
};
-static umode_t max31790_attrs_visible(struct kobject *kobj,
- struct attribute *a, int n)
-{
- struct device *dev = container_of(kobj, struct device, kobj);
- struct max31790_data *data = dev_get_drvdata(dev);
- struct device_attribute *devattr =
- container_of(a, struct device_attribute, attr);
- int index = to_sensor_dev_attr(devattr)->index % NR_CHANNEL;
- u8 fan_config;
+static const struct hwmon_channel_info max31790_fan = {
+ .type = hwmon_fan,
+ .config = max31790_fan_config,
+};
- fan_config = data->fan_config[index];
+static const u32 max31790_pwm_config[] = {
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+ 0
+};
- if (n >= NR_CHANNEL * 2 && n < NR_CHANNEL * 4 &&
- !(fan_config & MAX31790_FAN_CFG_TACH_INPUT))
- return 0;
- if (n >= NR_CHANNEL * 4 && (fan_config & MAX31790_FAN_CFG_TACH_INPUT))
- return 0;
+static const struct hwmon_channel_info max31790_pwm = {
+ .type = hwmon_pwm,
+ .config = max31790_pwm_config,
+};
- return a->mode;
-}
+static const struct hwmon_channel_info *max31790_info[] = {
+ &max31790_fan,
+ &max31790_pwm,
+ NULL
+};
+
+static const struct hwmon_ops max31790_hwmon_ops = {
+ .is_visible = max31790_is_visible,
+ .read = max31790_read,
+ .write = max31790_write,
+};
-static const struct attribute_group max31790_group = {
- .attrs = max31790_attrs,
- .is_visible = max31790_attrs_visible,
+static const struct hwmon_chip_info max31790_chip_info = {
+ .ops = &max31790_hwmon_ops,
+ .info = max31790_info,
};
-__ATTRIBUTE_GROUPS(max31790);
static int max31790_init_client(struct i2c_client *client,
struct max31790_data *data)
@@ -575,8 +500,10 @@ static int max31790_probe(struct i2c_client *client,
if (err)
return err;
- hwmon_dev = devm_hwmon_device_register_with_groups(dev,
- client->name, data, max31790_groups);
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
+ data,
+ &max31790_chip_info,
+ NULL);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
diff --git a/drivers/hwmon/max6650.c b/drivers/hwmon/max6650.c
index 162a520f4bd6..a993b44ed538 100644
--- a/drivers/hwmon/max6650.c
+++ b/drivers/hwmon/max6650.c
@@ -39,6 +39,7 @@
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
+#include <linux/of_device.h>
/*
* Insmod parameters
@@ -48,7 +49,7 @@
static int fan_voltage;
/* prescaler: Possible values are 1, 2, 4, 8, 16 or 0 for don't change */
static int prescaler;
-/* clock: The clock frequency of the chip the driver should assume */
+/* clock: The clock frequency of the chip (max6651 can be clocked externally) */
static int clock = 254000;
module_param(fan_voltage, int, S_IRUGO);
@@ -133,6 +134,19 @@ static const u8 tach_reg[] = {
MAX6650_REG_TACH3,
};
+static const struct of_device_id max6650_dt_match[] = {
+ {
+ .compatible = "maxim,max6650",
+ .data = (void *)1
+ },
+ {
+ .compatible = "maxim,max6651",
+ .data = (void *)4
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, max6650_dt_match);
+
static struct max6650_data *max6650_update_device(struct device *dev)
{
struct max6650_data *data = dev_get_drvdata(dev);
@@ -171,6 +185,30 @@ static struct max6650_data *max6650_update_device(struct device *dev)
return data;
}
+/*
+ * Change the operating mode of the chip (if needed).
+ * mode is one of the MAX6650_CFG_MODE_* values.
+ */
+static int max6650_set_operating_mode(struct max6650_data *data, u8 mode)
+{
+ int result;
+ u8 config = data->config;
+
+ if (mode == (config & MAX6650_CFG_MODE_MASK))
+ return 0;
+
+ config = (config & ~MAX6650_CFG_MODE_MASK) | mode;
+
+ result = i2c_smbus_write_byte_data(data->client, MAX6650_REG_CONFIG,
+ config);
+ if (result < 0)
+ return result;
+
+ data->config = config;
+
+ return 0;
+}
+
static ssize_t get_fan(struct device *dev, struct device_attribute *devattr,
char *buf)
{
@@ -252,18 +290,12 @@ static ssize_t get_target(struct device *dev, struct device_attribute *devattr,
return sprintf(buf, "%d\n", rpm);
}
-static ssize_t set_target(struct device *dev, struct device_attribute *devattr,
- const char *buf, size_t count)
+static int max6650_set_target(struct max6650_data *data, unsigned long rpm)
{
- struct max6650_data *data = dev_get_drvdata(dev);
- struct i2c_client *client = data->client;
int kscale, ktach;
- unsigned long rpm;
- int err;
- err = kstrtoul(buf, 10, &rpm);
- if (err)
- return err;
+ if (rpm == 0)
+ return max6650_set_operating_mode(data, MAX6650_CFG_MODE_OFF);
rpm = clamp_val(rpm, FAN_RPM_MIN, FAN_RPM_MAX);
@@ -274,8 +306,6 @@ static ssize_t set_target(struct device *dev, struct device_attribute *devattr,
* KTACH = [(fCLK x KSCALE) / (256 x FanSpeed)] - 1
*/
- mutex_lock(&data->update_lock);
-
kscale = DIV_FROM_REG(data->config);
ktach = ((clock * kscale) / (256 * rpm / 60)) - 1;
if (ktach < 0)
@@ -284,10 +314,30 @@ static ssize_t set_target(struct device *dev, struct device_attribute *devattr,
ktach = 255;
data->speed = ktach;
- i2c_smbus_write_byte_data(client, MAX6650_REG_SPEED, data->speed);
+ return i2c_smbus_write_byte_data(data->client, MAX6650_REG_SPEED,
+ data->speed);
+}
+
+static ssize_t set_target(struct device *dev, struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct max6650_data *data = dev_get_drvdata(dev);
+ unsigned long rpm;
+ int err;
+
+ err = kstrtoul(buf, 10, &rpm);
+ if (err)
+ return err;
+
+ mutex_lock(&data->update_lock);
+
+ err = max6650_set_target(data, rpm);
mutex_unlock(&data->update_lock);
+ if (err < 0)
+ return err;
+
return count;
}
@@ -341,12 +391,11 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr,
data->dac = 180 - (180 * pwm)/255;
else
data->dac = 76 - (76 * pwm)/255;
-
- i2c_smbus_write_byte_data(client, MAX6650_REG_DAC, data->dac);
+ err = i2c_smbus_write_byte_data(client, MAX6650_REG_DAC, data->dac);
mutex_unlock(&data->update_lock);
- return count;
+ return err < 0 ? err : count;
}
/*
@@ -355,14 +404,14 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr,
* 0 = Fan always on
* 1 = Open loop, Voltage is set according to speed, not regulated.
* 2 = Closed loop, RPM for all fans regulated by fan1 tachometer
+ * 3 = Fan off
*/
-
static ssize_t get_enable(struct device *dev, struct device_attribute *devattr,
char *buf)
{
struct max6650_data *data = max6650_update_device(dev);
int mode = (data->config & MAX6650_CFG_MODE_MASK) >> 4;
- int sysfs_modes[4] = {0, 1, 2, 1};
+ int sysfs_modes[4] = {0, 3, 2, 1};
return sprintf(buf, "%d\n", sysfs_modes[mode]);
}
@@ -371,25 +420,25 @@ static ssize_t set_enable(struct device *dev, struct device_attribute *devattr,
const char *buf, size_t count)
{
struct max6650_data *data = dev_get_drvdata(dev);
- struct i2c_client *client = data->client;
- int max6650_modes[3] = {0, 3, 2};
unsigned long mode;
int err;
+ const u8 max6650_modes[] = {
+ MAX6650_CFG_MODE_ON,
+ MAX6650_CFG_MODE_OPEN_LOOP,
+ MAX6650_CFG_MODE_CLOSED_LOOP,
+ MAX6650_CFG_MODE_OFF,
+ };
err = kstrtoul(buf, 10, &mode);
if (err)
return err;
- if (mode > 2)
+ if (mode >= ARRAY_SIZE(max6650_modes))
return -EINVAL;
mutex_lock(&data->update_lock);
- data->config = i2c_smbus_read_byte_data(client, MAX6650_REG_CONFIG);
- data->config = (data->config & ~MAX6650_CFG_MODE_MASK)
- | (max6650_modes[mode] << 4);
-
- i2c_smbus_write_byte_data(client, MAX6650_REG_CONFIG, data->config);
+ max6650_set_operating_mode(data, max6650_modes[mode]);
mutex_unlock(&data->update_lock);
@@ -566,6 +615,18 @@ static int max6650_init_client(struct max6650_data *data,
struct device *dev = &client->dev;
int config;
int err = -EIO;
+ u32 voltage;
+ u32 prescale;
+ u32 target_rpm;
+
+ if (of_property_read_u32(dev->of_node, "maxim,fan-microvolt",
+ &voltage))
+ voltage = fan_voltage;
+ else
+ voltage /= 1000000; /* Microvolts to volts */
+ if (of_property_read_u32(dev->of_node, "maxim,fan-prescale",
+ &prescale))
+ prescale = prescaler;
config = i2c_smbus_read_byte_data(client, MAX6650_REG_CONFIG);
@@ -574,7 +635,7 @@ static int max6650_init_client(struct max6650_data *data,
return err;
}
- switch (fan_voltage) {
+ switch (voltage) {
case 0:
break;
case 5:
@@ -584,14 +645,10 @@ static int max6650_init_client(struct max6650_data *data,
config |= MAX6650_CFG_V12;
break;
default:
- dev_err(dev, "illegal value for fan_voltage (%d)\n",
- fan_voltage);
+ dev_err(dev, "illegal value for fan_voltage (%d)\n", voltage);
}
- dev_info(dev, "Fan voltage is set to %dV.\n",
- (config & MAX6650_CFG_V12) ? 12 : 5);
-
- switch (prescaler) {
+ switch (prescale) {
case 0:
break;
case 1:
@@ -614,28 +671,13 @@ static int max6650_init_client(struct max6650_data *data,
| MAX6650_CFG_PRESCALER_16;
break;
default:
- dev_err(dev, "illegal value for prescaler (%d)\n", prescaler);
+ dev_err(dev, "illegal value for prescaler (%d)\n", prescale);
}
- dev_info(dev, "Prescaler is set to %d.\n",
+ dev_info(dev, "Fan voltage: %dV, prescaler: %d.\n",
+ (config & MAX6650_CFG_V12) ? 12 : 5,
1 << (config & MAX6650_CFG_PRESCALER_MASK));
- /*
- * If mode is set to "full off", we change it to "open loop" and
- * set DAC to 255, which has the same effect. We do this because
- * there's no "full off" mode defined in hwmon specifications.
- */
-
- if ((config & MAX6650_CFG_MODE_MASK) == MAX6650_CFG_MODE_OFF) {
- dev_dbg(dev, "Change mode to open loop, full off.\n");
- config = (config & ~MAX6650_CFG_MODE_MASK)
- | MAX6650_CFG_MODE_OPEN_LOOP;
- if (i2c_smbus_write_byte_data(client, MAX6650_REG_DAC, 255)) {
- dev_err(dev, "DAC write error, aborting.\n");
- return err;
- }
- }
-
if (i2c_smbus_write_byte_data(client, MAX6650_REG_CONFIG, config)) {
dev_err(dev, "Config write error, aborting.\n");
return err;
@@ -644,6 +686,12 @@ static int max6650_init_client(struct max6650_data *data,
data->config = config;
data->count = i2c_smbus_read_byte_data(client, MAX6650_REG_COUNT);
+ if (!of_property_read_u32(client->dev.of_node, "maxim,fan-target-rpm",
+ &target_rpm)) {
+ max6650_set_target(data, target_rpm);
+ max6650_set_operating_mode(data, MAX6650_CFG_MODE_CLOSED_LOOP);
+ }
+
return 0;
}
@@ -651,6 +699,8 @@ static int max6650_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
+ const struct of_device_id *of_id =
+ of_match_device(of_match_ptr(max6650_dt_match), dev);
struct max6650_data *data;
struct device *hwmon_dev;
int err;
@@ -661,7 +711,7 @@ static int max6650_probe(struct i2c_client *client,
data->client = client;
mutex_init(&data->update_lock);
- data->nr_fans = id->driver_data;
+ data->nr_fans = of_id ? (int)(uintptr_t)of_id->data : id->driver_data;
/*
* Initialize the max6650 chip
@@ -691,6 +741,7 @@ MODULE_DEVICE_TABLE(i2c, max6650_id);
static struct i2c_driver max6650_driver = {
.driver = {
.name = "max6650",
+ .of_match_table = of_match_ptr(max6650_dt_match),
},
.probe = max6650_probe,
.id_table = max6650_id,
diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c
index d087a8e00cf5..ce75dd4db7eb 100644
--- a/drivers/hwmon/nct6775.c
+++ b/drivers/hwmon/nct6775.c
@@ -195,6 +195,8 @@ superio_exit(int ioreg)
#define NUM_FAN 6
+#define TEMP_SOURCE_VIRTUAL 0x1f
+
/* Common and NCT6775 specific data */
/* Voltage min/max registers for nr=7..14 are in bank 5 */
@@ -3940,7 +3942,7 @@ static int nct6775_probe(struct platform_device *pdev)
continue;
src = nct6775_read_value(data, data->REG_TEMP_SEL[i]) & 0x1f;
- if (!src || (mask & (1 << src)))
+ if (!src)
continue;
if (src >= data->temp_label_num ||
@@ -3952,7 +3954,16 @@ static int nct6775_probe(struct platform_device *pdev)
continue;
}
- mask |= 1 << src;
+ /*
+ * For virtual temperature sources, the 'virtual' temperature
+ * for each fan reflects a different temperature, and there
+ * are no duplicates.
+ */
+ if (src != TEMP_SOURCE_VIRTUAL) {
+ if (mask & (1 << src))
+ continue;
+ mask |= 1 << src;
+ }
/* Use fixed index for SYSTIN(1), CPUTIN(2), AUXTIN(3) */
if (src <= data->temp_fixed_num) {
@@ -4232,11 +4243,11 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data)
if (err)
return err;
- if (force_id)
+ val = (superio_inb(sioaddr, SIO_REG_DEVID) << 8) |
+ superio_inb(sioaddr, SIO_REG_DEVID + 1);
+ if (force_id && val != 0xffff)
val = force_id;
- else
- val = (superio_inb(sioaddr, SIO_REG_DEVID) << 8)
- | superio_inb(sioaddr, SIO_REG_DEVID + 1);
+
switch (val & SIO_ID_MASK) {
case SIO_NCT6106_ID:
sio_data->kind = nct6106;
diff --git a/drivers/hwmon/nct7904.c b/drivers/hwmon/nct7904.c
index 08ff89d222e5..95a68ab175c7 100644
--- a/drivers/hwmon/nct7904.c
+++ b/drivers/hwmon/nct7904.c
@@ -21,7 +21,6 @@
#include <linux/i2c.h>
#include <linux/mutex.h>
#include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
#define VENDOR_ID_REG 0x7A /* Any bank */
#define NUVOTON_ID 0x50
@@ -153,341 +152,230 @@ static int nct7904_write_reg(struct nct7904_data *data,
return ret;
}
-/* FANIN ATTR */
-static ssize_t show_fan(struct device *dev,
- struct device_attribute *devattr, char *buf)
+static int nct7904_read_fan(struct device *dev, u32 attr, int channel,
+ long *val)
{
- int index = to_sensor_dev_attr(devattr)->index;
struct nct7904_data *data = dev_get_drvdata(dev);
+ unsigned int cnt, rpm;
int ret;
- unsigned cnt, rpm;
- ret = nct7904_read_reg16(data, BANK_0, FANIN1_HV_REG + index * 2);
- if (ret < 0)
- return ret;
- cnt = ((ret & 0xff00) >> 3) | (ret & 0x1f);
- if (cnt == 0x1fff)
- rpm = 0;
- else
- rpm = 1350000 / cnt;
- return sprintf(buf, "%u\n", rpm);
+ switch(attr) {
+ case hwmon_fan_input:
+ ret = nct7904_read_reg16(data, BANK_0,
+ FANIN1_HV_REG + channel * 2);
+ if (ret < 0)
+ return ret;
+ cnt = ((ret & 0xff00) >> 3) | (ret & 0x1f);
+ if (cnt == 0x1fff)
+ rpm = 0;
+ else
+ rpm = 1350000 / cnt;
+ *val = rpm;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static umode_t nct7904_fanin_is_visible(struct kobject *kobj,
- struct attribute *a, int n)
+static umode_t nct7904_fan_is_visible(const void *_data, u32 attr, int channel)
{
- struct device *dev = container_of(kobj, struct device, kobj);
- struct nct7904_data *data = dev_get_drvdata(dev);
+ const struct nct7904_data *data = _data;
- if (data->fanin_mask & (1 << n))
- return a->mode;
+ if (attr == hwmon_fan_input && data->fanin_mask & (1 << channel))
+ return S_IRUGO;
return 0;
}
-static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0);
-static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1);
-static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2);
-static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 3);
-static SENSOR_DEVICE_ATTR(fan5_input, S_IRUGO, show_fan, NULL, 4);
-static SENSOR_DEVICE_ATTR(fan6_input, S_IRUGO, show_fan, NULL, 5);
-static SENSOR_DEVICE_ATTR(fan7_input, S_IRUGO, show_fan, NULL, 6);
-static SENSOR_DEVICE_ATTR(fan8_input, S_IRUGO, show_fan, NULL, 7);
-static SENSOR_DEVICE_ATTR(fan9_input, S_IRUGO, show_fan, NULL, 8);
-static SENSOR_DEVICE_ATTR(fan10_input, S_IRUGO, show_fan, NULL, 9);
-static SENSOR_DEVICE_ATTR(fan11_input, S_IRUGO, show_fan, NULL, 10);
-static SENSOR_DEVICE_ATTR(fan12_input, S_IRUGO, show_fan, NULL, 11);
-
-static struct attribute *nct7904_fanin_attrs[] = {
- &sensor_dev_attr_fan1_input.dev_attr.attr,
- &sensor_dev_attr_fan2_input.dev_attr.attr,
- &sensor_dev_attr_fan3_input.dev_attr.attr,
- &sensor_dev_attr_fan4_input.dev_attr.attr,
- &sensor_dev_attr_fan5_input.dev_attr.attr,
- &sensor_dev_attr_fan6_input.dev_attr.attr,
- &sensor_dev_attr_fan7_input.dev_attr.attr,
- &sensor_dev_attr_fan8_input.dev_attr.attr,
- &sensor_dev_attr_fan9_input.dev_attr.attr,
- &sensor_dev_attr_fan10_input.dev_attr.attr,
- &sensor_dev_attr_fan11_input.dev_attr.attr,
- &sensor_dev_attr_fan12_input.dev_attr.attr,
- NULL
-};
-
-static const struct attribute_group nct7904_fanin_group = {
- .attrs = nct7904_fanin_attrs,
- .is_visible = nct7904_fanin_is_visible,
+static u8 nct7904_chan_to_index[] = {
+ 0, /* Not used */
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 18, 19, 20, 16
};
-/* VSEN ATTR */
-static ssize_t show_voltage(struct device *dev,
- struct device_attribute *devattr, char *buf)
+static int nct7904_read_in(struct device *dev, u32 attr, int channel,
+ long *val)
{
- int index = to_sensor_dev_attr(devattr)->index;
struct nct7904_data *data = dev_get_drvdata(dev);
- int ret;
- int volt;
+ int ret, volt, index;
- ret = nct7904_read_reg16(data, BANK_0, VSEN1_HV_REG + index * 2);
- if (ret < 0)
- return ret;
- volt = ((ret & 0xff00) >> 5) | (ret & 0x7);
- if (index < 14)
- volt *= 2; /* 0.002V scale */
- else
- volt *= 6; /* 0.006V scale */
+ index = nct7904_chan_to_index[channel];
- return sprintf(buf, "%d\n", volt);
+ switch(attr) {
+ case hwmon_in_input:
+ ret = nct7904_read_reg16(data, BANK_0,
+ VSEN1_HV_REG + index * 2);
+ if (ret < 0)
+ return ret;
+ volt = ((ret & 0xff00) >> 5) | (ret & 0x7);
+ if (index < 14)
+ volt *= 2; /* 0.002V scale */
+ else
+ volt *= 6; /* 0.006V scale */
+ *val = volt;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static ssize_t show_ltemp(struct device *dev,
- struct device_attribute *devattr, char *buf)
+static umode_t nct7904_in_is_visible(const void *_data, u32 attr, int channel)
{
- struct nct7904_data *data = dev_get_drvdata(dev);
- int ret;
- int temp;
+ const struct nct7904_data *data = _data;
+ int index = nct7904_chan_to_index[channel];
- ret = nct7904_read_reg16(data, BANK_0, LTD_HV_REG);
- if (ret < 0)
- return ret;
- temp = ((ret & 0xff00) >> 5) | (ret & 0x7);
- temp = sign_extend32(temp, 10) * 125;
-
- return sprintf(buf, "%d\n", temp);
-}
-
-static umode_t nct7904_vsen_is_visible(struct kobject *kobj,
- struct attribute *a, int n)
-{
- struct device *dev = container_of(kobj, struct device, kobj);
- struct nct7904_data *data = dev_get_drvdata(dev);
+ if (channel > 0 && attr == hwmon_in_input &&
+ (data->vsen_mask & BIT(index)))
+ return S_IRUGO;
- if (data->vsen_mask & (1 << n))
- return a->mode;
return 0;
}
-static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_voltage, NULL, 0);
-static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_voltage, NULL, 1);
-static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_voltage, NULL, 2);
-static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, show_voltage, NULL, 3);
-static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, show_voltage, NULL, 4);
-static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, show_voltage, NULL, 5);
-static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, show_voltage, NULL, 6);
-static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, show_voltage, NULL, 7);
-static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, show_voltage, NULL, 8);
-static SENSOR_DEVICE_ATTR(in10_input, S_IRUGO, show_voltage, NULL, 9);
-static SENSOR_DEVICE_ATTR(in11_input, S_IRUGO, show_voltage, NULL, 10);
-static SENSOR_DEVICE_ATTR(in12_input, S_IRUGO, show_voltage, NULL, 11);
-static SENSOR_DEVICE_ATTR(in13_input, S_IRUGO, show_voltage, NULL, 12);
-static SENSOR_DEVICE_ATTR(in14_input, S_IRUGO, show_voltage, NULL, 13);
-/*
- * Next 3 voltage sensors have specific names in the Nuvoton doc
- * (3VDD, VBAT, 3VSB) but we use vacant numbers for them.
- */
-static SENSOR_DEVICE_ATTR(in15_input, S_IRUGO, show_voltage, NULL, 14);
-static SENSOR_DEVICE_ATTR(in16_input, S_IRUGO, show_voltage, NULL, 15);
-static SENSOR_DEVICE_ATTR(in20_input, S_IRUGO, show_voltage, NULL, 16);
-/* This is not a voltage, but a local temperature sensor. */
-static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_ltemp, NULL, 0);
-static SENSOR_DEVICE_ATTR(in17_input, S_IRUGO, show_voltage, NULL, 18);
-static SENSOR_DEVICE_ATTR(in18_input, S_IRUGO, show_voltage, NULL, 19);
-static SENSOR_DEVICE_ATTR(in19_input, S_IRUGO, show_voltage, NULL, 20);
-
-static struct attribute *nct7904_vsen_attrs[] = {
- &sensor_dev_attr_in1_input.dev_attr.attr,
- &sensor_dev_attr_in2_input.dev_attr.attr,
- &sensor_dev_attr_in3_input.dev_attr.attr,
- &sensor_dev_attr_in4_input.dev_attr.attr,
- &sensor_dev_attr_in5_input.dev_attr.attr,
- &sensor_dev_attr_in6_input.dev_attr.attr,
- &sensor_dev_attr_in7_input.dev_attr.attr,
- &sensor_dev_attr_in8_input.dev_attr.attr,
- &sensor_dev_attr_in9_input.dev_attr.attr,
- &sensor_dev_attr_in10_input.dev_attr.attr,
- &sensor_dev_attr_in11_input.dev_attr.attr,
- &sensor_dev_attr_in12_input.dev_attr.attr,
- &sensor_dev_attr_in13_input.dev_attr.attr,
- &sensor_dev_attr_in14_input.dev_attr.attr,
- &sensor_dev_attr_in15_input.dev_attr.attr,
- &sensor_dev_attr_in16_input.dev_attr.attr,
- &sensor_dev_attr_in20_input.dev_attr.attr,
- &sensor_dev_attr_temp1_input.dev_attr.attr,
- &sensor_dev_attr_in17_input.dev_attr.attr,
- &sensor_dev_attr_in18_input.dev_attr.attr,
- &sensor_dev_attr_in19_input.dev_attr.attr,
- NULL
-};
-
-static const struct attribute_group nct7904_vsen_group = {
- .attrs = nct7904_vsen_attrs,
- .is_visible = nct7904_vsen_is_visible,
-};
-
-/* CPU_TEMP ATTR */
-static ssize_t show_tcpu(struct device *dev,
- struct device_attribute *devattr, char *buf)
+static int nct7904_read_temp(struct device *dev, u32 attr, int channel,
+ long *val)
{
- int index = to_sensor_dev_attr(devattr)->index;
struct nct7904_data *data = dev_get_drvdata(dev);
- int ret;
- int temp;
-
- ret = nct7904_read_reg16(data, BANK_0, T_CPU1_HV_REG + index * 2);
- if (ret < 0)
- return ret;
-
- temp = ((ret & 0xff00) >> 5) | (ret & 0x7);
- temp = sign_extend32(temp, 10) * 125;
- return sprintf(buf, "%d\n", temp);
+ int ret, temp;
+
+ switch(attr) {
+ case hwmon_temp_input:
+ if (channel == 0)
+ ret = nct7904_read_reg16(data, BANK_0, LTD_HV_REG);
+ else
+ ret = nct7904_read_reg16(data, BANK_0,
+ T_CPU1_HV_REG + (channel - 1) * 2);
+ if (ret < 0)
+ return ret;
+ temp = ((ret & 0xff00) >> 5) | (ret & 0x7);
+ *val = sign_extend32(temp, 10) * 125;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static umode_t nct7904_tcpu_is_visible(struct kobject *kobj,
- struct attribute *a, int n)
+static umode_t nct7904_temp_is_visible(const void *_data, u32 attr, int channel)
{
- struct device *dev = container_of(kobj, struct device, kobj);
- struct nct7904_data *data = dev_get_drvdata(dev);
+ const struct nct7904_data *data = _data;
+
+ if (attr == hwmon_temp_input) {
+ if (channel == 0) {
+ if (data->vsen_mask & BIT(17))
+ return S_IRUGO;
+ } else {
+ if (data->tcpu_mask & BIT(channel - 1))
+ return S_IRUGO;
+ }
+ }
- if (data->tcpu_mask & (1 << n))
- return a->mode;
return 0;
}
-/* "temp1_input" reserved for local temp */
-static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_tcpu, NULL, 0);
-static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_tcpu, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_tcpu, NULL, 2);
-static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, show_tcpu, NULL, 3);
-static SENSOR_DEVICE_ATTR(temp6_input, S_IRUGO, show_tcpu, NULL, 4);
-static SENSOR_DEVICE_ATTR(temp7_input, S_IRUGO, show_tcpu, NULL, 5);
-static SENSOR_DEVICE_ATTR(temp8_input, S_IRUGO, show_tcpu, NULL, 6);
-static SENSOR_DEVICE_ATTR(temp9_input, S_IRUGO, show_tcpu, NULL, 7);
-
-static struct attribute *nct7904_tcpu_attrs[] = {
- &sensor_dev_attr_temp2_input.dev_attr.attr,
- &sensor_dev_attr_temp3_input.dev_attr.attr,
- &sensor_dev_attr_temp4_input.dev_attr.attr,
- &sensor_dev_attr_temp5_input.dev_attr.attr,
- &sensor_dev_attr_temp6_input.dev_attr.attr,
- &sensor_dev_attr_temp7_input.dev_attr.attr,
- &sensor_dev_attr_temp8_input.dev_attr.attr,
- &sensor_dev_attr_temp9_input.dev_attr.attr,
- NULL
-};
-
-static const struct attribute_group nct7904_tcpu_group = {
- .attrs = nct7904_tcpu_attrs,
- .is_visible = nct7904_tcpu_is_visible,
-};
-
-/* PWM ATTR */
-static ssize_t store_pwm(struct device *dev, struct device_attribute *devattr,
- const char *buf, size_t count)
+static int nct7904_read_pwm(struct device *dev, u32 attr, int channel,
+ long *val)
{
- int index = to_sensor_dev_attr(devattr)->index;
struct nct7904_data *data = dev_get_drvdata(dev);
- unsigned long val;
int ret;
- if (kstrtoul(buf, 10, &val) < 0)
- return -EINVAL;
- if (val > 255)
- return -EINVAL;
-
- ret = nct7904_write_reg(data, BANK_3, FANCTL1_OUT_REG + index, val);
+ switch(attr) {
+ case hwmon_pwm_input:
+ ret = nct7904_read_reg(data, BANK_3, FANCTL1_OUT_REG + channel);
+ if (ret < 0)
+ return ret;
+ *val = ret;
+ return 0;
+ case hwmon_pwm_enable:
+ ret = nct7904_read_reg(data, BANK_3, FANCTL1_FMR_REG + channel);
+ if (ret < 0)
+ return ret;
- return ret ? ret : count;
+ *val = ret ? 2 : 1;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static ssize_t show_pwm(struct device *dev,
- struct device_attribute *devattr, char *buf)
+static int nct7904_write_pwm(struct device *dev, u32 attr, int channel,
+ long val)
{
- int index = to_sensor_dev_attr(devattr)->index;
struct nct7904_data *data = dev_get_drvdata(dev);
- int val;
-
- val = nct7904_read_reg(data, BANK_3, FANCTL1_OUT_REG + index);
- if (val < 0)
- return val;
+ int ret;
- return sprintf(buf, "%d\n", val);
+ switch(attr) {
+ case hwmon_pwm_input:
+ if (val < 0 || val > 255)
+ return -EINVAL;
+ ret = nct7904_write_reg(data, BANK_3, FANCTL1_OUT_REG + channel,
+ val);
+ return ret;
+ case hwmon_pwm_enable:
+ if (val < 1 || val > 2 ||
+ (val == 2 && !data->fan_mode[channel]))
+ return -EINVAL;
+ ret = nct7904_write_reg(data, BANK_3, FANCTL1_FMR_REG + channel,
+ val == 2 ? data->fan_mode[channel] : 0);
+ return ret;
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static ssize_t store_enable(struct device *dev,
- struct device_attribute *devattr,
- const char *buf, size_t count)
+static umode_t nct7904_pwm_is_visible(const void *_data, u32 attr, int channel)
{
- int index = to_sensor_dev_attr(devattr)->index;
- struct nct7904_data *data = dev_get_drvdata(dev);
- unsigned long val;
- int ret;
-
- if (kstrtoul(buf, 10, &val) < 0)
- return -EINVAL;
- if (val < 1 || val > 2 || (val == 2 && !data->fan_mode[index]))
- return -EINVAL;
-
- ret = nct7904_write_reg(data, BANK_3, FANCTL1_FMR_REG + index,
- val == 2 ? data->fan_mode[index] : 0);
-
- return ret ? ret : count;
+ switch(attr) {
+ case hwmon_pwm_input:
+ case hwmon_pwm_enable:
+ return S_IRUGO | S_IWUSR;
+ default:
+ return 0;
+ }
}
-/* Return 1 for manual mode or 2 for SmartFan mode */
-static ssize_t show_enable(struct device *dev,
- struct device_attribute *devattr, char *buf)
+static int nct7904_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
{
- int index = to_sensor_dev_attr(devattr)->index;
- struct nct7904_data *data = dev_get_drvdata(dev);
- int val;
-
- val = nct7904_read_reg(data, BANK_3, FANCTL1_FMR_REG + index);
- if (val < 0)
- return val;
-
- return sprintf(buf, "%d\n", val ? 2 : 1);
+ switch (type) {
+ case hwmon_in:
+ return nct7904_read_in(dev, attr, channel, val);
+ case hwmon_fan:
+ return nct7904_read_fan(dev, attr, channel, val);
+ case hwmon_pwm:
+ return nct7904_read_pwm(dev, attr, channel, val);
+ case hwmon_temp:
+ return nct7904_read_temp(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
}
-/* 2 attributes per channel: pwm and mode */
-static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR,
- show_pwm, store_pwm, 0);
-static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR,
- show_enable, store_enable, 0);
-static SENSOR_DEVICE_ATTR(pwm2, S_IRUGO | S_IWUSR,
- show_pwm, store_pwm, 1);
-static SENSOR_DEVICE_ATTR(pwm2_enable, S_IRUGO | S_IWUSR,
- show_enable, store_enable, 1);
-static SENSOR_DEVICE_ATTR(pwm3, S_IRUGO | S_IWUSR,
- show_pwm, store_pwm, 2);
-static SENSOR_DEVICE_ATTR(pwm3_enable, S_IRUGO | S_IWUSR,
- show_enable, store_enable, 2);
-static SENSOR_DEVICE_ATTR(pwm4, S_IRUGO | S_IWUSR,
- show_pwm, store_pwm, 3);
-static SENSOR_DEVICE_ATTR(pwm4_enable, S_IRUGO | S_IWUSR,
- show_enable, store_enable, 3);
-
-static struct attribute *nct7904_fanctl_attrs[] = {
- &sensor_dev_attr_pwm1.dev_attr.attr,
- &sensor_dev_attr_pwm1_enable.dev_attr.attr,
- &sensor_dev_attr_pwm2.dev_attr.attr,
- &sensor_dev_attr_pwm2_enable.dev_attr.attr,
- &sensor_dev_attr_pwm3.dev_attr.attr,
- &sensor_dev_attr_pwm3_enable.dev_attr.attr,
- &sensor_dev_attr_pwm4.dev_attr.attr,
- &sensor_dev_attr_pwm4_enable.dev_attr.attr,
- NULL
-};
-
-static const struct attribute_group nct7904_fanctl_group = {
- .attrs = nct7904_fanctl_attrs,
-};
+static int nct7904_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
+{
+ switch (type) {
+ case hwmon_pwm:
+ return nct7904_write_pwm(dev, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
-static const struct attribute_group *nct7904_groups[] = {
- &nct7904_fanin_group,
- &nct7904_vsen_group,
- &nct7904_tcpu_group,
- &nct7904_fanctl_group,
- NULL
-};
+static umode_t nct7904_is_visible(const void *data,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel)
+{
+ switch (type) {
+ case hwmon_in:
+ return nct7904_in_is_visible(data, attr, channel);
+ case hwmon_fan:
+ return nct7904_fan_is_visible(data, attr, channel);
+ case hwmon_pwm:
+ return nct7904_pwm_is_visible(data, attr, channel);
+ case hwmon_temp:
+ return nct7904_temp_is_visible(data, attr, channel);
+ default:
+ return 0;
+ }
+}
/* Return 0 if detection is successful, -ENODEV otherwise */
static int nct7904_detect(struct i2c_client *client,
@@ -512,6 +400,103 @@ static int nct7904_detect(struct i2c_client *client,
return 0;
}
+static const u32 nct7904_in_config[] = {
+ HWMON_I_INPUT, /* dummy, skipped in is_visible */
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ HWMON_I_INPUT,
+ 0
+};
+
+static const struct hwmon_channel_info nct7904_in = {
+ .type = hwmon_in,
+ .config = nct7904_in_config,
+};
+
+static const u32 nct7904_fan_config[] = {
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ HWMON_F_INPUT,
+ 0
+};
+
+static const struct hwmon_channel_info nct7904_fan = {
+ .type = hwmon_fan,
+ .config = nct7904_fan_config,
+};
+
+static const u32 nct7904_pwm_config[] = {
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
+ 0
+};
+
+static const struct hwmon_channel_info nct7904_pwm = {
+ .type = hwmon_pwm,
+ .config = nct7904_pwm_config,
+};
+
+static const u32 nct7904_temp_config[] = {
+ HWMON_T_INPUT,
+ HWMON_T_INPUT,
+ HWMON_T_INPUT,
+ HWMON_T_INPUT,
+ HWMON_T_INPUT,
+ HWMON_T_INPUT,
+ HWMON_T_INPUT,
+ HWMON_T_INPUT,
+ HWMON_T_INPUT,
+ 0
+};
+
+static const struct hwmon_channel_info nct7904_temp = {
+ .type = hwmon_temp,
+ .config = nct7904_temp_config,
+};
+
+static const struct hwmon_channel_info *nct7904_info[] = {
+ &nct7904_in,
+ &nct7904_fan,
+ &nct7904_pwm,
+ &nct7904_temp,
+ NULL
+};
+
+static const struct hwmon_ops nct7904_hwmon_ops = {
+ .is_visible = nct7904_is_visible,
+ .read = nct7904_read,
+ .write = nct7904_write,
+};
+
+static const struct hwmon_chip_info nct7904_chip_info = {
+ .ops = &nct7904_hwmon_ops,
+ .info = nct7904_info,
+};
+
static int nct7904_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -566,8 +551,8 @@ static int nct7904_probe(struct i2c_client *client,
}
hwmon_dev =
- devm_hwmon_device_register_with_groups(dev, client->name, data,
- nct7904_groups);
+ devm_hwmon_device_register_with_info(dev, client->name, data,
+ &nct7904_chip_info, NULL);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c
index 8ef7b713cb1a..c52d07c6b49f 100644
--- a/drivers/hwmon/ntc_thermistor.c
+++ b/drivers/hwmon/ntc_thermistor.c
@@ -253,12 +253,9 @@ static const struct ntc_compensation b57330v2103[] = {
};
struct ntc_data {
- struct device *hwmon_dev;
struct ntc_thermistor_platform_data *pdata;
const struct ntc_compensation *comp;
- struct device *dev;
int n_comp;
- char name[PLATFORM_NAME_SIZE];
};
#if defined(CONFIG_OF) && IS_ENABLED(CONFIG_IIO)
@@ -316,22 +313,22 @@ static const struct of_device_id ntc_match[] = {
MODULE_DEVICE_TABLE(of, ntc_match);
static struct ntc_thermistor_platform_data *
-ntc_thermistor_parse_dt(struct platform_device *pdev)
+ntc_thermistor_parse_dt(struct device *dev)
{
struct iio_channel *chan;
enum iio_chan_type type;
- struct device_node *np = pdev->dev.of_node;
+ struct device_node *np = dev->of_node;
struct ntc_thermistor_platform_data *pdata;
int ret;
if (!np)
return NULL;
- pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return ERR_PTR(-ENOMEM);
- chan = iio_channel_get(&pdev->dev, NULL);
+ chan = devm_iio_channel_get(dev, NULL);
if (IS_ERR(chan))
return ERR_CAST(chan);
@@ -359,22 +356,15 @@ ntc_thermistor_parse_dt(struct platform_device *pdev)
return pdata;
}
-static void ntc_iio_channel_release(struct ntc_thermistor_platform_data *pdata)
-{
- if (pdata->chan)
- iio_channel_release(pdata->chan);
-}
#else
static struct ntc_thermistor_platform_data *
-ntc_thermistor_parse_dt(struct platform_device *pdev)
+ntc_thermistor_parse_dt(struct device *dev)
{
return NULL;
}
#define ntc_match NULL
-static void ntc_iio_channel_release(struct ntc_thermistor_platform_data *pdata)
-{ }
#endif
static inline u64 div64_u64_safe(u64 dividend, u64 divisor)
@@ -516,9 +506,8 @@ static int ntc_thermistor_get_ohm(struct ntc_data *data)
return -EINVAL;
}
-static int ntc_read_temp(void *dev, int *temp)
+static int ntc_read_temp(void *data, int *temp)
{
- struct ntc_data *data = dev_get_drvdata(dev);
int ohm;
ohm = ntc_thermistor_get_ohm(data);
@@ -530,14 +519,6 @@ static int ntc_read_temp(void *dev, int *temp)
return 0;
}
-static ssize_t ntc_show_name(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct ntc_data *data = dev_get_drvdata(dev);
-
- return sprintf(buf, "%s\n", data->name);
-}
-
static ssize_t ntc_show_type(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -559,18 +540,13 @@ static ssize_t ntc_show_temp(struct device *dev,
static SENSOR_DEVICE_ATTR(temp1_type, S_IRUGO, ntc_show_type, NULL, 0);
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, ntc_show_temp, NULL, 0);
-static DEVICE_ATTR(name, S_IRUGO, ntc_show_name, NULL);
-static struct attribute *ntc_attributes[] = {
- &dev_attr_name.attr,
+static struct attribute *ntc_attrs[] = {
&sensor_dev_attr_temp1_type.dev_attr.attr,
&sensor_dev_attr_temp1_input.dev_attr.attr,
NULL,
};
-
-static const struct attribute_group ntc_attr_group = {
- .attrs = ntc_attributes,
-};
+ATTRIBUTE_GROUPS(ntc);
static const struct thermal_zone_of_device_ops ntc_of_thermal_ops = {
.get_temp = ntc_read_temp,
@@ -579,33 +555,34 @@ static const struct thermal_zone_of_device_ops ntc_of_thermal_ops = {
static int ntc_thermistor_probe(struct platform_device *pdev)
{
struct thermal_zone_device *tz;
+ struct device *dev = &pdev->dev;
const struct of_device_id *of_id =
- of_match_device(of_match_ptr(ntc_match), &pdev->dev);
+ of_match_device(of_match_ptr(ntc_match), dev);
const struct platform_device_id *pdev_id;
struct ntc_thermistor_platform_data *pdata;
+ struct device *hwmon_dev;
struct ntc_data *data;
- int ret;
- pdata = ntc_thermistor_parse_dt(pdev);
+ pdata = ntc_thermistor_parse_dt(dev);
if (IS_ERR(pdata))
return PTR_ERR(pdata);
else if (pdata == NULL)
- pdata = dev_get_platdata(&pdev->dev);
+ pdata = dev_get_platdata(dev);
if (!pdata) {
- dev_err(&pdev->dev, "No platform init data supplied.\n");
+ dev_err(dev, "No platform init data supplied.\n");
return -ENODEV;
}
/* Either one of the two is required. */
if (!pdata->read_uv && !pdata->read_ohm) {
- dev_err(&pdev->dev,
+ dev_err(dev,
"Both read_uv and read_ohm missing. Need either one of the two.\n");
return -EINVAL;
}
if (pdata->read_uv && pdata->read_ohm) {
- dev_warn(&pdev->dev,
+ dev_warn(dev,
"Only one of read_uv and read_ohm is needed; ignoring read_uv.\n");
pdata->read_uv = NULL;
}
@@ -617,20 +594,17 @@ static int ntc_thermistor_probe(struct platform_device *pdev)
NTC_CONNECTED_POSITIVE) ||
(pdata->connect != NTC_CONNECTED_POSITIVE &&
pdata->connect != NTC_CONNECTED_GROUND))) {
- dev_err(&pdev->dev,
- "Required data to use read_uv not supplied.\n");
+ dev_err(dev, "Required data to use read_uv not supplied.\n");
return -EINVAL;
}
- data = devm_kzalloc(&pdev->dev, sizeof(struct ntc_data), GFP_KERNEL);
+ data = devm_kzalloc(dev, sizeof(struct ntc_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
pdev_id = of_id ? of_id->data : platform_get_device_id(pdev);
- data->dev = &pdev->dev;
data->pdata = pdata;
- strlcpy(data->name, pdev_id->name, sizeof(data->name));
switch (pdev_id->driver_data) {
case TYPE_NCPXXWB473:
@@ -654,49 +628,25 @@ static int ntc_thermistor_probe(struct platform_device *pdev)
data->n_comp = ARRAY_SIZE(ncpXXxh103);
break;
default:
- dev_err(&pdev->dev, "Unknown device type: %lu(%s)\n",
+ dev_err(dev, "Unknown device type: %lu(%s)\n",
pdev_id->driver_data, pdev_id->name);
return -EINVAL;
}
- platform_set_drvdata(pdev, data);
-
- ret = sysfs_create_group(&data->dev->kobj, &ntc_attr_group);
- if (ret) {
- dev_err(data->dev, "unable to create sysfs files\n");
- return ret;
- }
-
- data->hwmon_dev = hwmon_device_register(data->dev);
- if (IS_ERR(data->hwmon_dev)) {
- dev_err(data->dev, "unable to register as hwmon device.\n");
- ret = PTR_ERR(data->hwmon_dev);
- goto err_after_sysfs;
+ hwmon_dev = devm_hwmon_device_register_with_groups(dev, pdev_id->name,
+ data, ntc_groups);
+ if (IS_ERR(hwmon_dev)) {
+ dev_err(dev, "unable to register as hwmon device.\n");
+ return PTR_ERR(hwmon_dev);
}
- dev_info(&pdev->dev, "Thermistor type: %s successfully probed.\n",
- pdev_id->name);
+ dev_info(dev, "Thermistor type: %s successfully probed.\n",
+ pdev_id->name);
- tz = devm_thermal_zone_of_sensor_register(data->dev, 0, data->dev,
+ tz = devm_thermal_zone_of_sensor_register(dev, 0, data,
&ntc_of_thermal_ops);
if (IS_ERR(tz))
- dev_dbg(&pdev->dev, "Failed to register to thermal fw.\n");
-
- return 0;
-err_after_sysfs:
- sysfs_remove_group(&data->dev->kobj, &ntc_attr_group);
- ntc_iio_channel_release(pdata);
- return ret;
-}
-
-static int ntc_thermistor_remove(struct platform_device *pdev)
-{
- struct ntc_data *data = platform_get_drvdata(pdev);
- struct ntc_thermistor_platform_data *pdata = data->pdata;
-
- hwmon_device_unregister(data->hwmon_dev);
- sysfs_remove_group(&data->dev->kobj, &ntc_attr_group);
- ntc_iio_channel_release(pdata);
+ dev_dbg(dev, "Failed to register to thermal fw.\n");
return 0;
}
@@ -707,7 +657,6 @@ static struct platform_driver ntc_thermistor_driver = {
.of_match_table = of_match_ptr(ntc_match),
},
.probe = ntc_thermistor_probe,
- .remove = ntc_thermistor_remove,
.id_table = ntc_thermistor_id,
};
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig
index 054d3d863802..cad1229b7e17 100644
--- a/drivers/hwmon/pmbus/Kconfig
+++ b/drivers/hwmon/pmbus/Kconfig
@@ -126,12 +126,12 @@ config SENSORS_TPS40422
be called tps40422.
config SENSORS_UCD9000
- tristate "TI UCD90120, UCD90124, UCD9090, UCD90910"
+ tristate "TI UCD90120, UCD90124, UCD90160, UCD9090, UCD90910"
default n
help
If you say yes here you get hardware monitoring support for TI
- UCD90120, UCD90124, UCD9090, UCD90910 Sequencer and System Health
- Controllers.
+ UCD90120, UCD90124, UCD90160, UCD9090, UCD90910, Sequencer and System
+ Health Controllers.
This driver can also be built as a module. If so, the module will
be called ucd9000.
diff --git a/drivers/hwmon/pmbus/pmbus.c b/drivers/hwmon/pmbus/pmbus.c
index 0a74991a60f0..44ca8a94873d 100644
--- a/drivers/hwmon/pmbus/pmbus.c
+++ b/drivers/hwmon/pmbus/pmbus.c
@@ -25,6 +25,7 @@
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/i2c.h>
+#include <linux/i2c/pmbus.h>
#include "pmbus.h"
/*
@@ -167,14 +168,26 @@ static int pmbus_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct pmbus_driver_info *info;
+ struct pmbus_platform_data *pdata = NULL;
+ struct device *dev = &client->dev;
- info = devm_kzalloc(&client->dev, sizeof(struct pmbus_driver_info),
- GFP_KERNEL);
+ info = devm_kzalloc(dev, sizeof(struct pmbus_driver_info), GFP_KERNEL);
if (!info)
return -ENOMEM;
+ if (!strcmp(id->name, "dps460") || !strcmp(id->name, "dps800") ||
+ !strcmp(id->name, "sgd009")) {
+ pdata = devm_kzalloc(dev, sizeof(struct pmbus_platform_data),
+ GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ pdata->flags = PMBUS_SKIP_STATUS_CHECK;
+ }
+
info->pages = id->driver_data;
info->identify = pmbus_identify;
+ dev->platform_data = pdata;
return pmbus_do_probe(client, id, info);
}
@@ -186,6 +199,8 @@ static const struct i2c_device_id pmbus_id[] = {
{"adp4000", 1},
{"bmr453", 1},
{"bmr454", 1},
+ {"dps460", 1},
+ {"dps800", 1},
{"mdt040", 1},
{"ncp4200", 1},
{"ncp4208", 1},
@@ -193,6 +208,7 @@ static const struct i2c_device_id pmbus_id[] = {
{"pdt006", 1},
{"pdt012", 1},
{"pmbus", 0},
+ {"sgd009", 1},
{"tps40400", 1},
{"tps544b20", 1},
{"tps544b25", 1},
diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c
index fbb1479d3ad4..3e3aa950277f 100644
--- a/drivers/hwmon/pmbus/ucd9000.c
+++ b/drivers/hwmon/pmbus/ucd9000.c
@@ -28,7 +28,7 @@
#include <linux/i2c/pmbus.h>
#include "pmbus.h"
-enum chips { ucd9000, ucd90120, ucd90124, ucd9090, ucd90910 };
+enum chips { ucd9000, ucd90120, ucd90124, ucd90160, ucd9090, ucd90910 };
#define UCD9000_MONITOR_CONFIG 0xd5
#define UCD9000_NUM_PAGES 0xd6
@@ -112,6 +112,7 @@ static const struct i2c_device_id ucd9000_id[] = {
{"ucd9000", ucd9000},
{"ucd90120", ucd90120},
{"ucd90124", ucd90124},
+ {"ucd90160", ucd90160},
{"ucd9090", ucd9090},
{"ucd90910", ucd90910},
{}
diff --git a/drivers/hwmon/scpi-hwmon.c b/drivers/hwmon/scpi-hwmon.c
index 25b44e68926d..559a3dcd64d8 100644
--- a/drivers/hwmon/scpi-hwmon.c
+++ b/drivers/hwmon/scpi-hwmon.c
@@ -255,7 +255,6 @@ static const struct of_device_id scpi_of_match[] = {
static struct platform_driver scpi_hwmon_platdrv = {
.driver = {
.name = "scpi-hwmon",
- .owner = THIS_MODULE,
.of_match_table = scpi_of_match,
},
.probe = scpi_hwmon_probe,
diff --git a/drivers/hwmon/tmp102.c b/drivers/hwmon/tmp102.c
index 8479ac5eb853..36bba2a816a4 100644
--- a/drivers/hwmon/tmp102.c
+++ b/drivers/hwmon/tmp102.c
@@ -25,7 +25,6 @@
#include <linux/device.h>
#include <linux/jiffies.h>
#include <linux/regmap.h>
-#include <linux/thermal.h>
#include <linux/of.h>
#define DRIVER_NAME "tmp102"
@@ -79,84 +78,113 @@ static inline u16 tmp102_mC_to_reg(int val)
return (val * 128) / 1000;
}
-static int tmp102_read_temp(void *dev, int *temp)
+static int tmp102_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *temp)
{
struct tmp102 *tmp102 = dev_get_drvdata(dev);
- unsigned int reg;
- int ret;
-
- if (time_before(jiffies, tmp102->ready_time)) {
- dev_dbg(dev, "%s: Conversion not ready yet..\n", __func__);
- return -EAGAIN;
+ unsigned int regval;
+ int err, reg;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ /* Is it too early to return a conversion ? */
+ if (time_before(jiffies, tmp102->ready_time)) {
+ dev_dbg(dev, "%s: Conversion not ready yet..\n", __func__);
+ return -EAGAIN;
+ }
+ reg = TMP102_TEMP_REG;
+ break;
+ case hwmon_temp_max_hyst:
+ reg = TMP102_TLOW_REG;
+ break;
+ case hwmon_temp_max:
+ reg = TMP102_THIGH_REG;
+ break;
+ default:
+ return -EOPNOTSUPP;
}
- ret = regmap_read(tmp102->regmap, TMP102_TEMP_REG, &reg);
- if (ret < 0)
- return ret;
-
- *temp = tmp102_reg_to_mC(reg);
+ err = regmap_read(tmp102->regmap, reg, &regval);
+ if (err < 0)
+ return err;
+ *temp = tmp102_reg_to_mC(regval);
return 0;
}
-static ssize_t tmp102_show_temp(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+static int tmp102_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long temp)
{
- struct sensor_device_attribute *sda = to_sensor_dev_attr(attr);
struct tmp102 *tmp102 = dev_get_drvdata(dev);
- int regaddr = sda->index;
- unsigned int reg;
- int err;
-
- if (regaddr == TMP102_TEMP_REG &&
- time_before(jiffies, tmp102->ready_time))
- return -EAGAIN;
-
- err = regmap_read(tmp102->regmap, regaddr, &reg);
- if (err < 0)
- return err;
+ int reg;
+
+ switch (attr) {
+ case hwmon_temp_max_hyst:
+ reg = TMP102_TLOW_REG;
+ break;
+ case hwmon_temp_max:
+ reg = TMP102_THIGH_REG;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
- return sprintf(buf, "%d\n", tmp102_reg_to_mC(reg));
+ temp = clamp_val(temp, -256000, 255000);
+ return regmap_write(tmp102->regmap, reg, tmp102_mC_to_reg(temp));
}
-static ssize_t tmp102_set_temp(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static umode_t tmp102_is_visible(const void *data, enum hwmon_sensor_types type,
+ u32 attr, int channel)
{
- struct sensor_device_attribute *sda = to_sensor_dev_attr(attr);
- struct tmp102 *tmp102 = dev_get_drvdata(dev);
- int reg = sda->index;
- long val;
- int err;
-
- if (kstrtol(buf, 10, &val) < 0)
- return -EINVAL;
- val = clamp_val(val, -256000, 255000);
-
- err = regmap_write(tmp102->regmap, reg, tmp102_mC_to_reg(val));
- return err ? : count;
+ if (type != hwmon_temp)
+ return 0;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ return S_IRUGO;
+ case hwmon_temp_max_hyst:
+ case hwmon_temp_max:
+ return S_IRUGO | S_IWUSR;
+ default:
+ return 0;
+ }
}
-static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, tmp102_show_temp, NULL,
- TMP102_TEMP_REG);
+static u32 tmp102_chip_config[] = {
+ HWMON_C_REGISTER_TZ,
+ 0
+};
+
+static const struct hwmon_channel_info tmp102_chip = {
+ .type = hwmon_chip,
+ .config = tmp102_chip_config,
+};
-static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, tmp102_show_temp,
- tmp102_set_temp, TMP102_TLOW_REG);
+static u32 tmp102_temp_config[] = {
+ HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST,
+ 0
+};
-static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, tmp102_show_temp,
- tmp102_set_temp, TMP102_THIGH_REG);
+static const struct hwmon_channel_info tmp102_temp = {
+ .type = hwmon_temp,
+ .config = tmp102_temp_config,
+};
-static struct attribute *tmp102_attrs[] = {
- &sensor_dev_attr_temp1_input.dev_attr.attr,
- &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
- &sensor_dev_attr_temp1_max.dev_attr.attr,
+static const struct hwmon_channel_info *tmp102_info[] = {
+ &tmp102_chip,
+ &tmp102_temp,
NULL
};
-ATTRIBUTE_GROUPS(tmp102);
-static const struct thermal_zone_of_device_ops tmp102_of_thermal_ops = {
- .get_temp = tmp102_read_temp,
+static const struct hwmon_ops tmp102_hwmon_ops = {
+ .is_visible = tmp102_is_visible,
+ .read = tmp102_read,
+ .write = tmp102_write,
+};
+
+static const struct hwmon_chip_info tmp102_chip_info = {
+ .ops = &tmp102_hwmon_ops,
+ .info = tmp102_info,
};
static void tmp102_restore_config(void *data)
@@ -188,7 +216,7 @@ static const struct regmap_config tmp102_regmap_config = {
};
static int tmp102_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+ const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct device *hwmon_dev;
@@ -249,16 +277,14 @@ static int tmp102_probe(struct i2c_client *client,
tmp102->ready_time += msecs_to_jiffies(CONVERSION_TIME_MS);
}
- hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
- tmp102,
- tmp102_groups);
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
+ tmp102,
+ &tmp102_chip_info,
+ NULL);
if (IS_ERR(hwmon_dev)) {
dev_dbg(dev, "unable to register hwmon device\n");
return PTR_ERR(hwmon_dev);
}
- devm_thermal_zone_of_sensor_register(hwmon_dev, 0, hwmon_dev,
- &tmp102_of_thermal_ops);
-
dev_info(dev, "initialized\n");
return 0;
diff --git a/drivers/hwmon/tmp421.c b/drivers/hwmon/tmp421.c
index 85d48d80822a..bfb98b96c781 100644
--- a/drivers/hwmon/tmp421.c
+++ b/drivers/hwmon/tmp421.c
@@ -72,6 +72,10 @@ MODULE_DEVICE_TABLE(i2c, tmp421_id);
struct tmp421_data {
struct i2c_client *client;
struct mutex update_lock;
+ u32 temp_config[5];
+ struct hwmon_channel_info temp_info;
+ const struct hwmon_channel_info *info[2];
+ struct hwmon_chip_info chip;
char valid;
unsigned long last_updated;
int channels;
@@ -125,85 +129,46 @@ static struct tmp421_data *tmp421_update_device(struct device *dev)
return data;
}
-static ssize_t show_temp_value(struct device *dev,
- struct device_attribute *devattr, char *buf)
+static int tmp421_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
{
- int index = to_sensor_dev_attr(devattr)->index;
- struct tmp421_data *data = tmp421_update_device(dev);
- int temp;
-
- mutex_lock(&data->update_lock);
- if (data->config & TMP421_CONFIG_RANGE)
- temp = temp_from_u16(data->temp[index]);
- else
- temp = temp_from_s16(data->temp[index]);
- mutex_unlock(&data->update_lock);
-
- return sprintf(buf, "%d\n", temp);
-}
+ struct tmp421_data *tmp421 = tmp421_update_device(dev);
+
+ switch (attr) {
+ case hwmon_temp_input:
+ if (tmp421->config & TMP421_CONFIG_RANGE)
+ *val = temp_from_u16(tmp421->temp[channel]);
+ else
+ *val = temp_from_s16(tmp421->temp[channel]);
+ return 0;
+ case hwmon_temp_fault:
+ /*
+ * The OPEN bit signals a fault. This is bit 0 of the temperature
+ * register (low byte).
+ */
+ *val = tmp421->temp[channel] & 0x01;
+ return 0;
+ default:
+ return -EOPNOTSUPP;
+ }
-static ssize_t show_fault(struct device *dev,
- struct device_attribute *devattr, char *buf)
-{
- int index = to_sensor_dev_attr(devattr)->index;
- struct tmp421_data *data = tmp421_update_device(dev);
-
- /*
- * The OPEN bit signals a fault. This is bit 0 of the temperature
- * register (low byte).
- */
- if (data->temp[index] & 0x01)
- return sprintf(buf, "1\n");
- else
- return sprintf(buf, "0\n");
}
-static umode_t tmp421_is_visible(struct kobject *kobj, struct attribute *a,
- int n)
+static umode_t tmp421_is_visible(const void *data, enum hwmon_sensor_types type,
+ u32 attr, int channel)
{
- struct device *dev = container_of(kobj, struct device, kobj);
- struct tmp421_data *data = dev_get_drvdata(dev);
- struct device_attribute *devattr;
- unsigned int index;
-
- devattr = container_of(a, struct device_attribute, attr);
- index = to_sensor_dev_attr(devattr)->index;
-
- if (index < data->channels)
- return a->mode;
-
- return 0;
+ switch (attr) {
+ case hwmon_temp_fault:
+ if (channel == 0)
+ return 0;
+ return S_IRUGO;
+ case hwmon_temp_input:
+ return S_IRUGO;
+ default:
+ return 0;
+ }
}
-static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_value, NULL, 0);
-static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp_value, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_fault, NULL, 1);
-static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp_value, NULL, 2);
-static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_fault, NULL, 2);
-static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp_value, NULL, 3);
-static SENSOR_DEVICE_ATTR(temp4_fault, S_IRUGO, show_fault, NULL, 3);
-
-static struct attribute *tmp421_attr[] = {
- &sensor_dev_attr_temp1_input.dev_attr.attr,
- &sensor_dev_attr_temp2_input.dev_attr.attr,
- &sensor_dev_attr_temp2_fault.dev_attr.attr,
- &sensor_dev_attr_temp3_input.dev_attr.attr,
- &sensor_dev_attr_temp3_fault.dev_attr.attr,
- &sensor_dev_attr_temp4_input.dev_attr.attr,
- &sensor_dev_attr_temp4_fault.dev_attr.attr,
- NULL
-};
-
-static const struct attribute_group tmp421_group = {
- .attrs = tmp421_attr,
- .is_visible = tmp421_is_visible,
-};
-
-static const struct attribute_group *tmp421_groups[] = {
- &tmp421_group,
- NULL
-};
-
static int tmp421_init_client(struct i2c_client *client)
{
int config, config_orig;
@@ -289,13 +254,18 @@ static int tmp421_detect(struct i2c_client *client,
return 0;
}
+static const struct hwmon_ops tmp421_ops = {
+ .is_visible = tmp421_is_visible,
+ .read = tmp421_read,
+};
+
static int tmp421_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct device *hwmon_dev;
struct tmp421_data *data;
- int err;
+ int i, err;
data = devm_kzalloc(dev, sizeof(struct tmp421_data), GFP_KERNEL);
if (!data)
@@ -309,8 +279,21 @@ static int tmp421_probe(struct i2c_client *client,
if (err)
return err;
- hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
- data, tmp421_groups);
+ for (i = 0; i < data->channels; i++)
+ data->temp_config[i] = HWMON_T_INPUT | HWMON_T_FAULT;
+
+ data->chip.ops = &tmp421_ops;
+ data->chip.info = data->info;
+
+ data->info[0] = &data->temp_info;
+
+ data->temp_info.type = hwmon_temp;
+ data->temp_info.config = data->temp_config;
+
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
+ data,
+ &data->chip,
+ NULL);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
diff --git a/drivers/hwmon/xgene-hwmon.c b/drivers/hwmon/xgene-hwmon.c
new file mode 100644
index 000000000000..9c0dbb8191ad
--- /dev/null
+++ b/drivers/hwmon/xgene-hwmon.c
@@ -0,0 +1,787 @@
+/*
+ * APM X-Gene SoC Hardware Monitoring Driver
+ *
+ * Copyright (c) 2016, Applied Micro Circuits Corporation
+ * Author: Loc Ho <lho@apm.com>
+ * Hoan Tran <hotran@apm.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * This driver provides the following features:
+ * - Retrieve CPU total power (uW)
+ * - Retrieve IO total power (uW)
+ * - Retrieve SoC temperature (milli-degree C) and alarm
+ */
+#include <linux/acpi.h>
+#include <linux/dma-mapping.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/kfifo.h>
+#include <linux/mailbox_controller.h>
+#include <linux/mailbox_client.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#include <acpi/pcc.h>
+
+/* SLIMpro message defines */
+#define MSG_TYPE_DBG 0
+#define MSG_TYPE_ERR 7
+#define MSG_TYPE_PWRMGMT 9
+
+#define MSG_TYPE(v) (((v) & 0xF0000000) >> 28)
+#define MSG_TYPE_SET(v) (((v) << 28) & 0xF0000000)
+#define MSG_SUBTYPE(v) (((v) & 0x0F000000) >> 24)
+#define MSG_SUBTYPE_SET(v) (((v) << 24) & 0x0F000000)
+
+#define DBG_SUBTYPE_SENSOR_READ 4
+#define SENSOR_RD_MSG 0x04FFE902
+#define SENSOR_RD_EN_ADDR(a) ((a) & 0x000FFFFF)
+#define PMD_PWR_REG 0x20
+#define PMD_PWR_MW_REG 0x26
+#define SOC_PWR_REG 0x21
+#define SOC_PWR_MW_REG 0x27
+#define SOC_TEMP_REG 0x10
+
+#define TEMP_NEGATIVE_BIT 8
+#define SENSOR_INVALID_DATA BIT(15)
+
+#define PWRMGMT_SUBTYPE_TPC 1
+#define TPC_ALARM 2
+#define TPC_GET_ALARM 3
+#define TPC_CMD(v) (((v) & 0x00FF0000) >> 16)
+#define TPC_CMD_SET(v) (((v) << 16) & 0x00FF0000)
+#define TPC_EN_MSG(hndl, cmd, type) \
+ (MSG_TYPE_SET(MSG_TYPE_PWRMGMT) | \
+ MSG_SUBTYPE_SET(hndl) | TPC_CMD_SET(cmd) | type)
+
+/* PCC defines */
+#define PCC_SIGNATURE_MASK 0x50424300
+#define PCCC_GENERATE_DB_INT BIT(15)
+#define PCCS_CMD_COMPLETE BIT(0)
+#define PCCS_SCI_DOORBEL BIT(1)
+#define PCCS_PLATFORM_NOTIFICATION BIT(3)
+/*
+ * Arbitrary retries in case the remote processor is slow to respond
+ * to PCC commands
+ */
+#define PCC_NUM_RETRIES 500
+
+#define ASYNC_MSG_FIFO_SIZE 16
+#define MBOX_OP_TIMEOUTMS 1000
+
+#define WATT_TO_mWATT(x) ((x) * 1000)
+#define mWATT_TO_uWATT(x) ((x) * 1000)
+#define CELSIUS_TO_mCELSIUS(x) ((x) * 1000)
+
+#define to_xgene_hwmon_dev(cl) \
+ container_of(cl, struct xgene_hwmon_dev, mbox_client)
+
+struct slimpro_resp_msg {
+ u32 msg;
+ u32 param1;
+ u32 param2;
+} __packed;
+
+struct xgene_hwmon_dev {
+ struct device *dev;
+ struct mbox_chan *mbox_chan;
+ struct mbox_client mbox_client;
+ int mbox_idx;
+
+ spinlock_t kfifo_lock;
+ struct mutex rd_mutex;
+ struct completion rd_complete;
+ int resp_pending;
+ struct slimpro_resp_msg sync_msg;
+
+ struct work_struct workq;
+ struct kfifo_rec_ptr_1 async_msg_fifo;
+
+ struct device *hwmon_dev;
+ bool temp_critical_alarm;
+
+ phys_addr_t comm_base_addr;
+ void *pcc_comm_addr;
+ u64 usecs_lat;
+};
+
+/*
+ * This function tests and clears a bitmask then returns its old value
+ */
+static u16 xgene_word_tst_and_clr(u16 *addr, u16 mask)
+{
+ u16 ret, val;
+
+ val = le16_to_cpu(READ_ONCE(*addr));
+ ret = val & mask;
+ val &= ~mask;
+ WRITE_ONCE(*addr, cpu_to_le16(val));
+
+ return ret;
+}
+
+static int xgene_hwmon_pcc_rd(struct xgene_hwmon_dev *ctx, u32 *msg)
+{
+ struct acpi_pcct_shared_memory *generic_comm_base = ctx->pcc_comm_addr;
+ u32 *ptr = (void *)(generic_comm_base + 1);
+ int rc, i;
+ u16 val;
+
+ mutex_lock(&ctx->rd_mutex);
+ init_completion(&ctx->rd_complete);
+ ctx->resp_pending = true;
+
+ /* Write signature for subspace */
+ WRITE_ONCE(generic_comm_base->signature,
+ cpu_to_le32(PCC_SIGNATURE_MASK | ctx->mbox_idx));
+
+ /* Write to the shared command region */
+ WRITE_ONCE(generic_comm_base->command,
+ cpu_to_le16(MSG_TYPE(msg[0]) | PCCC_GENERATE_DB_INT));
+
+ /* Flip CMD COMPLETE bit */
+ val = le16_to_cpu(READ_ONCE(generic_comm_base->status));
+ val &= ~PCCS_CMD_COMPLETE;
+ WRITE_ONCE(generic_comm_base->status, cpu_to_le16(val));
+
+ /* Copy the message to the PCC comm space */
+ for (i = 0; i < sizeof(struct slimpro_resp_msg) / 4; i++)
+ WRITE_ONCE(ptr[i], cpu_to_le32(msg[i]));
+
+ /* Ring the doorbell */
+ rc = mbox_send_message(ctx->mbox_chan, msg);
+ if (rc < 0) {
+ dev_err(ctx->dev, "Mailbox send error %d\n", rc);
+ goto err;
+ }
+ if (!wait_for_completion_timeout(&ctx->rd_complete,
+ usecs_to_jiffies(ctx->usecs_lat))) {
+ dev_err(ctx->dev, "Mailbox operation timed out\n");
+ rc = -ETIMEDOUT;
+ goto err;
+ }
+
+ /* Check for error message */
+ if (MSG_TYPE(ctx->sync_msg.msg) == MSG_TYPE_ERR) {
+ rc = -EINVAL;
+ goto err;
+ }
+
+ msg[0] = ctx->sync_msg.msg;
+ msg[1] = ctx->sync_msg.param1;
+ msg[2] = ctx->sync_msg.param2;
+
+err:
+ mbox_chan_txdone(ctx->mbox_chan, 0);
+ ctx->resp_pending = false;
+ mutex_unlock(&ctx->rd_mutex);
+ return rc;
+}
+
+static int xgene_hwmon_rd(struct xgene_hwmon_dev *ctx, u32 *msg)
+{
+ int rc;
+
+ mutex_lock(&ctx->rd_mutex);
+ init_completion(&ctx->rd_complete);
+ ctx->resp_pending = true;
+
+ rc = mbox_send_message(ctx->mbox_chan, msg);
+ if (rc < 0) {
+ dev_err(ctx->dev, "Mailbox send error %d\n", rc);
+ goto err;
+ }
+
+ if (!wait_for_completion_timeout(&ctx->rd_complete,
+ msecs_to_jiffies(MBOX_OP_TIMEOUTMS))) {
+ dev_err(ctx->dev, "Mailbox operation timed out\n");
+ rc = -ETIMEDOUT;
+ goto err;
+ }
+
+ /* Check for error message */
+ if (MSG_TYPE(ctx->sync_msg.msg) == MSG_TYPE_ERR) {
+ rc = -EINVAL;
+ goto err;
+ }
+
+ msg[0] = ctx->sync_msg.msg;
+ msg[1] = ctx->sync_msg.param1;
+ msg[2] = ctx->sync_msg.param2;
+
+err:
+ ctx->resp_pending = false;
+ mutex_unlock(&ctx->rd_mutex);
+ return rc;
+}
+
+static int xgene_hwmon_reg_map_rd(struct xgene_hwmon_dev *ctx, u32 addr,
+ u32 *data)
+{
+ u32 msg[3];
+ int rc;
+
+ msg[0] = SENSOR_RD_MSG;
+ msg[1] = SENSOR_RD_EN_ADDR(addr);
+ msg[2] = 0;
+
+ if (acpi_disabled)
+ rc = xgene_hwmon_rd(ctx, msg);
+ else
+ rc = xgene_hwmon_pcc_rd(ctx, msg);
+
+ if (rc < 0)
+ return rc;
+
+ /*
+ * Check if sensor data is valid.
+ */
+ if (msg[1] & SENSOR_INVALID_DATA)
+ return -ENODATA;
+
+ *data = msg[1];
+
+ return rc;
+}
+
+static int xgene_hwmon_get_notification_msg(struct xgene_hwmon_dev *ctx,
+ u32 *amsg)
+{
+ u32 msg[3];
+ int rc;
+
+ msg[0] = TPC_EN_MSG(PWRMGMT_SUBTYPE_TPC, TPC_GET_ALARM, 0);
+ msg[1] = 0;
+ msg[2] = 0;
+
+ rc = xgene_hwmon_pcc_rd(ctx, msg);
+ if (rc < 0)
+ return rc;
+
+ amsg[0] = msg[0];
+ amsg[1] = msg[1];
+ amsg[2] = msg[2];
+
+ return rc;
+}
+
+static int xgene_hwmon_get_cpu_pwr(struct xgene_hwmon_dev *ctx, u32 *val)
+{
+ u32 watt, mwatt;
+ int rc;
+
+ rc = xgene_hwmon_reg_map_rd(ctx, PMD_PWR_REG, &watt);
+ if (rc < 0)
+ return rc;
+
+ rc = xgene_hwmon_reg_map_rd(ctx, PMD_PWR_MW_REG, &mwatt);
+ if (rc < 0)
+ return rc;
+
+ *val = WATT_TO_mWATT(watt) + mwatt;
+ return 0;
+}
+
+static int xgene_hwmon_get_io_pwr(struct xgene_hwmon_dev *ctx, u32 *val)
+{
+ u32 watt, mwatt;
+ int rc;
+
+ rc = xgene_hwmon_reg_map_rd(ctx, SOC_PWR_REG, &watt);
+ if (rc < 0)
+ return rc;
+
+ rc = xgene_hwmon_reg_map_rd(ctx, SOC_PWR_MW_REG, &mwatt);
+ if (rc < 0)
+ return rc;
+
+ *val = WATT_TO_mWATT(watt) + mwatt;
+ return 0;
+}
+
+static int xgene_hwmon_get_temp(struct xgene_hwmon_dev *ctx, u32 *val)
+{
+ return xgene_hwmon_reg_map_rd(ctx, SOC_TEMP_REG, val);
+}
+
+/*
+ * Sensor temperature/power functions
+ */
+static ssize_t temp1_input_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct xgene_hwmon_dev *ctx = dev_get_drvdata(dev);
+ int rc, temp;
+ u32 val;
+
+ rc = xgene_hwmon_get_temp(ctx, &val);
+ if (rc < 0)
+ return rc;
+
+ temp = sign_extend32(val, TEMP_NEGATIVE_BIT);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", CELSIUS_TO_mCELSIUS(temp));
+}
+
+static ssize_t temp1_label_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "SoC Temperature\n");
+}
+
+static ssize_t temp1_critical_alarm_show(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct xgene_hwmon_dev *ctx = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", ctx->temp_critical_alarm);
+}
+
+static ssize_t power1_label_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "CPU power\n");
+}
+
+static ssize_t power2_label_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "IO power\n");
+}
+
+static ssize_t power1_input_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct xgene_hwmon_dev *ctx = dev_get_drvdata(dev);
+ u32 val;
+ int rc;
+
+ rc = xgene_hwmon_get_cpu_pwr(ctx, &val);
+ if (rc < 0)
+ return rc;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", mWATT_TO_uWATT(val));
+}
+
+static ssize_t power2_input_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct xgene_hwmon_dev *ctx = dev_get_drvdata(dev);
+ u32 val;
+ int rc;
+
+ rc = xgene_hwmon_get_io_pwr(ctx, &val);
+ if (rc < 0)
+ return rc;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", mWATT_TO_uWATT(val));
+}
+
+static DEVICE_ATTR_RO(temp1_label);
+static DEVICE_ATTR_RO(temp1_input);
+static DEVICE_ATTR_RO(temp1_critical_alarm);
+static DEVICE_ATTR_RO(power1_label);
+static DEVICE_ATTR_RO(power1_input);
+static DEVICE_ATTR_RO(power2_label);
+static DEVICE_ATTR_RO(power2_input);
+
+static struct attribute *xgene_hwmon_attrs[] = {
+ &dev_attr_temp1_label.attr,
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp1_critical_alarm.attr,
+ &dev_attr_power1_label.attr,
+ &dev_attr_power1_input.attr,
+ &dev_attr_power2_label.attr,
+ &dev_attr_power2_input.attr,
+ NULL,
+};
+
+ATTRIBUTE_GROUPS(xgene_hwmon);
+
+static int xgene_hwmon_tpc_alarm(struct xgene_hwmon_dev *ctx,
+ struct slimpro_resp_msg *amsg)
+{
+ ctx->temp_critical_alarm = !!amsg->param2;
+ sysfs_notify(&ctx->dev->kobj, NULL, "temp1_critical_alarm");
+
+ return 0;
+}
+
+static void xgene_hwmon_process_pwrmsg(struct xgene_hwmon_dev *ctx,
+ struct slimpro_resp_msg *amsg)
+{
+ if ((MSG_SUBTYPE(amsg->msg) == PWRMGMT_SUBTYPE_TPC) &&
+ (TPC_CMD(amsg->msg) == TPC_ALARM))
+ xgene_hwmon_tpc_alarm(ctx, amsg);
+}
+
+/*
+ * This function is called to process async work queue
+ */
+static void xgene_hwmon_evt_work(struct work_struct *work)
+{
+ struct slimpro_resp_msg amsg;
+ struct xgene_hwmon_dev *ctx;
+ int ret;
+
+ ctx = container_of(work, struct xgene_hwmon_dev, workq);
+ while (kfifo_out_spinlocked(&ctx->async_msg_fifo, &amsg,
+ sizeof(struct slimpro_resp_msg),
+ &ctx->kfifo_lock)) {
+ /*
+ * If PCC, send a consumer command to Platform to get info
+ * If Slimpro Mailbox, get message from specific FIFO
+ */
+ if (!acpi_disabled) {
+ ret = xgene_hwmon_get_notification_msg(ctx,
+ (u32 *)&amsg);
+ if (ret < 0)
+ continue;
+ }
+
+ if (MSG_TYPE(amsg.msg) == MSG_TYPE_PWRMGMT)
+ xgene_hwmon_process_pwrmsg(ctx, &amsg);
+ }
+}
+
+static int xgene_hwmon_rx_ready(struct xgene_hwmon_dev *ctx, void *msg)
+{
+ if (IS_ERR_OR_NULL(ctx->hwmon_dev) && !ctx->resp_pending) {
+ /* Enqueue to the FIFO */
+ kfifo_in_spinlocked(&ctx->async_msg_fifo, msg,
+ sizeof(struct slimpro_resp_msg),
+ &ctx->kfifo_lock);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/*
+ * This function is called when the SLIMpro Mailbox received a message
+ */
+static void xgene_hwmon_rx_cb(struct mbox_client *cl, void *msg)
+{
+ struct xgene_hwmon_dev *ctx = to_xgene_hwmon_dev(cl);
+
+ /*
+ * While the driver registers with the mailbox framework, an interrupt
+ * can be pending before the probe function completes its
+ * initialization. If such condition occurs, just queue up the message
+ * as the driver is not ready for servicing the callback.
+ */
+ if (xgene_hwmon_rx_ready(ctx, msg) < 0)
+ return;
+
+ /*
+ * Response message format:
+ * msg[0] is the return code of the operation
+ * msg[1] is the first parameter word
+ * msg[2] is the second parameter word
+ *
+ * As message only supports dword size, just assign it.
+ */
+
+ /* Check for sync query */
+ if (ctx->resp_pending &&
+ ((MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_ERR) ||
+ (MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_DBG &&
+ MSG_SUBTYPE(((u32 *)msg)[0]) == DBG_SUBTYPE_SENSOR_READ) ||
+ (MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_PWRMGMT &&
+ MSG_SUBTYPE(((u32 *)msg)[0]) == PWRMGMT_SUBTYPE_TPC &&
+ TPC_CMD(((u32 *)msg)[0]) == TPC_ALARM))) {
+ ctx->sync_msg.msg = ((u32 *)msg)[0];
+ ctx->sync_msg.param1 = ((u32 *)msg)[1];
+ ctx->sync_msg.param2 = ((u32 *)msg)[2];
+
+ /* Operation waiting for response */
+ complete(&ctx->rd_complete);
+
+ return;
+ }
+
+ /* Enqueue to the FIFO */
+ kfifo_in_spinlocked(&ctx->async_msg_fifo, msg,
+ sizeof(struct slimpro_resp_msg), &ctx->kfifo_lock);
+ /* Schedule the bottom handler */
+ schedule_work(&ctx->workq);
+}
+
+/*
+ * This function is called when the PCC Mailbox received a message
+ */
+static void xgene_hwmon_pcc_rx_cb(struct mbox_client *cl, void *msg)
+{
+ struct xgene_hwmon_dev *ctx = to_xgene_hwmon_dev(cl);
+ struct acpi_pcct_shared_memory *generic_comm_base = ctx->pcc_comm_addr;
+ struct slimpro_resp_msg amsg;
+
+ /*
+ * While the driver registers with the mailbox framework, an interrupt
+ * can be pending before the probe function completes its
+ * initialization. If such condition occurs, just queue up the message
+ * as the driver is not ready for servicing the callback.
+ */
+ if (xgene_hwmon_rx_ready(ctx, &amsg) < 0)
+ return;
+
+ msg = generic_comm_base + 1;
+ /* Check if platform sends interrupt */
+ if (!xgene_word_tst_and_clr(&generic_comm_base->status,
+ PCCS_SCI_DOORBEL))
+ return;
+
+ /*
+ * Response message format:
+ * msg[0] is the return code of the operation
+ * msg[1] is the first parameter word
+ * msg[2] is the second parameter word
+ *
+ * As message only supports dword size, just assign it.
+ */
+
+ /* Check for sync query */
+ if (ctx->resp_pending &&
+ ((MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_ERR) ||
+ (MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_DBG &&
+ MSG_SUBTYPE(((u32 *)msg)[0]) == DBG_SUBTYPE_SENSOR_READ) ||
+ (MSG_TYPE(((u32 *)msg)[0]) == MSG_TYPE_PWRMGMT &&
+ MSG_SUBTYPE(((u32 *)msg)[0]) == PWRMGMT_SUBTYPE_TPC &&
+ TPC_CMD(((u32 *)msg)[0]) == TPC_ALARM))) {
+ /* Check if platform completes command */
+ if (xgene_word_tst_and_clr(&generic_comm_base->status,
+ PCCS_CMD_COMPLETE)) {
+ ctx->sync_msg.msg = ((u32 *)msg)[0];
+ ctx->sync_msg.param1 = ((u32 *)msg)[1];
+ ctx->sync_msg.param2 = ((u32 *)msg)[2];
+
+ /* Operation waiting for response */
+ complete(&ctx->rd_complete);
+
+ return;
+ }
+ }
+
+ /*
+ * Platform notifies interrupt to OSPM.
+ * OPSM schedules a consumer command to get this information
+ * in a workqueue. Platform must wait until OSPM has issued
+ * a consumer command that serves this notification.
+ */
+
+ /* Enqueue to the FIFO */
+ kfifo_in_spinlocked(&ctx->async_msg_fifo, &amsg,
+ sizeof(struct slimpro_resp_msg), &ctx->kfifo_lock);
+ /* Schedule the bottom handler */
+ schedule_work(&ctx->workq);
+}
+
+static void xgene_hwmon_tx_done(struct mbox_client *cl, void *msg, int ret)
+{
+ if (ret) {
+ dev_dbg(cl->dev, "TX did not complete: CMD sent:%x, ret:%d\n",
+ *(u16 *)msg, ret);
+ } else {
+ dev_dbg(cl->dev, "TX completed. CMD sent:%x, ret:%d\n",
+ *(u16 *)msg, ret);
+ }
+}
+
+static int xgene_hwmon_probe(struct platform_device *pdev)
+{
+ struct xgene_hwmon_dev *ctx;
+ struct mbox_client *cl;
+ int rc;
+
+ ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->dev = &pdev->dev;
+ platform_set_drvdata(pdev, ctx);
+ cl = &ctx->mbox_client;
+
+ spin_lock_init(&ctx->kfifo_lock);
+ mutex_init(&ctx->rd_mutex);
+
+ rc = kfifo_alloc(&ctx->async_msg_fifo,
+ sizeof(struct slimpro_resp_msg) * ASYNC_MSG_FIFO_SIZE,
+ GFP_KERNEL);
+ if (rc)
+ goto out_mbox_free;
+
+ INIT_WORK(&ctx->workq, xgene_hwmon_evt_work);
+
+ /* Request mailbox channel */
+ cl->dev = &pdev->dev;
+ cl->tx_done = xgene_hwmon_tx_done;
+ cl->tx_block = false;
+ cl->tx_tout = MBOX_OP_TIMEOUTMS;
+ cl->knows_txdone = false;
+ if (acpi_disabled) {
+ cl->rx_callback = xgene_hwmon_rx_cb;
+ ctx->mbox_chan = mbox_request_channel(cl, 0);
+ if (IS_ERR(ctx->mbox_chan)) {
+ dev_err(&pdev->dev,
+ "SLIMpro mailbox channel request failed\n");
+ return -ENODEV;
+ }
+ } else {
+ struct acpi_pcct_hw_reduced *cppc_ss;
+
+ if (device_property_read_u32(&pdev->dev, "pcc-channel",
+ &ctx->mbox_idx)) {
+ dev_err(&pdev->dev, "no pcc-channel property\n");
+ return -ENODEV;
+ }
+
+ cl->rx_callback = xgene_hwmon_pcc_rx_cb;
+ ctx->mbox_chan = pcc_mbox_request_channel(cl, ctx->mbox_idx);
+ if (IS_ERR(ctx->mbox_chan)) {
+ dev_err(&pdev->dev,
+ "PPC channel request failed\n");
+ return -ENODEV;
+ }
+
+ /*
+ * The PCC mailbox controller driver should
+ * have parsed the PCCT (global table of all
+ * PCC channels) and stored pointers to the
+ * subspace communication region in con_priv.
+ */
+ cppc_ss = ctx->mbox_chan->con_priv;
+ if (!cppc_ss) {
+ dev_err(&pdev->dev, "PPC subspace not found\n");
+ rc = -ENODEV;
+ goto out_mbox_free;
+ }
+
+ if (!ctx->mbox_chan->mbox->txdone_irq) {
+ dev_err(&pdev->dev, "PCC IRQ not supported\n");
+ rc = -ENODEV;
+ goto out_mbox_free;
+ }
+
+ /*
+ * This is the shared communication region
+ * for the OS and Platform to communicate over.
+ */
+ ctx->comm_base_addr = cppc_ss->base_address;
+ if (ctx->comm_base_addr) {
+ ctx->pcc_comm_addr = memremap(ctx->comm_base_addr,
+ cppc_ss->length,
+ MEMREMAP_WB);
+ } else {
+ dev_err(&pdev->dev, "Failed to get PCC comm region\n");
+ rc = -ENODEV;
+ goto out_mbox_free;
+ }
+
+ if (!ctx->pcc_comm_addr) {
+ dev_err(&pdev->dev,
+ "Failed to ioremap PCC comm region\n");
+ rc = -ENOMEM;
+ goto out_mbox_free;
+ }
+
+ /*
+ * cppc_ss->latency is just a Nominal value. In reality
+ * the remote processor could be much slower to reply.
+ * So add an arbitrary amount of wait on top of Nominal.
+ */
+ ctx->usecs_lat = PCC_NUM_RETRIES * cppc_ss->latency;
+ }
+
+ ctx->hwmon_dev = hwmon_device_register_with_groups(ctx->dev,
+ "apm_xgene",
+ ctx,
+ xgene_hwmon_groups);
+ if (IS_ERR(ctx->hwmon_dev)) {
+ dev_err(&pdev->dev, "Failed to register HW monitor device\n");
+ rc = PTR_ERR(ctx->hwmon_dev);
+ goto out;
+ }
+
+ /*
+ * Schedule the bottom handler if there is a pending message.
+ */
+ schedule_work(&ctx->workq);
+
+ dev_info(&pdev->dev, "APM X-Gene SoC HW monitor driver registered\n");
+
+ return 0;
+
+out:
+ if (acpi_disabled)
+ mbox_free_channel(ctx->mbox_chan);
+ else
+ pcc_mbox_free_channel(ctx->mbox_chan);
+out_mbox_free:
+ kfifo_free(&ctx->async_msg_fifo);
+
+ return rc;
+}
+
+static int xgene_hwmon_remove(struct platform_device *pdev)
+{
+ struct xgene_hwmon_dev *ctx = platform_get_drvdata(pdev);
+
+ hwmon_device_unregister(ctx->hwmon_dev);
+ kfifo_free(&ctx->async_msg_fifo);
+ if (acpi_disabled)
+ mbox_free_channel(ctx->mbox_chan);
+ else
+ pcc_mbox_free_channel(ctx->mbox_chan);
+
+ return 0;
+}
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id xgene_hwmon_acpi_match[] = {
+ {"APMC0D29", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, xgene_hwmon_acpi_match);
+#endif
+
+static const struct of_device_id xgene_hwmon_of_match[] = {
+ {.compatible = "apm,xgene-slimpro-hwmon"},
+ {}
+};
+MODULE_DEVICE_TABLE(of, xgene_hwmon_of_match);
+
+static struct platform_driver xgene_hwmon_driver __refdata = {
+ .probe = xgene_hwmon_probe,
+ .remove = xgene_hwmon_remove,
+ .driver = {
+ .name = "xgene-slimpro-hwmon",
+ .of_match_table = xgene_hwmon_of_match,
+ .acpi_match_table = ACPI_PTR(xgene_hwmon_acpi_match),
+ },
+};
+module_platform_driver(xgene_hwmon_driver);
+
+MODULE_DESCRIPTION("APM X-Gene SoC hardware monitor");
+MODULE_LICENSE("GPL");