diff options
author | Linus Torvalds | 2021-08-31 14:37:41 -0700 |
---|---|---|
committer | Linus Torvalds | 2021-08-31 14:37:41 -0700 |
commit | e7c1bbcf0c315c56cd970642214aa1df3d8cf61d (patch) | |
tree | 45a5242c87fbe418b98cebf81659b809e21ed313 /drivers | |
parent | 871dda463c6f2c2a4a660937e2f57616146f42de (diff) | |
parent | 0e35f63f7f4eebd268ec236fd1bbf4e561ce8de5 (diff) |
Merge tag 'hwmon-for-v5.15' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging
Pull hwmon updates from Guenter Roeck:
"New drivers for:
- Aquacomputer D5 Next
- SB-RMI power module
Added chip support to existing drivers:
- Support for various Zen2 and Zen3 APUs and for Yellow Carp (SMU
v13) added to k10temp driver
- Support for Silicom n5010 PAC added to intel-m10-bmc driver
- Support for BPD-RS600 added to pmbus/bpa-rs600 driver
Other notable changes:
- In k10temp, do not display Tdie on Zen CPUs if there is no
difference between Tdie and Tctl
- Converted adt7470 and dell-smm drivers to use
devm_hwmon_device_register_with_info API
- Support for temperature/pwm tables added to axi-fan-control driver
- Enabled fan control for Dell Precision 7510 in dell-smm driver
Various other minor improvements and fixes in several drivers"
* tag 'hwmon-for-v5.15' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (41 commits)
hwmon: add driver for Aquacomputer D5 Next
hwmon: (adt7470) Convert to devm_hwmon_device_register_with_info API
hwmon: (adt7470) Convert to use regmap
hwmon: (adt7470) Fix some style issues
hwmon: (k10temp) Add support for yellow carp
hwmon: (k10temp) Rework the temperature offset calculation
hwmon: (k10temp) Don't show Tdie for all Zen/Zen2/Zen3 CPU/APU
hwmon: (k10temp) Add additional missing Zen2 and Zen3 APUs
hwmon: remove amd_energy driver in Makefile
hwmon: (dell-smm) Rework SMM function debugging
hwmon: (dell-smm) Mark i8k_get_fan_nominal_speed as __init
hwmon: (dell-smm) Mark tables as __initconst
hwmon: (pmbus/bpa-rs600) Add workaround for incorrect Pin max
hwmon: (pmbus/bpa-rs600) Don't use rated limits as warn limits
hwmon: (axi-fan-control) Support temperature vs pwm points
hwmon: (axi-fan-control) Handle irqs in natural order
hwmon: (axi-fan-control) Make sure the clock is enabled
hwmon: (pmbus/ibm-cffps) Fix write bits for LED control
hwmon: (w83781d) Match on device tree compatibles
dt-bindings: hwmon: Add bindings for Winbond W83781D
...
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/hwmon/Kconfig | 20 | ||||
-rw-r--r-- | drivers/hwmon/Makefile | 3 | ||||
-rw-r--r-- | drivers/hwmon/adt7470.c | 1051 | ||||
-rw-r--r-- | drivers/hwmon/aquacomputer_d5next.c | 363 | ||||
-rw-r--r-- | drivers/hwmon/axi-fan-control.c | 117 | ||||
-rw-r--r-- | drivers/hwmon/dell-smm-hwmon.c | 910 | ||||
-rw-r--r-- | drivers/hwmon/fam15h_power.c | 4 | ||||
-rw-r--r-- | drivers/hwmon/intel-m10-bmc-hwmon.c | 116 | ||||
-rw-r--r-- | drivers/hwmon/k10temp.c | 30 | ||||
-rw-r--r-- | drivers/hwmon/ntc_thermistor.c | 20 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/bpa-rs600.c | 68 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/ibm-cffps.c | 6 | ||||
-rw-r--r-- | drivers/hwmon/sbrmi.c | 359 | ||||
-rw-r--r-- | drivers/hwmon/w83627ehf.c | 122 | ||||
-rw-r--r-- | drivers/hwmon/w83781d.c | 11 |
15 files changed, 2025 insertions, 1175 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index e3675377bc5d..c4578e8f34bb 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -254,6 +254,16 @@ config SENSORS_AHT10 This driver can also be built as a module. If so, the module will be called aht10. +config SENSORS_AQUACOMPUTER_D5NEXT + tristate "Aquacomputer D5 Next watercooling pump" + depends on USB_HID + help + If you say yes here you get support for the Aquacomputer D5 Next + watercooling pump sensors. + + This driver can also be built as a module. If so, the module + will be called aquacomputer_d5next. + config SENSORS_AS370 tristate "Synaptics AS370 SoC hardware monitoring driver" help @@ -1551,6 +1561,16 @@ config SENSORS_SBTSI This driver can also be built as a module. If so, the module will be called sbtsi_temp. +config SENSORS_SBRMI + tristate "Emulated SB-RMI sensor" + depends on I2C + help + If you say yes here you get support for emulated RMI + sensors on AMD SoCs with APML interface connected to a BMC device. + + This driver can also be built as a module. If so, the module will + be called sbrmi. + config SENSORS_SHT15 tristate "Sensiron humidity and temperature sensors. SHT15 and compat." depends on GPIOLIB || COMPILE_TEST diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index d712c61c1f5e..162940270661 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -45,8 +45,8 @@ obj-$(CONFIG_SENSORS_ADT7462) += adt7462.o obj-$(CONFIG_SENSORS_ADT7470) += adt7470.o obj-$(CONFIG_SENSORS_ADT7475) += adt7475.o obj-$(CONFIG_SENSORS_AHT10) += aht10.o -obj-$(CONFIG_SENSORS_AMD_ENERGY) += amd_energy.o obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o +obj-$(CONFIG_SENSORS_AQUACOMPUTER_D5NEXT) += aquacomputer_d5next.o obj-$(CONFIG_SENSORS_ARM_SCMI) += scmi-hwmon.o obj-$(CONFIG_SENSORS_ARM_SCPI) += scpi-hwmon.o obj-$(CONFIG_SENSORS_AS370) += as370-hwmon.o @@ -164,6 +164,7 @@ obj-$(CONFIG_SENSORS_PWM_FAN) += pwm-fan.o obj-$(CONFIG_SENSORS_RASPBERRYPI_HWMON) += raspberrypi-hwmon.o obj-$(CONFIG_SENSORS_S3C) += s3c-hwmon.o obj-$(CONFIG_SENSORS_SBTSI) += sbtsi_temp.o +obj-$(CONFIG_SENSORS_SBRMI) += sbrmi.o obj-$(CONFIG_SENSORS_SCH56XX_COMMON)+= sch56xx-common.o obj-$(CONFIG_SENSORS_SCH5627) += sch5627.o obj-$(CONFIG_SENSORS_SCH5636) += sch5636.o diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index 2e8feacccf84..d519aca4a9d6 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -18,6 +18,7 @@ #include <linux/delay.h> #include <linux/log2.h> #include <linux/kthread.h> +#include <linux/regmap.h> #include <linux/slab.h> #include <linux/util_macros.h> @@ -35,7 +36,10 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; #define ADT7470_REG_PWM_MAX_BASE_ADDR 0x38 #define ADT7470_REG_PWM_MAX_MAX_ADDR 0x3B #define ADT7470_REG_CFG 0x40 +#define ADT7470_STRT_MASK 0x01 +#define ADT7470_TEST_MASK 0x02 #define ADT7470_FSPD_MASK 0x04 +#define ADT7470_T05_STB_MASK 0x80 #define ADT7470_REG_ALARM1 0x41 #define ADT7470_R1T_ALARM 0x01 #define ADT7470_R2T_ALARM 0x02 @@ -137,7 +141,7 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; #define ADT7470_FREQ_SHIFT 4 struct adt7470_data { - struct i2c_client *client; + struct regmap *regmap; struct mutex lock; char sensors_valid; char limits_valid; @@ -171,51 +175,76 @@ struct adt7470_data { * 16-bit registers on the ADT7470 are low-byte first. The data sheet says * that the low byte must be read before the high byte. */ -static inline int adt7470_read_word_data(struct i2c_client *client, u8 reg) +static inline int adt7470_read_word_data(struct adt7470_data *data, unsigned int reg, + unsigned int *val) { - u16 foo; - foo = i2c_smbus_read_byte_data(client, reg); - foo |= ((u16)i2c_smbus_read_byte_data(client, reg + 1) << 8); - return foo; + u8 regval[2]; + int err; + + err = regmap_bulk_read(data->regmap, reg, ®val, 2); + if (err < 0) + return err; + + *val = regval[0] | (regval[1] << 8); + + return 0; } -static inline int adt7470_write_word_data(struct i2c_client *client, u8 reg, - u16 value) +static inline int adt7470_write_word_data(struct adt7470_data *data, unsigned int reg, + unsigned int val) { - return i2c_smbus_write_byte_data(client, reg, value & 0xFF) - || i2c_smbus_write_byte_data(client, reg + 1, value >> 8); + u8 regval[2]; + + regval[0] = val & 0xFF; + regval[1] = val >> 8; + + return regmap_bulk_write(data->regmap, reg, ®val, 2); } /* Probe for temperature sensors. Assumes lock is held */ -static int adt7470_read_temperatures(struct i2c_client *client, - struct adt7470_data *data) +static int adt7470_read_temperatures(struct adt7470_data *data) { unsigned long res; + unsigned int pwm_cfg[2]; + int err; int i; - u8 cfg, pwm[4], pwm_cfg[2]; + u8 pwm[ADT7470_FAN_COUNT]; /* save pwm[1-4] config register */ - pwm_cfg[0] = i2c_smbus_read_byte_data(client, ADT7470_REG_PWM_CFG(0)); - pwm_cfg[1] = i2c_smbus_read_byte_data(client, ADT7470_REG_PWM_CFG(2)); + err = regmap_read(data->regmap, ADT7470_REG_PWM_CFG(0), &pwm_cfg[0]); + if (err < 0) + return err; + err = regmap_read(data->regmap, ADT7470_REG_PWM_CFG(2), &pwm_cfg[1]); + if (err < 0) + return err; /* set manual pwm to whatever it is set to now */ - for (i = 0; i < ADT7470_FAN_COUNT; i++) - pwm[i] = i2c_smbus_read_byte_data(client, ADT7470_REG_PWM(i)); + err = regmap_bulk_read(data->regmap, ADT7470_REG_PWM(0), &pwm[0], + ADT7470_PWM_COUNT); + if (err < 0) + return err; /* put pwm in manual mode */ - i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_CFG(0), - pwm_cfg[0] & ~(ADT7470_PWM_AUTO_MASK)); - i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_CFG(2), - pwm_cfg[1] & ~(ADT7470_PWM_AUTO_MASK)); + err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(0), + ADT7470_PWM_AUTO_MASK, 0); + if (err < 0) + return err; + err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(2), + ADT7470_PWM_AUTO_MASK, 0); + if (err < 0) + return err; /* write pwm control to whatever it was */ - for (i = 0; i < ADT7470_FAN_COUNT; i++) - i2c_smbus_write_byte_data(client, ADT7470_REG_PWM(i), pwm[i]); + err = regmap_bulk_write(data->regmap, ADT7470_REG_PWM(0), &pwm[0], + ADT7470_PWM_COUNT); + if (err < 0) + return err; /* start reading temperature sensors */ - cfg = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG); - cfg |= 0x80; - i2c_smbus_write_byte_data(client, ADT7470_REG_CFG, cfg); + err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, + ADT7470_T05_STB_MASK, ADT7470_T05_STB_MASK); + if (err < 0) + return err; /* Delay is 200ms * number of temp sensors. */ res = msleep_interruptible((data->num_temp_sensors >= 0 ? @@ -223,26 +252,31 @@ static int adt7470_read_temperatures(struct i2c_client *client, TEMP_COLLECTION_TIME)); /* done reading temperature sensors */ - cfg = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG); - cfg &= ~0x80; - i2c_smbus_write_byte_data(client, ADT7470_REG_CFG, cfg); + err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, + ADT7470_T05_STB_MASK, 0); + if (err < 0) + return err; /* restore pwm[1-4] config registers */ - i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]); - i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]); - - if (res) { - pr_err("ha ha, interrupted\n"); + err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]); + if (err < 0) + return err; + err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]); + if (err < 0) + return err; + + if (res) return -EAGAIN; - } /* Only count fans if we have to */ if (data->num_temp_sensors >= 0) return 0; + err = regmap_bulk_read(data->regmap, ADT7470_TEMP_REG(0), &data->temp[0], + ADT7470_TEMP_COUNT); + if (err < 0) + return err; for (i = 0; i < ADT7470_TEMP_COUNT; i++) { - data->temp[i] = i2c_smbus_read_byte_data(client, - ADT7470_TEMP_REG(i)); if (data->temp[i]) data->num_temp_sensors = i + 1; } @@ -257,7 +291,7 @@ static int adt7470_update_thread(void *p) while (!kthread_should_stop()) { mutex_lock(&data->lock); - adt7470_read_temperatures(client, data); + adt7470_read_temperatures(data); mutex_unlock(&data->lock); set_current_state(TASK_INTERRUPTIBLE); @@ -272,89 +306,116 @@ static int adt7470_update_thread(void *p) static int adt7470_update_sensors(struct adt7470_data *data) { - struct i2c_client *client = data->client; - u8 cfg; + unsigned int val; + int err; int i; if (!data->temperatures_probed) - adt7470_read_temperatures(client, data); + err = adt7470_read_temperatures(data); else - for (i = 0; i < ADT7470_TEMP_COUNT; i++) - data->temp[i] = i2c_smbus_read_byte_data(client, - ADT7470_TEMP_REG(i)); + err = regmap_bulk_read(data->regmap, ADT7470_TEMP_REG(0), &data->temp[0], + ADT7470_TEMP_COUNT); + if (err < 0) + return err; - for (i = 0; i < ADT7470_FAN_COUNT; i++) - data->fan[i] = adt7470_read_word_data(client, - ADT7470_REG_FAN(i)); + for (i = 0; i < ADT7470_FAN_COUNT; i++) { + err = adt7470_read_word_data(data, ADT7470_REG_FAN(i), &val); + if (err < 0) + return err; + data->fan[i] = val; + } - for (i = 0; i < ADT7470_PWM_COUNT; i++) { - int reg; - int reg_mask; + err = regmap_bulk_read(data->regmap, ADT7470_REG_PWM(0), &data->pwm[0], ADT7470_PWM_COUNT); + if (err < 0) + return err; - data->pwm[i] = i2c_smbus_read_byte_data(client, - ADT7470_REG_PWM(i)); + for (i = 0; i < ADT7470_PWM_COUNT; i++) { + unsigned int mask; if (i % 2) - reg_mask = ADT7470_PWM2_AUTO_MASK; + mask = ADT7470_PWM2_AUTO_MASK; else - reg_mask = ADT7470_PWM1_AUTO_MASK; + mask = ADT7470_PWM1_AUTO_MASK; - reg = ADT7470_REG_PWM_CFG(i); - if (i2c_smbus_read_byte_data(client, reg) & reg_mask) - data->pwm_automatic[i] = 1; - else - data->pwm_automatic[i] = 0; + err = regmap_read(data->regmap, ADT7470_REG_PWM_CFG(i), &val); + if (err < 0) + return err; + data->pwm_automatic[i] = !!(val & mask); - reg = ADT7470_REG_PWM_AUTO_TEMP(i); - cfg = i2c_smbus_read_byte_data(client, reg); + err = regmap_read(data->regmap, ADT7470_REG_PWM_AUTO_TEMP(i), &val); + if (err < 0) + return err; if (!(i % 2)) - data->pwm_auto_temp[i] = cfg >> 4; + data->pwm_auto_temp[i] = val >> 4; else - data->pwm_auto_temp[i] = cfg & 0xF; + data->pwm_auto_temp[i] = val & 0xF; } - if (i2c_smbus_read_byte_data(client, ADT7470_REG_CFG) & - ADT7470_FSPD_MASK) - data->force_pwm_max = 1; - else - data->force_pwm_max = 0; + err = regmap_read(data->regmap, ADT7470_REG_CFG, &val); + if (err < 0) + return err; + data->force_pwm_max = !!(val & ADT7470_FSPD_MASK); + + err = regmap_read(data->regmap, ADT7470_REG_ALARM1, &val); + if (err < 0) + return err; + data->alarm = val; + if (data->alarm & ADT7470_OOL_ALARM) { + err = regmap_read(data->regmap, ADT7470_REG_ALARM2, &val); + if (err < 0) + return err; + data->alarm |= ALARM2(val); + } - data->alarm = i2c_smbus_read_byte_data(client, ADT7470_REG_ALARM1); - if (data->alarm & ADT7470_OOL_ALARM) - data->alarm |= ALARM2(i2c_smbus_read_byte_data(client, - ADT7470_REG_ALARM2)); - data->alarms_mask = adt7470_read_word_data(client, - ADT7470_REG_ALARM1_MASK); + err = adt7470_read_word_data(data, ADT7470_REG_ALARM1_MASK, &val); + if (err < 0) + return err; + data->alarms_mask = val; return 0; } static int adt7470_update_limits(struct adt7470_data *data) { - struct i2c_client *client = data->client; + unsigned int val; + int err; int i; for (i = 0; i < ADT7470_TEMP_COUNT; i++) { - data->temp_min[i] = i2c_smbus_read_byte_data(client, - ADT7470_TEMP_MIN_REG(i)); - data->temp_max[i] = i2c_smbus_read_byte_data(client, - ADT7470_TEMP_MAX_REG(i)); + err = regmap_read(data->regmap, ADT7470_TEMP_MIN_REG(i), &val); + if (err < 0) + return err; + data->temp_min[i] = (s8)val; + err = regmap_read(data->regmap, ADT7470_TEMP_MAX_REG(i), &val); + if (err < 0) + return err; + data->temp_max[i] = (s8)val; } for (i = 0; i < ADT7470_FAN_COUNT; i++) { - data->fan_min[i] = adt7470_read_word_data(client, - ADT7470_REG_FAN_MIN(i)); - data->fan_max[i] = adt7470_read_word_data(client, - ADT7470_REG_FAN_MAX(i)); + err = adt7470_read_word_data(data, ADT7470_REG_FAN_MIN(i), &val); + if (err < 0) + return err; + data->fan_min[i] = val; + err = adt7470_read_word_data(data, ADT7470_REG_FAN_MAX(i), &val); + if (err < 0) + return err; + data->fan_max[i] = val; } for (i = 0; i < ADT7470_PWM_COUNT; i++) { - data->pwm_max[i] = i2c_smbus_read_byte_data(client, - ADT7470_REG_PWM_MAX(i)); - data->pwm_min[i] = i2c_smbus_read_byte_data(client, - ADT7470_REG_PWM_MIN(i)); - data->pwm_tmin[i] = i2c_smbus_read_byte_data(client, - ADT7470_REG_PWM_TMIN(i)); + err = regmap_read(data->regmap, ADT7470_REG_PWM_MAX(i), &val); + if (err < 0) + return err; + data->pwm_max[i] = val; + err = regmap_read(data->regmap, ADT7470_REG_PWM_MIN(i), &val); + if (err < 0) + return err; + data->pwm_min[i] = val; + err = regmap_read(data->regmap, ADT7470_REG_PWM_TMIN(i), &val); + if (err < 0) + return err; + data->pwm_tmin[i] = (s8)val; } return 0; @@ -472,93 +533,63 @@ static ssize_t num_temp_sensors_store(struct device *dev, return count; } -static ssize_t temp_min_show(struct device *dev, - struct device_attribute *devattr, char *buf) +static int adt7470_temp_read(struct device *dev, u32 attr, int channel, long *val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct adt7470_data *data = adt7470_update_device(dev); if (IS_ERR(data)) return PTR_ERR(data); - return sprintf(buf, "%d\n", 1000 * data->temp_min[attr->index]); -} - -static ssize_t temp_min_store(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7470_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - long temp; - - if (kstrtol(buf, 10, &temp)) - return -EINVAL; - - temp = clamp_val(temp, -128000, 127000); - temp = DIV_ROUND_CLOSEST(temp, 1000); - - mutex_lock(&data->lock); - data->temp_min[attr->index] = temp; - i2c_smbus_write_byte_data(client, ADT7470_TEMP_MIN_REG(attr->index), - temp); - mutex_unlock(&data->lock); - - return count; -} - -static ssize_t temp_max_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7470_data *data = adt7470_update_device(dev); - - if (IS_ERR(data)) - return PTR_ERR(data); + switch (attr) { + case hwmon_temp_input: + *val = 1000 * data->temp[channel]; + break; + case hwmon_temp_min: + *val = 1000 * data->temp_min[channel]; + break; + case hwmon_temp_max: + *val = 1000 * data->temp_max[channel]; + break; + case hwmon_temp_alarm: + *val = !!(data->alarm & channel); + break; + default: + return -EOPNOTSUPP; + } - return sprintf(buf, "%d\n", 1000 * data->temp_max[attr->index]); + return 0; } -static ssize_t temp_max_store(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) +static int adt7470_temp_write(struct device *dev, u32 attr, int channel, long val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct adt7470_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - long temp; - - if (kstrtol(buf, 10, &temp)) - return -EINVAL; - - temp = clamp_val(temp, -128000, 127000); - temp = DIV_ROUND_CLOSEST(temp, 1000); - - mutex_lock(&data->lock); - data->temp_max[attr->index] = temp; - i2c_smbus_write_byte_data(client, ADT7470_TEMP_MAX_REG(attr->index), - temp); - mutex_unlock(&data->lock); - - return count; -} + int err; -static ssize_t temp_show(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7470_data *data = adt7470_update_device(dev); + val = clamp_val(val, -128000, 127000); + val = DIV_ROUND_CLOSEST(val, 1000); - if (IS_ERR(data)) - return PTR_ERR(data); + switch (attr) { + case hwmon_temp_min: + mutex_lock(&data->lock); + data->temp_min[channel] = val; + err = regmap_write(data->regmap, ADT7470_TEMP_MIN_REG(channel), val); + mutex_unlock(&data->lock); + break; + case hwmon_temp_max: + mutex_lock(&data->lock); + data->temp_max[channel] = val; + err = regmap_write(data->regmap, ADT7470_TEMP_MAX_REG(channel), val); + mutex_unlock(&data->lock); + break; + default: + return -EOPNOTSUPP; + } - return sprintf(buf, "%d\n", 1000 * data->temp[attr->index]); + return err; } static ssize_t alarm_mask_show(struct device *dev, - struct device_attribute *devattr, - char *buf) + struct device_attribute *devattr, char *buf) { struct adt7470_data *data = adt7470_update_device(dev); @@ -574,6 +605,7 @@ static ssize_t alarm_mask_store(struct device *dev, { struct adt7470_data *data = dev_get_drvdata(dev); long mask; + int err; if (kstrtoul(buf, 0, &mask)) return -EINVAL; @@ -583,104 +615,74 @@ static ssize_t alarm_mask_store(struct device *dev, mutex_lock(&data->lock); data->alarms_mask = mask; - adt7470_write_word_data(data->client, ADT7470_REG_ALARM1_MASK, mask); + err = adt7470_write_word_data(data, ADT7470_REG_ALARM1_MASK, mask); mutex_unlock(&data->lock); - return count; + return err < 0 ? err : count; } -static ssize_t fan_max_show(struct device *dev, - struct device_attribute *devattr, char *buf) +static int adt7470_fan_read(struct device *dev, u32 attr, int channel, long *val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct adt7470_data *data = adt7470_update_device(dev); if (IS_ERR(data)) return PTR_ERR(data); - if (FAN_DATA_VALID(data->fan_max[attr->index])) - return sprintf(buf, "%d\n", - FAN_PERIOD_TO_RPM(data->fan_max[attr->index])); - else - return sprintf(buf, "0\n"); -} - -static ssize_t fan_max_store(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7470_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - long temp; - - if (kstrtol(buf, 10, &temp) || !temp) - return -EINVAL; - - temp = FAN_RPM_TO_PERIOD(temp); - temp = clamp_val(temp, 1, 65534); - - mutex_lock(&data->lock); - data->fan_max[attr->index] = temp; - adt7470_write_word_data(client, ADT7470_REG_FAN_MAX(attr->index), temp); - mutex_unlock(&data->lock); - - return count; -} - -static ssize_t fan_min_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7470_data *data = adt7470_update_device(dev); - - if (IS_ERR(data)) - return PTR_ERR(data); + switch (attr) { + case hwmon_fan_input: + if (FAN_DATA_VALID(data->fan[channel])) + *val = FAN_PERIOD_TO_RPM(data->fan[channel]); + else + *val = 0; + break; + case hwmon_fan_min: + if (FAN_DATA_VALID(data->fan_min[channel])) + *val = FAN_PERIOD_TO_RPM(data->fan_min[channel]); + else + *val = 0; + break; + case hwmon_fan_max: + if (FAN_DATA_VALID(data->fan_max[channel])) + *val = FAN_PERIOD_TO_RPM(data->fan_max[channel]); + else + *val = 0; + break; + case hwmon_fan_alarm: + *val = !!(data->alarm & (1 << (12 + channel))); + break; + default: + return -EOPNOTSUPP; + } - if (FAN_DATA_VALID(data->fan_min[attr->index])) - return sprintf(buf, "%d\n", - FAN_PERIOD_TO_RPM(data->fan_min[attr->index])); - else - return sprintf(buf, "0\n"); + return 0; } -static ssize_t fan_min_store(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) +static int adt7470_fan_write(struct device *dev, u32 attr, int channel, long val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct adt7470_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - long temp; - - if (kstrtol(buf, 10, &temp) || !temp) - return -EINVAL; - - temp = FAN_RPM_TO_PERIOD(temp); - temp = clamp_val(temp, 1, 65534); - - mutex_lock(&data->lock); - data->fan_min[attr->index] = temp; - adt7470_write_word_data(client, ADT7470_REG_FAN_MIN(attr->index), temp); - mutex_unlock(&data->lock); - - return count; -} + int err; -static ssize_t fan_show(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7470_data *data = adt7470_update_device(dev); + val = FAN_RPM_TO_PERIOD(val); + val = clamp_val(val, 1, 65534); - if (IS_ERR(data)) - return PTR_ERR(data); + switch (attr) { + case hwmon_fan_min: + mutex_lock(&data->lock); + data->fan_min[channel] = val; + err = adt7470_write_word_data(data, ADT7470_REG_FAN_MIN(channel), val); + mutex_unlock(&data->lock); + break; + case hwmon_fan_max: + mutex_lock(&data->lock); + data->fan_max[channel] = val; + err = adt7470_write_word_data(data, ADT7470_REG_FAN_MAX(channel), val); + mutex_unlock(&data->lock); + break; + default: + return -EOPNOTSUPP; + } - if (FAN_DATA_VALID(data->fan[attr->index])) - return sprintf(buf, "%d\n", - FAN_PERIOD_TO_RPM(data->fan[attr->index])); - else - return sprintf(buf, "0\n"); + return err; } static ssize_t force_pwm_max_show(struct device *dev, @@ -699,57 +701,20 @@ static ssize_t force_pwm_max_store(struct device *dev, const char *buf, size_t count) { struct adt7470_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; long temp; - u8 reg; + int err; if (kstrtol(buf, 10, &temp)) return -EINVAL; mutex_lock(&data->lock); data->force_pwm_max = temp; - reg = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG); - if (temp) - reg |= ADT7470_FSPD_MASK; - else - reg &= ~ADT7470_FSPD_MASK; - i2c_smbus_write_byte_data(client, ADT7470_REG_CFG, reg); + err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, + ADT7470_FSPD_MASK, + temp ? ADT7470_FSPD_MASK : 0); mutex_unlock(&data->lock); - return count; -} - -static ssize_t pwm_show(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7470_data *data = adt7470_update_device(dev); - - if (IS_ERR(data)) - return PTR_ERR(data); - - return sprintf(buf, "%d\n", data->pwm[attr->index]); -} - -static ssize_t pwm_store(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7470_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - long temp; - - if (kstrtol(buf, 10, &temp)) - return -EINVAL; - - temp = clamp_val(temp, 0, 255); - - mutex_lock(&data->lock); - data->pwm[attr->index] = temp; - i2c_smbus_write_byte_data(client, ADT7470_REG_PWM(attr->index), temp); - mutex_unlock(&data->lock); - - return count; + return err < 0 ? err : count; } /* These are the valid PWM frequencies to the nearest Hz */ @@ -757,17 +722,20 @@ static const int adt7470_freq_map[] = { 11, 15, 22, 29, 35, 44, 59, 88, 1400, 22500 }; -static ssize_t pwm1_freq_show(struct device *dev, - struct device_attribute *devattr, char *buf) +static int pwm1_freq_get(struct device *dev) { - struct adt7470_data *data = adt7470_update_device(dev); - unsigned char cfg_reg_1; - unsigned char cfg_reg_2; + struct adt7470_data *data = dev_get_drvdata(dev); + unsigned int cfg_reg_1, cfg_reg_2; int index; + int err; 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); + err = regmap_read(data->regmap, ADT7470_REG_CFG, &cfg_reg_1); + if (err < 0) + goto out; + err = regmap_read(data->regmap, ADT7470_REG_CFG_2, &cfg_reg_2); + if (err < 0) + goto out; mutex_unlock(&data->lock); index = (cfg_reg_2 & ADT7470_FREQ_MASK) >> ADT7470_FREQ_SHIFT; @@ -776,22 +744,43 @@ static ssize_t pwm1_freq_show(struct device *dev, 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]); + return adt7470_freq_map[index]; + +out: + mutex_unlock(&data->lock); + return err; +} + +static int adt7470_pwm_read(struct device *dev, u32 attr, int channel, long *val) +{ + struct adt7470_data *data = adt7470_update_device(dev); + + if (IS_ERR(data)) + return PTR_ERR(data); + + switch (attr) { + case hwmon_pwm_input: + *val = data->pwm[channel]; + break; + case hwmon_pwm_enable: + *val = 1 + data->pwm_automatic[channel]; + break; + case hwmon_pwm_freq: + *val = pwm1_freq_get(dev); + break; + default: + return -EOPNOTSUPP; + } + + return 0; } -static ssize_t pwm1_freq_store(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) +static int pwm1_freq_set(struct device *dev, long freq) { struct adt7470_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - long freq; + unsigned int low_freq = ADT7470_CFG_LF; int index; - int low_freq = ADT7470_CFG_LF; - unsigned char val; - - if (kstrtol(buf, 10, &freq)) - return -EINVAL; + int err; /* Round the user value given to the closest available frequency */ index = find_closest(freq, adt7470_freq_map, @@ -804,16 +793,61 @@ static ssize_t pwm1_freq_store(struct device *dev, 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); + err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, + ADT7470_CFG_LF, low_freq); + if (err < 0) + goto out; + /* 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)); + err = regmap_update_bits(data->regmap, ADT7470_REG_CFG_2, + ADT7470_FREQ_MASK, + index << ADT7470_FREQ_SHIFT); +out: mutex_unlock(&data->lock); - return count; + return err; +} + +static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val) +{ + struct adt7470_data *data = dev_get_drvdata(dev); + unsigned int pwm_auto_reg_mask; + int err; + + switch (attr) { + case hwmon_pwm_input: + val = clamp_val(val, 0, 255); + mutex_lock(&data->lock); + data->pwm[channel] = val; + err = regmap_write(data->regmap, ADT7470_REG_PWM(channel), + data->pwm[channel]); + mutex_unlock(&data->lock); + break; + case hwmon_pwm_enable: + if (channel % 2) + pwm_auto_reg_mask = ADT7470_PWM2_AUTO_MASK; + else + pwm_auto_reg_mask = ADT7470_PWM1_AUTO_MASK; + + if (val != 2 && val != 1) + return -EINVAL; + val--; + + mutex_lock(&data->lock); + data->pwm_automatic[channel] = val; + err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(channel), + pwm_auto_reg_mask, + val ? pwm_auto_reg_mask : 0); + mutex_unlock(&data->lock); + break; + case hwmon_pwm_freq: + err = pwm1_freq_set(dev, val); + break; + default: + return -EOPNOTSUPP; + } + + return err; } static ssize_t pwm_max_show(struct device *dev, @@ -834,8 +868,8 @@ static ssize_t pwm_max_store(struct device *dev, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct adt7470_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; long temp; + int err; if (kstrtol(buf, 10, &temp)) return -EINVAL; @@ -844,11 +878,11 @@ static ssize_t pwm_max_store(struct device *dev, mutex_lock(&data->lock); data->pwm_max[attr->index] = temp; - i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_MAX(attr->index), - temp); + err = regmap_write(data->regmap, ADT7470_REG_PWM_MAX(attr->index), + temp); mutex_unlock(&data->lock); - return count; + return err < 0 ? err : count; } static ssize_t pwm_min_show(struct device *dev, @@ -869,8 +903,8 @@ static ssize_t pwm_min_store(struct device *dev, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct adt7470_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; long temp; + int err; if (kstrtol(buf, 10, &temp)) return -EINVAL; @@ -879,11 +913,11 @@ static ssize_t pwm_min_store(struct device *dev, mutex_lock(&data->lock); data->pwm_min[attr->index] = temp; - i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_MIN(attr->index), - temp); + err = regmap_write(data->regmap, ADT7470_REG_PWM_MIN(attr->index), + temp); mutex_unlock(&data->lock); - return count; + return err < 0 ? err : count; } static ssize_t pwm_tmax_show(struct device *dev, @@ -917,8 +951,8 @@ static ssize_t pwm_tmin_store(struct device *dev, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct adt7470_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; long temp; + int err; if (kstrtol(buf, 10, &temp)) return -EINVAL; @@ -928,60 +962,11 @@ static ssize_t pwm_tmin_store(struct device *dev, mutex_lock(&data->lock); data->pwm_tmin[attr->index] = temp; - i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_TMIN(attr->index), - temp); - mutex_unlock(&data->lock); - - return count; -} - -static ssize_t pwm_auto_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7470_data *data = adt7470_update_device(dev); - - if (IS_ERR(data)) - return PTR_ERR(data); - - return sprintf(buf, "%d\n", 1 + data->pwm_automatic[attr->index]); -} - -static ssize_t pwm_auto_store(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7470_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - int pwm_auto_reg = ADT7470_REG_PWM_CFG(attr->index); - int pwm_auto_reg_mask; - long temp; - u8 reg; - - if (kstrtol(buf, 10, &temp)) - return -EINVAL; - - if (attr->index % 2) - pwm_auto_reg_mask = ADT7470_PWM2_AUTO_MASK; - else - pwm_auto_reg_mask = ADT7470_PWM1_AUTO_MASK; - - if (temp != 2 && temp != 1) - return -EINVAL; - temp--; - - mutex_lock(&data->lock); - data->pwm_automatic[attr->index] = temp; - reg = i2c_smbus_read_byte_data(client, pwm_auto_reg); - if (temp) - reg |= pwm_auto_reg_mask; - else - reg &= ~pwm_auto_reg_mask; - i2c_smbus_write_byte_data(client, pwm_auto_reg, reg); + err = regmap_write(data->regmap, ADT7470_REG_PWM_TMIN(attr->index), + temp); mutex_unlock(&data->lock); - return count; + return err < 0 ? err : count; } static ssize_t pwm_auto_temp_show(struct device *dev, @@ -1016,10 +1001,10 @@ static ssize_t pwm_auto_temp_store(struct device *dev, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct adt7470_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; int pwm_auto_reg = ADT7470_REG_PWM_AUTO_TEMP(attr->index); + unsigned int mask, val; long temp; - u8 reg; + int err; if (kstrtol(buf, 10, &temp)) return -EINVAL; @@ -1030,111 +1015,27 @@ static ssize_t pwm_auto_temp_store(struct device *dev, mutex_lock(&data->lock); data->pwm_automatic[attr->index] = temp; - reg = i2c_smbus_read_byte_data(client, pwm_auto_reg); if (!(attr->index % 2)) { - reg &= 0xF; - reg |= (temp << 4) & 0xF0; + mask = 0xF0; + val = (temp << 4) & 0xF0; } else { - reg &= 0xF0; - reg |= temp & 0xF; + mask = 0x0F; + val = temp & 0x0F; } - i2c_smbus_write_byte_data(client, pwm_auto_reg, reg); + err = regmap_update_bits(data->regmap, pwm_auto_reg, mask, val); mutex_unlock(&data->lock); - return count; -} - -static ssize_t alarm_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adt7470_data *data = adt7470_update_device(dev); - - if (data->alarm & attr->index) - return sprintf(buf, "1\n"); - else - return sprintf(buf, "0\n"); + return err < 0 ? err : count; } static DEVICE_ATTR_RW(alarm_mask); static DEVICE_ATTR_RW(num_temp_sensors); static DEVICE_ATTR_RW(auto_update_interval); -static SENSOR_DEVICE_ATTR_RW(temp1_max, temp_max, 0); -static SENSOR_DEVICE_ATTR_RW(temp2_max, temp_max, 1); -static SENSOR_DEVICE_ATTR_RW(temp3_max, temp_max, 2); -static SENSOR_DEVICE_ATTR_RW(temp4_max, temp_max, 3); -static SENSOR_DEVICE_ATTR_RW(temp5_max, temp_max, 4); -static SENSOR_DEVICE_ATTR_RW(temp6_max, temp_max, 5); -static SENSOR_DEVICE_ATTR_RW(temp7_max, temp_max, 6); -static SENSOR_DEVICE_ATTR_RW(temp8_max, temp_max, 7); -static SENSOR_DEVICE_ATTR_RW(temp9_max, temp_max, 8); -static SENSOR_DEVICE_ATTR_RW(temp10_max, temp_max, 9); - -static SENSOR_DEVICE_ATTR_RW(temp1_min, temp_min, 0); -static SENSOR_DEVICE_ATTR_RW(temp2_min, temp_min, 1); -static SENSOR_DEVICE_ATTR_RW(temp3_min, temp_min, 2); -static SENSOR_DEVICE_ATTR_RW(temp4_min, temp_min, 3); -static SENSOR_DEVICE_ATTR_RW(temp5_min, temp_min, 4); -static SENSOR_DEVICE_ATTR_RW(temp6_min, temp_min, 5); -static SENSOR_DEVICE_ATTR_RW(temp7_min, temp_min, 6); -static SENSOR_DEVICE_ATTR_RW(temp8_min, temp_min, 7); -static SENSOR_DEVICE_ATTR_RW(temp9_min, temp_min, 8); -static SENSOR_DEVICE_ATTR_RW(temp10_min, temp_min, 9); - -static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, 0); -static SENSOR_DEVICE_ATTR_RO(temp2_input, temp, 1); -static SENSOR_DEVICE_ATTR_RO(temp3_input, temp, 2); -static SENSOR_DEVICE_ATTR_RO(temp4_input, temp, 3); -static SENSOR_DEVICE_ATTR_RO(temp5_input, temp, 4); -static SENSOR_DEVICE_ATTR_RO(temp6_input, temp, 5); -static SENSOR_DEVICE_ATTR_RO(temp7_input, temp, 6); -static SENSOR_DEVICE_ATTR_RO(temp8_input, temp, 7); -static SENSOR_DEVICE_ATTR_RO(temp9_input, temp, 8); -static SENSOR_DEVICE_ATTR_RO(temp10_input, temp, 9); - -static SENSOR_DEVICE_ATTR_RO(temp1_alarm, alarm, ADT7470_R1T_ALARM); -static SENSOR_DEVICE_ATTR_RO(temp2_alarm, alarm, ADT7470_R2T_ALARM); -static SENSOR_DEVICE_ATTR_RO(temp3_alarm, alarm, ADT7470_R3T_ALARM); -static SENSOR_DEVICE_ATTR_RO(temp4_alarm, alarm, ADT7470_R4T_ALARM); -static SENSOR_DEVICE_ATTR_RO(temp5_alarm, alarm, ADT7470_R5T_ALARM); -static SENSOR_DEVICE_ATTR_RO(temp6_alarm, alarm, ADT7470_R6T_ALARM); -static SENSOR_DEVICE_ATTR_RO(temp7_alarm, alarm, ADT7470_R7T_ALARM); -static SENSOR_DEVICE_ATTR_RO(temp8_alarm, alarm, ALARM2(ADT7470_R8T_ALARM)); -static SENSOR_DEVICE_ATTR_RO(temp9_alarm, alarm, ALARM2(ADT7470_R9T_ALARM)); -static SENSOR_DEVICE_ATTR_RO(temp10_alarm, alarm, ALARM2(ADT7470_R10T_ALARM)); - -static SENSOR_DEVICE_ATTR_RW(fan1_max, fan_max, 0); -static SENSOR_DEVICE_ATTR_RW(fan2_max, fan_max, 1); -static SENSOR_DEVICE_ATTR_RW(fan3_max, fan_max, 2); -static SENSOR_DEVICE_ATTR_RW(fan4_max, fan_max, 3); - -static SENSOR_DEVICE_ATTR_RW(fan1_min, fan_min, 0); -static SENSOR_DEVICE_ATTR_RW(fan2_min, fan_min, 1); -static SENSOR_DEVICE_ATTR_RW(fan3_min, fan_min, 2); -static SENSOR_DEVICE_ATTR_RW(fan4_min, fan_min, 3); - -static SENSOR_DEVICE_ATTR_RO(fan1_input, fan, 0); -static SENSOR_DEVICE_ATTR_RO(fan2_input, fan, 1); -static SENSOR_DEVICE_ATTR_RO(fan3_input, fan, 2); -static SENSOR_DEVICE_ATTR_RO(fan4_input, fan, 3); - -static SENSOR_DEVICE_ATTR_RO(fan1_alarm, alarm, ALARM2(ADT7470_FAN1_ALARM)); -static SENSOR_DEVICE_ATTR_RO(fan2_alarm, alarm, ALARM2(ADT7470_FAN2_ALARM)); -static SENSOR_DEVICE_ATTR_RO(fan3_alarm, alarm, ALARM2(ADT7470_FAN3_ALARM)); -static SENSOR_DEVICE_ATTR_RO(fan4_alarm, alarm, ALARM2(ADT7470_FAN4_ALARM)); - static SENSOR_DEVICE_ATTR_RW(force_pwm_max, force_pwm_max, 0); -static SENSOR_DEVICE_ATTR_RW(pwm1, pwm, 0); -static SENSOR_DEVICE_ATTR_RW(pwm2, pwm, 1); -static SENSOR_DEVICE_ATTR_RW(pwm3, pwm, 2); -static SENSOR_DEVICE_ATTR_RW(pwm4, pwm, 3); - -static DEVICE_ATTR_RW(pwm1_freq); - static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point1_pwm, pwm_min, 0); static SENSOR_DEVICE_ATTR_RW(pwm2_auto_point1_pwm, pwm_min, 1); static SENSOR_DEVICE_ATTR_RW(pwm3_auto_point1_pwm, pwm_min, 2); @@ -1155,11 +1056,6 @@ static SENSOR_DEVICE_ATTR_RO(pwm2_auto_point2_temp, pwm_tmax, 1); static SENSOR_DEVICE_ATTR_RO(pwm3_auto_point2_temp, pwm_tmax, 2); static SENSOR_DEVICE_ATTR_RO(pwm4_auto_point2_temp, pwm_tmax, 3); -static SENSOR_DEVICE_ATTR_RW(pwm1_enable, pwm_auto, 0); -static SENSOR_DEVICE_ATTR_RW(pwm2_enable, pwm_auto, 1); -static SENSOR_DEVICE_ATTR_RW(pwm3_enable, pwm_auto, 2); -static SENSOR_DEVICE_ATTR_RW(pwm4_enable, pwm_auto, 3); - static SENSOR_DEVICE_ATTR_RW(pwm1_auto_channels_temp, pwm_auto_temp, 0); static SENSOR_DEVICE_ATTR_RW(pwm2_auto_channels_temp, pwm_auto_temp, 1); static SENSOR_DEVICE_ATTR_RW(pwm3_auto_channels_temp, pwm_auto_temp, 2); @@ -1169,68 +1065,7 @@ static struct attribute *adt7470_attrs[] = { &dev_attr_alarm_mask.attr, &dev_attr_num_temp_sensors.attr, &dev_attr_auto_update_interval.attr, - &sensor_dev_attr_temp1_max.dev_attr.attr, - &sensor_dev_attr_temp2_max.dev_attr.attr, - &sensor_dev_attr_temp3_max.dev_attr.attr, - &sensor_dev_attr_temp4_max.dev_attr.attr, - &sensor_dev_attr_temp5_max.dev_attr.attr, - &sensor_dev_attr_temp6_max.dev_attr.attr, - &sensor_dev_attr_temp7_max.dev_attr.attr, - &sensor_dev_attr_temp8_max.dev_attr.attr, - &sensor_dev_attr_temp9_max.dev_attr.attr, - &sensor_dev_attr_temp10_max.dev_attr.attr, - &sensor_dev_attr_temp1_min.dev_attr.attr, - &sensor_dev_attr_temp2_min.dev_attr.attr, - &sensor_dev_attr_temp3_min.dev_attr.attr, - &sensor_dev_attr_temp4_min.dev_attr.attr, - &sensor_dev_attr_temp5_min.dev_attr.attr, - &sensor_dev_attr_temp6_min.dev_attr.attr, - &sensor_dev_attr_temp7_min.dev_attr.attr, - &sensor_dev_attr_temp8_min.dev_attr.attr, - &sensor_dev_attr_temp9_min.dev_attr.attr, - &sensor_dev_attr_temp10_min.dev_attr.attr, - &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_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, - &sensor_dev_attr_temp10_input.dev_attr.attr, - &sensor_dev_attr_temp1_alarm.dev_attr.attr, - &sensor_dev_attr_temp2_alarm.dev_attr.attr, - &sensor_dev_attr_temp3_alarm.dev_attr.attr, - &sensor_dev_attr_temp4_alarm.dev_attr.attr, - &sensor_dev_attr_temp5_alarm.dev_attr.attr, - &sensor_dev_attr_temp6_alarm.dev_attr.attr, - &sensor_dev_attr_temp7_alarm.dev_attr.attr, - &sensor_dev_attr_temp8_alarm.dev_attr.attr, - &sensor_dev_attr_temp9_alarm.dev_attr.attr, - &sensor_dev_attr_temp10_alarm.dev_attr.attr, - &sensor_dev_attr_fan1_max.dev_attr.attr, - &sensor_dev_attr_fan2_max.dev_attr.attr, - &sensor_dev_attr_fan3_max.dev_attr.attr, - &sensor_dev_attr_fan4_max.dev_attr.attr, - &sensor_dev_attr_fan1_min.dev_attr.attr, - &sensor_dev_attr_fan2_min.dev_attr.attr, - &sensor_dev_attr_fan3_min.dev_attr.attr, - &sensor_dev_attr_fan4_min.dev_attr.attr, - &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_fan1_alarm.dev_attr.attr, - &sensor_dev_attr_fan2_alarm.dev_attr.attr, - &sensor_dev_attr_fan3_alarm.dev_attr.attr, - &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, &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr, &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr, &sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr, @@ -1247,10 +1082,6 @@ static struct attribute *adt7470_attrs[] = { &sensor_dev_attr_pwm2_auto_point2_temp.dev_attr.attr, &sensor_dev_attr_pwm3_auto_point2_temp.dev_attr.attr, &sensor_dev_attr_pwm4_auto_point2_temp.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_pwm1_auto_channels_temp.dev_attr.attr, &sensor_dev_attr_pwm2_auto_channels_temp.dev_attr.attr, &sensor_dev_attr_pwm3_auto_channels_temp.dev_attr.attr, @@ -1260,6 +1091,129 @@ static struct attribute *adt7470_attrs[] = { ATTRIBUTE_GROUPS(adt7470); +static int adt7470_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, long *val) +{ + switch (type) { + case hwmon_temp: + return adt7470_temp_read(dev, attr, channel, val); + case hwmon_fan: + return adt7470_fan_read(dev, attr, channel, val); + case hwmon_pwm: + return adt7470_pwm_read(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} + +static int adt7470_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, long val) +{ + switch (type) { + case hwmon_temp: + return adt7470_temp_write(dev, attr, channel, val); + case hwmon_fan: + return adt7470_fan_write(dev, attr, channel, val); + case hwmon_pwm: + return adt7470_pwm_write(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} + +static umode_t adt7470_is_visible(const void *_data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + umode_t mode = 0; + + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp: + case hwmon_temp_alarm: + mode = 0444; + break; + case hwmon_temp_min: + case hwmon_temp_max: + mode = 0644; + break; + default: + break; + } + break; + case hwmon_fan: + switch (attr) { + case hwmon_fan_input: + case hwmon_fan_alarm: + mode = 0444; + break; + case hwmon_fan_min: + case hwmon_fan_max: + mode = 0644; + break; + default: + break; + } + break; + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_input: + case hwmon_pwm_enable: + mode = 0644; + break; + case hwmon_pwm_freq: + if (channel == 0) + mode = 0644; + else + mode = 0; + break; + default: + break; + } + break; + default: + break; + } + + return mode; +} + +static const struct hwmon_ops adt7470_hwmon_ops = { + .is_visible = adt7470_is_visible, + .read = adt7470_read, + .write = adt7470_write, +}; + +static const struct hwmon_channel_info *adt7470_info[] = { + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM, + HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM, + HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM, + HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM, + HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM, + HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM, + HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM, + HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM, + HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM, + HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | HWMON_T_ALARM), + HWMON_CHANNEL_INFO(fan, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX | HWMON_F_DIV | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX | HWMON_F_DIV | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX | HWMON_F_DIV | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX | HWMON_F_DIV | HWMON_F_ALARM), + HWMON_CHANNEL_INFO(pwm, + HWMON_PWM_INPUT | HWMON_PWM_ENABLE | HWMON_PWM_FREQ, + HWMON_PWM_INPUT | HWMON_PWM_ENABLE, + HWMON_PWM_INPUT | HWMON_PWM_ENABLE, + HWMON_PWM_INPUT | HWMON_PWM_ENABLE), + NULL +}; + +static const struct hwmon_chip_info adt7470_chip_info = { + .ops = &adt7470_hwmon_ops, + .info = adt7470_info, +}; + /* Return 0 if detection is successful, -ENODEV otherwise */ static int adt7470_detect(struct i2c_client *client, struct i2c_board_info *info) @@ -1282,28 +1236,24 @@ static int adt7470_detect(struct i2c_client *client, if (revision != ADT7470_REVISION) return -ENODEV; - strlcpy(info->type, "adt7470", I2C_NAME_SIZE); + strscpy(info->type, "adt7470", I2C_NAME_SIZE); return 0; } -static void adt7470_init_client(struct i2c_client *client) -{ - int reg = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG); - - if (reg < 0) { - dev_err(&client->dev, "cannot read configuration register\n"); - } else { - /* start monitoring (and do a self-test) */ - i2c_smbus_write_byte_data(client, ADT7470_REG_CFG, reg | 3); - } -} +static const struct regmap_config adt7470_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .use_single_read = true, + .use_single_write = true, +}; static int adt7470_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct adt7470_data *data; struct device *hwmon_dev; + int err; data = devm_kzalloc(dev, sizeof(struct adt7470_data), GFP_KERNEL); if (!data) @@ -1311,29 +1261,34 @@ static int adt7470_probe(struct i2c_client *client) data->num_temp_sensors = -1; data->auto_update_interval = AUTO_UPDATE_INTERVAL; + data->regmap = devm_regmap_init_i2c(client, &adt7470_regmap_config); + if (IS_ERR(data->regmap)) + return PTR_ERR(data->regmap); i2c_set_clientdata(client, data); - data->client = client; mutex_init(&data->lock); dev_info(&client->dev, "%s chip found\n", client->name); /* Initialize the ADT7470 chip */ - adt7470_init_client(client); + err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, + ADT7470_STRT_MASK | ADT7470_TEST_MASK, + ADT7470_STRT_MASK | ADT7470_TEST_MASK); + if (err < 0) + return err; /* Register sysfs hooks */ - hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, - data, - adt7470_groups); + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, + &adt7470_chip_info, + adt7470_groups); if (IS_ERR(hwmon_dev)) return PTR_ERR(hwmon_dev); data->auto_update = kthread_run(adt7470_update_thread, client, "%s", dev_name(hwmon_dev)); - if (IS_ERR(data->auto_update)) { + if (IS_ERR(data->auto_update)) return PTR_ERR(data->auto_update); - } return 0; } diff --git a/drivers/hwmon/aquacomputer_d5next.c b/drivers/hwmon/aquacomputer_d5next.c new file mode 100644 index 000000000000..fb9341a53051 --- /dev/null +++ b/drivers/hwmon/aquacomputer_d5next.c @@ -0,0 +1,363 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * hwmon driver for Aquacomputer D5 Next watercooling pump + * + * The D5 Next sends HID reports (with ID 0x01) every second to report sensor values + * (coolant temperature, pump and fan speed, voltage, current and power). It responds to + * Get_Report requests, but returns a dummy value of no use. + * + * Copyright 2021 Aleksa Savic <savicaleksa83@gmail.com> + */ + +#include <asm/unaligned.h> +#include <linux/debugfs.h> +#include <linux/hid.h> +#include <linux/hwmon.h> +#include <linux/jiffies.h> +#include <linux/module.h> +#include <linux/seq_file.h> + +#define DRIVER_NAME "aquacomputer-d5next" + +#define D5NEXT_STATUS_REPORT_ID 0x01 +#define D5NEXT_STATUS_UPDATE_INTERVAL (2 * HZ) /* In seconds */ + +/* Register offsets for the D5 Next pump */ + +#define D5NEXT_SERIAL_FIRST_PART 3 +#define D5NEXT_SERIAL_SECOND_PART 5 +#define D5NEXT_FIRMWARE_VERSION 13 +#define D5NEXT_POWER_CYCLES 24 + +#define D5NEXT_COOLANT_TEMP 87 + +#define D5NEXT_PUMP_SPEED 116 +#define D5NEXT_FAN_SPEED 103 + +#define D5NEXT_PUMP_POWER 114 +#define D5NEXT_FAN_POWER 101 + +#define D5NEXT_PUMP_VOLTAGE 110 +#define D5NEXT_FAN_VOLTAGE 97 +#define D5NEXT_5V_VOLTAGE 57 + +#define D5NEXT_PUMP_CURRENT 112 +#define D5NEXT_FAN_CURRENT 99 + +/* Labels for provided values */ + +#define L_COOLANT_TEMP "Coolant temp" + +#define L_PUMP_SPEED "Pump speed" +#define L_FAN_SPEED "Fan speed" + +#define L_PUMP_POWER "Pump power" +#define L_FAN_POWER "Fan power" + +#define L_PUMP_VOLTAGE "Pump voltage" +#define L_FAN_VOLTAGE "Fan voltage" +#define L_5V_VOLTAGE "+5V voltage" + +#define L_PUMP_CURRENT "Pump current" +#define L_FAN_CURRENT "Fan current" + +static const char *const label_speeds[] = { + L_PUMP_SPEED, + L_FAN_SPEED, +}; + +static const char *const label_power[] = { + L_PUMP_POWER, + L_FAN_POWER, +}; + +static const char *const label_voltages[] = { + L_PUMP_VOLTAGE, + L_FAN_VOLTAGE, + L_5V_VOLTAGE, +}; + +static const char *const label_current[] = { + L_PUMP_CURRENT, + L_FAN_CURRENT, +}; + +struct d5next_data { + struct hid_device *hdev; + struct device *hwmon_dev; + struct dentry *debugfs; + s32 temp_input; + u16 speed_input[2]; + u32 power_input[2]; + u16 voltage_input[3]; + u16 current_input[2]; + u32 serial_number[2]; + u16 firmware_version; + u32 power_cycles; /* How many times the device was powered on */ + unsigned long updated; +}; + +static umode_t d5next_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr, + int channel) +{ + return 0444; +} + +static int d5next_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, + long *val) +{ + struct d5next_data *priv = dev_get_drvdata(dev); + + if (time_after(jiffies, priv->updated + D5NEXT_STATUS_UPDATE_INTERVAL)) + return -ENODATA; + + switch (type) { + case hwmon_temp: + *val = priv->temp_input; + break; + case hwmon_fan: + *val = priv->speed_input[channel]; + break; + case hwmon_power: + *val = priv->power_input[channel]; + break; + case hwmon_in: + *val = priv->voltage_input[channel]; + break; + case hwmon_curr: + *val = priv->current_input[channel]; + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int d5next_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, const char **str) +{ + switch (type) { + case hwmon_temp: + *str = L_COOLANT_TEMP; + break; + case hwmon_fan: + *str = label_speeds[channel]; + break; + case hwmon_power: + *str = label_power[channel]; + break; + case hwmon_in: + *str = label_voltages[channel]; + break; + case hwmon_curr: + *str = label_current[channel]; + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static const struct hwmon_ops d5next_hwmon_ops = { + .is_visible = d5next_is_visible, + .read = d5next_read, + .read_string = d5next_read_string, +}; + +static const struct hwmon_channel_info *d5next_info[] = { + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_LABEL), + HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_LABEL, HWMON_F_INPUT | HWMON_F_LABEL), + HWMON_CHANNEL_INFO(power, HWMON_P_INPUT | HWMON_P_LABEL, HWMON_P_INPUT | HWMON_P_LABEL), + HWMON_CHANNEL_INFO(in, HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL), + HWMON_CHANNEL_INFO(curr, HWMON_C_INPUT | HWMON_C_LABEL, HWMON_C_INPUT | HWMON_C_LABEL), + NULL +}; + +static const struct hwmon_chip_info d5next_chip_info = { + .ops = &d5next_hwmon_ops, + .info = d5next_info, +}; + +static int d5next_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size) +{ + struct d5next_data *priv; + + if (report->id != D5NEXT_STATUS_REPORT_ID) + return 0; + + priv = hid_get_drvdata(hdev); + + /* Info provided with every report */ + + priv->serial_number[0] = get_unaligned_be16(data + D5NEXT_SERIAL_FIRST_PART); + priv->serial_number[1] = get_unaligned_be16(data + D5NEXT_SERIAL_SECOND_PART); + + priv->firmware_version = get_unaligned_be16(data + D5NEXT_FIRMWARE_VERSION); + priv->power_cycles = get_unaligned_be32(data + D5NEXT_POWER_CYCLES); + + /* Sensor readings */ + + priv->temp_input = get_unaligned_be16(data + D5NEXT_COOLANT_TEMP) * 10; + + priv->speed_input[0] = get_unaligned_be16(data + D5NEXT_PUMP_SPEED); + priv->speed_input[1] = get_unaligned_be16(data + D5NEXT_FAN_SPEED); + + priv->power_input[0] = get_unaligned_be16(data + D5NEXT_PUMP_POWER) * 10000; + priv->power_input[1] = get_unaligned_be16(data + D5NEXT_FAN_POWER) * 10000; + + priv->voltage_input[0] = get_unaligned_be16(data + D5NEXT_PUMP_VOLTAGE) * 10; + priv->voltage_input[1] = get_unaligned_be16(data + D5NEXT_FAN_VOLTAGE) * 10; + priv->voltage_input[2] = get_unaligned_be16(data + D5NEXT_5V_VOLTAGE) * 10; + + priv->current_input[0] = get_unaligned_be16(data + D5NEXT_PUMP_CURRENT); + priv->current_input[1] = get_unaligned_be16(data + D5NEXT_FAN_CURRENT); + + priv->updated = jiffies; + + return 0; +} + +#ifdef CONFIG_DEBUG_FS + +static int serial_number_show(struct seq_file *seqf, void *unused) +{ + struct d5next_data *priv = seqf->private; + + seq_printf(seqf, "%05u-%05u\n", priv->serial_number[0], priv->serial_number[1]); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(serial_number); + +static int firmware_version_show(struct seq_file *seqf, void *unused) +{ + struct d5next_data *priv = seqf->private; + + seq_printf(seqf, "%u\n", priv->firmware_version); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(firmware_version); + +static int power_cycles_show(struct seq_file *seqf, void *unused) +{ + struct d5next_data *priv = seqf->private; + + seq_printf(seqf, "%u\n", priv->power_cycles); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(power_cycles); + +static void d5next_debugfs_init(struct d5next_data *priv) +{ + char name[32]; + + scnprintf(name, sizeof(name), "%s-%s", DRIVER_NAME, dev_name(&priv->hdev->dev)); + + priv->debugfs = debugfs_create_dir(name, NULL); + debugfs_create_file("serial_number", 0444, priv->debugfs, priv, &serial_number_fops); + debugfs_create_file("firmware_version", 0444, priv->debugfs, priv, &firmware_version_fops); + debugfs_create_file("power_cycles", 0444, priv->debugfs, priv, &power_cycles_fops); +} + +#else + +static void d5next_debugfs_init(struct d5next_data *priv) +{ +} + +#endif + +static int d5next_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + struct d5next_data *priv; + int ret; + + priv = devm_kzalloc(&hdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->hdev = hdev; + hid_set_drvdata(hdev, priv); + + priv->updated = jiffies - D5NEXT_STATUS_UPDATE_INTERVAL; + + ret = hid_parse(hdev); + if (ret) + return ret; + + ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW); + if (ret) + return ret; + + ret = hid_hw_open(hdev); + if (ret) + goto fail_and_stop; + + priv->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "d5next", priv, + &d5next_chip_info, NULL); + + if (IS_ERR(priv->hwmon_dev)) { + ret = PTR_ERR(priv->hwmon_dev); + goto fail_and_close; + } + + d5next_debugfs_init(priv); + + return 0; + +fail_and_close: + hid_hw_close(hdev); +fail_and_stop: + hid_hw_stop(hdev); + return ret; +} + +static void d5next_remove(struct hid_device *hdev) +{ + struct d5next_data *priv = hid_get_drvdata(hdev); + + debugfs_remove_recursive(priv->debugfs); + hwmon_device_unregister(priv->hwmon_dev); + + hid_hw_close(hdev); + hid_hw_stop(hdev); +} + +static const struct hid_device_id d5next_table[] = { + { HID_USB_DEVICE(0x0c70, 0xf00e) }, /* Aquacomputer D5 Next */ + {}, +}; + +MODULE_DEVICE_TABLE(hid, d5next_table); + +static struct hid_driver d5next_driver = { + .name = DRIVER_NAME, + .id_table = d5next_table, + .probe = d5next_probe, + .remove = d5next_remove, + .raw_event = d5next_raw_event, +}; + +static int __init d5next_init(void) +{ + return hid_register_driver(&d5next_driver); +} + +static void __exit d5next_exit(void) +{ + hid_unregister_driver(&d5next_driver); +} + +/* Request to initialize after the HID bus to ensure it's not being loaded before */ + +late_initcall(d5next_init); +module_exit(d5next_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Aleksa Savic <savicaleksa83@gmail.com>"); +MODULE_DESCRIPTION("Hwmon driver for Aquacomputer D5 Next pump"); diff --git a/drivers/hwmon/axi-fan-control.c b/drivers/hwmon/axi-fan-control.c index e3f6b03e6764..d2092c17d993 100644 --- a/drivers/hwmon/axi-fan-control.c +++ b/drivers/hwmon/axi-fan-control.c @@ -8,6 +8,7 @@ #include <linux/clk.h> #include <linux/fpga/adi-axi-common.h> #include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/kernel.h> @@ -23,6 +24,14 @@ #define ADI_REG_PWM_PERIOD 0x00c0 #define ADI_REG_TACH_MEASUR 0x00c4 #define ADI_REG_TEMPERATURE 0x00c8 +#define ADI_REG_TEMP_00_H 0x0100 +#define ADI_REG_TEMP_25_L 0x0104 +#define ADI_REG_TEMP_25_H 0x0108 +#define ADI_REG_TEMP_50_L 0x010c +#define ADI_REG_TEMP_50_H 0x0110 +#define ADI_REG_TEMP_75_L 0x0114 +#define ADI_REG_TEMP_75_H 0x0118 +#define ADI_REG_TEMP_100_L 0x011c #define ADI_REG_IRQ_MASK 0x0040 #define ADI_REG_IRQ_PENDING 0x0044 @@ -62,6 +71,39 @@ static inline u32 axi_ioread(const u32 reg, return ioread32(ctl->base + reg); } +/* + * The core calculates the temperature as: + * T = /raw * 509.3140064 / 65535) - 280.2308787 + */ +static ssize_t axi_fan_control_show(struct device *dev, struct device_attribute *da, char *buf) +{ + struct axi_fan_control_data *ctl = dev_get_drvdata(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + u32 temp = axi_ioread(attr->index, ctl); + + temp = DIV_ROUND_CLOSEST_ULL(temp * 509314ULL, 65535) - 280230; + + return sprintf(buf, "%u\n", temp); +} + +static ssize_t axi_fan_control_store(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct axi_fan_control_data *ctl = dev_get_drvdata(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + u32 temp; + int ret; + + ret = kstrtou32(buf, 10, &temp); + if (ret) + return ret; + + temp = DIV_ROUND_CLOSEST_ULL((temp + 280230) * 65535ULL, 509314); + axi_iowrite(temp, attr->index, ctl); + + return count; +} + static long axi_fan_control_get_pwm_duty(const struct axi_fan_control_data *ctl) { u32 pwm_width = axi_ioread(ADI_REG_PWM_WIDTH, ctl); @@ -283,18 +325,9 @@ static irqreturn_t axi_fan_control_irq_handler(int irq, void *data) u32 irq_pending = axi_ioread(ADI_REG_IRQ_PENDING, ctl); u32 clear_mask; - if (irq_pending & ADI_IRQ_SRC_NEW_MEASUR) { - if (ctl->update_tacho_params) { - u32 new_tach = axi_ioread(ADI_REG_TACH_MEASUR, ctl); - - /* get 25% tolerance */ - u32 tach_tol = DIV_ROUND_CLOSEST(new_tach * 25, 100); - /* set new tacho parameters */ - axi_iowrite(new_tach, ADI_REG_TACH_PERIOD, ctl); - axi_iowrite(tach_tol, ADI_REG_TACH_TOLERANCE, ctl); - ctl->update_tacho_params = false; - } - } + if (irq_pending & ADI_IRQ_SRC_TEMP_INCREASE) + /* hardware requested a new pwm */ + ctl->hw_pwm_req = true; if (irq_pending & ADI_IRQ_SRC_PWM_CHANGED) { /* @@ -310,9 +343,18 @@ static irqreturn_t axi_fan_control_irq_handler(int irq, void *data) } } - if (irq_pending & ADI_IRQ_SRC_TEMP_INCREASE) - /* hardware requested a new pwm */ - ctl->hw_pwm_req = true; + if (irq_pending & ADI_IRQ_SRC_NEW_MEASUR) { + if (ctl->update_tacho_params) { + u32 new_tach = axi_ioread(ADI_REG_TACH_MEASUR, ctl); + /* get 25% tolerance */ + u32 tach_tol = DIV_ROUND_CLOSEST(new_tach * 25, 100); + + /* set new tacho parameters */ + axi_iowrite(new_tach, ADI_REG_TACH_PERIOD, ctl); + axi_iowrite(tach_tol, ADI_REG_TACH_TOLERANCE, ctl); + ctl->update_tacho_params = false; + } + } if (irq_pending & ADI_IRQ_SRC_TACH_ERR) ctl->fan_fault = 1; @@ -351,6 +393,11 @@ static int axi_fan_control_init(struct axi_fan_control_data *ctl, return ret; } +static void axi_fan_control_clk_disable(void *clk) +{ + clk_disable_unprepare(clk); +} + static const struct hwmon_channel_info *axi_fan_control_info[] = { HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT), HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_FAULT | HWMON_F_LABEL), @@ -370,6 +417,36 @@ static const struct hwmon_chip_info axi_chip_info = { .info = axi_fan_control_info, }; +/* temperature threshold below which PWM should be 0% */ +static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point1_temp_hyst, axi_fan_control, ADI_REG_TEMP_00_H); +/* temperature threshold above which PWM should be 25% */ +static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point1_temp, axi_fan_control, ADI_REG_TEMP_25_L); +/* temperature threshold below which PWM should be 25% */ +static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point2_temp_hyst, axi_fan_control, ADI_REG_TEMP_25_H); +/* temperature threshold above which PWM should be 50% */ +static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point2_temp, axi_fan_control, ADI_REG_TEMP_50_L); +/* temperature threshold below which PWM should be 50% */ +static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point3_temp_hyst, axi_fan_control, ADI_REG_TEMP_50_H); +/* temperature threshold above which PWM should be 75% */ +static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point3_temp, axi_fan_control, ADI_REG_TEMP_75_L); +/* temperature threshold below which PWM should be 75% */ +static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point4_temp_hyst, axi_fan_control, ADI_REG_TEMP_75_H); +/* temperature threshold above which PWM should be 100% */ +static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point4_temp, axi_fan_control, ADI_REG_TEMP_100_L); + +static struct attribute *axi_fan_control_attrs[] = { + &sensor_dev_attr_pwm1_auto_point1_temp_hyst.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point2_temp_hyst.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point2_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point3_temp_hyst.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point3_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point4_temp_hyst.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point4_temp.dev_attr.attr, + NULL, +}; +ATTRIBUTE_GROUPS(axi_fan_control); + static const u32 version_1_0_0 = ADI_AXI_PCORE_VER(1, 0, 'a'); static const struct of_device_id axi_fan_control_of_match[] = { @@ -406,6 +483,14 @@ static int axi_fan_control_probe(struct platform_device *pdev) return PTR_ERR(clk); } + ret = clk_prepare_enable(clk); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&pdev->dev, axi_fan_control_clk_disable, clk); + if (ret) + return ret; + ctl->clk_rate = clk_get_rate(clk); if (!ctl->clk_rate) return -EINVAL; @@ -446,7 +531,7 @@ static int axi_fan_control_probe(struct platform_device *pdev) name, ctl, &axi_chip_info, - NULL); + axi_fan_control_groups); return PTR_ERR_OR_ZERO(ctl->hdev); } diff --git a/drivers/hwmon/dell-smm-hwmon.c b/drivers/hwmon/dell-smm-hwmon.c index f2221ca0aa7b..774c1b0715d9 100644 --- a/drivers/hwmon/dell-smm-hwmon.c +++ b/drivers/hwmon/dell-smm-hwmon.c @@ -14,7 +14,9 @@ #include <linux/cpu.h> #include <linux/delay.h> +#include <linux/err.h> #include <linux/module.h> +#include <linux/platform_device.h> #include <linux/types.h> #include <linux/init.h> #include <linux/proc_fs.h> @@ -23,7 +25,6 @@ #include <linux/capability.h> #include <linux/mutex.h> #include <linux/hwmon.h> -#include <linux/hwmon-sysfs.h> #include <linux/uaccess.h> #include <linux/io.h> #include <linux/sched.h> @@ -58,32 +59,24 @@ #define I8K_POWER_AC 0x05 #define I8K_POWER_BATTERY 0x01 -static DEFINE_MUTEX(i8k_mutex); -static char bios_version[4]; -static char bios_machineid[16]; -static struct device *i8k_hwmon_dev; -static u32 i8k_hwmon_flags; -static uint i8k_fan_mult = I8K_FAN_MULT; -static uint i8k_pwm_mult; -static uint i8k_fan_max = I8K_FAN_HIGH; -static bool disallow_fan_type_call; -static bool disallow_fan_support; -static unsigned int manual_fan; -static unsigned int auto_fan; - -#define I8K_HWMON_HAVE_TEMP1 (1 << 0) -#define I8K_HWMON_HAVE_TEMP2 (1 << 1) -#define I8K_HWMON_HAVE_TEMP3 (1 << 2) -#define I8K_HWMON_HAVE_TEMP4 (1 << 3) -#define I8K_HWMON_HAVE_TEMP5 (1 << 4) -#define I8K_HWMON_HAVE_TEMP6 (1 << 5) -#define I8K_HWMON_HAVE_TEMP7 (1 << 6) -#define I8K_HWMON_HAVE_TEMP8 (1 << 7) -#define I8K_HWMON_HAVE_TEMP9 (1 << 8) -#define I8K_HWMON_HAVE_TEMP10 (1 << 9) -#define I8K_HWMON_HAVE_FAN1 (1 << 10) -#define I8K_HWMON_HAVE_FAN2 (1 << 11) -#define I8K_HWMON_HAVE_FAN3 (1 << 12) +#define DELL_SMM_NO_TEMP 10 +#define DELL_SMM_NO_FANS 3 + +struct dell_smm_data { + struct mutex i8k_mutex; /* lock for sensors writes */ + char bios_version[4]; + char bios_machineid[16]; + uint i8k_fan_mult; + uint i8k_pwm_mult; + uint i8k_fan_max; + bool disallow_fan_type_call; + bool disallow_fan_support; + unsigned int manual_fan; + unsigned int auto_fan; + int temp_type[DELL_SMM_NO_TEMP]; + bool fan[DELL_SMM_NO_FANS]; + int fan_type[DELL_SMM_NO_FANS]; +}; MODULE_AUTHOR("Massimo Dal Zotto (dz@debian.org)"); MODULE_AUTHOR("Pali Rohár <pali@kernel.org>"); @@ -126,7 +119,34 @@ struct smm_regs { unsigned int edi __packed; }; -static inline const char *i8k_get_dmi_data(int field) +static const char * const temp_labels[] = { + "CPU", + "GPU", + "SODIMM", + "Other", + "Ambient", + "Other", +}; + +static const char * const fan_labels[] = { + "Processor Fan", + "Motherboard Fan", + "Video Fan", + "Power Supply Fan", + "Chipset Fan", + "Other Fan", +}; + +static const char * const docking_labels[] = { + "Docking Processor Fan", + "Docking Motherboard Fan", + "Docking Video Fan", + "Docking Power Supply Fan", + "Docking Chipset Fan", + "Docking Other Fan", +}; + +static inline const char __init *i8k_get_dmi_data(int field) { const char *dmi_data = dmi_get_system_info(field); @@ -138,17 +158,12 @@ static inline const char *i8k_get_dmi_data(int field) */ static int i8k_smm_func(void *par) { - int rc; + ktime_t calltime = ktime_get(); struct smm_regs *regs = par; int eax = regs->eax; - -#ifdef DEBUG int ebx = regs->ebx; - unsigned long duration; - ktime_t calltime, delta, rettime; - - calltime = ktime_get(); -#endif + long long duration; + int rc; /* SMM requires CPU 0 */ if (smp_processor_id() != 0) @@ -210,13 +225,9 @@ static int i8k_smm_func(void *par) if (rc != 0 || (regs->eax & 0xffff) == 0xffff || regs->eax == eax) rc = -EINVAL; -#ifdef DEBUG - rettime = ktime_get(); - delta = ktime_sub(rettime, calltime); - duration = ktime_to_ns(delta) >> 10; - pr_debug("smm(0x%.4x 0x%.4x) = 0x%.4x (took %7lu usecs)\n", eax, ebx, - (rc ? 0xffff : regs->eax & 0xffff), duration); -#endif + duration = ktime_us_delta(ktime_get(), calltime); + pr_debug("smm(0x%.4x 0x%.4x) = 0x%.4x (took %7lld usecs)\n", eax, ebx, + (rc ? 0xffff : regs->eax & 0xffff), duration); return rc; } @@ -228,9 +239,9 @@ static int i8k_smm(struct smm_regs *regs) { int ret; - get_online_cpus(); + cpus_read_lock(); ret = smp_call_on_cpu(0, i8k_smm_func, regs, true); - put_online_cpus(); + cpus_read_unlock(); return ret; } @@ -238,11 +249,11 @@ static int i8k_smm(struct smm_regs *regs) /* * Read the fan status. */ -static int i8k_get_fan_status(int fan) +static int i8k_get_fan_status(const struct dell_smm_data *data, int fan) { struct smm_regs regs = { .eax = I8K_SMM_GET_FAN, }; - if (disallow_fan_support) + if (data->disallow_fan_support) return -EINVAL; regs.ebx = fan & 0xff; @@ -252,87 +263,85 @@ static int i8k_get_fan_status(int fan) /* * Read the fan speed in RPM. */ -static int i8k_get_fan_speed(int fan) +static int i8k_get_fan_speed(const struct dell_smm_data *data, int fan) { struct smm_regs regs = { .eax = I8K_SMM_GET_SPEED, }; - if (disallow_fan_support) + if (data->disallow_fan_support) return -EINVAL; regs.ebx = fan & 0xff; - return i8k_smm(®s) ? : (regs.eax & 0xffff) * i8k_fan_mult; + return i8k_smm(®s) ? : (regs.eax & 0xffff) * data->i8k_fan_mult; } /* * Read the fan type. */ -static int _i8k_get_fan_type(int fan) +static int _i8k_get_fan_type(const struct dell_smm_data *data, int fan) { struct smm_regs regs = { .eax = I8K_SMM_GET_FAN_TYPE, }; - if (disallow_fan_support || disallow_fan_type_call) + if (data->disallow_fan_support || data->disallow_fan_type_call) return -EINVAL; regs.ebx = fan & 0xff; return i8k_smm(®s) ? : regs.eax & 0xff; } -static int i8k_get_fan_type(int fan) +static int i8k_get_fan_type(struct dell_smm_data *data, int fan) { /* I8K_SMM_GET_FAN_TYPE SMM call is expensive, so cache values */ - static int types[3] = { INT_MIN, INT_MIN, INT_MIN }; - - if (types[fan] == INT_MIN) - types[fan] = _i8k_get_fan_type(fan); + if (data->fan_type[fan] == INT_MIN) + data->fan_type[fan] = _i8k_get_fan_type(data, fan); - return types[fan]; + return data->fan_type[fan]; } /* * Read the fan nominal rpm for specific fan speed. */ -static int i8k_get_fan_nominal_speed(int fan, int speed) +static int __init i8k_get_fan_nominal_speed(const struct dell_smm_data *data, int fan, int speed) { struct smm_regs regs = { .eax = I8K_SMM_GET_NOM_SPEED, }; - if (disallow_fan_support) + if (data->disallow_fan_support) return -EINVAL; regs.ebx = (fan & 0xff) | (speed << 8); - return i8k_smm(®s) ? : (regs.eax & 0xffff) * i8k_fan_mult; + return i8k_smm(®s) ? : (regs.eax & 0xffff) * data->i8k_fan_mult; } /* * Enable or disable automatic BIOS fan control support */ -static int i8k_enable_fan_auto_mode(bool enable) +static int i8k_enable_fan_auto_mode(const struct dell_smm_data *data, bool enable) { struct smm_regs regs = { }; - if (disallow_fan_support) + if (data->disallow_fan_support) return -EINVAL; - regs.eax = enable ? auto_fan : manual_fan; + regs.eax = enable ? data->auto_fan : data->manual_fan; return i8k_smm(®s); } /* * Set the fan speed (off, low, high). Returns the new fan status. */ -static int i8k_set_fan(int fan, int speed) +static int i8k_set_fan(const struct dell_smm_data *data, int fan, int speed) { struct smm_regs regs = { .eax = I8K_SMM_SET_FAN, }; - if (disallow_fan_support) + if (data->disallow_fan_support) return -EINVAL; - speed = (speed < 0) ? 0 : ((speed > i8k_fan_max) ? i8k_fan_max : speed); + speed = (speed < 0) ? 0 : ((speed > data->i8k_fan_max) ? data->i8k_fan_max : speed); regs.ebx = (fan & 0xff) | (speed << 8); - return i8k_smm(®s) ? : i8k_get_fan_status(fan); + return i8k_smm(®s) ? : i8k_get_fan_status(data, fan); } -static int i8k_get_temp_type(int sensor) +static int __init i8k_get_temp_type(int sensor) { struct smm_regs regs = { .eax = I8K_SMM_GET_TEMP_TYPE, }; @@ -382,7 +391,7 @@ static int i8k_get_temp(int sensor) return temp; } -static int i8k_get_dell_signature(int req_fn) +static int __init i8k_get_dell_signature(int req_fn) { struct smm_regs regs = { .eax = req_fn, }; int rc; @@ -440,7 +449,7 @@ static int i8k_get_power_status(void) */ static int -i8k_ioctl_unlocked(struct file *fp, unsigned int cmd, unsigned long arg) +i8k_ioctl_unlocked(struct file *fp, struct dell_smm_data *data, unsigned int cmd, unsigned long arg) { int val = 0; int speed; @@ -452,12 +461,12 @@ i8k_ioctl_unlocked(struct file *fp, unsigned int cmd, unsigned long arg) switch (cmd) { case I8K_BIOS_VERSION: - if (!isdigit(bios_version[0]) || !isdigit(bios_version[1]) || - !isdigit(bios_version[2])) + if (!isdigit(data->bios_version[0]) || !isdigit(data->bios_version[1]) || + !isdigit(data->bios_version[2])) return -EINVAL; - val = (bios_version[0] << 16) | - (bios_version[1] << 8) | bios_version[2]; + val = (data->bios_version[0] << 16) | + (data->bios_version[1] << 8) | data->bios_version[2]; break; case I8K_MACHINE_ID: @@ -465,7 +474,7 @@ i8k_ioctl_unlocked(struct file *fp, unsigned int cmd, unsigned long arg) return -EPERM; memset(buff, 0, sizeof(buff)); - strlcpy(buff, bios_machineid, sizeof(buff)); + strscpy(buff, data->bios_machineid, sizeof(buff)); break; case I8K_FN_STATUS: @@ -484,14 +493,14 @@ i8k_ioctl_unlocked(struct file *fp, unsigned int cmd, unsigned long arg) if (copy_from_user(&val, argp, sizeof(int))) return -EFAULT; - val = i8k_get_fan_speed(val); + val = i8k_get_fan_speed(data, val); break; case I8K_GET_FAN: if (copy_from_user(&val, argp, sizeof(int))) return -EFAULT; - val = i8k_get_fan_status(val); + val = i8k_get_fan_status(data, val); break; case I8K_SET_FAN: @@ -504,7 +513,7 @@ i8k_ioctl_unlocked(struct file *fp, unsigned int cmd, unsigned long arg) if (copy_from_user(&speed, argp + 1, sizeof(int))) return -EFAULT; - val = i8k_set_fan(val, speed); + val = i8k_set_fan(data, val, speed); break; default: @@ -537,11 +546,12 @@ i8k_ioctl_unlocked(struct file *fp, unsigned int cmd, unsigned long arg) static long i8k_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) { + struct dell_smm_data *data = PDE_DATA(file_inode(fp)); long ret; - mutex_lock(&i8k_mutex); - ret = i8k_ioctl_unlocked(fp, cmd, arg); - mutex_unlock(&i8k_mutex); + mutex_lock(&data->i8k_mutex); + ret = i8k_ioctl_unlocked(fp, data, cmd, arg); + mutex_unlock(&data->i8k_mutex); return ret; } @@ -551,17 +561,18 @@ static long i8k_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) */ static int i8k_proc_show(struct seq_file *seq, void *offset) { + struct dell_smm_data *data = seq->private; int fn_key, cpu_temp, ac_power; int left_fan, right_fan, left_speed, right_speed; - cpu_temp = i8k_get_temp(0); /* 11100 µs */ - left_fan = i8k_get_fan_status(I8K_FAN_LEFT); /* 580 µs */ - right_fan = i8k_get_fan_status(I8K_FAN_RIGHT); /* 580 µs */ - left_speed = i8k_get_fan_speed(I8K_FAN_LEFT); /* 580 µs */ - right_speed = i8k_get_fan_speed(I8K_FAN_RIGHT); /* 580 µs */ - fn_key = i8k_get_fn_status(); /* 750 µs */ + cpu_temp = i8k_get_temp(0); /* 11100 µs */ + left_fan = i8k_get_fan_status(data, I8K_FAN_LEFT); /* 580 µs */ + right_fan = i8k_get_fan_status(data, I8K_FAN_RIGHT); /* 580 µs */ + left_speed = i8k_get_fan_speed(data, I8K_FAN_LEFT); /* 580 µs */ + right_speed = i8k_get_fan_speed(data, I8K_FAN_RIGHT); /* 580 µs */ + fn_key = i8k_get_fn_status(); /* 750 µs */ if (power_status) - ac_power = i8k_get_power_status(); /* 14700 µs */ + ac_power = i8k_get_power_status(); /* 14700 µs */ else ac_power = -1; @@ -581,8 +592,8 @@ static int i8k_proc_show(struct seq_file *seq, void *offset) */ seq_printf(seq, "%s %s %s %d %d %d %d %d %d %d\n", I8K_PROC_FMT, - bios_version, - (restricted && !capable(CAP_SYS_ADMIN)) ? "-1" : bios_machineid, + data->bios_version, + (restricted && !capable(CAP_SYS_ADMIN)) ? "-1" : data->bios_machineid, cpu_temp, left_fan, right_fan, left_speed, right_speed, ac_power, fn_key); @@ -592,7 +603,7 @@ static int i8k_proc_show(struct seq_file *seq, void *offset) static int i8k_open_fs(struct inode *inode, struct file *file) { - return single_open(file, i8k_proc_show, NULL); + return single_open(file, i8k_proc_show, PDE_DATA(inode)); } static const struct proc_ops i8k_proc_ops = { @@ -603,24 +614,24 @@ static const struct proc_ops i8k_proc_ops = { .proc_ioctl = i8k_ioctl, }; -static void __init i8k_init_procfs(void) +static void i8k_exit_procfs(void *param) { - /* Register the proc entry */ - proc_create("i8k", 0, NULL, &i8k_proc_ops); + remove_proc_entry("i8k", NULL); } -static void __exit i8k_exit_procfs(void) +static void __init i8k_init_procfs(struct device *dev) { - remove_proc_entry("i8k", NULL); -} + struct dell_smm_data *data = dev_get_drvdata(dev); -#else + /* Register the proc entry */ + proc_create_data("i8k", 0, NULL, &i8k_proc_ops, data); -static inline void __init i8k_init_procfs(void) -{ + devm_add_action_or_reset(dev, i8k_exit_procfs, NULL); } -static inline void __exit i8k_exit_procfs(void) +#else + +static void __init i8k_init_procfs(struct device *dev) { } @@ -630,341 +641,299 @@ static inline void __exit i8k_exit_procfs(void) * Hwmon interface */ -static ssize_t i8k_hwmon_temp_label_show(struct device *dev, - struct device_attribute *devattr, - char *buf) +static umode_t dell_smm_is_visible(const void *drvdata, enum hwmon_sensor_types type, u32 attr, + int channel) { - static const char * const labels[] = { - "CPU", - "GPU", - "SODIMM", - "Other", - "Ambient", - "Other", - }; - int index = to_sensor_dev_attr(devattr)->index; - int type; + const struct dell_smm_data *data = drvdata; - type = i8k_get_temp_type(index); - if (type < 0) - return type; - if (type >= ARRAY_SIZE(labels)) - type = ARRAY_SIZE(labels) - 1; - return sprintf(buf, "%s\n", labels[type]); + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + case hwmon_temp_label: + if (data->temp_type[channel] >= 0) + return 0444; + + break; + default: + break; + } + break; + case hwmon_fan: + if (data->disallow_fan_support) + break; + + switch (attr) { + case hwmon_fan_input: + if (data->fan[channel]) + return 0444; + + break; + case hwmon_fan_label: + if (data->fan[channel] && !data->disallow_fan_type_call) + return 0444; + + break; + default: + break; + } + break; + case hwmon_pwm: + if (data->disallow_fan_support) + break; + + switch (attr) { + case hwmon_pwm_input: + if (data->fan[channel]) + return 0644; + + break; + case hwmon_pwm_enable: + if (data->auto_fan) + /* + * There is no command for retrieve the current status + * from BIOS, and userspace/firmware itself can change + * it. + * Thus we can only provide write-only access for now. + */ + return 0200; + + break; + default: + break; + } + break; + default: + break; + } + + return 0; } -static ssize_t i8k_hwmon_temp_show(struct device *dev, - struct device_attribute *devattr, - char *buf) +static int dell_smm_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, + long *val) { - int index = to_sensor_dev_attr(devattr)->index; - int temp; + struct dell_smm_data *data = dev_get_drvdata(dev); + int ret; + + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + ret = i8k_get_temp(channel); + if (ret < 0) + return ret; + + *val = ret * 1000; + + return 0; + default: + break; + } + break; + case hwmon_fan: + switch (attr) { + case hwmon_fan_input: + ret = i8k_get_fan_speed(data, channel); + if (ret < 0) + return ret; - temp = i8k_get_temp(index); - if (temp < 0) - return temp; - return sprintf(buf, "%d\n", temp * 1000); + *val = ret; + + return 0; + default: + break; + } + break; + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_input: + ret = i8k_get_fan_status(data, channel); + if (ret < 0) + return ret; + + *val = clamp_val(ret * data->i8k_pwm_mult, 0, 255); + + return 0; + default: + break; + } + break; + default: + break; + } + + return -EOPNOTSUPP; } -static ssize_t i8k_hwmon_fan_label_show(struct device *dev, - struct device_attribute *devattr, - char *buf) +static const char *dell_smm_fan_label(struct dell_smm_data *data, int channel) { - static const char * const labels[] = { - "Processor Fan", - "Motherboard Fan", - "Video Fan", - "Power Supply Fan", - "Chipset Fan", - "Other Fan", - }; - int index = to_sensor_dev_attr(devattr)->index; bool dock = false; - int type; + int type = i8k_get_fan_type(data, channel); - type = i8k_get_fan_type(index); if (type < 0) - return type; + return ERR_PTR(type); if (type & 0x10) { dock = true; type &= 0x0F; } - if (type >= ARRAY_SIZE(labels)) - type = (ARRAY_SIZE(labels) - 1); - - return sprintf(buf, "%s%s\n", (dock ? "Docking " : ""), labels[type]); -} - -static ssize_t i8k_hwmon_fan_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - int index = to_sensor_dev_attr(devattr)->index; - int fan_speed; + if (type >= ARRAY_SIZE(fan_labels)) + type = ARRAY_SIZE(fan_labels) - 1; - fan_speed = i8k_get_fan_speed(index); - if (fan_speed < 0) - return fan_speed; - return sprintf(buf, "%d\n", fan_speed); + return dock ? docking_labels[type] : fan_labels[type]; } -static ssize_t i8k_hwmon_pwm_show(struct device *dev, - struct device_attribute *devattr, char *buf) +static int dell_smm_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, const char **str) { - int index = to_sensor_dev_attr(devattr)->index; - int status; + struct dell_smm_data *data = dev_get_drvdata(dev); + + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_label: + *str = temp_labels[data->temp_type[channel]]; + return 0; + default: + break; + } + break; + case hwmon_fan: + switch (attr) { + case hwmon_fan_label: + *str = dell_smm_fan_label(data, channel); + return PTR_ERR_OR_ZERO(*str); + default: + break; + } + break; + default: + break; + } - status = i8k_get_fan_status(index); - if (status < 0) - return -EIO; - return sprintf(buf, "%d\n", clamp_val(status * i8k_pwm_mult, 0, 255)); + return -EOPNOTSUPP; } -static ssize_t i8k_hwmon_pwm_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static int dell_smm_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, + long val) { - int index = to_sensor_dev_attr(attr)->index; - unsigned long val; + struct dell_smm_data *data = dev_get_drvdata(dev); + unsigned long pwm; + bool enable; int err; - err = kstrtoul(buf, 10, &val); - if (err) - return err; - val = clamp_val(DIV_ROUND_CLOSEST(val, i8k_pwm_mult), 0, i8k_fan_max); + switch (type) { + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_input: + pwm = clamp_val(DIV_ROUND_CLOSEST(val, data->i8k_pwm_mult), 0, + data->i8k_fan_max); - mutex_lock(&i8k_mutex); - err = i8k_set_fan(index, val); - mutex_unlock(&i8k_mutex); + mutex_lock(&data->i8k_mutex); + err = i8k_set_fan(data, channel, pwm); + mutex_unlock(&data->i8k_mutex); - return err < 0 ? -EIO : count; -} + if (err < 0) + return err; -static ssize_t i8k_hwmon_pwm_enable_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - int err; - bool enable; - unsigned long val; + return 0; + case hwmon_pwm_enable: + if (!val) + return -EINVAL; - if (!auto_fan) - return -ENODEV; + if (val == 1) + enable = false; + else + enable = true; - err = kstrtoul(buf, 10, &val); - if (err) - return err; + mutex_lock(&data->i8k_mutex); + err = i8k_enable_fan_auto_mode(data, enable); + mutex_unlock(&data->i8k_mutex); - if (val == 1) - enable = false; - else if (val == 2) - enable = true; - else - return -EINVAL; + if (err < 0) + return err; - mutex_lock(&i8k_mutex); - err = i8k_enable_fan_auto_mode(enable); - mutex_unlock(&i8k_mutex); + return 0; + default: + break; + } + break; + default: + break; + } - return err ? err : count; + return -EOPNOTSUPP; } -static SENSOR_DEVICE_ATTR_RO(temp1_input, i8k_hwmon_temp, 0); -static SENSOR_DEVICE_ATTR_RO(temp1_label, i8k_hwmon_temp_label, 0); -static SENSOR_DEVICE_ATTR_RO(temp2_input, i8k_hwmon_temp, 1); -static SENSOR_DEVICE_ATTR_RO(temp2_label, i8k_hwmon_temp_label, 1); -static SENSOR_DEVICE_ATTR_RO(temp3_input, i8k_hwmon_temp, 2); -static SENSOR_DEVICE_ATTR_RO(temp3_label, i8k_hwmon_temp_label, 2); -static SENSOR_DEVICE_ATTR_RO(temp4_input, i8k_hwmon_temp, 3); -static SENSOR_DEVICE_ATTR_RO(temp4_label, i8k_hwmon_temp_label, 3); -static SENSOR_DEVICE_ATTR_RO(temp5_input, i8k_hwmon_temp, 4); -static SENSOR_DEVICE_ATTR_RO(temp5_label, i8k_hwmon_temp_label, 4); -static SENSOR_DEVICE_ATTR_RO(temp6_input, i8k_hwmon_temp, 5); -static SENSOR_DEVICE_ATTR_RO(temp6_label, i8k_hwmon_temp_label, 5); -static SENSOR_DEVICE_ATTR_RO(temp7_input, i8k_hwmon_temp, 6); -static SENSOR_DEVICE_ATTR_RO(temp7_label, i8k_hwmon_temp_label, 6); -static SENSOR_DEVICE_ATTR_RO(temp8_input, i8k_hwmon_temp, 7); -static SENSOR_DEVICE_ATTR_RO(temp8_label, i8k_hwmon_temp_label, 7); -static SENSOR_DEVICE_ATTR_RO(temp9_input, i8k_hwmon_temp, 8); -static SENSOR_DEVICE_ATTR_RO(temp9_label, i8k_hwmon_temp_label, 8); -static SENSOR_DEVICE_ATTR_RO(temp10_input, i8k_hwmon_temp, 9); -static SENSOR_DEVICE_ATTR_RO(temp10_label, i8k_hwmon_temp_label, 9); -static SENSOR_DEVICE_ATTR_RO(fan1_input, i8k_hwmon_fan, 0); -static SENSOR_DEVICE_ATTR_RO(fan1_label, i8k_hwmon_fan_label, 0); -static SENSOR_DEVICE_ATTR_RW(pwm1, i8k_hwmon_pwm, 0); -static SENSOR_DEVICE_ATTR_WO(pwm1_enable, i8k_hwmon_pwm_enable, 0); -static SENSOR_DEVICE_ATTR_RO(fan2_input, i8k_hwmon_fan, 1); -static SENSOR_DEVICE_ATTR_RO(fan2_label, i8k_hwmon_fan_label, 1); -static SENSOR_DEVICE_ATTR_RW(pwm2, i8k_hwmon_pwm, 1); -static SENSOR_DEVICE_ATTR_RO(fan3_input, i8k_hwmon_fan, 2); -static SENSOR_DEVICE_ATTR_RO(fan3_label, i8k_hwmon_fan_label, 2); -static SENSOR_DEVICE_ATTR_RW(pwm3, i8k_hwmon_pwm, 2); - -static struct attribute *i8k_attrs[] = { - &sensor_dev_attr_temp1_input.dev_attr.attr, /* 0 */ - &sensor_dev_attr_temp1_label.dev_attr.attr, /* 1 */ - &sensor_dev_attr_temp2_input.dev_attr.attr, /* 2 */ - &sensor_dev_attr_temp2_label.dev_attr.attr, /* 3 */ - &sensor_dev_attr_temp3_input.dev_attr.attr, /* 4 */ - &sensor_dev_attr_temp3_label.dev_attr.attr, /* 5 */ - &sensor_dev_attr_temp4_input.dev_attr.attr, /* 6 */ - &sensor_dev_attr_temp4_label.dev_attr.attr, /* 7 */ - &sensor_dev_attr_temp5_input.dev_attr.attr, /* 8 */ - &sensor_dev_attr_temp5_label.dev_attr.attr, /* 9 */ - &sensor_dev_attr_temp6_input.dev_attr.attr, /* 10 */ - &sensor_dev_attr_temp6_label.dev_attr.attr, /* 11 */ - &sensor_dev_attr_temp7_input.dev_attr.attr, /* 12 */ - &sensor_dev_attr_temp7_label.dev_attr.attr, /* 13 */ - &sensor_dev_attr_temp8_input.dev_attr.attr, /* 14 */ - &sensor_dev_attr_temp8_label.dev_attr.attr, /* 15 */ - &sensor_dev_attr_temp9_input.dev_attr.attr, /* 16 */ - &sensor_dev_attr_temp9_label.dev_attr.attr, /* 17 */ - &sensor_dev_attr_temp10_input.dev_attr.attr, /* 18 */ - &sensor_dev_attr_temp10_label.dev_attr.attr, /* 19 */ - &sensor_dev_attr_fan1_input.dev_attr.attr, /* 20 */ - &sensor_dev_attr_fan1_label.dev_attr.attr, /* 21 */ - &sensor_dev_attr_pwm1.dev_attr.attr, /* 22 */ - &sensor_dev_attr_pwm1_enable.dev_attr.attr, /* 23 */ - &sensor_dev_attr_fan2_input.dev_attr.attr, /* 24 */ - &sensor_dev_attr_fan2_label.dev_attr.attr, /* 25 */ - &sensor_dev_attr_pwm2.dev_attr.attr, /* 26 */ - &sensor_dev_attr_fan3_input.dev_attr.attr, /* 27 */ - &sensor_dev_attr_fan3_label.dev_attr.attr, /* 28 */ - &sensor_dev_attr_pwm3.dev_attr.attr, /* 29 */ +static const struct hwmon_ops dell_smm_ops = { + .is_visible = dell_smm_is_visible, + .read = dell_smm_read, + .read_string = dell_smm_read_string, + .write = dell_smm_write, +}; + +static const struct hwmon_channel_info *dell_smm_info[] = { + HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ), + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL + ), + HWMON_CHANNEL_INFO(fan, + HWMON_F_INPUT | HWMON_F_LABEL, + HWMON_F_INPUT | HWMON_F_LABEL, + HWMON_F_INPUT | HWMON_F_LABEL + ), + HWMON_CHANNEL_INFO(pwm, + HWMON_PWM_INPUT | HWMON_PWM_ENABLE, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT + ), NULL }; -static umode_t i8k_is_visible(struct kobject *kobj, struct attribute *attr, - int index) -{ - if (disallow_fan_support && index >= 20) - return 0; - if (disallow_fan_type_call && - (index == 21 || index == 25 || index == 28)) - return 0; - if (index >= 0 && index <= 1 && - !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP1)) - return 0; - if (index >= 2 && index <= 3 && - !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP2)) - return 0; - if (index >= 4 && index <= 5 && - !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP3)) - return 0; - if (index >= 6 && index <= 7 && - !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP4)) - return 0; - if (index >= 8 && index <= 9 && - !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP5)) - return 0; - if (index >= 10 && index <= 11 && - !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP6)) - return 0; - if (index >= 12 && index <= 13 && - !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP7)) - return 0; - if (index >= 14 && index <= 15 && - !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP8)) - return 0; - if (index >= 16 && index <= 17 && - !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP9)) - return 0; - if (index >= 18 && index <= 19 && - !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP10)) - return 0; +static const struct hwmon_chip_info dell_smm_chip_info = { + .ops = &dell_smm_ops, + .info = dell_smm_info, +}; - if (index >= 20 && index <= 23 && - !(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN1)) - return 0; - if (index >= 24 && index <= 26 && - !(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN2)) - return 0; - if (index >= 27 && index <= 29 && - !(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN3)) - return 0; +static int __init dell_smm_init_hwmon(struct device *dev) +{ + struct dell_smm_data *data = dev_get_drvdata(dev); + struct device *dell_smm_hwmon_dev; + int i, err; - if (index == 23 && !auto_fan) - return 0; + for (i = 0; i < DELL_SMM_NO_TEMP; i++) { + data->temp_type[i] = i8k_get_temp_type(i); + if (data->temp_type[i] < 0) + continue; - return attr->mode; -} + if (data->temp_type[i] >= ARRAY_SIZE(temp_labels)) + data->temp_type[i] = ARRAY_SIZE(temp_labels) - 1; + } -static const struct attribute_group i8k_group = { - .attrs = i8k_attrs, - .is_visible = i8k_is_visible, -}; -__ATTRIBUTE_GROUPS(i8k); + for (i = 0; i < DELL_SMM_NO_FANS; i++) { + data->fan_type[i] = INT_MIN; + err = i8k_get_fan_status(data, i); + if (err < 0) + err = i8k_get_fan_type(data, i); + if (err >= 0) + data->fan[i] = true; + } -static int __init i8k_init_hwmon(void) -{ - int err; + dell_smm_hwmon_dev = devm_hwmon_device_register_with_info(dev, "dell_smm", data, + &dell_smm_chip_info, NULL); - i8k_hwmon_flags = 0; - - /* CPU temperature attributes, if temperature type is OK */ - err = i8k_get_temp_type(0); - if (err >= 0) - i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP1; - /* check for additional temperature sensors */ - err = i8k_get_temp_type(1); - if (err >= 0) - i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP2; - err = i8k_get_temp_type(2); - if (err >= 0) - i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP3; - err = i8k_get_temp_type(3); - if (err >= 0) - i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP4; - err = i8k_get_temp_type(4); - if (err >= 0) - i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP5; - err = i8k_get_temp_type(5); - if (err >= 0) - i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP6; - err = i8k_get_temp_type(6); - if (err >= 0) - i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP7; - err = i8k_get_temp_type(7); - if (err >= 0) - i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP8; - err = i8k_get_temp_type(8); - if (err >= 0) - i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP9; - err = i8k_get_temp_type(9); - if (err >= 0) - i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP10; - - /* First fan attributes, if fan status or type is OK */ - err = i8k_get_fan_status(0); - if (err < 0) - err = i8k_get_fan_type(0); - if (err >= 0) - i8k_hwmon_flags |= I8K_HWMON_HAVE_FAN1; - - /* Second fan attributes, if fan status or type is OK */ - err = i8k_get_fan_status(1); - if (err < 0) - err = i8k_get_fan_type(1); - if (err >= 0) - i8k_hwmon_flags |= I8K_HWMON_HAVE_FAN2; - - /* Third fan attributes, if fan status or type is OK */ - err = i8k_get_fan_status(2); - if (err < 0) - err = i8k_get_fan_type(2); - if (err >= 0) - i8k_hwmon_flags |= I8K_HWMON_HAVE_FAN3; - - i8k_hwmon_dev = hwmon_device_register_with_groups(NULL, "dell_smm", - NULL, i8k_groups); - if (IS_ERR(i8k_hwmon_dev)) { - err = PTR_ERR(i8k_hwmon_dev); - i8k_hwmon_dev = NULL; - pr_err("hwmon registration failed (%d)\n", err); - return err; - } - return 0; + return PTR_ERR_OR_ZERO(dell_smm_hwmon_dev); } struct i8k_config_data { @@ -979,7 +948,7 @@ enum i8k_configs { DELL_XPS, }; -static const struct i8k_config_data i8k_config_data[] = { +static const struct i8k_config_data i8k_config_data[] __initconst = { [DELL_LATITUDE_D520] = { .fan_mult = 1, .fan_max = I8K_FAN_TURBO, @@ -1137,7 +1106,7 @@ static const struct dmi_system_id i8k_blacklist_fan_type_dmi_table[] __initconst * support for affected blacklisted Dell machines stay disabled. * See bug: https://bugzilla.kernel.org/show_bug.cgi?id=195751 */ -static struct dmi_system_id i8k_blacklist_fan_support_dmi_table[] __initdata = { +static const struct dmi_system_id i8k_blacklist_fan_support_dmi_table[] __initconst = { { .ident = "Dell Inspiron 7720", .matches = { @@ -1178,22 +1147,14 @@ enum i8k_fan_controls { I8K_FAN_34A3_35A3, }; -static const struct i8k_fan_control_data i8k_fan_control_data[] = { +static const struct i8k_fan_control_data i8k_fan_control_data[] __initconst = { [I8K_FAN_34A3_35A3] = { .manual_fan = 0x34a3, .auto_fan = 0x35a3, }, }; -static struct dmi_system_id i8k_whitelist_fan_control[] __initdata = { - { - .ident = "Dell Precision 5530", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Precision 5530"), - }, - .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3], - }, +static const struct dmi_system_id i8k_whitelist_fan_control[] __initconst = { { .ident = "Dell Latitude 5480", .matches = { @@ -1218,57 +1179,56 @@ static struct dmi_system_id i8k_whitelist_fan_control[] __initdata = { }, .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3], }, + { + .ident = "Dell Precision 5530", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Precision 5530"), + }, + .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3], + }, + { + .ident = "Dell Precision 7510", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Precision 7510"), + }, + .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3], + }, { } }; -/* - * Probe for the presence of a supported laptop. - */ -static int __init i8k_probe(void) +static int __init dell_smm_probe(struct platform_device *pdev) { + struct dell_smm_data *data; const struct dmi_system_id *id, *fan_control; int fan, ret; - /* - * Get DMI information - */ - if (!dmi_check_system(i8k_dmi_table)) { - if (!ignore_dmi && !force) - return -ENODEV; + data = devm_kzalloc(&pdev->dev, sizeof(struct dell_smm_data), GFP_KERNEL); + if (!data) + return -ENOMEM; - pr_info("not running on a supported Dell system.\n"); - pr_info("vendor=%s, model=%s, version=%s\n", - i8k_get_dmi_data(DMI_SYS_VENDOR), - i8k_get_dmi_data(DMI_PRODUCT_NAME), - i8k_get_dmi_data(DMI_BIOS_VERSION)); - } + mutex_init(&data->i8k_mutex); + data->i8k_fan_mult = I8K_FAN_MULT; + data->i8k_fan_max = I8K_FAN_HIGH; + platform_set_drvdata(pdev, data); if (dmi_check_system(i8k_blacklist_fan_support_dmi_table)) { - pr_warn("broken Dell BIOS detected, disallow fan support\n"); + dev_warn(&pdev->dev, "broken Dell BIOS detected, disallow fan support\n"); if (!force) - disallow_fan_support = true; + data->disallow_fan_support = true; } if (dmi_check_system(i8k_blacklist_fan_type_dmi_table)) { - pr_warn("broken Dell BIOS detected, disallow fan type call\n"); + dev_warn(&pdev->dev, "broken Dell BIOS detected, disallow fan type call\n"); if (!force) - disallow_fan_type_call = true; + data->disallow_fan_type_call = true; } - strlcpy(bios_version, i8k_get_dmi_data(DMI_BIOS_VERSION), - sizeof(bios_version)); - strlcpy(bios_machineid, i8k_get_dmi_data(DMI_PRODUCT_SERIAL), - sizeof(bios_machineid)); - - /* - * Get SMM Dell signature - */ - if (i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG1) && - i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG2)) { - pr_err("unable to get SMM Dell signature\n"); - if (!force) - return -ENODEV; - } + strscpy(data->bios_version, i8k_get_dmi_data(DMI_BIOS_VERSION), + sizeof(data->bios_version)); + strscpy(data->bios_machineid, i8k_get_dmi_data(DMI_PRODUCT_SERIAL), + sizeof(data->bios_machineid)); /* * Set fan multiplier and maximal fan speed from dmi config @@ -1277,22 +1237,24 @@ static int __init i8k_probe(void) id = dmi_first_match(i8k_dmi_table); if (id && id->driver_data) { const struct i8k_config_data *conf = id->driver_data; + if (!fan_mult && conf->fan_mult) fan_mult = conf->fan_mult; + if (!fan_max && conf->fan_max) fan_max = conf->fan_max; } - i8k_fan_max = fan_max ? : I8K_FAN_HIGH; /* Must not be 0 */ - i8k_pwm_mult = DIV_ROUND_UP(255, i8k_fan_max); + data->i8k_fan_max = fan_max ? : I8K_FAN_HIGH; /* Must not be 0 */ + data->i8k_pwm_mult = DIV_ROUND_UP(255, data->i8k_fan_max); fan_control = dmi_first_match(i8k_whitelist_fan_control); if (fan_control && fan_control->driver_data) { - const struct i8k_fan_control_data *data = fan_control->driver_data; + const struct i8k_fan_control_data *control = fan_control->driver_data; - manual_fan = data->manual_fan; - auto_fan = data->auto_fan; - pr_info("enabling support for setting automatic/manual fan control\n"); + data->manual_fan = control->manual_fan; + data->auto_fan = control->auto_fan; + dev_info(&pdev->dev, "enabling support for setting automatic/manual fan control\n"); } if (!fan_mult) { @@ -1300,42 +1262,76 @@ static int __init i8k_probe(void) * Autodetect fan multiplier based on nominal rpm * If fan reports rpm value too high then set multiplier to 1 */ - for (fan = 0; fan < 2; ++fan) { - ret = i8k_get_fan_nominal_speed(fan, i8k_fan_max); + for (fan = 0; fan < DELL_SMM_NO_FANS; ++fan) { + ret = i8k_get_fan_nominal_speed(data, fan, data->i8k_fan_max); if (ret < 0) continue; + if (ret > I8K_FAN_MAX_RPM) - i8k_fan_mult = 1; + data->i8k_fan_mult = 1; break; } } else { /* Fan multiplier was specified in module param or in dmi */ - i8k_fan_mult = fan_mult; + data->i8k_fan_mult = fan_mult; } + ret = dell_smm_init_hwmon(&pdev->dev); + if (ret) + return ret; + + i8k_init_procfs(&pdev->dev); + return 0; } +static struct platform_driver dell_smm_driver = { + .driver = { + .name = KBUILD_MODNAME, + }, +}; + +static struct platform_device *dell_smm_device; + +/* + * Probe for the presence of a supported laptop. + */ static int __init i8k_init(void) { - int err; + /* + * Get DMI information + */ + if (!dmi_check_system(i8k_dmi_table)) { + if (!ignore_dmi && !force) + return -ENODEV; - /* Are we running on an supported laptop? */ - if (i8k_probe()) - return -ENODEV; + pr_info("not running on a supported Dell system.\n"); + pr_info("vendor=%s, model=%s, version=%s\n", + i8k_get_dmi_data(DMI_SYS_VENDOR), + i8k_get_dmi_data(DMI_PRODUCT_NAME), + i8k_get_dmi_data(DMI_BIOS_VERSION)); + } - err = i8k_init_hwmon(); - if (err) - return err; + /* + * Get SMM Dell signature + */ + if (i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG1) && + i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG2)) { + pr_err("unable to get SMM Dell signature\n"); + if (!force) + return -ENODEV; + } - i8k_init_procfs(); - return 0; + dell_smm_device = platform_create_bundle(&dell_smm_driver, dell_smm_probe, NULL, 0, NULL, + 0); + + return PTR_ERR_OR_ZERO(dell_smm_device); } static void __exit i8k_exit(void) { - hwmon_device_unregister(i8k_hwmon_dev); - i8k_exit_procfs(); + platform_device_unregister(dell_smm_device); + platform_driver_unregister(&dell_smm_driver); } module_init(i8k_init); diff --git a/drivers/hwmon/fam15h_power.c b/drivers/hwmon/fam15h_power.c index 29f5fed28c2a..521534d5c1e5 100644 --- a/drivers/hwmon/fam15h_power.c +++ b/drivers/hwmon/fam15h_power.c @@ -166,7 +166,7 @@ static int read_registers(struct fam15h_power_data *data) memset(data->cu_on, 0, sizeof(int) * MAX_CUS); - get_online_cpus(); + cpus_read_lock(); /* * Choose the first online core of each compute unit, and then @@ -190,7 +190,7 @@ static int read_registers(struct fam15h_power_data *data) on_each_cpu_mask(mask, do_read_registers_on_cu, data, true); - put_online_cpus(); + cpus_read_unlock(); free_cpumask_var(mask); return 0; diff --git a/drivers/hwmon/intel-m10-bmc-hwmon.c b/drivers/hwmon/intel-m10-bmc-hwmon.c index bd7ed2ed3a1e..7a08e4c44a4b 100644 --- a/drivers/hwmon/intel-m10-bmc-hwmon.c +++ b/drivers/hwmon/intel-m10-bmc-hwmon.c @@ -228,6 +228,118 @@ static const struct m10bmc_hwmon_board_data d5005bmc_hwmon_bdata = { .hinfo = d5005bmc_hinfo, }; +static const struct m10bmc_sdata n5010bmc_temp_tbl[] = { + { 0x100, 0x0, 0x104, 0x0, 0x0, 1000, "Board Local Temperature" }, + { 0x108, 0x0, 0x10c, 0x0, 0x0, 1000, "FPGA 1 Temperature" }, + { 0x110, 0x0, 0x114, 0x0, 0x0, 1000, "FPGA 2 Temperature" }, + { 0x118, 0x0, 0x0, 0x0, 0x0, 1000, "Card Top Temperature" }, + { 0x11c, 0x0, 0x0, 0x0, 0x0, 1000, "Card Bottom Temperature" }, + { 0x128, 0x0, 0x0, 0x0, 0x0, 1000, "FPGA 1.2V Temperature" }, + { 0x134, 0x0, 0x0, 0x0, 0x0, 1000, "FPGA 5V Temperature" }, + { 0x140, 0x0, 0x0, 0x0, 0x0, 1000, "FPGA 0.9V Temperature" }, + { 0x14c, 0x0, 0x0, 0x0, 0x0, 1000, "FPGA 0.85V Temperature" }, + { 0x158, 0x0, 0x0, 0x0, 0x0, 1000, "AUX 12V Temperature" }, + { 0x164, 0x0, 0x0, 0x0, 0x0, 1000, "Backplane 12V Temperature" }, + { 0x1a8, 0x0, 0x0, 0x0, 0x0, 1000, "QSFP28-1 Temperature" }, + { 0x1ac, 0x0, 0x0, 0x0, 0x0, 1000, "QSFP28-2 Temperature" }, + { 0x1b0, 0x0, 0x0, 0x0, 0x0, 1000, "QSFP28-3 Temperature" }, + { 0x1b4, 0x0, 0x0, 0x0, 0x0, 1000, "QSFP28-4 Temperature" }, + { 0x1b8, 0x0, 0x0, 0x0, 0x0, 1000, "CVL1 Internal Temperature" }, + { 0x1bc, 0x0, 0x0, 0x0, 0x0, 1000, "CVL2 Internal Temperature" }, +}; + +static const struct m10bmc_sdata n5010bmc_in_tbl[] = { + { 0x120, 0x0, 0x0, 0x0, 0x0, 1, "FPGA 1.2V Voltage" }, + { 0x12c, 0x0, 0x0, 0x0, 0x0, 1, "FPGA 5V Voltage" }, + { 0x138, 0x0, 0x0, 0x0, 0x0, 1, "FPGA 0.9V Voltage" }, + { 0x144, 0x0, 0x0, 0x0, 0x0, 1, "FPGA 0.85V Voltage" }, + { 0x150, 0x0, 0x0, 0x0, 0x0, 1, "AUX 12V Voltage" }, + { 0x15c, 0x0, 0x0, 0x0, 0x0, 1, "Backplane 12V Voltage" }, + { 0x16c, 0x0, 0x0, 0x0, 0x0, 1, "DDR4 1.2V Voltage" }, + { 0x17c, 0x0, 0x0, 0x0, 0x0, 1, "FPGA 1.8V Voltage" }, + { 0x184, 0x0, 0x0, 0x0, 0x0, 1, "QDR 1.3V Voltage" }, + { 0x18c, 0x0, 0x0, 0x0, 0x0, 1, "CVL1 0.8V Voltage" }, + { 0x194, 0x0, 0x0, 0x0, 0x0, 1, "CVL1 1.05V Voltage" }, + { 0x19c, 0x0, 0x0, 0x0, 0x0, 1, "CVL2 1.05V Voltage" }, + { 0x1a4, 0x0, 0x0, 0x0, 0x0, 1, "CVL2 0.8V Voltage" }, +}; + +static const struct m10bmc_sdata n5010bmc_curr_tbl[] = { + { 0x124, 0x0, 0x0, 0x0, 0x0, 1, "FPGA 1.2V Current" }, + { 0x130, 0x0, 0x0, 0x0, 0x0, 1, "FPGA 5V Current" }, + { 0x13c, 0x0, 0x0, 0x0, 0x0, 1, "FPGA 0.9V Current" }, + { 0x148, 0x0, 0x0, 0x0, 0x0, 1, "FPGA 0.85V Current" }, + { 0x154, 0x0, 0x0, 0x0, 0x0, 1, "AUX 12V Current" }, + { 0x160, 0x0, 0x0, 0x0, 0x0, 1, "Backplane 12V Current" }, + { 0x168, 0x0, 0x0, 0x0, 0x0, 1, "DDR4 1.2V Current" }, + { 0x178, 0x0, 0x0, 0x0, 0x0, 1, "FPGA 1.8V Current" }, + { 0x180, 0x0, 0x0, 0x0, 0x0, 1, "QDR 1.3V Current" }, + { 0x188, 0x0, 0x0, 0x0, 0x0, 1, "CVL1 0.8V Current" }, + { 0x190, 0x0, 0x0, 0x0, 0x0, 1, "CVL1 1.05V Current" }, + { 0x198, 0x0, 0x0, 0x0, 0x0, 1, "CVL2 1.05V Current" }, + { 0x1a0, 0x0, 0x0, 0x0, 0x0, 1, "CVL2 0.8V Current" }, +}; + +static const struct hwmon_channel_info *n5010bmc_hinfo[] = { + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_CRIT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL), + HWMON_CHANNEL_INFO(in, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL), + HWMON_CHANNEL_INFO(curr, + HWMON_C_INPUT | HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_LABEL), + NULL +}; + +static const struct m10bmc_hwmon_board_data n5010bmc_hwmon_bdata = { + .tables = { + [hwmon_temp] = n5010bmc_temp_tbl, + [hwmon_in] = n5010bmc_in_tbl, + [hwmon_curr] = n5010bmc_curr_tbl, + }, + + .hinfo = n5010bmc_hinfo, +}; + static umode_t m10bmc_hwmon_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr, int channel) @@ -438,6 +550,10 @@ static const struct platform_device_id intel_m10bmc_hwmon_ids[] = { .name = "d5005bmc-hwmon", .driver_data = (unsigned long)&d5005bmc_hwmon_bdata, }, + { + .name = "n5010bmc-hwmon", + .driver_data = (unsigned long)&n5010bmc_hwmon_bdata, + }, { } }; diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c index 5ff3669c2b60..38bc35ac8135 100644 --- a/drivers/hwmon/k10temp.c +++ b/drivers/hwmon/k10temp.c @@ -65,10 +65,11 @@ static DEFINE_MUTEX(nb_smu_ind_mutex); #define F15H_M60H_HARDWARE_TEMP_CTRL_OFFSET 0xd8200c64 #define F15H_M60H_REPORTED_TEMP_CTRL_OFFSET 0xd8200ca4 -/* Common for Zen CPU families (Family 17h and 18h) */ -#define ZEN_REPORTED_TEMP_CTRL_OFFSET 0x00059800 +/* Common for Zen CPU families (Family 17h and 18h and 19h) */ +#define ZEN_REPORTED_TEMP_CTRL_BASE 0x00059800 -#define ZEN_CCD_TEMP(x) (0x00059954 + ((x) * 4)) +#define ZEN_CCD_TEMP(offset, x) (ZEN_REPORTED_TEMP_CTRL_BASE + \ + (offset) + ((x) * 4)) #define ZEN_CCD_TEMP_VALID BIT(11) #define ZEN_CCD_TEMP_MASK GENMASK(10, 0) @@ -103,6 +104,7 @@ struct k10temp_data { u32 temp_adjust_mask; u32 show_temp; bool is_zen; + u32 ccd_offset; }; #define TCTL_BIT 0 @@ -163,7 +165,7 @@ static void read_tempreg_nb_f15(struct pci_dev *pdev, u32 *regval) static void read_tempreg_nb_zen(struct pci_dev *pdev, u32 *regval) { amd_smn_read(amd_pci_dev_to_node_id(pdev), - ZEN_REPORTED_TEMP_CTRL_OFFSET, regval); + ZEN_REPORTED_TEMP_CTRL_BASE, regval); } static long get_raw_temp(struct k10temp_data *data) @@ -226,7 +228,8 @@ static int k10temp_read_temp(struct device *dev, u32 attr, int channel, break; case 2 ... 9: /* Tccd{1-8} */ amd_smn_read(amd_pci_dev_to_node_id(data->pdev), - ZEN_CCD_TEMP(channel - 2), ®val); + ZEN_CCD_TEMP(data->ccd_offset, channel - 2), + ®val); *val = (regval & ZEN_CCD_TEMP_MASK) * 125 - 49000; break; default: @@ -387,7 +390,7 @@ static void k10temp_get_ccd_support(struct pci_dev *pdev, for (i = 0; i < limit; i++) { amd_smn_read(amd_pci_dev_to_node_id(pdev), - ZEN_CCD_TEMP(i), ®val); + ZEN_CCD_TEMP(data->ccd_offset, i), ®val); if (regval & ZEN_CCD_TEMP_VALID) data->show_temp |= BIT(TCCD_BIT(i)); } @@ -426,7 +429,6 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id) } else if (boot_cpu_data.x86 == 0x17 || boot_cpu_data.x86 == 0x18) { data->temp_adjust_mask = ZEN_CUR_TEMP_RANGE_SEL_MASK; data->read_tempreg = read_tempreg_nb_zen; - data->show_temp |= BIT(TDIE_BIT); /* show Tdie */ data->is_zen = true; switch (boot_cpu_data.x86_model) { @@ -434,22 +436,31 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id) case 0x8: /* Zen+ */ case 0x11: /* Zen APU */ case 0x18: /* Zen+ APU */ + data->ccd_offset = 0x154; k10temp_get_ccd_support(pdev, data, 4); break; case 0x31: /* Zen2 Threadripper */ + case 0x60: /* Renoir */ + case 0x68: /* Lucienne */ case 0x71: /* Zen2 */ + data->ccd_offset = 0x154; k10temp_get_ccd_support(pdev, data, 8); break; } } else if (boot_cpu_data.x86 == 0x19) { data->temp_adjust_mask = ZEN_CUR_TEMP_RANGE_SEL_MASK; data->read_tempreg = read_tempreg_nb_zen; - data->show_temp |= BIT(TDIE_BIT); data->is_zen = true; switch (boot_cpu_data.x86_model) { case 0x0 ... 0x1: /* Zen3 SP3/TR */ case 0x21: /* Zen3 Ryzen Desktop */ + case 0x50 ... 0x5f: /* Green Sardine */ + data->ccd_offset = 0x154; + k10temp_get_ccd_support(pdev, data, 8); + break; + case 0x40 ... 0x4f: /* Yellow Carp */ + data->ccd_offset = 0x300; k10temp_get_ccd_support(pdev, data, 8); break; } @@ -463,6 +474,7 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (boot_cpu_data.x86 == entry->model && strstr(boot_cpu_data.x86_model_id, entry->id)) { + data->show_temp |= BIT(TDIE_BIT); /* show Tdie */ data->temp_offset = entry->offset; break; } @@ -491,6 +503,8 @@ static const struct pci_device_id k10temp_id_table[] = { { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M60H_DF_F3) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M70H_DF_F3) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_19H_DF_F3) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_19H_M40H_DF_F3) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_19H_M50H_DF_F3) }, { PCI_VDEVICE(HYGON, PCI_DEVICE_ID_AMD_17H_DF_F3) }, {} }; diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c index 18fd6f12ca16..cf26c44f2b88 100644 --- a/drivers/hwmon/ntc_thermistor.c +++ b/drivers/hwmon/ntc_thermistor.c @@ -13,6 +13,7 @@ #include <linux/err.h> #include <linux/of.h> #include <linux/of_device.h> +#include <linux/fixp-arith.h> #include <linux/platform_data/ntc_thermistor.h> @@ -549,15 +550,16 @@ static int get_temp_mc(struct ntc_data *data, unsigned int ohm) int temp; lookup_comp(data, ohm, &low, &high); - if (low == high) { - /* Unable to use linear approximation */ - temp = data->comp[low].temp_c * 1000; - } else { - temp = data->comp[low].temp_c * 1000 + - ((data->comp[high].temp_c - data->comp[low].temp_c) * - 1000 * ((int)ohm - (int)data->comp[low].ohm)) / - ((int)data->comp[high].ohm - (int)data->comp[low].ohm); - } + /* + * First multiplying the table temperatures with 1000 to get to + * millicentigrades (which is what we want) and then interpolating + * will give the best precision. + */ + temp = fixp_linear_interpolate(data->comp[low].ohm, + data->comp[low].temp_c * 1000, + data->comp[high].ohm, + data->comp[high].temp_c * 1000, + ohm); return temp; } diff --git a/drivers/hwmon/pmbus/bpa-rs600.c b/drivers/hwmon/pmbus/bpa-rs600.c index 2be69fedfa36..f2d4e378a775 100644 --- a/drivers/hwmon/pmbus/bpa-rs600.c +++ b/drivers/hwmon/pmbus/bpa-rs600.c @@ -12,14 +12,7 @@ #include <linux/pmbus.h> #include "pmbus.h" -#define BPARS600_MFR_VIN_MIN 0xa0 -#define BPARS600_MFR_VIN_MAX 0xa1 -#define BPARS600_MFR_IIN_MAX 0xa2 -#define BPARS600_MFR_PIN_MAX 0xa3 -#define BPARS600_MFR_VOUT_MIN 0xa4 -#define BPARS600_MFR_VOUT_MAX 0xa5 -#define BPARS600_MFR_IOUT_MAX 0xa6 -#define BPARS600_MFR_POUT_MAX 0xa7 +enum chips { bpa_rs600, bpd_rs600 }; static int bpa_rs600_read_byte_data(struct i2c_client *client, int page, int reg) { @@ -72,6 +65,26 @@ static int bpa_rs600_read_vin(struct i2c_client *client) return ret; } +/* + * Firmware V5.70 incorrectly reports 1640W for MFR_PIN_MAX. + * Deal with this by returning a sensible value. + */ +static int bpa_rs600_read_pin_max(struct i2c_client *client) +{ + int ret; + + ret = pmbus_read_word_data(client, 0, 0xff, PMBUS_MFR_PIN_MAX); + if (ret < 0) + return ret; + + /* Detect invalid 1640W (linear encoding) */ + if (ret == 0x0b34) + /* Report 700W (linear encoding) */ + return 0x095e; + + return ret; +} + static int bpa_rs600_read_word_data(struct i2c_client *client, int page, int phase, int reg) { int ret; @@ -81,29 +94,13 @@ static int bpa_rs600_read_word_data(struct i2c_client *client, int page, int pha switch (reg) { case PMBUS_VIN_UV_WARN_LIMIT: - ret = pmbus_read_word_data(client, 0, 0xff, BPARS600_MFR_VIN_MIN); - break; case PMBUS_VIN_OV_WARN_LIMIT: - ret = pmbus_read_word_data(client, 0, 0xff, BPARS600_MFR_VIN_MAX); - break; case PMBUS_VOUT_UV_WARN_LIMIT: - ret = pmbus_read_word_data(client, 0, 0xff, BPARS600_MFR_VOUT_MIN); - break; case PMBUS_VOUT_OV_WARN_LIMIT: - ret = pmbus_read_word_data(client, 0, 0xff, BPARS600_MFR_VOUT_MAX); - break; case PMBUS_IIN_OC_WARN_LIMIT: - ret = pmbus_read_word_data(client, 0, 0xff, BPARS600_MFR_IIN_MAX); - break; case PMBUS_IOUT_OC_WARN_LIMIT: - ret = pmbus_read_word_data(client, 0, 0xff, BPARS600_MFR_IOUT_MAX); - break; case PMBUS_PIN_OP_WARN_LIMIT: - ret = pmbus_read_word_data(client, 0, 0xff, BPARS600_MFR_PIN_MAX); - break; case PMBUS_POUT_OP_WARN_LIMIT: - ret = pmbus_read_word_data(client, 0, 0xff, BPARS600_MFR_POUT_MAX); - break; case PMBUS_VIN_UV_FAULT_LIMIT: case PMBUS_VIN_OV_FAULT_LIMIT: case PMBUS_VOUT_UV_FAULT_LIMIT: @@ -114,6 +111,9 @@ static int bpa_rs600_read_word_data(struct i2c_client *client, int page, int pha case PMBUS_READ_VIN: ret = bpa_rs600_read_vin(client); break; + case PMBUS_MFR_PIN_MAX: + ret = bpa_rs600_read_pin_max(client); + break; default: if (reg >= PMBUS_VIRT_BASE) ret = -ENXIO; @@ -146,11 +146,19 @@ static struct pmbus_driver_info bpa_rs600_info = { .read_word_data = bpa_rs600_read_word_data, }; +static const struct i2c_device_id bpa_rs600_id[] = { + { "bpa-rs600", bpa_rs600 }, + { "bpd-rs600", bpd_rs600 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, bpa_rs600_id); + static int bpa_rs600_probe(struct i2c_client *client) { struct device *dev = &client->dev; u8 buf[I2C_SMBUS_BLOCK_MAX + 1]; int ret; + const struct i2c_device_id *mid; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BYTE_DATA @@ -164,7 +172,11 @@ static int bpa_rs600_probe(struct i2c_client *client) return ret; } - if (strncmp(buf, "BPA-RS600", 8)) { + for (mid = bpa_rs600_id; mid->name[0]; mid++) { + if (!strncasecmp(buf, mid->name, strlen(mid->name))) + break; + } + if (!mid->name[0]) { buf[ret] = '\0'; dev_err(dev, "Unsupported Manufacturer Model '%s'\n", buf); return -ENODEV; @@ -173,12 +185,6 @@ static int bpa_rs600_probe(struct i2c_client *client) return pmbus_do_probe(client, &bpa_rs600_info); } -static const struct i2c_device_id bpa_rs600_id[] = { - { "bpars600", 0 }, - {}, -}; -MODULE_DEVICE_TABLE(i2c, bpa_rs600_id); - static const struct of_device_id __maybe_unused bpa_rs600_of_match[] = { { .compatible = "blutek,bpa-rs600" }, {}, diff --git a/drivers/hwmon/pmbus/ibm-cffps.c b/drivers/hwmon/pmbus/ibm-cffps.c index 5668d8305b78..df712ce4b164 100644 --- a/drivers/hwmon/pmbus/ibm-cffps.c +++ b/drivers/hwmon/pmbus/ibm-cffps.c @@ -50,9 +50,9 @@ #define CFFPS_MFR_VAUX_FAULT BIT(6) #define CFFPS_MFR_CURRENT_SHARE_WARNING BIT(7) -#define CFFPS_LED_BLINK BIT(0) -#define CFFPS_LED_ON BIT(1) -#define CFFPS_LED_OFF BIT(2) +#define CFFPS_LED_BLINK (BIT(0) | BIT(6)) +#define CFFPS_LED_ON (BIT(1) | BIT(6)) +#define CFFPS_LED_OFF (BIT(2) | BIT(6)) #define CFFPS_BLINK_RATE_MS 250 enum { diff --git a/drivers/hwmon/sbrmi.c b/drivers/hwmon/sbrmi.c new file mode 100644 index 000000000000..7bf0c3fba75f --- /dev/null +++ b/drivers/hwmon/sbrmi.c @@ -0,0 +1,359 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * sbrmi.c - hwmon driver for a SB-RMI mailbox + * compliant AMD SoC device. + * + * Copyright (C) 2020-2021 Advanced Micro Devices, Inc. + */ + +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/hwmon.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> + +/* Do not allow setting negative power limit */ +#define SBRMI_PWR_MIN 0 +/* Mask for Status Register bit[1] */ +#define SW_ALERT_MASK 0x2 + +/* Software Interrupt for triggering */ +#define START_CMD 0x80 +#define TRIGGER_MAILBOX 0x01 + +/* + * SB-RMI supports soft mailbox service request to MP1 (power management + * firmware) through SBRMI inbound/outbound message registers. + * SB-RMI message IDs + */ +enum sbrmi_msg_id { + SBRMI_READ_PKG_PWR_CONSUMPTION = 0x1, + SBRMI_WRITE_PKG_PWR_LIMIT, + SBRMI_READ_PKG_PWR_LIMIT, + SBRMI_READ_PKG_MAX_PWR_LIMIT, +}; + +/* SB-RMI registers */ +enum sbrmi_reg { + SBRMI_CTRL = 0x01, + SBRMI_STATUS, + SBRMI_OUTBNDMSG0 = 0x30, + SBRMI_OUTBNDMSG1, + SBRMI_OUTBNDMSG2, + SBRMI_OUTBNDMSG3, + SBRMI_OUTBNDMSG4, + SBRMI_OUTBNDMSG5, + SBRMI_OUTBNDMSG6, + SBRMI_OUTBNDMSG7, + SBRMI_INBNDMSG0, + SBRMI_INBNDMSG1, + SBRMI_INBNDMSG2, + SBRMI_INBNDMSG3, + SBRMI_INBNDMSG4, + SBRMI_INBNDMSG5, + SBRMI_INBNDMSG6, + SBRMI_INBNDMSG7, + SBRMI_SW_INTERRUPT, +}; + +/* Each client has this additional data */ +struct sbrmi_data { + struct i2c_client *client; + struct mutex lock; + u32 pwr_limit_max; +}; + +struct sbrmi_mailbox_msg { + u8 cmd; + bool read; + u32 data_in; + u32 data_out; +}; + +static int sbrmi_enable_alert(struct i2c_client *client) +{ + int ctrl; + + /* + * Enable the SB-RMI Software alert status + * by writing 0 to bit 4 of Control register(0x1) + */ + ctrl = i2c_smbus_read_byte_data(client, SBRMI_CTRL); + if (ctrl < 0) + return ctrl; + + if (ctrl & 0x10) { + ctrl &= ~0x10; + return i2c_smbus_write_byte_data(client, + SBRMI_CTRL, ctrl); + } + + return 0; +} + +static int rmi_mailbox_xfer(struct sbrmi_data *data, + struct sbrmi_mailbox_msg *msg) +{ + int i, ret, retry = 10; + int sw_status; + u8 byte; + + mutex_lock(&data->lock); + + /* Indicate firmware a command is to be serviced */ + ret = i2c_smbus_write_byte_data(data->client, + SBRMI_INBNDMSG7, START_CMD); + if (ret < 0) + goto exit_unlock; + + /* Write the command to SBRMI::InBndMsg_inst0 */ + ret = i2c_smbus_write_byte_data(data->client, + SBRMI_INBNDMSG0, msg->cmd); + if (ret < 0) + goto exit_unlock; + + /* + * For both read and write the initiator (BMC) writes + * Command Data In[31:0] to SBRMI::InBndMsg_inst[4:1] + * SBRMI_x3C(MSB):SBRMI_x39(LSB) + */ + for (i = 0; i < 4; i++) { + byte = (msg->data_in >> i * 8) & 0xff; + ret = i2c_smbus_write_byte_data(data->client, + SBRMI_INBNDMSG1 + i, byte); + if (ret < 0) + goto exit_unlock; + } + + /* + * Write 0x01 to SBRMI::SoftwareInterrupt to notify firmware to + * perform the requested read or write command + */ + ret = i2c_smbus_write_byte_data(data->client, + SBRMI_SW_INTERRUPT, TRIGGER_MAILBOX); + if (ret < 0) + goto exit_unlock; + + /* + * Firmware will write SBRMI::Status[SwAlertSts]=1 to generate + * an ALERT (if enabled) to initiator (BMC) to indicate completion + * of the requested command + */ + do { + sw_status = i2c_smbus_read_byte_data(data->client, + SBRMI_STATUS); + if (sw_status < 0) { + ret = sw_status; + goto exit_unlock; + } + if (sw_status & SW_ALERT_MASK) + break; + usleep_range(50, 100); + } while (retry--); + + if (retry < 0) { + dev_err(&data->client->dev, + "Firmware fail to indicate command completion\n"); + ret = -EIO; + goto exit_unlock; + } + + /* + * For a read operation, the initiator (BMC) reads the firmware + * response Command Data Out[31:0] from SBRMI::OutBndMsg_inst[4:1] + * {SBRMI_x34(MSB):SBRMI_x31(LSB)}. + */ + if (msg->read) { + for (i = 0; i < 4; i++) { + ret = i2c_smbus_read_byte_data(data->client, + SBRMI_OUTBNDMSG1 + i); + if (ret < 0) + goto exit_unlock; + msg->data_out |= ret << i * 8; + } + } + + /* + * BMC must write 1'b1 to SBRMI::Status[SwAlertSts] to clear the + * ALERT to initiator + */ + ret = i2c_smbus_write_byte_data(data->client, SBRMI_STATUS, + sw_status | SW_ALERT_MASK); + +exit_unlock: + mutex_unlock(&data->lock); + return ret; +} + +static int sbrmi_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct sbrmi_data *data = dev_get_drvdata(dev); + struct sbrmi_mailbox_msg msg = { 0 }; + int ret; + + if (type != hwmon_power) + return -EINVAL; + + msg.read = true; + switch (attr) { + case hwmon_power_input: + msg.cmd = SBRMI_READ_PKG_PWR_CONSUMPTION; + ret = rmi_mailbox_xfer(data, &msg); + break; + case hwmon_power_cap: + msg.cmd = SBRMI_READ_PKG_PWR_LIMIT; + ret = rmi_mailbox_xfer(data, &msg); + break; + case hwmon_power_cap_max: + msg.data_out = data->pwr_limit_max; + ret = 0; + break; + default: + return -EINVAL; + } + if (ret < 0) + return ret; + /* hwmon power attributes are in microWatt */ + *val = (long)msg.data_out * 1000; + return ret; +} + +static int sbrmi_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct sbrmi_data *data = dev_get_drvdata(dev); + struct sbrmi_mailbox_msg msg = { 0 }; + + if (type != hwmon_power && attr != hwmon_power_cap) + return -EINVAL; + /* + * hwmon power attributes are in microWatt + * mailbox read/write is in mWatt + */ + val /= 1000; + + val = clamp_val(val, SBRMI_PWR_MIN, data->pwr_limit_max); + + msg.cmd = SBRMI_WRITE_PKG_PWR_LIMIT; + msg.data_in = val; + msg.read = false; + + return rmi_mailbox_xfer(data, &msg); +} + +static umode_t sbrmi_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_power: + switch (attr) { + case hwmon_power_input: + case hwmon_power_cap_max: + return 0444; + case hwmon_power_cap: + return 0644; + } + break; + default: + break; + } + return 0; +} + +static const struct hwmon_channel_info *sbrmi_info[] = { + HWMON_CHANNEL_INFO(power, + HWMON_P_INPUT | HWMON_P_CAP | HWMON_P_CAP_MAX), + NULL +}; + +static const struct hwmon_ops sbrmi_hwmon_ops = { + .is_visible = sbrmi_is_visible, + .read = sbrmi_read, + .write = sbrmi_write, +}; + +static const struct hwmon_chip_info sbrmi_chip_info = { + .ops = &sbrmi_hwmon_ops, + .info = sbrmi_info, +}; + +static int sbrmi_get_max_pwr_limit(struct sbrmi_data *data) +{ + struct sbrmi_mailbox_msg msg = { 0 }; + int ret; + + msg.cmd = SBRMI_READ_PKG_MAX_PWR_LIMIT; + msg.read = true; + ret = rmi_mailbox_xfer(data, &msg); + if (ret < 0) + return ret; + data->pwr_limit_max = msg.data_out; + + return ret; +} + +static int sbrmi_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device *hwmon_dev; + struct sbrmi_data *data; + int ret; + + data = devm_kzalloc(dev, sizeof(struct sbrmi_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->client = client; + mutex_init(&data->lock); + + /* Enable alert for SB-RMI sequence */ + ret = sbrmi_enable_alert(client); + if (ret < 0) + return ret; + + /* Cache maximum power limit */ + ret = sbrmi_get_max_pwr_limit(data); + if (ret < 0) + return ret; + + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, + &sbrmi_chip_info, NULL); + + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct i2c_device_id sbrmi_id[] = { + {"sbrmi", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, sbrmi_id); + +static const struct of_device_id __maybe_unused sbrmi_of_match[] = { + { + .compatible = "amd,sbrmi", + }, + { }, +}; +MODULE_DEVICE_TABLE(of, sbrmi_of_match); + +static struct i2c_driver sbrmi_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "sbrmi", + .of_match_table = of_match_ptr(sbrmi_of_match), + }, + .probe = sbrmi_probe, + .id_table = sbrmi_id, +}; + +module_i2c_driver(sbrmi_driver); + +MODULE_AUTHOR("Akshay Gupta <akshay.gupta@amd.com>"); +MODULE_DESCRIPTION("Hwmon driver for AMD SB-RMI emulated sensor"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c index 8618aaf32350..705a59663d42 100644 --- a/drivers/hwmon/w83627ehf.c +++ b/drivers/hwmon/w83627ehf.c @@ -372,12 +372,10 @@ struct w83627ehf_data { u8 temp3_val_only:1; u8 have_vid:1; -#ifdef CONFIG_PM /* Remember extra register values over suspend/resume */ u8 vbat; u8 fandiv1; u8 fandiv2; -#endif }; struct w83627ehf_sio_data { @@ -1083,7 +1081,7 @@ cpu0_vid_show(struct device *dev, struct device_attribute *attr, char *buf) struct w83627ehf_data *data = dev_get_drvdata(dev); return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm)); } -DEVICE_ATTR_RO(cpu0_vid); +static DEVICE_ATTR_RO(cpu0_vid); /* Case open detection */ @@ -1694,7 +1692,7 @@ static const struct hwmon_chip_info w83627ehf_chip_info = { .info = w83627ehf_info, }; -static int w83627ehf_probe(struct platform_device *pdev) +static int __init w83627ehf_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev); @@ -1705,20 +1703,12 @@ static int w83627ehf_probe(struct platform_device *pdev) struct device *hwmon_dev; res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (!request_region(res->start, IOREGION_LENGTH, DRVNAME)) { - err = -EBUSY; - dev_err(dev, "Failed to request region 0x%lx-0x%lx\n", - (unsigned long)res->start, - (unsigned long)res->start + IOREGION_LENGTH - 1); - goto exit; - } + if (!devm_request_region(dev, res->start, IOREGION_LENGTH, DRVNAME)) + return -EBUSY; - data = devm_kzalloc(&pdev->dev, sizeof(struct w83627ehf_data), - GFP_KERNEL); - if (!data) { - err = -ENOMEM; - goto exit_release; - } + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; data->addr = res->start; mutex_init(&data->lock); @@ -1882,7 +1872,7 @@ static int w83627ehf_probe(struct platform_device *pdev) err = superio_enter(sio_data->sioreg); if (err) - goto exit_release; + return err; /* Read VID value */ if (sio_data->kind == w83667hg || sio_data->kind == w83667hg_b) { @@ -1951,30 +1941,10 @@ static int w83627ehf_probe(struct platform_device *pdev) data, &w83627ehf_chip_info, w83627ehf_groups); - if (IS_ERR(hwmon_dev)) { - err = PTR_ERR(hwmon_dev); - goto exit_release; - } - - return 0; - -exit_release: - release_region(res->start, IOREGION_LENGTH); -exit: - return err; + return PTR_ERR_OR_ZERO(hwmon_dev); } -static int w83627ehf_remove(struct platform_device *pdev) -{ - struct w83627ehf_data *data = platform_get_drvdata(pdev); - - release_region(data->addr, IOREGION_LENGTH); - - return 0; -} - -#ifdef CONFIG_PM -static int w83627ehf_suspend(struct device *dev) +static int __maybe_unused w83627ehf_suspend(struct device *dev) { struct w83627ehf_data *data = w83627ehf_update_device(dev); @@ -1985,7 +1955,7 @@ static int w83627ehf_suspend(struct device *dev) return 0; } -static int w83627ehf_resume(struct device *dev) +static int __maybe_unused w83627ehf_resume(struct device *dev) { struct w83627ehf_data *data = dev_get_drvdata(dev); int i; @@ -2040,25 +2010,13 @@ static int w83627ehf_resume(struct device *dev) return 0; } -static const struct dev_pm_ops w83627ehf_dev_pm_ops = { - .suspend = w83627ehf_suspend, - .resume = w83627ehf_resume, - .freeze = w83627ehf_suspend, - .restore = w83627ehf_resume, -}; - -#define W83627EHF_DEV_PM_OPS (&w83627ehf_dev_pm_ops) -#else -#define W83627EHF_DEV_PM_OPS NULL -#endif /* CONFIG_PM */ +static SIMPLE_DEV_PM_OPS(w83627ehf_dev_pm_ops, w83627ehf_suspend, w83627ehf_resume); static struct platform_driver w83627ehf_driver = { .driver = { .name = DRVNAME, - .pm = W83627EHF_DEV_PM_OPS, + .pm = &w83627ehf_dev_pm_ops, }, - .probe = w83627ehf_probe, - .remove = w83627ehf_remove, }; /* w83627ehf_find() looks for a '627 in the Super-I/O config space */ @@ -2150,8 +2108,7 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr, /* * when Super-I/O functions move to a separate file, the Super-I/O * bus will manage the lifetime of the device and this module will only keep - * track of the w83627ehf driver. But since we platform_device_alloc(), we - * must keep track of the device + * track of the w83627ehf driver. */ static struct platform_device *pdev; @@ -2159,7 +2116,10 @@ static int __init sensors_w83627ehf_init(void) { int err; unsigned short address; - struct resource res; + struct resource res = { + .name = DRVNAME, + .flags = IORESOURCE_IO, + }; struct w83627ehf_sio_data sio_data; /* @@ -2173,55 +2133,17 @@ static int __init sensors_w83627ehf_init(void) w83627ehf_find(0x4e, &address, &sio_data)) return -ENODEV; - err = platform_driver_register(&w83627ehf_driver); - if (err) - goto exit; - - pdev = platform_device_alloc(DRVNAME, address); - if (!pdev) { - err = -ENOMEM; - pr_err("Device allocation failed\n"); - goto exit_unregister; - } - - err = platform_device_add_data(pdev, &sio_data, - sizeof(struct w83627ehf_sio_data)); - if (err) { - pr_err("Platform data allocation failed\n"); - goto exit_device_put; - } - - memset(&res, 0, sizeof(res)); - res.name = DRVNAME; res.start = address + IOREGION_OFFSET; res.end = address + IOREGION_OFFSET + IOREGION_LENGTH - 1; - res.flags = IORESOURCE_IO; err = acpi_check_resource_conflict(&res); if (err) - goto exit_device_put; - - err = platform_device_add_resources(pdev, &res, 1); - if (err) { - pr_err("Device resource addition failed (%d)\n", err); - goto exit_device_put; - } - - /* platform_device_add calls probe() */ - err = platform_device_add(pdev); - if (err) { - pr_err("Device addition failed (%d)\n", err); - goto exit_device_put; - } + return err; - return 0; + pdev = platform_create_bundle(&w83627ehf_driver, w83627ehf_probe, &res, 1, &sio_data, + sizeof(struct w83627ehf_sio_data)); -exit_device_put: - platform_device_put(pdev); -exit_unregister: - platform_driver_unregister(&w83627ehf_driver); -exit: - return err; + return PTR_ERR_OR_ZERO(pdev); } static void __exit sensors_w83627ehf_exit(void) diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c index e84aa5604e64..ce8e2c10e854 100644 --- a/drivers/hwmon/w83781d.c +++ b/drivers/hwmon/w83781d.c @@ -1571,10 +1571,21 @@ static const struct i2c_device_id w83781d_ids[] = { }; MODULE_DEVICE_TABLE(i2c, w83781d_ids); +static const struct of_device_id w83781d_of_match[] = { + { .compatible = "winbond,w83781d" }, + { .compatible = "winbond,w83781g" }, + { .compatible = "winbond,w83782d" }, + { .compatible = "winbond,w83783s" }, + { .compatible = "asus,as99127f" }, + { }, +}; +MODULE_DEVICE_TABLE(of, w83781d_of_match); + static struct i2c_driver w83781d_driver = { .class = I2C_CLASS_HWMON, .driver = { .name = "w83781d", + .of_match_table = w83781d_of_match, }, .probe_new = w83781d_probe, .remove = w83781d_remove, |