diff options
34 files changed, 3613 insertions, 1496 deletions
diff --git a/Documentation/devicetree/bindings/hwmon/adi,adm1177.yaml b/Documentation/devicetree/bindings/hwmon/adi,adm1177.yaml new file mode 100644 index 000000000000..2a9822075b36 --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/adi,adm1177.yaml @@ -0,0 +1,66 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/hwmon/adi,adm1177.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices ADM1177 Hot Swap Controller and Digital Power Monitor + +maintainers: + - Michael Hennerich <michael.hennerich@analog.com> + - Beniamin Bia <beniamin.bia@analog.com> + +description: | + Analog Devices ADM1177 Hot Swap Controller and Digital Power Monitor + https://www.analog.com/media/en/technical-documentation/data-sheets/ADM1177.pdf + +properties: + compatible: + enum: + - adi,adm1177 + + reg: + maxItems: 1 + + avcc-supply: + description: + Phandle to the Avcc power supply + + shunt-resistor-micro-ohms: + description: + The value of curent sense resistor in microohms. If not provided, + the current reading and overcurrent alert is disabled. + + adi,shutdown-threshold-microamp: + description: + Specifies the current level at which an over current alert occurs. + If not provided, the overcurrent alert is configured to max ADC range + based on shunt-resistor-micro-ohms. + + adi,vrange-high-enable: + description: + Specifies which internal voltage divider to be used. A 1 selects + a 7:2 voltage divider while a 0 selects a 14:1 voltage divider. + type: boolean + +required: + - compatible + - reg + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + #include <dt-bindings/interrupt-controller/irq.h> + i2c0 { + #address-cells = <1>; + #size-cells = <0>; + + pwmon@5a { + compatible = "adi,adm1177"; + reg = <0x5a>; + shunt-resistor-micro-ohms = <50000>; /* 50 mOhm */ + adi,shutdown-threshold-microamp = <1059000>; /* 1.059 A */ + adi,vrange-high-enable; + }; + }; +... diff --git a/Documentation/devicetree/bindings/hwmon/pmbus/ti,ucd90320.yaml b/Documentation/devicetree/bindings/hwmon/pmbus/ti,ucd90320.yaml new file mode 100644 index 000000000000..5d42e1304202 --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/pmbus/ti,ucd90320.yaml @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- + +$id: http://devicetree.org/schemas/hwmon/pmbus/ti,ucd90320.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: UCD90320 power sequencer + +maintainers: + - Jim Wright <wrightj@linux.vnet.ibm.com> + +description: | + The UCD90320 is a 32-rail PMBus/I2C addressable power-supply sequencer and + monitor. The 24 integrated ADC channels (AMONx) monitor the power supply + voltage, current, and temperature. Of the 84 GPIO pins, 8 can be used as + digital monitors (DMONx), 32 to enable the power supply (ENx), 24 for + margining (MARx), 16 for logical GPO, and 32 GPIs for cascading, and system + function. + + http://focus.ti.com/lit/ds/symlink/ucd90320.pdf + +properties: + compatible: + enum: + - ti,ucd90320 + + reg: + maxItems: 1 + +required: + - compatible + - reg + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + ucd90320@11 { + compatible = "ti,ucd90320"; + reg = <0x11>; + }; + }; diff --git a/Documentation/hwmon/adm1177.rst b/Documentation/hwmon/adm1177.rst new file mode 100644 index 000000000000..c81e0b4abd28 --- /dev/null +++ b/Documentation/hwmon/adm1177.rst @@ -0,0 +1,36 @@ +Kernel driver adm1177 +===================== + +Supported chips: + * Analog Devices ADM1177 + Prefix: 'adm1177' + Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADM1177.pdf + +Author: Beniamin Bia <beniamin.bia@analog.com> + + +Description +----------- + +This driver supports hardware monitoring for Analog Devices ADM1177 +Hot-Swap Controller and Digital Power Monitors with Soft Start Pin. + + +Usage Notes +----------- + +This driver does not auto-detect devices. You will have to instantiate the +devices explicitly. Please see Documentation/i2c/instantiating-devices for +details. + + +Sysfs entries +------------- + +The following attributes are supported. Current maxim attribute +is read-write, all other attributes are read-only. + +in0_input Measured voltage in microvolts. + +curr1_input Measured current in microamperes. +curr1_max_alarm Overcurrent alarm in microamperes. diff --git a/Documentation/hwmon/drivetemp.rst b/Documentation/hwmon/drivetemp.rst new file mode 100644 index 000000000000..2d37d049247f --- /dev/null +++ b/Documentation/hwmon/drivetemp.rst @@ -0,0 +1,52 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Kernel driver drivetemp +======================= + + +References +---------- + +ANS T13/1699-D +Information technology - AT Attachment 8 - ATA/ATAPI Command Set (ATA8-ACS) + +ANS Project T10/BSR INCITS 513 +Information technology - SCSI Primary Commands - 4 (SPC-4) + +ANS Project INCITS 557 +Information technology - SCSI / ATA Translation - 5 (SAT-5) + + +Description +----------- + +This driver supports reporting the temperature of disk and solid state +drives with temperature sensors. + +If supported, it uses the ATA SCT Command Transport feature to read +the current drive temperature and, if available, temperature limits +as well as historic minimum and maximum temperatures. If SCT Command +Transport is not supported, the driver uses SMART attributes to read +the drive temperature. + + +Sysfs entries +------------- + +Only the temp1_input attribute is always available. Other attributes are +available only if reported by the drive. All temperatures are reported in +milli-degrees Celsius. + +======================= ===================================================== +temp1_input Current drive temperature +temp1_lcrit Minimum temperature limit. Operating the device below + this temperature may cause physical damage to the + device. +temp1_min Minimum recommended continuous operating limit +temp1_max Maximum recommended continuous operating temperature +temp1_crit Maximum temperature limit. Operating the device above + this temperature may cause physical damage to the + device. +temp1_lowest Minimum temperature seen this power cycle +temp1_highest Maximum temperature seen this power cycle +======================= ===================================================== diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index 43cc605741ea..b24adb67ddca 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -29,6 +29,7 @@ Hardware Monitoring Kernel Drivers adm1025 adm1026 adm1031 + adm1177 adm1275 adm9240 ads7828 @@ -47,6 +48,7 @@ Hardware Monitoring Kernel Drivers da9055 dell-smm-hwmon dme1737 + drivetemp ds1621 ds620 emc1403 @@ -106,8 +108,10 @@ Hardware Monitoring Kernel Drivers max1619 max1668 max197 + max20730 max20751 max31722 + max31730 max31785 max31790 max34440 @@ -177,6 +181,7 @@ Hardware Monitoring Kernel Drivers wm831x wm8350 xgene-hwmon + xdpe12284 zl6100 .. only:: subproject and html diff --git a/Documentation/hwmon/max20730.rst b/Documentation/hwmon/max20730.rst new file mode 100644 index 000000000000..cea7ae58c2f7 --- /dev/null +++ b/Documentation/hwmon/max20730.rst @@ -0,0 +1,74 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Kernel driver max20730 +====================== + +Supported chips: + + * Maxim MAX20730 + + Prefix: 'max20730' + + Addresses scanned: - + + Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX20730.pdf + + * Maxim MAX20734 + + Prefix: 'max20734' + + Addresses scanned: - + + Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX20734.pdf + + * Maxim MAX20743 + + Prefix: 'max20743' + + Addresses scanned: - + + Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX20743.pdf + +Author: Guenter Roeck <linux@roeck-us.net> + + +Description +----------- + +This driver implements support for Maxim MAX20730, MAX20734, and MAX20743 +Integrated, Step-Down Switching Regulators with PMBus support. + +The driver is a client driver to the core PMBus driver. +Please see Documentation/hwmon/pmbus.rst for details on PMBus client drivers. + + +Usage Notes +----------- + +This driver does not auto-detect devices. You will have to instantiate the +devices explicitly. Please see Documentation/i2c/instantiating-devices.rst for +details. + + +Sysfs entries +------------- + +=================== ===== ======================================================= +curr1_crit RW/RO Critical output current. Please see datasheet for + supported limits. Read-only if the chip is + write protected; read-write otherwise. +curr1_crit_alarm RO Output current critical alarm +curr1_input RO Output current +curr1_label RO 'iout1' +in1_alarm RO Input voltage alarm +in1_input RO Input voltage +in1_label RO 'vin' +in2_alarm RO Output voltage alarm +in2_input RO Output voltage +in2_label RO 'vout1' +temp1_crit RW/RO Critical temeperature. Supported values are 130 or 150 + degrees C. Read-only if the chip is write protected; + read-write otherwise. +temp1_crit_alarm RO Temperature critical alarm +temp1_input RO Chip temperature +=================== ===== ======================================================= diff --git a/Documentation/hwmon/max31730.rst b/Documentation/hwmon/max31730.rst new file mode 100644 index 000000000000..def0de19dbd2 --- /dev/null +++ b/Documentation/hwmon/max31730.rst @@ -0,0 +1,44 @@ +Kernel driver max31790 +====================== + +Supported chips: + + * Maxim MAX31730 + + Prefix: 'max31730' + + Addresses scanned: 0x1c, 0x1d, 0x1e, 0x1f, 0x4c, 0x4d, 0x4e, 0x4f + + Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX31730.pdf + +Author: Guenter Roeck <linux@roeck-us.net> + + +Description +----------- + +This driver implements support for Maxim MAX31730. + +The MAX31730 temperature sensor monitors its own temperature and the +temperatures of three external diode-connected transistors. The operating +supply voltage is from 3.0V to 3.6V. Resistance cancellation compensates +for high series resistance in circuit-board traces and the external thermal +diode, while beta compensation corrects for temperature-measurement +errors due to low-beta sensing transistors. + + +Sysfs entries +------------- + +=================== == ======================================================= +temp[1-4]_enable RW Temperature enable/disable + Set to 0 to enable channel, 0 to disable +temp[1-4]_input RO Temperature input +temp[2-4]_fault RO Fault indicator for remote channels +temp[1-4]_max RW Maximum temperature +temp[1-4]_max_alarm RW Maximum temperature alarm +temp[1-4]_min RW Minimum temperature. Common for all channels. + Only temp1_min is writeable. +temp[1-4]_min_alarm RO Minimum temperature alarm +temp[2-4]_offset RW Temperature offset for remote channels +=================== == ======================================================= diff --git a/Documentation/hwmon/pmbus.rst b/Documentation/hwmon/pmbus.rst index abfb9dd4857d..f787984e88a9 100644 --- a/Documentation/hwmon/pmbus.rst +++ b/Documentation/hwmon/pmbus.rst @@ -63,6 +63,16 @@ Supported chips: http://www.ti.com/lit/gpn/tps544c25 + * Maxim MAX20796 + + Prefix: 'max20796' + + Addresses scanned: - + + Datasheet: + + Not published + * Generic PMBus devices Prefix: 'pmbus' diff --git a/Documentation/hwmon/ucd9000.rst b/Documentation/hwmon/ucd9000.rst index 746f21fcb48c..704f0cbd95d3 100644 --- a/Documentation/hwmon/ucd9000.rst +++ b/Documentation/hwmon/ucd9000.rst @@ -3,9 +3,10 @@ Kernel driver ucd9000 Supported chips: - * TI UCD90120, UCD90124, UCD90160, UCD9090, and UCD90910 + * TI UCD90120, UCD90124, UCD90160, UCD90320, UCD9090, and UCD90910 - Prefixes: 'ucd90120', 'ucd90124', 'ucd90160', 'ucd9090', 'ucd90910' + Prefixes: 'ucd90120', 'ucd90124', 'ucd90160', 'ucd90320', 'ucd9090', + 'ucd90910' Addresses scanned: - @@ -14,6 +15,7 @@ Supported chips: - http://focus.ti.com/lit/ds/symlink/ucd90120.pdf - http://focus.ti.com/lit/ds/symlink/ucd90124.pdf - http://focus.ti.com/lit/ds/symlink/ucd90160.pdf + - http://focus.ti.com/lit/ds/symlink/ucd90320.pdf - http://focus.ti.com/lit/ds/symlink/ucd9090.pdf - http://focus.ti.com/lit/ds/symlink/ucd90910.pdf @@ -45,6 +47,12 @@ power-on reset signals, external interrupts, cascading, or other system functions. Twelve of these pins offer PWM functionality. Using these pins, the UCD90160 offers support for margining, and general-purpose PWM functions. +The UCD90320 is a 32-rail PMBus/I2C addressable power-supply sequencer and +monitor. The 24 integrated ADC channels (AMONx) monitor the power supply +voltage, current, and temperature. Of the 84 GPIO pins, 8 can be used as +digital monitors (DMONx), 32 to enable the power supply (ENx), 24 for margining +(MARx), 16 for logical GPO, and 32 GPIs for cascading, and system function. + The UCD9090 is a 10-rail PMBus/I2C addressable power-supply sequencer and monitor. The device integrates a 12-bit ADC for monitoring up to 10 power-supply voltage inputs. Twenty-three GPIO pins can be used for power supply enables, diff --git a/Documentation/hwmon/xdpe12284.rst b/Documentation/hwmon/xdpe12284.rst new file mode 100644 index 000000000000..6b7ae98cc536 --- /dev/null +++ b/Documentation/hwmon/xdpe12284.rst @@ -0,0 +1,101 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Kernel driver xdpe122 +===================== + +Supported chips: + + * Infineon XDPE12254 + + Prefix: 'xdpe12254' + + * Infineon XDPE12284 + + Prefix: 'xdpe12284' + +Authors: + + Vadim Pasternak <vadimp@mellanox.com> + +Description +----------- + +This driver implements support for Infineon Multi-phase XDPE122 family +dual loop voltage regulators. +The family includes XDPE12284 and XDPE12254 devices. +The devices from this family complaint with: +- Intel VR13 and VR13HC rev 1.3, IMVP8 rev 1.2 and IMPVP9 rev 1.3 DC-DC + converter specification. +- Intel SVID rev 1.9. protocol. +- PMBus rev 1.3 interface. + +Devices support linear format for reading input voltage, input and output current, +input and output power and temperature. +Device supports VID format for reading output voltage. The below modes are +supported: +- VR12.0 mode, 5-mV DAC - 0x01. +- VR12.5 mode, 10-mV DAC - 0x02. +- IMVP9 mode, 5-mV DAC - 0x03. +- AMD mode 6.25mV - 0x10. + +Devices support two pages for telemetry. + +The driver provides for current: input, maximum and critical thresholds +and maximum and critical alarms. Critical thresholds and critical alarm are +supported only for current output. +The driver exports the following attributes for via the sysfs files, where +indexes 1, 2 are for "iin" and 3, 4 for "iout": + +**curr[3-4]_crit** + +**curr[3-4]_crit_alarm** + +**curr[1-4]_input** + +**curr[1-4]_label** + +**curr[1-4]_max** + +**curr[1-4]_max_alarm** + +The driver provides for voltage: input, critical and low critical thresholds +and critical and low critical alarms. +The driver exports the following attributes for via the sysfs files, where +indexes 1, 2 are for "vin" and 3, 4 for "vout": + +**in[1-4]_crit** + +**in[1-4_crit_alarm** + +**in[1-4]_input** + +**in[1-4_label** + +**in[1-4]_lcrit** + +**in[1-41_lcrit_alarm** + +The driver provides for power: input and alarms. Power alarm is supported only +for power input. +The driver exports the following attributes for via the sysfs files, where +indexes 1, 2 are for "pin" and 3, 4 for "pout": + +**power[1-2]_alarm** + +**power[1-4]_input** + +**power[1-4]_label** + +The driver provides for temperature: input, maximum and critical thresholds +and maximum and critical alarms. +The driver exports the following attributes for via the sysfs files: + +**temp[1-2]_crit** + +**temp[1-2]_crit_alarm** + +**temp[1-2]_input** + +**temp[1-2]_max** + +**temp[1-2]_max_alarm** diff --git a/MAINTAINERS b/MAINTAINERS index 56765f542244..ae8ff55f90df 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -977,6 +977,15 @@ W: http://ez.analog.com/community/linux-device-drivers F: drivers/iio/imu/adis16460.c F: Documentation/devicetree/bindings/iio/imu/adi,adis16460.yaml +ANALOG DEVICES INC ADM1177 DRIVER +M: Beniamin Bia <beniamin.bia@analog.com> +M: Michael Hennerich <Michael.Hennerich@analog.com> +L: linux-hwmon@vger.kernel.org +W: http://ez.analog.com/community/linux-device-drivers +S: Supported +F: drivers/hwmon/adm1177.c +F: Documentation/devicetree/bindings/hwmon/adi,adm1177.yaml + ANALOG DEVICES INC ADP5061 DRIVER M: Stefan Popa <stefan.popa@analog.com> L: linux-pm@vger.kernel.org diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 23dfe848979a..47ac20aee06f 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -164,6 +164,16 @@ config SENSORS_ADM1031 This driver can also be built as a module. If so, the module will be called adm1031. +config SENSORS_ADM1177 + tristate "Analog Devices ADM1177 and compatibles" + depends on I2C + help + If you say yes here you get support for Analog Devices ADM1177 + sensor chips. + + This driver can also be built as a module. If so, the module + will be called adm1177. + config SENSORS_ADM9240 tristate "Analog Devices ADM9240 and compatibles" depends on I2C @@ -385,6 +395,16 @@ config SENSORS_ATXP1 This driver can also be built as a module. If so, the module will be called atxp1. +config SENSORS_DRIVETEMP + tristate "Hard disk drives with temperature sensors" + depends on SCSI && ATA + help + If you say yes you get support for the temperature sensor on + hard disk drives. + + This driver can also be built as a module. If so, the module + will be called satatemp. + config SENSORS_DS620 tristate "Dallas Semiconductor DS620" depends on I2C @@ -889,7 +909,7 @@ config SENSORS_MAX197 will be called max197. config SENSORS_MAX31722 -tristate "MAX31722 temperature sensor" + tristate "MAX31722 temperature sensor" depends on SPI help Support for the Maxim Integrated MAX31722/MAX31723 digital @@ -898,6 +918,16 @@ tristate "MAX31722 temperature sensor" This driver can also be built as a module. If so, the module will be called max31722. +config SENSORS_MAX31730 + tristate "MAX31730 temperature sensor" + depends on I2C + help + Support for the Maxim Integrated MAX31730 3-Channel Remote + Temperature Sensor. + + This driver can also be built as a module. If so, the module + will be called max31730. + config SENSORS_MAX6621 tristate "Maxim MAX6621 sensor chip" depends on I2C @@ -1905,7 +1935,7 @@ config SENSORS_W83627HF will be called w83627hf. config SENSORS_W83627EHF - tristate "Winbond W83627EHF/EHG/DHG/UHG, W83667HG, NCT6775F, NCT6776F" + tristate "Winbond W83627EHF/EHG/DHG/UHG, W83667HG" depends on !PPC select HWMON_VID help @@ -1918,8 +1948,7 @@ config SENSORS_W83627EHF the Core 2 Duo. And also the W83627UHG, which is a stripped down version of the W83627DHG (as far as hardware monitoring goes.) - This driver also supports Nuvoton W83667HG, W83667HG-B, NCT6775F - (also known as W83667HG-I), and NCT6776F. + This driver also supports Nuvoton W83667HG and W83667HG-B. This driver can also be built as a module. If so, the module will be called w83627ehf. diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 6db5db9cdc29..613f50987965 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_SENSORS_ADM1025) += adm1025.o obj-$(CONFIG_SENSORS_ADM1026) += adm1026.o obj-$(CONFIG_SENSORS_ADM1029) += adm1029.o obj-$(CONFIG_SENSORS_ADM1031) += adm1031.o +obj-$(CONFIG_SENSORS_ADM1177) += adm1177.o obj-$(CONFIG_SENSORS_ADM9240) += adm9240.o obj-$(CONFIG_SENSORS_ADS7828) += ads7828.o obj-$(CONFIG_SENSORS_ADS7871) += ads7871.o @@ -56,6 +57,7 @@ obj-$(CONFIG_SENSORS_DA9052_ADC)+= da9052-hwmon.o obj-$(CONFIG_SENSORS_DA9055)+= da9055-hwmon.o obj-$(CONFIG_SENSORS_DELL_SMM) += dell-smm-hwmon.o obj-$(CONFIG_SENSORS_DME1737) += dme1737.o +obj-$(CONFIG_SENSORS_DRIVETEMP) += drivetemp.o obj-$(CONFIG_SENSORS_DS620) += ds620.o obj-$(CONFIG_SENSORS_DS1621) += ds1621.o obj-$(CONFIG_SENSORS_EMC1403) += emc1403.o @@ -123,6 +125,7 @@ obj-$(CONFIG_SENSORS_MAX1619) += max1619.o obj-$(CONFIG_SENSORS_MAX1668) += max1668.o obj-$(CONFIG_SENSORS_MAX197) += max197.o obj-$(CONFIG_SENSORS_MAX31722) += max31722.o +obj-$(CONFIG_SENSORS_MAX31730) += max31730.o obj-$(CONFIG_SENSORS_MAX6621) += max6621.o obj-$(CONFIG_SENSORS_MAX6639) += max6639.o obj-$(CONFIG_SENSORS_MAX6642) += max6642.o diff --git a/drivers/hwmon/adm1177.c b/drivers/hwmon/adm1177.c new file mode 100644 index 000000000000..d314223a404a --- /dev/null +++ b/drivers/hwmon/adm1177.c @@ -0,0 +1,288 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ADM1177 Hot Swap Controller and Digital Power Monitor with Soft Start Pin + * + * Copyright 2015-2019 Analog Devices Inc. + */ + +#include <linux/bits.h> +#include <linux/device.h> +#include <linux/hwmon.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/regulator/consumer.h> + +/* Command Byte Operations */ +#define ADM1177_CMD_V_CONT BIT(0) +#define ADM1177_CMD_I_CONT BIT(2) +#define ADM1177_CMD_VRANGE BIT(4) + +/* Extended Register */ +#define ADM1177_REG_ALERT_TH 2 + +#define ADM1177_BITS 12 + +/** + * struct adm1177_state - driver instance specific data + * @client pointer to i2c client + * @reg regulator info for the the power supply of the device + * @r_sense_uohm current sense resistor value + * @alert_threshold_ua current limit for shutdown + * @vrange_high internal voltage divider + */ +struct adm1177_state { + struct i2c_client *client; + struct regulator *reg; + u32 r_sense_uohm; + u32 alert_threshold_ua; + bool vrange_high; +}; + +static int adm1177_read_raw(struct adm1177_state *st, u8 num, u8 *data) +{ + return i2c_master_recv(st->client, data, num); +} + +static int adm1177_write_cmd(struct adm1177_state *st, u8 cmd) +{ + return i2c_smbus_write_byte(st->client, cmd); +} + +static int adm1177_write_alert_thr(struct adm1177_state *st, + u32 alert_threshold_ua) +{ + u64 val; + int ret; + + val = 0xFFULL * alert_threshold_ua * st->r_sense_uohm; + val = div_u64(val, 105840000U); + val = div_u64(val, 1000U); + if (val > 0xFF) + val = 0xFF; + + ret = i2c_smbus_write_byte_data(st->client, ADM1177_REG_ALERT_TH, + val); + if (ret) + return ret; + + st->alert_threshold_ua = alert_threshold_ua; + return 0; +} + +static int adm1177_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct adm1177_state *st = dev_get_drvdata(dev); + u8 data[3]; + long dummy; + int ret; + + switch (type) { + case hwmon_curr: + switch (attr) { + case hwmon_curr_input: + ret = adm1177_read_raw(st, 3, data); + if (ret < 0) + return ret; + dummy = (data[1] << 4) | (data[2] & 0xF); + /* + * convert to milliamperes + * ((105.84mV / 4096) x raw) / senseResistor(ohm) + */ + *val = div_u64((105840000ull * dummy), + 4096 * st->r_sense_uohm); + return 0; + case hwmon_curr_max_alarm: + *val = st->alert_threshold_ua; + return 0; + default: + return -EOPNOTSUPP; + } + case hwmon_in: + ret = adm1177_read_raw(st, 3, data); + if (ret < 0) + return ret; + dummy = (data[0] << 4) | (data[2] >> 4); + /* + * convert to millivolts based on resistor devision + * (V_fullscale / 4096) * raw + */ + if (st->vrange_high) + dummy *= 26350; + else + dummy *= 6650; + + *val = DIV_ROUND_CLOSEST(dummy, 4096); + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int adm1177_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct adm1177_state *st = dev_get_drvdata(dev); + + switch (type) { + case hwmon_curr: + switch (attr) { + case hwmon_curr_max_alarm: + adm1177_write_alert_thr(st, val); + return 0; + default: + return -EOPNOTSUPP; + } + default: + return -EOPNOTSUPP; + } +} + +static umode_t adm1177_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + const struct adm1177_state *st = data; + + switch (type) { + case hwmon_in: + switch (attr) { + case hwmon_in_input: + return 0444; + } + break; + case hwmon_curr: + switch (attr) { + case hwmon_curr_input: + if (st->r_sense_uohm) + return 0444; + return 0; + case hwmon_curr_max_alarm: + if (st->r_sense_uohm) + return 0644; + return 0; + } + break; + default: + break; + } + return 0; +} + +static const struct hwmon_channel_info *adm1177_info[] = { + HWMON_CHANNEL_INFO(curr, + HWMON_C_INPUT | HWMON_C_MAX_ALARM), + HWMON_CHANNEL_INFO(in, + HWMON_I_INPUT), + NULL +}; + +static const struct hwmon_ops adm1177_hwmon_ops = { + .is_visible = adm1177_is_visible, + .read = adm1177_read, + .write = adm1177_write, +}; + +static const struct hwmon_chip_info adm1177_chip_info = { + .ops = &adm1177_hwmon_ops, + .info = adm1177_info, +}; + +static void adm1177_remove(void *data) +{ + struct adm1177_state *st = data; + + regulator_disable(st->reg); +} + +static int adm1177_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device *hwmon_dev; + struct adm1177_state *st; + u32 alert_threshold_ua; + int ret; + + st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL); + if (!st) + return -ENOMEM; + + st->client = client; + + st->reg = devm_regulator_get_optional(&client->dev, "vref"); + if (IS_ERR(st->reg)) { + if (PTR_ERR(st->reg) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + st->reg = NULL; + } else { + ret = regulator_enable(st->reg); + if (ret) + return ret; + ret = devm_add_action_or_reset(&client->dev, adm1177_remove, + st); + if (ret) + return ret; + } + + if (device_property_read_u32(dev, "shunt-resistor-micro-ohms", + &st->r_sense_uohm)) + st->r_sense_uohm = 0; + if (device_property_read_u32(dev, "adi,shutdown-threshold-microamp", + &alert_threshold_ua)) { + if (st->r_sense_uohm) + /* + * set maximum default value from datasheet based on + * shunt-resistor + */ + alert_threshold_ua = div_u64(105840000000, + st->r_sense_uohm); + else + alert_threshold_ua = 0; + } + st->vrange_high = device_property_read_bool(dev, + "adi,vrange-high-enable"); + if (alert_threshold_ua && st->r_sense_uohm) + adm1177_write_alert_thr(st, alert_threshold_ua); + + ret = adm1177_write_cmd(st, ADM1177_CMD_V_CONT | + ADM1177_CMD_I_CONT | + (st->vrange_high ? 0 : ADM1177_CMD_VRANGE)); + if (ret) + return ret; + + hwmon_dev = + devm_hwmon_device_register_with_info(dev, client->name, st, + &adm1177_chip_info, NULL); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct i2c_device_id adm1177_id[] = { + {"adm1177", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, adm1177_id); + +static const struct of_device_id adm1177_dt_ids[] = { + { .compatible = "adi,adm1177" }, + {}, +}; +MODULE_DEVICE_TABLE(of, adm1177_dt_ids); + +static struct i2c_driver adm1177_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "adm1177", + .of_match_table = adm1177_dt_ids, + }, + .probe = adm1177_probe, + .id_table = adm1177_id, +}; +module_i2c_driver(adm1177_driver); + +MODULE_AUTHOR("Beniamin Bia <beniamin.bia@analog.com>"); +MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); +MODULE_DESCRIPTION("Analog Devices ADM1177 ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hwmon/drivetemp.c b/drivers/hwmon/drivetemp.c new file mode 100644 index 000000000000..370d0c74eb01 --- /dev/null +++ b/drivers/hwmon/drivetemp.c @@ -0,0 +1,574 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Hwmon client for disk and solid state drives with temperature sensors + * Copyright (C) 2019 Zodiac Inflight Innovations + * + * With input from: + * Hwmon client for S.M.A.R.T. hard disk drives with temperature sensors. + * (C) 2018 Linus Walleij + * + * hwmon: Driver for SCSI/ATA temperature sensors + * by Constantin Baranov <const@mimas.ru>, submitted September 2009 + * + * This drive supports reporting the temperatire of SATA drives. It can be + * easily extended to report the temperature of SCSI drives. + * + * The primary means to read drive temperatures and temperature limits + * for ATA drives is the SCT Command Transport feature set as specified in + * ATA8-ACS. + * It can be used to read the current drive temperature, temperature limits, + * and historic minimum and maximum temperatures. The SCT Command Transport + * feature set is documented in "AT Attachment 8 - ATA/ATAPI Command Set + * (ATA8-ACS)". + * + * If the SCT Command Transport feature set is not available, drive temperatures + * may be readable through SMART attributes. Since SMART attributes are not well + * defined, this method is only used as fallback mechanism. + * + * There are three SMART attributes which may report drive temperatures. + * Those are defined as follows (from + * http://www.cropel.com/library/smart-attribute-list.aspx). + * + * 190 Temperature Temperature, monitored by a sensor somewhere inside + * the drive. Raw value typicaly holds the actual + * temperature (hexadecimal) in its rightmost two digits. + * + * 194 Temperature Temperature, monitored by a sensor somewhere inside + * the drive. Raw value typicaly holds the actual + * temperature (hexadecimal) in its rightmost two digits. + * + * 231 Temperature Temperature, monitored by a sensor somewhere inside + * the drive. Raw value typicaly holds the actual + * temperature (hexadecimal) in its rightmost two digits. + * + * Wikipedia defines attributes a bit differently. + * + * 190 Temperature Value is equal to (100-temp. °C), allowing manufacturer + * Difference or to set a minimum threshold which corresponds to a + * Airflow maximum temperature. This also follows the convention of + * Temperature 100 being a best-case value and lower values being + * undesirable. However, some older drives may instead + * report raw Temperature (identical to 0xC2) or + * Temperature minus 50 here. + * 194 Temperature or Indicates the device temperature, if the appropriate + * Temperature sensor is fitted. Lowest byte of the raw value contains + * Celsius the exact temperature value (Celsius degrees). + * 231 Life Left Indicates the approximate SSD life left, in terms of + * (SSDs) or program/erase cycles or available reserved blocks. + * Temperature A normalized value of 100 represents a new drive, with + * a threshold value at 10 indicating a need for + * replacement. A value of 0 may mean that the drive is + * operating in read-only mode to allow data recovery. + * Previously (pre-2010) occasionally used for Drive + * Temperature (more typically reported at 0xC2). + * + * Common denominator is that the first raw byte reports the temperature + * in degrees C on almost all drives. Some drives may report a fractional + * temperature in the second raw byte. + * + * Known exceptions (from libatasmart): + * - SAMSUNG SV0412H and SAMSUNG SV1204H) report the temperature in 10th + * degrees C in the first two raw bytes. + * - A few Maxtor drives report an unknown or bad value in attribute 194. + * - Certain Apple SSD drives report an unknown value in attribute 190. + * Only certain firmware versions are affected. + * + * Those exceptions affect older ATA drives and are currently ignored. + * Also, the second raw byte (possibly reporting the fractional temperature) + * is currently ignored. + * + * Many drives also report temperature limits in additional SMART data raw + * bytes. The format of those is not well defined and varies widely. + * The driver does not currently attempt to report those limits. + * + * According to data in smartmontools, attribute 231 is rarely used to report + * drive temperatures. At the same time, several drives report SSD life left + * in attribute 231, but do not support temperature sensors. For this reason, + * attribute 231 is currently ignored. + * + * Following above definitions, temperatures are reported as follows. + * If SCT Command Transport is supported, it is used to read the + * temperature and, if available, temperature limits. + * - Otherwise, if SMART attribute 194 is supported, it is used to read + * the temperature. + * - Otherwise, if SMART attribute 190 is supported, it is used to read + * the temperature. + */ + +#include <linux/ata.h> +#include <linux/bits.h> +#include <linux/device.h> +#include <linux/hwmon.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <scsi/scsi_cmnd.h> +#include <scsi/scsi_device.h> +#include <scsi/scsi_driver.h> +#include <scsi/scsi_proto.h> + +struct drivetemp_data { + struct list_head list; /* list of instantiated devices */ + struct mutex lock; /* protect data buffer accesses */ + struct scsi_device *sdev; /* SCSI device */ + struct device *dev; /* instantiating device */ + struct device *hwdev; /* hardware monitoring device */ + u8 smartdata[ATA_SECT_SIZE]; /* local buffer */ + int (*get_temp)(struct drivetemp_data *st, u32 attr, long *val); + bool have_temp_lowest; /* lowest temp in SCT status */ + bool have_temp_highest; /* highest temp in SCT status */ + bool have_temp_min; /* have min temp */ + bool have_temp_max; /* have max temp */ + bool have_temp_lcrit; /* have lower critical limit */ + bool have_temp_crit; /* have critical limit */ + int temp_min; /* min temp */ + int temp_max; /* max temp */ + int temp_lcrit; /* lower critical limit */ + int temp_crit; /* critical limit */ +}; + +static LIST_HEAD(drivetemp_devlist); + +#define ATA_MAX_SMART_ATTRS 30 +#define SMART_TEMP_PROP_190 190 +#define SMART_TEMP_PROP_194 194 + +#define SCT_STATUS_REQ_ADDR 0xe0 +#define SCT_STATUS_VERSION_LOW 0 /* log byte offsets */ +#define SCT_STATUS_VERSION_HIGH 1 +#define SCT_STATUS_TEMP 200 +#define SCT_STATUS_TEMP_LOWEST 201 +#define SCT_STATUS_TEMP_HIGHEST 202 +#define SCT_READ_LOG_ADDR 0xe1 +#define SMART_READ_LOG 0xd5 +#define SMART_WRITE_LOG 0xd6 + +#define INVALID_TEMP 0x80 + +#define temp_is_valid(temp) ((temp) != INVALID_TEMP) +#define temp_from_sct(temp) (((s8)(temp)) * 1000) + +static inline bool ata_id_smart_supported(u16 *id) +{ + return id[ATA_ID_COMMAND_SET_1] & BIT(0); +} + +static inline bool ata_id_smart_enabled(u16 *id) +{ + return id[ATA_ID_CFS_ENABLE_1] & BIT(0); +} + +static int drivetemp_scsi_command(struct drivetemp_data *st, + u8 ata_command, u8 feature, + u8 lba_low, u8 lba_mid, u8 lba_high) +{ + u8 scsi_cmd[MAX_COMMAND_SIZE]; + int data_dir; + + memset(scsi_cmd, 0, sizeof(scsi_cmd)); + scsi_cmd[0] = ATA_16; + if (ata_command == ATA_CMD_SMART && feature == SMART_WRITE_LOG) { + scsi_cmd[1] = (5 << 1); /* PIO Data-out */ + /* + * No off.line or cc, write to dev, block count in sector count + * field. + */ + scsi_cmd[2] = 0x06; + data_dir = DMA_TO_DEVICE; + } else { + scsi_cmd[1] = (4 << 1); /* PIO Data-in */ + /* + * No off.line or cc, read from dev, block count in sector count + * field. + */ + scsi_cmd[2] = 0x0e; + data_dir = DMA_FROM_DEVICE; + } + scsi_cmd[4] = feature; + scsi_cmd[6] = 1; /* 1 sector */ + scsi_cmd[8] = lba_low; + scsi_cmd[10] = lba_mid; + scsi_cmd[12] = lba_high; + scsi_cmd[14] = ata_command; + + return scsi_execute_req(st->sdev, scsi_cmd, data_dir, + st->smartdata, ATA_SECT_SIZE, NULL, HZ, 5, + NULL); +} + +static int drivetemp_ata_command(struct drivetemp_data *st, u8 feature, + u8 select) +{ + return drivetemp_scsi_command(st, ATA_CMD_SMART, feature, select, + ATA_SMART_LBAM_PASS, ATA_SMART_LBAH_PASS); +} + +static int drivetemp_get_smarttemp(struct drivetemp_data *st, u32 attr, + long *temp) +{ + u8 *buf = st->smartdata; + bool have_temp = false; + u8 temp_raw; + u8 csum; + int err; + int i; + + err = drivetemp_ata_command(st, ATA_SMART_READ_VALUES, 0); + if (err) + return err; + + /* Checksum the read value table */ + csum = 0; + for (i = 0; i < ATA_SECT_SIZE; i++) + csum += buf[i]; + if (csum) { + dev_dbg(&st->sdev->sdev_gendev, + "checksum error reading SMART values\n"); + return -EIO; + } + + for (i = 0; i < ATA_MAX_SMART_ATTRS; i++) { + u8 *attr = buf + i * 12; + int id = attr[2]; + + if (!id) + continue; + + if (id == SMART_TEMP_PROP_190) { + temp_raw = attr[7]; + have_temp = true; + } + if (id == SMART_TEMP_PROP_194) { + temp_raw = attr[7]; + have_temp = true; + break; + } + } + + if (have_temp) { + *temp = temp_raw * 1000; + return 0; + } + + return -ENXIO; +} + +static int drivetemp_get_scttemp(struct drivetemp_data *st, u32 attr, long *val) +{ + u8 *buf = st->smartdata; + int err; + + err = drivetemp_ata_command(st, SMART_READ_LOG, SCT_STATUS_REQ_ADDR); + if (err) + return err; + switch (attr) { + case hwmon_temp_input: + *val = temp_from_sct(buf[SCT_STATUS_TEMP]); + break; + case hwmon_temp_lowest: + *val = temp_from_sct(buf[SCT_STATUS_TEMP_LOWEST]); + break; + case hwmon_temp_highest: + *val = temp_from_sct(buf[SCT_STATUS_TEMP_HIGHEST]); + break; + default: + err = -EINVAL; + break; + } + return err; +} + +static int drivetemp_identify_sata(struct drivetemp_data *st) +{ + struct scsi_device *sdev = st->sdev; + u8 *buf = st->smartdata; + struct scsi_vpd *vpd; + bool is_ata, is_sata; + bool have_sct_data_table; + bool have_sct_temp; + bool have_smart; + bool have_sct; + u16 *ata_id; + u16 version; + long temp; + int err; + + /* SCSI-ATA Translation present? */ + rcu_read_lock(); + vpd = rcu_dereference(sdev->vpd_pg89); + + /* + * Verify that ATA IDENTIFY DEVICE data is included in ATA Information + * VPD and that the drive implements the SATA protocol. + */ + if (!vpd || vpd->len < 572 || vpd->data[56] != ATA_CMD_ID_ATA || + vpd->data[36] != 0x34) { + rcu_read_unlock(); + return -ENODEV; + } + ata_id = (u16 *)&vpd->data[60]; + is_ata = ata_id_is_ata(ata_id); + is_sata = ata_id_is_sata(ata_id); + have_sct = ata_id_sct_supported(ata_id); + have_sct_data_table = ata_id_sct_data_tables(ata_id); + have_smart = ata_id_smart_supported(ata_id) && + ata_id_smart_enabled(ata_id); + + rcu_read_unlock(); + + /* bail out if this is not a SATA device */ + if (!is_ata || !is_sata) + return -ENODEV; + if (!have_sct) + goto skip_sct; + + err = drivetemp_ata_command(st, SMART_READ_LOG, SCT_STATUS_REQ_ADDR); + if (err) + goto skip_sct; + + version = (buf[SCT_STATUS_VERSION_HIGH] << 8) | + buf[SCT_STATUS_VERSION_LOW]; + if (version != 2 && version != 3) + goto skip_sct; + + have_sct_temp = temp_is_valid(buf[SCT_STATUS_TEMP]); + if (!have_sct_temp) + goto skip_sct; + + st->have_temp_lowest = temp_is_valid(buf[SCT_STATUS_TEMP_LOWEST]); + st->have_temp_highest = temp_is_valid(buf[SCT_STATUS_TEMP_HIGHEST]); + + if (!have_sct_data_table) + goto skip_sct; + + /* Request and read temperature history table */ + memset(buf, '\0', sizeof(st->smartdata)); + buf[0] = 5; /* data table command */ + buf[2] = 1; /* read table */ + buf[4] = 2; /* temperature history table */ + + err = drivetemp_ata_command(st, SMART_WRITE_LOG, SCT_STATUS_REQ_ADDR); + if (err) + goto skip_sct_data; + + err = drivetemp_ata_command(st, SMART_READ_LOG, SCT_READ_LOG_ADDR); + if (err) + goto skip_sct_data; + + /* + * Temperature limits per AT Attachment 8 - + * ATA/ATAPI Command Set (ATA8-ACS) + */ + st->have_temp_max = temp_is_valid(buf[6]); + st->have_temp_crit = temp_is_valid(buf[7]); + st->have_temp_min = temp_is_valid(buf[8]); + st->have_temp_lcrit = temp_is_valid(buf[9]); + + st->temp_max = temp_from_sct(buf[6]); + st->temp_crit = temp_from_sct(buf[7]); + st->temp_min = temp_from_sct(buf[8]); + st->temp_lcrit = temp_from_sct(buf[9]); + +skip_sct_data: + if (have_sct_temp) { + st->get_temp = drivetemp_get_scttemp; + return 0; + } +skip_sct: + if (!have_smart) + return -ENODEV; + st->get_temp = drivetemp_get_smarttemp; + return drivetemp_get_smarttemp(st, hwmon_temp_input, &temp); +} + +static int drivetemp_identify(struct drivetemp_data *st) +{ + struct scsi_device *sdev = st->sdev; + + /* Bail out immediately if there is no inquiry data */ + if (!sdev->inquiry || sdev->inquiry_len < 16) + return -ENODEV; + + /* Disk device? */ + if (sdev->type != TYPE_DISK && sdev->type != TYPE_ZBC) + return -ENODEV; + + return drivetemp_identify_sata(st); +} + +static int drivetemp_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct drivetemp_data *st = dev_get_drvdata(dev); + int err = 0; + + if (type != hwmon_temp) + return -EINVAL; + + switch (attr) { + case hwmon_temp_input: + case hwmon_temp_lowest: + case hwmon_temp_highest: + mutex_lock(&st->lock); + err = st->get_temp(st, attr, val); + mutex_unlock(&st->lock); + break; + case hwmon_temp_lcrit: + *val = st->temp_lcrit; + break; + case hwmon_temp_min: + *val = st->temp_min; + break; + case hwmon_temp_max: + *val = st->temp_max; + break; + case hwmon_temp_crit: + *val = st->temp_crit; + break; + default: + err = -EINVAL; + break; + } + return err; +} + +static umode_t drivetemp_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + const struct drivetemp_data *st = data; + + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + return 0444; + case hwmon_temp_lowest: + if (st->have_temp_lowest) + return 0444; + break; + case hwmon_temp_highest: + if (st->have_temp_highest) + return 0444; + break; + case hwmon_temp_min: + if (st->have_temp_min) + return 0444; + break; + case hwmon_temp_max: + if (st->have_temp_max) + return 0444; + break; + case hwmon_temp_lcrit: + if (st->have_temp_lcrit) + return 0444; + break; + case hwmon_temp_crit: + if (st->have_temp_crit) + return 0444; + break; + default: + break; + } + break; + default: + break; + } + return 0; +} + +static const struct hwmon_channel_info *drivetemp_info[] = { + HWMON_CHANNEL_INFO(chip, + HWMON_C_REGISTER_TZ), + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | + HWMON_T_LOWEST | HWMON_T_HIGHEST | + HWMON_T_MIN | HWMON_T_MAX | + HWMON_T_LCRIT | HWMON_T_CRIT), + NULL +}; + +static const struct hwmon_ops drivetemp_ops = { + .is_visible = drivetemp_is_visible, + .read = drivetemp_read, +}; + +static const struct hwmon_chip_info drivetemp_chip_info = { + .ops = &drivetemp_ops, + .info = drivetemp_info, +}; + +/* + * The device argument points to sdev->sdev_dev. Its parent is + * sdev->sdev_gendev, which we can use to get the scsi_device pointer. + */ +static int drivetemp_add(struct device *dev, struct class_interface *intf) +{ + struct scsi_device *sdev = to_scsi_device(dev->parent); + struct drivetemp_data *st; + int err; + + st = kzalloc(sizeof(*st), GFP_KERNEL); + if (!st) + return -ENOMEM; + + st->sdev = sdev; + st->dev = dev; + mutex_init(&st->lock); + + if (drivetemp_identify(st)) { + err = -ENODEV; + goto abort; + } + + st->hwdev = hwmon_device_register_with_info(dev->parent, "drivetemp", + st, &drivetemp_chip_info, + NULL); + if (IS_ERR(st->hwdev)) { + err = PTR_ERR(st->hwdev); + goto abort; + } + + list_add(&st->list, &drivetemp_devlist); + return 0; + +abort: + kfree(st); + return err; +} + +static void drivetemp_remove(struct device *dev, struct class_interface *intf) +{ + struct drivetemp_data *st, *tmp; + + list_for_each_entry_safe(st, tmp, &drivetemp_devlist, list) { + if (st->dev == dev) { + list_del(&st->list); + hwmon_device_unregister(st->hwdev); + kfree(st); + break; + } + } +} + +static struct class_interface drivetemp_interface = { + .add_dev = drivetemp_add, + .remove_dev = drivetemp_remove, +}; + +static int __init drivetemp_init(void) +{ + return scsi_register_interface(&drivetemp_interface); +} + +static void __exit drivetemp_exit(void) +{ + scsi_unregister_interface(&drivetemp_interface); +} + +module_init(drivetemp_init); +module_exit(drivetemp_exit); + +MODULE_AUTHOR("Guenter Roeck <linus@roeck-us.net>"); +MODULE_DESCRIPTION("Hard drive temperature monitor"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index d018b20089ec..6a30fb453f7a 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -188,7 +188,7 @@ static int hwmon_thermal_add_sensor(struct device *dev, int index) static int hwmon_attr_base(enum hwmon_sensor_types type) { - if (type == hwmon_in) + if (type == hwmon_in || type == hwmon_intrusion) return 0; return 1; } @@ -343,6 +343,7 @@ static const char * const hwmon_chip_attrs[] = { }; static const char * const hwmon_temp_attr_templates[] = { + [hwmon_temp_enable] = "temp%d_enable", [hwmon_temp_input] = "temp%d_input", [hwmon_temp_type] = "temp%d_type", [hwmon_temp_lcrit] = "temp%d_lcrit", @@ -370,6 +371,7 @@ static const char * const hwmon_temp_attr_templates[] = { }; static const char * const hwmon_in_attr_templates[] = { + [hwmon_in_enable] = "in%d_enable", [hwmon_in_input] = "in%d_input", [hwmon_in_min] = "in%d_min", [hwmon_in_max] = "in%d_max", @@ -385,10 +387,10 @@ static const char * const hwmon_in_attr_templates[] = { [hwmon_in_max_alarm] = "in%d_max_alarm", [hwmon_in_lcrit_alarm] = "in%d_lcrit_alarm", [hwmon_in_crit_alarm] = "in%d_crit_alarm", - [hwmon_in_enable] = "in%d_enable", }; static const char * const hwmon_curr_attr_templates[] = { + [hwmon_curr_enable] = "curr%d_enable", [hwmon_curr_input] = "curr%d_input", [hwmon_curr_min] = "curr%d_min", [hwmon_curr_max] = "curr%d_max", @@ -407,6 +409,7 @@ static const char * const hwmon_curr_attr_templates[] = { }; static const char * const hwmon_power_attr_templates[] = { + [hwmon_power_enable] = "power%d_enable", [hwmon_power_average] = "power%d_average", [hwmon_power_average_interval] = "power%d_average_interval", [hwmon_power_average_interval_max] = "power%d_interval_max", @@ -438,11 +441,13 @@ static const char * const hwmon_power_attr_templates[] = { }; static const char * const hwmon_energy_attr_templates[] = { + [hwmon_energy_enable] = "energy%d_enable", [hwmon_energy_input] = "energy%d_input", [hwmon_energy_label] = "energy%d_label", }; static const char * const hwmon_humidity_attr_templates[] = { + [hwmon_humidity_enable] = "humidity%d_enable", [hwmon_humidity_input] = "humidity%d_input", [hwmon_humidity_label] = "humidity%d_label", [hwmon_humidity_min] = "humidity%d_min", @@ -454,6 +459,7 @@ static const char * const hwmon_humidity_attr_templates[] = { }; static const char * const hwmon_fan_attr_templates[] = { + [hwmon_fan_enable] = "fan%d_enable", [hwmon_fan_input] = "fan%d_input", [hwmon_fan_label] = "fan%d_label", [hwmon_fan_min] = "fan%d_min", @@ -474,6 +480,11 @@ static const char * const hwmon_pwm_attr_templates[] = { [hwmon_pwm_freq] = "pwm%d_freq", }; +static const char * const hwmon_intrusion_attr_templates[] = { + [hwmon_intrusion_alarm] = "intrusion%d_alarm", + [hwmon_intrusion_beep] = "intrusion%d_beep", +}; + static const char * const *__templates[] = { [hwmon_chip] = hwmon_chip_attrs, [hwmon_temp] = hwmon_temp_attr_templates, @@ -484,6 +495,7 @@ static const char * const *__templates[] = { [hwmon_humidity] = hwmon_humidity_attr_templates, [hwmon_fan] = hwmon_fan_attr_templates, [hwmon_pwm] = hwmon_pwm_attr_templates, + [hwmon_intrusion] = hwmon_intrusion_attr_templates, }; static const int __templates_size[] = { @@ -496,6 +508,7 @@ static const int __templates_size[] = { [hwmon_humidity] = ARRAY_SIZE(hwmon_humidity_attr_templates), [hwmon_fan] = ARRAY_SIZE(hwmon_fan_attr_templates), [hwmon_pwm] = ARRAY_SIZE(hwmon_pwm_attr_templates), + [hwmon_intrusion] = ARRAY_SIZE(hwmon_intrusion_attr_templates), }; static int hwmon_num_channel_attrs(const struct hwmon_channel_info *info) diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c index 5c1dddde193c..e39354ffe973 100644 --- a/drivers/hwmon/k10temp.c +++ b/drivers/hwmon/k10temp.c @@ -1,13 +1,29 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * k10temp.c - AMD Family 10h/11h/12h/14h/15h/16h processor hardware monitoring + * k10temp.c - AMD Family 10h/11h/12h/14h/15h/16h/17h + * processor hardware monitoring * * Copyright (c) 2009 Clemens Ladisch <clemens@ladisch.de> + * Copyright (c) 2020 Guenter Roeck <linux@roeck-us.net> + * + * Implementation notes: + * - CCD register address information as well as the calculation to + * convert raw register values is from https://github.com/ocerman/zenpower. + * The information is not confirmed from chip datasheets, but experiments + * suggest that it provides reasonable temperature values. + * - Register addresses to read chip voltage and current are also from + * https://github.com/ocerman/zenpower, and not confirmed from chip + * datasheets. Current calibration is board specific and not typically + * shared by board vendors. For this reason, current values are + * normalized to report 1A/LSB for core current and and 0.25A/LSB for SoC + * current. Reported values can be adjusted using the sensors configuration + * file. */ +#include <linux/bitops.h> +#include <linux/debugfs.h> #include <linux/err.h> #include <linux/hwmon.h> -#include <linux/hwmon-sysfs.h> #include <linux/init.h> #include <linux/module.h> #include <linux/pci.h> @@ -31,22 +47,22 @@ static DEFINE_MUTEX(nb_smu_ind_mutex); #endif /* CPUID function 0x80000001, ebx */ -#define CPUID_PKGTYPE_MASK 0xf0000000 +#define CPUID_PKGTYPE_MASK GENMASK(31, 28) #define CPUID_PKGTYPE_F 0x00000000 #define CPUID_PKGTYPE_AM2R2_AM3 0x10000000 /* DRAM controller (PCI function 2) */ #define REG_DCT0_CONFIG_HIGH 0x094 -#define DDR3_MODE 0x00000100 +#define DDR3_MODE BIT(8) /* miscellaneous (PCI function 3) */ #define REG_HARDWARE_THERMAL_CONTROL 0x64 -#define HTC_ENABLE 0x00000001 +#define HTC_ENABLE BIT(0) #define REG_REPORTED_TEMPERATURE 0xa4 #define REG_NORTHBRIDGE_CAPABILITIES 0xe8 -#define NB_CAP_HTC 0x00000400 +#define NB_CAP_HTC BIT(10) /* * For F15h M60h and M70h, REG_HARDWARE_THERMAL_CONTROL @@ -60,6 +76,20 @@ static DEFINE_MUTEX(nb_smu_ind_mutex); /* F17h M01h Access througn SMN */ #define F17H_M01H_REPORTED_TEMP_CTRL_OFFSET 0x00059800 +#define F17H_M70H_CCD_TEMP(x) (0x00059954 + ((x) * 4)) +#define F17H_M70H_CCD_TEMP_VALID BIT(11) +#define F17H_M70H_CCD_TEMP_MASK GENMASK(10, 0) + +#define F17H_M01H_SVI 0x0005A000 +#define F17H_M01H_SVI_TEL_PLANE0 (F17H_M01H_SVI + 0xc) +#define F17H_M01H_SVI_TEL_PLANE1 (F17H_M01H_SVI + 0x10) + +#define CUR_TEMP_SHIFT 21 +#define CUR_TEMP_RANGE_SEL_MASK BIT(19) + +#define CFACTOR_ICORE 1000000 /* 1A / LSB */ +#define CFACTOR_ISOC 250000 /* 0.25A / LSB */ + struct k10temp_data { struct pci_dev *pdev; void (*read_htcreg)(struct pci_dev *pdev, u32 *regval); @@ -67,6 +97,10 @@ struct k10temp_data { int temp_offset; u32 temp_adjust_mask; bool show_tdie; + u32 show_tccd; + u32 svi_addr[2]; + bool show_current; + int cfactor[2]; }; struct tctl_offset { @@ -84,6 +118,16 @@ static const struct tctl_offset tctl_offset_table[] = { { 0x17, "AMD Ryzen Threadripper 29", 27000 }, /* 29{20,50,70,90}[W]X */ }; +static bool is_threadripper(void) +{ + return strstr(boot_cpu_data.x86_model_id, "Threadripper"); +} + +static bool is_epyc(void) +{ + return strstr(boot_cpu_data.x86_model_id, "EPYC"); +} + static void read_htcreg_pci(struct pci_dev *pdev, u32 *regval) { pci_read_config_dword(pdev, REG_HARDWARE_THERMAL_CONTROL, regval); @@ -123,130 +167,237 @@ static void read_tempreg_nb_f17(struct pci_dev *pdev, u32 *regval) F17H_M01H_REPORTED_TEMP_CTRL_OFFSET, regval); } -static unsigned int get_raw_temp(struct k10temp_data *data) +static long get_raw_temp(struct k10temp_data *data) { - unsigned int temp; u32 regval; + long temp; data->read_tempreg(data->pdev, ®val); - temp = (regval >> 21) * 125; + temp = (regval >> CUR_TEMP_SHIFT) * 125; if (regval & data->temp_adjust_mask) temp -= 49000; return temp; } -static ssize_t temp1_input_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct k10temp_data *data = dev_get_drvdata(dev); - unsigned int temp = get_raw_temp(data); +const char *k10temp_temp_label[] = { + "Tdie", + "Tctl", + "Tccd1", + "Tccd2", + "Tccd3", + "Tccd4", + "Tccd5", + "Tccd6", + "Tccd7", + "Tccd8", +}; - if (temp > data->temp_offset) - temp -= data->temp_offset; - else - temp = 0; +const char *k10temp_in_label[] = { + "Vcore", + "Vsoc", +}; - return sprintf(buf, "%u\n", temp); -} +const char *k10temp_curr_label[] = { + "Icore", + "Isoc", +}; -static ssize_t temp2_input_show(struct device *dev, - struct device_attribute *devattr, char *buf) +static int k10temp_read_labels(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) { - struct k10temp_data *data = dev_get_drvdata(dev); - unsigned int temp = get_raw_temp(data); - - return sprintf(buf, "%u\n", temp); + switch (type) { + case hwmon_temp: + *str = k10temp_temp_label[channel]; + break; + case hwmon_in: + *str = k10temp_in_label[channel]; + break; + case hwmon_curr: + *str = k10temp_curr_label[channel]; + break; + default: + return -EOPNOTSUPP; + } + return 0; } -static ssize_t temp_label_show(struct device *dev, - struct device_attribute *devattr, char *buf) +static int k10temp_read_curr(struct device *dev, u32 attr, int channel, + long *val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct k10temp_data *data = dev_get_drvdata(dev); + u32 regval; - return sprintf(buf, "%s\n", attr->index ? "Tctl" : "Tdie"); + switch (attr) { + case hwmon_curr_input: + amd_smn_read(amd_pci_dev_to_node_id(data->pdev), + data->svi_addr[channel], ®val); + *val = DIV_ROUND_CLOSEST(data->cfactor[channel] * + (regval & 0xff), + 1000); + break; + default: + return -EOPNOTSUPP; + } + return 0; } -static ssize_t temp1_max_show(struct device *dev, - struct device_attribute *attr, char *buf) +static int k10temp_read_in(struct device *dev, u32 attr, int channel, long *val) { - return sprintf(buf, "%d\n", 70 * 1000); + struct k10temp_data *data = dev_get_drvdata(dev); + u32 regval; + + switch (attr) { + case hwmon_in_input: + amd_smn_read(amd_pci_dev_to_node_id(data->pdev), + data->svi_addr[channel], ®val); + regval = (regval >> 16) & 0xff; + *val = DIV_ROUND_CLOSEST(155000 - regval * 625, 100); + break; + default: + return -EOPNOTSUPP; + } + return 0; } -static ssize_t temp_crit_show(struct device *dev, - struct device_attribute *devattr, char *buf) +static int k10temp_read_temp(struct device *dev, u32 attr, int channel, + long *val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct k10temp_data *data = dev_get_drvdata(dev); - int show_hyst = attr->index; u32 regval; - int value; - data->read_htcreg(data->pdev, ®val); - value = ((regval >> 16) & 0x7f) * 500 + 52000; - if (show_hyst) - value -= ((regval >> 24) & 0xf) * 500; - return sprintf(buf, "%d\n", value); + switch (attr) { + case hwmon_temp_input: + switch (channel) { + case 0: /* Tdie */ + *val = get_raw_temp(data) - data->temp_offset; + if (*val < 0) + *val = 0; + break; + case 1: /* Tctl */ + *val = get_raw_temp(data); + if (*val < 0) + *val = 0; + break; + case 2 ... 9: /* Tccd{1-8} */ + amd_smn_read(amd_pci_dev_to_node_id(data->pdev), + F17H_M70H_CCD_TEMP(channel - 2), ®val); + *val = (regval & F17H_M70H_CCD_TEMP_MASK) * 125 - 49000; + break; + default: + return -EOPNOTSUPP; + } + break; + case hwmon_temp_max: + *val = 70 * 1000; + break; + case hwmon_temp_crit: + data->read_htcreg(data->pdev, ®val); + *val = ((regval >> 16) & 0x7f) * 500 + 52000; + break; + case hwmon_temp_crit_hyst: + data->read_htcreg(data->pdev, ®val); + *val = (((regval >> 16) & 0x7f) + - ((regval >> 24) & 0xf)) * 500 + 52000; + break; + default: + return -EOPNOTSUPP; + } + return 0; } -static DEVICE_ATTR_RO(temp1_input); -static DEVICE_ATTR_RO(temp1_max); -static SENSOR_DEVICE_ATTR_RO(temp1_crit, temp_crit, 0); -static SENSOR_DEVICE_ATTR_RO(temp1_crit_hyst, temp_crit, 1); - -static SENSOR_DEVICE_ATTR_RO(temp1_label, temp_label, 0); -static DEVICE_ATTR_RO(temp2_input); -static SENSOR_DEVICE_ATTR_RO(temp2_label, temp_label, 1); +static int k10temp_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + switch (type) { + case hwmon_temp: + return k10temp_read_temp(dev, attr, channel, val); + case hwmon_in: + return k10temp_read_in(dev, attr, channel, val); + case hwmon_curr: + return k10temp_read_curr(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} -static umode_t k10temp_is_visible(struct kobject *kobj, - struct attribute *attr, int index) +static umode_t k10temp_is_visible(const void *_data, + enum hwmon_sensor_types type, + u32 attr, int channel) { - struct device *dev = container_of(kobj, struct device, kobj); - struct k10temp_data *data = dev_get_drvdata(dev); + const struct k10temp_data *data = _data; struct pci_dev *pdev = data->pdev; u32 reg; - switch (index) { - case 0 ... 1: /* temp1_input, temp1_max */ - default: - break; - case 2 ... 3: /* temp1_crit, temp1_crit_hyst */ - if (!data->read_htcreg) - return 0; - - pci_read_config_dword(pdev, REG_NORTHBRIDGE_CAPABILITIES, - ®); - if (!(reg & NB_CAP_HTC)) - return 0; - - data->read_htcreg(data->pdev, ®); - if (!(reg & HTC_ENABLE)) + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + switch (channel) { + case 0: /* Tdie, or Tctl if we don't show it */ + break; + case 1: /* Tctl */ + if (!data->show_tdie) + return 0; + break; + case 2 ... 9: /* Tccd{1-8} */ + if (!(data->show_tccd & BIT(channel - 2))) + return 0; + break; + default: + return 0; + } + break; + case hwmon_temp_max: + if (channel || data->show_tdie) + return 0; + break; + case hwmon_temp_crit: + case hwmon_temp_crit_hyst: + if (channel || !data->read_htcreg) + return 0; + + pci_read_config_dword(pdev, + REG_NORTHBRIDGE_CAPABILITIES, + ®); + if (!(reg & NB_CAP_HTC)) + return 0; + + data->read_htcreg(data->pdev, ®); + if (!(reg & HTC_ENABLE)) + return 0; + break; + case hwmon_temp_label: + /* No labels if we don't show the die temperature */ + if (!data->show_tdie) + return 0; + switch (channel) { + case 0: /* Tdie */ + case 1: /* Tctl */ + break; + case 2 ... 9: /* Tccd{1-8} */ + if (!(data->show_tccd & BIT(channel - 2))) + return 0; + break; + default: + return 0; + } + break; + default: return 0; + } break; - case 4 ... 6: /* temp1_label, temp2_input, temp2_label */ - if (!data->show_tdie) + case hwmon_in: + case hwmon_curr: + if (!data->show_current) return 0; break; + default: + return 0; } - return attr->mode; + return 0444; } -static struct attribute *k10temp_attrs[] = { - &dev_attr_temp1_input.attr, - &dev_attr_temp1_max.attr, - &sensor_dev_attr_temp1_crit.dev_attr.attr, - &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, - &sensor_dev_attr_temp1_label.dev_attr.attr, - &dev_attr_temp2_input.attr, - &sensor_dev_attr_temp2_label.dev_attr.attr, - NULL -}; - -static const struct attribute_group k10temp_group = { - .attrs = k10temp_attrs, - .is_visible = k10temp_is_visible, -}; -__ATTRIBUTE_GROUPS(k10temp); - static bool has_erratum_319(struct pci_dev *pdev) { u32 pkg_type, reg_dram_cfg; @@ -281,8 +432,125 @@ static bool has_erratum_319(struct pci_dev *pdev) (boot_cpu_data.x86_model == 4 && boot_cpu_data.x86_stepping <= 2); } -static int k10temp_probe(struct pci_dev *pdev, - const struct pci_device_id *id) +#ifdef CONFIG_DEBUG_FS + +static void k10temp_smn_regs_show(struct seq_file *s, struct pci_dev *pdev, + u32 addr, int count) +{ + u32 reg; + int i; + + for (i = 0; i < count; i++) { + if (!(i & 3)) + seq_printf(s, "0x%06x: ", addr + i * 4); + amd_smn_read(amd_pci_dev_to_node_id(pdev), addr + i * 4, ®); + seq_printf(s, "%08x ", reg); + if ((i & 3) == 3) + seq_puts(s, "\n"); + } +} + +static int svi_show(struct seq_file *s, void *unused) +{ + struct k10temp_data *data = s->private; + + k10temp_smn_regs_show(s, data->pdev, F17H_M01H_SVI, 32); + return 0; +} +DEFINE_SHOW_ATTRIBUTE(svi); + +static int thm_show(struct seq_file *s, void *unused) +{ + struct k10temp_data *data = s->private; + + k10temp_smn_regs_show(s, data->pdev, + F17H_M01H_REPORTED_TEMP_CTRL_OFFSET, 256); + return 0; +} +DEFINE_SHOW_ATTRIBUTE(thm); + +static void k10temp_debugfs_cleanup(void *ddir) +{ + debugfs_remove_recursive(ddir); +} + +static void k10temp_init_debugfs(struct k10temp_data *data) +{ + struct dentry *debugfs; + char name[32]; + + /* Only show debugfs data for Family 17h/18h CPUs */ + if (!data->show_tdie) + return; + + scnprintf(name, sizeof(name), "k10temp-%s", pci_name(data->pdev)); + + debugfs = debugfs_create_dir(name, NULL); + if (debugfs) { + debugfs_create_file("svi", 0444, debugfs, data, &svi_fops); + debugfs_create_file("thm", 0444, debugfs, data, &thm_fops); + devm_add_action_or_reset(&data->pdev->dev, + k10temp_debugfs_cleanup, debugfs); + } +} + +#else + +static void k10temp_init_debugfs(struct k10temp_data *data) +{ +} + +#endif + +static const struct hwmon_channel_info *k10temp_info[] = { + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_MAX | + HWMON_T_CRIT | HWMON_T_CRIT_HYST | + HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL), + HWMON_CHANNEL_INFO(in, + HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL), + HWMON_CHANNEL_INFO(curr, + HWMON_C_INPUT | HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_LABEL), + NULL +}; + +static const struct hwmon_ops k10temp_hwmon_ops = { + .is_visible = k10temp_is_visible, + .read = k10temp_read, + .read_string = k10temp_read_labels, +}; + +static const struct hwmon_chip_info k10temp_chip_info = { + .ops = &k10temp_hwmon_ops, + .info = k10temp_info, +}; + +static void k10temp_get_ccd_support(struct pci_dev *pdev, + struct k10temp_data *data, int limit) +{ + u32 regval; + int i; + + for (i = 0; i < limit; i++) { + amd_smn_read(amd_pci_dev_to_node_id(pdev), + F17H_M70H_CCD_TEMP(i), ®val); + if (regval & F17H_M70H_CCD_TEMP_VALID) + data->show_tccd |= BIT(i); + } +} + +static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id) { int unreliable = has_erratum_319(pdev); struct device *dev = &pdev->dev; @@ -312,9 +580,32 @@ static int k10temp_probe(struct pci_dev *pdev, data->read_htcreg = read_htcreg_nb_f15; data->read_tempreg = read_tempreg_nb_f15; } else if (boot_cpu_data.x86 == 0x17 || boot_cpu_data.x86 == 0x18) { - data->temp_adjust_mask = 0x80000; + data->temp_adjust_mask = CUR_TEMP_RANGE_SEL_MASK; data->read_tempreg = read_tempreg_nb_f17; data->show_tdie = true; + + switch (boot_cpu_data.x86_model) { + case 0x1: /* Zen */ + case 0x8: /* Zen+ */ + case 0x11: /* Zen APU */ + case 0x18: /* Zen+ APU */ + data->show_current = !is_threadripper() && !is_epyc(); + data->svi_addr[0] = F17H_M01H_SVI_TEL_PLANE0; + data->svi_addr[1] = F17H_M01H_SVI_TEL_PLANE1; + data->cfactor[0] = CFACTOR_ICORE; + data->cfactor[1] = CFACTOR_ISOC; + k10temp_get_ccd_support(pdev, data, 4); + break; + case 0x31: /* Zen2 Threadripper */ + case 0x71: /* Zen2 */ + data->show_current = !is_threadripper() && !is_epyc(); + data->cfactor[0] = CFACTOR_ICORE; + data->cfactor[1] = CFACTOR_ISOC; + data->svi_addr[0] = F17H_M01H_SVI_TEL_PLANE1; + data->svi_addr[1] = F17H_M01H_SVI_TEL_PLANE0; + k10temp_get_ccd_support(pdev, data, 8); + break; + } } else { data->read_htcreg = read_htcreg_pci; data->read_tempreg = read_tempreg_pci; @@ -330,9 +621,15 @@ static int k10temp_probe(struct pci_dev *pdev, } } - hwmon_dev = devm_hwmon_device_register_with_groups(dev, "k10temp", data, - k10temp_groups); - return PTR_ERR_OR_ZERO(hwmon_dev); + hwmon_dev = devm_hwmon_device_register_with_info(dev, "k10temp", data, + &k10temp_chip_info, + NULL); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); + + k10temp_init_debugfs(data); + + return 0; } static const struct pci_device_id k10temp_id_table[] = { diff --git a/drivers/hwmon/max31730.c b/drivers/hwmon/max31730.c new file mode 100644 index 000000000000..eb22a34dc36b --- /dev/null +++ b/drivers/hwmon/max31730.c @@ -0,0 +1,440 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for MAX31730 3-Channel Remote Temperature Sensor + * + * Copyright (c) 2019 Guenter Roeck <linux@roeck-us.net> + */ + +#include <linux/bits.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/hwmon.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/of.h> +#include <linux/slab.h> + +/* Addresses scanned */ +static const unsigned short normal_i2c[] = { 0x1c, 0x1d, 0x1e, 0x1f, 0x4c, + 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; + +/* The MAX31730 registers */ +#define MAX31730_REG_TEMP 0x00 +#define MAX31730_REG_CONF 0x13 +#define MAX31730_STOP BIT(7) +#define MAX31730_EXTRANGE BIT(1) +#define MAX31730_REG_TEMP_OFFSET 0x16 +#define MAX31730_TEMP_OFFSET_BASELINE 0x77 +#define MAX31730_REG_OFFSET_ENABLE 0x17 +#define MAX31730_REG_TEMP_MAX 0x20 +#define MAX31730_REG_TEMP_MIN 0x30 +#define MAX31730_REG_STATUS_HIGH 0x32 +#define MAX31730_REG_STATUS_LOW 0x33 +#define MAX31730_REG_CHANNEL_ENABLE 0x35 +#define MAX31730_REG_TEMP_FAULT 0x36 + +#define MAX31730_REG_MFG_ID 0x50 +#define MAX31730_MFG_ID 0x4d +#define MAX31730_REG_MFG_REV 0x51 +#define MAX31730_MFG_REV 0x01 + +#define MAX31730_TEMP_MIN (-128000) +#define MAX31730_TEMP_MAX 127937 + +/* Each client has this additional data */ +struct max31730_data { + struct i2c_client *client; + u8 orig_conf; + u8 current_conf; + u8 offset_enable; + u8 channel_enable; +}; + +/*-----------------------------------------------------------------------*/ + +static inline long max31730_reg_to_mc(s16 temp) +{ + return DIV_ROUND_CLOSEST((temp >> 4) * 1000, 16); +} + +static int max31730_write_config(struct max31730_data *data, u8 set_mask, + u8 clr_mask) +{ + u8 value; + + clr_mask |= MAX31730_EXTRANGE; + value = data->current_conf & ~clr_mask; + value |= set_mask; + + if (data->current_conf != value) { + s32 err; + + err = i2c_smbus_write_byte_data(data->client, MAX31730_REG_CONF, + value); + if (err) + return err; + data->current_conf = value; + } + return 0; +} + +static int max31730_set_enable(struct i2c_client *client, int reg, + u8 *confdata, int channel, bool enable) +{ + u8 regval = *confdata; + int err; + + if (enable) + regval |= BIT(channel); + else + regval &= ~BIT(channel); + + if (regval != *confdata) { + err = i2c_smbus_write_byte_data(client, reg, regval); + if (err) + return err; + *confdata = regval; + } + return 0; +} + +static int max31730_set_offset_enable(struct max31730_data *data, int channel, + bool enable) +{ + return max31730_set_enable(data->client, MAX31730_REG_OFFSET_ENABLE, + &data->offset_enable, channel, enable); +} + +static int max31730_set_channel_enable(struct max31730_data *data, int channel, + bool enable) +{ + return max31730_set_enable(data->client, MAX31730_REG_CHANNEL_ENABLE, + &data->channel_enable, channel, enable); +} + +static int max31730_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct max31730_data *data = dev_get_drvdata(dev); + int regval, reg, offset; + + if (type != hwmon_temp) + return -EINVAL; + + switch (attr) { + case hwmon_temp_input: + if (!(data->channel_enable & BIT(channel))) + return -ENODATA; + reg = MAX31730_REG_TEMP + (channel * 2); + break; + case hwmon_temp_max: + reg = MAX31730_REG_TEMP_MAX + (channel * 2); + break; + case hwmon_temp_min: + reg = MAX31730_REG_TEMP_MIN; + break; + case hwmon_temp_enable: + *val = !!(data->channel_enable & BIT(channel)); + return 0; + case hwmon_temp_offset: + if (!channel) + return -EINVAL; + if (!(data->offset_enable & BIT(channel))) { + *val = 0; + return 0; + } + offset = i2c_smbus_read_byte_data(data->client, + MAX31730_REG_TEMP_OFFSET); + if (offset < 0) + return offset; + *val = (offset - MAX31730_TEMP_OFFSET_BASELINE) * 125; + return 0; + case hwmon_temp_fault: + regval = i2c_smbus_read_byte_data(data->client, + MAX31730_REG_TEMP_FAULT); + if (regval < 0) + return regval; + *val = !!(regval & BIT(channel)); + return 0; + case hwmon_temp_min_alarm: + regval = i2c_smbus_read_byte_data(data->client, + MAX31730_REG_STATUS_LOW); + if (regval < 0) + return regval; + *val = !!(regval & BIT(channel)); + return 0; + case hwmon_temp_max_alarm: + regval = i2c_smbus_read_byte_data(data->client, + MAX31730_REG_STATUS_HIGH); + if (regval < 0) + return regval; + *val = !!(regval & BIT(channel)); + return 0; + default: + return -EINVAL; + } + regval = i2c_smbus_read_word_swapped(data->client, reg); + if (regval < 0) + return regval; + + *val = max31730_reg_to_mc(regval); + + return 0; +} + +static int max31730_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct max31730_data *data = dev_get_drvdata(dev); + int reg, err; + + if (type != hwmon_temp) + return -EINVAL; + + switch (attr) { + case hwmon_temp_max: + reg = MAX31730_REG_TEMP_MAX + channel * 2; + break; + case hwmon_temp_min: + reg = MAX31730_REG_TEMP_MIN; + break; + case hwmon_temp_enable: + if (val != 0 && val != 1) + return -EINVAL; + return max31730_set_channel_enable(data, channel, val); + case hwmon_temp_offset: + val = clamp_val(val, -14875, 17000) + 14875; + val = DIV_ROUND_CLOSEST(val, 125); + err = max31730_set_offset_enable(data, channel, + val != MAX31730_TEMP_OFFSET_BASELINE); + if (err) + return err; + return i2c_smbus_write_byte_data(data->client, + MAX31730_REG_TEMP_OFFSET, val); + default: + return -EINVAL; + } + + val = clamp_val(val, MAX31730_TEMP_MIN, MAX31730_TEMP_MAX); + val = DIV_ROUND_CLOSEST(val << 4, 1000) << 4; + + return i2c_smbus_write_word_swapped(data->client, reg, (u16)val); +} + +static umode_t max31730_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + case hwmon_temp_min_alarm: + case hwmon_temp_max_alarm: + case hwmon_temp_fault: + return 0444; + case hwmon_temp_min: + return channel ? 0444 : 0644; + case hwmon_temp_offset: + case hwmon_temp_enable: + case hwmon_temp_max: + return 0644; + } + break; + default: + break; + } + return 0; +} + +static const struct hwmon_channel_info *max31730_info[] = { + HWMON_CHANNEL_INFO(chip, + HWMON_C_REGISTER_TZ), + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | + HWMON_T_ENABLE | + HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM, + HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | + HWMON_T_OFFSET | HWMON_T_ENABLE | + HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM | + HWMON_T_FAULT, + HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | + HWMON_T_OFFSET | HWMON_T_ENABLE | + HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM | + HWMON_T_FAULT, + HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | + HWMON_T_OFFSET | HWMON_T_ENABLE | + HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM | + HWMON_T_FAULT + ), + NULL +}; + +static const struct hwmon_ops max31730_hwmon_ops = { + .is_visible = max31730_is_visible, + .read = max31730_read, + .write = max31730_write, +}; + +static const struct hwmon_chip_info max31730_chip_info = { + .ops = &max31730_hwmon_ops, + .info = max31730_info, +}; + +static void max31730_remove(void *data) +{ + struct max31730_data *max31730 = data; + struct i2c_client *client = max31730->client; + + i2c_smbus_write_byte_data(client, MAX31730_REG_CONF, + max31730->orig_conf); +} + +static int +max31730_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct device *hwmon_dev; + struct max31730_data *data; + int status, err; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) + return -EIO; + + data = devm_kzalloc(dev, sizeof(struct max31730_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->client = client; + + /* Cache original configuration and enable status */ + status = i2c_smbus_read_byte_data(client, MAX31730_REG_CHANNEL_ENABLE); + if (status < 0) + return status; + data->channel_enable = status; + + status = i2c_smbus_read_byte_data(client, MAX31730_REG_OFFSET_ENABLE); + if (status < 0) + return status; + data->offset_enable = status; + + status = i2c_smbus_read_byte_data(client, MAX31730_REG_CONF); + if (status < 0) + return status; + data->orig_conf = status; + data->current_conf = status; + + err = max31730_write_config(data, + data->channel_enable ? 0 : MAX31730_STOP, + data->channel_enable ? MAX31730_STOP : 0); + if (err) + return err; + + dev_set_drvdata(dev, data); + + err = devm_add_action_or_reset(dev, max31730_remove, data); + if (err) + return err; + + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, + data, + &max31730_chip_info, + NULL); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct i2c_device_id max31730_ids[] = { + { "max31730", 0, }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max31730_ids); + +static const struct of_device_id __maybe_unused max31730_of_match[] = { + { + .compatible = "maxim,max31730", + }, + { }, +}; +MODULE_DEVICE_TABLE(of, max31730_of_match); + +static bool max31730_check_reg_temp(struct i2c_client *client, + int reg) +{ + int regval; + + regval = i2c_smbus_read_byte_data(client, reg + 1); + return regval < 0 || (regval & 0x0f); +} + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int max31730_detect(struct i2c_client *client, + struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + int regval; + int i; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA)) + return -ENODEV; + + regval = i2c_smbus_read_byte_data(client, MAX31730_REG_MFG_ID); + if (regval != MAX31730_MFG_ID) + return -ENODEV; + regval = i2c_smbus_read_byte_data(client, MAX31730_REG_MFG_REV); + if (regval != MAX31730_MFG_REV) + return -ENODEV; + + /* lower 4 bit of temperature and limit registers must be 0 */ + if (max31730_check_reg_temp(client, MAX31730_REG_TEMP_MIN)) + return -ENODEV; + + for (i = 0; i < 4; i++) { + if (max31730_check_reg_temp(client, MAX31730_REG_TEMP + i * 2)) + return -ENODEV; + if (max31730_check_reg_temp(client, + MAX31730_REG_TEMP_MAX + i * 2)) + return -ENODEV; + } + + strlcpy(info->type, "max31730", I2C_NAME_SIZE); + + return 0; +} + +static int __maybe_unused max31730_suspend(struct device *dev) +{ + struct max31730_data *data = dev_get_drvdata(dev); + + return max31730_write_config(data, MAX31730_STOP, 0); +} + +static int __maybe_unused max31730_resume(struct device *dev) +{ + struct max31730_data *data = dev_get_drvdata(dev); + + return max31730_write_config(data, 0, MAX31730_STOP); +} + +static SIMPLE_DEV_PM_OPS(max31730_pm_ops, max31730_suspend, max31730_resume); + +static struct i2c_driver max31730_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "max31730", + .of_match_table = of_match_ptr(max31730_of_match), + .pm = &max31730_pm_ops, + }, + .probe = max31730_probe, + .id_table = max31730_ids, + .detect = max31730_detect, + .address_list = normal_i2c, +}; + +module_i2c_driver(max31730_driver); + +MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); +MODULE_DESCRIPTION("MAX31730 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 59859979571d..a9ea06204767 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -20,8 +20,8 @@ config SENSORS_PMBUS help If you say yes here you get hardware monitoring support for generic PMBus devices, including but not limited to ADP4000, BMR453, BMR454, - MDT040, NCP4200, NCP4208, PDT003, PDT006, PDT012, TPS40400, TPS544B20, - TPS544B25, TPS544C20, TPS544C25, and UDT020. + MAX20796, MDT040, NCP4200, NCP4208, PDT003, PDT006, PDT012, TPS40400, + TPS544B20, TPS544B25, TPS544C20, TPS544C25, and UDT020. This driver can also be built as a module. If so, the module will be called pmbus. @@ -145,6 +145,15 @@ config SENSORS_MAX16064 This driver can also be built as a module. If so, the module will be called max16064. +config SENSORS_MAX20730 + tristate "Maxim MAX20730, MAX20734, MAX20743" + help + If you say yes here you get hardware monitoring support for Maxim + MAX20730, MAX20734, and MAX20743. + + This driver can also be built as a module. If so, the module will + be called max20730. + config SENSORS_MAX20751 tristate "Maxim MAX20751" help @@ -200,20 +209,20 @@ config SENSORS_TPS40422 be called tps40422. config SENSORS_TPS53679 - tristate "TI TPS53679" + tristate "TI TPS53679, TPS53688" help If you say yes here you get hardware monitoring support for TI - TPS53679. + TPS53679, TPS53688 This driver can also be built as a module. If so, the module will be called tps53679. config SENSORS_UCD9000 - tristate "TI UCD90120, UCD90124, UCD90160, UCD9090, UCD90910" + tristate "TI UCD90120, UCD90124, UCD90160, UCD90320, UCD9090, UCD90910" help If you say yes here you get hardware monitoring support for TI - UCD90120, UCD90124, UCD90160, UCD9090, UCD90910, Sequencer and System - Health Controllers. + UCD90120, UCD90124, UCD90160, UCD90320, UCD9090, UCD90910, Sequencer + and System Health Controllers. This driver can also be built as a module. If so, the module will be called ucd9000. @@ -228,6 +237,15 @@ config SENSORS_UCD9200 This driver can also be built as a module. If so, the module will be called ucd9200. +config SENSORS_XDPE122 + tristate "Infineon XDPE122 family" + help + If you say yes here you get hardware monitoring support for Infineon + XDPE12254, XDPE12284, device. + + This driver can also be built as a module. If so, the module will + be called xdpe12284. + config SENSORS_ZL6100 tristate "Intersil ZL6100 and compatibles" help diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile index 3f8c1014938b..5feb45806123 100644 --- a/drivers/hwmon/pmbus/Makefile +++ b/drivers/hwmon/pmbus/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_SENSORS_LM25066) += lm25066.o obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o obj-$(CONFIG_SENSORS_LTC3815) += ltc3815.o obj-$(CONFIG_SENSORS_MAX16064) += max16064.o +obj-$(CONFIG_SENSORS_MAX20730) += max20730.o obj-$(CONFIG_SENSORS_MAX20751) += max20751.o obj-$(CONFIG_SENSORS_MAX31785) += max31785.o obj-$(CONFIG_SENSORS_MAX34440) += max34440.o @@ -26,4 +27,5 @@ obj-$(CONFIG_SENSORS_TPS40422) += tps40422.o obj-$(CONFIG_SENSORS_TPS53679) += tps53679.o obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o +obj-$(CONFIG_SENSORS_XDPE122) += xdpe12284.o obj-$(CONFIG_SENSORS_ZL6100) += zl6100.o diff --git a/drivers/hwmon/pmbus/ibm-cffps.c b/drivers/hwmon/pmbus/ibm-cffps.c index d359b76bcb36..3795fe55b84f 100644 --- a/drivers/hwmon/pmbus/ibm-cffps.c +++ b/drivers/hwmon/pmbus/ibm-cffps.c @@ -20,12 +20,15 @@ #define CFFPS_FRU_CMD 0x9A #define CFFPS_PN_CMD 0x9B +#define CFFPS_HEADER_CMD 0x9C #define CFFPS_SN_CMD 0x9E +#define CFFPS_MAX_POWER_OUT_CMD 0xA7 #define CFFPS_CCIN_CMD 0xBD #define CFFPS_FW_CMD 0xFA #define CFFPS1_FW_NUM_BYTES 4 #define CFFPS2_FW_NUM_WORDS 3 #define CFFPS_SYS_CONFIG_CMD 0xDA +#define CFFPS_12VCS_VOUT_CMD 0xDE #define CFFPS_INPUT_HISTORY_CMD 0xD6 #define CFFPS_INPUT_HISTORY_SIZE 100 @@ -44,22 +47,21 @@ #define CFFPS_MFR_VAUX_FAULT BIT(6) #define CFFPS_MFR_CURRENT_SHARE_WARNING BIT(7) -/* - * LED off state actually relinquishes LED control to PSU firmware, so it can - * turn on the LED for faults. - */ -#define CFFPS_LED_OFF 0 #define CFFPS_LED_BLINK BIT(0) #define CFFPS_LED_ON BIT(1) +#define CFFPS_LED_OFF BIT(2) #define CFFPS_BLINK_RATE_MS 250 enum { CFFPS_DEBUGFS_INPUT_HISTORY = 0, CFFPS_DEBUGFS_FRU, CFFPS_DEBUGFS_PN, + CFFPS_DEBUGFS_HEADER, CFFPS_DEBUGFS_SN, + CFFPS_DEBUGFS_MAX_POWER_OUT, CFFPS_DEBUGFS_CCIN, CFFPS_DEBUGFS_FW, + CFFPS_DEBUGFS_ON_OFF_CONFIG, CFFPS_DEBUGFS_NUM_ENTRIES }; @@ -136,15 +138,15 @@ static ssize_t ibm_cffps_read_input_history(struct ibm_cffps *psu, psu->input_history.byte_count); } -static ssize_t ibm_cffps_debugfs_op(struct file *file, char __user *buf, - size_t count, loff_t *ppos) +static ssize_t ibm_cffps_debugfs_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) { u8 cmd; int i, rc; int *idxp = file->private_data; int idx = *idxp; struct ibm_cffps *psu = to_psu(idxp, idx); - char data[I2C_SMBUS_BLOCK_MAX] = { 0 }; + char data[I2C_SMBUS_BLOCK_MAX + 2] = { 0 }; pmbus_set_page(psu->client, 0); @@ -157,9 +159,20 @@ static ssize_t ibm_cffps_debugfs_op(struct file *file, char __user *buf, case CFFPS_DEBUGFS_PN: cmd = CFFPS_PN_CMD; break; + case CFFPS_DEBUGFS_HEADER: + cmd = CFFPS_HEADER_CMD; + break; case CFFPS_DEBUGFS_SN: cmd = CFFPS_SN_CMD; break; + case CFFPS_DEBUGFS_MAX_POWER_OUT: + rc = i2c_smbus_read_word_swapped(psu->client, + CFFPS_MAX_POWER_OUT_CMD); + if (rc < 0) + return rc; + + rc = snprintf(data, I2C_SMBUS_BLOCK_MAX, "%d", rc); + goto done; case CFFPS_DEBUGFS_CCIN: rc = i2c_smbus_read_word_swapped(psu->client, CFFPS_CCIN_CMD); if (rc < 0) @@ -199,6 +212,14 @@ static ssize_t ibm_cffps_debugfs_op(struct file *file, char __user *buf, return -EOPNOTSUPP; } goto done; + case CFFPS_DEBUGFS_ON_OFF_CONFIG: + rc = i2c_smbus_read_byte_data(psu->client, + PMBUS_ON_OFF_CONFIG); + if (rc < 0) + return rc; + + rc = snprintf(data, 3, "%02x", rc); + goto done; default: return -EINVAL; } @@ -214,9 +235,42 @@ done: return simple_read_from_buffer(buf, count, ppos, data, rc); } +static ssize_t ibm_cffps_debugfs_write(struct file *file, + const char __user *buf, size_t count, + loff_t *ppos) +{ + u8 data; + ssize_t rc; + int *idxp = file->private_data; + int idx = *idxp; + struct ibm_cffps *psu = to_psu(idxp, idx); + + switch (idx) { + case CFFPS_DEBUGFS_ON_OFF_CONFIG: + pmbus_set_page(psu->client, 0); + + rc = simple_write_to_buffer(&data, 1, ppos, buf, count); + if (rc <= 0) + return rc; + + rc = i2c_smbus_write_byte_data(psu->client, + PMBUS_ON_OFF_CONFIG, data); + if (rc) + return rc; + + rc = 1; + break; + default: + return -EINVAL; + } + + return rc; +} + static const struct file_operations ibm_cffps_fops = { .llseek = noop_llseek, - .read = ibm_cffps_debugfs_op, + .read = ibm_cffps_debugfs_read, + .write = ibm_cffps_debugfs_write, .open = simple_open, }; @@ -293,6 +347,9 @@ static int ibm_cffps_read_word_data(struct i2c_client *client, int page, if (mfr & CFFPS_MFR_PS_KILL) rc |= PB_STATUS_OFF; break; + case PMBUS_VIRT_READ_VMON: + rc = pmbus_read_word_data(client, page, CFFPS_12VCS_VOUT_CMD); + break; default: rc = -ENODATA; break; @@ -375,6 +432,9 @@ static void ibm_cffps_create_led_class(struct ibm_cffps *psu) rc = devm_led_classdev_register(dev, &psu->led); if (rc) dev_warn(dev, "failed to register led class: %d\n", rc); + else + i2c_smbus_write_byte_data(client, CFFPS_SYS_CONFIG_CMD, + CFFPS_LED_OFF); } static struct pmbus_driver_info ibm_cffps_info[] = { @@ -396,7 +456,7 @@ static struct pmbus_driver_info ibm_cffps_info[] = { PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP | - PMBUS_HAVE_STATUS_FAN12, + PMBUS_HAVE_STATUS_FAN12 | PMBUS_HAVE_VMON, .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT, @@ -486,15 +546,24 @@ static int ibm_cffps_probe(struct i2c_client *client, debugfs_create_file("part_number", 0444, ibm_cffps_dir, &psu->debugfs_entries[CFFPS_DEBUGFS_PN], &ibm_cffps_fops); + debugfs_create_file("header", 0444, ibm_cffps_dir, + &psu->debugfs_entries[CFFPS_DEBUGFS_HEADER], + &ibm_cffps_fops); debugfs_create_file("serial_number", 0444, ibm_cffps_dir, &psu->debugfs_entries[CFFPS_DEBUGFS_SN], &ibm_cffps_fops); + debugfs_create_file("max_power_out", 0444, ibm_cffps_dir, + &psu->debugfs_entries[CFFPS_DEBUGFS_MAX_POWER_OUT], + &ibm_cffps_fops); debugfs_create_file("ccin", 0444, ibm_cffps_dir, &psu->debugfs_entries[CFFPS_DEBUGFS_CCIN], &ibm_cffps_fops); debugfs_create_file("fw_version", 0444, ibm_cffps_dir, &psu->debugfs_entries[CFFPS_DEBUGFS_FW], &ibm_cffps_fops); + debugfs_create_file("on_off_config", 0644, ibm_cffps_dir, + &psu->debugfs_entries[CFFPS_DEBUGFS_ON_OFF_CONFIG], + &ibm_cffps_fops); return 0; } diff --git a/drivers/hwmon/pmbus/max20730.c b/drivers/hwmon/pmbus/max20730.c new file mode 100644 index 000000000000..294e2212f61e --- /dev/null +++ b/drivers/hwmon/pmbus/max20730.c @@ -0,0 +1,372 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for MAX20730, MAX20734, and MAX20743 Integrated, Step-Down + * Switching Regulators + * + * Copyright 2019 Google LLC. + */ + +#include <linux/bits.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of_device.h> +#include <linux/pmbus.h> +#include <linux/util_macros.h> +#include "pmbus.h" + +enum chips { + max20730, + max20734, + max20743 +}; + +struct max20730_data { + enum chips id; + struct pmbus_driver_info info; + struct mutex lock; /* Used to protect against parallel writes */ + u16 mfr_devset1; +}; + +#define to_max20730_data(x) container_of(x, struct max20730_data, info) + +#define MAX20730_MFR_DEVSET1 0xd2 + +/* + * Convert discreet value to direct data format. Strictly speaking, all passed + * values are constants, so we could do that calculation manually. On the + * downside, that would make the driver more difficult to maintain, so lets + * use this approach. + */ +static u16 val_to_direct(int v, enum pmbus_sensor_classes class, + const struct pmbus_driver_info *info) +{ + int R = info->R[class] - 3; /* take milli-units into account */ + int b = info->b[class] * 1000; + long d; + + d = v * info->m[class] + b; + /* + * R < 0 is true for all callers, so we don't need to bother + * about the R > 0 case. + */ + while (R < 0) { + d = DIV_ROUND_CLOSEST(d, 10); + R++; + } + return (u16)d; +} + +static long direct_to_val(u16 w, enum pmbus_sensor_classes class, + const struct pmbus_driver_info *info) +{ + int R = info->R[class] - 3; + int b = info->b[class] * 1000; + int m = info->m[class]; + long d = (s16)w; + + if (m == 0) + return 0; + + while (R < 0) { + d *= 10; + R++; + } + d = (d - b) / m; + return d; +} + +static u32 max_current[][5] = { + [max20730] = { 13000, 16600, 20100, 23600 }, + [max20734] = { 21000, 27000, 32000, 38000 }, + [max20743] = { 18900, 24100, 29200, 34100 }, +}; + +static int max20730_read_word_data(struct i2c_client *client, int page, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + const struct max20730_data *data = to_max20730_data(info); + int ret = 0; + u32 max_c; + + switch (reg) { + case PMBUS_OT_FAULT_LIMIT: + switch ((data->mfr_devset1 >> 11) & 0x3) { + case 0x0: + ret = val_to_direct(150000, PSC_TEMPERATURE, info); + break; + case 0x1: + ret = val_to_direct(130000, PSC_TEMPERATURE, info); + break; + default: + ret = -ENODATA; + break; + } + break; + case PMBUS_IOUT_OC_FAULT_LIMIT: + max_c = max_current[data->id][(data->mfr_devset1 >> 5) & 0x3]; + ret = val_to_direct(max_c, PSC_CURRENT_OUT, info); + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static int max20730_write_word_data(struct i2c_client *client, int page, + int reg, u16 word) +{ + struct pmbus_driver_info *info; + struct max20730_data *data; + u16 devset1; + int ret = 0; + int idx; + + info = (struct pmbus_driver_info *)pmbus_get_driver_info(client); + data = to_max20730_data(info); + + mutex_lock(&data->lock); + devset1 = data->mfr_devset1; + + switch (reg) { + case PMBUS_OT_FAULT_LIMIT: + devset1 &= ~(BIT(11) | BIT(12)); + if (direct_to_val(word, PSC_TEMPERATURE, info) < 140000) + devset1 |= BIT(11); + break; + case PMBUS_IOUT_OC_FAULT_LIMIT: + devset1 &= ~(BIT(5) | BIT(6)); + + idx = find_closest(direct_to_val(word, PSC_CURRENT_OUT, info), + max_current[data->id], 4); + devset1 |= (idx << 5); + break; + default: + ret = -ENODATA; + break; + } + + if (!ret && devset1 != data->mfr_devset1) { + ret = i2c_smbus_write_word_data(client, MAX20730_MFR_DEVSET1, + devset1); + if (!ret) { + data->mfr_devset1 = devset1; + pmbus_clear_cache(client); + } + } + mutex_unlock(&data->lock); + return ret; +} + +static const struct pmbus_driver_info max20730_info[] = { + [max20730] = { + .pages = 1, + .read_word_data = max20730_read_word_data, + .write_word_data = max20730_write_word_data, + + /* Source : Maxim AN6042 */ + .format[PSC_TEMPERATURE] = direct, + .m[PSC_TEMPERATURE] = 21, + .b[PSC_TEMPERATURE] = 5887, + .R[PSC_TEMPERATURE] = -1, + + .format[PSC_VOLTAGE_IN] = direct, + .m[PSC_VOLTAGE_IN] = 3609, + .b[PSC_VOLTAGE_IN] = 0, + .R[PSC_VOLTAGE_IN] = -2, + + /* + * Values in the datasheet are adjusted for temperature and + * for the relationship between Vin and Vout. + * Unfortunately, the data sheet suggests that Vout measurement + * may be scaled with a resistor array. This is indeed the case + * at least on the evaulation boards. As a result, any in-driver + * adjustments would either be wrong or require elaborate means + * to configure the scaling. Instead of doing that, just report + * raw values and let userspace handle adjustments. + */ + .format[PSC_CURRENT_OUT] = direct, + .m[PSC_CURRENT_OUT] = 153, + .b[PSC_CURRENT_OUT] = 4976, + .R[PSC_CURRENT_OUT] = -1, + + .format[PSC_VOLTAGE_OUT] = linear, + + .func[0] = PMBUS_HAVE_VIN | + PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + }, + [max20734] = { + .pages = 1, + .read_word_data = max20730_read_word_data, + .write_word_data = max20730_write_word_data, + + /* Source : Maxim AN6209 */ + .format[PSC_TEMPERATURE] = direct, + .m[PSC_TEMPERATURE] = 21, + .b[PSC_TEMPERATURE] = 5887, + .R[PSC_TEMPERATURE] = -1, + + .format[PSC_VOLTAGE_IN] = direct, + .m[PSC_VOLTAGE_IN] = 3592, + .b[PSC_VOLTAGE_IN] = 0, + .R[PSC_VOLTAGE_IN] = -2, + + .format[PSC_CURRENT_OUT] = direct, + .m[PSC_CURRENT_OUT] = 111, + .b[PSC_CURRENT_OUT] = 3461, + .R[PSC_CURRENT_OUT] = -1, + + .format[PSC_VOLTAGE_OUT] = linear, + + .func[0] = PMBUS_HAVE_VIN | + PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + }, + [max20743] = { + .pages = 1, + .read_word_data = max20730_read_word_data, + .write_word_data = max20730_write_word_data, + + /* Source : Maxim AN6042 */ + .format[PSC_TEMPERATURE] = direct, + .m[PSC_TEMPERATURE] = 21, + .b[PSC_TEMPERATURE] = 5887, + .R[PSC_TEMPERATURE] = -1, + + .format[PSC_VOLTAGE_IN] = direct, + .m[PSC_VOLTAGE_IN] = 3597, + .b[PSC_VOLTAGE_IN] = 0, + .R[PSC_VOLTAGE_IN] = -2, + + .format[PSC_CURRENT_OUT] = direct, + .m[PSC_CURRENT_OUT] = 95, + .b[PSC_CURRENT_OUT] = 5014, + .R[PSC_CURRENT_OUT] = -1, + + .format[PSC_VOLTAGE_OUT] = linear, + + .func[0] = PMBUS_HAVE_VIN | + PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, + }, +}; + +static int max20730_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + u8 buf[I2C_SMBUS_BLOCK_MAX + 1]; + struct max20730_data *data; + enum chips chip_id; + int ret; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA | + I2C_FUNC_SMBUS_READ_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA)) + return -ENODEV; + + ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf); + if (ret < 0) { + dev_err(&client->dev, "Failed to read Manufacturer ID\n"); + return ret; + } + if (ret != 5 || strncmp(buf, "MAXIM", 5)) { + buf[ret] = '\0'; + dev_err(dev, "Unsupported Manufacturer ID '%s'\n", buf); + return -ENODEV; + } + + /* + * The chips support reading PMBUS_MFR_MODEL. On both MAX20730 + * and MAX20734, reading it returns M20743. Presumably that is + * the reason why the command is not documented. Unfortunately, + * that means that there is no reliable means to detect the chip. + * However, we can at least detect the chip series. Compare + * the returned value against 'M20743' and bail out if there is + * a mismatch. If that doesn't work for all chips, we may have + * to remove this check. + */ + 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 (ret != 6 || strncmp(buf, "M20743", 6)) { + buf[ret] = '\0'; + dev_err(dev, "Unsupported Manufacturer Model '%s'\n", buf); + return -ENODEV; + } + + ret = i2c_smbus_read_block_data(client, PMBUS_MFR_REVISION, buf); + if (ret < 0) { + dev_err(dev, "Failed to read Manufacturer Revision\n"); + return ret; + } + if (ret != 1 || buf[0] != 'F') { + buf[ret] = '\0'; + dev_err(dev, "Unsupported Manufacturer Revision '%s'\n", buf); + return -ENODEV; + } + + if (client->dev.of_node) + chip_id = (enum chips)of_device_get_match_data(dev); + else + chip_id = id->driver_data; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + data->id = chip_id; + mutex_init(&data->lock); + memcpy(&data->info, &max20730_info[chip_id], sizeof(data->info)); + + ret = i2c_smbus_read_word_data(client, MAX20730_MFR_DEVSET1); + if (ret < 0) + return ret; + data->mfr_devset1 = ret; + + return pmbus_do_probe(client, id, &data->info); +} + +static const struct i2c_device_id max20730_id[] = { + { "max20730", max20730 }, + { "max20734", max20734 }, + { "max20743", max20743 }, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, max20730_id); + +static const struct of_device_id max20730_of_match[] = { + { .compatible = "maxim,max20730", .data = (void *)max20730 }, + { .compatible = "maxim,max20734", .data = (void *)max20734 }, + { .compatible = "maxim,max20743", .data = (void *)max20743 }, + { }, +}; + +MODULE_DEVICE_TABLE(of, max20730_of_match); + +static struct i2c_driver max20730_driver = { + .driver = { + .name = "max20730", + .of_match_table = max20730_of_match, + }, + .probe = max20730_probe, + .remove = pmbus_do_remove, + .id_table = max20730_id, +}; + +module_i2c_driver(max20730_driver); + +MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); +MODULE_DESCRIPTION("PMBus driver for Maxim MAX20730 / MAX20734 / MAX20743"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/pmbus/max20751.c b/drivers/hwmon/pmbus/max20751.c index ee5f0cdbde06..da3c38cb9a5c 100644 --- a/drivers/hwmon/pmbus/max20751.c +++ b/drivers/hwmon/pmbus/max20751.c @@ -16,7 +16,7 @@ static struct pmbus_driver_info max20751_info = { .pages = 1, .format[PSC_VOLTAGE_IN] = linear, .format[PSC_VOLTAGE_OUT] = vid, - .vrm_version = vr12, + .vrm_version[0] = vr12, .format[PSC_TEMPERATURE] = linear, .format[PSC_CURRENT_OUT] = linear, .format[PSC_POWER] = linear, diff --git a/drivers/hwmon/pmbus/pmbus.c b/drivers/hwmon/pmbus/pmbus.c index c0bc43d01018..51e8312b6c2d 100644 --- a/drivers/hwmon/pmbus/pmbus.c +++ b/drivers/hwmon/pmbus/pmbus.c @@ -115,7 +115,7 @@ static int pmbus_identify(struct i2c_client *client, } if (pmbus_check_byte_register(client, 0, PMBUS_VOUT_MODE)) { - int vout_mode; + int vout_mode, i; vout_mode = pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE); if (vout_mode >= 0 && vout_mode != 0xff) { @@ -124,7 +124,8 @@ static int pmbus_identify(struct i2c_client *client, break; case 1: info->format[PSC_VOLTAGE_OUT] = vid; - info->vrm_version = vr11; + for (i = 0; i < info->pages; i++) + info->vrm_version[i] = vr11; break; case 2: info->format[PSC_VOLTAGE_OUT] = direct; @@ -210,6 +211,7 @@ static const struct i2c_device_id pmbus_id[] = { {"dps460", (kernel_ulong_t)&pmbus_info_one_skip}, {"dps650ab", (kernel_ulong_t)&pmbus_info_one_skip}, {"dps800", (kernel_ulong_t)&pmbus_info_one_skip}, + {"max20796", (kernel_ulong_t)&pmbus_info_one}, {"mdt040", (kernel_ulong_t)&pmbus_info_one}, {"ncp4200", (kernel_ulong_t)&pmbus_info_one}, {"ncp4208", (kernel_ulong_t)&pmbus_info_one}, diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h index d198af3a92b6..13b34bd67f23 100644 --- a/drivers/hwmon/pmbus/pmbus.h +++ b/drivers/hwmon/pmbus/pmbus.h @@ -22,6 +22,8 @@ enum pmbus_regs { PMBUS_CLEAR_FAULTS = 0x03, PMBUS_PHASE = 0x04, + PMBUS_WRITE_PROTECT = 0x10, + PMBUS_CAPABILITY = 0x19, PMBUS_QUERY = 0x1A, @@ -226,6 +228,15 @@ enum pmbus_regs { #define PB_OPERATION_CONTROL_ON BIT(7) /* + * WRITE_PROTECT + */ +#define PB_WP_ALL BIT(7) /* all but WRITE_PROTECT */ +#define PB_WP_OP BIT(6) /* all but WP, OPERATION, PAGE */ +#define PB_WP_VOUT BIT(5) /* all but WP, OPERATION, PAGE, VOUT, ON_OFF */ + +#define PB_WP_ANY (PB_WP_ALL | PB_WP_OP | PB_WP_VOUT) + +/* * CAPABILITY */ #define PB_CAPABILITY_SMBALERT BIT(4) @@ -377,12 +388,12 @@ enum pmbus_sensor_classes { #define PMBUS_PAGE_VIRTUAL BIT(31) enum pmbus_data_format { linear = 0, direct, vid }; -enum vrm_version { vr11 = 0, vr12, vr13 }; +enum vrm_version { vr11 = 0, vr12, vr13, imvp9, amd625mv }; struct pmbus_driver_info { int pages; /* Total number of pages */ enum pmbus_data_format format[PSC_NUM_CLASSES]; - enum vrm_version vrm_version; + enum vrm_version vrm_version[PMBUS_PAGES]; /* vrm version per page */ /* * Support one set of coefficients for each sensor type * Used for chips providing data in direct mode. diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 8470097907bc..d9c17feb7b4a 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -696,7 +696,7 @@ static long pmbus_reg2data_vid(struct pmbus_data *data, long val = sensor->data; long rv = 0; - switch (data->info->vrm_version) { + switch (data->info->vrm_version[sensor->page]) { case vr11: if (val >= 0x02 && val <= 0xb2) rv = DIV_ROUND_CLOSEST(160000 - (val - 2) * 625, 100); @@ -709,6 +709,14 @@ static long pmbus_reg2data_vid(struct pmbus_data *data, if (val >= 0x01) rv = 500 + (val - 1) * 10; break; + case imvp9: + if (val >= 0x01) + rv = 200 + (val - 1) * 10; + break; + case amd625mv: + if (val >= 0x0 && val <= 0xd8) + rv = DIV_ROUND_CLOSEST(155000 - val * 625, 100); + break; } return rv; } @@ -1088,6 +1096,9 @@ static struct pmbus_sensor *pmbus_add_sensor(struct pmbus_data *data, snprintf(sensor->name, sizeof(sensor->name), "%s%d", name, seq); + if (data->flags & PMBUS_WRITE_PROTECTED) + readonly = true; + sensor->page = page; sensor->reg = reg; sensor->class = class; @@ -2141,6 +2152,15 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, if (ret >= 0 && (ret & PB_CAPABILITY_ERROR_CHECK)) client->flags |= I2C_CLIENT_PEC; + /* + * Check if the chip is write protected. If it is, we can not clear + * faults, and we should not try it. Also, in that case, writes into + * limit registers need to be disabled. + */ + ret = i2c_smbus_read_byte_data(client, PMBUS_WRITE_PROTECT); + if (ret > 0 && (ret & PB_WP_ANY)) + data->flags |= PMBUS_WRITE_PROTECTED | PMBUS_SKIP_STATUS_CHECK; + if (data->info->pages) pmbus_clear_faults(client); else diff --git a/drivers/hwmon/pmbus/pxe1610.c b/drivers/hwmon/pmbus/pxe1610.c index ebe3f023f840..517584cff3de 100644 --- a/drivers/hwmon/pmbus/pxe1610.c +++ b/drivers/hwmon/pmbus/pxe1610.c @@ -19,26 +19,30 @@ static int pxe1610_identify(struct i2c_client *client, struct pmbus_driver_info *info) { - if (pmbus_check_byte_register(client, 0, PMBUS_VOUT_MODE)) { - u8 vout_mode; - int ret; - - /* Read the register with VOUT scaling value.*/ - ret = pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE); - if (ret < 0) - return ret; - - vout_mode = ret & GENMASK(4, 0); - - switch (vout_mode) { - case 1: - info->vrm_version = vr12; - break; - case 2: - info->vrm_version = vr13; - break; - default: - return -ENODEV; + int i; + + for (i = 0; i < PXE1610_NUM_PAGES; i++) { + if (pmbus_check_byte_register(client, i, PMBUS_VOUT_MODE)) { + u8 vout_mode; + int ret; + + /* Read the register with VOUT scaling value.*/ + ret = pmbus_read_byte_data(client, i, PMBUS_VOUT_MODE); + if (ret < 0) + return ret; + + vout_mode = ret & GENMASK(4, 0); + + switch (vout_mode) { + case 1: + info->vrm_version[i] = vr12; + break; + case 2: + info->vrm_version[i] = vr13; + break; + default: + return -ENODEV; + } } } diff --git a/drivers/hwmon/pmbus/tps53679.c b/drivers/hwmon/pmbus/tps53679.c index 86bb3aca09ed..9c22e9013dd7 100644 --- a/drivers/hwmon/pmbus/tps53679.c +++ b/drivers/hwmon/pmbus/tps53679.c @@ -24,27 +24,29 @@ static int tps53679_identify(struct i2c_client *client, struct pmbus_driver_info *info) { u8 vout_params; - int ret; - - /* Read the register with VOUT scaling value.*/ - ret = pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE); - if (ret < 0) - return ret; - - vout_params = ret & GENMASK(4, 0); - - switch (vout_params) { - case TPS53679_PROT_VR13_10MV: - case TPS53679_PROT_VR12_5_10MV: - info->vrm_version = vr13; - break; - case TPS53679_PROT_VR13_5MV: - case TPS53679_PROT_VR12_5MV: - case TPS53679_PROT_IMVP8_5MV: - info->vrm_version = vr12; - break; - default: - return -EINVAL; + int i, ret; + + for (i = 0; i < TPS53679_PAGE_NUM; i++) { + /* Read the register with VOUT scaling value.*/ + ret = pmbus_read_byte_data(client, i, PMBUS_VOUT_MODE); + if (ret < 0) + return ret; + + vout_params = ret & GENMASK(4, 0); + + switch (vout_params) { + case TPS53679_PROT_VR13_10MV: + case TPS53679_PROT_VR12_5_10MV: + info->vrm_version[i] = vr13; + break; + case TPS53679_PROT_VR13_5MV: + case TPS53679_PROT_VR12_5MV: + case TPS53679_PROT_IMVP8_5MV: + info->vrm_version[i] = vr12; + break; + default: + return -EINVAL; + } } return 0; @@ -83,6 +85,7 @@ static int tps53679_probe(struct i2c_client *client, static const struct i2c_device_id tps53679_id[] = { {"tps53679", 0}, + {"tps53688", 0}, {} }; @@ -90,6 +93,7 @@ MODULE_DEVICE_TABLE(i2c, tps53679_id); static const struct of_device_id __maybe_unused tps53679_of_match[] = { {.compatible = "ti,tps53679"}, + {.compatible = "ti,tps53688"}, {} }; MODULE_DEVICE_TABLE(of, tps53679_of_match); diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c index a9229c6b0e84..23ea3415f166 100644 --- a/drivers/hwmon/pmbus/ucd9000.c +++ b/drivers/hwmon/pmbus/ucd9000.c @@ -18,7 +18,8 @@ #include <linux/gpio/driver.h> #include "pmbus.h" -enum chips { ucd9000, ucd90120, ucd90124, ucd90160, ucd9090, ucd90910 }; +enum chips { ucd9000, ucd90120, ucd90124, ucd90160, ucd90320, ucd9090, + ucd90910 }; #define UCD9000_MONITOR_CONFIG 0xd5 #define UCD9000_NUM_PAGES 0xd6 @@ -38,7 +39,7 @@ enum chips { ucd9000, ucd90120, ucd90124, ucd90160, ucd9090, ucd90910 }; #define UCD9000_GPIO_OUTPUT 1 #define UCD9000_MON_TYPE(x) (((x) >> 5) & 0x07) -#define UCD9000_MON_PAGE(x) ((x) & 0x0f) +#define UCD9000_MON_PAGE(x) ((x) & 0x1f) #define UCD9000_MON_VOLTAGE 1 #define UCD9000_MON_TEMPERATURE 2 @@ -50,10 +51,12 @@ enum chips { ucd9000, ucd90120, ucd90124, ucd90160, ucd9090, ucd90910 }; #define UCD9000_GPIO_NAME_LEN 16 #define UCD9090_NUM_GPIOS 23 #define UCD901XX_NUM_GPIOS 26 +#define UCD90320_NUM_GPIOS 84 #define UCD90910_NUM_GPIOS 26 #define UCD9000_DEBUGFS_NAME_LEN 24 #define UCD9000_GPI_COUNT 8 +#define UCD90320_GPI_COUNT 32 struct ucd9000_data { u8 fan_data[UCD9000_NUM_FAN][I2C_SMBUS_BLOCK_MAX]; @@ -131,6 +134,7 @@ static const struct i2c_device_id ucd9000_id[] = { {"ucd90120", ucd90120}, {"ucd90124", ucd90124}, {"ucd90160", ucd90160}, + {"ucd90320", ucd90320}, {"ucd9090", ucd9090}, {"ucd90910", ucd90910}, {} @@ -155,6 +159,10 @@ static const struct of_device_id __maybe_unused ucd9000_of_match[] = { .data = (void *)ucd90160 }, { + .compatible = "ti,ucd90320", + .data = (void *)ucd90320 + }, + { .compatible = "ti,ucd9090", .data = (void *)ucd9090 }, @@ -322,6 +330,9 @@ static void ucd9000_probe_gpio(struct i2c_client *client, case ucd90160: data->gpio.ngpio = UCD901XX_NUM_GPIOS; break; + case ucd90320: + data->gpio.ngpio = UCD90320_NUM_GPIOS; + break; case ucd90910: data->gpio.ngpio = UCD90910_NUM_GPIOS; break; @@ -372,17 +383,18 @@ static int ucd9000_debugfs_show_mfr_status_bit(void *data, u64 *val) struct ucd9000_debugfs_entry *entry = data; struct i2c_client *client = entry->client; u8 buffer[I2C_SMBUS_BLOCK_MAX]; - int ret; + int ret, i; ret = ucd9000_get_mfr_status(client, buffer); if (ret < 0) return ret; /* - * Attribute only created for devices with gpi fault bits at bits - * 16-23, which is the second byte of the response. + * GPI fault bits are in sets of 8, two bytes from end of response. */ - *val = !!(buffer[1] & BIT(entry->index)); + i = ret - 3 - entry->index / 8; + if (i >= 0) + *val = !!(buffer[i] & BIT(entry->index % 8)); return 0; } @@ -422,7 +434,7 @@ static int ucd9000_init_debugfs(struct i2c_client *client, { struct dentry *debugfs; struct ucd9000_debugfs_entry *entries; - int i; + int i, gpi_count; char name[UCD9000_DEBUGFS_NAME_LEN]; debugfs = pmbus_get_debugfs_dir(client); @@ -435,18 +447,21 @@ static int ucd9000_init_debugfs(struct i2c_client *client, /* * Of the chips this driver supports, only the UCD9090, UCD90160, - * and UCD90910 report GPI faults in their MFR_STATUS register, so only - * create the GPI fault debugfs attributes for those chips. + * UCD90320, and UCD90910 report GPI faults in their MFR_STATUS + * register, so only create the GPI fault debugfs attributes for those + * chips. */ if (mid->driver_data == ucd9090 || mid->driver_data == ucd90160 || - mid->driver_data == ucd90910) { + mid->driver_data == ucd90320 || mid->driver_data == ucd90910) { + gpi_count = mid->driver_data == ucd90320 ? UCD90320_GPI_COUNT + : UCD9000_GPI_COUNT; entries = devm_kcalloc(&client->dev, - UCD9000_GPI_COUNT, sizeof(*entries), + gpi_count, sizeof(*entries), GFP_KERNEL); if (!entries) return -ENOMEM; - for (i = 0; i < UCD9000_GPI_COUNT; i++) { + for (i = 0; i < gpi_count; i++) { entries[i].client = client; entries[i].index = i; scnprintf(name, UCD9000_DEBUGFS_NAME_LEN, diff --git a/drivers/hwmon/pmbus/xdpe12284.c b/drivers/hwmon/pmbus/xdpe12284.c new file mode 100644 index 000000000000..3d47806ff4d3 --- /dev/null +++ b/drivers/hwmon/pmbus/xdpe12284.c @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for Infineon Multi-phase Digital VR Controllers + * + * Copyright (c) 2020 Mellanox Technologies. All rights reserved. + */ + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include "pmbus.h" + +#define XDPE122_PROT_VR12_5MV 0x01 /* VR12.0 mode, 5-mV DAC */ +#define XDPE122_PROT_VR12_5_10MV 0x02 /* VR12.5 mode, 10-mV DAC */ +#define XDPE122_PROT_IMVP9_10MV 0x03 /* IMVP9 mode, 10-mV DAC */ +#define XDPE122_AMD_625MV 0x10 /* AMD mode 6.25mV */ +#define XDPE122_PAGE_NUM 2 + +static int xdpe122_identify(struct i2c_client *client, + struct pmbus_driver_info *info) +{ + u8 vout_params; + int i, ret; + + for (i = 0; i < XDPE122_PAGE_NUM; i++) { + /* Read the register with VOUT scaling value.*/ + ret = pmbus_read_byte_data(client, i, PMBUS_VOUT_MODE); + if (ret < 0) + return ret; + + vout_params = ret & GENMASK(4, 0); + + switch (vout_params) { + case XDPE122_PROT_VR12_5_10MV: + info->vrm_version[i] = vr13; + break; + case XDPE122_PROT_VR12_5MV: + info->vrm_version[i] = vr12; + break; + case XDPE122_PROT_IMVP9_10MV: + info->vrm_version[i] = imvp9; + break; + case XDPE122_AMD_625MV: + info->vrm_version[i] = amd625mv; + break; + default: + return -EINVAL; + } + } + + return 0; +} + +static struct pmbus_driver_info xdpe122_info = { + .pages = XDPE122_PAGE_NUM, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = vid, + .format[PSC_TEMPERATURE] = linear, + .format[PSC_CURRENT_IN] = linear, + .format[PSC_CURRENT_OUT] = linear, + .format[PSC_POWER] = linear, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | + PMBUS_HAVE_POUT | PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT, + .func[1] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | + PMBUS_HAVE_POUT | PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT, + .identify = xdpe122_identify, +}; + +static int xdpe122_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct pmbus_driver_info *info; + + info = devm_kmemdup(&client->dev, &xdpe122_info, sizeof(*info), + GFP_KERNEL); + if (!info) + return -ENOMEM; + + return pmbus_do_probe(client, id, info); +} + +static const struct i2c_device_id xdpe122_id[] = { + {"xdpe12254", 0}, + {"xdpe12284", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, xdpe122_id); + +static const struct of_device_id __maybe_unused xdpe122_of_match[] = { + {.compatible = "infineon, xdpe12254"}, + {.compatible = "infineon, xdpe12284"}, + {} +}; +MODULE_DEVICE_TABLE(of, xdpe122_of_match); + +static struct i2c_driver xdpe122_driver = { + .driver = { + .name = "xdpe12284", + .of_match_table = of_match_ptr(xdpe122_of_match), + }, + .probe = xdpe122_probe, + .remove = pmbus_do_remove, + .id_table = xdpe122_id, +}; + +module_i2c_driver(xdpe122_driver); + +MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>"); +MODULE_DESCRIPTION("PMBus driver for Infineon XDPE122 family"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c index 42ffd2e5182d..30b7b3ea8836 100644 --- a/drivers/hwmon/pwm-fan.c +++ b/drivers/hwmon/pwm-fan.c @@ -390,8 +390,7 @@ static int pwm_fan_probe(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM_SLEEP -static int pwm_fan_suspend(struct device *dev) +static int pwm_fan_disable(struct device *dev) { struct pwm_fan_ctx *ctx = dev_get_drvdata(dev); struct pwm_args args; @@ -418,6 +417,17 @@ static int pwm_fan_suspend(struct device *dev) return 0; } +static void pwm_fan_shutdown(struct platform_device *pdev) +{ + pwm_fan_disable(&pdev->dev); +} + +#ifdef CONFIG_PM_SLEEP +static int pwm_fan_suspend(struct device *dev) +{ + return pwm_fan_disable(dev); +} + static int pwm_fan_resume(struct device *dev) { struct pwm_fan_ctx *ctx = dev_get_drvdata(dev); @@ -455,6 +465,7 @@ MODULE_DEVICE_TABLE(of, of_pwm_fan_match); static struct platform_driver pwm_fan_driver = { .probe = pwm_fan_probe, + .shutdown = pwm_fan_shutdown, .driver = { .name = "pwm-fan", .pm = &pwm_fan_pm, diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c index eb171d15ac48..7ffadc2da57b 100644 --- a/drivers/hwmon/w83627ehf.c +++ b/drivers/hwmon/w83627ehf.c @@ -28,8 +28,6 @@ * w83627uhg 8 2 2 3 0xa230 0xc1 0x5ca3 * w83667hg 9 5 3 3 0xa510 0xc1 0x5ca3 * w83667hg-b 9 5 3 4 0xb350 0xc1 0x5ca3 - * nct6775f 9 4 3 9 0xb470 0xc1 0x5ca3 - * nct6776f 9 5 3 9 0xC330 0xc1 0x5ca3 */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -50,7 +48,7 @@ enum kinds { w83627ehf, w83627dhg, w83627dhg_p, w83627uhg, - w83667hg, w83667hg_b, nct6775, nct6776, + w83667hg, w83667hg_b, }; /* used to set data->name = w83627ehf_device_names[data->sio_kind] */ @@ -61,18 +59,12 @@ static const char * const w83627ehf_device_names[] = { "w83627uhg", "w83667hg", "w83667hg", - "nct6775", - "nct6776", }; static unsigned short force_id; module_param(force_id, ushort, 0); MODULE_PARM_DESC(force_id, "Override the detected device ID"); -static unsigned short fan_debounce; -module_param(fan_debounce, ushort, 0); -MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal"); - #define DRVNAME "w83627ehf" /* @@ -97,8 +89,6 @@ MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal"); #define SIO_W83627UHG_ID 0xa230 #define SIO_W83667HG_ID 0xa510 #define SIO_W83667HG_B_ID 0xb350 -#define SIO_NCT6775_ID 0xb470 -#define SIO_NCT6776_ID 0xc330 #define SIO_ID_MASK 0xFFF0 static inline void @@ -187,11 +177,6 @@ static const u16 W83627EHF_REG_TEMP_CONFIG[] = { 0, 0x152, 0x252, 0 }; #define W83627EHF_REG_DIODE 0x59 #define W83627EHF_REG_SMI_OVT 0x4C -/* NCT6775F has its own fan divider registers */ -#define NCT6775_REG_FANDIV1 0x506 -#define NCT6775_REG_FANDIV2 0x507 -#define NCT6775_REG_FAN_DEBOUNCE 0xf0 - #define W83627EHF_REG_ALARM1 0x459 #define W83627EHF_REG_ALARM2 0x45A #define W83627EHF_REG_ALARM3 0x45B @@ -235,28 +220,6 @@ static const u16 W83627EHF_REG_FAN_STEP_OUTPUT_W83667_B[] static const u16 W83627EHF_REG_TEMP_OFFSET[] = { 0x454, 0x455, 0x456 }; -static const u16 NCT6775_REG_TARGET[] = { 0x101, 0x201, 0x301 }; -static const u16 NCT6775_REG_FAN_MODE[] = { 0x102, 0x202, 0x302 }; -static const u16 NCT6775_REG_FAN_STOP_OUTPUT[] = { 0x105, 0x205, 0x305 }; -static const u16 NCT6775_REG_FAN_START_OUTPUT[] = { 0x106, 0x206, 0x306 }; -static const u16 NCT6775_REG_FAN_STOP_TIME[] = { 0x107, 0x207, 0x307 }; -static const u16 NCT6775_REG_PWM[] = { 0x109, 0x209, 0x309 }; -static const u16 NCT6775_REG_FAN_MAX_OUTPUT[] = { 0x10a, 0x20a, 0x30a }; -static const u16 NCT6775_REG_FAN_STEP_OUTPUT[] = { 0x10b, 0x20b, 0x30b }; -static const u16 NCT6775_REG_FAN[] = { 0x630, 0x632, 0x634, 0x636, 0x638 }; -static const u16 NCT6776_REG_FAN_MIN[] = { 0x63a, 0x63c, 0x63e, 0x640, 0x642}; - -static const u16 NCT6775_REG_TEMP[] - = { 0x27, 0x150, 0x250, 0x73, 0x75, 0x77, 0x62b, 0x62c, 0x62d }; -static const u16 NCT6775_REG_TEMP_CONFIG[] - = { 0, 0x152, 0x252, 0, 0, 0, 0x628, 0x629, 0x62A }; -static const u16 NCT6775_REG_TEMP_HYST[] - = { 0x3a, 0x153, 0x253, 0, 0, 0, 0x673, 0x678, 0x67D }; -static const u16 NCT6775_REG_TEMP_OVER[] - = { 0x39, 0x155, 0x255, 0, 0, 0, 0x672, 0x677, 0x67C }; -static const u16 NCT6775_REG_TEMP_SOURCE[] - = { 0x621, 0x622, 0x623, 0x100, 0x200, 0x300, 0x624, 0x625, 0x626 }; - static const char *const w83667hg_b_temp_label[] = { "SYSTIN", "CPUTIN", @@ -268,57 +231,7 @@ static const char *const w83667hg_b_temp_label[] = { "PECI Agent 4" }; -static const char *const nct6775_temp_label[] = { - "", - "SYSTIN", - "CPUTIN", - "AUXTIN", - "AMD SB-TSI", - "PECI Agent 0", - "PECI Agent 1", - "PECI Agent 2", - "PECI Agent 3", - "PECI Agent 4", - "PECI Agent 5", - "PECI Agent 6", - "PECI Agent 7", - "PCH_CHIP_CPU_MAX_TEMP", - "PCH_CHIP_TEMP", - "PCH_CPU_TEMP", - "PCH_MCH_TEMP", - "PCH_DIM0_TEMP", - "PCH_DIM1_TEMP", - "PCH_DIM2_TEMP", - "PCH_DIM3_TEMP" -}; - -static const char *const nct6776_temp_label[] = { - "", - "SYSTIN", - "CPUTIN", - "AUXTIN", - "SMBUSMASTER 0", - "SMBUSMASTER 1", - "SMBUSMASTER 2", - "SMBUSMASTER 3", - "SMBUSMASTER 4", - "SMBUSMASTER 5", - "SMBUSMASTER 6", - "SMBUSMASTER 7", - "PECI Agent 0", - "PECI Agent 1", - "PCH_CHIP_CPU_MAX_TEMP", - "PCH_CHIP_TEMP", - "PCH_CPU_TEMP", - "PCH_MCH_TEMP", - "PCH_DIM0_TEMP", - "PCH_DIM1_TEMP", - "PCH_DIM2_TEMP", - "PCH_DIM3_TEMP", - "BYTE_TEMP" -}; - -#define NUM_REG_TEMP ARRAY_SIZE(NCT6775_REG_TEMP) +#define NUM_REG_TEMP ARRAY_SIZE(W83627EHF_REG_TEMP) static int is_word_sized(u16 reg) { @@ -358,31 +271,6 @@ static unsigned int fan_from_reg8(u16 reg, unsigned int divreg) return 1350000U / (reg << divreg); } -static unsigned int fan_from_reg13(u16 reg, unsigned int divreg) -{ - if ((reg & 0xff1f) == 0xff1f) - return 0; - - reg = (reg & 0x1f) | ((reg & 0xff00) >> 3); - - if (reg == 0) - return 0; - - return 1350000U / reg; -} - -static unsigned int fan_from_reg16(u16 reg, unsigned int divreg) -{ - if (reg == 0 || reg == 0xffff) - return 0; - - /* - * Even though the registers are 16 bit wide, the fan divisor - * still applies. - */ - return 1350000U / (reg << divreg); -} - static inline unsigned int div_from_reg(u8 reg) { @@ -418,7 +306,6 @@ struct w83627ehf_data { int addr; /* IO base of hw monitor block */ const char *name; - struct device *hwmon_dev; struct mutex lock; u16 reg_temp[NUM_REG_TEMP]; @@ -428,20 +315,10 @@ struct w83627ehf_data { u8 temp_src[NUM_REG_TEMP]; const char * const *temp_label; - const u16 *REG_PWM; - const u16 *REG_TARGET; - const u16 *REG_FAN; - const u16 *REG_FAN_MIN; - const u16 *REG_FAN_START_OUTPUT; - const u16 *REG_FAN_STOP_OUTPUT; - const u16 *REG_FAN_STOP_TIME; const u16 *REG_FAN_MAX_OUTPUT; const u16 *REG_FAN_STEP_OUTPUT; const u16 *scale_in; - unsigned int (*fan_from_reg)(u16 reg, unsigned int divreg); - unsigned int (*fan_from_reg_min)(u16 reg, unsigned int divreg); - struct mutex update_lock; char valid; /* !=0 if following fields are valid */ unsigned long last_updated; /* In jiffies */ @@ -457,7 +334,6 @@ struct w83627ehf_data { u8 fan_div[5]; u8 has_fan; /* some fan inputs can be disabled */ u8 has_fan_min; /* some fans don't have min register */ - bool has_fan_div; u8 temp_type[3]; s8 temp_offset[3]; s16 temp[9]; @@ -494,6 +370,7 @@ struct w83627ehf_data { u16 have_temp_offset; u8 in6_skip:1; u8 temp3_val_only:1; + u8 have_vid:1; #ifdef CONFIG_PM /* Remember extra register values over suspend/resume */ @@ -584,35 +461,6 @@ static int w83627ehf_write_temp(struct w83627ehf_data *data, u16 reg, } /* This function assumes that the caller holds data->update_lock */ -static void nct6775_write_fan_div(struct w83627ehf_data *data, int nr) -{ - u8 reg; - - switch (nr) { - case 0: - reg = (w83627ehf_read_value(data, NCT6775_REG_FANDIV1) & 0x70) - | (data->fan_div[0] & 0x7); - w83627ehf_write_value(data, NCT6775_REG_FANDIV1, reg); - break; - case 1: - reg = (w83627ehf_read_value(data, NCT6775_REG_FANDIV1) & 0x7) - | ((data->fan_div[1] << 4) & 0x70); - w83627ehf_write_value(data, NCT6775_REG_FANDIV1, reg); - break; - case 2: - reg = (w83627ehf_read_value(data, NCT6775_REG_FANDIV2) & 0x70) - | (data->fan_div[2] & 0x7); - w83627ehf_write_value(data, NCT6775_REG_FANDIV2, reg); - break; - case 3: - reg = (w83627ehf_read_value(data, NCT6775_REG_FANDIV2) & 0x7) - | ((data->fan_div[3] << 4) & 0x70); - w83627ehf_write_value(data, NCT6775_REG_FANDIV2, reg); - break; - } -} - -/* This function assumes that the caller holds data->update_lock */ static void w83627ehf_write_fan_div(struct w83627ehf_data *data, int nr) { u8 reg; @@ -663,32 +511,6 @@ static void w83627ehf_write_fan_div(struct w83627ehf_data *data, int nr) } } -static void w83627ehf_write_fan_div_common(struct device *dev, - struct w83627ehf_data *data, int nr) -{ - struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev); - - if (sio_data->kind == nct6776) - ; /* no dividers, do nothing */ - else if (sio_data->kind == nct6775) - nct6775_write_fan_div(data, nr); - else - w83627ehf_write_fan_div(data, nr); -} - -static void nct6775_update_fan_div(struct w83627ehf_data *data) -{ - u8 i; - - i = w83627ehf_read_value(data, NCT6775_REG_FANDIV1); - data->fan_div[0] = i & 0x7; - data->fan_div[1] = (i & 0x70) >> 4; - i = w83627ehf_read_value(data, NCT6775_REG_FANDIV2); - data->fan_div[2] = i & 0x7; - if (data->has_fan & (1<<3)) - data->fan_div[3] = (i & 0x70) >> 4; -} - static void w83627ehf_update_fan_div(struct w83627ehf_data *data) { int i; @@ -714,37 +536,6 @@ static void w83627ehf_update_fan_div(struct w83627ehf_data *data) } } -static void w83627ehf_update_fan_div_common(struct device *dev, - struct w83627ehf_data *data) -{ - struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev); - - if (sio_data->kind == nct6776) - ; /* no dividers, do nothing */ - else if (sio_data->kind == nct6775) - nct6775_update_fan_div(data); - else - w83627ehf_update_fan_div(data); -} - -static void nct6775_update_pwm(struct w83627ehf_data *data) -{ - int i; - int pwmcfg, fanmodecfg; - - for (i = 0; i < data->pwm_num; i++) { - pwmcfg = w83627ehf_read_value(data, - W83627EHF_REG_PWM_ENABLE[i]); - fanmodecfg = w83627ehf_read_value(data, - NCT6775_REG_FAN_MODE[i]); - data->pwm_mode[i] = - ((pwmcfg >> W83627EHF_PWM_MODE_SHIFT[i]) & 1) ? 0 : 1; - data->pwm_enable[i] = ((fanmodecfg >> 4) & 7) + 1; - data->tolerance[i] = fanmodecfg & 0x0f; - data->pwm[i] = w83627ehf_read_value(data, data->REG_PWM[i]); - } -} - static void w83627ehf_update_pwm(struct w83627ehf_data *data) { int i; @@ -765,28 +556,15 @@ static void w83627ehf_update_pwm(struct w83627ehf_data *data) ((pwmcfg >> W83627EHF_PWM_MODE_SHIFT[i]) & 1) ? 0 : 1; data->pwm_enable[i] = ((pwmcfg >> W83627EHF_PWM_ENABLE_SHIFT[i]) & 3) + 1; - data->pwm[i] = w83627ehf_read_value(data, data->REG_PWM[i]); + data->pwm[i] = w83627ehf_read_value(data, W83627EHF_REG_PWM[i]); data->tolerance[i] = (tolerance >> (i == 1 ? 4 : 0)) & 0x0f; } } -static void w83627ehf_update_pwm_common(struct device *dev, - struct w83627ehf_data *data) -{ - struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev); - - if (sio_data->kind == nct6775 || sio_data->kind == nct6776) - nct6775_update_pwm(data); - else - w83627ehf_update_pwm(data); -} - static struct w83627ehf_data *w83627ehf_update_device(struct device *dev) { struct w83627ehf_data *data = dev_get_drvdata(dev); - struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev); - int i; mutex_lock(&data->update_lock); @@ -794,7 +572,7 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev) if (time_after(jiffies, data->last_updated + HZ + HZ/2) || !data->valid) { /* Fan clock dividers */ - w83627ehf_update_fan_div_common(dev, data); + w83627ehf_update_fan_div(data); /* Measured voltages and limits */ for (i = 0; i < data->in_num; i++) { @@ -816,40 +594,36 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev) if (!(data->has_fan & (1 << i))) continue; - reg = w83627ehf_read_value(data, data->REG_FAN[i]); - data->rpm[i] = data->fan_from_reg(reg, - data->fan_div[i]); + reg = w83627ehf_read_value(data, W83627EHF_REG_FAN[i]); + data->rpm[i] = fan_from_reg8(reg, data->fan_div[i]); if (data->has_fan_min & (1 << i)) data->fan_min[i] = w83627ehf_read_value(data, - data->REG_FAN_MIN[i]); + W83627EHF_REG_FAN_MIN[i]); /* * If we failed to measure the fan speed and clock * divider can be increased, let's try that for next * time */ - if (data->has_fan_div - && (reg >= 0xff || (sio_data->kind == nct6775 - && reg == 0x00)) - && data->fan_div[i] < 0x07) { + if (reg >= 0xff && data->fan_div[i] < 0x07) { dev_dbg(dev, "Increasing fan%d clock divider from %u to %u\n", i + 1, div_from_reg(data->fan_div[i]), div_from_reg(data->fan_div[i] + 1)); data->fan_div[i]++; - w83627ehf_write_fan_div_common(dev, data, i); + w83627ehf_write_fan_div(data, i); /* Preserve min limit if possible */ if ((data->has_fan_min & (1 << i)) && data->fan_min[i] >= 2 && data->fan_min[i] != 255) w83627ehf_write_value(data, - data->REG_FAN_MIN[i], + W83627EHF_REG_FAN_MIN[i], (data->fan_min[i] /= 2)); } } - w83627ehf_update_pwm_common(dev, data); + w83627ehf_update_pwm(data); for (i = 0; i < data->pwm_num; i++) { if (!(data->has_fan & (1 << i))) @@ -857,13 +631,13 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev) data->fan_start_output[i] = w83627ehf_read_value(data, - data->REG_FAN_START_OUTPUT[i]); + W83627EHF_REG_FAN_START_OUTPUT[i]); data->fan_stop_output[i] = w83627ehf_read_value(data, - data->REG_FAN_STOP_OUTPUT[i]); + W83627EHF_REG_FAN_STOP_OUTPUT[i]); data->fan_stop_time[i] = w83627ehf_read_value(data, - data->REG_FAN_STOP_TIME[i]); + W83627EHF_REG_FAN_STOP_TIME[i]); if (data->REG_FAN_MAX_OUTPUT && data->REG_FAN_MAX_OUTPUT[i] != 0xff) @@ -879,7 +653,7 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev) data->target_temp[i] = w83627ehf_read_value(data, - data->REG_TARGET[i]) & + W83627EHF_REG_TARGET[i]) & (data->pwm_mode[i] == 1 ? 0x7f : 0xff); } @@ -923,199 +697,61 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev) return data; } -/* - * Sysfs callback functions - */ -#define show_in_reg(reg) \ -static ssize_t \ -show_##reg(struct device *dev, struct device_attribute *attr, \ - char *buf) \ -{ \ - struct w83627ehf_data *data = w83627ehf_update_device(dev); \ - struct sensor_device_attribute *sensor_attr = \ - to_sensor_dev_attr(attr); \ - int nr = sensor_attr->index; \ - return sprintf(buf, "%ld\n", in_from_reg(data->reg[nr], nr, \ - data->scale_in)); \ -} -show_in_reg(in) -show_in_reg(in_min) -show_in_reg(in_max) - #define store_in_reg(REG, reg) \ -static ssize_t \ -store_in_##reg(struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ +static int \ +store_in_##reg(struct device *dev, struct w83627ehf_data *data, int channel, \ + long val) \ { \ - struct w83627ehf_data *data = dev_get_drvdata(dev); \ - struct sensor_device_attribute *sensor_attr = \ - to_sensor_dev_attr(attr); \ - int nr = sensor_attr->index; \ - unsigned long val; \ - int err; \ - err = kstrtoul(buf, 10, &val); \ - if (err < 0) \ - return err; \ + if (val < 0) \ + return -EINVAL; \ mutex_lock(&data->update_lock); \ - data->in_##reg[nr] = in_to_reg(val, nr, data->scale_in); \ - w83627ehf_write_value(data, W83627EHF_REG_IN_##REG(nr), \ - data->in_##reg[nr]); \ + data->in_##reg[channel] = in_to_reg(val, channel, data->scale_in); \ + w83627ehf_write_value(data, W83627EHF_REG_IN_##REG(channel), \ + data->in_##reg[channel]); \ mutex_unlock(&data->update_lock); \ - return count; \ + return 0; \ } store_in_reg(MIN, min) store_in_reg(MAX, max) -static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, - char *buf) +static int +store_fan_min(struct device *dev, struct w83627ehf_data *data, int channel, + long val) { - struct w83627ehf_data *data = w83627ehf_update_device(dev); - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - return sprintf(buf, "%u\n", (data->alarms >> nr) & 0x01); -} - -static struct sensor_device_attribute sda_in_input[] = { - SENSOR_ATTR(in0_input, S_IRUGO, show_in, NULL, 0), - SENSOR_ATTR(in1_input, S_IRUGO, show_in, NULL, 1), - SENSOR_ATTR(in2_input, S_IRUGO, show_in, NULL, 2), - SENSOR_ATTR(in3_input, S_IRUGO, show_in, NULL, 3), - SENSOR_ATTR(in4_input, S_IRUGO, show_in, NULL, 4), - SENSOR_ATTR(in5_input, S_IRUGO, show_in, NULL, 5), - SENSOR_ATTR(in6_input, S_IRUGO, show_in, NULL, 6), - SENSOR_ATTR(in7_input, S_IRUGO, show_in, NULL, 7), - SENSOR_ATTR(in8_input, S_IRUGO, show_in, NULL, 8), - SENSOR_ATTR(in9_input, S_IRUGO, show_in, NULL, 9), -}; - -static struct sensor_device_attribute sda_in_alarm[] = { - SENSOR_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0), - SENSOR_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1), - SENSOR_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2), - SENSOR_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, 3), - SENSOR_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, 8), - SENSOR_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, 21), - SENSOR_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 20), - SENSOR_ATTR(in7_alarm, S_IRUGO, show_alarm, NULL, 16), - SENSOR_ATTR(in8_alarm, S_IRUGO, show_alarm, NULL, 17), - SENSOR_ATTR(in9_alarm, S_IRUGO, show_alarm, NULL, 19), -}; - -static struct sensor_device_attribute sda_in_min[] = { - SENSOR_ATTR(in0_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 0), - SENSOR_ATTR(in1_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 1), - SENSOR_ATTR(in2_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 2), - SENSOR_ATTR(in3_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 3), - SENSOR_ATTR(in4_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 4), - SENSOR_ATTR(in5_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 5), - SENSOR_ATTR(in6_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 6), - SENSOR_ATTR(in7_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 7), - SENSOR_ATTR(in8_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 8), - SENSOR_ATTR(in9_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 9), -}; - -static struct sensor_device_attribute sda_in_max[] = { - SENSOR_ATTR(in0_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 0), - SENSOR_ATTR(in1_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 1), - SENSOR_ATTR(in2_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 2), - SENSOR_ATTR(in3_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 3), - SENSOR_ATTR(in4_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 4), - SENSOR_ATTR(in5_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 5), - SENSOR_ATTR(in6_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 6), - SENSOR_ATTR(in7_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 7), - SENSOR_ATTR(in8_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 8), - SENSOR_ATTR(in9_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 9), -}; - -static ssize_t -show_fan(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct w83627ehf_data *data = w83627ehf_update_device(dev); - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - return sprintf(buf, "%d\n", data->rpm[nr]); -} - -static ssize_t -show_fan_min(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct w83627ehf_data *data = w83627ehf_update_device(dev); - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - return sprintf(buf, "%d\n", - data->fan_from_reg_min(data->fan_min[nr], - data->fan_div[nr])); -} - -static ssize_t -show_fan_div(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct w83627ehf_data *data = w83627ehf_update_device(dev); - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - return sprintf(buf, "%u\n", div_from_reg(data->fan_div[nr])); -} - -static ssize_t -store_fan_min(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct w83627ehf_data *data = dev_get_drvdata(dev); - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - unsigned long val; - int err; unsigned int reg; u8 new_div; - err = kstrtoul(buf, 10, &val); - if (err < 0) - return err; + if (val < 0) + return -EINVAL; mutex_lock(&data->update_lock); - if (!data->has_fan_div) { - /* - * Only NCT6776F for now, so we know that this is a 13 bit - * register - */ - if (!val) { - val = 0xff1f; - } else { - if (val > 1350000U) - val = 135000U; - val = 1350000U / val; - val = (val & 0x1f) | ((val << 3) & 0xff00); - } - data->fan_min[nr] = val; - goto done; /* Leave fan divider alone */ - } if (!val) { /* No min limit, alarm disabled */ - data->fan_min[nr] = 255; - new_div = data->fan_div[nr]; /* No change */ - dev_info(dev, "fan%u low limit and alarm disabled\n", nr + 1); + data->fan_min[channel] = 255; + new_div = data->fan_div[channel]; /* No change */ + dev_info(dev, "fan%u low limit and alarm disabled\n", + channel + 1); } else if ((reg = 1350000U / val) >= 128 * 255) { /* * Speed below this value cannot possibly be represented, * even with the highest divider (128) */ - data->fan_min[nr] = 254; + data->fan_min[channel] = 254; new_div = 7; /* 128 == (1 << 7) */ dev_warn(dev, "fan%u low limit %lu below minimum %u, set to minimum\n", - nr + 1, val, data->fan_from_reg_min(254, 7)); + channel + 1, val, fan_from_reg8(254, 7)); } else if (!reg) { /* * Speed above this value cannot possibly be represented, * even with the lowest divider (1) */ - data->fan_min[nr] = 1; + data->fan_min[channel] = 1; new_div = 0; /* 1 == (1 << 0) */ dev_warn(dev, "fan%u low limit %lu above maximum %u, set to maximum\n", - nr + 1, val, data->fan_from_reg_min(1, 0)); + channel + 1, val, fan_from_reg8(1, 0)); } else { /* * Automatically pick the best divider, i.e. the one such @@ -1127,362 +763,117 @@ store_fan_min(struct device *dev, struct device_attribute *attr, reg >>= 1; new_div++; } - data->fan_min[nr] = reg; + data->fan_min[channel] = reg; } /* * Write both the fan clock divider (if it changed) and the new * fan min (unconditionally) */ - if (new_div != data->fan_div[nr]) { + if (new_div != data->fan_div[channel]) { dev_dbg(dev, "fan%u clock divider changed from %u to %u\n", - nr + 1, div_from_reg(data->fan_div[nr]), + channel + 1, div_from_reg(data->fan_div[channel]), div_from_reg(new_div)); - data->fan_div[nr] = new_div; - w83627ehf_write_fan_div_common(dev, data, nr); + data->fan_div[channel] = new_div; + w83627ehf_write_fan_div(data, channel); /* Give the chip time to sample a new speed value */ data->last_updated = jiffies; } -done: - w83627ehf_write_value(data, data->REG_FAN_MIN[nr], - data->fan_min[nr]); - mutex_unlock(&data->update_lock); - return count; -} - -static struct sensor_device_attribute sda_fan_input[] = { - SENSOR_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0), - SENSOR_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1), - SENSOR_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2), - SENSOR_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 3), - SENSOR_ATTR(fan5_input, S_IRUGO, show_fan, NULL, 4), -}; - -static struct sensor_device_attribute sda_fan_alarm[] = { - SENSOR_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, 6), - SENSOR_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 7), - SENSOR_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, 11), - SENSOR_ATTR(fan4_alarm, S_IRUGO, show_alarm, NULL, 10), - SENSOR_ATTR(fan5_alarm, S_IRUGO, show_alarm, NULL, 23), -}; - -static struct sensor_device_attribute sda_fan_min[] = { - SENSOR_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min, - store_fan_min, 0), - SENSOR_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min, - store_fan_min, 1), - SENSOR_ATTR(fan3_min, S_IWUSR | S_IRUGO, show_fan_min, - store_fan_min, 2), - SENSOR_ATTR(fan4_min, S_IWUSR | S_IRUGO, show_fan_min, - store_fan_min, 3), - SENSOR_ATTR(fan5_min, S_IWUSR | S_IRUGO, show_fan_min, - store_fan_min, 4), -}; - -static struct sensor_device_attribute sda_fan_div[] = { - SENSOR_ATTR(fan1_div, S_IRUGO, show_fan_div, NULL, 0), - SENSOR_ATTR(fan2_div, S_IRUGO, show_fan_div, NULL, 1), - SENSOR_ATTR(fan3_div, S_IRUGO, show_fan_div, NULL, 2), - SENSOR_ATTR(fan4_div, S_IRUGO, show_fan_div, NULL, 3), - SENSOR_ATTR(fan5_div, S_IRUGO, show_fan_div, NULL, 4), -}; - -static ssize_t -show_temp_label(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct w83627ehf_data *data = w83627ehf_update_device(dev); - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - return sprintf(buf, "%s\n", data->temp_label[data->temp_src[nr]]); -} + w83627ehf_write_value(data, W83627EHF_REG_FAN_MIN[channel], + data->fan_min[channel]); + mutex_unlock(&data->update_lock); -#define show_temp_reg(addr, reg) \ -static ssize_t \ -show_##reg(struct device *dev, struct device_attribute *attr, \ - char *buf) \ -{ \ - struct w83627ehf_data *data = w83627ehf_update_device(dev); \ - struct sensor_device_attribute *sensor_attr = \ - to_sensor_dev_attr(attr); \ - int nr = sensor_attr->index; \ - return sprintf(buf, "%d\n", LM75_TEMP_FROM_REG(data->reg[nr])); \ + return 0; } -show_temp_reg(reg_temp, temp); -show_temp_reg(reg_temp_over, temp_max); -show_temp_reg(reg_temp_hyst, temp_max_hyst); #define store_temp_reg(addr, reg) \ -static ssize_t \ -store_##reg(struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ +static int \ +store_##reg(struct device *dev, struct w83627ehf_data *data, int channel, \ + long val) \ { \ - struct w83627ehf_data *data = dev_get_drvdata(dev); \ - struct sensor_device_attribute *sensor_attr = \ - to_sensor_dev_attr(attr); \ - int nr = sensor_attr->index; \ - int err; \ - long val; \ - err = kstrtol(buf, 10, &val); \ - if (err < 0) \ - return err; \ mutex_lock(&data->update_lock); \ - data->reg[nr] = LM75_TEMP_TO_REG(val); \ - w83627ehf_write_temp(data, data->addr[nr], data->reg[nr]); \ + data->reg[channel] = LM75_TEMP_TO_REG(val); \ + w83627ehf_write_temp(data, data->addr[channel], data->reg[channel]); \ mutex_unlock(&data->update_lock); \ - return count; \ + return 0; \ } store_temp_reg(reg_temp_over, temp_max); store_temp_reg(reg_temp_hyst, temp_max_hyst); -static ssize_t -show_temp_offset(struct device *dev, struct device_attribute *attr, char *buf) +static int +store_temp_offset(struct device *dev, struct w83627ehf_data *data, int channel, + long val) { - struct w83627ehf_data *data = w83627ehf_update_device(dev); - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - - return sprintf(buf, "%d\n", - data->temp_offset[sensor_attr->index] * 1000); -} - -static ssize_t -store_temp_offset(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct w83627ehf_data *data = dev_get_drvdata(dev); - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - long val; - int err; - - err = kstrtol(buf, 10, &val); - if (err < 0) - return err; - val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), -128, 127); mutex_lock(&data->update_lock); - data->temp_offset[nr] = val; - w83627ehf_write_value(data, W83627EHF_REG_TEMP_OFFSET[nr], val); + data->temp_offset[channel] = val; + w83627ehf_write_value(data, W83627EHF_REG_TEMP_OFFSET[channel], val); mutex_unlock(&data->update_lock); - return count; -} - -static ssize_t -show_temp_type(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct w83627ehf_data *data = w83627ehf_update_device(dev); - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - return sprintf(buf, "%d\n", (int)data->temp_type[nr]); -} - -static struct sensor_device_attribute sda_temp_input[] = { - SENSOR_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0), - SENSOR_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1), - SENSOR_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2), - SENSOR_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 3), - SENSOR_ATTR(temp5_input, S_IRUGO, show_temp, NULL, 4), - SENSOR_ATTR(temp6_input, S_IRUGO, show_temp, NULL, 5), - SENSOR_ATTR(temp7_input, S_IRUGO, show_temp, NULL, 6), - SENSOR_ATTR(temp8_input, S_IRUGO, show_temp, NULL, 7), - SENSOR_ATTR(temp9_input, S_IRUGO, show_temp, NULL, 8), -}; - -static struct sensor_device_attribute sda_temp_label[] = { - SENSOR_ATTR(temp1_label, S_IRUGO, show_temp_label, NULL, 0), - SENSOR_ATTR(temp2_label, S_IRUGO, show_temp_label, NULL, 1), - SENSOR_ATTR(temp3_label, S_IRUGO, show_temp_label, NULL, 2), - SENSOR_ATTR(temp4_label, S_IRUGO, show_temp_label, NULL, 3), - SENSOR_ATTR(temp5_label, S_IRUGO, show_temp_label, NULL, 4), - SENSOR_ATTR(temp6_label, S_IRUGO, show_temp_label, NULL, 5), - SENSOR_ATTR(temp7_label, S_IRUGO, show_temp_label, NULL, 6), - SENSOR_ATTR(temp8_label, S_IRUGO, show_temp_label, NULL, 7), - SENSOR_ATTR(temp9_label, S_IRUGO, show_temp_label, NULL, 8), -}; - -static struct sensor_device_attribute sda_temp_max[] = { - SENSOR_ATTR(temp1_max, S_IRUGO | S_IWUSR, show_temp_max, - store_temp_max, 0), - SENSOR_ATTR(temp2_max, S_IRUGO | S_IWUSR, show_temp_max, - store_temp_max, 1), - SENSOR_ATTR(temp3_max, S_IRUGO | S_IWUSR, show_temp_max, - store_temp_max, 2), - SENSOR_ATTR(temp4_max, S_IRUGO | S_IWUSR, show_temp_max, - store_temp_max, 3), - SENSOR_ATTR(temp5_max, S_IRUGO | S_IWUSR, show_temp_max, - store_temp_max, 4), - SENSOR_ATTR(temp6_max, S_IRUGO | S_IWUSR, show_temp_max, - store_temp_max, 5), - SENSOR_ATTR(temp7_max, S_IRUGO | S_IWUSR, show_temp_max, - store_temp_max, 6), - SENSOR_ATTR(temp8_max, S_IRUGO | S_IWUSR, show_temp_max, - store_temp_max, 7), - SENSOR_ATTR(temp9_max, S_IRUGO | S_IWUSR, show_temp_max, - store_temp_max, 8), -}; - -static struct sensor_device_attribute sda_temp_max_hyst[] = { - SENSOR_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst, - store_temp_max_hyst, 0), - SENSOR_ATTR(temp2_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst, - store_temp_max_hyst, 1), - SENSOR_ATTR(temp3_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst, - store_temp_max_hyst, 2), - SENSOR_ATTR(temp4_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst, - store_temp_max_hyst, 3), - SENSOR_ATTR(temp5_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst, - store_temp_max_hyst, 4), - SENSOR_ATTR(temp6_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst, - store_temp_max_hyst, 5), - SENSOR_ATTR(temp7_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst, - store_temp_max_hyst, 6), - SENSOR_ATTR(temp8_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst, - store_temp_max_hyst, 7), - SENSOR_ATTR(temp9_max_hyst, S_IRUGO | S_IWUSR, show_temp_max_hyst, - store_temp_max_hyst, 8), -}; - -static struct sensor_device_attribute sda_temp_alarm[] = { - SENSOR_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 4), - SENSOR_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 5), - SENSOR_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 13), -}; - -static struct sensor_device_attribute sda_temp_type[] = { - SENSOR_ATTR(temp1_type, S_IRUGO, show_temp_type, NULL, 0), - SENSOR_ATTR(temp2_type, S_IRUGO, show_temp_type, NULL, 1), - SENSOR_ATTR(temp3_type, S_IRUGO, show_temp_type, NULL, 2), -}; - -static struct sensor_device_attribute sda_temp_offset[] = { - SENSOR_ATTR(temp1_offset, S_IRUGO | S_IWUSR, show_temp_offset, - store_temp_offset, 0), - SENSOR_ATTR(temp2_offset, S_IRUGO | S_IWUSR, show_temp_offset, - store_temp_offset, 1), - SENSOR_ATTR(temp3_offset, S_IRUGO | S_IWUSR, show_temp_offset, - store_temp_offset, 2), -}; - -#define show_pwm_reg(reg) \ -static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \ - char *buf) \ -{ \ - struct w83627ehf_data *data = w83627ehf_update_device(dev); \ - struct sensor_device_attribute *sensor_attr = \ - to_sensor_dev_attr(attr); \ - int nr = sensor_attr->index; \ - return sprintf(buf, "%d\n", data->reg[nr]); \ + return 0; } -show_pwm_reg(pwm_mode) -show_pwm_reg(pwm_enable) -show_pwm_reg(pwm) - -static ssize_t -store_pwm_mode(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static int +store_pwm_mode(struct device *dev, struct w83627ehf_data *data, int channel, + long val) { - struct w83627ehf_data *data = dev_get_drvdata(dev); - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev); - int nr = sensor_attr->index; - unsigned long val; - int err; u16 reg; - err = kstrtoul(buf, 10, &val); - if (err < 0) - return err; - - if (val > 1) - return -EINVAL; - - /* On NCT67766F, DC mode is only supported for pwm1 */ - if (sio_data->kind == nct6776 && nr && val != 1) + if (val < 0 || val > 1) return -EINVAL; mutex_lock(&data->update_lock); - reg = w83627ehf_read_value(data, W83627EHF_REG_PWM_ENABLE[nr]); - data->pwm_mode[nr] = val; - reg &= ~(1 << W83627EHF_PWM_MODE_SHIFT[nr]); + reg = w83627ehf_read_value(data, W83627EHF_REG_PWM_ENABLE[channel]); + data->pwm_mode[channel] = val; + reg &= ~(1 << W83627EHF_PWM_MODE_SHIFT[channel]); if (!val) - reg |= 1 << W83627EHF_PWM_MODE_SHIFT[nr]; - w83627ehf_write_value(data, W83627EHF_REG_PWM_ENABLE[nr], reg); + reg |= 1 << W83627EHF_PWM_MODE_SHIFT[channel]; + w83627ehf_write_value(data, W83627EHF_REG_PWM_ENABLE[channel], reg); mutex_unlock(&data->update_lock); - return count; + return 0; } -static ssize_t -store_pwm(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static int +store_pwm(struct device *dev, struct w83627ehf_data *data, int channel, + long val) { - struct w83627ehf_data *data = dev_get_drvdata(dev); - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - unsigned long val; - int err; - - err = kstrtoul(buf, 10, &val); - if (err < 0) - return err; - val = clamp_val(val, 0, 255); mutex_lock(&data->update_lock); - data->pwm[nr] = val; - w83627ehf_write_value(data, data->REG_PWM[nr], val); + data->pwm[channel] = val; + w83627ehf_write_value(data, W83627EHF_REG_PWM[channel], val); mutex_unlock(&data->update_lock); - return count; + return 0; } -static ssize_t -store_pwm_enable(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static int +store_pwm_enable(struct device *dev, struct w83627ehf_data *data, int channel, + long val) { - struct w83627ehf_data *data = dev_get_drvdata(dev); - struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev); - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - unsigned long val; - int err; u16 reg; - err = kstrtoul(buf, 10, &val); - if (err < 0) - return err; - - if (!val || (val > 4 && val != data->pwm_enable_orig[nr])) - return -EINVAL; - /* SmartFan III mode is not supported on NCT6776F */ - if (sio_data->kind == nct6776 && val == 4) + if (!val || val < 0 || + (val > 4 && val != data->pwm_enable_orig[channel])) return -EINVAL; mutex_lock(&data->update_lock); - data->pwm_enable[nr] = val; - if (sio_data->kind == nct6775 || sio_data->kind == nct6776) { - reg = w83627ehf_read_value(data, - NCT6775_REG_FAN_MODE[nr]); - reg &= 0x0f; - reg |= (val - 1) << 4; - w83627ehf_write_value(data, - NCT6775_REG_FAN_MODE[nr], reg); - } else { - reg = w83627ehf_read_value(data, W83627EHF_REG_PWM_ENABLE[nr]); - reg &= ~(0x03 << W83627EHF_PWM_ENABLE_SHIFT[nr]); - reg |= (val - 1) << W83627EHF_PWM_ENABLE_SHIFT[nr]; - w83627ehf_write_value(data, W83627EHF_REG_PWM_ENABLE[nr], reg); - } + data->pwm_enable[channel] = val; + reg = w83627ehf_read_value(data, + W83627EHF_REG_PWM_ENABLE[channel]); + reg &= ~(0x03 << W83627EHF_PWM_ENABLE_SHIFT[channel]); + reg |= (val - 1) << W83627EHF_PWM_ENABLE_SHIFT[channel]; + w83627ehf_write_value(data, W83627EHF_REG_PWM_ENABLE[channel], + reg); mutex_unlock(&data->update_lock); - return count; + return 0; } - #define show_tol_temp(reg) \ static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \ char *buf) \ { \ - struct w83627ehf_data *data = w83627ehf_update_device(dev); \ + struct w83627ehf_data *data = w83627ehf_update_device(dev->parent); \ struct sensor_device_attribute *sensor_attr = \ to_sensor_dev_attr(attr); \ int nr = sensor_attr->index; \ @@ -1510,7 +901,7 @@ store_target_temp(struct device *dev, struct device_attribute *attr, mutex_lock(&data->update_lock); data->target_temp[nr] = val; - w83627ehf_write_value(data, data->REG_TARGET[nr], val); + w83627ehf_write_value(data, W83627EHF_REG_TARGET[nr], val); mutex_unlock(&data->update_lock); return count; } @@ -1520,7 +911,6 @@ store_tolerance(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct w83627ehf_data *data = dev_get_drvdata(dev); - struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev); struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); int nr = sensor_attr->index; u16 reg; @@ -1535,76 +925,34 @@ store_tolerance(struct device *dev, struct device_attribute *attr, val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0, 15); mutex_lock(&data->update_lock); - if (sio_data->kind == nct6775 || sio_data->kind == nct6776) { - /* Limit tolerance further for NCT6776F */ - if (sio_data->kind == nct6776 && val > 7) - val = 7; - reg = w83627ehf_read_value(data, NCT6775_REG_FAN_MODE[nr]); + reg = w83627ehf_read_value(data, W83627EHF_REG_TOLERANCE[nr]); + if (nr == 1) + reg = (reg & 0x0f) | (val << 4); + else reg = (reg & 0xf0) | val; - w83627ehf_write_value(data, NCT6775_REG_FAN_MODE[nr], reg); - } else { - reg = w83627ehf_read_value(data, W83627EHF_REG_TOLERANCE[nr]); - if (nr == 1) - reg = (reg & 0x0f) | (val << 4); - else - reg = (reg & 0xf0) | val; - w83627ehf_write_value(data, W83627EHF_REG_TOLERANCE[nr], reg); - } + w83627ehf_write_value(data, W83627EHF_REG_TOLERANCE[nr], reg); data->tolerance[nr] = val; mutex_unlock(&data->update_lock); return count; } -static struct sensor_device_attribute sda_pwm[] = { - SENSOR_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0), - SENSOR_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1), - SENSOR_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2), - SENSOR_ATTR(pwm4, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 3), -}; - -static struct sensor_device_attribute sda_pwm_mode[] = { - SENSOR_ATTR(pwm1_mode, S_IWUSR | S_IRUGO, show_pwm_mode, - store_pwm_mode, 0), - SENSOR_ATTR(pwm2_mode, S_IWUSR | S_IRUGO, show_pwm_mode, - store_pwm_mode, 1), - SENSOR_ATTR(pwm3_mode, S_IWUSR | S_IRUGO, show_pwm_mode, - store_pwm_mode, 2), - SENSOR_ATTR(pwm4_mode, S_IWUSR | S_IRUGO, show_pwm_mode, - store_pwm_mode, 3), -}; - -static struct sensor_device_attribute sda_pwm_enable[] = { - SENSOR_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, show_pwm_enable, - store_pwm_enable, 0), - SENSOR_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, show_pwm_enable, - store_pwm_enable, 1), - SENSOR_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, show_pwm_enable, - store_pwm_enable, 2), - SENSOR_ATTR(pwm4_enable, S_IWUSR | S_IRUGO, show_pwm_enable, - store_pwm_enable, 3), -}; - -static struct sensor_device_attribute sda_target_temp[] = { - SENSOR_ATTR(pwm1_target, S_IWUSR | S_IRUGO, show_target_temp, - store_target_temp, 0), - SENSOR_ATTR(pwm2_target, S_IWUSR | S_IRUGO, show_target_temp, - store_target_temp, 1), - SENSOR_ATTR(pwm3_target, S_IWUSR | S_IRUGO, show_target_temp, - store_target_temp, 2), - SENSOR_ATTR(pwm4_target, S_IWUSR | S_IRUGO, show_target_temp, - store_target_temp, 3), -}; - -static struct sensor_device_attribute sda_tolerance[] = { - SENSOR_ATTR(pwm1_tolerance, S_IWUSR | S_IRUGO, show_tolerance, - store_tolerance, 0), - SENSOR_ATTR(pwm2_tolerance, S_IWUSR | S_IRUGO, show_tolerance, - store_tolerance, 1), - SENSOR_ATTR(pwm3_tolerance, S_IWUSR | S_IRUGO, show_tolerance, - store_tolerance, 2), - SENSOR_ATTR(pwm4_tolerance, S_IWUSR | S_IRUGO, show_tolerance, - store_tolerance, 3), -}; +static SENSOR_DEVICE_ATTR(pwm1_target, 0644, show_target_temp, + store_target_temp, 0); +static SENSOR_DEVICE_ATTR(pwm2_target, 0644, show_target_temp, + store_target_temp, 1); +static SENSOR_DEVICE_ATTR(pwm3_target, 0644, show_target_temp, + store_target_temp, 2); +static SENSOR_DEVICE_ATTR(pwm4_target, 0644, show_target_temp, + store_target_temp, 3); + +static SENSOR_DEVICE_ATTR(pwm1_tolerance, 0644, show_tolerance, + store_tolerance, 0); +static SENSOR_DEVICE_ATTR(pwm2_tolerance, 0644, show_tolerance, + store_tolerance, 1); +static SENSOR_DEVICE_ATTR(pwm3_tolerance, 0644, show_tolerance, + store_tolerance, 2); +static SENSOR_DEVICE_ATTR(pwm4_tolerance, 0644, show_tolerance, + store_tolerance, 3); /* Smart Fan registers */ @@ -1612,7 +960,7 @@ static struct sensor_device_attribute sda_tolerance[] = { static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \ char *buf) \ { \ - struct w83627ehf_data *data = w83627ehf_update_device(dev); \ + struct w83627ehf_data *data = w83627ehf_update_device(dev->parent); \ struct sensor_device_attribute *sensor_attr = \ to_sensor_dev_attr(attr); \ int nr = sensor_attr->index; \ @@ -1634,21 +982,21 @@ store_##reg(struct device *dev, struct device_attribute *attr, \ val = clamp_val(val, 1, 255); \ mutex_lock(&data->update_lock); \ data->reg[nr] = val; \ - w83627ehf_write_value(data, data->REG_##REG[nr], val); \ + w83627ehf_write_value(data, REG[nr], val); \ mutex_unlock(&data->update_lock); \ return count; \ } -fan_functions(fan_start_output, FAN_START_OUTPUT) -fan_functions(fan_stop_output, FAN_STOP_OUTPUT) -fan_functions(fan_max_output, FAN_MAX_OUTPUT) -fan_functions(fan_step_output, FAN_STEP_OUTPUT) +fan_functions(fan_start_output, W83627EHF_REG_FAN_START_OUTPUT) +fan_functions(fan_stop_output, W83627EHF_REG_FAN_STOP_OUTPUT) +fan_functions(fan_max_output, data->REG_FAN_MAX_OUTPUT) +fan_functions(fan_step_output, data->REG_FAN_STEP_OUTPUT) #define fan_time_functions(reg, REG) \ static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \ char *buf) \ { \ - struct w83627ehf_data *data = w83627ehf_update_device(dev); \ + struct w83627ehf_data *data = w83627ehf_update_device(dev->parent); \ struct sensor_device_attribute *sensor_attr = \ to_sensor_dev_attr(attr); \ int nr = sensor_attr->index; \ @@ -1673,78 +1021,61 @@ store_##reg(struct device *dev, struct device_attribute *attr, \ val = step_time_to_reg(val, data->pwm_mode[nr]); \ mutex_lock(&data->update_lock); \ data->reg[nr] = val; \ - w83627ehf_write_value(data, data->REG_##REG[nr], val); \ + w83627ehf_write_value(data, REG[nr], val); \ mutex_unlock(&data->update_lock); \ return count; \ } \ -fan_time_functions(fan_stop_time, FAN_STOP_TIME) - -static ssize_t name_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct w83627ehf_data *data = dev_get_drvdata(dev); - - return sprintf(buf, "%s\n", data->name); -} -static DEVICE_ATTR_RO(name); - -static struct sensor_device_attribute sda_sf3_arrays_fan4[] = { - SENSOR_ATTR(pwm4_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time, - store_fan_stop_time, 3), - SENSOR_ATTR(pwm4_start_output, S_IWUSR | S_IRUGO, show_fan_start_output, - store_fan_start_output, 3), - SENSOR_ATTR(pwm4_stop_output, S_IWUSR | S_IRUGO, show_fan_stop_output, - store_fan_stop_output, 3), - SENSOR_ATTR(pwm4_max_output, S_IWUSR | S_IRUGO, show_fan_max_output, - store_fan_max_output, 3), - SENSOR_ATTR(pwm4_step_output, S_IWUSR | S_IRUGO, show_fan_step_output, - store_fan_step_output, 3), -}; - -static struct sensor_device_attribute sda_sf3_arrays_fan3[] = { - SENSOR_ATTR(pwm3_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time, - store_fan_stop_time, 2), - SENSOR_ATTR(pwm3_start_output, S_IWUSR | S_IRUGO, show_fan_start_output, - store_fan_start_output, 2), - SENSOR_ATTR(pwm3_stop_output, S_IWUSR | S_IRUGO, show_fan_stop_output, - store_fan_stop_output, 2), -}; - -static struct sensor_device_attribute sda_sf3_arrays[] = { - SENSOR_ATTR(pwm1_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time, - store_fan_stop_time, 0), - SENSOR_ATTR(pwm2_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time, - store_fan_stop_time, 1), - SENSOR_ATTR(pwm1_start_output, S_IWUSR | S_IRUGO, show_fan_start_output, - store_fan_start_output, 0), - SENSOR_ATTR(pwm2_start_output, S_IWUSR | S_IRUGO, show_fan_start_output, - store_fan_start_output, 1), - SENSOR_ATTR(pwm1_stop_output, S_IWUSR | S_IRUGO, show_fan_stop_output, - store_fan_stop_output, 0), - SENSOR_ATTR(pwm2_stop_output, S_IWUSR | S_IRUGO, show_fan_stop_output, - store_fan_stop_output, 1), -}; +fan_time_functions(fan_stop_time, W83627EHF_REG_FAN_STOP_TIME) + +static SENSOR_DEVICE_ATTR(pwm4_stop_time, 0644, show_fan_stop_time, + store_fan_stop_time, 3); +static SENSOR_DEVICE_ATTR(pwm4_start_output, 0644, show_fan_start_output, + store_fan_start_output, 3); +static SENSOR_DEVICE_ATTR(pwm4_stop_output, 0644, show_fan_stop_output, + store_fan_stop_output, 3); +static SENSOR_DEVICE_ATTR(pwm4_max_output, 0644, show_fan_max_output, + store_fan_max_output, 3); +static SENSOR_DEVICE_ATTR(pwm4_step_output, 0644, show_fan_step_output, + store_fan_step_output, 3); + +static SENSOR_DEVICE_ATTR(pwm3_stop_time, 0644, show_fan_stop_time, + store_fan_stop_time, 2); +static SENSOR_DEVICE_ATTR(pwm3_start_output, 0644, show_fan_start_output, + store_fan_start_output, 2); +static SENSOR_DEVICE_ATTR(pwm3_stop_output, 0644, show_fan_stop_output, + store_fan_stop_output, 2); + +static SENSOR_DEVICE_ATTR(pwm1_stop_time, 0644, show_fan_stop_time, + store_fan_stop_time, 0); +static SENSOR_DEVICE_ATTR(pwm2_stop_time, 0644, show_fan_stop_time, + store_fan_stop_time, 1); +static SENSOR_DEVICE_ATTR(pwm1_start_output, 0644, show_fan_start_output, + store_fan_start_output, 0); +static SENSOR_DEVICE_ATTR(pwm2_start_output, 0644, show_fan_start_output, + store_fan_start_output, 1); +static SENSOR_DEVICE_ATTR(pwm1_stop_output, 0644, show_fan_stop_output, + store_fan_stop_output, 0); +static SENSOR_DEVICE_ATTR(pwm2_stop_output, 0644, show_fan_stop_output, + store_fan_stop_output, 1); /* * pwm1 and pwm3 don't support max and step settings on all chips. * Need to check support while generating/removing attribute files. */ -static struct sensor_device_attribute sda_sf3_max_step_arrays[] = { - SENSOR_ATTR(pwm1_max_output, S_IWUSR | S_IRUGO, show_fan_max_output, - store_fan_max_output, 0), - SENSOR_ATTR(pwm1_step_output, S_IWUSR | S_IRUGO, show_fan_step_output, - store_fan_step_output, 0), - SENSOR_ATTR(pwm2_max_output, S_IWUSR | S_IRUGO, show_fan_max_output, - store_fan_max_output, 1), - SENSOR_ATTR(pwm2_step_output, S_IWUSR | S_IRUGO, show_fan_step_output, - store_fan_step_output, 1), - SENSOR_ATTR(pwm3_max_output, S_IWUSR | S_IRUGO, show_fan_max_output, - store_fan_max_output, 2), - SENSOR_ATTR(pwm3_step_output, S_IWUSR | S_IRUGO, show_fan_step_output, - store_fan_step_output, 2), -}; +static SENSOR_DEVICE_ATTR(pwm1_max_output, 0644, show_fan_max_output, + store_fan_max_output, 0); +static SENSOR_DEVICE_ATTR(pwm1_step_output, 0644, show_fan_step_output, + store_fan_step_output, 0); +static SENSOR_DEVICE_ATTR(pwm2_max_output, 0644, show_fan_max_output, + store_fan_max_output, 1); +static SENSOR_DEVICE_ATTR(pwm2_step_output, 0644, show_fan_step_output, + store_fan_step_output, 1); +static SENSOR_DEVICE_ATTR(pwm3_max_output, 0644, show_fan_max_output, + store_fan_max_output, 2); +static SENSOR_DEVICE_ATTR(pwm3_step_output, 0644, show_fan_step_output, + store_fan_step_output, 2); static ssize_t cpu0_vid_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -1752,33 +1083,20 @@ cpu0_vid_show(struct device *dev, struct device_attribute *attr, char *buf) struct w83627ehf_data *data = dev_get_drvdata(dev); return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm)); } -static DEVICE_ATTR_RO(cpu0_vid); +DEVICE_ATTR_RO(cpu0_vid); /* Case open detection */ - -static ssize_t -show_caseopen(struct device *dev, struct device_attribute *attr, char *buf) +static int +clear_caseopen(struct device *dev, struct w83627ehf_data *data, int channel, + long val) { - struct w83627ehf_data *data = w83627ehf_update_device(dev); - - return sprintf(buf, "%d\n", - !!(data->caseopen & to_sensor_dev_attr_2(attr)->index)); -} - -static ssize_t -clear_caseopen(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct w83627ehf_data *data = dev_get_drvdata(dev); - unsigned long val; - u16 reg, mask; + const u16 mask = 0x80; + u16 reg; - if (kstrtoul(buf, 10, &val) || val != 0) + if (val != 0 || channel != 0) return -EINVAL; - mask = to_sensor_dev_attr_2(attr)->nr; - mutex_lock(&data->update_lock); reg = w83627ehf_read_value(data, W83627EHF_REG_CASEOPEN_CLR); w83627ehf_write_value(data, W83627EHF_REG_CASEOPEN_CLR, reg | mask); @@ -1786,85 +1104,116 @@ clear_caseopen(struct device *dev, struct device_attribute *attr, data->valid = 0; /* Force cache refresh */ mutex_unlock(&data->update_lock); - return count; + return 0; } -static struct sensor_device_attribute_2 sda_caseopen[] = { - SENSOR_ATTR_2(intrusion0_alarm, S_IWUSR | S_IRUGO, show_caseopen, - clear_caseopen, 0x80, 0x10), - SENSOR_ATTR_2(intrusion1_alarm, S_IWUSR | S_IRUGO, show_caseopen, - clear_caseopen, 0x40, 0x40), -}; - -/* - * Driver and device management - */ - -static void w83627ehf_device_remove_files(struct device *dev) +static umode_t w83627ehf_attrs_visible(struct kobject *kobj, + struct attribute *a, int n) { - /* - * some entries in the following arrays may not have been used in - * device_create_file(), but device_remove_file() will ignore them - */ - int i; + struct device *dev = container_of(kobj, struct device, kobj); struct w83627ehf_data *data = dev_get_drvdata(dev); + struct device_attribute *devattr; + struct sensor_device_attribute *sda; + + devattr = container_of(a, struct device_attribute, attr); + + /* Not sensor */ + if (devattr->show == cpu0_vid_show && data->have_vid) + return a->mode; + + sda = (struct sensor_device_attribute *)devattr; + + if (sda->index < 2 && + (devattr->show == show_fan_stop_time || + devattr->show == show_fan_start_output || + devattr->show == show_fan_stop_output)) + return a->mode; + + if (sda->index < 3 && + (devattr->show == show_fan_max_output || + devattr->show == show_fan_step_output) && + data->REG_FAN_STEP_OUTPUT && + data->REG_FAN_STEP_OUTPUT[sda->index] != 0xff) + return a->mode; + + /* if fan3 and fan4 are enabled create the files for them */ + if (sda->index == 2 && + (data->has_fan & (1 << 2)) && data->pwm_num >= 3 && + (devattr->show == show_fan_stop_time || + devattr->show == show_fan_start_output || + devattr->show == show_fan_stop_output)) + return a->mode; + + if (sda->index == 3 && + (data->has_fan & (1 << 3)) && data->pwm_num >= 4 && + (devattr->show == show_fan_stop_time || + devattr->show == show_fan_start_output || + devattr->show == show_fan_stop_output || + devattr->show == show_fan_max_output || + devattr->show == show_fan_step_output)) + return a->mode; + + if ((devattr->show == show_target_temp || + devattr->show == show_tolerance) && + (data->has_fan & (1 << sda->index)) && + sda->index < data->pwm_num) + return a->mode; - for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays); i++) - device_remove_file(dev, &sda_sf3_arrays[i].dev_attr); - for (i = 0; i < ARRAY_SIZE(sda_sf3_max_step_arrays); i++) { - struct sensor_device_attribute *attr = - &sda_sf3_max_step_arrays[i]; - if (data->REG_FAN_STEP_OUTPUT && - data->REG_FAN_STEP_OUTPUT[attr->index] != 0xff) - device_remove_file(dev, &attr->dev_attr); - } - for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan3); i++) - device_remove_file(dev, &sda_sf3_arrays_fan3[i].dev_attr); - for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++) - device_remove_file(dev, &sda_sf3_arrays_fan4[i].dev_attr); - for (i = 0; i < data->in_num; i++) { - if ((i == 6) && data->in6_skip) - continue; - device_remove_file(dev, &sda_in_input[i].dev_attr); - device_remove_file(dev, &sda_in_alarm[i].dev_attr); - device_remove_file(dev, &sda_in_min[i].dev_attr); - device_remove_file(dev, &sda_in_max[i].dev_attr); - } - for (i = 0; i < 5; i++) { - device_remove_file(dev, &sda_fan_input[i].dev_attr); - device_remove_file(dev, &sda_fan_alarm[i].dev_attr); - device_remove_file(dev, &sda_fan_div[i].dev_attr); - device_remove_file(dev, &sda_fan_min[i].dev_attr); - } - for (i = 0; i < data->pwm_num; i++) { - device_remove_file(dev, &sda_pwm[i].dev_attr); - device_remove_file(dev, &sda_pwm_mode[i].dev_attr); - device_remove_file(dev, &sda_pwm_enable[i].dev_attr); - device_remove_file(dev, &sda_target_temp[i].dev_attr); - device_remove_file(dev, &sda_tolerance[i].dev_attr); - } - for (i = 0; i < NUM_REG_TEMP; i++) { - if (!(data->have_temp & (1 << i))) - continue; - device_remove_file(dev, &sda_temp_input[i].dev_attr); - device_remove_file(dev, &sda_temp_label[i].dev_attr); - if (i == 2 && data->temp3_val_only) - continue; - device_remove_file(dev, &sda_temp_max[i].dev_attr); - device_remove_file(dev, &sda_temp_max_hyst[i].dev_attr); - if (i > 2) - continue; - device_remove_file(dev, &sda_temp_alarm[i].dev_attr); - device_remove_file(dev, &sda_temp_type[i].dev_attr); - device_remove_file(dev, &sda_temp_offset[i].dev_attr); - } + return 0; +} + +/* These groups handle non-standard attributes used in this device */ +static struct attribute *w83627ehf_attrs[] = { + + &sensor_dev_attr_pwm1_stop_time.dev_attr.attr, + &sensor_dev_attr_pwm1_start_output.dev_attr.attr, + &sensor_dev_attr_pwm1_stop_output.dev_attr.attr, + &sensor_dev_attr_pwm1_max_output.dev_attr.attr, + &sensor_dev_attr_pwm1_step_output.dev_attr.attr, + &sensor_dev_attr_pwm1_target.dev_attr.attr, + &sensor_dev_attr_pwm1_tolerance.dev_attr.attr, + + &sensor_dev_attr_pwm2_stop_time.dev_attr.attr, + &sensor_dev_attr_pwm2_start_output.dev_attr.attr, + &sensor_dev_attr_pwm2_stop_output.dev_attr.attr, + &sensor_dev_attr_pwm2_max_output.dev_attr.attr, + &sensor_dev_attr_pwm2_step_output.dev_attr.attr, + &sensor_dev_attr_pwm2_target.dev_attr.attr, + &sensor_dev_attr_pwm2_tolerance.dev_attr.attr, + + &sensor_dev_attr_pwm3_stop_time.dev_attr.attr, + &sensor_dev_attr_pwm3_start_output.dev_attr.attr, + &sensor_dev_attr_pwm3_stop_output.dev_attr.attr, + &sensor_dev_attr_pwm3_max_output.dev_attr.attr, + &sensor_dev_attr_pwm3_step_output.dev_attr.attr, + &sensor_dev_attr_pwm3_target.dev_attr.attr, + &sensor_dev_attr_pwm3_tolerance.dev_attr.attr, + + &sensor_dev_attr_pwm4_stop_time.dev_attr.attr, + &sensor_dev_attr_pwm4_start_output.dev_attr.attr, + &sensor_dev_attr_pwm4_stop_output.dev_attr.attr, + &sensor_dev_attr_pwm4_max_output.dev_attr.attr, + &sensor_dev_attr_pwm4_step_output.dev_attr.attr, + &sensor_dev_attr_pwm4_target.dev_attr.attr, + &sensor_dev_attr_pwm4_tolerance.dev_attr.attr, + + &dev_attr_cpu0_vid.attr, + NULL +}; - device_remove_file(dev, &sda_caseopen[0].dev_attr); - device_remove_file(dev, &sda_caseopen[1].dev_attr); +static const struct attribute_group w83627ehf_group = { + .attrs = w83627ehf_attrs, + .is_visible = w83627ehf_attrs_visible, +}; - device_remove_file(dev, &dev_attr_name); - device_remove_file(dev, &dev_attr_cpu0_vid); -} +static const struct attribute_group *w83627ehf_groups[] = { + &w83627ehf_group, + NULL +}; + +/* + * Driver and device management + */ /* Get the monitoring functions started */ static inline void w83627ehf_init_device(struct w83627ehf_data *data, @@ -1927,16 +1276,6 @@ static inline void w83627ehf_init_device(struct w83627ehf_data *data, } } -static void w82627ehf_swap_tempreg(struct w83627ehf_data *data, - int r1, int r2) -{ - swap(data->temp_src[r1], data->temp_src[r2]); - swap(data->reg_temp[r1], data->reg_temp[r2]); - swap(data->reg_temp_over[r1], data->reg_temp_over[r2]); - swap(data->reg_temp_hyst[r1], data->reg_temp_hyst[r2]); - swap(data->reg_temp_config[r1], data->reg_temp_config[r2]); -} - static void w83627ehf_set_temp_reg_ehf(struct w83627ehf_data *data, int n_temp) { @@ -1954,7 +1293,7 @@ static void w83627ehf_check_fan_inputs(const struct w83627ehf_sio_data *sio_data, struct w83627ehf_data *data) { - int fan3pin, fan4pin, fan4min, fan5pin, regval; + int fan3pin, fan4pin, fan5pin, regval; /* The W83627UHG is simple, only two fan inputs, no config */ if (sio_data->kind == w83627uhg) { @@ -1964,77 +1303,392 @@ w83627ehf_check_fan_inputs(const struct w83627ehf_sio_data *sio_data, } /* fan4 and fan5 share some pins with the GPIO and serial flash */ - if (sio_data->kind == nct6775) { - /* On NCT6775, fan4 shares pins with the fdc interface */ - fan3pin = 1; - fan4pin = !(superio_inb(sio_data->sioreg, 0x2A) & 0x80); - fan4min = 0; - fan5pin = 0; - } else if (sio_data->kind == nct6776) { - bool gpok = superio_inb(sio_data->sioreg, 0x27) & 0x80; - - superio_select(sio_data->sioreg, W83627EHF_LD_HWM); - regval = superio_inb(sio_data->sioreg, SIO_REG_ENABLE); - - if (regval & 0x80) - fan3pin = gpok; - else - fan3pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x40); - - if (regval & 0x40) - fan4pin = gpok; - else - fan4pin = !!(superio_inb(sio_data->sioreg, 0x1C) & 0x01); - - if (regval & 0x20) - fan5pin = gpok; - else - fan5pin = !!(superio_inb(sio_data->sioreg, 0x1C) & 0x02); - - fan4min = fan4pin; - } else if (sio_data->kind == w83667hg || sio_data->kind == w83667hg_b) { + if (sio_data->kind == w83667hg || sio_data->kind == w83667hg_b) { fan3pin = 1; fan4pin = superio_inb(sio_data->sioreg, 0x27) & 0x40; fan5pin = superio_inb(sio_data->sioreg, 0x27) & 0x20; - fan4min = fan4pin; } else { fan3pin = 1; fan4pin = !(superio_inb(sio_data->sioreg, 0x29) & 0x06); fan5pin = !(superio_inb(sio_data->sioreg, 0x24) & 0x02); - fan4min = fan4pin; } data->has_fan = data->has_fan_min = 0x03; /* fan1 and fan2 */ data->has_fan |= (fan3pin << 2); data->has_fan_min |= (fan3pin << 2); - if (sio_data->kind == nct6775 || sio_data->kind == nct6776) { - /* - * NCT6775F and NCT6776F don't have the W83627EHF_REG_FANDIV1 - * register - */ - data->has_fan |= (fan4pin << 3) | (fan5pin << 4); - data->has_fan_min |= (fan4min << 3) | (fan5pin << 4); - } else { - /* - * It looks like fan4 and fan5 pins can be alternatively used - * as fan on/off switches, but fan5 control is write only :/ - * We assume that if the serial interface is disabled, designers - * connected fan5 as input unless they are emitting log 1, which - * is not the default. - */ - regval = w83627ehf_read_value(data, W83627EHF_REG_FANDIV1); - if ((regval & (1 << 2)) && fan4pin) { - data->has_fan |= (1 << 3); - data->has_fan_min |= (1 << 3); + /* + * It looks like fan4 and fan5 pins can be alternatively used + * as fan on/off switches, but fan5 control is write only :/ + * We assume that if the serial interface is disabled, designers + * connected fan5 as input unless they are emitting log 1, which + * is not the default. + */ + regval = w83627ehf_read_value(data, W83627EHF_REG_FANDIV1); + if ((regval & (1 << 2)) && fan4pin) { + data->has_fan |= (1 << 3); + data->has_fan_min |= (1 << 3); + } + if (!(regval & (1 << 1)) && fan5pin) { + data->has_fan |= (1 << 4); + data->has_fan_min |= (1 << 4); + } +} + +static umode_t +w83627ehf_is_visible(const void *drvdata, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + const struct w83627ehf_data *data = drvdata; + + switch (type) { + case hwmon_temp: + /* channel 0.., name 1.. */ + if (!(data->have_temp & (1 << channel))) + return 0; + if (attr == hwmon_temp_input || attr == hwmon_temp_label) + return 0444; + if (channel == 2 && data->temp3_val_only) + return 0; + if (attr == hwmon_temp_max) { + if (data->reg_temp_over[channel]) + return 0644; + else + return 0; + } + if (attr == hwmon_temp_max_hyst) { + if (data->reg_temp_hyst[channel]) + return 0644; + else + return 0; + } + if (channel > 2) + return 0; + if (attr == hwmon_temp_alarm || attr == hwmon_temp_type) + return 0444; + if (attr == hwmon_temp_offset) { + if (data->have_temp_offset & (1 << channel)) + return 0644; + else + return 0; + } + break; + + case hwmon_fan: + /* channel 0.., name 1.. */ + if (!(data->has_fan & (1 << channel))) + return 0; + if (attr == hwmon_fan_input || attr == hwmon_fan_alarm) + return 0444; + if (attr == hwmon_fan_div) { + return 0444; } - if (!(regval & (1 << 1)) && fan5pin) { - data->has_fan |= (1 << 4); - data->has_fan_min |= (1 << 4); + if (attr == hwmon_fan_min) { + if (data->has_fan_min & (1 << channel)) + return 0644; + else + return 0; } + break; + + case hwmon_in: + /* channel 0.., name 0.. */ + if (channel >= data->in_num) + return 0; + if (channel == 6 && data->in6_skip) + return 0; + if (attr == hwmon_in_alarm || attr == hwmon_in_input) + return 0444; + if (attr == hwmon_in_min || attr == hwmon_in_max) + return 0644; + break; + + case hwmon_pwm: + /* channel 0.., name 1.. */ + if (!(data->has_fan & (1 << channel)) || + channel >= data->pwm_num) + return 0; + if (attr == hwmon_pwm_mode || attr == hwmon_pwm_enable || + attr == hwmon_pwm_input) + return 0644; + break; + + case hwmon_intrusion: + return 0644; + + default: /* Shouldn't happen */ + return 0; } + + return 0; /* Shouldn't happen */ } +static int +w83627ehf_do_read_temp(struct w83627ehf_data *data, u32 attr, + int channel, long *val) +{ + switch (attr) { + case hwmon_temp_input: + *val = LM75_TEMP_FROM_REG(data->temp[channel]); + return 0; + case hwmon_temp_max: + *val = LM75_TEMP_FROM_REG(data->temp_max[channel]); + return 0; + case hwmon_temp_max_hyst: + *val = LM75_TEMP_FROM_REG(data->temp_max_hyst[channel]); + return 0; + case hwmon_temp_offset: + *val = data->temp_offset[channel] * 1000; + return 0; + case hwmon_temp_type: + *val = (int)data->temp_type[channel]; + return 0; + case hwmon_temp_alarm: + if (channel < 3) { + int bit[] = { 4, 5, 13 }; + *val = (data->alarms >> bit[channel]) & 1; + return 0; + } + break; + + default: + break; + } + + return -EOPNOTSUPP; +} + +static int +w83627ehf_do_read_in(struct w83627ehf_data *data, u32 attr, + int channel, long *val) +{ + switch (attr) { + case hwmon_in_input: + *val = in_from_reg(data->in[channel], channel, data->scale_in); + return 0; + case hwmon_in_min: + *val = in_from_reg(data->in_min[channel], channel, + data->scale_in); + return 0; + case hwmon_in_max: + *val = in_from_reg(data->in_max[channel], channel, + data->scale_in); + return 0; + case hwmon_in_alarm: + if (channel < 10) { + int bit[] = { 0, 1, 2, 3, 8, 21, 20, 16, 17, 19 }; + *val = (data->alarms >> bit[channel]) & 1; + return 0; + } + break; + default: + break; + } + return -EOPNOTSUPP; +} + +static int +w83627ehf_do_read_fan(struct w83627ehf_data *data, u32 attr, + int channel, long *val) +{ + switch (attr) { + case hwmon_fan_input: + *val = data->rpm[channel]; + return 0; + case hwmon_fan_min: + *val = fan_from_reg8(data->fan_min[channel], + data->fan_div[channel]); + return 0; + case hwmon_fan_div: + *val = div_from_reg(data->fan_div[channel]); + return 0; + case hwmon_fan_alarm: + if (channel < 5) { + int bit[] = { 6, 7, 11, 10, 23 }; + *val = (data->alarms >> bit[channel]) & 1; + return 0; + } + break; + default: + break; + } + return -EOPNOTSUPP; +} + +static int +w83627ehf_do_read_pwm(struct w83627ehf_data *data, u32 attr, + int channel, long *val) +{ + switch (attr) { + case hwmon_pwm_input: + *val = data->pwm[channel]; + return 0; + case hwmon_pwm_enable: + *val = data->pwm_enable[channel]; + return 0; + case hwmon_pwm_mode: + *val = data->pwm_enable[channel]; + return 0; + default: + break; + } + return -EOPNOTSUPP; +} + +static int +w83627ehf_do_read_intrusion(struct w83627ehf_data *data, u32 attr, + int channel, long *val) +{ + if (attr != hwmon_intrusion_alarm || channel != 0) + return -EOPNOTSUPP; /* shouldn't happen */ + + *val = !!(data->caseopen & 0x10); + return 0; +} + +static int +w83627ehf_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct w83627ehf_data *data = w83627ehf_update_device(dev->parent); + + switch (type) { + case hwmon_fan: + return w83627ehf_do_read_fan(data, attr, channel, val); + + case hwmon_in: + return w83627ehf_do_read_in(data, attr, channel, val); + + case hwmon_pwm: + return w83627ehf_do_read_pwm(data, attr, channel, val); + + case hwmon_temp: + return w83627ehf_do_read_temp(data, attr, channel, val); + + case hwmon_intrusion: + return w83627ehf_do_read_intrusion(data, attr, channel, val); + + default: + break; + } + + return -EOPNOTSUPP; +} + +static int +w83627ehf_read_string(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) +{ + struct w83627ehf_data *data = dev_get_drvdata(dev); + + switch (type) { + case hwmon_temp: + if (attr == hwmon_temp_label) { + *str = data->temp_label[data->temp_src[channel]]; + return 0; + } + break; + + default: + break; + } + /* Nothing else should be read as a string */ + return -EOPNOTSUPP; +} + +static int +w83627ehf_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct w83627ehf_data *data = dev_get_drvdata(dev); + + if (type == hwmon_in && attr == hwmon_in_min) + return store_in_min(dev, data, channel, val); + if (type == hwmon_in && attr == hwmon_in_max) + return store_in_max(dev, data, channel, val); + + if (type == hwmon_fan && attr == hwmon_fan_min) + return store_fan_min(dev, data, channel, val); + + if (type == hwmon_temp && attr == hwmon_temp_max) + return store_temp_max(dev, data, channel, val); + if (type == hwmon_temp && attr == hwmon_temp_max_hyst) + return store_temp_max_hyst(dev, data, channel, val); + if (type == hwmon_temp && attr == hwmon_temp_offset) + return store_temp_offset(dev, data, channel, val); + + if (type == hwmon_pwm && attr == hwmon_pwm_mode) + return store_pwm_mode(dev, data, channel, val); + if (type == hwmon_pwm && attr == hwmon_pwm_enable) + return store_pwm_enable(dev, data, channel, val); + if (type == hwmon_pwm && attr == hwmon_pwm_input) + return store_pwm(dev, data, channel, val); + + if (type == hwmon_intrusion && attr == hwmon_intrusion_alarm) + return clear_caseopen(dev, data, channel, val); + + return -EOPNOTSUPP; +} + +static const struct hwmon_ops w83627ehf_ops = { + .is_visible = w83627ehf_is_visible, + .read = w83627ehf_read, + .read_string = w83627ehf_read_string, + .write = w83627ehf_write, +}; + +static const struct hwmon_channel_info *w83627ehf_info[] = { + HWMON_CHANNEL_INFO(fan, + HWMON_F_ALARM | HWMON_F_DIV | HWMON_F_INPUT | HWMON_F_MIN, + HWMON_F_ALARM | HWMON_F_DIV | HWMON_F_INPUT | HWMON_F_MIN, + HWMON_F_ALARM | HWMON_F_DIV | HWMON_F_INPUT | HWMON_F_MIN, + HWMON_F_ALARM | HWMON_F_DIV | HWMON_F_INPUT | HWMON_F_MIN, + HWMON_F_ALARM | HWMON_F_DIV | HWMON_F_INPUT | HWMON_F_MIN), + HWMON_CHANNEL_INFO(in, + HWMON_I_ALARM | HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_MIN, + HWMON_I_ALARM | HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_MIN, + HWMON_I_ALARM | HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_MIN, + HWMON_I_ALARM | HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_MIN, + HWMON_I_ALARM | HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_MIN, + HWMON_I_ALARM | HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_MIN, + HWMON_I_ALARM | HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_MIN, + HWMON_I_ALARM | HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_MIN, + HWMON_I_ALARM | HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_MIN, + HWMON_I_ALARM | HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_MIN), + HWMON_CHANNEL_INFO(pwm, + HWMON_PWM_ENABLE | HWMON_PWM_INPUT | HWMON_PWM_MODE, + HWMON_PWM_ENABLE | HWMON_PWM_INPUT | HWMON_PWM_MODE, + HWMON_PWM_ENABLE | HWMON_PWM_INPUT | HWMON_PWM_MODE, + HWMON_PWM_ENABLE | HWMON_PWM_INPUT | HWMON_PWM_MODE), + HWMON_CHANNEL_INFO(temp, + HWMON_T_ALARM | HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_MAX | + HWMON_T_MAX_HYST | HWMON_T_OFFSET | HWMON_T_TYPE, + HWMON_T_ALARM | HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_MAX | + HWMON_T_MAX_HYST | HWMON_T_OFFSET | HWMON_T_TYPE, + HWMON_T_ALARM | HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_MAX | + HWMON_T_MAX_HYST | HWMON_T_OFFSET | HWMON_T_TYPE, + HWMON_T_ALARM | HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_MAX | + HWMON_T_MAX_HYST | HWMON_T_OFFSET | HWMON_T_TYPE, + HWMON_T_ALARM | HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_MAX | + HWMON_T_MAX_HYST | HWMON_T_OFFSET | HWMON_T_TYPE, + HWMON_T_ALARM | HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_MAX | + HWMON_T_MAX_HYST | HWMON_T_OFFSET | HWMON_T_TYPE, + HWMON_T_ALARM | HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_MAX | + HWMON_T_MAX_HYST | HWMON_T_OFFSET | HWMON_T_TYPE, + HWMON_T_ALARM | HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_MAX | + HWMON_T_MAX_HYST | HWMON_T_OFFSET | HWMON_T_TYPE, + HWMON_T_ALARM | HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_MAX | + HWMON_T_MAX_HYST | HWMON_T_OFFSET | HWMON_T_TYPE), + HWMON_CHANNEL_INFO(intrusion, + HWMON_INTRUSION_ALARM), + NULL +}; + +static const struct hwmon_chip_info w83627ehf_chip_info = { + .ops = &w83627ehf_ops, + .info = w83627ehf_info, +}; + static int w83627ehf_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -2043,6 +1697,7 @@ static int w83627ehf_probe(struct platform_device *pdev) struct resource *res; u8 en_vrm10; int i, err = 0; + struct device *hwmon_dev; res = platform_get_resource(pdev, IORESOURCE_IO, 0); if (!request_region(res->start, IOREGION_LENGTH, DRVNAME)) { @@ -2069,15 +1724,13 @@ static int w83627ehf_probe(struct platform_device *pdev) /* 627EHG and 627EHF have 10 voltage inputs; 627DHG and 667HG have 9 */ data->in_num = (sio_data->kind == w83627ehf) ? 10 : 9; - /* 667HG, NCT6775F, and NCT6776F have 3 pwms, and 627UHG has only 2 */ + /* 667HG has 3 pwms, and 627UHG has only 2 */ switch (sio_data->kind) { default: data->pwm_num = 4; break; case w83667hg: case w83667hg_b: - case nct6775: - case nct6776: data->pwm_num = 3; break; case w83627uhg: @@ -2089,83 +1742,7 @@ static int w83627ehf_probe(struct platform_device *pdev) data->have_temp = 0x07; /* Deal with temperature register setup first. */ - if (sio_data->kind == nct6775 || sio_data->kind == nct6776) { - int mask = 0; - - /* - * Display temperature sensor output only if it monitors - * a source other than one already reported. Always display - * first three temperature registers, though. - */ - for (i = 0; i < NUM_REG_TEMP; i++) { - u8 src; - - data->reg_temp[i] = NCT6775_REG_TEMP[i]; - data->reg_temp_over[i] = NCT6775_REG_TEMP_OVER[i]; - data->reg_temp_hyst[i] = NCT6775_REG_TEMP_HYST[i]; - data->reg_temp_config[i] = NCT6775_REG_TEMP_CONFIG[i]; - - src = w83627ehf_read_value(data, - NCT6775_REG_TEMP_SOURCE[i]); - src &= 0x1f; - if (src && !(mask & (1 << src))) { - data->have_temp |= 1 << i; - mask |= 1 << src; - } - - data->temp_src[i] = src; - - /* - * Now do some register swapping if index 0..2 don't - * point to SYSTIN(1), CPUIN(2), and AUXIN(3). - * Idea is to have the first three attributes - * report SYSTIN, CPUIN, and AUXIN if possible - * without overriding the basic system configuration. - */ - if (i > 0 && data->temp_src[0] != 1 - && data->temp_src[i] == 1) - w82627ehf_swap_tempreg(data, 0, i); - if (i > 1 && data->temp_src[1] != 2 - && data->temp_src[i] == 2) - w82627ehf_swap_tempreg(data, 1, i); - if (i > 2 && data->temp_src[2] != 3 - && data->temp_src[i] == 3) - w82627ehf_swap_tempreg(data, 2, i); - } - if (sio_data->kind == nct6776) { - /* - * On NCT6776, AUXTIN and VIN3 pins are shared. - * Only way to detect it is to check if AUXTIN is used - * as a temperature source, and if that source is - * enabled. - * - * If that is the case, disable in6, which reports VIN3. - * Otherwise disable temp3. - */ - if (data->temp_src[2] == 3) { - u8 reg; - - if (data->reg_temp_config[2]) - reg = w83627ehf_read_value(data, - data->reg_temp_config[2]); - else - reg = 0; /* Assume AUXTIN is used */ - - if (reg & 0x01) - data->have_temp &= ~(1 << 2); - else - data->in6_skip = 1; - } - data->temp_label = nct6776_temp_label; - } else { - data->temp_label = nct6775_temp_label; - } - data->have_temp_offset = data->have_temp & 0x07; - for (i = 0; i < 3; i++) { - if (data->temp_src[i] > 3) - data->have_temp_offset &= ~(1 << i); - } - } else if (sio_data->kind == w83667hg_b) { + if (sio_data->kind == w83667hg_b) { u8 reg; w83627ehf_set_temp_reg_ehf(data, 4); @@ -2275,56 +1852,12 @@ static int w83627ehf_probe(struct platform_device *pdev) data->have_temp_offset = data->have_temp & 0x07; } - if (sio_data->kind == nct6775) { - data->has_fan_div = true; - data->fan_from_reg = fan_from_reg16; - data->fan_from_reg_min = fan_from_reg8; - data->REG_PWM = NCT6775_REG_PWM; - data->REG_TARGET = NCT6775_REG_TARGET; - data->REG_FAN = NCT6775_REG_FAN; - data->REG_FAN_MIN = W83627EHF_REG_FAN_MIN; - data->REG_FAN_START_OUTPUT = NCT6775_REG_FAN_START_OUTPUT; - data->REG_FAN_STOP_OUTPUT = NCT6775_REG_FAN_STOP_OUTPUT; - data->REG_FAN_STOP_TIME = NCT6775_REG_FAN_STOP_TIME; - data->REG_FAN_MAX_OUTPUT = NCT6775_REG_FAN_MAX_OUTPUT; - data->REG_FAN_STEP_OUTPUT = NCT6775_REG_FAN_STEP_OUTPUT; - } else if (sio_data->kind == nct6776) { - data->has_fan_div = false; - data->fan_from_reg = fan_from_reg13; - data->fan_from_reg_min = fan_from_reg13; - data->REG_PWM = NCT6775_REG_PWM; - data->REG_TARGET = NCT6775_REG_TARGET; - data->REG_FAN = NCT6775_REG_FAN; - data->REG_FAN_MIN = NCT6776_REG_FAN_MIN; - data->REG_FAN_START_OUTPUT = NCT6775_REG_FAN_START_OUTPUT; - data->REG_FAN_STOP_OUTPUT = NCT6775_REG_FAN_STOP_OUTPUT; - data->REG_FAN_STOP_TIME = NCT6775_REG_FAN_STOP_TIME; - } else if (sio_data->kind == w83667hg_b) { - data->has_fan_div = true; - data->fan_from_reg = fan_from_reg8; - data->fan_from_reg_min = fan_from_reg8; - data->REG_PWM = W83627EHF_REG_PWM; - data->REG_TARGET = W83627EHF_REG_TARGET; - data->REG_FAN = W83627EHF_REG_FAN; - data->REG_FAN_MIN = W83627EHF_REG_FAN_MIN; - data->REG_FAN_START_OUTPUT = W83627EHF_REG_FAN_START_OUTPUT; - data->REG_FAN_STOP_OUTPUT = W83627EHF_REG_FAN_STOP_OUTPUT; - data->REG_FAN_STOP_TIME = W83627EHF_REG_FAN_STOP_TIME; + if (sio_data->kind == w83667hg_b) { data->REG_FAN_MAX_OUTPUT = W83627EHF_REG_FAN_MAX_OUTPUT_W83667_B; data->REG_FAN_STEP_OUTPUT = W83627EHF_REG_FAN_STEP_OUTPUT_W83667_B; } else { - data->has_fan_div = true; - data->fan_from_reg = fan_from_reg8; - data->fan_from_reg_min = fan_from_reg8; - data->REG_PWM = W83627EHF_REG_PWM; - data->REG_TARGET = W83627EHF_REG_TARGET; - data->REG_FAN = W83627EHF_REG_FAN; - data->REG_FAN_MIN = W83627EHF_REG_FAN_MIN; - data->REG_FAN_START_OUTPUT = W83627EHF_REG_FAN_START_OUTPUT; - data->REG_FAN_STOP_OUTPUT = W83627EHF_REG_FAN_STOP_OUTPUT; - data->REG_FAN_STOP_TIME = W83627EHF_REG_FAN_STOP_TIME; data->REG_FAN_MAX_OUTPUT = W83627EHF_REG_FAN_MAX_OUTPUT_COMMON; data->REG_FAN_STEP_OUTPUT = @@ -2347,8 +1880,7 @@ static int w83627ehf_probe(struct platform_device *pdev) goto exit_release; /* Read VID value */ - if (sio_data->kind == w83667hg || sio_data->kind == w83667hg_b || - sio_data->kind == nct6775 || sio_data->kind == nct6776) { + if (sio_data->kind == w83667hg || sio_data->kind == w83667hg_b) { /* * W83667HG has different pins for VID input and output, so * we can get the VID input values directly at logical device D @@ -2356,11 +1888,7 @@ static int w83627ehf_probe(struct platform_device *pdev) */ superio_select(sio_data->sioreg, W83667HG_LD_VID); data->vid = superio_inb(sio_data->sioreg, 0xe3); - err = device_create_file(dev, &dev_attr_cpu0_vid); - if (err) { - superio_exit(sio_data->sioreg); - goto exit_release; - } + data->have_vid = true; } else if (sio_data->kind != w83627uhg) { superio_select(sio_data->sioreg, W83627EHF_LD_HWM); if (superio_inb(sio_data->sioreg, SIO_REG_VID_CTRL) & 0x80) { @@ -2394,190 +1922,33 @@ static int w83627ehf_probe(struct platform_device *pdev) SIO_REG_VID_DATA); if (sio_data->kind == w83627ehf) /* 6 VID pins only */ data->vid &= 0x3f; - - err = device_create_file(dev, &dev_attr_cpu0_vid); - if (err) { - superio_exit(sio_data->sioreg); - goto exit_release; - } + data->have_vid = true; } else { dev_info(dev, "VID pins in output mode, CPU VID not available\n"); } } - if (fan_debounce && - (sio_data->kind == nct6775 || sio_data->kind == nct6776)) { - u8 tmp; - - superio_select(sio_data->sioreg, W83627EHF_LD_HWM); - tmp = superio_inb(sio_data->sioreg, NCT6775_REG_FAN_DEBOUNCE); - if (sio_data->kind == nct6776) - superio_outb(sio_data->sioreg, NCT6775_REG_FAN_DEBOUNCE, - 0x3e | tmp); - else - superio_outb(sio_data->sioreg, NCT6775_REG_FAN_DEBOUNCE, - 0x1e | tmp); - pr_info("Enabled fan debounce for chip %s\n", data->name); - } - w83627ehf_check_fan_inputs(sio_data, data); superio_exit(sio_data->sioreg); /* Read fan clock dividers immediately */ - w83627ehf_update_fan_div_common(dev, data); + w83627ehf_update_fan_div(data); /* Read pwm data to save original values */ - w83627ehf_update_pwm_common(dev, data); + w83627ehf_update_pwm(data); for (i = 0; i < data->pwm_num; i++) data->pwm_enable_orig[i] = data->pwm_enable[i]; - /* Register sysfs hooks */ - for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays); i++) { - err = device_create_file(dev, &sda_sf3_arrays[i].dev_attr); - if (err) - goto exit_remove; - } - - for (i = 0; i < ARRAY_SIZE(sda_sf3_max_step_arrays); i++) { - struct sensor_device_attribute *attr = - &sda_sf3_max_step_arrays[i]; - if (data->REG_FAN_STEP_OUTPUT && - data->REG_FAN_STEP_OUTPUT[attr->index] != 0xff) { - err = device_create_file(dev, &attr->dev_attr); - if (err) - goto exit_remove; - } - } - /* if fan3 and fan4 are enabled create the sf3 files for them */ - if ((data->has_fan & (1 << 2)) && data->pwm_num >= 3) - for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan3); i++) { - err = device_create_file(dev, - &sda_sf3_arrays_fan3[i].dev_attr); - if (err) - goto exit_remove; - } - if ((data->has_fan & (1 << 3)) && data->pwm_num >= 4) - for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++) { - err = device_create_file(dev, - &sda_sf3_arrays_fan4[i].dev_attr); - if (err) - goto exit_remove; - } - - for (i = 0; i < data->in_num; i++) { - if ((i == 6) && data->in6_skip) - continue; - if ((err = device_create_file(dev, &sda_in_input[i].dev_attr)) - || (err = device_create_file(dev, - &sda_in_alarm[i].dev_attr)) - || (err = device_create_file(dev, - &sda_in_min[i].dev_attr)) - || (err = device_create_file(dev, - &sda_in_max[i].dev_attr))) - goto exit_remove; - } - - for (i = 0; i < 5; i++) { - if (data->has_fan & (1 << i)) { - if ((err = device_create_file(dev, - &sda_fan_input[i].dev_attr)) - || (err = device_create_file(dev, - &sda_fan_alarm[i].dev_attr))) - goto exit_remove; - if (sio_data->kind != nct6776) { - err = device_create_file(dev, - &sda_fan_div[i].dev_attr); - if (err) - goto exit_remove; - } - if (data->has_fan_min & (1 << i)) { - err = device_create_file(dev, - &sda_fan_min[i].dev_attr); - if (err) - goto exit_remove; - } - if (i < data->pwm_num && - ((err = device_create_file(dev, - &sda_pwm[i].dev_attr)) - || (err = device_create_file(dev, - &sda_pwm_mode[i].dev_attr)) - || (err = device_create_file(dev, - &sda_pwm_enable[i].dev_attr)) - || (err = device_create_file(dev, - &sda_target_temp[i].dev_attr)) - || (err = device_create_file(dev, - &sda_tolerance[i].dev_attr)))) - goto exit_remove; - } - } + hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, + data->name, + data, + &w83627ehf_chip_info, + w83627ehf_groups); - for (i = 0; i < NUM_REG_TEMP; i++) { - if (!(data->have_temp & (1 << i))) - continue; - err = device_create_file(dev, &sda_temp_input[i].dev_attr); - if (err) - goto exit_remove; - if (data->temp_label) { - err = device_create_file(dev, - &sda_temp_label[i].dev_attr); - if (err) - goto exit_remove; - } - if (i == 2 && data->temp3_val_only) - continue; - if (data->reg_temp_over[i]) { - err = device_create_file(dev, - &sda_temp_max[i].dev_attr); - if (err) - goto exit_remove; - } - if (data->reg_temp_hyst[i]) { - err = device_create_file(dev, - &sda_temp_max_hyst[i].dev_attr); - if (err) - goto exit_remove; - } - if (i > 2) - continue; - if ((err = device_create_file(dev, - &sda_temp_alarm[i].dev_attr)) - || (err = device_create_file(dev, - &sda_temp_type[i].dev_attr))) - goto exit_remove; - if (data->have_temp_offset & (1 << i)) { - err = device_create_file(dev, - &sda_temp_offset[i].dev_attr); - if (err) - goto exit_remove; - } - } - - err = device_create_file(dev, &sda_caseopen[0].dev_attr); - if (err) - goto exit_remove; - - if (sio_data->kind == nct6776) { - err = device_create_file(dev, &sda_caseopen[1].dev_attr); - if (err) - goto exit_remove; - } - - err = device_create_file(dev, &dev_attr_name); - if (err) - goto exit_remove; - - data->hwmon_dev = hwmon_device_register(dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto exit_remove; - } + return PTR_ERR_OR_ZERO(hwmon_dev); - return 0; - -exit_remove: - w83627ehf_device_remove_files(dev); exit_release: release_region(res->start, IOREGION_LENGTH); exit: @@ -2588,8 +1959,6 @@ static int w83627ehf_remove(struct platform_device *pdev) { struct w83627ehf_data *data = platform_get_drvdata(pdev); - hwmon_device_unregister(data->hwmon_dev); - w83627ehf_device_remove_files(&pdev->dev); release_region(data->addr, IOREGION_LENGTH); return 0; @@ -2599,14 +1968,9 @@ static int w83627ehf_remove(struct platform_device *pdev) static int w83627ehf_suspend(struct device *dev) { struct w83627ehf_data *data = w83627ehf_update_device(dev); - struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev); mutex_lock(&data->update_lock); data->vbat = w83627ehf_read_value(data, W83627EHF_REG_VBAT); - if (sio_data->kind == nct6775) { - data->fandiv1 = w83627ehf_read_value(data, NCT6775_REG_FANDIV1); - data->fandiv2 = w83627ehf_read_value(data, NCT6775_REG_FANDIV2); - } mutex_unlock(&data->update_lock); return 0; @@ -2615,7 +1979,6 @@ static int w83627ehf_suspend(struct device *dev) static int w83627ehf_resume(struct device *dev) { struct w83627ehf_data *data = dev_get_drvdata(dev); - struct w83627ehf_sio_data *sio_data = dev_get_platdata(dev); int i; mutex_lock(&data->update_lock); @@ -2636,7 +1999,7 @@ static int w83627ehf_resume(struct device *dev) if (!(data->has_fan_min & (1 << i))) continue; - w83627ehf_write_value(data, data->REG_FAN_MIN[i], + w83627ehf_write_value(data, W83627EHF_REG_FAN_MIN[i], data->fan_min[i]); } @@ -2660,10 +2023,6 @@ static int w83627ehf_resume(struct device *dev) /* Restore other settings */ w83627ehf_write_value(data, W83627EHF_REG_VBAT, data->vbat); - if (sio_data->kind == nct6775) { - w83627ehf_write_value(data, NCT6775_REG_FANDIV1, data->fandiv1); - w83627ehf_write_value(data, NCT6775_REG_FANDIV2, data->fandiv2); - } /* Force re-reading all values */ data->valid = 0; @@ -2704,8 +2063,6 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr, static const char sio_name_W83627UHG[] __initconst = "W83627UHG"; static const char sio_name_W83667HG[] __initconst = "W83667HG"; static const char sio_name_W83667HG_B[] __initconst = "W83667HG-B"; - static const char sio_name_NCT6775[] __initconst = "NCT6775F"; - static const char sio_name_NCT6776[] __initconst = "NCT6776F"; u16 val; const char *sio_name; @@ -2749,14 +2106,6 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr, sio_data->kind = w83667hg_b; sio_name = sio_name_W83667HG_B; break; - case SIO_NCT6775_ID: - sio_data->kind = nct6775; - sio_name = sio_name_NCT6775; - break; - case SIO_NCT6776_ID: - sio_data->kind = nct6776; - sio_name = sio_name_NCT6776; - break; default: if (val != 0xffff) pr_debug("unsupported chip ID: 0x%04x\n", val); diff --git a/include/linux/hwmon.h b/include/linux/hwmon.h index 72579168189d..5e609f25878c 100644 --- a/include/linux/hwmon.h +++ b/include/linux/hwmon.h @@ -27,6 +27,7 @@ enum hwmon_sensor_types { hwmon_humidity, hwmon_fan, hwmon_pwm, + hwmon_intrusion, hwmon_max, }; @@ -59,7 +60,8 @@ enum hwmon_chip_attributes { #define HWMON_C_TEMP_SAMPLES BIT(hwmon_chip_temp_samples) enum hwmon_temp_attributes { - hwmon_temp_input = 0, + hwmon_temp_enable, + hwmon_temp_input, hwmon_temp_type, hwmon_temp_lcrit, hwmon_temp_lcrit_hyst, @@ -85,6 +87,7 @@ enum hwmon_temp_attributes { hwmon_temp_reset_history, }; +#define HWMON_T_ENABLE BIT(hwmon_temp_enable) #define HWMON_T_INPUT BIT(hwmon_temp_input) #define HWMON_T_TYPE BIT(hwmon_temp_type) #define HWMON_T_LCRIT BIT(hwmon_temp_lcrit) @@ -111,6 +114,7 @@ enum hwmon_temp_attributes { #define HWMON_T_RESET_HISTORY BIT(hwmon_temp_reset_history) enum hwmon_in_attributes { + hwmon_in_enable, hwmon_in_input, hwmon_in_min, hwmon_in_max, @@ -126,9 +130,9 @@ enum hwmon_in_attributes { hwmon_in_max_alarm, hwmon_in_lcrit_alarm, hwmon_in_crit_alarm, - hwmon_in_enable, }; +#define HWMON_I_ENABLE BIT(hwmon_in_enable) #define HWMON_I_INPUT BIT(hwmon_in_input) #define HWMON_I_MIN BIT(hwmon_in_min) #define HWMON_I_MAX BIT(hwmon_in_max) @@ -144,9 +148,9 @@ enum hwmon_in_attributes { #define HWMON_I_MAX_ALARM BIT(hwmon_in_max_alarm) #define HWMON_I_LCRIT_ALARM BIT(hwmon_in_lcrit_alarm) #define HWMON_I_CRIT_ALARM BIT(hwmon_in_crit_alarm) -#define HWMON_I_ENABLE BIT(hwmon_in_enable) enum hwmon_curr_attributes { + hwmon_curr_enable, hwmon_curr_input, hwmon_curr_min, hwmon_curr_max, @@ -164,6 +168,7 @@ enum hwmon_curr_attributes { hwmon_curr_crit_alarm, }; +#define HWMON_C_ENABLE BIT(hwmon_curr_enable) #define HWMON_C_INPUT BIT(hwmon_curr_input) #define HWMON_C_MIN BIT(hwmon_curr_min) #define HWMON_C_MAX BIT(hwmon_curr_max) @@ -181,6 +186,7 @@ enum hwmon_curr_attributes { #define HWMON_C_CRIT_ALARM BIT(hwmon_curr_crit_alarm) enum hwmon_power_attributes { + hwmon_power_enable, hwmon_power_average, hwmon_power_average_interval, hwmon_power_average_interval_max, @@ -211,6 +217,7 @@ enum hwmon_power_attributes { hwmon_power_crit_alarm, }; +#define HWMON_P_ENABLE BIT(hwmon_power_enable) #define HWMON_P_AVERAGE BIT(hwmon_power_average) #define HWMON_P_AVERAGE_INTERVAL BIT(hwmon_power_average_interval) #define HWMON_P_AVERAGE_INTERVAL_MAX BIT(hwmon_power_average_interval_max) @@ -241,14 +248,17 @@ enum hwmon_power_attributes { #define HWMON_P_CRIT_ALARM BIT(hwmon_power_crit_alarm) enum hwmon_energy_attributes { + hwmon_energy_enable, hwmon_energy_input, hwmon_energy_label, }; +#define HWMON_E_ENABLE BIT(hwmon_energy_enable) #define HWMON_E_INPUT BIT(hwmon_energy_input) #define HWMON_E_LABEL BIT(hwmon_energy_label) enum hwmon_humidity_attributes { + hwmon_humidity_enable, hwmon_humidity_input, hwmon_humidity_label, hwmon_humidity_min, @@ -259,6 +269,7 @@ enum hwmon_humidity_attributes { hwmon_humidity_fault, }; +#define HWMON_H_ENABLE BIT(hwmon_humidity_enable) #define HWMON_H_INPUT BIT(hwmon_humidity_input) #define HWMON_H_LABEL BIT(hwmon_humidity_label) #define HWMON_H_MIN BIT(hwmon_humidity_min) @@ -269,6 +280,7 @@ enum hwmon_humidity_attributes { #define HWMON_H_FAULT BIT(hwmon_humidity_fault) enum hwmon_fan_attributes { + hwmon_fan_enable, hwmon_fan_input, hwmon_fan_label, hwmon_fan_min, @@ -282,6 +294,7 @@ enum hwmon_fan_attributes { hwmon_fan_fault, }; +#define HWMON_F_ENABLE BIT(hwmon_fan_enable) #define HWMON_F_INPUT BIT(hwmon_fan_input) #define HWMON_F_LABEL BIT(hwmon_fan_label) #define HWMON_F_MIN BIT(hwmon_fan_min) @@ -306,6 +319,13 @@ enum hwmon_pwm_attributes { #define HWMON_PWM_MODE BIT(hwmon_pwm_mode) #define HWMON_PWM_FREQ BIT(hwmon_pwm_freq) +enum hwmon_intrusion_attributes { + hwmon_intrusion_alarm, + hwmon_intrusion_beep, +}; +#define HWMON_INTRUSION_ALARM BIT(hwmon_intrusion_alarm) +#define HWMON_INTRUSION_BEEP BIT(hwmon_intrusion_beep) + /** * struct hwmon_ops - hwmon device operations * @is_visible: Callback to return attribute visibility. Mandatory. diff --git a/include/linux/pmbus.h b/include/linux/pmbus.h index 08468fca5ea2..1ea5bae708a1 100644 --- a/include/linux/pmbus.h +++ b/include/linux/pmbus.h @@ -8,6 +8,8 @@ #ifndef _PMBUS_H_ #define _PMBUS_H_ +#include <linux/bits.h> + /* flags */ /* @@ -23,7 +25,14 @@ * communication errors for no explicable reason. For such chips, checking * the status register must be disabled. */ -#define PMBUS_SKIP_STATUS_CHECK (1 << 0) +#define PMBUS_SKIP_STATUS_CHECK BIT(0) + +/* + * PMBUS_WRITE_PROTECTED + * Set if the chip is write protected and write protection is not determined + * by the standard WRITE_PROTECT command. + */ +#define PMBUS_WRITE_PROTECTED BIT(1) struct pmbus_platform_data { u32 flags; /* Device specific flags */ |