diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2021-04-26 14:59:21 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2021-04-26 14:59:21 -0700 |
commit | 47080f2286110c371b9cf75ac7b34a6f2f1cf4ba (patch) | |
tree | fdc9cf3da77cffd1add8a9e7c44df988291837c7 /drivers/hwmon | |
parent | 55ba0fe059a577fa08f23223991b24564962620f (diff) | |
parent | 9049572fb145746725b198a19e27fa2671b80448 (diff) | |
download | linux-47080f2286110c371b9cf75ac7b34a6f2f1cf4ba.tar.bz2 |
Merge tag 'hwmon-for-v5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging
Pull hwmon updates from Guenter Roeck:
"The most notable change is the removal of the amd_energy driver. It
was rendered all but unusable by making its attributes privileged-only
to work around a security issue. A suggested remedy was rejected by
AMD, so the only real solution was to remove the driver. For the
future, we'll have to make sure that no privileged-access-only drivers
are accepted into the hwmon subsystem in the first place. The hwmon
ABI document was updated accordingly.
Other changes:
PMBus drivers:
- Added driver for MAX15301
- Added driver for BluTek BPA-RS600
- Added driver for fsp-3y PSUs and PDUs
- Added driver for Infineon IR36021
- Added driver for ST STPDDC60
- Added support for TI TPS53676 to tps53679 driver
- Introduced PMBUS symbol namespace. This was made necessary by a
suggestion to use its exported functions from outside the hwmon
subsystem.
- Minor improvements and bug fixes
New drivers:
- Driver for NZXT Kraken X42/X52/X62/X72
Driver enhancements:
- Added support for Intel D5005 to intel-m10-bmc-hwmon driver
- Added support for NCT6686D to nct6683 driver
Other:
- Converted sch5627 and amd9240 drivers to
hwmon_device_register_with_info()
- Added support for fan drawers capability and present registers to
mlxreg-fan driver
- Added Dell Latitude E7440 to fan control whitelist in dell-smm
driver
- Replaced snprintf in show functions with sysfs_emit. Done with
coccinelle script for all drivers to preempt endless per-driver
submissions of the same change.
- Use kobj_to_dev(). Another coccinelle based change to preempt
endless per-driver submissions of the same change.
- Various minor fixes and improvements"
* tag 'hwmon-for-v5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (38 commits)
hwmon: Remove amd_energy driver
hwmon: Clarify scope of attribute access
hwmon: (pmbus) Introduce PMBUS symbol namespace
hwmon: (pmbus) Add pmbus driver for MAX15301
hwmon: (sch5627) Remove unnecessary error path
hwmon: (sch5627) Use devres function
hwmon: (pmbus/pxe1610) don't bail out when not all pages are active
hwmon: Add driver for fsp-3y PSUs and PDUs
hwmon: (intel-m10-bmc-hwmon) add sensor support of Intel D5005 card
hwmon: (sch5627) Split sch5627_update_device()
hwmon: (sch5627) Convert to hwmon_device_register_with_info()
hwmon: (nct6683) remove useless function
hwmon: (dell-smm) Add Dell Latitude E7440 to fan control whitelist
MAINTAINERS: Add keyword pattern for hwmon registration functions
hwmon: (mlxreg-fan) Add support for fan drawers capability and present registers
hwmon: (pmbus/tps53679) Add support for TI TPS53676
dt-bindings: Add trivial device entry for TPS53676
hwmon: (ftsteutates) Rudimentary typo fixes
hwmon: (pmbus) Add driver for BluTek BPA-RS600
dt-bindings: Add vendor prefix and trivial device for BluTek BPA-RS600
...
Diffstat (limited to 'drivers/hwmon')
78 files changed, 2665 insertions, 1496 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 54f04e61fb83..87624902ea80 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -321,16 +321,6 @@ config SENSORS_FAM15H_POWER This driver can also be built as a module. If so, the module will be called fam15h_power. -config SENSORS_AMD_ENERGY - tristate "AMD RAPL MSR based Energy driver" - depends on X86 - help - If you say yes here you get support for core and package energy - sensors, based on RAPL MSR for AMD family 17h and above CPUs. - - This driver can also be built as a module. If so, the module - will be called as amd_energy. - config SENSORS_APPLESMC tristate "Apple SMC (Motion sensor, light sensor, keyboard backlight)" depends on INPUT && X86 @@ -1492,6 +1482,16 @@ config SENSORS_NSA320 This driver can also be built as a module. If so, the module will be called nsa320-hwmon. +config SENSORS_NZXT_KRAKEN2 + tristate "NZXT Kraken X42/X51/X62/X72 liquid coolers" + depends on USB_HID + help + If you say yes here you get support for hardware monitoring for the + NZXT Kraken X42/X52/X62/X72 all-in-one CPU liquid coolers. + + This driver can also be built as a module. If so, the module + will be called nzxt-kraken2. + source "drivers/hwmon/occ/Kconfig" config SENSORS_PCF8591 diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index fe38e8a5c979..59e78bc212cf 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -155,6 +155,7 @@ obj-$(CONFIG_SENSORS_NCT7904) += nct7904.o obj-$(CONFIG_SENSORS_NPCM7XX) += npcm750-pwm-fan.o obj-$(CONFIG_SENSORS_NSA320) += nsa320-hwmon.o obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o +obj-$(CONFIG_SENSORS_NZXT_KRAKEN2) += nzxt-kraken2.o obj-$(CONFIG_SENSORS_PC87360) += pc87360.o obj-$(CONFIG_SENSORS_PC87427) += pc87427.o obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o diff --git a/drivers/hwmon/adc128d818.c b/drivers/hwmon/adc128d818.c index 6c9a906631b8..fd938c70293f 100644 --- a/drivers/hwmon/adc128d818.c +++ b/drivers/hwmon/adc128d818.c @@ -248,7 +248,7 @@ static ssize_t adc128_alarm_show(struct device *dev, static umode_t adc128_is_visible(struct kobject *kobj, struct attribute *attr, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct adc128_data *data = dev_get_drvdata(dev); if (index < ADC128_ATTR_NUM_VOLT) { diff --git a/drivers/hwmon/adm9240.c b/drivers/hwmon/adm9240.c index cc3e0184e720..5677263bcf0d 100644 --- a/drivers/hwmon/adm9240.c +++ b/drivers/hwmon/adm9240.c @@ -28,6 +28,7 @@ * LM81 extended temp reading not implemented */ +#include <linux/bits.h> #include <linux/init.h> #include <linux/module.h> #include <linux/slab.h> @@ -37,7 +38,6 @@ #include <linux/hwmon-vid.h> #include <linux/err.h> #include <linux/mutex.h> -#include <linux/jiffies.h> #include <linux/regmap.h> /* Addresses to scan */ @@ -123,32 +123,18 @@ static inline unsigned int AOUT_FROM_REG(u8 reg) /* per client data */ struct adm9240_data { - struct i2c_client *client; + struct device *dev; struct regmap *regmap; struct mutex update_lock; - char valid; - unsigned long last_updated_measure; - unsigned long last_updated_config; - - u8 in[6]; /* ro in0_input */ - u8 in_max[6]; /* rw in0_max */ - u8 in_min[6]; /* rw in0_min */ - u8 fan[2]; /* ro fan1_input */ - u8 fan_min[2]; /* rw fan1_min */ + u8 fan_div[2]; /* rw fan1_div, read-only accessor */ - s16 temp; /* ro temp1_input, 9-bit sign-extended */ - s8 temp_max[2]; /* rw 0 -> temp_max, 1 -> temp_max_hyst */ - u16 alarms; /* ro alarms */ - u8 aout; /* rw aout_output */ - u8 vid; /* ro vid */ u8 vrm; /* -- vrm set on startup, no accessor */ }; /* write new fan div, callers must hold data->update_lock */ -static int adm9240_write_fan_div(struct adm9240_data *data, int nr, - u8 fan_div) +static int adm9240_write_fan_div(struct adm9240_data *data, int channel, u8 fan_div) { - unsigned int reg, old, shift = (nr + 2) * 2; + unsigned int reg, old, shift = (channel + 2) * 2; int err; err = regmap_read(data->regmap, ADM9240_REG_VID_FAN_DIV, ®); @@ -160,336 +146,13 @@ static int adm9240_write_fan_div(struct adm9240_data *data, int nr, err = regmap_write(data->regmap, ADM9240_REG_VID_FAN_DIV, reg); if (err < 0) return err; - dev_dbg(&data->client->dev, - "fan%d clock divider changed from %u to %u\n", - nr + 1, 1 << old, 1 << fan_div); - - return 0; -} - -static int adm9240_update_measure(struct adm9240_data *data) -{ - unsigned int val; - u8 regs[2]; - int err; - int i; - - err = regmap_bulk_read(data->regmap, ADM9240_REG_IN(0), &data->in[0], 6); - if (err < 0) - return err; - err = regmap_bulk_read(data->regmap, ADM9240_REG_INT(0), ®s, 2); - if (err < 0) - return err; - - data->alarms = regs[0] | regs[1] << 8; - - /* - * read temperature: assume temperature changes less than - * 0.5'C per two measurement cycles thus ignore possible - * but unlikely aliasing error on lsb reading. --Grant - */ - err = regmap_read(data->regmap, ADM9240_REG_TEMP, &val); - if (err < 0) - return err; - data->temp = val << 8; - err = regmap_read(data->regmap, ADM9240_REG_TEMP_CONF, &val); - if (err < 0) - return err; - data->temp |= val; - - err = regmap_bulk_read(data->regmap, ADM9240_REG_FAN(0), - &data->fan[0], 2); - if (err < 0) - return err; - - for (i = 0; i < 2; i++) { /* read fans */ - /* adjust fan clock divider on overflow */ - if (data->valid && data->fan[i] == 255 && - data->fan_div[i] < 3) { - - err = adm9240_write_fan_div(data, i, - ++data->fan_div[i]); - if (err < 0) - return err; - - /* adjust fan_min if active, but not to 0 */ - if (data->fan_min[i] < 255 && - data->fan_min[i] >= 2) - data->fan_min[i] /= 2; - } - } + dev_dbg(data->dev, + "fan%d clock divider changed from %lu to %lu\n", + channel + 1, BIT(old), BIT(fan_div)); return 0; } -static int adm9240_update_config(struct adm9240_data *data) -{ - unsigned int val; - int i; - int err; - - for (i = 0; i < 6; i++) { - err = regmap_raw_read(data->regmap, ADM9240_REG_IN_MIN(i), - &data->in_min[i], 1); - if (err < 0) - return err; - err = regmap_raw_read(data->regmap, ADM9240_REG_IN_MAX(i), - &data->in_max[i], 1); - if (err < 0) - return err; - } - err = regmap_bulk_read(data->regmap, ADM9240_REG_FAN_MIN(0), - &data->fan_min[0], 2); - if (err < 0) - return err; - err = regmap_bulk_read(data->regmap, ADM9240_REG_TEMP_MAX(0), - &data->temp_max[0], 2); - if (err < 0) - return err; - - /* read fan divs and 5-bit VID */ - err = regmap_read(data->regmap, ADM9240_REG_VID_FAN_DIV, &val); - if (err < 0) - return err; - data->fan_div[0] = (val >> 4) & 3; - data->fan_div[1] = (val >> 6) & 3; - data->vid = val & 0x0f; - err = regmap_read(data->regmap, ADM9240_REG_VID4, &val); - if (err < 0) - return err; - data->vid |= (val & 1) << 4; - /* read analog out */ - err = regmap_raw_read(data->regmap, ADM9240_REG_ANALOG_OUT, - &data->aout, 1); - - return err; -} - -static struct adm9240_data *adm9240_update_device(struct device *dev) -{ - struct adm9240_data *data = dev_get_drvdata(dev); - int err; - - mutex_lock(&data->update_lock); - - /* minimum measurement cycle: 1.75 seconds */ - if (time_after(jiffies, data->last_updated_measure + (HZ * 7 / 4)) - || !data->valid) { - err = adm9240_update_measure(data); - if (err < 0) { - data->valid = 0; - mutex_unlock(&data->update_lock); - return ERR_PTR(err); - } - data->last_updated_measure = jiffies; - } - - /* minimum config reading cycle: 300 seconds */ - if (time_after(jiffies, data->last_updated_config + (HZ * 300)) - || !data->valid) { - err = adm9240_update_config(data); - if (err < 0) { - data->valid = 0; - mutex_unlock(&data->update_lock); - return ERR_PTR(err); - } - data->last_updated_config = jiffies; - data->valid = 1; - } - mutex_unlock(&data->update_lock); - return data; -} - -/*** sysfs accessors ***/ - -/* temperature */ -static ssize_t temp1_input_show(struct device *dev, - struct device_attribute *dummy, char *buf) -{ - struct adm9240_data *data = adm9240_update_device(dev); - - if (IS_ERR(data)) - return PTR_ERR(data); - - return sprintf(buf, "%d\n", data->temp / 128 * 500); /* 9-bit value */ -} - -static ssize_t max_show(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adm9240_data *data = adm9240_update_device(dev); - - if (IS_ERR(data)) - return PTR_ERR(data); - - return sprintf(buf, "%d\n", data->temp_max[attr->index] * 1000); -} - -static ssize_t 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 adm9240_data *data = dev_get_drvdata(dev); - long val; - int err; - - err = kstrtol(buf, 10, &val); - if (err) - return err; - - mutex_lock(&data->update_lock); - data->temp_max[attr->index] = TEMP_TO_REG(val); - err = regmap_write(data->regmap, ADM9240_REG_TEMP_MAX(attr->index), - data->temp_max[attr->index]); - mutex_unlock(&data->update_lock); - return err < 0 ? err : count; -} - -static DEVICE_ATTR_RO(temp1_input); -static SENSOR_DEVICE_ATTR_RW(temp1_max, max, 0); -static SENSOR_DEVICE_ATTR_RW(temp1_max_hyst, max, 1); - -/* voltage */ -static ssize_t in_show(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adm9240_data *data = adm9240_update_device(dev); - - if (IS_ERR(data)) - return PTR_ERR(data); - - return sprintf(buf, "%d\n", IN_FROM_REG(data->in[attr->index], - attr->index)); -} - -static ssize_t in_min_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adm9240_data *data = adm9240_update_device(dev); - - if (IS_ERR(data)) - return PTR_ERR(data); - - return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[attr->index], - attr->index)); -} - -static ssize_t in_max_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adm9240_data *data = adm9240_update_device(dev); - - if (IS_ERR(data)) - return PTR_ERR(data); - - return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[attr->index], - attr->index)); -} - -static ssize_t in_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 adm9240_data *data = dev_get_drvdata(dev); - unsigned long val; - int err; - - err = kstrtoul(buf, 10, &val); - if (err) - return err; - - mutex_lock(&data->update_lock); - data->in_min[attr->index] = IN_TO_REG(val, attr->index); - err = regmap_write(data->regmap, ADM9240_REG_IN_MIN(attr->index), - data->in_min[attr->index]); - mutex_unlock(&data->update_lock); - return err < 0 ? err : count; -} - -static ssize_t in_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 adm9240_data *data = dev_get_drvdata(dev); - unsigned long val; - int err; - - err = kstrtoul(buf, 10, &val); - if (err) - return err; - - mutex_lock(&data->update_lock); - data->in_max[attr->index] = IN_TO_REG(val, attr->index); - err = regmap_write(data->regmap, ADM9240_REG_IN_MAX(attr->index), - data->in_max[attr->index]); - mutex_unlock(&data->update_lock); - return err < 0 ? err : count; -} - -static SENSOR_DEVICE_ATTR_RO(in0_input, in, 0); -static SENSOR_DEVICE_ATTR_RW(in0_min, in_min, 0); -static SENSOR_DEVICE_ATTR_RW(in0_max, in_max, 0); -static SENSOR_DEVICE_ATTR_RO(in1_input, in, 1); -static SENSOR_DEVICE_ATTR_RW(in1_min, in_min, 1); -static SENSOR_DEVICE_ATTR_RW(in1_max, in_max, 1); -static SENSOR_DEVICE_ATTR_RO(in2_input, in, 2); -static SENSOR_DEVICE_ATTR_RW(in2_min, in_min, 2); -static SENSOR_DEVICE_ATTR_RW(in2_max, in_max, 2); -static SENSOR_DEVICE_ATTR_RO(in3_input, in, 3); -static SENSOR_DEVICE_ATTR_RW(in3_min, in_min, 3); -static SENSOR_DEVICE_ATTR_RW(in3_max, in_max, 3); -static SENSOR_DEVICE_ATTR_RO(in4_input, in, 4); -static SENSOR_DEVICE_ATTR_RW(in4_min, in_min, 4); -static SENSOR_DEVICE_ATTR_RW(in4_max, in_max, 4); -static SENSOR_DEVICE_ATTR_RO(in5_input, in, 5); -static SENSOR_DEVICE_ATTR_RW(in5_min, in_min, 5); -static SENSOR_DEVICE_ATTR_RW(in5_max, in_max, 5); - -/* fans */ -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 adm9240_data *data = adm9240_update_device(dev); - - if (IS_ERR(data)) - return PTR_ERR(data); - - return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[attr->index], - 1 << data->fan_div[attr->index])); -} - -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 adm9240_data *data = adm9240_update_device(dev); - - if (IS_ERR(data)) - return PTR_ERR(data); - - return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[attr->index], - 1 << data->fan_div[attr->index])); -} - -static ssize_t fan_div_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adm9240_data *data = adm9240_update_device(dev); - - if (IS_ERR(data)) - return PTR_ERR(data); - - return sprintf(buf, "%d\n", 1 << data->fan_div[attr->index]); -} - /* * set fan speed low limit: * @@ -501,38 +164,25 @@ static ssize_t fan_div_show(struct device *dev, * - otherwise: select fan clock divider to suit fan speed low limit, * measurement code may adjust registers to ensure fan speed reading */ -static ssize_t fan_min_store(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) +static int adm9240_fan_min_write(struct adm9240_data *data, int channel, long val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct adm9240_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - int nr = attr->index; u8 new_div; - unsigned long val; + u8 fan_min; int err; - err = kstrtoul(buf, 10, &val); - if (err) - return err; - mutex_lock(&data->update_lock); if (!val) { - data->fan_min[nr] = 255; - new_div = data->fan_div[nr]; - - dev_dbg(&client->dev, "fan%u low limit set disabled\n", - nr + 1); + fan_min = 255; + new_div = data->fan_div[channel]; + dev_dbg(data->dev, "fan%u low limit set disabled\n", channel + 1); } else if (val < 1350000 / (8 * 254)) { new_div = 3; - data->fan_min[nr] = 254; - - dev_dbg(&client->dev, "fan%u low limit set minimum %u\n", - nr + 1, FAN_FROM_REG(254, 1 << new_div)); + fan_min = 254; + dev_dbg(data->dev, "fan%u low limit set minimum %u\n", + channel + 1, FAN_FROM_REG(254, BIT(new_div))); } else { unsigned int new_min = 1350000 / val; @@ -544,87 +194,55 @@ static ssize_t fan_min_store(struct device *dev, if (!new_min) /* keep > 0 */ new_min++; - data->fan_min[nr] = new_min; + fan_min = new_min; - dev_dbg(&client->dev, "fan%u low limit set fan speed %u\n", - nr + 1, FAN_FROM_REG(new_min, 1 << new_div)); + dev_dbg(data->dev, "fan%u low limit set fan speed %u\n", + channel + 1, FAN_FROM_REG(new_min, BIT(new_div))); } - if (new_div != data->fan_div[nr]) { - data->fan_div[nr] = new_div; - adm9240_write_fan_div(data, nr, new_div); + if (new_div != data->fan_div[channel]) { + data->fan_div[channel] = new_div; + adm9240_write_fan_div(data, channel, new_div); } - err = regmap_write(data->regmap, ADM9240_REG_FAN_MIN(nr), - data->fan_min[nr]); + err = regmap_write(data->regmap, ADM9240_REG_FAN_MIN(channel), fan_min); mutex_unlock(&data->update_lock); - return err < 0 ? err : count; -} - -static SENSOR_DEVICE_ATTR_RO(fan1_input, fan, 0); -static SENSOR_DEVICE_ATTR_RW(fan1_min, fan_min, 0); -static SENSOR_DEVICE_ATTR_RO(fan1_div, fan_div, 0); -static SENSOR_DEVICE_ATTR_RO(fan2_input, fan, 1); -static SENSOR_DEVICE_ATTR_RW(fan2_min, fan_min, 1); -static SENSOR_DEVICE_ATTR_RO(fan2_div, fan_div, 1); -/* alarms */ -static ssize_t alarms_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct adm9240_data *data = adm9240_update_device(dev); - - if (IS_ERR(data)) - return PTR_ERR(data); - - return sprintf(buf, "%u\n", data->alarms); + return err; } -static DEVICE_ATTR_RO(alarms); -static ssize_t alarm_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - int bitnr = to_sensor_dev_attr(attr)->index; - struct adm9240_data *data = adm9240_update_device(dev); - - if (IS_ERR(data)) - return PTR_ERR(data); - - return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1); -} -static SENSOR_DEVICE_ATTR_RO(in0_alarm, alarm, 0); -static SENSOR_DEVICE_ATTR_RO(in1_alarm, alarm, 1); -static SENSOR_DEVICE_ATTR_RO(in2_alarm, alarm, 2); -static SENSOR_DEVICE_ATTR_RO(in3_alarm, alarm, 3); -static SENSOR_DEVICE_ATTR_RO(in4_alarm, alarm, 8); -static SENSOR_DEVICE_ATTR_RO(in5_alarm, alarm, 9); -static SENSOR_DEVICE_ATTR_RO(temp1_alarm, alarm, 4); -static SENSOR_DEVICE_ATTR_RO(fan1_alarm, alarm, 6); -static SENSOR_DEVICE_ATTR_RO(fan2_alarm, alarm, 7); - -/* vid */ static ssize_t cpu0_vid_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct adm9240_data *data = adm9240_update_device(dev); - - if (IS_ERR(data)) - return PTR_ERR(data); + struct adm9240_data *data = dev_get_drvdata(dev); + unsigned int regval; + int err; + u8 vid; - return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm)); + err = regmap_read(data->regmap, ADM9240_REG_VID_FAN_DIV, ®val); + if (err < 0) + return err; + vid = regval & 0x0f; + err = regmap_read(data->regmap, ADM9240_REG_VID4, ®val); + if (err < 0) + return err; + vid |= (regval & 1) << 4; + return sprintf(buf, "%d\n", vid_from_reg(vid, data->vrm)); } static DEVICE_ATTR_RO(cpu0_vid); -/* analog output */ static ssize_t aout_output_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct adm9240_data *data = adm9240_update_device(dev); + struct adm9240_data *data = dev_get_drvdata(dev); + unsigned int regval; + int err; - if (IS_ERR(data)) - return PTR_ERR(data); + err = regmap_read(data->regmap, ADM9240_REG_ANALOG_OUT, ®val); + if (err) + return err; - return sprintf(buf, "%d\n", AOUT_FROM_REG(data->aout)); + return sprintf(buf, "%d\n", AOUT_FROM_REG(regval)); } static ssize_t aout_output_store(struct device *dev, @@ -639,76 +257,13 @@ static ssize_t aout_output_store(struct device *dev, if (err) return err; - mutex_lock(&data->update_lock); - data->aout = AOUT_TO_REG(val); - err = regmap_write(data->regmap, ADM9240_REG_ANALOG_OUT, data->aout); - mutex_unlock(&data->update_lock); + err = regmap_write(data->regmap, ADM9240_REG_ANALOG_OUT, AOUT_TO_REG(val)); return err < 0 ? err : count; } static DEVICE_ATTR_RW(aout_output); -static ssize_t alarm_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct adm9240_data *data = dev_get_drvdata(dev); - unsigned long val; - int err; - - if (kstrtoul(buf, 10, &val) || val != 0) - return -EINVAL; - - mutex_lock(&data->update_lock); - err = regmap_write(data->regmap, ADM9240_REG_CHASSIS_CLEAR, 0x80); - data->valid = 0; /* Force cache refresh */ - mutex_unlock(&data->update_lock); - if (err < 0) - return err; - dev_dbg(&data->client->dev, "chassis intrusion latch cleared\n"); - - return count; -} -static SENSOR_DEVICE_ATTR_RW(intrusion0_alarm, alarm, 12); - static struct attribute *adm9240_attrs[] = { - &sensor_dev_attr_in0_input.dev_attr.attr, - &sensor_dev_attr_in0_min.dev_attr.attr, - &sensor_dev_attr_in0_max.dev_attr.attr, - &sensor_dev_attr_in0_alarm.dev_attr.attr, - &sensor_dev_attr_in1_input.dev_attr.attr, - &sensor_dev_attr_in1_min.dev_attr.attr, - &sensor_dev_attr_in1_max.dev_attr.attr, - &sensor_dev_attr_in1_alarm.dev_attr.attr, - &sensor_dev_attr_in2_input.dev_attr.attr, - &sensor_dev_attr_in2_min.dev_attr.attr, - &sensor_dev_attr_in2_max.dev_attr.attr, - &sensor_dev_attr_in2_alarm.dev_attr.attr, - &sensor_dev_attr_in3_input.dev_attr.attr, - &sensor_dev_attr_in3_min.dev_attr.attr, - &sensor_dev_attr_in3_max.dev_attr.attr, - &sensor_dev_attr_in3_alarm.dev_attr.attr, - &sensor_dev_attr_in4_input.dev_attr.attr, - &sensor_dev_attr_in4_min.dev_attr.attr, - &sensor_dev_attr_in4_max.dev_attr.attr, - &sensor_dev_attr_in4_alarm.dev_attr.attr, - &sensor_dev_attr_in5_input.dev_attr.attr, - &sensor_dev_attr_in5_min.dev_attr.attr, - &sensor_dev_attr_in5_max.dev_attr.attr, - &sensor_dev_attr_in5_alarm.dev_attr.attr, - &dev_attr_temp1_input.attr, - &sensor_dev_attr_temp1_max.dev_attr.attr, - &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, - &sensor_dev_attr_temp1_alarm.dev_attr.attr, - &sensor_dev_attr_fan1_input.dev_attr.attr, - &sensor_dev_attr_fan1_div.dev_attr.attr, - &sensor_dev_attr_fan1_min.dev_attr.attr, - &sensor_dev_attr_fan1_alarm.dev_attr.attr, - &sensor_dev_attr_fan2_input.dev_attr.attr, - &sensor_dev_attr_fan2_div.dev_attr.attr, - &sensor_dev_attr_fan2_min.dev_attr.attr, - &sensor_dev_attr_fan2_alarm.dev_attr.attr, - &dev_attr_alarms.attr, &dev_attr_aout_output.attr, - &sensor_dev_attr_intrusion0_alarm.dev_attr.attr, &dev_attr_cpu0_vid.attr, NULL }; @@ -730,26 +285,19 @@ static int adm9240_detect(struct i2c_client *new_client, return -ENODEV; /* verify chip: reg address should match i2c address */ - if (i2c_smbus_read_byte_data(new_client, ADM9240_REG_I2C_ADDR) - != address) { - dev_err(&adapter->dev, "detect fail: address match, 0x%02x\n", - address); + if (i2c_smbus_read_byte_data(new_client, ADM9240_REG_I2C_ADDR) != address) return -ENODEV; - } /* check known chip manufacturer */ man_id = i2c_smbus_read_byte_data(new_client, ADM9240_REG_MAN_ID); - if (man_id == 0x23) { + if (man_id == 0x23) name = "adm9240"; - } else if (man_id == 0xda) { + else if (man_id == 0xda) name = "ds1780"; - } else if (man_id == 0x01) { + else if (man_id == 0x01) name = "lm81"; - } else { - dev_err(&adapter->dev, "detect fail: unknown manuf, 0x%02x\n", - man_id); + else return -ENODEV; - } /* successful detect, print chip info */ die_rev = i2c_smbus_read_byte_data(new_client, ADM9240_REG_DIE_REV); @@ -757,13 +305,14 @@ static int adm9240_detect(struct i2c_client *new_client, man_id == 0x23 ? "ADM9240" : man_id == 0xda ? "DS1780" : "LM81", die_rev); - strlcpy(info->type, name, I2C_NAME_SIZE); + strscpy(info->type, name, I2C_NAME_SIZE); return 0; } -static int adm9240_init_client(struct i2c_client *client, struct adm9240_data *data) +static int adm9240_init_client(struct adm9240_data *data) { + unsigned int regval; u8 conf, mode; int err; @@ -777,13 +326,13 @@ static int adm9240_init_client(struct i2c_client *client, struct adm9240_data *d data->vrm = vid_which_vrm(); /* need this to report vid as mV */ - dev_info(&client->dev, "Using VRM: %d.%d\n", data->vrm / 10, - data->vrm % 10); + dev_info(data->dev, "Using VRM: %d.%d\n", data->vrm / 10, + data->vrm % 10); if (conf & 1) { /* measurement cycle running: report state */ - dev_info(&client->dev, "status: config 0x%02x mode %u\n", - conf, mode); + dev_info(data->dev, "status: config 0x%02x mode %u\n", + conf, mode); } else { /* cold start: open limits before starting chip */ int i; @@ -800,13 +349,13 @@ static int adm9240_init_client(struct i2c_client *client, struct adm9240_data *d } for (i = 0; i < 2; i++) { err = regmap_write(data->regmap, - ADM9240_REG_FAN_MIN(i), 255); + ADM9240_REG_FAN_MIN(i), 255); if (err < 0) return err; } for (i = 0; i < 2; i++) { err = regmap_write(data->regmap, - ADM9240_REG_TEMP_MAX(i), 127); + ADM9240_REG_TEMP_MAX(i), 127); if (err < 0) return err; } @@ -816,23 +365,417 @@ static int adm9240_init_client(struct i2c_client *client, struct adm9240_data *d if (err < 0) return err; - dev_info(&client->dev, + dev_info(data->dev, "cold start: config was 0x%02x mode %u\n", conf, mode); } + /* read fan divs */ + err = regmap_read(data->regmap, ADM9240_REG_VID_FAN_DIV, ®val); + if (err < 0) + return err; + data->fan_div[0] = (regval >> 4) & 3; + data->fan_div[1] = (regval >> 6) & 3; return 0; } +static int adm9240_chip_read(struct device *dev, u32 attr, long *val) +{ + struct adm9240_data *data = dev_get_drvdata(dev); + u8 regs[2]; + int err; + + switch (attr) { + case hwmon_chip_alarms: + err = regmap_bulk_read(data->regmap, ADM9240_REG_INT(0), ®s, 2); + if (err < 0) + return err; + *val = regs[0] | regs[1] << 8; + break; + default: + return -EOPNOTSUPP; + } + return 0; +} + +static int adm9240_intrusion_read(struct device *dev, u32 attr, long *val) +{ + struct adm9240_data *data = dev_get_drvdata(dev); + unsigned int regval; + int err; + + switch (attr) { + case hwmon_intrusion_alarm: + err = regmap_read(data->regmap, ADM9240_REG_INT(1), ®val); + if (err < 0) + return err; + *val = !!(regval & BIT(4)); + break; + default: + return -EOPNOTSUPP; + } + return 0; +} + +static int adm9240_intrusion_write(struct device *dev, u32 attr, long val) +{ + struct adm9240_data *data = dev_get_drvdata(dev); + int err; + + switch (attr) { + case hwmon_intrusion_alarm: + if (val) + return -EINVAL; + err = regmap_write(data->regmap, ADM9240_REG_CHASSIS_CLEAR, 0x80); + if (err < 0) + return err; + dev_dbg(data->dev, "chassis intrusion latch cleared\n"); + break; + default: + return -EOPNOTSUPP; + } + return 0; +} + +static int adm9240_in_read(struct device *dev, u32 attr, int channel, long *val) +{ + struct adm9240_data *data = dev_get_drvdata(dev); + unsigned int regval; + int reg; + int err; + + switch (attr) { + case hwmon_in_input: + reg = ADM9240_REG_IN(channel); + break; + case hwmon_in_min: + reg = ADM9240_REG_IN_MIN(channel); + break; + case hwmon_in_max: + reg = ADM9240_REG_IN_MAX(channel); + break; + case hwmon_in_alarm: + if (channel < 4) { + reg = ADM9240_REG_INT(0); + } else { + reg = ADM9240_REG_INT(1); + channel -= 4; + } + err = regmap_read(data->regmap, reg, ®val); + if (err < 0) + return err; + *val = !!(regval & BIT(channel)); + return 0; + default: + return -EOPNOTSUPP; + } + err = regmap_read(data->regmap, reg, ®val); + if (err < 0) + return err; + *val = IN_FROM_REG(regval, channel); + return 0; +} + +static int adm9240_in_write(struct device *dev, u32 attr, int channel, long val) +{ + struct adm9240_data *data = dev_get_drvdata(dev); + int reg; + + switch (attr) { + case hwmon_in_min: + reg = ADM9240_REG_IN_MIN(channel); + break; + case hwmon_in_max: + reg = ADM9240_REG_IN(channel); + break; + default: + return -EOPNOTSUPP; + } + return regmap_write(data->regmap, reg, IN_TO_REG(val, channel)); +} + +static int adm9240_fan_read(struct device *dev, u32 attr, int channel, long *val) +{ + struct adm9240_data *data = dev_get_drvdata(dev); + unsigned int regval; + int err; + + switch (attr) { + case hwmon_fan_input: + err = regmap_read(data->regmap, ADM9240_REG_FAN(channel), ®val); + if (err < 0) + return err; + if (regval == 255 && data->fan_div[channel] < 3) { + /* adjust fan clock divider on overflow */ + err = adm9240_write_fan_div(data, channel, + ++data->fan_div[channel]); + if (err) + return err; + } + *val = FAN_FROM_REG(regval, BIT(data->fan_div[channel])); + break; + case hwmon_fan_div: + *val = BIT(data->fan_div[channel]); + break; + case hwmon_fan_min: + err = regmap_read(data->regmap, ADM9240_REG_FAN_MIN(channel), ®val); + if (err < 0) + return err; + *val = FAN_FROM_REG(regval, BIT(data->fan_div[channel])); + break; + case hwmon_fan_alarm: + err = regmap_read(data->regmap, ADM9240_REG_INT(0), ®val); + if (err < 0) + return err; + *val = !!(regval & BIT(channel + 6)); + break; + default: + return -EOPNOTSUPP; + } + return 0; +} + +static int adm9240_fan_write(struct device *dev, u32 attr, int channel, long val) +{ + struct adm9240_data *data = dev_get_drvdata(dev); + int err; + + switch (attr) { + case hwmon_fan_min: + err = adm9240_fan_min_write(data, channel, val); + if (err < 0) + return err; + break; + default: + return -EOPNOTSUPP; + } + return 0; +} + +static int adm9240_temp_read(struct device *dev, u32 attr, int channel, long *val) +{ + struct adm9240_data *data = dev_get_drvdata(dev); + unsigned int regval; + int err, temp; + + switch (attr) { + case hwmon_temp_input: + err = regmap_read(data->regmap, ADM9240_REG_TEMP, ®val); + if (err < 0) + return err; + temp = regval << 1; + err = regmap_read(data->regmap, ADM9240_REG_TEMP_CONF, ®val); + if (err < 0) + return err; + temp |= regval >> 7; + *val = sign_extend32(temp, 8) * 500; + break; + case hwmon_temp_max: + err = regmap_read(data->regmap, ADM9240_REG_TEMP_MAX(0), ®val); + if (err < 0) + return err; + *val = (s8)regval * 1000; + break; + case hwmon_temp_max_hyst: + err = regmap_read(data->regmap, ADM9240_REG_TEMP_MAX(1), ®val); + if (err < 0) + return err; + *val = (s8)regval * 1000; + break; + case hwmon_temp_alarm: + err = regmap_read(data->regmap, ADM9240_REG_INT(0), ®val); + if (err < 0) + return err; + *val = !!(regval & BIT(4)); + break; + default: + return -EOPNOTSUPP; + } + return 0; +} + +static int adm9240_temp_write(struct device *dev, u32 attr, int channel, long val) +{ + struct adm9240_data *data = dev_get_drvdata(dev); + int reg; + + switch (attr) { + case hwmon_temp_max: + reg = ADM9240_REG_TEMP_MAX(0); + break; + case hwmon_temp_max_hyst: + reg = ADM9240_REG_TEMP_MAX(1); + break; + default: + return -EOPNOTSUPP; + } + return regmap_write(data->regmap, reg, TEMP_TO_REG(val)); +} + +static int adm9240_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, long *val) +{ + switch (type) { + case hwmon_chip: + return adm9240_chip_read(dev, attr, val); + case hwmon_intrusion: + return adm9240_intrusion_read(dev, attr, val); + case hwmon_in: + return adm9240_in_read(dev, attr, channel, val); + case hwmon_fan: + return adm9240_fan_read(dev, attr, channel, val); + case hwmon_temp: + return adm9240_temp_read(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} + +static int adm9240_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, long val) +{ + switch (type) { + case hwmon_intrusion: + return adm9240_intrusion_write(dev, attr, val); + case hwmon_in: + return adm9240_in_write(dev, attr, channel, val); + case hwmon_fan: + return adm9240_fan_write(dev, attr, channel, val); + case hwmon_temp: + return adm9240_temp_write(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} + +static umode_t adm9240_is_visible(const void *_data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + umode_t mode = 0; + + switch (type) { + case hwmon_chip: + switch (attr) { + case hwmon_chip_alarms: + mode = 0444; + break; + default: + break; + } + break; + case hwmon_intrusion: + switch (attr) { + case hwmon_intrusion_alarm: + mode = 0644; + break; + default: + break; + } + break; + case hwmon_temp: + switch (attr) { + case hwmon_temp: + case hwmon_temp_alarm: + mode = 0444; + break; + case hwmon_temp_max: + case hwmon_temp_max_hyst: + mode = 0644; + break; + default: + break; + } + break; + case hwmon_fan: + switch (attr) { + case hwmon_fan_input: + case hwmon_fan_div: + case hwmon_fan_alarm: + mode = 0444; + break; + case hwmon_fan_min: + mode = 0644; + break; + default: + break; + } + break; + case hwmon_in: + switch (attr) { + case hwmon_in_input: + case hwmon_in_alarm: + mode = 0444; + break; + case hwmon_in_min: + case hwmon_in_max: + mode = 0644; + break; + default: + break; + } + break; + default: + break; + } + return mode; +} + +static const struct hwmon_ops adm9240_hwmon_ops = { + .is_visible = adm9240_is_visible, + .read = adm9240_read, + .write = adm9240_write, +}; + +static const struct hwmon_channel_info *adm9240_info[] = { + HWMON_CHANNEL_INFO(chip, HWMON_C_ALARMS), + HWMON_CHANNEL_INFO(intrusion, HWMON_INTRUSION_ALARM), + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | HWMON_T_ALARM), + HWMON_CHANNEL_INFO(in, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_ALARM), + HWMON_CHANNEL_INFO(fan, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_DIV | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_DIV | HWMON_F_ALARM), + NULL +}; + +static const struct hwmon_chip_info adm9240_chip_info = { + .ops = &adm9240_hwmon_ops, + .info = adm9240_info, +}; + +static bool adm9240_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ADM9240_REG_IN(0) ... ADM9240_REG_IN(5): + case ADM9240_REG_FAN(0) ... ADM9240_REG_FAN(1): + case ADM9240_REG_INT(0) ... ADM9240_REG_INT(1): + case ADM9240_REG_TEMP: + case ADM9240_REG_TEMP_CONF: + case ADM9240_REG_VID_FAN_DIV: + case ADM9240_REG_VID4: + case ADM9240_REG_ANALOG_OUT: + return true; + default: + return false; + } +} + static const struct regmap_config adm9240_regmap_config = { .reg_bits = 8, .val_bits = 8, .use_single_read = true, .use_single_write = true, + .volatile_reg = adm9240_volatile_reg, }; -static int adm9240_probe(struct i2c_client *new_client) +static int adm9240_probe(struct i2c_client *client) { - struct device *dev = &new_client->dev; + struct device *dev = &client->dev; struct device *hwmon_dev; struct adm9240_data *data; int err; @@ -841,20 +784,19 @@ static int adm9240_probe(struct i2c_client *new_client) if (!data) return -ENOMEM; - data->client = new_client; + data->dev = dev; mutex_init(&data->update_lock); - data->regmap = devm_regmap_init_i2c(new_client, &adm9240_regmap_config); + data->regmap = devm_regmap_init_i2c(client, &adm9240_regmap_config); if (IS_ERR(data->regmap)) return PTR_ERR(data->regmap); - err = adm9240_init_client(new_client, data); + err = adm9240_init_client(data); if (err < 0) return err; - hwmon_dev = devm_hwmon_device_register_with_groups(dev, - new_client->name, - data, - adm9240_groups); + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, + &adm9240_chip_info, + adm9240_groups); return PTR_ERR_OR_ZERO(hwmon_dev); } diff --git a/drivers/hwmon/amd_energy.c b/drivers/hwmon/amd_energy.c deleted file mode 100644 index a86cc8d6d93d..000000000000 --- a/drivers/hwmon/amd_energy.c +++ /dev/null @@ -1,379 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only - -/* - * Copyright (C) 2020 Advanced Micro Devices, Inc. - */ -#include <asm/cpu_device_id.h> - -#include <linux/bits.h> -#include <linux/cpu.h> -#include <linux/cpumask.h> -#include <linux/delay.h> -#include <linux/device.h> -#include <linux/hwmon.h> -#include <linux/kernel.h> -#include <linux/kthread.h> -#include <linux/list.h> -#include <linux/module.h> -#include <linux/mutex.h> -#include <linux/processor.h> -#include <linux/platform_device.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/topology.h> -#include <linux/types.h> - -#define DRVNAME "amd_energy" - -#define ENERGY_PWR_UNIT_MSR 0xC0010299 -#define ENERGY_CORE_MSR 0xC001029A -#define ENERGY_PKG_MSR 0xC001029B - -#define AMD_ENERGY_UNIT_MASK 0x01F00 -#define AMD_ENERGY_MASK 0xFFFFFFFF - -struct sensor_accumulator { - u64 energy_ctr; - u64 prev_value; -}; - -struct amd_energy_data { - struct hwmon_channel_info energy_info; - const struct hwmon_channel_info *info[2]; - struct hwmon_chip_info chip; - struct task_struct *wrap_accumulate; - /* Lock around the accumulator */ - struct mutex lock; - /* An accumulator for each core and socket */ - struct sensor_accumulator *accums; - unsigned int timeout_ms; - /* Energy Status Units */ - int energy_units; - int nr_cpus; - int nr_socks; - int core_id; - char (*label)[10]; -}; - -static int amd_energy_read_labels(struct device *dev, - enum hwmon_sensor_types type, - u32 attr, int channel, - const char **str) -{ - struct amd_energy_data *data = dev_get_drvdata(dev); - - *str = data->label[channel]; - return 0; -} - -static void get_energy_units(struct amd_energy_data *data) -{ - u64 rapl_units; - - rdmsrl_safe(ENERGY_PWR_UNIT_MSR, &rapl_units); - data->energy_units = (rapl_units & AMD_ENERGY_UNIT_MASK) >> 8; -} - -static void accumulate_delta(struct amd_energy_data *data, - int channel, int cpu, u32 reg) -{ - struct sensor_accumulator *accum; - u64 input; - - mutex_lock(&data->lock); - rdmsrl_safe_on_cpu(cpu, reg, &input); - input &= AMD_ENERGY_MASK; - - accum = &data->accums[channel]; - if (input >= accum->prev_value) - accum->energy_ctr += - input - accum->prev_value; - else - accum->energy_ctr += UINT_MAX - - accum->prev_value + input; - - accum->prev_value = input; - mutex_unlock(&data->lock); -} - -static void read_accumulate(struct amd_energy_data *data) -{ - int sock, scpu, cpu; - - for (sock = 0; sock < data->nr_socks; sock++) { - scpu = cpumask_first_and(cpu_online_mask, - cpumask_of_node(sock)); - - accumulate_delta(data, data->nr_cpus + sock, - scpu, ENERGY_PKG_MSR); - } - - if (data->core_id >= data->nr_cpus) - data->core_id = 0; - - cpu = data->core_id; - if (cpu_online(cpu)) - accumulate_delta(data, cpu, cpu, ENERGY_CORE_MSR); - - data->core_id++; -} - -static void amd_add_delta(struct amd_energy_data *data, int ch, - int cpu, long *val, u32 reg) -{ - struct sensor_accumulator *accum; - u64 input; - - mutex_lock(&data->lock); - rdmsrl_safe_on_cpu(cpu, reg, &input); - input &= AMD_ENERGY_MASK; - - accum = &data->accums[ch]; - if (input >= accum->prev_value) - input += accum->energy_ctr - - accum->prev_value; - else - input += UINT_MAX - accum->prev_value + - accum->energy_ctr; - - /* Energy consumed = (1/(2^ESU) * RAW * 1000000UL) μJoules */ - *val = div64_ul(input * 1000000UL, BIT(data->energy_units)); - - mutex_unlock(&data->lock); -} - -static int amd_energy_read(struct device *dev, - enum hwmon_sensor_types type, - u32 attr, int channel, long *val) -{ - struct amd_energy_data *data = dev_get_drvdata(dev); - u32 reg; - int cpu; - - if (channel >= data->nr_cpus) { - cpu = cpumask_first_and(cpu_online_mask, - cpumask_of_node - (channel - data->nr_cpus)); - reg = ENERGY_PKG_MSR; - } else { - cpu = channel; - if (!cpu_online(cpu)) - return -ENODEV; - - reg = ENERGY_CORE_MSR; - } - amd_add_delta(data, channel, cpu, val, reg); - - return 0; -} - -static umode_t amd_energy_is_visible(const void *_data, - enum hwmon_sensor_types type, - u32 attr, int channel) -{ - return 0440; -} - -static int energy_accumulator(void *p) -{ - struct amd_energy_data *data = (struct amd_energy_data *)p; - unsigned int timeout = data->timeout_ms; - - while (!kthread_should_stop()) { - /* - * Ignoring the conditions such as - * cpu being offline or rdmsr failure - */ - read_accumulate(data); - - set_current_state(TASK_INTERRUPTIBLE); - if (kthread_should_stop()) - break; - - schedule_timeout(msecs_to_jiffies(timeout)); - } - return 0; -} - -static const struct hwmon_ops amd_energy_ops = { - .is_visible = amd_energy_is_visible, - .read = amd_energy_read, - .read_string = amd_energy_read_labels, -}; - -static int amd_create_sensor(struct device *dev, - struct amd_energy_data *data, - enum hwmon_sensor_types type, u32 config) -{ - struct hwmon_channel_info *info = &data->energy_info; - struct sensor_accumulator *accums; - int i, num_siblings, cpus, sockets; - u32 *s_config; - char (*label_l)[10]; - - /* Identify the number of siblings per core */ - num_siblings = ((cpuid_ebx(0x8000001e) >> 8) & 0xff) + 1; - - sockets = num_possible_nodes(); - - /* - * Energy counter register is accessed at core level. - * Hence, filterout the siblings. - */ - cpus = num_present_cpus() / num_siblings; - - s_config = devm_kcalloc(dev, cpus + sockets + 1, - sizeof(u32), GFP_KERNEL); - if (!s_config) - return -ENOMEM; - - accums = devm_kcalloc(dev, cpus + sockets, - sizeof(struct sensor_accumulator), - GFP_KERNEL); - if (!accums) - return -ENOMEM; - - label_l = devm_kcalloc(dev, cpus + sockets, - sizeof(*label_l), GFP_KERNEL); - if (!label_l) - return -ENOMEM; - - info->type = type; - info->config = s_config; - - data->nr_cpus = cpus; - data->nr_socks = sockets; - data->accums = accums; - data->label = label_l; - - for (i = 0; i < cpus + sockets; i++) { - s_config[i] = config; - if (i < cpus) - scnprintf(label_l[i], 10, "Ecore%03u", i); - else - scnprintf(label_l[i], 10, "Esocket%u", (i - cpus)); - } - - s_config[i] = 0; - return 0; -} - -static int amd_energy_probe(struct platform_device *pdev) -{ - struct device *hwmon_dev; - struct amd_energy_data *data; - struct device *dev = &pdev->dev; - int ret; - - data = devm_kzalloc(dev, - sizeof(struct amd_energy_data), GFP_KERNEL); - if (!data) - return -ENOMEM; - - data->chip.ops = &amd_energy_ops; - data->chip.info = data->info; - - dev_set_drvdata(dev, data); - /* Populate per-core energy reporting */ - data->info[0] = &data->energy_info; - ret = amd_create_sensor(dev, data, hwmon_energy, - HWMON_E_INPUT | HWMON_E_LABEL); - if (ret) - return ret; - - mutex_init(&data->lock); - get_energy_units(data); - - hwmon_dev = devm_hwmon_device_register_with_info(dev, DRVNAME, - data, - &data->chip, - NULL); - if (IS_ERR(hwmon_dev)) - return PTR_ERR(hwmon_dev); - - /* - * On a system with peak wattage of 250W - * timeout = 2 ^ 32 / 2 ^ energy_units / 250 secs - */ - data->timeout_ms = 1000 * - BIT(min(28, 31 - data->energy_units)) / 250; - - data->wrap_accumulate = kthread_run(energy_accumulator, data, - "%s", dev_name(hwmon_dev)); - return PTR_ERR_OR_ZERO(data->wrap_accumulate); -} - -static int amd_energy_remove(struct platform_device *pdev) -{ - struct amd_energy_data *data = dev_get_drvdata(&pdev->dev); - - if (data && data->wrap_accumulate) - kthread_stop(data->wrap_accumulate); - - return 0; -} - -static const struct platform_device_id amd_energy_ids[] = { - { .name = DRVNAME, }, - {} -}; -MODULE_DEVICE_TABLE(platform, amd_energy_ids); - -static struct platform_driver amd_energy_driver = { - .probe = amd_energy_probe, - .remove = amd_energy_remove, - .id_table = amd_energy_ids, - .driver = { - .name = DRVNAME, - }, -}; - -static struct platform_device *amd_energy_platdev; - -static const struct x86_cpu_id cpu_ids[] __initconst = { - X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x17, 0x31, NULL), - X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x19, 0x01, NULL), - X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x19, 0x30, NULL), - {} -}; -MODULE_DEVICE_TABLE(x86cpu, cpu_ids); - -static int __init amd_energy_init(void) -{ - int ret; - - if (!x86_match_cpu(cpu_ids)) - return -ENODEV; - - ret = platform_driver_register(&amd_energy_driver); - if (ret) - return ret; - - amd_energy_platdev = platform_device_alloc(DRVNAME, 0); - if (!amd_energy_platdev) { - platform_driver_unregister(&amd_energy_driver); - return -ENOMEM; - } - - ret = platform_device_add(amd_energy_platdev); - if (ret) { - platform_device_put(amd_energy_platdev); - platform_driver_unregister(&amd_energy_driver); - return ret; - } - - return ret; -} - -static void __exit amd_energy_exit(void) -{ - platform_device_unregister(amd_energy_platdev); - platform_driver_unregister(&amd_energy_driver); -} - -module_init(amd_energy_init); -module_exit(amd_energy_exit); - -MODULE_DESCRIPTION("Driver for AMD Energy reporting from RAPL MSR via HWMON interface"); -MODULE_AUTHOR("Naveen Krishna Chatradhi <nchatrad@amd.com>"); -MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c index 28b137eedf2e..c31759794a29 100644 --- a/drivers/hwmon/applesmc.c +++ b/drivers/hwmon/applesmc.c @@ -741,7 +741,7 @@ static void applesmc_idev_poll(struct input_dev *idev) static ssize_t applesmc_name_show(struct device *dev, struct device_attribute *attr, char *buf) { - return snprintf(buf, PAGE_SIZE, "applesmc\n"); + return sysfs_emit(buf, "applesmc\n"); } static ssize_t applesmc_position_show(struct device *dev, @@ -763,8 +763,8 @@ static ssize_t applesmc_position_show(struct device *dev, out: if (ret) return ret; - else - return snprintf(buf, PAGE_SIZE, "(%d,%d,%d)\n", x, y, z); + + return sysfs_emit(buf, "(%d,%d,%d)\n", x, y, z); } static ssize_t applesmc_light_show(struct device *dev, @@ -804,8 +804,8 @@ static ssize_t applesmc_light_show(struct device *dev, out: if (ret) return ret; - else - return snprintf(sysfsbuf, PAGE_SIZE, "(%d,%d)\n", left, right); + + return sysfs_emit(sysfsbuf, "(%d,%d)\n", left, right); } /* Displays sensor key as label */ @@ -814,7 +814,7 @@ static ssize_t applesmc_show_sensor_label(struct device *dev, { const char *key = smcreg.index[to_index(devattr)]; - return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", key); + return sysfs_emit(sysfsbuf, "%s\n", key); } /* Displays degree Celsius * 1000 */ @@ -832,7 +832,7 @@ static ssize_t applesmc_show_temperature(struct device *dev, temp = 250 * (value >> 6); - return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", temp); + return sysfs_emit(sysfsbuf, "%d\n", temp); } static ssize_t applesmc_show_fan_speed(struct device *dev, @@ -851,7 +851,7 @@ static ssize_t applesmc_show_fan_speed(struct device *dev, return ret; speed = ((buffer[0] << 8 | buffer[1]) >> 2); - return snprintf(sysfsbuf, PAGE_SIZE, "%u\n", speed); + return sysfs_emit(sysfsbuf, "%u\n", speed); } static ssize_t applesmc_store_fan_speed(struct device *dev, @@ -891,7 +891,7 @@ static ssize_t applesmc_show_fan_manual(struct device *dev, return ret; manual = ((buffer[0] << 8 | buffer[1]) >> to_index(attr)) & 0x01; - return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", manual); + return sysfs_emit(sysfsbuf, "%d\n", manual); } static ssize_t applesmc_store_fan_manual(struct device *dev, @@ -943,14 +943,14 @@ static ssize_t applesmc_show_fan_position(struct device *dev, if (ret) return ret; - else - return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", buffer+4); + + return sysfs_emit(sysfsbuf, "%s\n", buffer + 4); } static ssize_t applesmc_calibrate_show(struct device *dev, struct device_attribute *attr, char *sysfsbuf) { - return snprintf(sysfsbuf, PAGE_SIZE, "(%d,%d)\n", rest_x, rest_y); + return sysfs_emit(sysfsbuf, "(%d,%d)\n", rest_x, rest_y); } static ssize_t applesmc_calibrate_store(struct device *dev, @@ -992,7 +992,7 @@ static ssize_t applesmc_key_count_show(struct device *dev, count = ((u32)buffer[0]<<24) + ((u32)buffer[1]<<16) + ((u32)buffer[2]<<8) + buffer[3]; - return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", count); + return sysfs_emit(sysfsbuf, "%d\n", count); } static ssize_t applesmc_key_at_index_read_show(struct device *dev, @@ -1020,7 +1020,7 @@ static ssize_t applesmc_key_at_index_data_length_show(struct device *dev, if (IS_ERR(entry)) return PTR_ERR(entry); - return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", entry->len); + return sysfs_emit(sysfsbuf, "%d\n", entry->len); } static ssize_t applesmc_key_at_index_type_show(struct device *dev, @@ -1032,7 +1032,7 @@ static ssize_t applesmc_key_at_index_type_show(struct device *dev, if (IS_ERR(entry)) return PTR_ERR(entry); - return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", entry->type); + return sysfs_emit(sysfsbuf, "%s\n", entry->type); } static ssize_t applesmc_key_at_index_name_show(struct device *dev, @@ -1044,13 +1044,13 @@ static ssize_t applesmc_key_at_index_name_show(struct device *dev, if (IS_ERR(entry)) return PTR_ERR(entry); - return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", entry->key); + return sysfs_emit(sysfsbuf, "%s\n", entry->key); } static ssize_t applesmc_key_at_index_show(struct device *dev, struct device_attribute *attr, char *sysfsbuf) { - return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", key_at_index); + return sysfs_emit(sysfsbuf, "%d\n", key_at_index); } static ssize_t applesmc_key_at_index_store(struct device *dev, diff --git a/drivers/hwmon/corsair-psu.c b/drivers/hwmon/corsair-psu.c index 99494056f4bd..3a5807e4a2ef 100644 --- a/drivers/hwmon/corsair-psu.c +++ b/drivers/hwmon/corsair-psu.c @@ -53,11 +53,17 @@ #define CMD_TIMEOUT_MS 250 #define SECONDS_PER_HOUR (60 * 60) #define SECONDS_PER_DAY (SECONDS_PER_HOUR * 24) +#define RAIL_COUNT 3 /* 3v3 + 5v + 12v */ +#define TEMP_COUNT 2 #define PSU_CMD_SELECT_RAIL 0x00 /* expects length 2 */ -#define PSU_CMD_IN_VOLTS 0x88 /* the rest of the commands expect length 3 */ +#define PSU_CMD_RAIL_VOLTS_HCRIT 0x40 /* the rest of the commands expect length 3 */ +#define PSU_CMD_RAIL_VOLTS_LCRIT 0x44 +#define PSU_CMD_RAIL_AMPS_HCRIT 0x46 +#define PSU_CMD_TEMP_HCRIT 0x4F +#define PSU_CMD_IN_VOLTS 0x88 #define PSU_CMD_IN_AMPS 0x89 -#define PSU_CMD_RAIL_OUT_VOLTS 0x8B +#define PSU_CMD_RAIL_VOLTS 0x8B #define PSU_CMD_RAIL_AMPS 0x8C #define PSU_CMD_TEMP0 0x8D #define PSU_CMD_TEMP1 0x8E @@ -116,30 +122,25 @@ struct corsairpsu_data { u8 *cmd_buffer; char vendor[REPLY_SIZE]; char product[REPLY_SIZE]; + long temp_crit[TEMP_COUNT]; + long in_crit[RAIL_COUNT]; + long in_lcrit[RAIL_COUNT]; + long curr_crit[RAIL_COUNT]; + u8 temp_crit_support; + u8 in_crit_support; + u8 in_lcrit_support; + u8 curr_crit_support; + bool in_curr_cmd_support; /* not all commands are supported on every PSU */ }; /* some values are SMBus LINEAR11 data which need a conversion */ -static int corsairpsu_linear11_to_int(const int val) +static int corsairpsu_linear11_to_int(const u16 val, const int scale) { - int exp = (val & 0xFFFF) >> 0x0B; - int mant = val & 0x7FF; - int i; - - if (exp > 0x0F) - exp -= 0x20; - if (mant > 0x3FF) - mant -= 0x800; - if ((mant & 0x01) == 1) - ++mant; - if (exp < 0) { - for (i = 0; i < -exp; ++i) - mant /= 2; - } else { - for (i = 0; i < exp; ++i) - mant *= 2; - } + const int exp = ((s16)val) >> 11; + const int mant = (((s16)(val & 0x7ff)) << 5) >> 5; + const int result = mant * scale; - return mant; + return (exp >= 0) ? (result << exp) : (result >> -exp); } static int corsairpsu_usb_cmd(struct corsairpsu_data *priv, u8 p0, u8 p1, u8 p2, void *data) @@ -207,7 +208,10 @@ static int corsairpsu_request(struct corsairpsu_data *priv, u8 cmd, u8 rail, voi mutex_lock(&priv->lock); switch (cmd) { - case PSU_CMD_RAIL_OUT_VOLTS: + case PSU_CMD_RAIL_VOLTS_HCRIT: + case PSU_CMD_RAIL_VOLTS_LCRIT: + case PSU_CMD_RAIL_AMPS_HCRIT: + case PSU_CMD_RAIL_VOLTS: case PSU_CMD_RAIL_AMPS: case PSU_CMD_RAIL_WATTS: ret = corsairpsu_usb_cmd(priv, 2, PSU_CMD_SELECT_RAIL, rail, NULL); @@ -243,20 +247,24 @@ static int corsairpsu_get_value(struct corsairpsu_data *priv, u8 cmd, u8 rail, l */ tmp = ((long)data[3] << 24) + (data[2] << 16) + (data[1] << 8) + data[0]; switch (cmd) { + case PSU_CMD_RAIL_VOLTS_HCRIT: + case PSU_CMD_RAIL_VOLTS_LCRIT: + case PSU_CMD_RAIL_AMPS_HCRIT: + case PSU_CMD_TEMP_HCRIT: case PSU_CMD_IN_VOLTS: case PSU_CMD_IN_AMPS: - case PSU_CMD_RAIL_OUT_VOLTS: + case PSU_CMD_RAIL_VOLTS: case PSU_CMD_RAIL_AMPS: case PSU_CMD_TEMP0: case PSU_CMD_TEMP1: - *val = corsairpsu_linear11_to_int(tmp & 0xFFFF) * 1000; + *val = corsairpsu_linear11_to_int(tmp & 0xFFFF, 1000); break; case PSU_CMD_FAN: - *val = corsairpsu_linear11_to_int(tmp & 0xFFFF); + *val = corsairpsu_linear11_to_int(tmp & 0xFFFF, 1); break; case PSU_CMD_RAIL_WATTS: case PSU_CMD_TOTAL_WATTS: - *val = corsairpsu_linear11_to_int(tmp & 0xFFFF) * 1000000; + *val = corsairpsu_linear11_to_int(tmp & 0xFFFF, 1000000); break; case PSU_CMD_TOTAL_UPTIME: case PSU_CMD_UPTIME: @@ -270,75 +278,265 @@ static int corsairpsu_get_value(struct corsairpsu_data *priv, u8 cmd, u8 rail, l return ret; } -static umode_t corsairpsu_hwmon_ops_is_visible(const void *data, enum hwmon_sensor_types type, - u32 attr, int channel) +static void corsairpsu_get_criticals(struct corsairpsu_data *priv) { - if (type == hwmon_temp && (attr == hwmon_temp_input || attr == hwmon_temp_label)) - return 0444; - else if (type == hwmon_fan && (attr == hwmon_fan_input || attr == hwmon_fan_label)) - return 0444; - else if (type == hwmon_power && (attr == hwmon_power_input || attr == hwmon_power_label)) - return 0444; - else if (type == hwmon_in && (attr == hwmon_in_input || attr == hwmon_in_label)) + long tmp; + int rail; + + for (rail = 0; rail < TEMP_COUNT; ++rail) { + if (!corsairpsu_get_value(priv, PSU_CMD_TEMP_HCRIT, rail, &tmp)) { + priv->temp_crit_support |= BIT(rail); + priv->temp_crit[rail] = tmp; + } + } + + for (rail = 0; rail < RAIL_COUNT; ++rail) { + if (!corsairpsu_get_value(priv, PSU_CMD_RAIL_VOLTS_HCRIT, rail, &tmp)) { + priv->in_crit_support |= BIT(rail); + priv->in_crit[rail] = tmp; + } + + if (!corsairpsu_get_value(priv, PSU_CMD_RAIL_VOLTS_LCRIT, rail, &tmp)) { + priv->in_lcrit_support |= BIT(rail); + priv->in_lcrit[rail] = tmp; + } + + if (!corsairpsu_get_value(priv, PSU_CMD_RAIL_AMPS_HCRIT, rail, &tmp)) { + priv->curr_crit_support |= BIT(rail); + priv->curr_crit[rail] = tmp; + } + } +} + +static void corsairpsu_check_cmd_support(struct corsairpsu_data *priv) +{ + long tmp; + + priv->in_curr_cmd_support = !corsairpsu_get_value(priv, PSU_CMD_IN_AMPS, 0, &tmp); +} + +static umode_t corsairpsu_hwmon_temp_is_visible(const struct corsairpsu_data *priv, u32 attr, + int channel) +{ + umode_t res = 0444; + + switch (attr) { + case hwmon_temp_input: + case hwmon_temp_label: + case hwmon_temp_crit: + if (channel > 0 && !(priv->temp_crit_support & BIT(channel - 1))) + res = 0; + break; + default: + break; + } + + return res; +} + +static umode_t corsairpsu_hwmon_fan_is_visible(const struct corsairpsu_data *priv, u32 attr, + int channel) +{ + switch (attr) { + case hwmon_fan_input: + case hwmon_fan_label: return 0444; - else if (type == hwmon_curr && (attr == hwmon_curr_input || attr == hwmon_curr_label)) + default: + return 0; + } +} + +static umode_t corsairpsu_hwmon_power_is_visible(const struct corsairpsu_data *priv, u32 attr, + int channel) +{ + switch (attr) { + case hwmon_power_input: + case hwmon_power_label: return 0444; + default: + return 0; + }; +} - return 0; +static umode_t corsairpsu_hwmon_in_is_visible(const struct corsairpsu_data *priv, u32 attr, + int channel) +{ + umode_t res = 0444; + + switch (attr) { + case hwmon_in_input: + case hwmon_in_label: + case hwmon_in_crit: + if (channel > 0 && !(priv->in_crit_support & BIT(channel - 1))) + res = 0; + break; + case hwmon_in_lcrit: + if (channel > 0 && !(priv->in_lcrit_support & BIT(channel - 1))) + res = 0; + break; + default: + break; + }; + + return res; } -static int corsairpsu_hwmon_ops_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, - int channel, long *val) +static umode_t corsairpsu_hwmon_curr_is_visible(const struct corsairpsu_data *priv, u32 attr, + int channel) { - struct corsairpsu_data *priv = dev_get_drvdata(dev); - int ret; + umode_t res = 0444; + + switch (attr) { + case hwmon_curr_input: + if (channel == 0 && !priv->in_curr_cmd_support) + res = 0; + break; + case hwmon_curr_label: + case hwmon_curr_crit: + if (channel > 0 && !(priv->curr_crit_support & BIT(channel - 1))) + res = 0; + break; + default: + break; + } + + return res; +} + +static umode_t corsairpsu_hwmon_ops_is_visible(const void *data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + const struct corsairpsu_data *priv = data; + + switch (type) { + case hwmon_temp: + return corsairpsu_hwmon_temp_is_visible(priv, attr, channel); + case hwmon_fan: + return corsairpsu_hwmon_fan_is_visible(priv, attr, channel); + case hwmon_power: + return corsairpsu_hwmon_power_is_visible(priv, attr, channel); + case hwmon_in: + return corsairpsu_hwmon_in_is_visible(priv, attr, channel); + case hwmon_curr: + return corsairpsu_hwmon_curr_is_visible(priv, attr, channel); + default: + return 0; + } +} + +static int corsairpsu_hwmon_temp_read(struct corsairpsu_data *priv, u32 attr, int channel, + long *val) +{ + int err = -EOPNOTSUPP; + + switch (attr) { + case hwmon_temp_input: + return corsairpsu_get_value(priv, channel ? PSU_CMD_TEMP1 : PSU_CMD_TEMP0, + channel, val); + case hwmon_temp_crit: + *val = priv->temp_crit[channel]; + err = 0; + break; + default: + break; + } - if (type == hwmon_temp && attr == hwmon_temp_input && channel < 2) { - ret = corsairpsu_get_value(priv, channel ? PSU_CMD_TEMP1 : PSU_CMD_TEMP0, channel, - val); - } else if (type == hwmon_fan && attr == hwmon_fan_input) { - ret = corsairpsu_get_value(priv, PSU_CMD_FAN, 0, val); - } else if (type == hwmon_power && attr == hwmon_power_input) { + return err; +} + +static int corsairpsu_hwmon_power_read(struct corsairpsu_data *priv, u32 attr, int channel, + long *val) +{ + if (attr == hwmon_power_input) { switch (channel) { case 0: - ret = corsairpsu_get_value(priv, PSU_CMD_TOTAL_WATTS, 0, val); - break; + return corsairpsu_get_value(priv, PSU_CMD_TOTAL_WATTS, 0, val); case 1 ... 3: - ret = corsairpsu_get_value(priv, PSU_CMD_RAIL_WATTS, channel - 1, val); - break; + return corsairpsu_get_value(priv, PSU_CMD_RAIL_WATTS, channel - 1, val); default: - return -EOPNOTSUPP; + break; } - } else if (type == hwmon_in && attr == hwmon_in_input) { + } + + return -EOPNOTSUPP; +} + +static int corsairpsu_hwmon_in_read(struct corsairpsu_data *priv, u32 attr, int channel, long *val) +{ + int err = -EOPNOTSUPP; + + switch (attr) { + case hwmon_in_input: switch (channel) { case 0: - ret = corsairpsu_get_value(priv, PSU_CMD_IN_VOLTS, 0, val); - break; + return corsairpsu_get_value(priv, PSU_CMD_IN_VOLTS, 0, val); case 1 ... 3: - ret = corsairpsu_get_value(priv, PSU_CMD_RAIL_OUT_VOLTS, channel - 1, val); - break; + return corsairpsu_get_value(priv, PSU_CMD_RAIL_VOLTS, channel - 1, val); default: - return -EOPNOTSUPP; + break; } - } else if (type == hwmon_curr && attr == hwmon_curr_input) { + break; + case hwmon_in_crit: + *val = priv->in_crit[channel - 1]; + err = 0; + break; + case hwmon_in_lcrit: + *val = priv->in_lcrit[channel - 1]; + err = 0; + break; + } + + return err; +} + +static int corsairpsu_hwmon_curr_read(struct corsairpsu_data *priv, u32 attr, int channel, + long *val) +{ + int err = -EOPNOTSUPP; + + switch (attr) { + case hwmon_curr_input: switch (channel) { case 0: - ret = corsairpsu_get_value(priv, PSU_CMD_IN_AMPS, 0, val); - break; + return corsairpsu_get_value(priv, PSU_CMD_IN_AMPS, 0, val); case 1 ... 3: - ret = corsairpsu_get_value(priv, PSU_CMD_RAIL_AMPS, channel - 1, val); - break; + return corsairpsu_get_value(priv, PSU_CMD_RAIL_AMPS, channel - 1, val); default: - return -EOPNOTSUPP; + break; } - } else { - return -EOPNOTSUPP; + break; + case hwmon_curr_crit: + *val = priv->curr_crit[channel - 1]; + err = 0; + break; + default: + break; } - if (ret < 0) - return ret; + return err; +} - return 0; +static int corsairpsu_hwmon_ops_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, long *val) +{ + struct corsairpsu_data *priv = dev_get_drvdata(dev); + + switch (type) { + case hwmon_temp: + return corsairpsu_hwmon_temp_read(priv, attr, channel, val); + case hwmon_fan: + if (attr == hwmon_fan_input) + return corsairpsu_get_value(priv, PSU_CMD_FAN, 0, val); + return -EOPNOTSUPP; + case hwmon_power: + return corsairpsu_hwmon_power_read(priv, attr, channel, val); + case hwmon_in: + return corsairpsu_hwmon_in_read(priv, attr, channel, val); + case hwmon_curr: + return corsairpsu_hwmon_curr_read(priv, attr, channel, val); + default: + return -EOPNOTSUPP; + } } static int corsairpsu_hwmon_ops_read_string(struct device *dev, enum hwmon_sensor_types type, @@ -374,8 +572,8 @@ static const struct hwmon_channel_info *corsairpsu_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_CRIT, + HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_CRIT), HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_LABEL), HWMON_CHANNEL_INFO(power, @@ -385,14 +583,14 @@ static const struct hwmon_channel_info *corsairpsu_info[] = { 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_I_INPUT | HWMON_I_LABEL), + HWMON_I_INPUT | HWMON_I_LABEL | HWMON_I_LCRIT | HWMON_I_CRIT, + HWMON_I_INPUT | HWMON_I_LABEL | HWMON_I_LCRIT | HWMON_I_CRIT, + HWMON_I_INPUT | HWMON_I_LABEL | HWMON_I_LCRIT | HWMON_I_CRIT), 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_CRIT, + HWMON_C_INPUT | HWMON_C_LABEL | HWMON_C_CRIT, + HWMON_C_INPUT | HWMON_C_LABEL | HWMON_C_CRIT), NULL }; @@ -527,6 +725,9 @@ static int corsairpsu_probe(struct hid_device *hdev, const struct hid_device_id goto fail_and_stop; } + corsairpsu_get_criticals(priv); + corsairpsu_check_cmd_support(priv); + priv->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "corsairpsu", priv, &corsairpsu_chip_info, 0); diff --git a/drivers/hwmon/dell-smm-hwmon.c b/drivers/hwmon/dell-smm-hwmon.c index 73b9db9e3aab..2970892bed82 100644 --- a/drivers/hwmon/dell-smm-hwmon.c +++ b/drivers/hwmon/dell-smm-hwmon.c @@ -1210,6 +1210,14 @@ static struct dmi_system_id i8k_whitelist_fan_control[] __initdata = { }, .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3], }, + { + .ident = "Dell Latitude E7440", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Latitude E7440"), + }, + .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3], + }, { } }; diff --git a/drivers/hwmon/ds1621.c b/drivers/hwmon/ds1621.c index e1d742bfc74c..bf1c4b7ecb40 100644 --- a/drivers/hwmon/ds1621.c +++ b/drivers/hwmon/ds1621.c @@ -326,7 +326,7 @@ static struct attribute *ds1621_attributes[] = { static umode_t ds1621_attribute_visible(struct kobject *kobj, struct attribute *attr, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct ds1621_data *data = dev_get_drvdata(dev); if (attr == &dev_attr_update_interval.attr) diff --git a/drivers/hwmon/ftsteutates.c b/drivers/hwmon/ftsteutates.c index ef88a156efc2..ceffc76a0c51 100644 --- a/drivers/hwmon/ftsteutates.c +++ b/drivers/hwmon/ftsteutates.c @@ -509,7 +509,7 @@ error: /* SysFS structs */ /*****************************************************************************/ -/* Temprature sensors */ +/* Temperature sensors */ static SENSOR_DEVICE_ATTR_RO(temp1_input, temp_value, 0); static SENSOR_DEVICE_ATTR_RO(temp2_input, temp_value, 1); static SENSOR_DEVICE_ATTR_RO(temp3_input, temp_value, 2); @@ -713,7 +713,7 @@ static int fts_detect(struct i2c_client *client, { int val; - /* detection works with revsion greater or equal to 0x2b */ + /* detection works with revision greater or equal to 0x2b */ val = i2c_smbus_read_byte_data(client, FTS_DEVICE_REVISION_REG); if (val < 0x2b) return -ENODEV; diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index 6c684058bfdf..fd47ab4e6892 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -79,7 +79,7 @@ static struct attribute *hwmon_dev_attrs[] = { static umode_t hwmon_dev_name_is_visible(struct kobject *kobj, struct attribute *attr, int n) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); if (to_hwmon_device(dev)->name == NULL) return 0; diff --git a/drivers/hwmon/ina209.c b/drivers/hwmon/ina209.c index f4c7b5f76359..fc3007c3e85c 100644 --- a/drivers/hwmon/ina209.c +++ b/drivers/hwmon/ina209.c @@ -259,7 +259,7 @@ static ssize_t ina209_interval_show(struct device *dev, { struct ina209_data *data = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "%d\n", data->update_interval); + return sysfs_emit(buf, "%d\n", data->update_interval); } /* @@ -343,7 +343,7 @@ static ssize_t ina209_value_show(struct device *dev, return PTR_ERR(data); val = ina209_from_reg(attr->index, data->regs[attr->index]); - return snprintf(buf, PAGE_SIZE, "%ld\n", val); + return sysfs_emit(buf, "%ld\n", val); } static ssize_t ina209_alarm_show(struct device *dev, @@ -363,7 +363,7 @@ static ssize_t ina209_alarm_show(struct device *dev, * All alarms are in the INA209_STATUS register. To avoid a long * switch statement, the mask is passed in attr->index */ - return snprintf(buf, PAGE_SIZE, "%u\n", !!(status & mask)); + return sysfs_emit(buf, "%u\n", !!(status & mask)); } /* Shunt voltage, history, limits, alarms */ diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index ca97f9e931bc..00fc70305a89 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -310,8 +310,7 @@ static ssize_t ina2xx_value_show(struct device *dev, if (err < 0) return err; - return snprintf(buf, PAGE_SIZE, "%d\n", - ina2xx_get_value(data, attr->index, regval)); + return sysfs_emit(buf, "%d\n", ina2xx_get_value(data, attr->index, regval)); } static int ina226_reg_to_alert(struct ina2xx_data *data, u8 bit, u16 regval) @@ -386,7 +385,7 @@ static ssize_t ina226_alert_show(struct device *dev, val = ina226_reg_to_alert(data, attr->index, regval); } - ret = snprintf(buf, PAGE_SIZE, "%d\n", val); + ret = sysfs_emit(buf, "%d\n", val); abort: mutex_unlock(&data->config_lock); return ret; @@ -450,7 +449,7 @@ static ssize_t ina226_alarm_show(struct device *dev, alarm = (regval & BIT(attr->index)) && (regval & INA226_ALERT_FUNCTION_FLAG); - return snprintf(buf, PAGE_SIZE, "%d\n", alarm); + return sysfs_emit(buf, "%d\n", alarm); } /* @@ -481,7 +480,7 @@ static ssize_t ina2xx_shunt_show(struct device *dev, { struct ina2xx_data *data = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "%li\n", data->rshunt); + return sysfs_emit(buf, "%li\n", data->rshunt); } static ssize_t ina2xx_shunt_store(struct device *dev, @@ -537,7 +536,7 @@ static ssize_t ina226_interval_show(struct device *dev, if (status) return status; - return snprintf(buf, PAGE_SIZE, "%d\n", ina226_reg_to_interval(regval)); + return sysfs_emit(buf, "%d\n", ina226_reg_to_interval(regval)); } /* shunt voltage */ diff --git a/drivers/hwmon/ina3221.c b/drivers/hwmon/ina3221.c index d80bd3efcd6d..c602583d19f3 100644 --- a/drivers/hwmon/ina3221.c +++ b/drivers/hwmon/ina3221.c @@ -698,7 +698,7 @@ static ssize_t ina3221_shunt_show(struct device *dev, unsigned int channel = sd_attr->index; struct ina3221_input *input = &ina->inputs[channel]; - return snprintf(buf, PAGE_SIZE, "%d\n", input->shunt_resistor); + return sysfs_emit(buf, "%d\n", input->shunt_resistor); } static ssize_t ina3221_shunt_store(struct device *dev, diff --git a/drivers/hwmon/intel-m10-bmc-hwmon.c b/drivers/hwmon/intel-m10-bmc-hwmon.c index 17d5e6b91c8a..bd7ed2ed3a1e 100644 --- a/drivers/hwmon/intel-m10-bmc-hwmon.c +++ b/drivers/hwmon/intel-m10-bmc-hwmon.c @@ -99,6 +99,50 @@ static const struct hwmon_channel_info *n3000bmc_hinfo[] = { NULL }; +static const struct m10bmc_sdata d5005bmc_temp_tbl[] = { + { 0x100, 0x104, 0x108, 0x10c, 0x0, 500, "Board Inlet Air Temperature" }, + { 0x110, 0x114, 0x118, 0x0, 0x0, 500, "FPGA Core Temperature" }, + { 0x11c, 0x120, 0x124, 0x128, 0x0, 500, "Board Exhaust Air Temperature" }, + { 0x12c, 0x130, 0x134, 0x0, 0x0, 500, "FPGA Transceiver Temperature" }, + { 0x138, 0x13c, 0x140, 0x144, 0x0, 500, "RDIMM0 Temperature" }, + { 0x148, 0x14c, 0x150, 0x154, 0x0, 500, "RDIMM1 Temperature" }, + { 0x158, 0x15c, 0x160, 0x164, 0x0, 500, "RDIMM2 Temperature" }, + { 0x168, 0x16c, 0x170, 0x174, 0x0, 500, "RDIMM3 Temperature" }, + { 0x178, 0x17c, 0x180, 0x0, 0x0, 500, "QSFP0 Temperature" }, + { 0x188, 0x18c, 0x190, 0x0, 0x0, 500, "QSFP1 Temperature" }, + { 0x1a0, 0x1a4, 0x1a8, 0x0, 0x0, 500, "3.3v Temperature" }, + { 0x1bc, 0x1c0, 0x1c4, 0x0, 0x0, 500, "VCCERAM Temperature" }, + { 0x1d8, 0x1dc, 0x1e0, 0x0, 0x0, 500, "VCCR Temperature" }, + { 0x1f4, 0x1f8, 0x1fc, 0x0, 0x0, 500, "VCCT Temperature" }, + { 0x210, 0x214, 0x218, 0x0, 0x0, 500, "1.8v Temperature" }, + { 0x22c, 0x230, 0x234, 0x0, 0x0, 500, "12v Backplane Temperature" }, + { 0x248, 0x24c, 0x250, 0x0, 0x0, 500, "12v AUX Temperature" }, +}; + +static const struct m10bmc_sdata d5005bmc_in_tbl[] = { + { 0x184, 0x0, 0x0, 0x0, 0x0, 1, "QSFP0 Supply Voltage" }, + { 0x194, 0x0, 0x0, 0x0, 0x0, 1, "QSFP1 Supply Voltage" }, + { 0x198, 0x0, 0x0, 0x0, 0x0, 1, "FPGA Core Voltage" }, + { 0x1ac, 0x1b0, 0x1b4, 0x0, 0x0, 1, "3.3v Voltage" }, + { 0x1c8, 0x1cc, 0x1d0, 0x0, 0x0, 1, "VCCERAM Voltage" }, + { 0x1e4, 0x1e8, 0x1ec, 0x0, 0x0, 1, "VCCR Voltage" }, + { 0x200, 0x204, 0x208, 0x0, 0x0, 1, "VCCT Voltage" }, + { 0x21c, 0x220, 0x224, 0x0, 0x0, 1, "1.8v Voltage" }, + { 0x238, 0x0, 0x0, 0x0, 0x23c, 1, "12v Backplane Voltage" }, + { 0x254, 0x0, 0x0, 0x0, 0x258, 1, "12v AUX Voltage" }, +}; + +static const struct m10bmc_sdata d5005bmc_curr_tbl[] = { + { 0x19c, 0x0, 0x0, 0x0, 0x0, 1, "FPGA Core Current" }, + { 0x1b8, 0x0, 0x0, 0x0, 0x0, 1, "3.3v Current" }, + { 0x1d4, 0x0, 0x0, 0x0, 0x0, 1, "VCCERAM Current" }, + { 0x1f0, 0x0, 0x0, 0x0, 0x0, 1, "VCCR Current" }, + { 0x20c, 0x0, 0x0, 0x0, 0x0, 1, "VCCT Current" }, + { 0x228, 0x0, 0x0, 0x0, 0x0, 1, "1.8v Current" }, + { 0x240, 0x244, 0x0, 0x0, 0x0, 1, "12v Backplane Current" }, + { 0x25c, 0x260, 0x0, 0x0, 0x0, 1, "12v AUX Current" }, +}; + static const struct m10bmc_hwmon_board_data n3000bmc_hwmon_bdata = { .tables = { [hwmon_temp] = n3000bmc_temp_tbl, @@ -110,6 +154,80 @@ static const struct m10bmc_hwmon_board_data n3000bmc_hwmon_bdata = { .hinfo = n3000bmc_hinfo, }; +static const struct hwmon_channel_info *d5005bmc_hinfo[] = { + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + 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_MAX | HWMON_I_CRIT | + HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_CRIT | + HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_CRIT | + HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_CRIT | + HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_CRIT | + HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_MIN | 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_MAX | HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_MAX | HWMON_C_LABEL), + NULL +}; + +static const struct m10bmc_hwmon_board_data d5005bmc_hwmon_bdata = { + .tables = { + [hwmon_temp] = d5005bmc_temp_tbl, + [hwmon_in] = d5005bmc_in_tbl, + [hwmon_curr] = d5005bmc_curr_tbl, + }, + + .hinfo = d5005bmc_hinfo, +}; + static umode_t m10bmc_hwmon_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr, int channel) @@ -316,6 +434,10 @@ static const struct platform_device_id intel_m10bmc_hwmon_ids[] = { .name = "n3000bmc-hwmon", .driver_data = (unsigned long)&n3000bmc_hwmon_bdata, }, + { + .name = "d5005bmc-hwmon", + .driver_data = (unsigned long)&d5005bmc_hwmon_bdata, + }, { } }; diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index fac9b5c68a6a..1f93134afcb9 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -1981,7 +1981,7 @@ static SENSOR_DEVICE_ATTR(in9_label, S_IRUGO, show_label, NULL, 3); static umode_t it87_in_is_visible(struct kobject *kobj, struct attribute *attr, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct it87_data *data = dev_get_drvdata(dev); int i = index / 5; /* voltage index */ int a = index % 5; /* attribute index */ @@ -2065,7 +2065,7 @@ static const struct attribute_group it87_group_in = { static umode_t it87_temp_is_visible(struct kobject *kobj, struct attribute *attr, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct it87_data *data = dev_get_drvdata(dev); int i = index / 7; /* temperature index */ int a = index % 7; /* attribute index */ @@ -2126,7 +2126,7 @@ static const struct attribute_group it87_group_temp = { static umode_t it87_is_visible(struct kobject *kobj, struct attribute *attr, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct it87_data *data = dev_get_drvdata(dev); if ((index == 2 || index == 3) && !data->has_vid) @@ -2158,7 +2158,7 @@ static const struct attribute_group it87_group = { static umode_t it87_fan_is_visible(struct kobject *kobj, struct attribute *attr, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct it87_data *data = dev_get_drvdata(dev); int i = index / 5; /* fan index */ int a = index % 5; /* attribute index */ @@ -2229,7 +2229,7 @@ static const struct attribute_group it87_group_fan = { static umode_t it87_pwm_is_visible(struct kobject *kobj, struct attribute *attr, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct it87_data *data = dev_get_drvdata(dev); int i = index / 4; /* pwm index */ int a = index % 4; /* attribute index */ @@ -2290,7 +2290,7 @@ static const struct attribute_group it87_group_pwm = { static umode_t it87_auto_pwm_is_visible(struct kobject *kobj, struct attribute *attr, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct it87_data *data = dev_get_drvdata(dev); int i = index / 11; /* pwm index */ int a = index % 11; /* attribute index */ diff --git a/drivers/hwmon/lineage-pem.c b/drivers/hwmon/lineage-pem.c index c83eb2fd80eb..1109fffa76fb 100644 --- a/drivers/hwmon/lineage-pem.c +++ b/drivers/hwmon/lineage-pem.c @@ -280,7 +280,7 @@ static ssize_t pem_bool_show(struct device *dev, struct device_attribute *da, return PTR_ERR(data); status = data->data_string[attr->nr] & attr->index; - return snprintf(buf, PAGE_SIZE, "%d\n", !!status); + return sysfs_emit(buf, "%d\n", !!status); } static ssize_t pem_data_show(struct device *dev, struct device_attribute *da, @@ -296,7 +296,7 @@ static ssize_t pem_data_show(struct device *dev, struct device_attribute *da, value = pem_get_data(data->data_string, sizeof(data->data_string), attr->index); - return snprintf(buf, PAGE_SIZE, "%ld\n", value); + return sysfs_emit(buf, "%ld\n", value); } static ssize_t pem_input_show(struct device *dev, struct device_attribute *da, @@ -312,7 +312,7 @@ static ssize_t pem_input_show(struct device *dev, struct device_attribute *da, value = pem_get_input(data->input_string, sizeof(data->input_string), attr->index); - return snprintf(buf, PAGE_SIZE, "%ld\n", value); + return sysfs_emit(buf, "%ld\n", value); } static ssize_t pem_fan_show(struct device *dev, struct device_attribute *da, @@ -328,7 +328,7 @@ static ssize_t pem_fan_show(struct device *dev, struct device_attribute *da, value = pem_get_fan(data->fan_speed, sizeof(data->fan_speed), attr->index); - return snprintf(buf, PAGE_SIZE, "%ld\n", value); + return sysfs_emit(buf, "%ld\n", value); } /* Voltages */ diff --git a/drivers/hwmon/lm63.c b/drivers/hwmon/lm63.c index 50f67265c71d..c8f93c5d1ccc 100644 --- a/drivers/hwmon/lm63.c +++ b/drivers/hwmon/lm63.c @@ -931,7 +931,7 @@ static const struct attribute_group lm63_group_extra_lut = { static umode_t lm63_attribute_mode(struct kobject *kobj, struct attribute *attr, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct lm63_data *data = dev_get_drvdata(dev); if (attr == &sensor_dev_attr_temp2_crit.dev_attr.attr diff --git a/drivers/hwmon/ltc2945.c b/drivers/hwmon/ltc2945.c index ba9c868a8641..9adebb59f604 100644 --- a/drivers/hwmon/ltc2945.c +++ b/drivers/hwmon/ltc2945.c @@ -226,7 +226,7 @@ static ssize_t ltc2945_value_show(struct device *dev, value = ltc2945_reg_to_val(dev, attr->index); if (value < 0) return value; - return snprintf(buf, PAGE_SIZE, "%lld\n", value); + return sysfs_emit(buf, "%lld\n", value); } static ssize_t ltc2945_value_store(struct device *dev, @@ -333,7 +333,7 @@ static ssize_t ltc2945_bool_show(struct device *dev, if (fault) /* Clear reported faults in chip register */ regmap_update_bits(regmap, LTC2945_FAULT, attr->index, 0); - return snprintf(buf, PAGE_SIZE, "%d\n", !!fault); + return sysfs_emit(buf, "%d\n", !!fault); } /* Input voltages */ diff --git a/drivers/hwmon/ltc2990.c b/drivers/hwmon/ltc2990.c index 78b191b26bb2..689f788b8563 100644 --- a/drivers/hwmon/ltc2990.c +++ b/drivers/hwmon/ltc2990.c @@ -147,13 +147,13 @@ static ssize_t ltc2990_value_show(struct device *dev, if (unlikely(ret < 0)) return ret; - return snprintf(buf, PAGE_SIZE, "%d\n", value); + return sysfs_emit(buf, "%d\n", value); } static umode_t ltc2990_attrs_visible(struct kobject *kobj, struct attribute *a, int n) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct ltc2990_data *data = dev_get_drvdata(dev); struct device_attribute *da = container_of(a, struct device_attribute, attr); diff --git a/drivers/hwmon/ltc4151.c b/drivers/hwmon/ltc4151.c index 321f54e237bd..13b85367a21f 100644 --- a/drivers/hwmon/ltc4151.c +++ b/drivers/hwmon/ltc4151.c @@ -128,7 +128,7 @@ static ssize_t ltc4151_value_show(struct device *dev, return PTR_ERR(data); value = ltc4151_get_value(data, attr->index); - return snprintf(buf, PAGE_SIZE, "%d\n", value); + return sysfs_emit(buf, "%d\n", value); } /* diff --git a/drivers/hwmon/ltc4215.c b/drivers/hwmon/ltc4215.c index 7cef3cb2962b..1d18c212054f 100644 --- a/drivers/hwmon/ltc4215.c +++ b/drivers/hwmon/ltc4215.c @@ -139,7 +139,7 @@ static ssize_t ltc4215_voltage_show(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(da); const int voltage = ltc4215_get_voltage(dev, attr->index); - return snprintf(buf, PAGE_SIZE, "%d\n", voltage); + return sysfs_emit(buf, "%d\n", voltage); } static ssize_t ltc4215_current_show(struct device *dev, @@ -147,7 +147,7 @@ static ssize_t ltc4215_current_show(struct device *dev, { const unsigned int curr = ltc4215_get_current(dev); - return snprintf(buf, PAGE_SIZE, "%u\n", curr); + return sysfs_emit(buf, "%u\n", curr); } static ssize_t ltc4215_power_show(struct device *dev, @@ -159,7 +159,7 @@ static ssize_t ltc4215_power_show(struct device *dev, /* current in mA * voltage in mV == power in uW */ const unsigned int power = abs(output_voltage * curr); - return snprintf(buf, PAGE_SIZE, "%u\n", power); + return sysfs_emit(buf, "%u\n", power); } static ssize_t ltc4215_alarm_show(struct device *dev, @@ -170,7 +170,7 @@ static ssize_t ltc4215_alarm_show(struct device *dev, const u8 reg = data->regs[LTC4215_STATUS]; const u32 mask = attr->index; - return snprintf(buf, PAGE_SIZE, "%u\n", !!(reg & mask)); + return sysfs_emit(buf, "%u\n", !!(reg & mask)); } /* diff --git a/drivers/hwmon/ltc4222.c b/drivers/hwmon/ltc4222.c index 3efce6d1cb88..d2027ca5c925 100644 --- a/drivers/hwmon/ltc4222.c +++ b/drivers/hwmon/ltc4222.c @@ -94,7 +94,7 @@ static ssize_t ltc4222_value_show(struct device *dev, value = ltc4222_get_value(dev, attr->index); if (value < 0) return value; - return snprintf(buf, PAGE_SIZE, "%d\n", value); + return sysfs_emit(buf, "%d\n", value); } static ssize_t ltc4222_bool_show(struct device *dev, @@ -112,7 +112,7 @@ static ssize_t ltc4222_bool_show(struct device *dev, if (fault) /* Clear reported faults in chip register */ regmap_update_bits(regmap, attr->nr, attr->index, 0); - return snprintf(buf, PAGE_SIZE, "%d\n", !!fault); + return sysfs_emit(buf, "%d\n", !!fault); } /* Voltages */ diff --git a/drivers/hwmon/ltc4260.c b/drivers/hwmon/ltc4260.c index d0beb43abf3f..75e89cec381e 100644 --- a/drivers/hwmon/ltc4260.c +++ b/drivers/hwmon/ltc4260.c @@ -79,7 +79,7 @@ static ssize_t ltc4260_value_show(struct device *dev, value = ltc4260_get_value(dev, attr->index); if (value < 0) return value; - return snprintf(buf, PAGE_SIZE, "%d\n", value); + return sysfs_emit(buf, "%d\n", value); } static ssize_t ltc4260_bool_show(struct device *dev, @@ -98,7 +98,7 @@ static ssize_t ltc4260_bool_show(struct device *dev, if (fault) /* Clear reported faults in chip register */ regmap_update_bits(regmap, LTC4260_FAULT, attr->index, 0); - return snprintf(buf, PAGE_SIZE, "%d\n", !!fault); + return sysfs_emit(buf, "%d\n", !!fault); } /* Voltages */ diff --git a/drivers/hwmon/ltc4261.c b/drivers/hwmon/ltc4261.c index 1dab84b52df5..b81e9c3d297b 100644 --- a/drivers/hwmon/ltc4261.c +++ b/drivers/hwmon/ltc4261.c @@ -130,7 +130,7 @@ static ssize_t ltc4261_value_show(struct device *dev, return PTR_ERR(data); value = ltc4261_get_value(data, attr->index); - return snprintf(buf, PAGE_SIZE, "%d\n", value); + return sysfs_emit(buf, "%d\n", value); } static ssize_t ltc4261_bool_show(struct device *dev, @@ -147,7 +147,7 @@ static ssize_t ltc4261_bool_show(struct device *dev, if (fault) /* Clear reported faults in chip register */ i2c_smbus_write_byte_data(data->client, LTC4261_FAULT, ~fault); - return snprintf(buf, PAGE_SIZE, "%d\n", fault ? 1 : 0); + return sysfs_emit(buf, "%d\n", fault ? 1 : 0); } /* diff --git a/drivers/hwmon/max16065.c b/drivers/hwmon/max16065.c index a26226e7bc37..ae3a6a7bdaa2 100644 --- a/drivers/hwmon/max16065.c +++ b/drivers/hwmon/max16065.c @@ -187,7 +187,7 @@ static ssize_t max16065_alarm_show(struct device *dev, i2c_smbus_write_byte_data(data->client, MAX16065_FAULT(attr2->nr), val); - return snprintf(buf, PAGE_SIZE, "%d\n", !!val); + return sysfs_emit(buf, "%d\n", !!val); } static ssize_t max16065_input_show(struct device *dev, @@ -200,8 +200,8 @@ static ssize_t max16065_input_show(struct device *dev, if (unlikely(adc < 0)) return adc; - return snprintf(buf, PAGE_SIZE, "%d\n", - ADC_TO_MV(adc, data->range[attr->index])); + return sysfs_emit(buf, "%d\n", + ADC_TO_MV(adc, data->range[attr->index])); } static ssize_t max16065_current_show(struct device *dev, @@ -212,8 +212,8 @@ static ssize_t max16065_current_show(struct device *dev, if (unlikely(data->curr_sense < 0)) return data->curr_sense; - return snprintf(buf, PAGE_SIZE, "%d\n", - ADC_TO_CURR(data->curr_sense, data->curr_gain)); + return sysfs_emit(buf, "%d\n", + ADC_TO_CURR(data->curr_sense, data->curr_gain)); } static ssize_t max16065_limit_store(struct device *dev, @@ -249,8 +249,8 @@ static ssize_t max16065_limit_show(struct device *dev, struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(da); struct max16065_data *data = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "%d\n", - data->limit[attr2->nr][attr2->index]); + return sysfs_emit(buf, "%d\n", + data->limit[attr2->nr][attr2->index]); } /* Construct a sensor_device_attribute structure for each register */ @@ -454,7 +454,7 @@ static struct attribute *max16065_max_attributes[] = { static umode_t max16065_basic_is_visible(struct kobject *kobj, struct attribute *a, int n) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct max16065_data *data = dev_get_drvdata(dev); int index = n / 4; @@ -466,7 +466,7 @@ static umode_t max16065_basic_is_visible(struct kobject *kobj, static umode_t max16065_secondary_is_visible(struct kobject *kobj, struct attribute *a, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct max16065_data *data = dev_get_drvdata(dev); if (index >= data->num_adc) diff --git a/drivers/hwmon/max6697.c b/drivers/hwmon/max6697.c index fc3241101178..2895cea54193 100644 --- a/drivers/hwmon/max6697.c +++ b/drivers/hwmon/max6697.c @@ -460,7 +460,7 @@ static DEVICE_ATTR(dummy, 0, NULL, NULL); static umode_t max6697_is_visible(struct kobject *kobj, struct attribute *attr, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct max6697_data *data = dev_get_drvdata(dev); const struct max6697_chip_data *chip = data->chip; int channel = index / 7; /* channel number */ diff --git a/drivers/hwmon/mlxreg-fan.c b/drivers/hwmon/mlxreg-fan.c index ed8d59d4eecb..116681fde33d 100644 --- a/drivers/hwmon/mlxreg-fan.c +++ b/drivers/hwmon/mlxreg-fan.c @@ -67,11 +67,13 @@ * @connected: indicates if tachometer is connected; * @reg: register offset; * @mask: fault mask; + * @prsnt: present register offset; */ struct mlxreg_fan_tacho { bool connected; u32 reg; u32 mask; + u32 prsnt; }; /* @@ -92,6 +94,7 @@ struct mlxreg_fan_pwm { * @regmap: register map of parent device; * @tacho: tachometer data; * @pwm: PWM data; + * @tachos_per_drwr - number of tachometers per drawer; * @samples: minimum allowed samples per pulse; * @divider: divider value for tachometer RPM calculation; * @cooling: cooling device levels; @@ -103,6 +106,7 @@ struct mlxreg_fan { struct mlxreg_core_platform_data *pdata; struct mlxreg_fan_tacho tacho[MLXREG_FAN_MAX_TACHO]; struct mlxreg_fan_pwm pwm; + int tachos_per_drwr; int samples; int divider; u8 cooling_levels[MLXREG_FAN_MAX_STATE + 1]; @@ -123,6 +127,26 @@ mlxreg_fan_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, tacho = &fan->tacho[channel]; switch (attr) { case hwmon_fan_input: + /* + * Check FAN presence: FAN related bit in presence register is one, + * if FAN is physically connected, zero - otherwise. + */ + if (tacho->prsnt && fan->tachos_per_drwr) { + err = regmap_read(fan->regmap, tacho->prsnt, ®val); + if (err) + return err; + + /* + * Map channel to presence bit - drawer can be equipped with + * one or few FANs, while presence is indicated per drawer. + */ + if (BIT(channel / fan->tachos_per_drwr) & regval) { + /* FAN is not connected - return zero for FAN speed. */ + *val = 0; + return 0; + } + } + err = regmap_read(fan->regmap, tacho->reg, ®val); if (err) return err; @@ -389,8 +413,8 @@ static int mlxreg_fan_config(struct mlxreg_fan *fan, struct mlxreg_core_platform_data *pdata) { struct mlxreg_core_data *data = pdata->data; + int tacho_num = 0, tacho_avail = 0, i; bool configured = false; - int tacho_num = 0, i; int err; fan->samples = MLXREG_FAN_TACHO_SAMPLES_PER_PULSE_DEF; @@ -415,7 +439,9 @@ static int mlxreg_fan_config(struct mlxreg_fan *fan, fan->tacho[tacho_num].reg = data->reg; fan->tacho[tacho_num].mask = data->mask; + fan->tacho[tacho_num].prsnt = data->reg_prsnt; fan->tacho[tacho_num++].connected = true; + tacho_avail++; } else if (strnstr(data->label, "pwm", sizeof(data->label))) { if (fan->pwm.connected) { dev_err(fan->dev, "duplicate pwm entry: %s\n", @@ -453,6 +479,29 @@ static int mlxreg_fan_config(struct mlxreg_fan *fan, } } + if (pdata->capability) { + int drwr_avail; + u32 regval; + + /* Obtain the number of FAN drawers, supported by system. */ + err = regmap_read(fan->regmap, pdata->capability, ®val); + if (err) { + dev_err(fan->dev, "Failed to query capability register 0x%08x\n", + pdata->capability); + return err; + } + + drwr_avail = hweight32(regval); + if (!tacho_avail || !drwr_avail || tacho_avail < drwr_avail) { + dev_err(fan->dev, "Configuration is invalid: drawers num %d tachos num %d\n", + drwr_avail, tacho_avail); + return -EINVAL; + } + + /* Set the number of tachometers per one drawer. */ + fan->tachos_per_drwr = tacho_avail / drwr_avail; + } + /* Init cooling levels per PWM state. */ for (i = 0; i < MLXREG_FAN_SPEED_MIN_LEVEL; i++) fan->cooling_levels[i] = MLXREG_FAN_SPEED_MIN_LEVEL; diff --git a/drivers/hwmon/nct6683.c b/drivers/hwmon/nct6683.c index a23047a3bfe2..35f8635dc7f3 100644 --- a/drivers/hwmon/nct6683.c +++ b/drivers/hwmon/nct6683.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* * nct6683 - Driver for the hardware monitoring functionality of - * Nuvoton NCT6683D/NCT6687D eSIO + * Nuvoton NCT6683D/NCT6686D/NCT6687D eSIO * * Copyright (C) 2013 Guenter Roeck <linux@roeck-us.net> * @@ -12,6 +12,7 @@ * * Chip #vin #fan #pwm #temp chip ID * nct6683d 21(1) 16 8 32(1) 0xc730 + * nct6686d 21(1) 16 8 32(1) 0xd440 * nct6687d 21(1) 16 8 32(1) 0xd590 * * Notes: @@ -33,7 +34,7 @@ #include <linux/platform_device.h> #include <linux/slab.h> -enum kinds { nct6683, nct6687 }; +enum kinds { nct6683, nct6686, nct6687 }; static bool force; module_param(force, bool, 0); @@ -41,11 +42,13 @@ MODULE_PARM_DESC(force, "Set to one to enable support for unknown vendors"); static const char * const nct6683_device_names[] = { "nct6683", + "nct6686", "nct6687", }; static const char * const nct6683_chip_names[] = { "NCT6683D", + "NCT6686D", "NCT6687D", }; @@ -66,6 +69,7 @@ static const char * const nct6683_chip_names[] = { #define SIO_NCT6681_ID 0xb270 /* for later */ #define SIO_NCT6683_ID 0xc730 +#define SIO_NCT6686_ID 0xd440 #define SIO_NCT6687_ID 0xd590 #define SIO_ID_MASK 0xFFF0 @@ -488,17 +492,6 @@ static inline long in_from_reg(u16 reg, u8 src) return reg * scale; } -static inline u16 in_to_reg(u32 val, u8 src) -{ - int scale = 16; - - if (src == MON_SRC_VCC || src == MON_SRC_VSB || src == MON_SRC_AVSB || - src == MON_SRC_VBAT) - scale <<= 1; - - return clamp_val(DIV_ROUND_CLOSEST(val, scale), 0, 127); -} - static u16 nct6683_read(struct nct6683_data *data, u16 reg) { int res; @@ -1362,6 +1355,9 @@ static int __init nct6683_find(int sioaddr, struct nct6683_sio_data *sio_data) case SIO_NCT6683_ID: sio_data->kind = nct6683; break; + case SIO_NCT6686_ID: + sio_data->kind = nct6686; + break; case SIO_NCT6687_ID: sio_data->kind = nct6687; break; diff --git a/drivers/hwmon/nzxt-kraken2.c b/drivers/hwmon/nzxt-kraken2.c new file mode 100644 index 000000000000..89f7ea4f42d4 --- /dev/null +++ b/drivers/hwmon/nzxt-kraken2.c @@ -0,0 +1,234 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * nzxt-kraken2.c - hwmon driver for NZXT Kraken X42/X52/X62/X72 coolers + * + * The device asynchronously sends HID reports (with id 0x04) twice a second to + * communicate current fan speed, pump speed and coolant temperature. The + * device does not respond to Get_Report requests for this status report. + * + * Copyright 2019-2021 Jonas Malaco <jonas@protocubo.io> + */ + +#include <asm/unaligned.h> +#include <linux/hid.h> +#include <linux/hwmon.h> +#include <linux/jiffies.h> +#include <linux/module.h> + +#define STATUS_REPORT_ID 0x04 +#define STATUS_VALIDITY 2 /* seconds; equivalent to 4 missed updates */ + +static const char *const kraken2_temp_label[] = { + "Coolant", +}; + +static const char *const kraken2_fan_label[] = { + "Fan", + "Pump", +}; + +struct kraken2_priv_data { + struct hid_device *hid_dev; + struct device *hwmon_dev; + s32 temp_input[1]; + u16 fan_input[2]; + unsigned long updated; /* jiffies */ +}; + +static umode_t kraken2_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + return 0444; +} + +static int kraken2_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct kraken2_priv_data *priv = dev_get_drvdata(dev); + + if (time_after(jiffies, priv->updated + STATUS_VALIDITY * HZ)) + return -ENODATA; + + switch (type) { + case hwmon_temp: + *val = priv->temp_input[channel]; + break; + case hwmon_fan: + *val = priv->fan_input[channel]; + break; + default: + return -EOPNOTSUPP; /* unreachable */ + } + + return 0; +} + +static int kraken2_read_string(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) +{ + switch (type) { + case hwmon_temp: + *str = kraken2_temp_label[channel]; + break; + case hwmon_fan: + *str = kraken2_fan_label[channel]; + break; + default: + return -EOPNOTSUPP; /* unreachable */ + } + return 0; +} + +static const struct hwmon_ops kraken2_hwmon_ops = { + .is_visible = kraken2_is_visible, + .read = kraken2_read, + .read_string = kraken2_read_string, +}; + +static const struct hwmon_channel_info *kraken2_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), + NULL +}; + +static const struct hwmon_chip_info kraken2_chip_info = { + .ops = &kraken2_hwmon_ops, + .info = kraken2_info, +}; + +static int kraken2_raw_event(struct hid_device *hdev, + struct hid_report *report, u8 *data, int size) +{ + struct kraken2_priv_data *priv; + + if (size < 7 || report->id != STATUS_REPORT_ID) + return 0; + + priv = hid_get_drvdata(hdev); + + /* + * The fractional byte of the coolant temperature has been observed to + * be in the interval [1,9], but some of these steps are also + * consistently skipped for certain integer parts. + * + * For the lack of a better idea, assume that the resolution is 0.1°C, + * and that the missing steps are artifacts of how the firmware + * processes the raw sensor data. + */ + priv->temp_input[0] = data[1] * 1000 + data[2] * 100; + + priv->fan_input[0] = get_unaligned_be16(data + 3); + priv->fan_input[1] = get_unaligned_be16(data + 5); + + priv->updated = jiffies; + + return 0; +} + +static int kraken2_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + struct kraken2_priv_data *priv; + int ret; + + priv = devm_kzalloc(&hdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->hid_dev = hdev; + hid_set_drvdata(hdev, priv); + + /* + * Initialize ->updated to STATUS_VALIDITY seconds in the past, making + * the initial empty data invalid for kraken2_read without the need for + * a special case there. + */ + priv->updated = jiffies - STATUS_VALIDITY * HZ; + + ret = hid_parse(hdev); + if (ret) { + hid_err(hdev, "hid parse failed with %d\n", ret); + return ret; + } + + /* + * Enable hidraw so existing user-space tools can continue to work. + */ + ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW); + if (ret) { + hid_err(hdev, "hid hw start failed with %d\n", ret); + goto fail_and_stop; + } + + ret = hid_hw_open(hdev); + if (ret) { + hid_err(hdev, "hid hw open failed with %d\n", ret); + goto fail_and_close; + } + + priv->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "kraken2", + priv, &kraken2_chip_info, + NULL); + if (IS_ERR(priv->hwmon_dev)) { + ret = PTR_ERR(priv->hwmon_dev); + hid_err(hdev, "hwmon registration failed with %d\n", ret); + goto fail_and_close; + } + + return 0; + +fail_and_close: + hid_hw_close(hdev); +fail_and_stop: + hid_hw_stop(hdev); + return ret; +} + +static void kraken2_remove(struct hid_device *hdev) +{ + struct kraken2_priv_data *priv = hid_get_drvdata(hdev); + + hwmon_device_unregister(priv->hwmon_dev); + + hid_hw_close(hdev); + hid_hw_stop(hdev); +} + +static const struct hid_device_id kraken2_table[] = { + { HID_USB_DEVICE(0x1e71, 0x170e) }, /* NZXT Kraken X42/X52/X62/X72 */ + { } +}; + +MODULE_DEVICE_TABLE(hid, kraken2_table); + +static struct hid_driver kraken2_driver = { + .name = "nzxt-kraken2", + .id_table = kraken2_table, + .probe = kraken2_probe, + .remove = kraken2_remove, + .raw_event = kraken2_raw_event, +}; + +static int __init kraken2_init(void) +{ + return hid_register_driver(&kraken2_driver); +} + +static void __exit kraken2_exit(void) +{ + hid_unregister_driver(&kraken2_driver); +} + +/* + * When compiled into the kernel, initialize after the hid bus. + */ +late_initcall(kraken2_init); +module_exit(kraken2_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jonas Malaco <jonas@protocubo.io>"); +MODULE_DESCRIPTION("Hwmon driver for NZXT Kraken X42/X52/X62/X72 coolers"); diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c index 7a5e539b567b..f1ac153d0b56 100644 --- a/drivers/hwmon/occ/common.c +++ b/drivers/hwmon/occ/common.c @@ -261,7 +261,7 @@ static ssize_t occ_show_temp_1(struct device *dev, return -EINVAL; } - return snprintf(buf, PAGE_SIZE - 1, "%u\n", val); + return sysfs_emit(buf, "%u\n", val); } static ssize_t occ_show_temp_2(struct device *dev, @@ -312,7 +312,7 @@ static ssize_t occ_show_temp_2(struct device *dev, return -EINVAL; } - return snprintf(buf, PAGE_SIZE - 1, "%u\n", val); + return sysfs_emit(buf, "%u\n", val); } static ssize_t occ_show_temp_10(struct device *dev, @@ -366,7 +366,7 @@ static ssize_t occ_show_temp_10(struct device *dev, return -EINVAL; } - return snprintf(buf, PAGE_SIZE - 1, "%u\n", val); + return sysfs_emit(buf, "%u\n", val); } static ssize_t occ_show_freq_1(struct device *dev, @@ -396,7 +396,7 @@ static ssize_t occ_show_freq_1(struct device *dev, return -EINVAL; } - return snprintf(buf, PAGE_SIZE - 1, "%u\n", val); + return sysfs_emit(buf, "%u\n", val); } static ssize_t occ_show_freq_2(struct device *dev, @@ -426,7 +426,7 @@ static ssize_t occ_show_freq_2(struct device *dev, return -EINVAL; } - return snprintf(buf, PAGE_SIZE - 1, "%u\n", val); + return sysfs_emit(buf, "%u\n", val); } static ssize_t occ_show_power_1(struct device *dev, @@ -465,7 +465,7 @@ static ssize_t occ_show_power_1(struct device *dev, return -EINVAL; } - return snprintf(buf, PAGE_SIZE - 1, "%llu\n", val); + return sysfs_emit(buf, "%llu\n", val); } static u64 occ_get_powr_avg(u64 *accum, u32 *samples) @@ -494,9 +494,9 @@ static ssize_t occ_show_power_2(struct device *dev, switch (sattr->nr) { case 0: - return snprintf(buf, PAGE_SIZE - 1, "%u_%u_%u\n", - get_unaligned_be32(&power->sensor_id), - power->function_id, power->apss_channel); + return sysfs_emit(buf, "%u_%u_%u\n", + get_unaligned_be32(&power->sensor_id), + power->function_id, power->apss_channel); case 1: val = occ_get_powr_avg(&power->accumulator, &power->update_tag); @@ -512,7 +512,7 @@ static ssize_t occ_show_power_2(struct device *dev, return -EINVAL; } - return snprintf(buf, PAGE_SIZE - 1, "%llu\n", val); + return sysfs_emit(buf, "%llu\n", val); } static ssize_t occ_show_power_a0(struct device *dev, @@ -533,8 +533,8 @@ static ssize_t occ_show_power_a0(struct device *dev, switch (sattr->nr) { case 0: - return snprintf(buf, PAGE_SIZE - 1, "%u_system\n", - get_unaligned_be32(&power->sensor_id)); + return sysfs_emit(buf, "%u_system\n", + get_unaligned_be32(&power->sensor_id)); case 1: val = occ_get_powr_avg(&power->system.accumulator, &power->system.update_tag); @@ -547,8 +547,8 @@ static ssize_t occ_show_power_a0(struct device *dev, val = get_unaligned_be16(&power->system.value) * 1000000ULL; break; case 4: - return snprintf(buf, PAGE_SIZE - 1, "%u_proc\n", - get_unaligned_be32(&power->sensor_id)); + return sysfs_emit(buf, "%u_proc\n", + get_unaligned_be32(&power->sensor_id)); case 5: val = occ_get_powr_avg(&power->proc.accumulator, &power->proc.update_tag); @@ -561,8 +561,8 @@ static ssize_t occ_show_power_a0(struct device *dev, val = get_unaligned_be16(&power->proc.value) * 1000000ULL; break; case 8: - return snprintf(buf, PAGE_SIZE - 1, "%u_vdd\n", - get_unaligned_be32(&power->sensor_id)); + return sysfs_emit(buf, "%u_vdd\n", + get_unaligned_be32(&power->sensor_id)); case 9: val = occ_get_powr_avg(&power->vdd.accumulator, &power->vdd.update_tag); @@ -575,8 +575,8 @@ static ssize_t occ_show_power_a0(struct device *dev, val = get_unaligned_be16(&power->vdd.value) * 1000000ULL; break; case 12: - return snprintf(buf, PAGE_SIZE - 1, "%u_vdn\n", - get_unaligned_be32(&power->sensor_id)); + return sysfs_emit(buf, "%u_vdn\n", + get_unaligned_be32(&power->sensor_id)); case 13: val = occ_get_powr_avg(&power->vdn.accumulator, &power->vdn.update_tag); @@ -592,7 +592,7 @@ static ssize_t occ_show_power_a0(struct device *dev, return -EINVAL; } - return snprintf(buf, PAGE_SIZE - 1, "%llu\n", val); + return sysfs_emit(buf, "%llu\n", val); } static ssize_t occ_show_caps_1_2(struct device *dev, @@ -613,7 +613,7 @@ static ssize_t occ_show_caps_1_2(struct device *dev, switch (sattr->nr) { case 0: - return snprintf(buf, PAGE_SIZE - 1, "system\n"); + return sysfs_emit(buf, "system\n"); case 1: val = get_unaligned_be16(&caps->cap) * 1000000ULL; break; @@ -642,7 +642,7 @@ static ssize_t occ_show_caps_1_2(struct device *dev, return -EINVAL; } - return snprintf(buf, PAGE_SIZE - 1, "%llu\n", val); + return sysfs_emit(buf, "%llu\n", val); } static ssize_t occ_show_caps_3(struct device *dev, @@ -663,7 +663,7 @@ static ssize_t occ_show_caps_3(struct device *dev, switch (sattr->nr) { case 0: - return snprintf(buf, PAGE_SIZE - 1, "system\n"); + return sysfs_emit(buf, "system\n"); case 1: val = get_unaligned_be16(&caps->cap) * 1000000ULL; break; @@ -689,7 +689,7 @@ static ssize_t occ_show_caps_3(struct device *dev, return -EINVAL; } - return snprintf(buf, PAGE_SIZE - 1, "%llu\n", val); + return sysfs_emit(buf, "%llu\n", val); } static ssize_t occ_store_caps_user(struct device *dev, @@ -732,21 +732,22 @@ static ssize_t occ_show_extended(struct device *dev, switch (sattr->nr) { case 0: - if (extn->flags & EXTN_FLAG_SENSOR_ID) - rc = snprintf(buf, PAGE_SIZE - 1, "%u", - get_unaligned_be32(&extn->sensor_id)); - else - rc = snprintf(buf, PAGE_SIZE - 1, "%02x%02x%02x%02x\n", - extn->name[0], extn->name[1], - extn->name[2], extn->name[3]); + if (extn->flags & EXTN_FLAG_SENSOR_ID) { + rc = sysfs_emit(buf, "%u", + get_unaligned_be32(&extn->sensor_id)); + } else { + rc = sysfs_emit(buf, "%02x%02x%02x%02x\n", + extn->name[0], extn->name[1], + extn->name[2], extn->name[3]); + } break; case 1: - rc = snprintf(buf, PAGE_SIZE - 1, "%02x\n", extn->flags); + rc = sysfs_emit(buf, "%02x\n", extn->flags); break; case 2: - rc = snprintf(buf, PAGE_SIZE - 1, "%02x%02x%02x%02x%02x%02x\n", - extn->data[0], extn->data[1], extn->data[2], - extn->data[3], extn->data[4], extn->data[5]); + rc = sysfs_emit(buf, "%02x%02x%02x%02x%02x%02x\n", + extn->data[0], extn->data[1], extn->data[2], + extn->data[3], extn->data[4], extn->data[5]); break; default: return -EINVAL; diff --git a/drivers/hwmon/occ/sysfs.c b/drivers/hwmon/occ/sysfs.c index c73be0747e66..03b16abef67f 100644 --- a/drivers/hwmon/occ/sysfs.c +++ b/drivers/hwmon/occ/sysfs.c @@ -67,7 +67,7 @@ static ssize_t occ_sysfs_show(struct device *dev, return -EINVAL; } - return snprintf(buf, PAGE_SIZE - 1, "%d\n", val); + return sysfs_emit(buf, "%d\n", val); } static ssize_t occ_error_show(struct device *dev, @@ -77,7 +77,7 @@ static ssize_t occ_error_show(struct device *dev, occ_update_response(occ); - return snprintf(buf, PAGE_SIZE - 1, "%d\n", occ->error); + return sysfs_emit(buf, "%d\n", occ->error); } static SENSOR_DEVICE_ATTR(occ_master, 0444, occ_sysfs_show, NULL, 0); diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 32d2fc850621..37a5c39784fa 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -56,6 +56,25 @@ config SENSORS_BEL_PFE This driver can also be built as a module. If so, the module will be called bel-pfe. +config SENSORS_BPA_RS600 + tristate "BluTek BPA-RS600 Power Supplies" + help + If you say yes here you get hardware monitoring support for BluTek + BPA-RS600 Power Supplies. + + This driver can also be built as a module. If so, the module will + be called bpa-rs600. + +config SENSORS_FSP_3Y + tristate "FSP/3Y-Power power supplies" + help + If you say yes here you get hardware monitoring support for + FSP/3Y-Power hot-swap power supplies. + Supported models: YH-5151E, YM-2151E + + This driver can also be built as a module. If so, the module will + be called fsp-3y. + config SENSORS_IBM_CFFPS tristate "IBM Common Form Factor Power Supply" depends on LEDS_CLASS @@ -84,6 +103,15 @@ config SENSORS_IR35221 This driver can also be built as a module. If so, the module will be called ir35221. +config SENSORS_IR36021 + tristate "Infineon IR36021" + help + If you say yes here you get hardware monitoring support for Infineon + IR36021. + + This driver can also be built as a module. If so, the module will + be called ir36021. + config SENSORS_IR38064 tristate "Infineon IR38064" help @@ -148,6 +176,15 @@ config SENSORS_LTC3815 This driver can also be built as a module. If so, the module will be called ltc3815. +config SENSORS_MAX15301 + tristate "Maxim MAX15301" + help + If you say yes here you get hardware monitoring support for Maxim + MAX15301, as well as for Flex BMR461. + + This driver can also be built as a module. If so, the module will + be called max15301. + config SENSORS_MAX16064 tristate "Maxim MAX16064" help @@ -247,6 +284,16 @@ config SENSORS_Q54SJ108A2 This driver can also be built as a module. If so, the module will be called q54sj108a2. +config SENSORS_STPDDC60 + tristate "ST STPDDC60" + help + If you say yes here you get hardware monitoring support for ST + STPDDC60 Universal Digital Multicell Controller, as well as for + Flex BMR481. + + This driver can also be built as a module. If so, the module will + be called stpddc60. + config SENSORS_TPS40422 tristate "TI TPS40422" help @@ -257,10 +304,10 @@ config SENSORS_TPS40422 be called tps40422. config SENSORS_TPS53679 - tristate "TI TPS53647, TPS53667, TPS53679, TPS53681, TPS53688" + tristate "TI TPS53647, TPS53667, TPS53676, TPS53679, TPS53681, TPS53688" help If you say yes here you get hardware monitoring support for TI - TPS53647, TPS53667, TPS53679, TPS53681, and TPS53688. + TPS53647, TPS53667, TPS53676, TPS53679, TPS53681, and TPS53688. This driver can also be built as a module. If so, the module will be called tps53679. diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile index 6a4ba0fdc1db..f8dcc27cd56a 100644 --- a/drivers/hwmon/pmbus/Makefile +++ b/drivers/hwmon/pmbus/Makefile @@ -8,15 +8,19 @@ obj-$(CONFIG_SENSORS_PMBUS) += pmbus.o obj-$(CONFIG_SENSORS_ADM1266) += adm1266.o obj-$(CONFIG_SENSORS_ADM1275) += adm1275.o obj-$(CONFIG_SENSORS_BEL_PFE) += bel-pfe.o +obj-$(CONFIG_SENSORS_BPA_RS600) += bpa-rs600.o +obj-$(CONFIG_SENSORS_FSP_3Y) += fsp-3y.o obj-$(CONFIG_SENSORS_IBM_CFFPS) += ibm-cffps.o obj-$(CONFIG_SENSORS_INSPUR_IPSPS) += inspur-ipsps.o obj-$(CONFIG_SENSORS_IR35221) += ir35221.o +obj-$(CONFIG_SENSORS_IR36021) += ir36021.o obj-$(CONFIG_SENSORS_IR38064) += ir38064.o obj-$(CONFIG_SENSORS_IRPS5401) += irps5401.o obj-$(CONFIG_SENSORS_ISL68137) += isl68137.o obj-$(CONFIG_SENSORS_LM25066) += lm25066.o obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o obj-$(CONFIG_SENSORS_LTC3815) += ltc3815.o +obj-$(CONFIG_SENSORS_MAX15301) += max15301.o obj-$(CONFIG_SENSORS_MAX16064) += max16064.o obj-$(CONFIG_SENSORS_MAX16601) += max16601.o obj-$(CONFIG_SENSORS_MAX20730) += max20730.o @@ -28,6 +32,7 @@ obj-$(CONFIG_SENSORS_MP2975) += mp2975.o obj-$(CONFIG_SENSORS_PM6764TR) += pm6764tr.o obj-$(CONFIG_SENSORS_PXE1610) += pxe1610.o obj-$(CONFIG_SENSORS_Q54SJ108A2) += q54sj108a2.o +obj-$(CONFIG_SENSORS_STPDDC60) += stpddc60.o obj-$(CONFIG_SENSORS_TPS40422) += tps40422.o obj-$(CONFIG_SENSORS_TPS53679) += tps53679.o obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c index 4d2e4ddcfbfd..ec5f932fc6f0 100644 --- a/drivers/hwmon/pmbus/adm1266.c +++ b/drivers/hwmon/pmbus/adm1266.c @@ -510,3 +510,4 @@ module_i2c_driver(adm1266_driver); MODULE_AUTHOR("Alexandru Tachici <alexandru.tachici@analog.com>"); MODULE_DESCRIPTION("PMBus driver for Analog Devices ADM1266"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c index 38a6515b0763..980a3850b2f3 100644 --- a/drivers/hwmon/pmbus/adm1275.c +++ b/drivers/hwmon/pmbus/adm1275.c @@ -805,3 +805,4 @@ module_i2c_driver(adm1275_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for Analog Devices ADM1275 and compatibles"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/bel-pfe.c b/drivers/hwmon/pmbus/bel-pfe.c index aed7542d7ce5..4100eefb7ac3 100644 --- a/drivers/hwmon/pmbus/bel-pfe.c +++ b/drivers/hwmon/pmbus/bel-pfe.c @@ -129,3 +129,4 @@ module_i2c_driver(pfe_pmbus_driver); MODULE_AUTHOR("Tao Ren <rentao.bupt@gmail.com>"); MODULE_DESCRIPTION("PMBus driver for BEL PFE Family Power Supplies"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/bpa-rs600.c b/drivers/hwmon/pmbus/bpa-rs600.c new file mode 100644 index 000000000000..f6558ee9dec3 --- /dev/null +++ b/drivers/hwmon/pmbus/bpa-rs600.c @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for BluTek BPA-RS600 Power Supplies + * + * Copyright 2021 Allied Telesis Labs + */ + +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#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 + +static int bpa_rs600_read_byte_data(struct i2c_client *client, int page, int reg) +{ + int ret; + + if (page > 0) + return -ENXIO; + + switch (reg) { + case PMBUS_FAN_CONFIG_12: + /* + * Two fans are reported in PMBUS_FAN_CONFIG_12 but there is + * only one fan in the module. Mask out the FAN2 bits. + */ + ret = pmbus_read_byte_data(client, 0, PMBUS_FAN_CONFIG_12); + if (ret >= 0) + ret &= ~(PB_FAN_2_INSTALLED | PB_FAN_2_PULSE_MASK); + break; + default: + ret = -ENODATA; + break; + } + + return ret; +} + +static int bpa_rs600_read_word_data(struct i2c_client *client, int page, int phase, int reg) +{ + int ret; + + if (page > 0) + return -ENXIO; + + 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: + case PMBUS_VOUT_OV_FAULT_LIMIT: + /* These commands return data but it is invalid/un-documented */ + ret = -ENXIO; + break; + default: + if (reg >= PMBUS_VIRT_BASE) + ret = -ENXIO; + else + ret = -ENODATA; + break; + } + + return ret; +} + +static struct pmbus_driver_info bpa_rs600_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = linear, + .format[PSC_CURRENT_IN] = linear, + .format[PSC_CURRENT_OUT] = linear, + .format[PSC_POWER] = linear, + .format[PSC_TEMPERATURE] = linear, + .format[PSC_FAN] = linear, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | + PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | + PMBUS_HAVE_PIN | PMBUS_HAVE_POUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | + PMBUS_HAVE_FAN12 | + PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP | + PMBUS_HAVE_STATUS_FAN12, + .read_byte_data = bpa_rs600_read_byte_data, + .read_word_data = bpa_rs600_read_word_data, +}; + +static int bpa_rs600_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + u8 buf[I2C_SMBUS_BLOCK_MAX + 1]; + int ret; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA + | I2C_FUNC_SMBUS_READ_WORD_DATA + | I2C_FUNC_SMBUS_READ_BLOCK_DATA)) + return -ENODEV; + + ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf); + if (ret < 0) { + dev_err(dev, "Failed to read Manufacturer Model\n"); + return ret; + } + + if (strncmp(buf, "BPA-RS600", 8)) { + buf[ret] = '\0'; + dev_err(dev, "Unsupported Manufacturer Model '%s'\n", buf); + return -ENODEV; + } + + 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" }, + {}, +}; +MODULE_DEVICE_TABLE(of, bpa_rs600_of_match); + +static struct i2c_driver bpa_rs600_driver = { + .driver = { + .name = "bpa-rs600", + .of_match_table = of_match_ptr(bpa_rs600_of_match), + }, + .probe_new = bpa_rs600_probe, + .id_table = bpa_rs600_id, +}; + +module_i2c_driver(bpa_rs600_driver); + +MODULE_AUTHOR("Chris Packham"); +MODULE_DESCRIPTION("PMBus driver for BluTek BPA-RS600"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/fsp-3y.c b/drivers/hwmon/pmbus/fsp-3y.c new file mode 100644 index 000000000000..b177987286ae --- /dev/null +++ b/drivers/hwmon/pmbus/fsp-3y.c @@ -0,0 +1,254 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for FSP 3Y-Power PSUs + * + * Copyright (c) 2021 Václav Kubernát, CESNET + * + * This driver is mostly reverse engineered with the help of a tool called pmbus_peek written by + * David Brownell (and later adopted by Jan Kundrát). The device has some sort of a timing issue + * when switching pages, details are explained in the code. The driver support is limited. It + * exposes only the values, that have been tested to work correctly. Unsupported values either + * aren't supported by the devices or their encondings are unknown. + */ + +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include "pmbus.h" + +#define YM2151_PAGE_12V_LOG 0x00 +#define YM2151_PAGE_12V_REAL 0x00 +#define YM2151_PAGE_5VSB_LOG 0x01 +#define YM2151_PAGE_5VSB_REAL 0x20 +#define YH5151E_PAGE_12V_LOG 0x00 +#define YH5151E_PAGE_12V_REAL 0x00 +#define YH5151E_PAGE_5V_LOG 0x01 +#define YH5151E_PAGE_5V_REAL 0x10 +#define YH5151E_PAGE_3V3_LOG 0x02 +#define YH5151E_PAGE_3V3_REAL 0x11 + +enum chips { + ym2151e, + yh5151e +}; + +struct fsp3y_data { + struct pmbus_driver_info info; + int chip; + int page; +}; + +#define to_fsp3y_data(x) container_of(x, struct fsp3y_data, info) + +static int page_log_to_page_real(int page_log, enum chips chip) +{ + switch (chip) { + case ym2151e: + switch (page_log) { + case YM2151_PAGE_12V_LOG: + return YM2151_PAGE_12V_REAL; + case YM2151_PAGE_5VSB_LOG: + return YM2151_PAGE_5VSB_REAL; + } + return -EINVAL; + case yh5151e: + switch (page_log) { + case YH5151E_PAGE_12V_LOG: + return YH5151E_PAGE_12V_REAL; + case YH5151E_PAGE_5V_LOG: + return YH5151E_PAGE_5V_LOG; + case YH5151E_PAGE_3V3_LOG: + return YH5151E_PAGE_3V3_REAL; + } + return -EINVAL; + } + + return -EINVAL; +} + +static int set_page(struct i2c_client *client, int page_log) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct fsp3y_data *data = to_fsp3y_data(info); + int rv; + int page_real; + + if (page_log < 0) + return 0; + + page_real = page_log_to_page_real(page_log, data->chip); + if (page_real < 0) + return page_real; + + if (data->page != page_real) { + rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page_real); + if (rv < 0) + return rv; + + data->page = page_real; + + /* + * Testing showed that the device has a timing issue. After + * setting a page, it takes a while, before the device actually + * gives the correct values from the correct page. 20 ms was + * tested to be enough to not give wrong values (15 ms wasn't + * enough). + */ + usleep_range(20000, 30000); + } + + return 0; +} + +static int fsp3y_read_byte_data(struct i2c_client *client, int page, int reg) +{ + int rv; + + rv = set_page(client, page); + if (rv < 0) + return rv; + + return i2c_smbus_read_byte_data(client, reg); +} + +static int fsp3y_read_word_data(struct i2c_client *client, int page, int phase, int reg) +{ + int rv; + + /* + * This masks commands which weren't tested to work correctly. Some of + * the masked commands return 0xFFFF. These would probably get tagged as + * invalid by pmbus_core. Other ones do return values which might be + * useful (that is, they are not 0xFFFF), but their encoding is unknown, + * and so they are unsupported. + */ + switch (reg) { + case PMBUS_READ_FAN_SPEED_1: + case PMBUS_READ_IIN: + case PMBUS_READ_IOUT: + case PMBUS_READ_PIN: + case PMBUS_READ_POUT: + case PMBUS_READ_TEMPERATURE_1: + case PMBUS_READ_TEMPERATURE_2: + case PMBUS_READ_TEMPERATURE_3: + case PMBUS_READ_VIN: + case PMBUS_READ_VOUT: + case PMBUS_STATUS_WORD: + break; + default: + return -ENXIO; + } + + rv = set_page(client, page); + if (rv < 0) + return rv; + + return i2c_smbus_read_word_data(client, reg); +} + +static struct pmbus_driver_info fsp3y_info[] = { + [ym2151e] = { + .pages = 2, + .func[YM2151_PAGE_12V_LOG] = + PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | + PMBUS_HAVE_PIN | PMBUS_HAVE_POUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | + PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | + PMBUS_HAVE_FAN12, + .func[YM2151_PAGE_5VSB_LOG] = + PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT, + PMBUS_HAVE_IIN, + .read_word_data = fsp3y_read_word_data, + .read_byte_data = fsp3y_read_byte_data, + }, + [yh5151e] = { + .pages = 3, + .func[YH5151E_PAGE_12V_LOG] = + PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | + PMBUS_HAVE_POUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3, + .func[YH5151E_PAGE_5V_LOG] = + PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | + PMBUS_HAVE_POUT, + .func[YH5151E_PAGE_3V3_LOG] = + PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | + PMBUS_HAVE_POUT, + .read_word_data = fsp3y_read_word_data, + .read_byte_data = fsp3y_read_byte_data, + } +}; + +static int fsp3y_detect(struct i2c_client *client) +{ + int rv; + u8 buf[I2C_SMBUS_BLOCK_MAX + 1]; + + rv = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf); + if (rv < 0) + return rv; + + buf[rv] = '\0'; + + if (rv == 8) { + if (!strcmp(buf, "YM-2151E")) + return ym2151e; + else if (!strcmp(buf, "YH-5151E")) + return yh5151e; + } + + dev_err(&client->dev, "Unsupported model %.*s\n", rv, buf); + return -ENODEV; +} + +static const struct i2c_device_id fsp3y_id[] = { + {"ym2151e", ym2151e}, + {"yh5151e", yh5151e}, + { } +}; + +static int fsp3y_probe(struct i2c_client *client) +{ + struct fsp3y_data *data; + const struct i2c_device_id *id; + int rv; + + data = devm_kzalloc(&client->dev, sizeof(struct fsp3y_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->chip = fsp3y_detect(client); + if (data->chip < 0) + return data->chip; + + id = i2c_match_id(fsp3y_id, client); + if (data->chip != id->driver_data) + dev_warn(&client->dev, "Device mismatch: Configured %s (%d), detected %d\n", + id->name, (int)id->driver_data, data->chip); + + rv = i2c_smbus_read_byte_data(client, PMBUS_PAGE); + if (rv < 0) + return rv; + data->page = rv; + + data->info = fsp3y_info[data->chip]; + + return pmbus_do_probe(client, &data->info); +} + +MODULE_DEVICE_TABLE(i2c, fsp3y_id); + +static struct i2c_driver fsp3y_driver = { + .driver = { + .name = "fsp3y", + }, + .probe_new = fsp3y_probe, + .id_table = fsp3y_id +}; + +module_i2c_driver(fsp3y_driver); + +MODULE_AUTHOR("Václav Kubernát"); +MODULE_DESCRIPTION("PMBus driver for FSP/3Y-Power power supplies"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/ibm-cffps.c b/drivers/hwmon/pmbus/ibm-cffps.c index ffde5aaa5036..5668d8305b78 100644 --- a/drivers/hwmon/pmbus/ibm-cffps.c +++ b/drivers/hwmon/pmbus/ibm-cffps.c @@ -625,3 +625,4 @@ module_i2c_driver(ibm_cffps_driver); MODULE_AUTHOR("Eddie James"); MODULE_DESCRIPTION("PMBus driver for IBM Common Form Factor power supplies"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/inspur-ipsps.c b/drivers/hwmon/pmbus/inspur-ipsps.c index 88c5865c4d6f..0f614e8d95f6 100644 --- a/drivers/hwmon/pmbus/inspur-ipsps.c +++ b/drivers/hwmon/pmbus/inspur-ipsps.c @@ -70,7 +70,7 @@ static ssize_t ipsps_string_show(struct device *dev, p = memscan(data, '#', rc); *p = '\0'; - return snprintf(buf, PAGE_SIZE, "%s\n", data); + return sysfs_emit(buf, "%s\n", data); } static ssize_t ipsps_fw_version_show(struct device *dev, @@ -91,9 +91,9 @@ static ssize_t ipsps_fw_version_show(struct device *dev, if (rc != 6) return -EPROTO; - return snprintf(buf, PAGE_SIZE, "%u.%02u%u-%u.%02u\n", - data[1], data[2]/* < 100 */, data[3]/*< 10*/, - data[4], data[5]/* < 100 */); + return sysfs_emit(buf, "%u.%02u%u-%u.%02u\n", + data[1], data[2]/* < 100 */, data[3]/*< 10*/, + data[4], data[5]/* < 100 */); } static ssize_t ipsps_mode_show(struct device *dev, @@ -111,19 +111,19 @@ static ssize_t ipsps_mode_show(struct device *dev, switch (rc) { case MODE_ACTIVE: - return snprintf(buf, PAGE_SIZE, "[%s] %s %s\n", - MODE_ACTIVE_STRING, - MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING); + return sysfs_emit(buf, "[%s] %s %s\n", + MODE_ACTIVE_STRING, + MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING); case MODE_STANDBY: - return snprintf(buf, PAGE_SIZE, "%s [%s] %s\n", - MODE_ACTIVE_STRING, - MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING); + return sysfs_emit(buf, "%s [%s] %s\n", + MODE_ACTIVE_STRING, + MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING); case MODE_REDUNDANCY: - return snprintf(buf, PAGE_SIZE, "%s %s [%s]\n", - MODE_ACTIVE_STRING, - MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING); + return sysfs_emit(buf, "%s %s [%s]\n", + MODE_ACTIVE_STRING, + MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING); default: - return snprintf(buf, PAGE_SIZE, "unspecified\n"); + return sysfs_emit(buf, "unspecified\n"); } } @@ -224,3 +224,4 @@ module_i2c_driver(ipsps_driver); MODULE_AUTHOR("John Wang"); MODULE_DESCRIPTION("PMBus driver for Inspur Power System power supplies"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/ir35221.c b/drivers/hwmon/pmbus/ir35221.c index 3aebeb1443fd..a6cf98e49666 100644 --- a/drivers/hwmon/pmbus/ir35221.c +++ b/drivers/hwmon/pmbus/ir35221.c @@ -145,3 +145,4 @@ module_i2c_driver(ir35221_driver); MODULE_AUTHOR("Samuel Mendoza-Jonas <sam@mendozajonas.com"); MODULE_DESCRIPTION("PMBus driver for IR35221"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/ir36021.c b/drivers/hwmon/pmbus/ir36021.c new file mode 100644 index 000000000000..4dca4767f571 --- /dev/null +++ b/drivers/hwmon/pmbus/ir36021.c @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Hardware monitoring driver for Infineon IR36021 + * + * Copyright (c) 2021 Allied Telesis + */ +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include "pmbus.h" + +static struct pmbus_driver_info ir36021_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = linear, + .format[PSC_CURRENT_IN] = linear, + .format[PSC_CURRENT_OUT] = linear, + .format[PSC_POWER] = linear, + .format[PSC_TEMPERATURE] = linear, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT + | PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT + | PMBUS_HAVE_PIN | PMBUS_HAVE_POUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 + | PMBUS_HAVE_STATUS_TEMP, +}; + +static int ir36021_probe(struct i2c_client *client) +{ + u8 buf[I2C_SMBUS_BLOCK_MAX]; + int ret; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA + | I2C_FUNC_SMBUS_READ_WORD_DATA + | I2C_FUNC_SMBUS_READ_BLOCK_DATA)) + return -ENODEV; + + ret = i2c_smbus_read_i2c_block_data(client, PMBUS_MFR_MODEL, 2, buf); + if (ret < 0) { + dev_err(&client->dev, "Failed to read PMBUS_MFR_MODEL\n"); + return ret; + } + if (ret != 2 || buf[0] != 0x01 || buf[1] != 0x2d) { + dev_err(&client->dev, "MFR_MODEL unrecognised\n"); + return -ENODEV; + } + + return pmbus_do_probe(client, &ir36021_info); +} + +static const struct i2c_device_id ir36021_id[] = { + { "ir36021", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, ir36021_id); + +static const struct of_device_id __maybe_unused ir36021_of_id[] = { + { .compatible = "infineon,ir36021" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ir36021_of_id); + +static struct i2c_driver ir36021_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "ir36021", + .of_match_table = of_match_ptr(ir36021_of_id), + }, + .probe_new = ir36021_probe, + .id_table = ir36021_id, +}; + +module_i2c_driver(ir36021_driver); + +MODULE_AUTHOR("Chris Packham <chris.packham@alliedtelesis.co.nz>"); +MODULE_DESCRIPTION("PMBus driver for Infineon IR36021"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/ir38064.c b/drivers/hwmon/pmbus/ir38064.c index 46f17c4b4873..1fb7f1248639 100644 --- a/drivers/hwmon/pmbus/ir38064.c +++ b/drivers/hwmon/pmbus/ir38064.c @@ -61,3 +61,4 @@ module_i2c_driver(ir38064_driver); MODULE_AUTHOR("Maxim Sloyko <maxims@google.com>"); MODULE_DESCRIPTION("PMBus driver for Infineon IR38064"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/irps5401.c b/drivers/hwmon/pmbus/irps5401.c index 93ef6d64a33a..de3449e4d77a 100644 --- a/drivers/hwmon/pmbus/irps5401.c +++ b/drivers/hwmon/pmbus/irps5401.c @@ -63,3 +63,4 @@ module_i2c_driver(irps5401_driver); MODULE_AUTHOR("Robert Hancock"); MODULE_DESCRIPTION("PMBus driver for Infineon IRPS5401"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/isl68137.c b/drivers/hwmon/pmbus/isl68137.c index 2bee930d3900..40597a9e799f 100644 --- a/drivers/hwmon/pmbus/isl68137.c +++ b/drivers/hwmon/pmbus/isl68137.c @@ -332,3 +332,4 @@ module_i2c_driver(isl68137_driver); MODULE_AUTHOR("Maxim Sloyko <maxims@google.com>"); MODULE_DESCRIPTION("PMBus driver for Renesas digital multiphase voltage regulators"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/lm25066.c b/drivers/hwmon/pmbus/lm25066.c index e9a66fd9e144..d209e0afc2ca 100644 --- a/drivers/hwmon/pmbus/lm25066.c +++ b/drivers/hwmon/pmbus/lm25066.c @@ -511,3 +511,4 @@ module_i2c_driver(lm25066_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for LM25066 and compatible chips"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c index 7e53fa95b92d..0127273883f0 100644 --- a/drivers/hwmon/pmbus/ltc2978.c +++ b/drivers/hwmon/pmbus/ltc2978.c @@ -883,3 +883,4 @@ module_i2c_driver(ltc2978_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for LTC2978 and compatible chips"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/ltc3815.c b/drivers/hwmon/pmbus/ltc3815.c index e45e14d26c9a..8e13a7ddcb42 100644 --- a/drivers/hwmon/pmbus/ltc3815.c +++ b/drivers/hwmon/pmbus/ltc3815.c @@ -208,3 +208,4 @@ module_i2c_driver(ltc3815_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for LTC3815"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/max15301.c b/drivers/hwmon/pmbus/max15301.c new file mode 100644 index 000000000000..0b6f88428ea8 --- /dev/null +++ b/drivers/hwmon/pmbus/max15301.c @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for Maxim MAX15301 + * + * Copyright (c) 2021 Flextronics International Sweden AB + * + * Even though the specification does not specifically mention it, + * extensive empirical testing has revealed that auto-detection of + * limit-registers will fail in a random fashion unless the delay + * parameter is set to above about 80us. The default delay is set + * to 100us to include some safety margin. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/ktime.h> +#include <linux/delay.h> +#include <linux/pmbus.h> +#include "pmbus.h" + +static const struct i2c_device_id max15301_id[] = { + {"bmr461", 0}, + {"max15301", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, max15301_id); + +struct max15301_data { + int id; + ktime_t access; /* Chip access time */ + int delay; /* Delay between chip accesses in us */ + struct pmbus_driver_info info; +}; + +#define to_max15301_data(x) container_of(x, struct max15301_data, info) + +#define MAX15301_WAIT_TIME 100 /* us */ + +static ushort delay = MAX15301_WAIT_TIME; +module_param(delay, ushort, 0644); +MODULE_PARM_DESC(delay, "Delay between chip accesses in us"); + +static struct max15301_data max15301_data = { + .info = { + .pages = 1, + .func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 + | PMBUS_HAVE_STATUS_TEMP + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT, + } +}; + +/* This chip needs a delay between accesses */ +static inline void max15301_wait(const struct max15301_data *data) +{ + if (data->delay) { + s64 delta = ktime_us_delta(ktime_get(), data->access); + + if (delta < data->delay) + udelay(data->delay - delta); + } +} + +static int max15301_read_word_data(struct i2c_client *client, int page, + int phase, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct max15301_data *data = to_max15301_data(info); + int ret; + + if (page > 0) + return -ENXIO; + + if (reg >= PMBUS_VIRT_BASE) + return -ENXIO; + + max15301_wait(data); + ret = pmbus_read_word_data(client, page, phase, reg); + data->access = ktime_get(); + + return ret; +} + +static int max15301_read_byte_data(struct i2c_client *client, int page, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct max15301_data *data = to_max15301_data(info); + int ret; + + if (page > 0) + return -ENXIO; + + max15301_wait(data); + ret = pmbus_read_byte_data(client, page, reg); + data->access = ktime_get(); + + return ret; +} + +static int max15301_write_word_data(struct i2c_client *client, int page, int reg, + u16 word) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct max15301_data *data = to_max15301_data(info); + int ret; + + if (page > 0) + return -ENXIO; + + if (reg >= PMBUS_VIRT_BASE) + return -ENXIO; + + max15301_wait(data); + ret = pmbus_write_word_data(client, page, reg, word); + data->access = ktime_get(); + + return ret; +} + +static int max15301_write_byte(struct i2c_client *client, int page, u8 value) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct max15301_data *data = to_max15301_data(info); + int ret; + + if (page > 0) + return -ENXIO; + + max15301_wait(data); + ret = pmbus_write_byte(client, page, value); + data->access = ktime_get(); + + return ret; +} + +static int max15301_probe(struct i2c_client *client) +{ + int status; + u8 device_id[I2C_SMBUS_BLOCK_MAX + 1]; + const struct i2c_device_id *mid; + struct pmbus_driver_info *info = &max15301_data.info; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA + | I2C_FUNC_SMBUS_BLOCK_DATA)) + return -ENODEV; + + status = i2c_smbus_read_block_data(client, PMBUS_IC_DEVICE_ID, device_id); + if (status < 0) { + dev_err(&client->dev, "Failed to read Device Id\n"); + return status; + } + for (mid = max15301_id; mid->name[0]; mid++) { + if (!strncasecmp(mid->name, device_id, strlen(mid->name))) + break; + } + if (!mid->name[0]) { + dev_err(&client->dev, "Unsupported device\n"); + return -ENODEV; + } + + max15301_data.delay = delay; + + info->read_byte_data = max15301_read_byte_data; + info->read_word_data = max15301_read_word_data; + info->write_byte = max15301_write_byte; + info->write_word_data = max15301_write_word_data; + + return pmbus_do_probe(client, info); +} + +static struct i2c_driver max15301_driver = { + .driver = { + .name = "max15301", + }, + .probe_new = max15301_probe, + .id_table = max15301_id, +}; + +module_i2c_driver(max15301_driver); + +MODULE_AUTHOR("Erik Rosen <erik.rosen@metormote.com>"); +MODULE_DESCRIPTION("PMBus driver for Maxim MAX15301"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/max16064.c b/drivers/hwmon/pmbus/max16064.c index d79add99083e..94f869039071 100644 --- a/drivers/hwmon/pmbus/max16064.c +++ b/drivers/hwmon/pmbus/max16064.c @@ -111,3 +111,4 @@ module_i2c_driver(max16064_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for Maxim MAX16064"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/max16601.c b/drivers/hwmon/pmbus/max16601.c index 0d1204c2dd54..5a226a564776 100644 --- a/drivers/hwmon/pmbus/max16601.c +++ b/drivers/hwmon/pmbus/max16601.c @@ -359,3 +359,4 @@ module_i2c_driver(max16601_driver); MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); MODULE_DESCRIPTION("PMBus driver for Maxim MAX16601"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/max20730.c b/drivers/hwmon/pmbus/max20730.c index 9dd3dd79bc18..ba39f03c6374 100644 --- a/drivers/hwmon/pmbus/max20730.c +++ b/drivers/hwmon/pmbus/max20730.c @@ -785,3 +785,4 @@ module_i2c_driver(max20730_driver); MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); MODULE_DESCRIPTION("PMBus driver for Maxim MAX20710 / MAX20730 / MAX20734 / MAX20743"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/max20751.c b/drivers/hwmon/pmbus/max20751.c index 9d42f82fdd99..2272dc8c2e38 100644 --- a/drivers/hwmon/pmbus/max20751.c +++ b/drivers/hwmon/pmbus/max20751.c @@ -51,3 +51,4 @@ module_i2c_driver(max20751_driver); MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); MODULE_DESCRIPTION("PMBus driver for Maxim MAX20751"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/max31785.c b/drivers/hwmon/pmbus/max31785.c index 17489abc49d5..95d79a64b483 100644 --- a/drivers/hwmon/pmbus/max31785.c +++ b/drivers/hwmon/pmbus/max31785.c @@ -403,3 +403,4 @@ module_i2c_driver(max31785_driver); MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>"); MODULE_DESCRIPTION("PMBus driver for the Maxim MAX31785"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/max34440.c b/drivers/hwmon/pmbus/max34440.c index dad66b3c0116..ea7609058a12 100644 --- a/drivers/hwmon/pmbus/max34440.c +++ b/drivers/hwmon/pmbus/max34440.c @@ -529,3 +529,4 @@ module_i2c_driver(max34440_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for Maxim MAX34440/MAX34441"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/max8688.c b/drivers/hwmon/pmbus/max8688.c index 329dc851fc59..5e66c28c0b71 100644 --- a/drivers/hwmon/pmbus/max8688.c +++ b/drivers/hwmon/pmbus/max8688.c @@ -191,3 +191,4 @@ module_i2c_driver(max8688_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for Maxim MAX8688"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/mp2975.c b/drivers/hwmon/pmbus/mp2975.c index 60fbdb371332..eb94bd5f4e2a 100644 --- a/drivers/hwmon/pmbus/mp2975.c +++ b/drivers/hwmon/pmbus/mp2975.c @@ -766,3 +766,4 @@ module_i2c_driver(mp2975_driver); MODULE_AUTHOR("Vadim Pasternak <vadimp@nvidia.com>"); MODULE_DESCRIPTION("PMBus driver for MPS MP2975 device"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/pm6764tr.c b/drivers/hwmon/pmbus/pm6764tr.c index d97cb6d6c87f..e0bbc8a30d21 100644 --- a/drivers/hwmon/pmbus/pm6764tr.c +++ b/drivers/hwmon/pmbus/pm6764tr.c @@ -73,3 +73,4 @@ module_i2c_driver(pm6764tr_driver); MODULE_AUTHOR("Charles Hsu"); MODULE_DESCRIPTION("PMBus driver for ST PM6764TR"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/pmbus.c b/drivers/hwmon/pmbus/pmbus.c index a1b4260e75b2..618c377664c4 100644 --- a/drivers/hwmon/pmbus/pmbus.c +++ b/drivers/hwmon/pmbus/pmbus.c @@ -246,3 +246,4 @@ module_i2c_driver(pmbus_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("Generic PMBus driver"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h index 4c30ec89f5bf..3968924f8533 100644 --- a/drivers/hwmon/pmbus/pmbus.h +++ b/drivers/hwmon/pmbus/pmbus.h @@ -475,6 +475,7 @@ extern const struct regulator_ops pmbus_regulator_ops; /* Function declarations */ void pmbus_clear_cache(struct i2c_client *client); +void pmbus_set_update(struct i2c_client *client, u8 reg, bool update); int pmbus_set_page(struct i2c_client *client, int page, int phase); int pmbus_read_word_data(struct i2c_client *client, int page, int phase, u8 reg); diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index aadea85fe630..bbd745178147 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -139,7 +139,18 @@ void pmbus_clear_cache(struct i2c_client *client) for (sensor = data->sensors; sensor; sensor = sensor->next) sensor->data = -ENODATA; } -EXPORT_SYMBOL_GPL(pmbus_clear_cache); +EXPORT_SYMBOL_NS_GPL(pmbus_clear_cache, PMBUS); + +void pmbus_set_update(struct i2c_client *client, u8 reg, bool update) +{ + struct pmbus_data *data = i2c_get_clientdata(client); + struct pmbus_sensor *sensor; + + for (sensor = data->sensors; sensor; sensor = sensor->next) + if (sensor->reg == reg) + sensor->update = update; +} +EXPORT_SYMBOL_NS_GPL(pmbus_set_update, PMBUS); int pmbus_set_page(struct i2c_client *client, int page, int phase) { @@ -175,7 +186,7 @@ int pmbus_set_page(struct i2c_client *client, int page, int phase) return 0; } -EXPORT_SYMBOL_GPL(pmbus_set_page); +EXPORT_SYMBOL_NS_GPL(pmbus_set_page, PMBUS); int pmbus_write_byte(struct i2c_client *client, int page, u8 value) { @@ -187,7 +198,7 @@ int pmbus_write_byte(struct i2c_client *client, int page, u8 value) return i2c_smbus_write_byte(client, value); } -EXPORT_SYMBOL_GPL(pmbus_write_byte); +EXPORT_SYMBOL_NS_GPL(pmbus_write_byte, PMBUS); /* * _pmbus_write_byte() is similar to pmbus_write_byte(), but checks if @@ -218,7 +229,7 @@ int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg, return i2c_smbus_write_word_data(client, reg, word); } -EXPORT_SYMBOL_GPL(pmbus_write_word_data); +EXPORT_SYMBOL_NS_GPL(pmbus_write_word_data, PMBUS); static int pmbus_write_virt_reg(struct i2c_client *client, int page, int reg, @@ -288,7 +299,7 @@ int pmbus_update_fan(struct i2c_client *client, int page, int id, return _pmbus_write_word_data(client, page, pmbus_fan_command_registers[id], command); } -EXPORT_SYMBOL_GPL(pmbus_update_fan); +EXPORT_SYMBOL_NS_GPL(pmbus_update_fan, PMBUS); int pmbus_read_word_data(struct i2c_client *client, int page, int phase, u8 reg) { @@ -300,7 +311,7 @@ int pmbus_read_word_data(struct i2c_client *client, int page, int phase, u8 reg) return i2c_smbus_read_word_data(client, reg); } -EXPORT_SYMBOL_GPL(pmbus_read_word_data); +EXPORT_SYMBOL_NS_GPL(pmbus_read_word_data, PMBUS); static int pmbus_read_virt_reg(struct i2c_client *client, int page, int reg) { @@ -359,7 +370,7 @@ int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg) return i2c_smbus_read_byte_data(client, reg); } -EXPORT_SYMBOL_GPL(pmbus_read_byte_data); +EXPORT_SYMBOL_NS_GPL(pmbus_read_byte_data, PMBUS); int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, u8 value) { @@ -371,7 +382,7 @@ int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, u8 value) return i2c_smbus_write_byte_data(client, reg, value); } -EXPORT_SYMBOL_GPL(pmbus_write_byte_data); +EXPORT_SYMBOL_NS_GPL(pmbus_write_byte_data, PMBUS); int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg, u8 mask, u8 value) @@ -390,7 +401,7 @@ int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg, return rv; } -EXPORT_SYMBOL_GPL(pmbus_update_byte_data); +EXPORT_SYMBOL_NS_GPL(pmbus_update_byte_data, PMBUS); /* * _pmbus_read_byte_data() is similar to pmbus_read_byte_data(), but checks if @@ -463,14 +474,14 @@ int pmbus_get_fan_rate_device(struct i2c_client *client, int page, int id, { return pmbus_get_fan_rate(client, page, id, mode, false); } -EXPORT_SYMBOL_GPL(pmbus_get_fan_rate_device); +EXPORT_SYMBOL_NS_GPL(pmbus_get_fan_rate_device, PMBUS); int pmbus_get_fan_rate_cached(struct i2c_client *client, int page, int id, enum pmbus_fan_mode mode) { return pmbus_get_fan_rate(client, page, id, mode, true); } -EXPORT_SYMBOL_GPL(pmbus_get_fan_rate_cached); +EXPORT_SYMBOL_NS_GPL(pmbus_get_fan_rate_cached, PMBUS); static void pmbus_clear_fault_page(struct i2c_client *client, int page) { @@ -485,7 +496,7 @@ void pmbus_clear_faults(struct i2c_client *client) for (i = 0; i < data->info->pages; i++) pmbus_clear_fault_page(client, i); } -EXPORT_SYMBOL_GPL(pmbus_clear_faults); +EXPORT_SYMBOL_NS_GPL(pmbus_clear_faults, PMBUS); static int pmbus_check_status_cml(struct i2c_client *client) { @@ -537,13 +548,13 @@ bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg) { return pmbus_check_register(client, _pmbus_read_byte_data, page, reg); } -EXPORT_SYMBOL_GPL(pmbus_check_byte_register); +EXPORT_SYMBOL_NS_GPL(pmbus_check_byte_register, PMBUS); bool pmbus_check_word_register(struct i2c_client *client, int page, int reg) { return pmbus_check_register(client, __pmbus_read_word_data, page, reg); } -EXPORT_SYMBOL_GPL(pmbus_check_word_register); +EXPORT_SYMBOL_NS_GPL(pmbus_check_word_register, PMBUS); const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client *client) { @@ -551,7 +562,7 @@ const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client *client) return data->info; } -EXPORT_SYMBOL_GPL(pmbus_get_driver_info); +EXPORT_SYMBOL_NS_GPL(pmbus_get_driver_info, PMBUS); static int pmbus_get_status(struct i2c_client *client, int page, int reg) { @@ -932,7 +943,7 @@ static ssize_t pmbus_show_boolean(struct device *dev, val = pmbus_get_boolean(client, boolean, attr->index); if (val < 0) return val; - return snprintf(buf, PAGE_SIZE, "%d\n", val); + return sysfs_emit(buf, "%d\n", val); } static ssize_t pmbus_show_sensor(struct device *dev, @@ -948,7 +959,7 @@ static ssize_t pmbus_show_sensor(struct device *dev, if (sensor->data < 0) ret = sensor->data; else - ret = snprintf(buf, PAGE_SIZE, "%lld\n", pmbus_reg2data(data, sensor)); + ret = sysfs_emit(buf, "%lld\n", pmbus_reg2data(data, sensor)); mutex_unlock(&data->update_lock); return ret; } @@ -984,7 +995,7 @@ static ssize_t pmbus_show_label(struct device *dev, { struct pmbus_label *label = to_pmbus_label(da); - return snprintf(buf, PAGE_SIZE, "%s\n", label->label); + return sysfs_emit(buf, "%s\n", label->label); } static int pmbus_add_attribute(struct pmbus_data *data, struct attribute *attr) @@ -2024,7 +2035,7 @@ static ssize_t pmbus_show_samples(struct device *dev, if (val < 0) return val; - return snprintf(buf, PAGE_SIZE, "%d\n", val); + return sysfs_emit(buf, "%d\n", val); } static ssize_t pmbus_set_samples(struct device *dev, @@ -2288,7 +2299,7 @@ const struct regulator_ops pmbus_regulator_ops = { .disable = pmbus_regulator_disable, .is_enabled = pmbus_regulator_is_enabled, }; -EXPORT_SYMBOL_GPL(pmbus_regulator_ops); +EXPORT_SYMBOL_NS_GPL(pmbus_regulator_ops, PMBUS); static int pmbus_regulator_register(struct pmbus_data *data) { @@ -2557,6 +2568,7 @@ int pmbus_do_probe(struct i2c_client *client, struct pmbus_driver_info *info) struct pmbus_data *data; size_t groups_num = 0; int ret; + char *name; if (!info) return -ENODEV; @@ -2606,10 +2618,15 @@ int pmbus_do_probe(struct i2c_client *client, struct pmbus_driver_info *info) return -ENODEV; } + name = devm_kstrdup(dev, client->name, GFP_KERNEL); + if (!name) + return -ENOMEM; + strreplace(name, '-', '_'); + data->groups[0] = &data->group; memcpy(data->groups + 1, info->groups, sizeof(void *) * groups_num); data->hwmon_dev = devm_hwmon_device_register_with_groups(dev, - client->name, data, data->groups); + name, data, data->groups); if (IS_ERR(data->hwmon_dev)) { dev_err(dev, "Failed to register hwmon device\n"); return PTR_ERR(data->hwmon_dev); @@ -2625,7 +2642,7 @@ int pmbus_do_probe(struct i2c_client *client, struct pmbus_driver_info *info) return 0; } -EXPORT_SYMBOL_GPL(pmbus_do_probe); +EXPORT_SYMBOL_NS_GPL(pmbus_do_probe, PMBUS); struct dentry *pmbus_get_debugfs_dir(struct i2c_client *client) { @@ -2633,7 +2650,7 @@ struct dentry *pmbus_get_debugfs_dir(struct i2c_client *client) return data->debugfs; } -EXPORT_SYMBOL_GPL(pmbus_get_debugfs_dir); +EXPORT_SYMBOL_NS_GPL(pmbus_get_debugfs_dir, PMBUS); static int __init pmbus_core_init(void) { diff --git a/drivers/hwmon/pmbus/pxe1610.c b/drivers/hwmon/pmbus/pxe1610.c index da27ce34ee3f..52bee5de2988 100644 --- a/drivers/hwmon/pmbus/pxe1610.c +++ b/drivers/hwmon/pmbus/pxe1610.c @@ -41,6 +41,15 @@ static int pxe1610_identify(struct i2c_client *client, info->vrm_version[i] = vr13; break; default: + /* + * If prior pages are available limit operation + * to them + */ + if (i != 0) { + info->pages = i; + return 0; + } + return -ENODEV; } } @@ -139,3 +148,4 @@ module_i2c_driver(pxe1610_driver); MODULE_AUTHOR("Vijay Khemka <vijaykhemka@fb.com>"); MODULE_DESCRIPTION("PMBus driver for Infineon PXE1610, PXE1110 and PXM1310"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/q54sj108a2.c b/drivers/hwmon/pmbus/q54sj108a2.c index aec512766c31..b6e8b20466f1 100644 --- a/drivers/hwmon/pmbus/q54sj108a2.c +++ b/drivers/hwmon/pmbus/q54sj108a2.c @@ -420,3 +420,4 @@ module_i2c_driver(q54sj108a2_driver); MODULE_AUTHOR("Xiao.Ma <xiao.mx.ma@deltaww.com>"); MODULE_DESCRIPTION("PMBus driver for Delta Q54SJ108A2 series modules"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/stpddc60.c b/drivers/hwmon/pmbus/stpddc60.c new file mode 100644 index 000000000000..357b9d9d896b --- /dev/null +++ b/drivers/hwmon/pmbus/stpddc60.c @@ -0,0 +1,249 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for the STPDDC60 controller + * + * Copyright (c) 2021 Flextronics International Sweden AB. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/pmbus.h> +#include "pmbus.h" + +#define STPDDC60_MFR_READ_VOUT 0xd2 +#define STPDDC60_MFR_OV_LIMIT_OFFSET 0xe5 +#define STPDDC60_MFR_UV_LIMIT_OFFSET 0xe6 + +static const struct i2c_device_id stpddc60_id[] = { + {"stpddc60", 0}, + {"bmr481", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, stpddc60_id); + +static struct pmbus_driver_info stpddc60_info = { + .pages = 1, + .func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT + | PMBUS_HAVE_POUT, +}; + +/* + * Calculate the closest absolute offset between commanded vout value + * and limit value in steps of 50mv in the range 0 (50mv) to 7 (400mv). + * Return 0 if the upper limit is lower than vout or if the lower limit + * is higher than vout. + */ +static u8 stpddc60_get_offset(int vout, u16 limit, bool over) +{ + int offset; + long v, l; + + v = 250 + (vout - 1) * 5; /* Convert VID to mv */ + l = (limit * 1000L) >> 8; /* Convert LINEAR to mv */ + + if (over == (l < v)) + return 0; + + offset = DIV_ROUND_CLOSEST(abs(l - v), 50); + + if (offset > 0) + offset--; + + return clamp_val(offset, 0, 7); +} + +/* + * Adjust the linear format word to use the given fixed exponent. + */ +static u16 stpddc60_adjust_linear(u16 word, s16 fixed) +{ + s16 e, m, d; + + e = ((s16)word) >> 11; + m = ((s16)((word & 0x7ff) << 5)) >> 5; + d = e - fixed; + + if (d >= 0) + m <<= d; + else + m >>= -d; + + return clamp_val(m, 0, 0x3ff) | ((fixed << 11) & 0xf800); +} + +/* + * The VOUT_COMMAND register uses the VID format but the vout alarm limit + * registers use the LINEAR format so we override VOUT_MODE here to force + * LINEAR format for all registers. + */ +static int stpddc60_read_byte_data(struct i2c_client *client, int page, int reg) +{ + int ret; + + if (page > 0) + return -ENXIO; + + switch (reg) { + case PMBUS_VOUT_MODE: + ret = 0x18; + break; + default: + ret = -ENODATA; + break; + } + + return ret; +} + +/* + * The vout related registers return values in LINEAR11 format when LINEAR16 + * is expected. Clear the top 5 bits to set the exponent part to zero to + * convert the value to LINEAR16 format. + */ +static int stpddc60_read_word_data(struct i2c_client *client, int page, + int phase, int reg) +{ + int ret; + + if (page > 0) + return -ENXIO; + + switch (reg) { + case PMBUS_READ_VOUT: + ret = pmbus_read_word_data(client, page, phase, + STPDDC60_MFR_READ_VOUT); + if (ret < 0) + return ret; + ret &= 0x7ff; + break; + case PMBUS_VOUT_OV_FAULT_LIMIT: + case PMBUS_VOUT_UV_FAULT_LIMIT: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + ret &= 0x7ff; + break; + default: + ret = -ENODATA; + break; + } + + return ret; +} + +/* + * The vout under- and over-voltage limits are set as an offset relative to + * the commanded vout voltage. The vin, iout, pout and temp limits must use + * the same fixed exponent the chip uses to encode the data when read. + */ +static int stpddc60_write_word_data(struct i2c_client *client, int page, + int reg, u16 word) +{ + int ret; + u8 offset; + + if (page > 0) + return -ENXIO; + + switch (reg) { + case PMBUS_VOUT_OV_FAULT_LIMIT: + ret = pmbus_read_word_data(client, page, 0xff, + PMBUS_VOUT_COMMAND); + if (ret < 0) + return ret; + offset = stpddc60_get_offset(ret, word, true); + ret = pmbus_write_byte_data(client, page, + STPDDC60_MFR_OV_LIMIT_OFFSET, + offset); + break; + case PMBUS_VOUT_UV_FAULT_LIMIT: + ret = pmbus_read_word_data(client, page, 0xff, + PMBUS_VOUT_COMMAND); + if (ret < 0) + return ret; + offset = stpddc60_get_offset(ret, word, false); + ret = pmbus_write_byte_data(client, page, + STPDDC60_MFR_UV_LIMIT_OFFSET, + offset); + break; + case PMBUS_VIN_OV_FAULT_LIMIT: + case PMBUS_VIN_UV_FAULT_LIMIT: + case PMBUS_OT_FAULT_LIMIT: + case PMBUS_OT_WARN_LIMIT: + case PMBUS_IOUT_OC_FAULT_LIMIT: + case PMBUS_IOUT_OC_WARN_LIMIT: + case PMBUS_POUT_OP_FAULT_LIMIT: + ret = pmbus_read_word_data(client, page, 0xff, reg); + if (ret < 0) + return ret; + word = stpddc60_adjust_linear(word, ret >> 11); + ret = pmbus_write_word_data(client, page, reg, word); + break; + default: + ret = -ENODATA; + break; + } + + return ret; +} + +static int stpddc60_probe(struct i2c_client *client) +{ + int status; + u8 device_id[I2C_SMBUS_BLOCK_MAX + 1]; + const struct i2c_device_id *mid; + struct pmbus_driver_info *info = &stpddc60_info; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA + | I2C_FUNC_SMBUS_BLOCK_DATA)) + return -ENODEV; + + status = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, device_id); + if (status < 0) { + dev_err(&client->dev, "Failed to read Manufacturer Model\n"); + return status; + } + for (mid = stpddc60_id; mid->name[0]; mid++) { + if (!strncasecmp(mid->name, device_id, strlen(mid->name))) + break; + } + if (!mid->name[0]) { + dev_err(&client->dev, "Unsupported device\n"); + return -ENODEV; + } + + info->read_byte_data = stpddc60_read_byte_data; + info->read_word_data = stpddc60_read_word_data; + info->write_word_data = stpddc60_write_word_data; + + status = pmbus_do_probe(client, info); + if (status < 0) + return status; + + pmbus_set_update(client, PMBUS_VOUT_OV_FAULT_LIMIT, true); + pmbus_set_update(client, PMBUS_VOUT_UV_FAULT_LIMIT, true); + + return 0; +} + +static struct i2c_driver stpddc60_driver = { + .driver = { + .name = "stpddc60", + }, + .probe_new = stpddc60_probe, + .id_table = stpddc60_id, +}; + +module_i2c_driver(stpddc60_driver); + +MODULE_AUTHOR("Erik Rosen <erik.rosen@metormote.com>"); +MODULE_DESCRIPTION("PMBus driver for ST STPDDC60"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/tps40422.c b/drivers/hwmon/pmbus/tps40422.c index f7f00ab6f46c..31bb83c0ef3e 100644 --- a/drivers/hwmon/pmbus/tps40422.c +++ b/drivers/hwmon/pmbus/tps40422.c @@ -51,3 +51,4 @@ module_i2c_driver(tps40422_driver); MODULE_AUTHOR("Zhu Laiwen <richard.zhu@nsn.com>"); MODULE_DESCRIPTION("PMBus driver for TI TPS40422"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/tps53679.c b/drivers/hwmon/pmbus/tps53679.c index ba838fa311c3..81b9d813655a 100644 --- a/drivers/hwmon/pmbus/tps53679.c +++ b/drivers/hwmon/pmbus/tps53679.c @@ -16,11 +16,14 @@ #include "pmbus.h" enum chips { - tps53647, tps53667, tps53679, tps53681, tps53688 + tps53647, tps53667, tps53676, tps53679, tps53681, tps53688 }; #define TPS53647_PAGE_NUM 1 +#define TPS53676_USER_DATA_03 0xb3 +#define TPS53676_MAX_PHASES 7 + #define TPS53679_PROT_VR12_5MV 0x01 /* VR12.0 mode, 5-mV DAC */ #define TPS53679_PROT_VR12_5_10MV 0x02 /* VR12.5 mode, 10-mV DAC */ #define TPS53679_PROT_VR13_10MV 0x04 /* VR13.0 mode, 10-mV DAC */ @@ -143,6 +146,45 @@ static int tps53681_identify(struct i2c_client *client, TPS53681_DEVICE_ID); } +static int tps53676_identify(struct i2c_client *client, + struct pmbus_driver_info *info) +{ + u8 buf[I2C_SMBUS_BLOCK_MAX]; + int phases_a = 0, phases_b = 0; + int i, ret; + + ret = i2c_smbus_read_block_data(client, PMBUS_IC_DEVICE_ID, buf); + if (ret < 0) + return ret; + if (strncmp("TI\x53\x67\x60", buf, 5)) { + dev_err(&client->dev, "Unexpected device ID: %s\n", buf); + return -ENODEV; + } + + ret = i2c_smbus_read_block_data(client, TPS53676_USER_DATA_03, buf); + if (ret < 0) + return ret; + if (ret != 24) + return -EIO; + for (i = 0; i < 2 * TPS53676_MAX_PHASES; i += 2) { + if (buf[i + 1] & 0x80) { + if (buf[i] & 0x08) + phases_b++; + else + phases_a++; + } + } + + info->format[PSC_VOLTAGE_OUT] = linear; + info->pages = 1; + info->phases[0] = phases_a; + if (phases_b > 0) { + info->pages = 2; + info->phases[1] = phases_b; + } + return 0; +} + static int tps53681_read_word_data(struct i2c_client *client, int page, int phase, int reg) { @@ -183,6 +225,7 @@ static struct pmbus_driver_info tps53679_info = { .pfunc[3] = PMBUS_HAVE_IOUT, .pfunc[4] = PMBUS_HAVE_IOUT, .pfunc[5] = PMBUS_HAVE_IOUT, + .pfunc[6] = PMBUS_HAVE_IOUT, }; static int tps53679_probe(struct i2c_client *client) @@ -206,6 +249,9 @@ static int tps53679_probe(struct i2c_client *client) info->pages = TPS53647_PAGE_NUM; info->identify = tps53679_identify; break; + case tps53676: + info->identify = tps53676_identify; + break; case tps53679: case tps53688: info->pages = TPS53679_PAGE_NUM; @@ -225,8 +271,10 @@ static int tps53679_probe(struct i2c_client *client) } static const struct i2c_device_id tps53679_id[] = { + {"bmr474", tps53676}, {"tps53647", tps53647}, {"tps53667", tps53667}, + {"tps53676", tps53676}, {"tps53679", tps53679}, {"tps53681", tps53681}, {"tps53688", tps53688}, @@ -238,6 +286,7 @@ MODULE_DEVICE_TABLE(i2c, tps53679_id); static const struct of_device_id __maybe_unused tps53679_of_match[] = { {.compatible = "ti,tps53647", .data = (void *)tps53647}, {.compatible = "ti,tps53667", .data = (void *)tps53667}, + {.compatible = "ti,tps53676", .data = (void *)tps53676}, {.compatible = "ti,tps53679", .data = (void *)tps53679}, {.compatible = "ti,tps53681", .data = (void *)tps53681}, {.compatible = "ti,tps53688", .data = (void *)tps53688}, @@ -259,3 +308,4 @@ module_i2c_driver(tps53679_driver); MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>"); MODULE_DESCRIPTION("PMBus driver for Texas Instruments TPS53679"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c index a15e6fe3e425..75fc770c9e40 100644 --- a/drivers/hwmon/pmbus/ucd9000.c +++ b/drivers/hwmon/pmbus/ucd9000.c @@ -629,3 +629,4 @@ module_i2c_driver(ucd9000_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for TI UCD90xxx"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/ucd9200.c b/drivers/hwmon/pmbus/ucd9200.c index 47cc7ca9d329..6bc3273e31e7 100644 --- a/drivers/hwmon/pmbus/ucd9200.c +++ b/drivers/hwmon/pmbus/ucd9200.c @@ -209,3 +209,4 @@ module_i2c_driver(ucd9200_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for TI UCD922x, UCD924x"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/xdpe12284.c b/drivers/hwmon/pmbus/xdpe12284.c index f8bc0f41cd5f..b07da06a40c9 100644 --- a/drivers/hwmon/pmbus/xdpe12284.c +++ b/drivers/hwmon/pmbus/xdpe12284.c @@ -168,3 +168,4 @@ module_i2c_driver(xdpe122_driver); MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>"); MODULE_DESCRIPTION("PMBus driver for Infineon XDPE122 family"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/pmbus/zl6100.c b/drivers/hwmon/pmbus/zl6100.c index 69120ca7aaa8..b7d4eacdc3ef 100644 --- a/drivers/hwmon/pmbus/zl6100.c +++ b/drivers/hwmon/pmbus/zl6100.c @@ -404,3 +404,4 @@ module_i2c_driver(zl6100_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for ZL6100 and compatibles"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); diff --git a/drivers/hwmon/s3c-hwmon.c b/drivers/hwmon/s3c-hwmon.c index f2703c5460d0..70ae665db477 100644 --- a/drivers/hwmon/s3c-hwmon.c +++ b/drivers/hwmon/s3c-hwmon.c @@ -166,7 +166,7 @@ static ssize_t s3c_hwmon_ch_show(struct device *dev, ret *= cfg->mult; ret = DIV_ROUND_CLOSEST(ret, cfg->div); - return snprintf(buf, PAGE_SIZE, "%d\n", ret); + return sysfs_emit(buf, "%d\n", ret); } /** @@ -187,7 +187,7 @@ static ssize_t s3c_hwmon_label_show(struct device *dev, cfg = pdata->in[sen_attr->index]; - return snprintf(buf, PAGE_SIZE, "%s\n", cfg->name); + return sysfs_emit(buf, "%s\n", cfg->name); } /** diff --git a/drivers/hwmon/sch5627.c b/drivers/hwmon/sch5627.c index 039644263101..4324a5dbc968 100644 --- a/drivers/hwmon/sch5627.c +++ b/drivers/hwmon/sch5627.c @@ -12,7 +12,6 @@ #include <linux/jiffies.h> #include <linux/platform_device.h> #include <linux/hwmon.h> -#include <linux/hwmon-sysfs.h> #include <linux/err.h> #include <linux/mutex.h> #include "sch56xx-common.h" @@ -65,7 +64,6 @@ static const char * const SCH5627_IN_LABELS[SCH5627_NO_IN] = { struct sch5627_data { unsigned short addr; - struct device *hwmon_dev; struct sch56xx_watchdog_data *watchdog; u8 control; u8 temp_max[SCH5627_NO_TEMPS]; @@ -74,66 +72,96 @@ struct sch5627_data { struct mutex update_lock; unsigned long last_battery; /* In jiffies */ - char valid; /* !=0 if following fields are valid */ - unsigned long last_updated; /* In jiffies */ + char temp_valid; /* !=0 if following fields are valid */ + char fan_valid; + char in_valid; + unsigned long temp_last_updated; /* In jiffies */ + unsigned long fan_last_updated; + unsigned long in_last_updated; u16 temp[SCH5627_NO_TEMPS]; u16 fan[SCH5627_NO_FANS]; u16 in[SCH5627_NO_IN]; }; -static struct sch5627_data *sch5627_update_device(struct device *dev) +static int sch5627_update_temp(struct sch5627_data *data) { - struct sch5627_data *data = dev_get_drvdata(dev); - struct sch5627_data *ret = data; + int ret = 0; int i, val; mutex_lock(&data->update_lock); - /* Trigger a Vbat voltage measurement every 5 minutes */ - if (time_after(jiffies, data->last_battery + 300 * HZ)) { - sch56xx_write_virtual_reg(data->addr, SCH5627_REG_CTRL, - data->control | 0x10); - data->last_battery = jiffies; - } - /* Cache the values for 1 second */ - if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { + if (time_after(jiffies, data->temp_last_updated + HZ) || !data->temp_valid) { for (i = 0; i < SCH5627_NO_TEMPS; i++) { - val = sch56xx_read_virtual_reg12(data->addr, - SCH5627_REG_TEMP_MSB[i], - SCH5627_REG_TEMP_LSN[i], - SCH5627_REG_TEMP_HIGH_NIBBLE[i]); + val = sch56xx_read_virtual_reg12(data->addr, SCH5627_REG_TEMP_MSB[i], + SCH5627_REG_TEMP_LSN[i], + SCH5627_REG_TEMP_HIGH_NIBBLE[i]); if (unlikely(val < 0)) { - ret = ERR_PTR(val); + ret = val; goto abort; } data->temp[i] = val; } + data->temp_last_updated = jiffies; + data->temp_valid = 1; + } +abort: + mutex_unlock(&data->update_lock); + return ret; +} +static int sch5627_update_fan(struct sch5627_data *data) +{ + int ret = 0; + int i, val; + + mutex_lock(&data->update_lock); + + /* Cache the values for 1 second */ + if (time_after(jiffies, data->fan_last_updated + HZ) || !data->fan_valid) { for (i = 0; i < SCH5627_NO_FANS; i++) { - val = sch56xx_read_virtual_reg16(data->addr, - SCH5627_REG_FAN[i]); + val = sch56xx_read_virtual_reg16(data->addr, SCH5627_REG_FAN[i]); if (unlikely(val < 0)) { - ret = ERR_PTR(val); + ret = val; goto abort; } data->fan[i] = val; } + data->fan_last_updated = jiffies; + data->fan_valid = 1; + } +abort: + mutex_unlock(&data->update_lock); + return ret; +} + +static int sch5627_update_in(struct sch5627_data *data) +{ + int ret = 0; + int i, val; + + mutex_lock(&data->update_lock); + /* Trigger a Vbat voltage measurement every 5 minutes */ + if (time_after(jiffies, data->last_battery + 300 * HZ)) { + sch56xx_write_virtual_reg(data->addr, SCH5627_REG_CTRL, data->control | 0x10); + data->last_battery = jiffies; + } + + /* Cache the values for 1 second */ + if (time_after(jiffies, data->in_last_updated + HZ) || !data->in_valid) { for (i = 0; i < SCH5627_NO_IN; i++) { - val = sch56xx_read_virtual_reg12(data->addr, - SCH5627_REG_IN_MSB[i], - SCH5627_REG_IN_LSN[i], - SCH5627_REG_IN_HIGH_NIBBLE[i]); + val = sch56xx_read_virtual_reg12(data->addr, SCH5627_REG_IN_MSB[i], + SCH5627_REG_IN_LSN[i], + SCH5627_REG_IN_HIGH_NIBBLE[i]); if (unlikely(val < 0)) { - ret = ERR_PTR(val); + ret = val; goto abort; } data->in[i] = val; } - - data->last_updated = jiffies; - data->valid = 1; + data->in_last_updated = jiffies; + data->in_valid = 1; } abort: mutex_unlock(&data->update_lock); @@ -192,249 +220,141 @@ static int reg_to_rpm(u16 reg) return 5400540 / reg; } -static ssize_t name_show(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%s\n", DEVNAME); -} - -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 sch5627_data *data = sch5627_update_device(dev); - int val; - - if (IS_ERR(data)) - return PTR_ERR(data); - - val = reg_to_temp(data->temp[attr->index]); - return snprintf(buf, PAGE_SIZE, "%d\n", val); -} - -static ssize_t temp_fault_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct sch5627_data *data = sch5627_update_device(dev); - - if (IS_ERR(data)) - return PTR_ERR(data); - - return snprintf(buf, PAGE_SIZE, "%d\n", data->temp[attr->index] == 0); -} - -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 sch5627_data *data = dev_get_drvdata(dev); - int val; - - val = reg_to_temp_limit(data->temp_max[attr->index]); - return snprintf(buf, PAGE_SIZE, "%d\n", val); -} - -static ssize_t temp_crit_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct sch5627_data *data = dev_get_drvdata(dev); - int val; - - val = reg_to_temp_limit(data->temp_crit[attr->index]); - return snprintf(buf, PAGE_SIZE, "%d\n", val); -} - -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 sch5627_data *data = sch5627_update_device(dev); - int val; - - if (IS_ERR(data)) - return PTR_ERR(data); - - val = reg_to_rpm(data->fan[attr->index]); - if (val < 0) - return val; - - return snprintf(buf, PAGE_SIZE, "%d\n", val); -} - -static ssize_t fan_fault_show(struct device *dev, - struct device_attribute *devattr, char *buf) +static umode_t sch5627_is_visible(const void *drvdata, enum hwmon_sensor_types type, u32 attr, + int channel) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct sch5627_data *data = sch5627_update_device(dev); - - if (IS_ERR(data)) - return PTR_ERR(data); - - return snprintf(buf, PAGE_SIZE, "%d\n", - data->fan[attr->index] == 0xffff); + return 0444; } -static ssize_t fan_min_show(struct device *dev, - struct device_attribute *devattr, char *buf) +static int sch5627_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, + long *val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct sch5627_data *data = dev_get_drvdata(dev); - int val = reg_to_rpm(data->fan_min[attr->index]); - if (val < 0) - return val; - - return snprintf(buf, PAGE_SIZE, "%d\n", val); -} - -static ssize_t in_show(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct sch5627_data *data = sch5627_update_device(dev); - int val; - - if (IS_ERR(data)) - return PTR_ERR(data); + int ret; + + switch (type) { + case hwmon_temp: + ret = sch5627_update_temp(data); + if (ret < 0) + return ret; + switch (attr) { + case hwmon_temp_input: + *val = reg_to_temp(data->temp[channel]); + return 0; + case hwmon_temp_max: + *val = reg_to_temp_limit(data->temp_max[channel]); + return 0; + case hwmon_temp_crit: + *val = reg_to_temp_limit(data->temp_crit[channel]); + return 0; + case hwmon_temp_fault: + *val = (data->temp[channel] == 0); + return 0; + default: + break; + } + break; + case hwmon_fan: + ret = sch5627_update_fan(data); + if (ret < 0) + return ret; + switch (attr) { + case hwmon_fan_input: + ret = reg_to_rpm(data->fan[channel]); + if (ret < 0) + return ret; + *val = ret; + return 0; + case hwmon_fan_min: + ret = reg_to_rpm(data->fan_min[channel]); + if (ret < 0) + return ret; + *val = ret; + return 0; + case hwmon_fan_fault: + *val = (data->fan[channel] == 0xffff); + return 0; + default: + break; + } + break; + case hwmon_in: + ret = sch5627_update_in(data); + if (ret < 0) + return ret; + switch (attr) { + case hwmon_in_input: + *val = DIV_ROUND_CLOSEST(data->in[channel] * SCH5627_REG_IN_FACTOR[channel], + 10000); + return 0; + default: + break; + } + break; + default: + break; + } - val = DIV_ROUND_CLOSEST( - data->in[attr->index] * SCH5627_REG_IN_FACTOR[attr->index], - 10000); - return snprintf(buf, PAGE_SIZE, "%d\n", val); + return -EOPNOTSUPP; } -static ssize_t in_label_show(struct device *dev, - struct device_attribute *devattr, char *buf) +static int sch5627_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, const char **str) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + switch (type) { + case hwmon_in: + switch (attr) { + case hwmon_in_label: + *str = SCH5627_IN_LABELS[channel]; + return 0; + default: + break; + } + break; + default: + break; + } - return snprintf(buf, PAGE_SIZE, "%s\n", - SCH5627_IN_LABELS[attr->index]); + return -EOPNOTSUPP; } -static DEVICE_ATTR_RO(name); -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(temp1_fault, temp_fault, 0); -static SENSOR_DEVICE_ATTR_RO(temp2_fault, temp_fault, 1); -static SENSOR_DEVICE_ATTR_RO(temp3_fault, temp_fault, 2); -static SENSOR_DEVICE_ATTR_RO(temp4_fault, temp_fault, 3); -static SENSOR_DEVICE_ATTR_RO(temp5_fault, temp_fault, 4); -static SENSOR_DEVICE_ATTR_RO(temp6_fault, temp_fault, 5); -static SENSOR_DEVICE_ATTR_RO(temp7_fault, temp_fault, 6); -static SENSOR_DEVICE_ATTR_RO(temp8_fault, temp_fault, 7); -static SENSOR_DEVICE_ATTR_RO(temp1_max, temp_max, 0); -static SENSOR_DEVICE_ATTR_RO(temp2_max, temp_max, 1); -static SENSOR_DEVICE_ATTR_RO(temp3_max, temp_max, 2); -static SENSOR_DEVICE_ATTR_RO(temp4_max, temp_max, 3); -static SENSOR_DEVICE_ATTR_RO(temp5_max, temp_max, 4); -static SENSOR_DEVICE_ATTR_RO(temp6_max, temp_max, 5); -static SENSOR_DEVICE_ATTR_RO(temp7_max, temp_max, 6); -static SENSOR_DEVICE_ATTR_RO(temp8_max, temp_max, 7); -static SENSOR_DEVICE_ATTR_RO(temp1_crit, temp_crit, 0); -static SENSOR_DEVICE_ATTR_RO(temp2_crit, temp_crit, 1); -static SENSOR_DEVICE_ATTR_RO(temp3_crit, temp_crit, 2); -static SENSOR_DEVICE_ATTR_RO(temp4_crit, temp_crit, 3); -static SENSOR_DEVICE_ATTR_RO(temp5_crit, temp_crit, 4); -static SENSOR_DEVICE_ATTR_RO(temp6_crit, temp_crit, 5); -static SENSOR_DEVICE_ATTR_RO(temp7_crit, temp_crit, 6); -static SENSOR_DEVICE_ATTR_RO(temp8_crit, temp_crit, 7); - -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_fault, fan_fault, 0); -static SENSOR_DEVICE_ATTR_RO(fan2_fault, fan_fault, 1); -static SENSOR_DEVICE_ATTR_RO(fan3_fault, fan_fault, 2); -static SENSOR_DEVICE_ATTR_RO(fan4_fault, fan_fault, 3); -static SENSOR_DEVICE_ATTR_RO(fan1_min, fan_min, 0); -static SENSOR_DEVICE_ATTR_RO(fan2_min, fan_min, 1); -static SENSOR_DEVICE_ATTR_RO(fan3_min, fan_min, 2); -static SENSOR_DEVICE_ATTR_RO(fan4_min, fan_min, 3); - -static SENSOR_DEVICE_ATTR_RO(in0_input, in, 0); -static SENSOR_DEVICE_ATTR_RO(in1_input, in, 1); -static SENSOR_DEVICE_ATTR_RO(in2_input, in, 2); -static SENSOR_DEVICE_ATTR_RO(in3_input, in, 3); -static SENSOR_DEVICE_ATTR_RO(in4_input, in, 4); -static SENSOR_DEVICE_ATTR_RO(in0_label, in_label, 0); -static SENSOR_DEVICE_ATTR_RO(in1_label, in_label, 1); -static SENSOR_DEVICE_ATTR_RO(in2_label, in_label, 2); -static SENSOR_DEVICE_ATTR_RO(in3_label, in_label, 3); - -static struct attribute *sch5627_attributes[] = { - &dev_attr_name.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_temp1_fault.dev_attr.attr, - &sensor_dev_attr_temp2_fault.dev_attr.attr, - &sensor_dev_attr_temp3_fault.dev_attr.attr, - &sensor_dev_attr_temp4_fault.dev_attr.attr, - &sensor_dev_attr_temp5_fault.dev_attr.attr, - &sensor_dev_attr_temp6_fault.dev_attr.attr, - &sensor_dev_attr_temp7_fault.dev_attr.attr, - &sensor_dev_attr_temp8_fault.dev_attr.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_temp1_crit.dev_attr.attr, - &sensor_dev_attr_temp2_crit.dev_attr.attr, - &sensor_dev_attr_temp3_crit.dev_attr.attr, - &sensor_dev_attr_temp4_crit.dev_attr.attr, - &sensor_dev_attr_temp5_crit.dev_attr.attr, - &sensor_dev_attr_temp6_crit.dev_attr.attr, - &sensor_dev_attr_temp7_crit.dev_attr.attr, - &sensor_dev_attr_temp8_crit.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_fault.dev_attr.attr, - &sensor_dev_attr_fan2_fault.dev_attr.attr, - &sensor_dev_attr_fan3_fault.dev_attr.attr, - &sensor_dev_attr_fan4_fault.dev_attr.attr, - &sensor_dev_attr_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_in0_input.dev_attr.attr, - &sensor_dev_attr_in1_input.dev_attr.attr, - &sensor_dev_attr_in2_input.dev_attr.attr, - &sensor_dev_attr_in3_input.dev_attr.attr, - &sensor_dev_attr_in4_input.dev_attr.attr, - &sensor_dev_attr_in0_label.dev_attr.attr, - &sensor_dev_attr_in1_label.dev_attr.attr, - &sensor_dev_attr_in2_label.dev_attr.attr, - &sensor_dev_attr_in3_label.dev_attr.attr, - /* No in4_label as in4 is a generic input pin */ +static const struct hwmon_ops sch5627_ops = { + .is_visible = sch5627_is_visible, + .read = sch5627_read, + .read_string = sch5627_read_string, +}; +static const struct hwmon_channel_info *sch5627_info[] = { + HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ), + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT + ), + HWMON_CHANNEL_INFO(fan, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_FAULT + ), + 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 + ), NULL }; -static const struct attribute_group sch5627_group = { - .attrs = sch5627_attributes, +static const struct hwmon_chip_info sch5627_chip_info = { + .ops = &sch5627_ops, + .info = sch5627_info, }; static int sch5627_remove(struct platform_device *pdev) @@ -444,17 +364,13 @@ static int sch5627_remove(struct platform_device *pdev) if (data->watchdog) sch56xx_watchdog_unregister(data->watchdog); - if (data->hwmon_dev) - hwmon_device_unregister(data->hwmon_dev); - - sysfs_remove_group(&pdev->dev.kobj, &sch5627_group); - return 0; } static int sch5627_probe(struct platform_device *pdev) { struct sch5627_data *data; + struct device *hwmon_dev; int err, build_code, build_id, hwmon_rev, val; data = devm_kzalloc(&pdev->dev, sizeof(struct sch5627_data), @@ -467,72 +383,58 @@ static int sch5627_probe(struct platform_device *pdev) platform_set_drvdata(pdev, data); val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_HWMON_ID); - if (val < 0) { - err = val; - goto error; - } + if (val < 0) + return val; + if (val != SCH5627_HWMON_ID) { pr_err("invalid %s id: 0x%02X (expected 0x%02X)\n", "hwmon", val, SCH5627_HWMON_ID); - err = -ENODEV; - goto error; + return -ENODEV; } val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_COMPANY_ID); - if (val < 0) { - err = val; - goto error; - } + if (val < 0) + return val; + if (val != SCH5627_COMPANY_ID) { pr_err("invalid %s id: 0x%02X (expected 0x%02X)\n", "company", val, SCH5627_COMPANY_ID); - err = -ENODEV; - goto error; + return -ENODEV; } val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_PRIMARY_ID); - if (val < 0) { - err = val; - goto error; - } + if (val < 0) + return val; + if (val != SCH5627_PRIMARY_ID) { pr_err("invalid %s id: 0x%02X (expected 0x%02X)\n", "primary", val, SCH5627_PRIMARY_ID); - err = -ENODEV; - goto error; + return -ENODEV; } build_code = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_BUILD_CODE); - if (build_code < 0) { - err = build_code; - goto error; - } + if (build_code < 0) + return build_code; build_id = sch56xx_read_virtual_reg16(data->addr, SCH5627_REG_BUILD_ID); - if (build_id < 0) { - err = build_id; - goto error; - } + if (build_id < 0) + return build_id; hwmon_rev = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_HWMON_REV); - if (hwmon_rev < 0) { - err = hwmon_rev; - goto error; - } + if (hwmon_rev < 0) + return hwmon_rev; val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_CTRL); - if (val < 0) { - err = val; - goto error; - } + if (val < 0) + return val; + data->control = val; if (!(data->control & 0x01)) { pr_err("hardware monitoring not enabled\n"); - err = -ENODEV; - goto error; + return -ENODEV; } /* Trigger a Vbat voltage measurement, so that we get a valid reading the first time we read Vbat */ @@ -546,23 +448,16 @@ static int sch5627_probe(struct platform_device *pdev) */ err = sch5627_read_limits(data); if (err) - goto error; + return err; pr_info("found %s chip at %#hx\n", DEVNAME, data->addr); pr_info("firmware build: code 0x%02X, id 0x%04X, hwmon: rev 0x%02X\n", build_code, build_id, hwmon_rev); - /* Register sysfs interface files */ - err = sysfs_create_group(&pdev->dev.kobj, &sch5627_group); - if (err) - goto error; - - data->hwmon_dev = hwmon_device_register(&pdev->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - data->hwmon_dev = NULL; - goto error; - } + hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, DEVNAME, data, + &sch5627_chip_info, NULL); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); /* Note failing to register the watchdog is not a fatal error */ data->watchdog = sch56xx_watchdog_register(&pdev->dev, data->addr, @@ -570,10 +465,6 @@ static int sch5627_probe(struct platform_device *pdev) &data->update_lock, 1); return 0; - -error: - sch5627_remove(pdev); - return err; } static struct platform_driver sch5627_driver = { diff --git a/drivers/hwmon/sch5636.c b/drivers/hwmon/sch5636.c index 200bb2bfc986..5683a38740f6 100644 --- a/drivers/hwmon/sch5636.c +++ b/drivers/hwmon/sch5636.c @@ -160,7 +160,7 @@ static int reg_to_rpm(u16 reg) static ssize_t name_show(struct device *dev, struct device_attribute *devattr, char *buf) { - return snprintf(buf, PAGE_SIZE, "%s\n", DEVNAME); + return sysfs_emit(buf, "%s\n", DEVNAME); } static ssize_t in_value_show(struct device *dev, @@ -176,7 +176,7 @@ static ssize_t in_value_show(struct device *dev, val = DIV_ROUND_CLOSEST( data->in[attr->index] * SCH5636_REG_IN_FACTORS[attr->index], 255); - return snprintf(buf, PAGE_SIZE, "%d\n", val); + return sysfs_emit(buf, "%d\n", val); } static ssize_t in_label_show(struct device *dev, @@ -184,8 +184,8 @@ static ssize_t in_label_show(struct device *dev, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - return snprintf(buf, PAGE_SIZE, "%s\n", - SCH5636_IN_LABELS[attr->index]); + return sysfs_emit(buf, "%s\n", + SCH5636_IN_LABELS[attr->index]); } static ssize_t temp_value_show(struct device *dev, @@ -199,7 +199,7 @@ static ssize_t temp_value_show(struct device *dev, return PTR_ERR(data); val = (data->temp_val[attr->index] - 64) * 1000; - return snprintf(buf, PAGE_SIZE, "%d\n", val); + return sysfs_emit(buf, "%d\n", val); } static ssize_t temp_fault_show(struct device *dev, @@ -213,7 +213,7 @@ static ssize_t temp_fault_show(struct device *dev, return PTR_ERR(data); val = (data->temp_ctrl[attr->index] & SCH5636_TEMP_WORKING) ? 0 : 1; - return snprintf(buf, PAGE_SIZE, "%d\n", val); + return sysfs_emit(buf, "%d\n", val); } static ssize_t temp_alarm_show(struct device *dev, @@ -227,7 +227,7 @@ static ssize_t temp_alarm_show(struct device *dev, return PTR_ERR(data); val = (data->temp_ctrl[attr->index] & SCH5636_TEMP_ALARM) ? 1 : 0; - return snprintf(buf, PAGE_SIZE, "%d\n", val); + return sysfs_emit(buf, "%d\n", val); } static ssize_t fan_value_show(struct device *dev, @@ -244,7 +244,7 @@ static ssize_t fan_value_show(struct device *dev, if (val < 0) return val; - return snprintf(buf, PAGE_SIZE, "%d\n", val); + return sysfs_emit(buf, "%d\n", val); } static ssize_t fan_fault_show(struct device *dev, @@ -258,7 +258,7 @@ static ssize_t fan_fault_show(struct device *dev, return PTR_ERR(data); val = (data->fan_ctrl[attr->index] & SCH5636_FAN_NOT_PRESENT) ? 1 : 0; - return snprintf(buf, PAGE_SIZE, "%d\n", val); + return sysfs_emit(buf, "%d\n", val); } static ssize_t fan_alarm_show(struct device *dev, @@ -272,7 +272,7 @@ static ssize_t fan_alarm_show(struct device *dev, return PTR_ERR(data); val = (data->fan_ctrl[attr->index] & SCH5636_FAN_ALARM) ? 1 : 0; - return snprintf(buf, PAGE_SIZE, "%d\n", val); + return sysfs_emit(buf, "%d\n", val); } static struct sensor_device_attribute sch5636_attr[] = { diff --git a/drivers/hwmon/smm665.c b/drivers/hwmon/smm665.c index b6cbe9810a1b..62906d9c4b86 100644 --- a/drivers/hwmon/smm665.c +++ b/drivers/hwmon/smm665.c @@ -351,7 +351,7 @@ static ssize_t smm665_show_crit_alarm(struct device *dev, if (data->faults & (1 << attr->index)) val = 1; - return snprintf(buf, PAGE_SIZE, "%d\n", val); + return sysfs_emit(buf, "%d\n", val); } static ssize_t smm665_show_input(struct device *dev, @@ -366,7 +366,7 @@ static ssize_t smm665_show_input(struct device *dev, return PTR_ERR(data); val = smm665_convert(data->adc[adc], adc); - return snprintf(buf, PAGE_SIZE, "%d\n", val); + return sysfs_emit(buf, "%d\n", val); } #define SMM665_SHOW(what) \ diff --git a/drivers/hwmon/stts751.c b/drivers/hwmon/stts751.c index 6928be6dbe4e..0ed28408aa07 100644 --- a/drivers/hwmon/stts751.c +++ b/drivers/hwmon/stts751.c @@ -387,7 +387,7 @@ static ssize_t max_alarm_show(struct device *dev, if (ret < 0) return ret; - return snprintf(buf, PAGE_SIZE, "%d\n", priv->max_alert); + return sysfs_emit(buf, "%d\n", priv->max_alert); } static ssize_t min_alarm_show(struct device *dev, @@ -404,7 +404,7 @@ static ssize_t min_alarm_show(struct device *dev, if (ret < 0) return ret; - return snprintf(buf, PAGE_SIZE, "%d\n", priv->min_alert); + return sysfs_emit(buf, "%d\n", priv->min_alert); } static ssize_t input_show(struct device *dev, struct device_attribute *attr, @@ -419,7 +419,7 @@ static ssize_t input_show(struct device *dev, struct device_attribute *attr, if (ret < 0) return ret; - return snprintf(buf, PAGE_SIZE, "%d\n", priv->temp); + return sysfs_emit(buf, "%d\n", priv->temp); } static ssize_t therm_show(struct device *dev, struct device_attribute *attr, @@ -427,7 +427,7 @@ static ssize_t therm_show(struct device *dev, struct device_attribute *attr, { struct stts751_priv *priv = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "%d\n", priv->therm); + return sysfs_emit(buf, "%d\n", priv->therm); } static ssize_t therm_store(struct device *dev, struct device_attribute *attr, @@ -469,7 +469,7 @@ static ssize_t hyst_show(struct device *dev, struct device_attribute *attr, { struct stts751_priv *priv = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "%d\n", priv->hyst); + return sysfs_emit(buf, "%d\n", priv->hyst); } static ssize_t hyst_store(struct device *dev, struct device_attribute *attr, @@ -509,7 +509,7 @@ static ssize_t therm_trip_show(struct device *dev, if (ret < 0) return ret; - return snprintf(buf, PAGE_SIZE, "%d\n", priv->therm_trip); + return sysfs_emit(buf, "%d\n", priv->therm_trip); } static ssize_t max_show(struct device *dev, struct device_attribute *attr, @@ -517,7 +517,7 @@ static ssize_t max_show(struct device *dev, struct device_attribute *attr, { struct stts751_priv *priv = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "%d\n", priv->event_max); + return sysfs_emit(buf, "%d\n", priv->event_max); } static ssize_t max_store(struct device *dev, struct device_attribute *attr, @@ -551,7 +551,7 @@ static ssize_t min_show(struct device *dev, struct device_attribute *attr, { struct stts751_priv *priv = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "%d\n", priv->event_min); + return sysfs_emit(buf, "%d\n", priv->event_min); } static ssize_t min_store(struct device *dev, struct device_attribute *attr, @@ -585,8 +585,8 @@ static ssize_t interval_show(struct device *dev, { struct stts751_priv *priv = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "%d\n", - stts751_intervals[priv->interval]); + return sysfs_emit(buf, "%d\n", + stts751_intervals[priv->interval]); } static ssize_t interval_store(struct device *dev, diff --git a/drivers/hwmon/vexpress-hwmon.c b/drivers/hwmon/vexpress-hwmon.c index e7109657129a..44d798be3d59 100644 --- a/drivers/hwmon/vexpress-hwmon.c +++ b/drivers/hwmon/vexpress-hwmon.c @@ -27,7 +27,7 @@ static ssize_t vexpress_hwmon_label_show(struct device *dev, { const char *label = of_get_property(dev->of_node, "label", NULL); - return snprintf(buffer, PAGE_SIZE, "%s\n", label); + return sysfs_emit(buffer, "%s\n", label); } static ssize_t vexpress_hwmon_u32_show(struct device *dev, @@ -41,8 +41,8 @@ static ssize_t vexpress_hwmon_u32_show(struct device *dev, if (err) return err; - return snprintf(buffer, PAGE_SIZE, "%u\n", value / - to_sensor_dev_attr(dev_attr)->index); + return sysfs_emit(buffer, "%u\n", value / + to_sensor_dev_attr(dev_attr)->index); } static ssize_t vexpress_hwmon_u64_show(struct device *dev, @@ -60,9 +60,9 @@ static ssize_t vexpress_hwmon_u64_show(struct device *dev, if (err) return err; - return snprintf(buffer, PAGE_SIZE, "%llu\n", - div_u64(((u64)value_hi << 32) | value_lo, - to_sensor_dev_attr(dev_attr)->index)); + return sysfs_emit(buffer, "%llu\n", + div_u64(((u64)value_hi << 32) | value_lo, + to_sensor_dev_attr(dev_attr)->index)); } static umode_t vexpress_hwmon_attr_is_visible(struct kobject *kobj, diff --git a/drivers/hwmon/xgene-hwmon.c b/drivers/hwmon/xgene-hwmon.c index 1489e83cb0c4..382ef0395d8e 100644 --- a/drivers/hwmon/xgene-hwmon.c +++ b/drivers/hwmon/xgene-hwmon.c @@ -329,14 +329,14 @@ static ssize_t temp1_input_show(struct device *dev, temp = sign_extend32(val, TEMP_NEGATIVE_BIT); - return snprintf(buf, PAGE_SIZE, "%d\n", CELSIUS_TO_mCELSIUS(temp)); + return sysfs_emit(buf, "%d\n", CELSIUS_TO_mCELSIUS(temp)); } static ssize_t temp1_label_show(struct device *dev, struct device_attribute *attr, char *buf) { - return snprintf(buf, PAGE_SIZE, "SoC Temperature\n"); + return sysfs_emit(buf, "SoC Temperature\n"); } static ssize_t temp1_critical_alarm_show(struct device *dev, @@ -345,21 +345,21 @@ static ssize_t temp1_critical_alarm_show(struct device *dev, { struct xgene_hwmon_dev *ctx = dev_get_drvdata(dev); - return snprintf(buf, PAGE_SIZE, "%d\n", ctx->temp_critical_alarm); + return sysfs_emit(buf, "%d\n", ctx->temp_critical_alarm); } static ssize_t power1_label_show(struct device *dev, struct device_attribute *attr, char *buf) { - return snprintf(buf, PAGE_SIZE, "CPU power\n"); + return sysfs_emit(buf, "CPU power\n"); } static ssize_t power2_label_show(struct device *dev, struct device_attribute *attr, char *buf) { - return snprintf(buf, PAGE_SIZE, "IO power\n"); + return sysfs_emit(buf, "IO power\n"); } static ssize_t power1_input_show(struct device *dev, @@ -374,7 +374,7 @@ static ssize_t power1_input_show(struct device *dev, if (rc < 0) return rc; - return snprintf(buf, PAGE_SIZE, "%u\n", mWATT_TO_uWATT(val)); + return sysfs_emit(buf, "%u\n", mWATT_TO_uWATT(val)); } static ssize_t power2_input_show(struct device *dev, @@ -389,7 +389,7 @@ static ssize_t power2_input_show(struct device *dev, if (rc < 0) return rc; - return snprintf(buf, PAGE_SIZE, "%u\n", mWATT_TO_uWATT(val)); + return sysfs_emit(buf, "%u\n", mWATT_TO_uWATT(val)); } static DEVICE_ATTR_RO(temp1_label); |