From b7f3e9650f12d1e06b94a0257bcb90279f691bf5 Mon Sep 17 00:00:00 2001 From: Aleksa Savic Date: Wed, 14 Sep 2022 13:43:27 +0200 Subject: hwmon: (aquacomputer_d5next) Fix Quadro fan speed offsets The offsets for setting speeds of fans connected to Quadro are off by one. Set them to their correct values. The offsets as shown point to registers for setting the fan control mode, which will be explored in future patches, but slipped in here. When setting fan speeds, the resulting values were overlapping, which made the fans still run in my initial testing. Fixes: cdbe34da01e3 ("hwmon: (aquacomputer_d5next) Add support for Aquacomputer Quadro fan controller") Signed-off-by: Aleksa Savic Link: https://lore.kernel.org/r/20220914114327.6941-1-savicaleksa83@gmail.com Cc: stable@vger.kenrel.org Signed-off-by: Guenter Roeck --- drivers/hwmon/aquacomputer_d5next.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/aquacomputer_d5next.c b/drivers/hwmon/aquacomputer_d5next.c index 66430553cc45..36752cf2cac9 100644 --- a/drivers/hwmon/aquacomputer_d5next.c +++ b/drivers/hwmon/aquacomputer_d5next.c @@ -110,7 +110,7 @@ static u16 octo_ctrl_fan_offsets[] = { 0x5B, 0xB0, 0x105, 0x15A, 0x1AF, 0x204, 0 static u8 quadro_sensor_fan_offsets[] = { 0x70, 0x7D, 0x8A, 0x97 }; /* Fan speed registers in Quadro control report (from 0-100%) */ -static u16 quadro_ctrl_fan_offsets[] = { 0x36, 0x8b, 0xe0, 0x135 }; +static u16 quadro_ctrl_fan_offsets[] = { 0x37, 0x8c, 0xe1, 0x136 }; /* Labels for D5 Next */ static const char *const label_d5next_temp[] = { -- cgit v1.2.3 From 7f62cf781e6567d59c8935dc8c6068ce2bb904b7 Mon Sep 17 00:00:00 2001 From: Liang He Date: Fri, 16 Sep 2022 23:47:08 +0800 Subject: hwmon: (gsc-hwmon) Call of_node_get() before of_find_xxx API In gsc_hwmon_get_devtree_pdata(), we should call of_node_get() before the of_find_compatible_node() which will automatically call of_node_put() for the 'from' argument. Fixes: 3bce5377ef66 ("hwmon: Add Gateworks System Controller support") Signed-off-by: Liang He Co-developed-by: Mengda Chen Signed-off-by: Mengda Chen Link: https://lore.kernel.org/r/20220916154708.3084515-1-chenmengda2009@163.com Cc: stable@vger.kernel.org Signed-off-by: Guenter Roeck --- drivers/hwmon/gsc-hwmon.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/gsc-hwmon.c b/drivers/hwmon/gsc-hwmon.c index d64be48f1ef6..b60ec95b5edb 100644 --- a/drivers/hwmon/gsc-hwmon.c +++ b/drivers/hwmon/gsc-hwmon.c @@ -267,6 +267,7 @@ gsc_hwmon_get_devtree_pdata(struct device *dev) pdata->nchannels = nchannels; /* fan controller base address */ + of_node_get(dev->parent->of_node); fan = of_find_compatible_node(dev->parent->of_node, NULL, "gw,gsc-fan"); if (fan && of_property_read_u32(fan, "reg", &pdata->fan_base)) { of_node_put(fan); -- cgit v1.2.3 From 1bce56b25a004b12b5d97b9b8452f451c8a76677 Mon Sep 17 00:00:00 2001 From: Eugene Shalygin Date: Wed, 20 Jul 2022 09:20:16 +0200 Subject: hwmon: (asus_wmi_ec_sensors) remove driver This driver utilises a WMI interface found in AMD 500 series ASUS boards, to read EC registers. But it turned out that ASUS abandoned the interface, as it disappeared from Intel 600 series boards. Additionally, the WMI interface was incredibly slow. Therefore this driver was deprecated in favor of the asus_ec_sensors driver, which supports more boards, more sensors, and is faster. Signed-off-by: Eugene Shalygin Link: https://lore.kernel.org/r/20220720072016.102086-2-eugene.shalygin@gmail.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/asus_wmi_ec_sensors.rst | 38 -- Documentation/hwmon/index.rst | 1 - MAINTAINERS | 7 - drivers/hwmon/Kconfig | 15 - drivers/hwmon/Makefile | 1 - drivers/hwmon/asus_wmi_ec_sensors.c | 622 ---------------------------- 6 files changed, 684 deletions(-) delete mode 100644 Documentation/hwmon/asus_wmi_ec_sensors.rst delete mode 100644 drivers/hwmon/asus_wmi_ec_sensors.c (limited to 'drivers/hwmon') diff --git a/Documentation/hwmon/asus_wmi_ec_sensors.rst b/Documentation/hwmon/asus_wmi_ec_sensors.rst deleted file mode 100644 index 1b287f229e86..000000000000 --- a/Documentation/hwmon/asus_wmi_ec_sensors.rst +++ /dev/null @@ -1,38 +0,0 @@ -.. SPDX-License-Identifier: GPL-2.0-or-later - -Kernel driver asus_wmi_ec_sensors -================================= - -Supported boards: - * PRIME X570-PRO, - * Pro WS X570-ACE, - * ROG CROSSHAIR VIII DARK HERO, - * ROG CROSSHAIR VIII FORMULA, - * ROG CROSSHAIR VIII HERO, - * ROG STRIX B550-E GAMING, - * ROG STRIX B550-I GAMING, - * ROG STRIX X570-E GAMING. - -Authors: - - Eugene Shalygin - -Description: ------------- -ASUS mainboards publish hardware monitoring information via Super I/O -chip and the ACPI embedded controller (EC) registers. Some of the sensors -are only available via the EC. - -ASUS WMI interface provides a method (BREC) to read data from EC registers, -which is utilized by this driver to publish those sensor readings to the -HWMON system. The driver is aware of and reads the following sensors: - -1. Chipset (PCH) temperature -2. CPU package temperature -3. Motherboard temperature -4. Readings from the T_Sensor header -5. VRM temperature -6. CPU_Opt fan RPM -7. Chipset fan RPM -8. Readings from the "Water flow meter" header (RPM) -9. Readings from the "Water In" and "Water Out" temperature headers -10. CPU current diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index f7113b0f8b2a..98fa687b361e 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -44,7 +44,6 @@ Hardware Monitoring Kernel Drivers asc7621 aspeed-pwm-tacho asus_ec_sensors - asus_wmi_ec_sensors asus_wmi_sensors bcm54140 bel-pfe diff --git a/MAINTAINERS b/MAINTAINERS index 936490dcc97b..cf7e3a9a011c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3233,13 +3233,6 @@ L: linux-hwmon@vger.kernel.org S: Maintained F: drivers/hwmon/asus_wmi_sensors.c -ASUS WMI EC HARDWARE MONITOR DRIVER -M: Eugene Shalygin -M: Denis Pauk -L: linux-hwmon@vger.kernel.org -S: Maintained -F: drivers/hwmon/asus_wmi_ec_sensors.c - ASUS EC HARDWARE MONITOR DRIVER M: Eugene Shalygin L: linux-hwmon@vger.kernel.org diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index e70d9614bec2..f2898725ad41 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -2341,21 +2341,6 @@ config SENSORS_ASUS_WMI This driver can also be built as a module. If so, the module will be called asus_wmi_sensors. -config SENSORS_ASUS_WMI_EC - tristate "ASUS WMI B550/X570" - depends on ACPI_WMI && SENSORS_ASUS_EC=n - help - If you say yes here you get support for the ACPI embedded controller - hardware monitoring interface found in B550/X570 ASUS motherboards. - This driver will provide readings of fans, voltages and temperatures - through the system firmware. - - This driver is deprecated in favor of the ASUS EC Sensors driver - which provides fully compatible output. - - This driver can also be built as a module. If so, the module - will be called asus_wmi_sensors_ec. - config SENSORS_ASUS_EC tristate "ASUS EC Sensors" depends on X86 diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 007e829d1d0d..f62c5c36b276 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -11,7 +11,6 @@ obj-$(CONFIG_SENSORS_ACPI_POWER) += acpi_power_meter.o obj-$(CONFIG_SENSORS_ATK0110) += asus_atk0110.o obj-$(CONFIG_SENSORS_ASUS_EC) += asus-ec-sensors.o obj-$(CONFIG_SENSORS_ASUS_WMI) += asus_wmi_sensors.o -obj-$(CONFIG_SENSORS_ASUS_WMI_EC) += asus_wmi_ec_sensors.o # Native drivers # asb100, then w83781d go first, as they can override other drivers' addresses. diff --git a/drivers/hwmon/asus_wmi_ec_sensors.c b/drivers/hwmon/asus_wmi_ec_sensors.c deleted file mode 100644 index a3a2f014dec0..000000000000 --- a/drivers/hwmon/asus_wmi_ec_sensors.c +++ /dev/null @@ -1,622 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * HWMON driver for ASUS B550/X570 motherboards that publish sensor - * values via the embedded controller registers. - * - * Copyright (C) 2021 Eugene Shalygin - * Copyright (C) 2018-2019 Ed Brindley - * - * EC provides: - * - Chipset temperature - * - CPU temperature - * - Motherboard temperature - * - T_Sensor temperature - * - VRM temperature - * - Water In temperature - * - Water Out temperature - * - CPU Optional Fan RPM - * - Chipset Fan RPM - * - Water Flow Fan RPM - * - CPU current - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define ASUSWMI_MONITORING_GUID "466747A0-70EC-11DE-8A39-0800200C9A66" -#define ASUSWMI_METHODID_BLOCK_READ_EC 0x42524543 /* BREC */ -/* From the ASUS DSDT source */ -#define ASUSWMI_BREC_REGISTERS_MAX 16 -#define ASUSWMI_MAX_BUF_LEN 128 -#define SENSOR_LABEL_LEN 16 - -static u32 hwmon_attributes[hwmon_max] = { - [hwmon_chip] = HWMON_C_REGISTER_TZ, - [hwmon_temp] = HWMON_T_INPUT | HWMON_T_LABEL, - [hwmon_in] = HWMON_I_INPUT | HWMON_I_LABEL, - [hwmon_curr] = HWMON_C_INPUT | HWMON_C_LABEL, - [hwmon_fan] = HWMON_F_INPUT | HWMON_F_LABEL, -}; - -struct asus_wmi_ec_sensor_address { - u8 index; - u8 bank; - u8 size; -}; - -#define MAKE_SENSOR_ADDRESS(size_i, bank_i, index_i) { \ - .size = size_i, \ - .bank = bank_i, \ - .index = index_i, \ -} - -struct ec_sensor_info { - struct asus_wmi_ec_sensor_address addr; - char label[SENSOR_LABEL_LEN]; - enum hwmon_sensor_types type; -}; - -#define EC_SENSOR(sensor_label, sensor_type, size, bank, index) { \ - .addr = MAKE_SENSOR_ADDRESS(size, bank, index), \ - .label = sensor_label, \ - .type = sensor_type, \ -} - -enum known_ec_sensor { - SENSOR_TEMP_CHIPSET, - SENSOR_TEMP_CPU, - SENSOR_TEMP_MB, - SENSOR_TEMP_T_SENSOR, - SENSOR_TEMP_VRM, - SENSOR_FAN_CPU_OPT, - SENSOR_FAN_CHIPSET, - SENSOR_FAN_VRM_HS, - SENSOR_FAN_WATER_FLOW, - SENSOR_CURR_CPU, - SENSOR_TEMP_WATER_IN, - SENSOR_TEMP_WATER_OUT, - SENSOR_MAX -}; - -/* All known sensors for ASUS EC controllers */ -static const struct ec_sensor_info known_ec_sensors[] = { - [SENSOR_TEMP_CHIPSET] = EC_SENSOR("Chipset", hwmon_temp, 1, 0x00, 0x3a), - [SENSOR_TEMP_CPU] = EC_SENSOR("CPU", hwmon_temp, 1, 0x00, 0x3b), - [SENSOR_TEMP_MB] = EC_SENSOR("Motherboard", hwmon_temp, 1, 0x00, 0x3c), - [SENSOR_TEMP_T_SENSOR] = EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x3d), - [SENSOR_TEMP_VRM] = EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x3e), - [SENSOR_FAN_CPU_OPT] = EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xb0), - [SENSOR_FAN_VRM_HS] = EC_SENSOR("VRM HS", hwmon_fan, 2, 0x00, 0xb2), - [SENSOR_FAN_CHIPSET] = EC_SENSOR("Chipset", hwmon_fan, 2, 0x00, 0xb4), - [SENSOR_FAN_WATER_FLOW] = EC_SENSOR("Water_Flow", hwmon_fan, 2, 0x00, 0xbc), - [SENSOR_CURR_CPU] = EC_SENSOR("CPU", hwmon_curr, 1, 0x00, 0xf4), - [SENSOR_TEMP_WATER_IN] = EC_SENSOR("Water_In", hwmon_temp, 1, 0x01, 0x00), - [SENSOR_TEMP_WATER_OUT] = EC_SENSOR("Water_Out", hwmon_temp, 1, 0x01, 0x01), -}; - -struct asus_wmi_data { - const enum known_ec_sensor known_board_sensors[SENSOR_MAX + 1]; -}; - -/* boards with EC support */ -static struct asus_wmi_data sensors_board_PW_X570_P = { - .known_board_sensors = { - SENSOR_TEMP_CHIPSET, SENSOR_TEMP_CPU, SENSOR_TEMP_MB, - SENSOR_TEMP_T_SENSOR, SENSOR_TEMP_VRM, - SENSOR_FAN_CHIPSET, - SENSOR_MAX - }, -}; - -static struct asus_wmi_data sensors_board_PW_X570_A = { - .known_board_sensors = { - SENSOR_TEMP_CHIPSET, SENSOR_TEMP_CPU, SENSOR_TEMP_MB, SENSOR_TEMP_VRM, - SENSOR_FAN_CHIPSET, - SENSOR_CURR_CPU, - SENSOR_MAX - }, -}; - -static struct asus_wmi_data sensors_board_R_C8H = { - .known_board_sensors = { - SENSOR_TEMP_CHIPSET, SENSOR_TEMP_CPU, SENSOR_TEMP_MB, - SENSOR_TEMP_T_SENSOR, SENSOR_TEMP_VRM, - SENSOR_TEMP_WATER_IN, SENSOR_TEMP_WATER_OUT, - SENSOR_FAN_CPU_OPT, SENSOR_FAN_CHIPSET, SENSOR_FAN_WATER_FLOW, - SENSOR_CURR_CPU, - SENSOR_MAX - }, -}; - -/* Same as Hero but without chipset fan */ -static struct asus_wmi_data sensors_board_R_C8DH = { - .known_board_sensors = { - SENSOR_TEMP_CHIPSET, SENSOR_TEMP_CPU, SENSOR_TEMP_MB, - SENSOR_TEMP_T_SENSOR, SENSOR_TEMP_VRM, - SENSOR_TEMP_WATER_IN, SENSOR_TEMP_WATER_OUT, - SENSOR_FAN_CPU_OPT, SENSOR_FAN_WATER_FLOW, - SENSOR_CURR_CPU, - SENSOR_MAX - }, -}; - -/* Same as Hero but without water */ -static struct asus_wmi_data sensors_board_R_C8F = { - .known_board_sensors = { - SENSOR_TEMP_CHIPSET, SENSOR_TEMP_CPU, SENSOR_TEMP_MB, - SENSOR_TEMP_T_SENSOR, SENSOR_TEMP_VRM, - SENSOR_FAN_CPU_OPT, SENSOR_FAN_CHIPSET, - SENSOR_CURR_CPU, - SENSOR_MAX - }, -}; - -static struct asus_wmi_data sensors_board_RS_B550_E_G = { - .known_board_sensors = { - SENSOR_TEMP_CHIPSET, SENSOR_TEMP_CPU, SENSOR_TEMP_MB, - SENSOR_TEMP_T_SENSOR, SENSOR_TEMP_VRM, - SENSOR_FAN_CPU_OPT, - SENSOR_MAX - }, -}; - -static struct asus_wmi_data sensors_board_RS_B550_I_G = { - .known_board_sensors = { - SENSOR_TEMP_CHIPSET, SENSOR_TEMP_CPU, SENSOR_TEMP_MB, - SENSOR_TEMP_T_SENSOR, SENSOR_TEMP_VRM, - SENSOR_FAN_VRM_HS, - SENSOR_CURR_CPU, - SENSOR_MAX - }, -}; - -static struct asus_wmi_data sensors_board_RS_X570_E_G = { - .known_board_sensors = { - SENSOR_TEMP_CHIPSET, SENSOR_TEMP_CPU, SENSOR_TEMP_MB, - SENSOR_TEMP_T_SENSOR, SENSOR_TEMP_VRM, - SENSOR_FAN_CHIPSET, - SENSOR_CURR_CPU, - SENSOR_MAX - }, -}; - -#define DMI_EXACT_MATCH_ASUS_BOARD_NAME(name, sensors) { \ - .matches = { \ - DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."), \ - DMI_EXACT_MATCH(DMI_BOARD_NAME, name), \ - }, \ - .driver_data = sensors, \ -} - -static const struct dmi_system_id asus_wmi_ec_dmi_table[] = { - DMI_EXACT_MATCH_ASUS_BOARD_NAME("PRIME X570-PRO", &sensors_board_PW_X570_P), - DMI_EXACT_MATCH_ASUS_BOARD_NAME("Pro WS X570-ACE", &sensors_board_PW_X570_A), - DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VIII DARK HERO", &sensors_board_R_C8DH), - DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VIII FORMULA", &sensors_board_R_C8F), - DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VIII HERO", &sensors_board_R_C8H), - DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B550-E GAMING", &sensors_board_RS_B550_E_G), - DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B550-I GAMING", &sensors_board_RS_B550_I_G), - DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X570-E GAMING", &sensors_board_RS_X570_E_G), - {} -}; -MODULE_DEVICE_TABLE(dmi, asus_wmi_ec_dmi_table); - -struct ec_sensor { - enum known_ec_sensor info_index; - long cached_value; -}; - -/** - * struct asus_wmi_ec_info - sensor info. - * @sensors: list of sensors. - * @read_arg: UTF-16LE string to pass to BRxx() WMI function. - * @read_buffer: decoded output from WMI result. - * @nr_sensors: number of board EC sensors. - * @nr_registers: number of EC registers to read (sensor might span more than 1 register). - * @last_updated: in jiffies. - */ -struct asus_wmi_ec_info { - struct ec_sensor sensors[SENSOR_MAX]; - char read_arg[(ASUSWMI_BREC_REGISTERS_MAX * 4 + 1) * 2]; - u8 read_buffer[ASUSWMI_BREC_REGISTERS_MAX]; - unsigned int nr_sensors; - unsigned int nr_registers; - unsigned long last_updated; -}; - -struct asus_wmi_sensors { - struct asus_wmi_ec_info ec; - /* lock access to internal cache */ - struct mutex lock; -}; - -static int asus_wmi_ec_fill_board_sensors(struct asus_wmi_ec_info *ec, - const enum known_ec_sensor *bsi) -{ - struct ec_sensor *s = ec->sensors; - int i; - - ec->nr_sensors = 0; - ec->nr_registers = 0; - - for (i = 0; bsi[i] != SENSOR_MAX; i++) { - s[i].info_index = bsi[i]; - ec->nr_sensors++; - ec->nr_registers += known_ec_sensors[bsi[i]].addr.size; - } - - return 0; -} - -/* - * The next four functions convert to or from BRxx string argument format. - * The format of the string is as follows: - * - The string consists of two-byte UTF-16LE characters. - * - The value of the very first byte in the string is equal to the total - * length of the next string in bytes, thus excluding the first two-byte - * character. - * - The rest of the string encodes the pairs of (bank, index) pairs, where - * both values are byte-long (0x00 to 0xFF). - * - Numbers are encoded as UTF-16LE hex values. - */ -static int asus_wmi_ec_decode_reply_buffer(const u8 *in, u32 length, u8 *out) -{ - char buffer[ASUSWMI_MAX_BUF_LEN * 2]; - u32 len = min_t(u32, get_unaligned_le16(in), length - 2); - - utf16s_to_utf8s((wchar_t *)(in + 2), len / 2, UTF16_LITTLE_ENDIAN, buffer, sizeof(buffer)); - - return hex2bin(out, buffer, len / 4); -} - -static void asus_wmi_ec_encode_registers(const u8 *in, u32 len, char *out) -{ - char buffer[ASUSWMI_MAX_BUF_LEN * 2]; - - bin2hex(buffer, in, len); - - utf8s_to_utf16s(buffer, len * 2, UTF16_LITTLE_ENDIAN, (wchar_t *)(out + 2), len * 2); - - put_unaligned_le16(len * 4, out); -} - -static void asus_wmi_ec_make_block_read_query(struct asus_wmi_ec_info *ec) -{ - u8 registers[ASUSWMI_BREC_REGISTERS_MAX * 2]; - const struct ec_sensor_info *si; - int i, j, offset; - - offset = 0; - for (i = 0; i < ec->nr_sensors; i++) { - si = &known_ec_sensors[ec->sensors[i].info_index]; - for (j = 0; j < si->addr.size; j++) { - registers[offset++] = si->addr.bank; - registers[offset++] = si->addr.index + j; - } - } - - asus_wmi_ec_encode_registers(registers, offset, ec->read_arg); -} - -static int asus_wmi_ec_block_read(u32 method_id, char *query, u8 *out) -{ - struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; - struct acpi_buffer input; - union acpi_object *obj; - acpi_status status; - int ret; - - /* The first byte of the BRxx() argument string has to be the string size. */ - input.length = query[0] + 2; - input.pointer = query; - status = wmi_evaluate_method(ASUSWMI_MONITORING_GUID, 0, method_id, &input, &output); - if (ACPI_FAILURE(status)) - return -EIO; - - obj = output.pointer; - if (!obj) - return -EIO; - - if (obj->type != ACPI_TYPE_BUFFER || obj->buffer.length < 2) { - ret = -EIO; - goto out_free_obj; - } - - ret = asus_wmi_ec_decode_reply_buffer(obj->buffer.pointer, obj->buffer.length, out); - -out_free_obj: - ACPI_FREE(obj); - return ret; -} - -static inline long get_sensor_value(const struct ec_sensor_info *si, u8 *data) -{ - switch (si->addr.size) { - case 1: - return *data; - case 2: - return get_unaligned_be16(data); - case 4: - return get_unaligned_be32(data); - default: - return 0; - } -} - -static void asus_wmi_ec_update_ec_sensors(struct asus_wmi_ec_info *ec) -{ - const struct ec_sensor_info *si; - struct ec_sensor *s; - u8 i_sensor; - u8 *data; - - data = ec->read_buffer; - for (i_sensor = 0; i_sensor < ec->nr_sensors; i_sensor++) { - s = &ec->sensors[i_sensor]; - si = &known_ec_sensors[s->info_index]; - s->cached_value = get_sensor_value(si, data); - data += si->addr.size; - } -} - -static long asus_wmi_ec_scale_sensor_value(long value, int data_type) -{ - switch (data_type) { - case hwmon_curr: - case hwmon_temp: - case hwmon_in: - return value * MILLI; - default: - return value; - } -} - -static int asus_wmi_ec_find_sensor_index(const struct asus_wmi_ec_info *ec, - enum hwmon_sensor_types type, int channel) -{ - int i; - - for (i = 0; i < ec->nr_sensors; i++) { - if (known_ec_sensors[ec->sensors[i].info_index].type == type) { - if (channel == 0) - return i; - - channel--; - } - } - return -EINVAL; -} - -static int asus_wmi_ec_get_cached_value_or_update(struct asus_wmi_sensors *sensor_data, - int sensor_index, - long *value) -{ - struct asus_wmi_ec_info *ec = &sensor_data->ec; - int ret = 0; - - mutex_lock(&sensor_data->lock); - - if (time_after(jiffies, ec->last_updated + HZ)) { - ret = asus_wmi_ec_block_read(ASUSWMI_METHODID_BLOCK_READ_EC, - ec->read_arg, ec->read_buffer); - if (ret) - goto unlock; - - asus_wmi_ec_update_ec_sensors(ec); - ec->last_updated = jiffies; - } - - *value = ec->sensors[sensor_index].cached_value; - -unlock: - mutex_unlock(&sensor_data->lock); - - return ret; -} - -/* Now follow the functions that implement the hwmon interface */ - -static int asus_wmi_ec_hwmon_read(struct device *dev, enum hwmon_sensor_types type, - u32 attr, int channel, long *val) -{ - struct asus_wmi_sensors *sensor_data = dev_get_drvdata(dev); - struct asus_wmi_ec_info *ec = &sensor_data->ec; - int ret, sidx, info_index; - long value = 0; - - sidx = asus_wmi_ec_find_sensor_index(ec, type, channel); - if (sidx < 0) - return sidx; - - ret = asus_wmi_ec_get_cached_value_or_update(sensor_data, sidx, &value); - if (ret) - return ret; - - info_index = ec->sensors[sidx].info_index; - *val = asus_wmi_ec_scale_sensor_value(value, known_ec_sensors[info_index].type); - - return ret; -} - -static int asus_wmi_ec_hwmon_read_string(struct device *dev, - enum hwmon_sensor_types type, u32 attr, - int channel, const char **str) -{ - struct asus_wmi_sensors *sensor_data = dev_get_drvdata(dev); - struct asus_wmi_ec_info *ec = &sensor_data->ec; - int sensor_index; - - sensor_index = asus_wmi_ec_find_sensor_index(ec, type, channel); - *str = known_ec_sensors[ec->sensors[sensor_index].info_index].label; - - return 0; -} - -static umode_t asus_wmi_ec_hwmon_is_visible(const void *drvdata, - enum hwmon_sensor_types type, u32 attr, - int channel) -{ - const struct asus_wmi_sensors *sensor_data = drvdata; - const struct asus_wmi_ec_info *ec = &sensor_data->ec; - int index; - - index = asus_wmi_ec_find_sensor_index(ec, type, channel); - - return index < 0 ? 0 : 0444; -} - -static int asus_wmi_hwmon_add_chan_info(struct hwmon_channel_info *asus_wmi_hwmon_chan, - struct device *dev, int num, - enum hwmon_sensor_types type, u32 config) -{ - u32 *cfg; - - cfg = devm_kcalloc(dev, num + 1, sizeof(*cfg), GFP_KERNEL); - if (!cfg) - return -ENOMEM; - - asus_wmi_hwmon_chan->type = type; - asus_wmi_hwmon_chan->config = cfg; - memset32(cfg, config, num); - - return 0; -} - -static const struct hwmon_ops asus_wmi_ec_hwmon_ops = { - .is_visible = asus_wmi_ec_hwmon_is_visible, - .read = asus_wmi_ec_hwmon_read, - .read_string = asus_wmi_ec_hwmon_read_string, -}; - -static struct hwmon_chip_info asus_wmi_ec_chip_info = { - .ops = &asus_wmi_ec_hwmon_ops, -}; - -static int asus_wmi_ec_configure_sensor_setup(struct device *dev, - const enum known_ec_sensor *bsi) -{ - struct asus_wmi_sensors *sensor_data = dev_get_drvdata(dev); - struct asus_wmi_ec_info *ec = &sensor_data->ec; - struct hwmon_channel_info *asus_wmi_hwmon_chan; - const struct hwmon_channel_info **asus_wmi_ci; - int nr_count[hwmon_max] = {}, nr_types = 0; - const struct hwmon_chip_info *chip_info; - const struct ec_sensor_info *si; - enum hwmon_sensor_types type; - struct device *hwdev; - int i, ret; - - ret = asus_wmi_ec_fill_board_sensors(ec, bsi); - if (ret) - return ret; - - if (!sensor_data->ec.nr_sensors) - return -ENODEV; - - for (i = 0; i < ec->nr_sensors; i++) { - si = &known_ec_sensors[ec->sensors[i].info_index]; - if (!nr_count[si->type]) - nr_types++; - nr_count[si->type]++; - } - - if (nr_count[hwmon_temp]) { - nr_count[hwmon_chip]++; - nr_types++; - } - - /* - * If we can get values for all the registers in a single query, - * the query will not change from call to call. - */ - asus_wmi_ec_make_block_read_query(ec); - - asus_wmi_hwmon_chan = devm_kcalloc(dev, nr_types, sizeof(*asus_wmi_hwmon_chan), - GFP_KERNEL); - if (!asus_wmi_hwmon_chan) - return -ENOMEM; - - asus_wmi_ci = devm_kcalloc(dev, nr_types + 1, sizeof(*asus_wmi_ci), GFP_KERNEL); - if (!asus_wmi_ci) - return -ENOMEM; - - asus_wmi_ec_chip_info.info = asus_wmi_ci; - chip_info = &asus_wmi_ec_chip_info; - - for (type = 0; type < hwmon_max; type++) { - if (!nr_count[type]) - continue; - - ret = asus_wmi_hwmon_add_chan_info(asus_wmi_hwmon_chan, dev, - nr_count[type], type, - hwmon_attributes[type]); - if (ret) - return ret; - - *asus_wmi_ci++ = asus_wmi_hwmon_chan++; - } - - dev_dbg(dev, "board has %d EC sensors that span %d registers", - ec->nr_sensors, ec->nr_registers); - - hwdev = devm_hwmon_device_register_with_info(dev, "asus_wmi_ec_sensors", - sensor_data, chip_info, NULL); - - return PTR_ERR_OR_ZERO(hwdev); -} - -static int asus_wmi_probe(struct wmi_device *wdev, const void *context) -{ - struct asus_wmi_sensors *sensor_data; - struct asus_wmi_data *board_sensors; - const struct dmi_system_id *dmi_id; - const enum known_ec_sensor *bsi; - struct device *dev = &wdev->dev; - - dmi_id = dmi_first_match(asus_wmi_ec_dmi_table); - if (!dmi_id) - return -ENODEV; - - board_sensors = dmi_id->driver_data; - bsi = board_sensors->known_board_sensors; - - sensor_data = devm_kzalloc(dev, sizeof(*sensor_data), GFP_KERNEL); - if (!sensor_data) - return -ENOMEM; - - mutex_init(&sensor_data->lock); - - dev_set_drvdata(dev, sensor_data); - - return asus_wmi_ec_configure_sensor_setup(dev, bsi); -} - -static const struct wmi_device_id asus_ec_wmi_id_table[] = { - { ASUSWMI_MONITORING_GUID, NULL }, - { } -}; - -static struct wmi_driver asus_sensors_wmi_driver = { - .driver = { - .name = "asus_wmi_ec_sensors", - }, - .id_table = asus_ec_wmi_id_table, - .probe = asus_wmi_probe, -}; -module_wmi_driver(asus_sensors_wmi_driver); - -MODULE_AUTHOR("Ed Brindley "); -MODULE_AUTHOR("Eugene Shalygin "); -MODULE_DESCRIPTION("Asus WMI Sensors Driver"); -MODULE_LICENSE("GPL"); -- cgit v1.2.3 From e2769f5e7f9f40b7c489e5183b86f89afce32528 Mon Sep 17 00:00:00 2001 From: Aleksa Savic Date: Wed, 17 Aug 2022 14:14:41 +0200 Subject: hwmon: (aquacomputer_d5next) Add support for reading virtual temp sensors Add support for reading virtual temperature sensors for the D5 Next, Octo, Quadro and Farbwerk 360. Virtual temperature sensors are written to the device by the user, pulling from an arbitrary value source. Writing to them is not yet reverse engineered, so the only way to set them for now is to use the official software. Signed-off-by: Aleksa Savic Link: https://lore.kernel.org/r/20220817121441.112198-1-savicaleksa83@gmail.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/aquacomputer_d5next.rst | 32 ++++++----- drivers/hwmon/aquacomputer_d5next.c | 88 ++++++++++++++++++++++++++--- 2 files changed, 100 insertions(+), 20 deletions(-) (limited to 'drivers/hwmon') diff --git a/Documentation/hwmon/aquacomputer_d5next.rst b/Documentation/hwmon/aquacomputer_d5next.rst index 33649a1e3a05..b63a78d47624 100644 --- a/Documentation/hwmon/aquacomputer_d5next.rst +++ b/Documentation/hwmon/aquacomputer_d5next.rst @@ -20,10 +20,11 @@ This driver exposes hardware sensors of listed Aquacomputer devices, which communicate through proprietary USB HID protocols. For the D5 Next pump, available sensors are pump and fan speed, power, voltage -and current, as well as coolant temperature. Also available through debugfs are -the serial number, firmware version and power-on count. Attaching a fan to it is -optional and allows it to be controlled using temperature curves directly from the -pump. If it's not connected, the fan-related sensors will report zeroes. +and current, as well as coolant temperature and eight virtual temp sensors. Also +available through debugfs are the serial number, firmware version and power-on +count. Attaching a fan to it is optional and allows it to be controlled using +temperature curves directly from the pump. If it's not connected, the fan-related +sensors will report zeroes. The pump can be configured either through software or via its physical interface. Configuring the pump through this driver is not implemented, as it @@ -31,14 +32,19 @@ seems to require sending it a complete configuration. That includes addressable RGB LEDs, for which there is no standard sysfs interface. Thus, that task is better suited for userspace tools. -The Octo exposes four temperature sensors and eight PWM controllable fans, along -with their speed (in RPM), power, voltage and current. +The Octo exposes four physical and sixteen virtual temperature sensors, as well as +eight PWM controllable fans, along with their speed (in RPM), power, voltage and +current. -The Quadro exposes four temperature sensors, a flow sensor and four PWM controllable -fans, along with their speed (in RPM), power, voltage and current. +The Quadro exposes four physical and sixteen virtual temperature sensors, a flow +sensor and four PWM controllable fans, along with their speed (in RPM), power, +voltage and current. -The Farbwerk and Farbwerk 360 expose four temperature sensors. Depending on the device, -not all sysfs and debugfs entries will be available. +The Farbwerk and Farbwerk 360 expose four temperature sensors. Additionally, +sixteen virtual temperature sensors of the Farbwerk 360 are exposed. + +Depending on the device, not all sysfs and debugfs entries will be available. +Writing to virtual temperature sensors is not currently supported. Usage notes ----------- @@ -49,14 +55,14 @@ the kernel and supports hotswapping. Sysfs entries ------------- -================ ============================================== -temp[1-4]_input Temperature sensors (in millidegrees Celsius) +================ ============================================================== +temp[1-20]_input Physical/virtual temperature sensors (in millidegrees Celsius) fan[1-8]_input Pump/fan speed (in RPM) / Flow speed (in dL/h) power[1-8]_input Pump/fan power (in micro Watts) in[0-7]_input Pump/fan voltage (in milli Volts) curr[1-8]_input Pump/fan current (in milli Amperes) pwm[1-8] Fan PWM (0 - 255) -================ ============================================== +================ ============================================================== Debugfs entries --------------- diff --git a/drivers/hwmon/aquacomputer_d5next.c b/drivers/hwmon/aquacomputer_d5next.c index 36752cf2cac9..77cc8f50b8af 100644 --- a/drivers/hwmon/aquacomputer_d5next.c +++ b/drivers/hwmon/aquacomputer_d5next.c @@ -71,6 +71,8 @@ static u8 secondary_ctrl_report[] = { #define D5NEXT_COOLANT_TEMP 0x57 #define D5NEXT_NUM_FANS 2 #define D5NEXT_NUM_SENSORS 1 +#define D5NEXT_NUM_VIRTUAL_SENSORS 8 +#define D5NEXT_VIRTUAL_SENSORS_START 0x3f #define D5NEXT_PUMP_OFFSET 0x6c #define D5NEXT_FAN_OFFSET 0x5f #define D5NEXT_5V_VOLTAGE 0x39 @@ -86,14 +88,18 @@ static u16 d5next_ctrl_fan_offsets[] = { 0x97, 0x42 }; #define FARBWERK_SENSOR_START 0x2f /* Register offsets for the Farbwerk 360 RGB controller */ -#define FARBWERK360_NUM_SENSORS 4 -#define FARBWERK360_SENSOR_START 0x32 +#define FARBWERK360_NUM_SENSORS 4 +#define FARBWERK360_SENSOR_START 0x32 +#define FARBWERK360_NUM_VIRTUAL_SENSORS 16 +#define FARBWERK360_VIRTUAL_SENSORS_START 0x3a /* Register offsets for the Octo fan controller */ #define OCTO_POWER_CYCLES 0x18 #define OCTO_NUM_FANS 8 #define OCTO_NUM_SENSORS 4 #define OCTO_SENSOR_START 0x3D +#define OCTO_NUM_VIRTUAL_SENSORS 16 +#define OCTO_VIRTUAL_SENSORS_START 0x45 #define OCTO_CTRL_REPORT_SIZE 0x65F static u8 octo_sensor_fan_offsets[] = { 0x7D, 0x8A, 0x97, 0xA4, 0xB1, 0xBE, 0xCB, 0xD8 }; @@ -105,6 +111,8 @@ static u16 octo_ctrl_fan_offsets[] = { 0x5B, 0xB0, 0x105, 0x15A, 0x1AF, 0x204, 0 #define QUADRO_NUM_FANS 4 #define QUADRO_NUM_SENSORS 4 #define QUADRO_SENSOR_START 0x34 +#define QUADRO_NUM_VIRTUAL_SENSORS 16 +#define QUADRO_VIRTUAL_SENSORS_START 0x3c #define QUADRO_CTRL_REPORT_SIZE 0x3c1 #define QUADRO_FLOW_SENSOR_OFFSET 0x6e static u8 quadro_sensor_fan_offsets[] = { 0x70, 0x7D, 0x8A, 0x97 }; @@ -147,6 +155,25 @@ static const char *const label_temp_sensors[] = { "Sensor 4" }; +static const char *const label_virtual_temp_sensors[] = { + "Virtual sensor 1", + "Virtual sensor 2", + "Virtual sensor 3", + "Virtual sensor 4", + "Virtual sensor 5", + "Virtual sensor 6", + "Virtual sensor 7", + "Virtual sensor 8", + "Virtual sensor 9", + "Virtual sensor 10", + "Virtual sensor 11", + "Virtual sensor 12", + "Virtual sensor 13", + "Virtual sensor 14", + "Virtual sensor 15", + "Virtual sensor 16", +}; + /* Labels for Octo and Quadro (except speed) */ static const char *const label_fan_speed[] = { "Fan 1 speed", @@ -220,6 +247,8 @@ struct aqc_data { u16 *fan_ctrl_offsets; int num_temp_sensors; int temp_sensor_start_offset; + int num_virtual_temp_sensors; + int virtual_temp_sensor_start_offset; u16 power_cycle_count_offset; u8 flow_sensor_offset; @@ -231,7 +260,7 @@ struct aqc_data { u32 power_cycles; /* Sensor values */ - s32 temp_input[4]; + s32 temp_input[20]; /* Max 4 physical and 16 virtual */ u16 speed_input[8]; u32 power_input[8]; u16 voltage_input[8]; @@ -239,6 +268,7 @@ struct aqc_data { /* Label values */ const char *const *temp_label; + const char *const *virtual_temp_label; const char *const *speed_label; const char *const *power_label; const char *const *voltage_label; @@ -345,7 +375,7 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3 switch (type) { case hwmon_temp: - if (channel < priv->num_temp_sensors) + if (channel < priv->num_temp_sensors + priv->num_virtual_temp_sensors) return 0444; break; case hwmon_pwm: @@ -447,7 +477,10 @@ static int aqc_read_string(struct device *dev, enum hwmon_sensor_types type, u32 switch (type) { case hwmon_temp: - *str = priv->temp_label[channel]; + if (channel < priv->num_temp_sensors) + *str = priv->temp_label[channel]; + else + *str = priv->virtual_temp_label[channel - priv->num_temp_sensors]; break; case hwmon_fan: *str = priv->speed_label[channel]; @@ -509,6 +542,22 @@ static const struct hwmon_ops aqc_hwmon_ops = { static const struct hwmon_channel_info *aqc_info[] = { HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_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, @@ -568,7 +617,7 @@ static const struct hwmon_chip_info aqc_chip_info = { static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size) { - int i, sensor_value; + int i, j, sensor_value; struct aqc_data *priv; if (report->id != STATUS_REPORT_ID) @@ -581,7 +630,7 @@ static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8 priv->serial_number[1] = get_unaligned_be16(data + SERIAL_SECOND_PART); priv->firmware_version = get_unaligned_be16(data + FIRMWARE_VERSION); - /* Temperature sensor readings */ + /* Physical temperature sensor readings */ for (i = 0; i < priv->num_temp_sensors; i++) { sensor_value = get_unaligned_be16(data + priv->temp_sensor_start_offset + @@ -592,6 +641,18 @@ static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8 priv->temp_input[i] = sensor_value * 10; } + /* Virtual temperature sensor readings */ + for (j = 0; j < priv->num_virtual_temp_sensors; j++) { + sensor_value = get_unaligned_be16(data + + priv->virtual_temp_sensor_start_offset + + j * AQC_TEMP_SENSOR_SIZE); + if (sensor_value == AQC_TEMP_SENSOR_DISCONNECTED) + priv->temp_input[i] = -ENODATA; + else + priv->temp_input[i] = sensor_value * 10; + i++; + } + /* Fan speed and related readings */ for (i = 0; i < priv->num_fans; i++) { priv->speed_input[i] = @@ -717,10 +778,13 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id) priv->fan_ctrl_offsets = d5next_ctrl_fan_offsets; priv->num_temp_sensors = D5NEXT_NUM_SENSORS; priv->temp_sensor_start_offset = D5NEXT_COOLANT_TEMP; + priv->num_virtual_temp_sensors = D5NEXT_NUM_VIRTUAL_SENSORS; + priv->virtual_temp_sensor_start_offset = D5NEXT_VIRTUAL_SENSORS_START; priv->power_cycle_count_offset = D5NEXT_POWER_CYCLES; priv->buffer_size = D5NEXT_CTRL_REPORT_SIZE; priv->temp_label = label_d5next_temp; + priv->virtual_temp_label = label_virtual_temp_sensors; priv->speed_label = label_d5next_speeds; priv->power_label = label_d5next_power; priv->voltage_label = label_d5next_voltages; @@ -740,7 +804,11 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id) priv->num_fans = 0; priv->num_temp_sensors = FARBWERK360_NUM_SENSORS; priv->temp_sensor_start_offset = FARBWERK360_SENSOR_START; + priv->num_virtual_temp_sensors = FARBWERK360_NUM_VIRTUAL_SENSORS; + priv->virtual_temp_sensor_start_offset = FARBWERK360_VIRTUAL_SENSORS_START; + priv->temp_label = label_temp_sensors; + priv->virtual_temp_label = label_virtual_temp_sensors; break; case USB_PRODUCT_ID_OCTO: priv->kind = octo; @@ -750,10 +818,13 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id) priv->fan_ctrl_offsets = octo_ctrl_fan_offsets; priv->num_temp_sensors = OCTO_NUM_SENSORS; priv->temp_sensor_start_offset = OCTO_SENSOR_START; + priv->num_virtual_temp_sensors = OCTO_NUM_VIRTUAL_SENSORS; + priv->virtual_temp_sensor_start_offset = OCTO_VIRTUAL_SENSORS_START; priv->power_cycle_count_offset = OCTO_POWER_CYCLES; priv->buffer_size = OCTO_CTRL_REPORT_SIZE; priv->temp_label = label_temp_sensors; + priv->virtual_temp_label = label_virtual_temp_sensors; priv->speed_label = label_fan_speed; priv->power_label = label_fan_power; priv->voltage_label = label_fan_voltage; @@ -767,11 +838,14 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id) priv->fan_ctrl_offsets = quadro_ctrl_fan_offsets; priv->num_temp_sensors = QUADRO_NUM_SENSORS; priv->temp_sensor_start_offset = QUADRO_SENSOR_START; + priv->num_virtual_temp_sensors = QUADRO_NUM_VIRTUAL_SENSORS; + priv->virtual_temp_sensor_start_offset = QUADRO_VIRTUAL_SENSORS_START; priv->power_cycle_count_offset = QUADRO_POWER_CYCLES; priv->buffer_size = QUADRO_CTRL_REPORT_SIZE; priv->flow_sensor_offset = QUADRO_FLOW_SENSOR_OFFSET; priv->temp_label = label_temp_sensors; + priv->virtual_temp_label = label_virtual_temp_sensors; priv->speed_label = label_quadro_speeds; priv->power_label = label_fan_power; priv->voltage_label = label_fan_voltage; -- cgit v1.2.3 From eb12f54876bb5b7789339610692b70f7f9c925a5 Mon Sep 17 00:00:00 2001 From: Wilken Gottwalt Date: Thu, 11 Aug 2022 08:26:37 +0000 Subject: hwmon: (corsair-psu) add reporting of rail mode via debugfs Add reporting if the PSU is running in single or multi rail mode via ocpmode debugfs entry. Also update the documentation and driver comments accordingly. Signed-off-by: Wilken Gottwalt Link: https://lore.kernel.org/r/YvS9PZKr0xqFqJny@monster.localdomain Signed-off-by: Guenter Roeck --- Documentation/hwmon/corsair-psu.rst | 5 +++-- drivers/hwmon/corsair-psu.c | 29 ++++++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 3 deletions(-) (limited to 'drivers/hwmon') diff --git a/Documentation/hwmon/corsair-psu.rst b/Documentation/hwmon/corsair-psu.rst index e8378e7a1d8c..c3a76305c587 100644 --- a/Documentation/hwmon/corsair-psu.rst +++ b/Documentation/hwmon/corsair-psu.rst @@ -86,8 +86,9 @@ Debugfs entries --------------- ======================= ======================================================== -uptime Current uptime of the psu +ocpmode Single or multi rail mode of the PCIe power connectors +product Product name of the psu +uptime Session uptime of the psu uptime_total Total uptime of the psu vendor Vendor name of the psu -product Product name of the psu ======================= ======================================================== diff --git a/drivers/hwmon/corsair-psu.c b/drivers/hwmon/corsair-psu.c index 14389fd7afb8..c99e4c6afc2d 100644 --- a/drivers/hwmon/corsair-psu.c +++ b/drivers/hwmon/corsair-psu.c @@ -55,6 +55,7 @@ #define SECONDS_PER_DAY (SECONDS_PER_HOUR * 24) #define RAIL_COUNT 3 /* 3v3 + 5v + 12v */ #define TEMP_COUNT 2 +#define OCP_MULTI_RAIL 0x02 #define PSU_CMD_SELECT_RAIL 0x00 /* expects length 2 */ #define PSU_CMD_RAIL_VOLTS_HCRIT 0x40 /* the rest of the commands expect length 3 */ @@ -71,9 +72,10 @@ #define PSU_CMD_RAIL_WATTS 0x96 #define PSU_CMD_VEND_STR 0x99 #define PSU_CMD_PROD_STR 0x9A -#define PSU_CMD_TOTAL_WATTS 0xEE #define PSU_CMD_TOTAL_UPTIME 0xD1 #define PSU_CMD_UPTIME 0xD2 +#define PSU_CMD_OCPMODE 0xD8 +#define PSU_CMD_TOTAL_WATTS 0xEE #define PSU_CMD_INIT 0xFE #define L_IN_VOLTS "v_in" @@ -268,6 +270,7 @@ static int corsairpsu_get_value(struct corsairpsu_data *priv, u8 cmd, u8 rail, l break; case PSU_CMD_TOTAL_UPTIME: case PSU_CMD_UPTIME: + case PSU_CMD_OCPMODE: *val = tmp; break; default: @@ -660,6 +663,29 @@ static int product_show(struct seq_file *seqf, void *unused) } DEFINE_SHOW_ATTRIBUTE(product); +static int ocpmode_show(struct seq_file *seqf, void *unused) +{ + struct corsairpsu_data *priv = seqf->private; + long val; + int ret; + + /* + * The rail mode is switchable on the fly. The RAW interface can be used for this. But it + * will not be included here, because I consider it somewhat dangerous for the health of the + * PSU. The returned value can be a bogus one, if the PSU is in the process of switching and + * getting of the value itself can also fail during this. Because of this every other value + * than OCP_MULTI_RAIL can be considered as "single rail". + */ + ret = corsairpsu_get_value(priv, PSU_CMD_OCPMODE, 0, &val); + if (ret < 0) + seq_puts(seqf, "N/A\n"); + else + seq_printf(seqf, "%s\n", (val == OCP_MULTI_RAIL) ? "multi rail" : "single rail"); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(ocpmode); + static void corsairpsu_debugfs_init(struct corsairpsu_data *priv) { char name[32]; @@ -671,6 +697,7 @@ static void corsairpsu_debugfs_init(struct corsairpsu_data *priv) debugfs_create_file("uptime_total", 0444, priv->debugfs, priv, &uptime_total_fops); debugfs_create_file("vendor", 0444, priv->debugfs, priv, &vendor_fops); debugfs_create_file("product", 0444, priv->debugfs, priv, &product_fops); + debugfs_create_file("ocpmode", 0444, priv->debugfs, priv, &ocpmode_fops); } #else -- cgit v1.2.3 From f2f394db4b5eb1d3e609c93ad85bb4d2d0490121 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Thu, 18 Aug 2022 23:00:11 +0200 Subject: hwmon: move from strlcpy with unused retval to strscpy Follow the advice of the below link and prefer 'strscpy' in this subsystem. Conversion is 1:1 because the return value is not used. Generated by a coccinelle script. Link: https://lore.kernel.org/r/CAHk-=wgfRnXz0W3D37d01q3JFkr_i_uTL=V6A6G1oUZcprmknw@mail.gmail.com/ Signed-off-by: Wolfram Sang Link: https://lore.kernel.org/r/20220818210014.6769-1-wsa+renesas@sang-engineering.com Signed-off-by: Guenter Roeck --- drivers/hwmon/adc128d818.c | 2 +- drivers/hwmon/adm1021.c | 2 +- drivers/hwmon/adm1025.c | 2 +- drivers/hwmon/adm1026.c | 2 +- drivers/hwmon/adm1029.c | 2 +- drivers/hwmon/adm1031.c | 2 +- drivers/hwmon/adt7411.c | 2 +- drivers/hwmon/adt7462.c | 2 +- drivers/hwmon/adt7475.c | 2 +- drivers/hwmon/amc6821.c | 2 +- drivers/hwmon/asb100.c | 2 +- drivers/hwmon/asc7621.c | 2 +- drivers/hwmon/dme1737.c | 2 +- drivers/hwmon/emc1403.c | 12 ++++++------ drivers/hwmon/emc2103.c | 2 +- drivers/hwmon/emc6w201.c | 2 +- drivers/hwmon/f75375s.c | 2 +- drivers/hwmon/fschmd.c | 2 +- drivers/hwmon/ftsteutates.c | 2 +- drivers/hwmon/gl518sm.c | 2 +- drivers/hwmon/gl520sm.c | 2 +- drivers/hwmon/jc42.c | 2 +- drivers/hwmon/lm63.c | 6 +++--- drivers/hwmon/lm73.c | 2 +- drivers/hwmon/lm75.c | 2 +- drivers/hwmon/lm77.c | 2 +- drivers/hwmon/lm78.c | 2 +- drivers/hwmon/lm80.c | 2 +- drivers/hwmon/lm83.c | 2 +- drivers/hwmon/lm85.c | 2 +- drivers/hwmon/lm87.c | 2 +- drivers/hwmon/lm90.c | 2 +- drivers/hwmon/lm92.c | 2 +- drivers/hwmon/lm93.c | 2 +- drivers/hwmon/lm95234.c | 2 +- drivers/hwmon/lm95241.c | 2 +- drivers/hwmon/lm95245.c | 2 +- drivers/hwmon/max1619.c | 2 +- drivers/hwmon/max1668.c | 2 +- drivers/hwmon/max31730.c | 2 +- drivers/hwmon/max6639.c | 2 +- drivers/hwmon/max6642.c | 2 +- drivers/hwmon/nct7802.c | 2 +- drivers/hwmon/nct7904.c | 2 +- drivers/hwmon/smsc47m192.c | 2 +- drivers/hwmon/stts751.c | 2 +- drivers/hwmon/thmc50.c | 2 +- drivers/hwmon/tmp401.c | 2 +- drivers/hwmon/tmp421.c | 2 +- drivers/hwmon/w83781d.c | 2 +- drivers/hwmon/w83791d.c | 2 +- drivers/hwmon/w83792d.c | 2 +- drivers/hwmon/w83793.c | 2 +- drivers/hwmon/w83795.c | 2 +- drivers/hwmon/w83l785ts.c | 2 +- drivers/hwmon/w83l786ng.c | 2 +- 56 files changed, 63 insertions(+), 63 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/adc128d818.c b/drivers/hwmon/adc128d818.c index fd938c70293f..e7b9578631ec 100644 --- a/drivers/hwmon/adc128d818.c +++ b/drivers/hwmon/adc128d818.c @@ -384,7 +384,7 @@ static int adc128_detect(struct i2c_client *client, struct i2c_board_info *info) if (i2c_smbus_read_byte_data(client, ADC128_REG_BUSY_STATUS) & 0xfc) return -ENODEV; - strlcpy(info->type, "adc128d818", I2C_NAME_SIZE); + strscpy(info->type, "adc128d818", I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/adm1021.c b/drivers/hwmon/adm1021.c index 91ecfee243bf..2dc45e958730 100644 --- a/drivers/hwmon/adm1021.c +++ b/drivers/hwmon/adm1021.c @@ -426,7 +426,7 @@ static int adm1021_detect(struct i2c_client *client, pr_debug("Detected chip %s at adapter %d, address 0x%02x.\n", type_name, i2c_adapter_id(adapter), client->addr); - strlcpy(info->type, type_name, I2C_NAME_SIZE); + strscpy(info->type, type_name, I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/adm1025.c b/drivers/hwmon/adm1025.c index 4352f6a884e8..2984c4f98496 100644 --- a/drivers/hwmon/adm1025.c +++ b/drivers/hwmon/adm1025.c @@ -470,7 +470,7 @@ static int adm1025_detect(struct i2c_client *client, else return -ENODEV; - strlcpy(info->type, name, I2C_NAME_SIZE); + strscpy(info->type, name, I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/adm1026.c b/drivers/hwmon/adm1026.c index 69b3ec752944..1f084f708743 100644 --- a/drivers/hwmon/adm1026.c +++ b/drivers/hwmon/adm1026.c @@ -1610,7 +1610,7 @@ static int adm1026_detect(struct i2c_client *client, return -ENODEV; } - strlcpy(info->type, "adm1026", I2C_NAME_SIZE); + strscpy(info->type, "adm1026", I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/adm1029.c b/drivers/hwmon/adm1029.c index 3e1999413f32..eaf6e5e04aac 100644 --- a/drivers/hwmon/adm1029.c +++ b/drivers/hwmon/adm1029.c @@ -329,7 +329,7 @@ static int adm1029_detect(struct i2c_client *client, return -ENODEV; } - strlcpy(info->type, "adm1029", I2C_NAME_SIZE); + strscpy(info->type, "adm1029", I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/adm1031.c b/drivers/hwmon/adm1031.c index ac841fa3a369..b42797bcb5b4 100644 --- a/drivers/hwmon/adm1031.c +++ b/drivers/hwmon/adm1031.c @@ -985,7 +985,7 @@ static int adm1031_detect(struct i2c_client *client, return -ENODEV; name = (id == 0x30) ? "adm1030" : "adm1031"; - strlcpy(info->type, name, I2C_NAME_SIZE); + strscpy(info->type, name, I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/adt7411.c b/drivers/hwmon/adt7411.c index fad74aa62b64..bf5c5618f8d0 100644 --- a/drivers/hwmon/adt7411.c +++ b/drivers/hwmon/adt7411.c @@ -590,7 +590,7 @@ static int adt7411_detect(struct i2c_client *client, return -ENODEV; } - strlcpy(info->type, "adt7411", I2C_NAME_SIZE); + strscpy(info->type, "adt7411", I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/adt7462.c b/drivers/hwmon/adt7462.c index e75bbd87ad09..9c0235849d4b 100644 --- a/drivers/hwmon/adt7462.c +++ b/drivers/hwmon/adt7462.c @@ -1782,7 +1782,7 @@ static int adt7462_detect(struct i2c_client *client, if (revision != ADT7462_REVISION) return -ENODEV; - strlcpy(info->type, "adt7462", I2C_NAME_SIZE); + strscpy(info->type, "adt7462", I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c index ac480e6e4818..51b3d16c3223 100644 --- a/drivers/hwmon/adt7475.c +++ b/drivers/hwmon/adt7475.c @@ -1342,7 +1342,7 @@ static int adt7475_detect(struct i2c_client *client, return -ENODEV; } - strlcpy(info->type, name, I2C_NAME_SIZE); + strscpy(info->type, name, I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/amc6821.c b/drivers/hwmon/amc6821.c index 0c16face3fd3..3bfd12ff4b3c 100644 --- a/drivers/hwmon/amc6821.c +++ b/drivers/hwmon/amc6821.c @@ -809,7 +809,7 @@ static int amc6821_detect( } dev_info(&adapter->dev, "amc6821: chip found at 0x%02x.\n", address); - strlcpy(info->type, "amc6821", I2C_NAME_SIZE); + strscpy(info->type, "amc6821", I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/asb100.c b/drivers/hwmon/asb100.c index 8cf0bcb85eb4..45bedf619457 100644 --- a/drivers/hwmon/asb100.c +++ b/drivers/hwmon/asb100.c @@ -769,7 +769,7 @@ static int asb100_detect(struct i2c_client *client, if (val1 != 0x31 || val2 != 0x06) return -ENODEV; - strlcpy(info->type, "asb100", I2C_NAME_SIZE); + strscpy(info->type, "asb100", I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/asc7621.c b/drivers/hwmon/asc7621.c index e835605a7456..8385dadf7e19 100644 --- a/drivers/hwmon/asc7621.c +++ b/drivers/hwmon/asc7621.c @@ -1153,7 +1153,7 @@ static int asc7621_detect(struct i2c_client *client, if (company == asc7621_chips[chip_index].company_id && verstep == asc7621_chips[chip_index].verstep_id) { - strlcpy(info->type, asc7621_chips[chip_index].name, + strscpy(info->type, asc7621_chips[chip_index].name, I2C_NAME_SIZE); dev_info(&adapter->dev, "Matched %s at 0x%02x\n", diff --git a/drivers/hwmon/dme1737.c b/drivers/hwmon/dme1737.c index e3ad4c2d0038..ae309e90477a 100644 --- a/drivers/hwmon/dme1737.c +++ b/drivers/hwmon/dme1737.c @@ -2456,7 +2456,7 @@ static int dme1737_i2c_detect(struct i2c_client *client, dev_info(dev, "Found a %s chip at 0x%02x (rev 0x%02x).\n", verstep == SCH5027_VERSTEP ? "SCH5027" : "DME1737", client->addr, verstep); - strlcpy(info->type, name, I2C_NAME_SIZE); + strscpy(info->type, name, I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/emc1403.c b/drivers/hwmon/emc1403.c index 314838272049..61d59189a6d1 100644 --- a/drivers/hwmon/emc1403.c +++ b/drivers/hwmon/emc1403.c @@ -329,22 +329,22 @@ static int emc1403_detect(struct i2c_client *client, id = i2c_smbus_read_byte_data(client, THERMAL_PID_REG); switch (id) { case 0x20: - strlcpy(info->type, "emc1402", I2C_NAME_SIZE); + strscpy(info->type, "emc1402", I2C_NAME_SIZE); break; case 0x21: - strlcpy(info->type, "emc1403", I2C_NAME_SIZE); + strscpy(info->type, "emc1403", I2C_NAME_SIZE); break; case 0x22: - strlcpy(info->type, "emc1422", I2C_NAME_SIZE); + strscpy(info->type, "emc1422", I2C_NAME_SIZE); break; case 0x23: - strlcpy(info->type, "emc1423", I2C_NAME_SIZE); + strscpy(info->type, "emc1423", I2C_NAME_SIZE); break; case 0x25: - strlcpy(info->type, "emc1404", I2C_NAME_SIZE); + strscpy(info->type, "emc1404", I2C_NAME_SIZE); break; case 0x27: - strlcpy(info->type, "emc1424", I2C_NAME_SIZE); + strscpy(info->type, "emc1424", I2C_NAME_SIZE); break; default: return -ENODEV; diff --git a/drivers/hwmon/emc2103.c b/drivers/hwmon/emc2103.c index e4c95ca9e19f..361cf9292456 100644 --- a/drivers/hwmon/emc2103.c +++ b/drivers/hwmon/emc2103.c @@ -643,7 +643,7 @@ emc2103_detect(struct i2c_client *new_client, struct i2c_board_info *info) if ((product != 0x24) && (product != 0x26)) return -ENODEV; - strlcpy(info->type, "emc2103", I2C_NAME_SIZE); + strscpy(info->type, "emc2103", I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/emc6w201.c b/drivers/hwmon/emc6w201.c index 29082c8463f4..bcd93f0fe982 100644 --- a/drivers/hwmon/emc6w201.c +++ b/drivers/hwmon/emc6w201.c @@ -439,7 +439,7 @@ static int emc6w201_detect(struct i2c_client *client, return -ENODEV; } - strlcpy(info->type, "emc6w201", I2C_NAME_SIZE); + strscpy(info->type, "emc6w201", I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/f75375s.c b/drivers/hwmon/f75375s.c index 57c8a473698d..8a469b2df5e1 100644 --- a/drivers/hwmon/f75375s.c +++ b/drivers/hwmon/f75375s.c @@ -897,7 +897,7 @@ static int f75375_detect(struct i2c_client *client, version = f75375_read8(client, F75375_REG_VERSION); dev_info(&adapter->dev, "found %s version: %02X\n", name, version); - strlcpy(info->type, name, I2C_NAME_SIZE); + strscpy(info->type, name, I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/fschmd.c b/drivers/hwmon/fschmd.c index c26195e3aad7..82652990063a 100644 --- a/drivers/hwmon/fschmd.c +++ b/drivers/hwmon/fschmd.c @@ -1075,7 +1075,7 @@ static int fschmd_detect(struct i2c_client *client, else return -ENODEV; - strlcpy(info->type, fschmd_id[kind].name, I2C_NAME_SIZE); + strscpy(info->type, fschmd_id[kind].name, I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/ftsteutates.c b/drivers/hwmon/ftsteutates.c index ceffc76a0c51..bb26ca97abbd 100644 --- a/drivers/hwmon/ftsteutates.c +++ b/drivers/hwmon/ftsteutates.c @@ -739,7 +739,7 @@ static int fts_detect(struct i2c_client *client, if (val != 0x11) return -ENODEV; - strlcpy(info->type, fts_id[0].name, I2C_NAME_SIZE); + strscpy(info->type, fts_id[0].name, I2C_NAME_SIZE); info->flags = 0; return 0; } diff --git a/drivers/hwmon/gl518sm.c b/drivers/hwmon/gl518sm.c index dd683b0a648f..95286c40f55a 100644 --- a/drivers/hwmon/gl518sm.c +++ b/drivers/hwmon/gl518sm.c @@ -586,7 +586,7 @@ static int gl518_detect(struct i2c_client *client, struct i2c_board_info *info) if (rev != 0x00 && rev != 0x80) return -ENODEV; - strlcpy(info->type, "gl518sm", I2C_NAME_SIZE); + strscpy(info->type, "gl518sm", I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/gl520sm.c b/drivers/hwmon/gl520sm.c index 096ba9797211..394da4ac977c 100644 --- a/drivers/hwmon/gl520sm.c +++ b/drivers/hwmon/gl520sm.c @@ -811,7 +811,7 @@ static int gl520_detect(struct i2c_client *client, struct i2c_board_info *info) return -ENODEV; } - strlcpy(info->type, "gl520sm", I2C_NAME_SIZE); + strscpy(info->type, "gl520sm", I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/jc42.c b/drivers/hwmon/jc42.c index 07f7f8b5b73d..8379a5da46b8 100644 --- a/drivers/hwmon/jc42.c +++ b/drivers/hwmon/jc42.c @@ -441,7 +441,7 @@ static int jc42_detect(struct i2c_client *client, struct i2c_board_info *info) struct jc42_chips *chip = &jc42_chips[i]; if (manid == chip->manid && (devid & chip->devid_mask) == chip->devid) { - strlcpy(info->type, "jc42", I2C_NAME_SIZE); + strscpy(info->type, "jc42", I2C_NAME_SIZE); return 0; } } diff --git a/drivers/hwmon/lm63.c b/drivers/hwmon/lm63.c index 339a145afc09..9ab2cab4c710 100644 --- a/drivers/hwmon/lm63.c +++ b/drivers/hwmon/lm63.c @@ -996,11 +996,11 @@ static int lm63_detect(struct i2c_client *client, } if (chip_id == 0x41 && address == 0x4c) - strlcpy(info->type, "lm63", I2C_NAME_SIZE); + strscpy(info->type, "lm63", I2C_NAME_SIZE); else if (chip_id == 0x51 && (address == 0x18 || address == 0x4e)) - strlcpy(info->type, "lm64", I2C_NAME_SIZE); + strscpy(info->type, "lm64", I2C_NAME_SIZE); else if (chip_id == 0x49 && address == 0x4c) - strlcpy(info->type, "lm96163", I2C_NAME_SIZE); + strscpy(info->type, "lm96163", I2C_NAME_SIZE); else return -ENODEV; diff --git a/drivers/hwmon/lm73.c b/drivers/hwmon/lm73.c index beb0d61bcd82..1346b3b3f463 100644 --- a/drivers/hwmon/lm73.c +++ b/drivers/hwmon/lm73.c @@ -257,7 +257,7 @@ static int lm73_detect(struct i2c_client *new_client, if (id < 0 || id != LM73_ID) return -ENODEV; - strlcpy(info->type, "lm73", I2C_NAME_SIZE); + strscpy(info->type, "lm73", I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c index 66dc826f7962..bcc3adcb3af1 100644 --- a/drivers/hwmon/lm75.c +++ b/drivers/hwmon/lm75.c @@ -893,7 +893,7 @@ static int lm75_detect(struct i2c_client *new_client, return -ENODEV; } - strlcpy(info->type, is_lm75a ? "lm75a" : "lm75", I2C_NAME_SIZE); + strscpy(info->type, is_lm75a ? "lm75a" : "lm75", I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/lm77.c b/drivers/hwmon/lm77.c index df6af85e170a..645cb2191abe 100644 --- a/drivers/hwmon/lm77.c +++ b/drivers/hwmon/lm77.c @@ -302,7 +302,7 @@ static int lm77_detect(struct i2c_client *client, struct i2c_board_info *info) || i2c_smbus_read_word_data(client, 7) != min) return -ENODEV; - strlcpy(info->type, "lm77", I2C_NAME_SIZE); + strscpy(info->type, "lm77", I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c index 5e129cbec1cb..694e171cab7f 100644 --- a/drivers/hwmon/lm78.c +++ b/drivers/hwmon/lm78.c @@ -617,7 +617,7 @@ static int lm78_i2c_detect(struct i2c_client *client, if (isa) mutex_unlock(&isa->update_lock); - strlcpy(info->type, client_name, I2C_NAME_SIZE); + strscpy(info->type, client_name, I2C_NAME_SIZE); return 0; diff --git a/drivers/hwmon/lm80.c b/drivers/hwmon/lm80.c index e85e062bbf32..35db0b97f912 100644 --- a/drivers/hwmon/lm80.c +++ b/drivers/hwmon/lm80.c @@ -586,7 +586,7 @@ static int lm80_detect(struct i2c_client *client, struct i2c_board_info *info) name = "lm80"; } - strlcpy(info->type, name, I2C_NAME_SIZE); + strscpy(info->type, name, I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/lm83.c b/drivers/hwmon/lm83.c index 905f5689f907..616449f2cc50 100644 --- a/drivers/hwmon/lm83.c +++ b/drivers/hwmon/lm83.c @@ -412,7 +412,7 @@ static int lm83_detect(struct i2c_client *client, return -ENODEV; } - strlcpy(info->type, name, I2C_NAME_SIZE); + strscpy(info->type, name, I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c index 88cf2012d34b..8d33c2484755 100644 --- a/drivers/hwmon/lm85.c +++ b/drivers/hwmon/lm85.c @@ -1539,7 +1539,7 @@ static int lm85_detect(struct i2c_client *client, struct i2c_board_info *info) if (!type_name) return -ENODEV; - strlcpy(info->type, type_name, I2C_NAME_SIZE); + strscpy(info->type, type_name, I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/lm87.c b/drivers/hwmon/lm87.c index 1750bc588856..818fb6195245 100644 --- a/drivers/hwmon/lm87.c +++ b/drivers/hwmon/lm87.c @@ -833,7 +833,7 @@ static int lm87_detect(struct i2c_client *client, struct i2c_board_info *info) return -ENODEV; } - strlcpy(info->type, name, I2C_NAME_SIZE); + strscpy(info->type, name, I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 221de01a327a..c151c0bf43f2 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -2547,7 +2547,7 @@ static int lm90_detect(struct i2c_client *client, struct i2c_board_info *info) return -ENODEV; } - strlcpy(info->type, name, I2C_NAME_SIZE); + strscpy(info->type, name, I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/lm92.c b/drivers/hwmon/lm92.c index 5bae6eedcaf1..2ff3044a677d 100644 --- a/drivers/hwmon/lm92.c +++ b/drivers/hwmon/lm92.c @@ -287,7 +287,7 @@ static int lm92_detect(struct i2c_client *new_client, else return -ENODEV; - strlcpy(info->type, "lm92", I2C_NAME_SIZE); + strscpy(info->type, "lm92", I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/lm93.c b/drivers/hwmon/lm93.c index dc67bf954b21..4cf50d5f4f59 100644 --- a/drivers/hwmon/lm93.c +++ b/drivers/hwmon/lm93.c @@ -2575,7 +2575,7 @@ static int lm93_detect(struct i2c_client *client, struct i2c_board_info *info) return -ENODEV; } - strlcpy(info->type, name, I2C_NAME_SIZE); + strscpy(info->type, name, I2C_NAME_SIZE); dev_dbg(&adapter->dev, "loading %s at %d, 0x%02x\n", client->name, i2c_adapter_id(client->adapter), client->addr); diff --git a/drivers/hwmon/lm95234.c b/drivers/hwmon/lm95234.c index ac169a994ae0..b4a9d0c223c4 100644 --- a/drivers/hwmon/lm95234.c +++ b/drivers/hwmon/lm95234.c @@ -644,7 +644,7 @@ static int lm95234_detect(struct i2c_client *client, if (val & model_mask) return -ENODEV; - strlcpy(info->type, name, I2C_NAME_SIZE); + strscpy(info->type, name, I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/lm95241.c b/drivers/hwmon/lm95241.c index 8ea46ff20be5..f1ed777a8735 100644 --- a/drivers/hwmon/lm95241.c +++ b/drivers/hwmon/lm95241.c @@ -389,7 +389,7 @@ static int lm95241_detect(struct i2c_client *new_client, } /* Fill the i2c board info */ - strlcpy(info->type, name, I2C_NAME_SIZE); + strscpy(info->type, name, I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/lm95245.c b/drivers/hwmon/lm95245.c index 29388fcf5f74..c433f0af2d31 100644 --- a/drivers/hwmon/lm95245.c +++ b/drivers/hwmon/lm95245.c @@ -461,7 +461,7 @@ static int lm95245_detect(struct i2c_client *new_client, return -ENODEV; } - strlcpy(info->type, name, I2C_NAME_SIZE); + strscpy(info->type, name, I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/max1619.c b/drivers/hwmon/max1619.c index eae9e68027bc..445c77197f69 100644 --- a/drivers/hwmon/max1619.c +++ b/drivers/hwmon/max1619.c @@ -241,7 +241,7 @@ static int max1619_detect(struct i2c_client *client, return -ENODEV; } - strlcpy(info->type, "max1619", I2C_NAME_SIZE); + strscpy(info->type, "max1619", I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/max1668.c b/drivers/hwmon/max1668.c index 78688e6cb87d..9f748973d6a3 100644 --- a/drivers/hwmon/max1668.c +++ b/drivers/hwmon/max1668.c @@ -386,7 +386,7 @@ static int max1668_detect(struct i2c_client *client, if (!type_name) return -ENODEV; - strlcpy(info->type, type_name, I2C_NAME_SIZE); + strscpy(info->type, type_name, I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/max31730.c b/drivers/hwmon/max31730.c index 23598b8b8793..9bdff881f59c 100644 --- a/drivers/hwmon/max31730.c +++ b/drivers/hwmon/max31730.c @@ -399,7 +399,7 @@ static int max31730_detect(struct i2c_client *client, return -ENODEV; } - strlcpy(info->type, "max31730", I2C_NAME_SIZE); + strscpy(info->type, "max31730", I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/max6639.c b/drivers/hwmon/max6639.c index 14bb7726f8d7..936861131d74 100644 --- a/drivers/hwmon/max6639.c +++ b/drivers/hwmon/max6639.c @@ -514,7 +514,7 @@ static int max6639_detect(struct i2c_client *client, if (dev_id != 0x58 || manu_id != 0x4D) return -ENODEV; - strlcpy(info->type, "max6639", I2C_NAME_SIZE); + strscpy(info->type, "max6639", I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/max6642.c b/drivers/hwmon/max6642.c index 699d265aae2e..47ea34ff78f3 100644 --- a/drivers/hwmon/max6642.c +++ b/drivers/hwmon/max6642.c @@ -148,7 +148,7 @@ static int max6642_detect(struct i2c_client *client, if ((reg_status & 0x2b) != 0x00) return -ENODEV; - strlcpy(info->type, "max6642", I2C_NAME_SIZE); + strscpy(info->type, "max6642", I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/nct7802.c b/drivers/hwmon/nct7802.c index d1eeef02b6dc..a175f8283695 100644 --- a/drivers/hwmon/nct7802.c +++ b/drivers/hwmon/nct7802.c @@ -1038,7 +1038,7 @@ static int nct7802_detect(struct i2c_client *client, if (reg < 0 || (reg & 0x3f)) return -ENODEV; - strlcpy(info->type, "nct7802", I2C_NAME_SIZE); + strscpy(info->type, "nct7802", I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/nct7904.c b/drivers/hwmon/nct7904.c index b1c837fc407a..ecc5db0011a3 100644 --- a/drivers/hwmon/nct7904.c +++ b/drivers/hwmon/nct7904.c @@ -798,7 +798,7 @@ static int nct7904_detect(struct i2c_client *client, (i2c_smbus_read_byte_data(client, BANK_SEL_REG) & 0xf8) != 0x00) return -ENODEV; - strlcpy(info->type, "nct7904", I2C_NAME_SIZE); + strscpy(info->type, "nct7904", I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/smsc47m192.c b/drivers/hwmon/smsc47m192.c index a5db15c087ae..70d2152234e2 100644 --- a/drivers/hwmon/smsc47m192.c +++ b/drivers/hwmon/smsc47m192.c @@ -582,7 +582,7 @@ static int smsc47m192_detect(struct i2c_client *client, return -ENODEV; } - strlcpy(info->type, "smsc47m192", I2C_NAME_SIZE); + strscpy(info->type, "smsc47m192", I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/stts751.c b/drivers/hwmon/stts751.c index 0ed28408aa07..2f67c6747ead 100644 --- a/drivers/hwmon/stts751.c +++ b/drivers/hwmon/stts751.c @@ -692,7 +692,7 @@ static int stts751_detect(struct i2c_client *new_client, } dev_dbg(&new_client->dev, "Chip %s detected", name); - strlcpy(info->type, stts751_id[0].name, I2C_NAME_SIZE); + strscpy(info->type, stts751_id[0].name, I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/thmc50.c b/drivers/hwmon/thmc50.c index 6a804f5036f4..81cdb012993c 100644 --- a/drivers/hwmon/thmc50.c +++ b/drivers/hwmon/thmc50.c @@ -352,7 +352,7 @@ static int thmc50_detect(struct i2c_client *client, pr_debug("thmc50: Detected %s (version %x, revision %x)\n", type_name, (revision >> 4) - 0xc, revision & 0xf); - strlcpy(info->type, type_name, I2C_NAME_SIZE); + strscpy(info->type, type_name, I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/tmp401.c b/drivers/hwmon/tmp401.c index cc0a1c219b1f..f358ba679626 100644 --- a/drivers/hwmon/tmp401.c +++ b/drivers/hwmon/tmp401.c @@ -671,7 +671,7 @@ static int tmp401_detect(struct i2c_client *client, if (reg > 15) return -ENODEV; - strlcpy(info->type, tmp401_id[kind].name, I2C_NAME_SIZE); + strscpy(info->type, tmp401_id[kind].name, I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/tmp421.c b/drivers/hwmon/tmp421.c index 1fd8d41d90c8..45fd7fb5ee01 100644 --- a/drivers/hwmon/tmp421.c +++ b/drivers/hwmon/tmp421.c @@ -353,7 +353,7 @@ static int tmp421_detect(struct i2c_client *client, return -ENODEV; } - strlcpy(info->type, tmp421_id[kind].name, I2C_NAME_SIZE); + strscpy(info->type, tmp421_id[kind].name, I2C_NAME_SIZE); dev_info(&adapter->dev, "Detected TI %s chip at 0x%02x\n", names[kind], client->addr); diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c index b3579721265f..35a847dfce68 100644 --- a/drivers/hwmon/w83781d.c +++ b/drivers/hwmon/w83781d.c @@ -1171,7 +1171,7 @@ w83781d_detect(struct i2c_client *client, struct i2c_board_info *info) if (isa) mutex_unlock(&isa->update_lock); - strlcpy(info->type, client_name, I2C_NAME_SIZE); + strscpy(info->type, client_name, I2C_NAME_SIZE); return 0; diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c index 80a9a78d7ce9..85ea408e28d1 100644 --- a/drivers/hwmon/w83791d.c +++ b/drivers/hwmon/w83791d.c @@ -1333,7 +1333,7 @@ static int w83791d_detect(struct i2c_client *client, if (val1 != 0x71 || val2 != 0x5c) return -ENODEV; - strlcpy(info->type, "w83791d", I2C_NAME_SIZE); + strscpy(info->type, "w83791d", I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/w83792d.c b/drivers/hwmon/w83792d.c index 31a1cdc30877..2c766a9209da 100644 --- a/drivers/hwmon/w83792d.c +++ b/drivers/hwmon/w83792d.c @@ -1346,7 +1346,7 @@ w83792d_detect(struct i2c_client *client, struct i2c_board_info *info) if (val1 != 0x7a || val2 != 0x5c) return -ENODEV; - strlcpy(info->type, "w83792d", I2C_NAME_SIZE); + strscpy(info->type, "w83792d", I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/w83793.c b/drivers/hwmon/w83793.c index 0a65d164c8f0..0c59162a7bf5 100644 --- a/drivers/hwmon/w83793.c +++ b/drivers/hwmon/w83793.c @@ -1636,7 +1636,7 @@ static int w83793_detect(struct i2c_client *client, if (chip_id != 0x7b) return -ENODEV; - strlcpy(info->type, "w83793", I2C_NAME_SIZE); + strscpy(info->type, "w83793", I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index 45b12c4287df..be5bb19e9afe 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -1967,7 +1967,7 @@ static int w83795_detect(struct i2c_client *client, else chip_name = "w83795g"; - strlcpy(info->type, chip_name, I2C_NAME_SIZE); + strscpy(info->type, chip_name, I2C_NAME_SIZE); dev_info(&adapter->dev, "Found %s rev. %c at 0x%02hx\n", chip_name, 'A' + (device_id & 0xf), address); diff --git a/drivers/hwmon/w83l785ts.c b/drivers/hwmon/w83l785ts.c index a41f989d66e2..56359350fd7f 100644 --- a/drivers/hwmon/w83l785ts.c +++ b/drivers/hwmon/w83l785ts.c @@ -157,7 +157,7 @@ static int w83l785ts_detect(struct i2c_client *client, return -ENODEV; } - strlcpy(info->type, "w83l785ts", I2C_NAME_SIZE); + strscpy(info->type, "w83l785ts", I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/w83l786ng.c b/drivers/hwmon/w83l786ng.c index 11ba23c1af85..2c4646fa8426 100644 --- a/drivers/hwmon/w83l786ng.c +++ b/drivers/hwmon/w83l786ng.c @@ -687,7 +687,7 @@ w83l786ng_detect(struct i2c_client *client, struct i2c_board_info *info) return -ENODEV; } - strlcpy(info->type, "w83l786ng", I2C_NAME_SIZE); + strscpy(info->type, "w83l786ng", I2C_NAME_SIZE); return 0; } -- cgit v1.2.3 From b7b568c2525b3a59a49eaf81ad4f283f0c25b5b6 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 26 Aug 2022 20:37:00 +0300 Subject: hwmon: (iio_hwmon) Make use of device properties Convert the module to be property provider agnostic and allow it to be used on non-OF platforms. Include mod_devicetable.h explicitly to replace the dropped of.h which included mod_devicetable.h indirectly. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220826173700.17395-1-andriy.shevchenko@linux.intel.com Signed-off-by: Guenter Roeck --- drivers/hwmon/iio_hwmon.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/iio_hwmon.c b/drivers/hwmon/iio_hwmon.c index 580a7d125b88..3aa40893fc09 100644 --- a/drivers/hwmon/iio_hwmon.c +++ b/drivers/hwmon/iio_hwmon.c @@ -6,11 +6,13 @@ #include #include +#include #include #include #include +#include + #include -#include #include #include #include @@ -149,8 +151,8 @@ static int iio_hwmon_probe(struct platform_device *pdev) st->attr_group.attrs = st->attrs; st->groups[0] = &st->attr_group; - if (dev->of_node) { - sname = devm_kasprintf(dev, GFP_KERNEL, "%pOFn", dev->of_node); + if (dev_fwnode(dev)) { + sname = devm_kasprintf(dev, GFP_KERNEL, "%pfwP", dev_fwnode(dev)); if (!sname) return -ENOMEM; strreplace(sname, '-', '_'); -- cgit v1.2.3 From 6ebab74e0973cb4c5bb60c8ce7c5d76943e180c5 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Mon, 22 Aug 2022 19:40:53 +0200 Subject: hwmon: (dell-smm) Improve warning messages When dell-smm-hwmon is loaded on a machine with a buggy BIOS with the option "force" being enabled, it wrongly prints that the buggy features where disabled. This may cause users to wrongly assume that the driver still protects them from these BIOS bugs even with "force" being enabled. Replace the messages with two messages each which are depending on the value of the "force" parameter. The messages which are being printed when "force" is not set use dev_notice() instead of dev_warn() since they only serve as a notice. Tested on a Dell Inspiron 3505. Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20220822174053.8750-3-W_Armin@gmx.de Signed-off-by: Guenter Roeck --- drivers/hwmon/dell-smm-hwmon.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/dell-smm-hwmon.c b/drivers/hwmon/dell-smm-hwmon.c index 7f8d95dd2717..1572b5416015 100644 --- a/drivers/hwmon/dell-smm-hwmon.c +++ b/drivers/hwmon/dell-smm-hwmon.c @@ -1355,15 +1355,21 @@ static int __init dell_smm_probe(struct platform_device *pdev) platform_set_drvdata(pdev, data); if (dmi_check_system(i8k_blacklist_fan_support_dmi_table)) { - dev_warn(&pdev->dev, "broken Dell BIOS detected, disallow fan support\n"); - if (!force) + if (!force) { + dev_notice(&pdev->dev, "Disabling fan support due to BIOS bugs\n"); data->disallow_fan_support = true; + } else { + dev_warn(&pdev->dev, "Enabling fan support despite BIOS bugs\n"); + } } if (dmi_check_system(i8k_blacklist_fan_type_dmi_table)) { - dev_warn(&pdev->dev, "broken Dell BIOS detected, disallow fan type call\n"); - if (!force) + if (!force) { + dev_notice(&pdev->dev, "Disabling fan type call due to BIOS bugs\n"); data->disallow_fan_type_call = true; + } else { + dev_warn(&pdev->dev, "Enabling fan type call despite BIOS bugs\n"); + } } strscpy(data->bios_version, i8k_get_dmi_data(DMI_BIOS_VERSION), -- cgit v1.2.3 From 907f2e4f1731d2b97ba3aa9f0590379768ba71e8 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Wed, 24 Aug 2022 12:25:13 +0200 Subject: hwmon: (sparx5) Use devm_clk_get_enabled() helper The devm_clk_get_enabled() helper: - calls devm_clk_get() - calls clk_prepare_enable() and registers what is needed in order to call clk_disable_unprepare() when needed, as a managed resource. This simplifies the code, the error handling paths and avoid the need of a dedicated function used with devm_add_action_or_reset(). Based on my test with allyesconfig, this reduces the .o size from: text data bss dec hex filename 2419 1472 0 3891 f33 drivers/hwmon/sparx5-temp.o down to: 2155 1472 0 3627 e2b drivers/hwmon/sparx5-temp.o Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/cfe4c965074b5ecbe03830b05e038b4594c7b970.1661336689.git.christophe.jaillet@wanadoo.fr Signed-off-by: Guenter Roeck --- drivers/hwmon/sparx5-temp.c | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/sparx5-temp.c b/drivers/hwmon/sparx5-temp.c index 98be48e3a22a..04fd8505e5d6 100644 --- a/drivers/hwmon/sparx5-temp.c +++ b/drivers/hwmon/sparx5-temp.c @@ -26,13 +26,6 @@ struct s5_hwmon { struct clk *clk; }; -static void s5_temp_clk_disable(void *data) -{ - struct clk *clk = data; - - clk_disable_unprepare(clk); -} - static void s5_temp_enable(struct s5_hwmon *hwmon) { u32 val = readl(hwmon->base + TEMP_CFG); @@ -113,7 +106,6 @@ static int s5_temp_probe(struct platform_device *pdev) { struct device *hwmon_dev; struct s5_hwmon *hwmon; - int ret; hwmon = devm_kzalloc(&pdev->dev, sizeof(*hwmon), GFP_KERNEL); if (!hwmon) @@ -123,19 +115,10 @@ static int s5_temp_probe(struct platform_device *pdev) if (IS_ERR(hwmon->base)) return PTR_ERR(hwmon->base); - hwmon->clk = devm_clk_get(&pdev->dev, NULL); + hwmon->clk = devm_clk_get_enabled(&pdev->dev, NULL); if (IS_ERR(hwmon->clk)) return PTR_ERR(hwmon->clk); - ret = clk_prepare_enable(hwmon->clk); - if (ret) - return ret; - - ret = devm_add_action_or_reset(&pdev->dev, s5_temp_clk_disable, - hwmon->clk); - if (ret) - return ret; - s5_temp_enable(hwmon); hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, -- cgit v1.2.3 From 2e2aa25cf5b0de2a2eb7da124c42184fc76d7afe Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 26 Aug 2022 20:26:40 +0300 Subject: hwmon: (pwm-fan) Replace OF specific call to PWM by plain one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no need to call OF specific devm_of_pwm_get() since the device node parameter duplicates in the device parameter. Hence we may safely replace it by plain devm_pwm_get() call. This allows to drop devm_of_pwm_get() as no more users will be. Signed-off-by: Andy Shevchenko Acked-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20220826172642.16404-1-andriy.shevchenko@linux.intel.com Signed-off-by: Guenter Roeck --- drivers/hwmon/pwm-fan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c index 6c08551d8d14..06fd1d75101d 100644 --- a/drivers/hwmon/pwm-fan.c +++ b/drivers/hwmon/pwm-fan.c @@ -302,7 +302,7 @@ static int pwm_fan_probe(struct platform_device *pdev) mutex_init(&ctx->lock); - ctx->pwm = devm_of_pwm_get(dev, dev->of_node, NULL); + ctx->pwm = devm_pwm_get(dev, NULL); if (IS_ERR(ctx->pwm)) return dev_err_probe(dev, PTR_ERR(ctx->pwm), "Could not get PWM\n"); -- cgit v1.2.3 From 5b38279e1a3f10c8046761b9732dccbfed78d614 Mon Sep 17 00:00:00 2001 From: Justin Ledford Date: Mon, 29 Aug 2022 19:59:30 +0000 Subject: hwmon: (max31790) add fanN_enable The MAX31790 has a tach input enable bit in each fan's configuration register. This is only enabled by the driver if RPM mode is selected, but the driver doesn't provide a way to independently enable tachometer input regardless of the regulator mode. By adding the fanN_enable sysfs files, we can decouple the tach input from the regulator mode. Also update the documentation. Signed-off-by: Justin Ledford Link: https://lore.kernel.org/r/20220829195930.2521755-1-justinledford@google.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/max31790.rst | 1 + drivers/hwmon/max31790.c | 38 +++++++++++++++++++++++++++++++------- 2 files changed, 32 insertions(+), 7 deletions(-) (limited to 'drivers/hwmon') diff --git a/Documentation/hwmon/max31790.rst b/Documentation/hwmon/max31790.rst index 7b097c3b9b90..33c5c7330efc 100644 --- a/Documentation/hwmon/max31790.rst +++ b/Documentation/hwmon/max31790.rst @@ -38,6 +38,7 @@ Sysfs entries fan[1-12]_input RO fan tachometer speed in RPM fan[1-12]_fault RO fan experienced fault fan[1-6]_target RW desired fan speed in RPM +fan[1-6]_enable RW enable or disable the tachometer input pwm[1-6]_enable RW regulator mode, 0=disabled (duty cycle=0%), 1=manual mode, 2=rpm mode pwm[1-6] RW read: current pwm duty cycle, write: target pwm duty cycle (0-255) diff --git a/drivers/hwmon/max31790.c b/drivers/hwmon/max31790.c index 7e9362f6dc29..20bf5ffadefe 100644 --- a/drivers/hwmon/max31790.c +++ b/drivers/hwmon/max31790.c @@ -202,6 +202,9 @@ static int max31790_read_fan(struct device *dev, u32 attr, int channel, } mutex_unlock(&data->update_lock); return 0; + case hwmon_fan_enable: + *val = !!(data->fan_config[channel] & MAX31790_FAN_CFG_TACH_INPUT_EN); + return 0; default: return -EOPNOTSUPP; } @@ -214,7 +217,7 @@ static int max31790_write_fan(struct device *dev, u32 attr, int channel, struct i2c_client *client = data->client; int target_count; int err = 0; - u8 bits; + u8 bits, fan_config; int sr; mutex_lock(&data->update_lock); @@ -243,6 +246,23 @@ static int max31790_write_fan(struct device *dev, u32 attr, int channel, MAX31790_REG_TARGET_COUNT(channel), data->target_count[channel]); break; + case hwmon_fan_enable: + fan_config = data->fan_config[channel]; + if (val == 0) { + fan_config &= ~MAX31790_FAN_CFG_TACH_INPUT_EN; + } else if (val == 1) { + fan_config |= MAX31790_FAN_CFG_TACH_INPUT_EN; + } else { + err = -EINVAL; + break; + } + if (fan_config != data->fan_config[channel]) { + err = i2c_smbus_write_byte_data(client, MAX31790_REG_FAN_CONFIG(channel), + fan_config); + if (!err) + data->fan_config[channel] = fan_config; + } + break; default: err = -EOPNOTSUPP; break; @@ -270,6 +290,10 @@ static umode_t max31790_fan_is_visible(const void *_data, u32 attr, int channel) !(fan_config & MAX31790_FAN_CFG_TACH_INPUT)) return 0644; return 0; + case hwmon_fan_enable: + if (channel < NR_CHANNEL) + return 0644; + return 0; default: return 0; } @@ -423,12 +447,12 @@ static umode_t max31790_is_visible(const void *data, static const struct hwmon_channel_info *max31790_info[] = { HWMON_CHANNEL_INFO(fan, - HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT, - HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT, - HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT, - HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT, - HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT, - HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT | HWMON_F_ENABLE, + HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT | HWMON_F_ENABLE, + HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT | HWMON_F_ENABLE, + HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT | HWMON_F_ENABLE, + HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT | HWMON_F_ENABLE, + HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT | HWMON_F_ENABLE, HWMON_F_INPUT | HWMON_F_FAULT, HWMON_F_INPUT | HWMON_F_FAULT, HWMON_F_INPUT | HWMON_F_FAULT, -- cgit v1.2.3 From 0d8400c5a2ce1595f31b2f99e3139cf5bc5f35fd Mon Sep 17 00:00:00 2001 From: Michael Shych Date: Wed, 10 Aug 2022 20:15:51 +0300 Subject: hwmon: (emc2305) add support for EMC2301/2/3/5 RPM-based PWM Fan Speed Controller. Add driver for Microchip EMC2301/2/3/5 RPM-based PWM Fan Speed Controller. Modify Makefile and Kconfig to support Microchip EMC2305 RPM-based PWM Fan Speed Controller. Signed-off-by: Michael Shych Reviewed-by: Vadim Pasternak Link: https://lore.kernel.org/r/20220810171552.56417-3-michaelsh@nvidia.com [groeck: Drop unnecessary () around DIV_ROUND_CLOSEST()] Signed-off-by: Guenter Roeck --- drivers/hwmon/Kconfig | 13 + drivers/hwmon/Makefile | 1 + drivers/hwmon/emc2305.c | 624 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 638 insertions(+) create mode 100644 drivers/hwmon/emc2305.c (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index f2898725ad41..d32f1705915c 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1785,6 +1785,19 @@ config SENSORS_EMC2103 This driver can also be built as a module. If so, the module will be called emc2103. +config SENSORS_EMC2305 + tristate "Microchip EMC2305 and compatible EMC2301/2/3" + depends on I2C + imply THERMAL + help + If you say yes here you get support for the Microchip EMC2305 + fan controller chips. + The Microchip EMC2305 is a fan controller for up to 5 fans. + Fan rotation speeds are reported in RPM. + + This driver can also be built as a module. If so, the module + will be called emc2305. + config SENSORS_EMC6W201 tristate "SMSC EMC6W201" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index f62c5c36b276..8b7a590091dd 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -69,6 +69,7 @@ obj-$(CONFIG_SENSORS_DS620) += ds620.o obj-$(CONFIG_SENSORS_DS1621) += ds1621.o obj-$(CONFIG_SENSORS_EMC1403) += emc1403.o obj-$(CONFIG_SENSORS_EMC2103) += emc2103.o +obj-$(CONFIG_SENSORS_EMC2305) += emc2305.o obj-$(CONFIG_SENSORS_EMC6W201) += emc6w201.o obj-$(CONFIG_SENSORS_F71805F) += f71805f.o obj-$(CONFIG_SENSORS_F71882FG) += f71882fg.o diff --git a/drivers/hwmon/emc2305.c b/drivers/hwmon/emc2305.c new file mode 100644 index 000000000000..9f6546ea3d69 --- /dev/null +++ b/drivers/hwmon/emc2305.c @@ -0,0 +1,624 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Hardware monitoring driver for EMC2305 fan controller + * + * Copyright (C) 2022 Nvidia Technologies Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include + +static const unsigned short +emc2305_normal_i2c[] = { 0x27, 0x2c, 0x2d, 0x2e, 0x2f, 0x4c, 0x4d, I2C_CLIENT_END }; + +#define EMC2305_REG_DRIVE_FAIL_STATUS 0x27 +#define EMC2305_REG_DEVICE 0xfd +#define EMC2305_REG_VENDOR 0xfe +#define EMC2305_FAN_MAX 0xff +#define EMC2305_FAN_MIN 0x00 +#define EMC2305_FAN_MAX_STATE 10 +#define EMC2305_DEVICE 0x34 +#define EMC2305_VENDOR 0x5d +#define EMC2305_REG_PRODUCT_ID 0xfd +#define EMC2305_TACH_REGS_UNUSE_BITS 3 +#define EMC2305_TACH_CNT_MULTIPLIER 0x02 +#define EMC2305_TACH_RANGE_MIN 480 + +#define EMC2305_PWM_DUTY2STATE(duty, max_state, pwm_max) \ + DIV_ROUND_CLOSEST((duty) * (max_state), (pwm_max)) +#define EMC2305_PWM_STATE2DUTY(state, max_state, pwm_max) \ + DIV_ROUND_CLOSEST((state) * (pwm_max), (max_state)) + +/* + * Factor by equations [2] and [3] from data sheet; valid for fans where the number of edges + * equal (poles * 2 + 1). + */ +#define EMC2305_RPM_FACTOR 3932160 + +#define EMC2305_REG_FAN_DRIVE(n) (0x30 + 0x10 * (n)) +#define EMC2305_REG_FAN_MIN_DRIVE(n) (0x38 + 0x10 * (n)) +#define EMC2305_REG_FAN_TACH(n) (0x3e + 0x10 * (n)) + +enum emc230x_product_id { + EMC2305 = 0x34, + EMC2303 = 0x35, + EMC2302 = 0x36, + EMC2301 = 0x37, +}; + +static const struct i2c_device_id emc2305_ids[] = { + { "emc2305", 0 }, + { "emc2303", 0 }, + { "emc2302", 0 }, + { "emc2301", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, emc2305_ids); + +/** + * @cdev: cooling device; + * @curr_state: cooling current state; + * @last_hwmon_state: last cooling state updated by hwmon subsystem; + * @last_thermal_state: last cooling state updated by thermal subsystem; + * + * The 'last_hwmon_state' and 'last_thermal_state' fields are provided to support fan low limit + * speed feature. The purpose of this feature is to provides ability to limit fan speed + * according to some system wise considerations, like absence of some replaceable units (PSU or + * line cards), high system ambient temperature, unreliable transceivers temperature sensing or + * some other factors which indirectly impacts system's airflow + * Fan low limit feature is supported through 'hwmon' interface: 'hwmon' 'pwm' attribute is + * used for setting low limit for fan speed in case 'thermal' subsystem is configured in + * kernel. In this case setting fan speed through 'hwmon' will never let the 'thermal' + * subsystem to select a lower duty cycle than the duty cycle selected with the 'pwm' + * attribute. + * From other side, fan speed is to be updated in hardware through 'pwm' only in case the + * requested fan speed is above last speed set by 'thermal' subsystem, otherwise requested fan + * speed will be just stored with no PWM update. + */ +struct emc2305_cdev_data { + struct thermal_cooling_device *cdev; + unsigned int cur_state; + unsigned long last_hwmon_state; + unsigned long last_thermal_state; +}; + +/** + * @client: i2c client; + * @hwmon_dev: hwmon device; + * @max_state: maximum cooling state of the cooling device; + * @pwm_num: number of PWM channels; + * @pwm_separate: separate PWM settings for every channel; + * @pwm_min: array of minimum PWM per channel; + * @cdev_data: array of cooling devices data; + */ +struct emc2305_data { + struct i2c_client *client; + struct device *hwmon_dev; + u8 max_state; + u8 pwm_num; + bool pwm_separate; + u8 pwm_min[EMC2305_PWM_MAX]; + struct emc2305_cdev_data cdev_data[EMC2305_PWM_MAX]; +}; + +static char *emc2305_fan_name[] = { + "emc2305_fan", + "emc2305_fan1", + "emc2305_fan2", + "emc2305_fan3", + "emc2305_fan4", + "emc2305_fan5", +}; + +static void emc2305_unset_tz(struct device *dev); + +static int emc2305_get_max_channel(const struct emc2305_data *data) +{ + return data->pwm_num; +} + +static int emc2305_get_cdev_idx(struct thermal_cooling_device *cdev) +{ + struct emc2305_data *data = cdev->devdata; + size_t len = strlen(cdev->type); + int ret; + + if (len <= 0) + return -EINVAL; + + /* + * Returns index of cooling device 0..4 in case of separate PWM setting. + * Zero index is used in case of one common PWM setting. + * If the mode is not set as pwm_separate, all PWMs are to be bound + * to the common thermal zone and should work at the same speed + * to perform cooling for the same thermal junction. + * Otherwise, return specific channel that will be used in bound + * related PWM to the thermal zone. + */ + if (!data->pwm_separate) + return 0; + + ret = cdev->type[len - 1]; + switch (ret) { + case '1' ... '5': + return ret - '1'; + default: + break; + } + return -EINVAL; +} + +static int emc2305_get_cur_state(struct thermal_cooling_device *cdev, unsigned long *state) +{ + int cdev_idx; + struct emc2305_data *data = cdev->devdata; + + cdev_idx = emc2305_get_cdev_idx(cdev); + if (cdev_idx < 0) + return cdev_idx; + + *state = data->cdev_data[cdev_idx].cur_state; + return 0; +} + +static int emc2305_get_max_state(struct thermal_cooling_device *cdev, unsigned long *state) +{ + struct emc2305_data *data = cdev->devdata; + *state = data->max_state; + return 0; +} + +static int emc2305_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) +{ + int cdev_idx, ret; + struct emc2305_data *data = cdev->devdata; + struct i2c_client *client = data->client; + u8 val, i; + + if (state > data->max_state) + return -EINVAL; + + cdev_idx = emc2305_get_cdev_idx(cdev); + if (cdev_idx < 0) + return cdev_idx; + + /* Save thermal state. */ + data->cdev_data[cdev_idx].last_thermal_state = state; + state = max_t(unsigned long, state, data->cdev_data[cdev_idx].last_hwmon_state); + + val = EMC2305_PWM_STATE2DUTY(state, data->max_state, EMC2305_FAN_MAX); + if (val > EMC2305_FAN_MAX) + return -EINVAL; + + data->cdev_data[cdev_idx].cur_state = state; + if (data->pwm_separate) { + ret = i2c_smbus_write_byte_data(client, EMC2305_REG_FAN_DRIVE(cdev_idx), val); + if (ret < 0) + return ret; + } else { + /* + * Set the same PWM value in all channels + * if common PWM channel is used. + */ + for (i = 0; i < data->pwm_num; i++) { + ret = i2c_smbus_write_byte_data(client, EMC2305_REG_FAN_DRIVE(i), val); + if (ret < 0) + return ret; + } + } + + return 0; +} + +static const struct thermal_cooling_device_ops emc2305_cooling_ops = { + .get_max_state = emc2305_get_max_state, + .get_cur_state = emc2305_get_cur_state, + .set_cur_state = emc2305_set_cur_state, +}; + +static int emc2305_show_fault(struct device *dev, int channel) +{ + struct emc2305_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + int status_reg; + + status_reg = i2c_smbus_read_byte_data(client, EMC2305_REG_DRIVE_FAIL_STATUS); + if (status_reg < 0) + return status_reg; + + return status_reg & (1 << channel) ? 1 : 0; +} + +static int emc2305_show_fan(struct device *dev, int channel) +{ + struct emc2305_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + int ret; + + ret = i2c_smbus_read_word_swapped(client, EMC2305_REG_FAN_TACH(channel)); + if (ret <= 0) + return ret; + + ret = ret >> EMC2305_TACH_REGS_UNUSE_BITS; + ret = EMC2305_RPM_FACTOR / ret; + if (ret <= EMC2305_TACH_RANGE_MIN) + return 0; + + return ret * EMC2305_TACH_CNT_MULTIPLIER; +} + +static int emc2305_show_pwm(struct device *dev, int channel) +{ + struct emc2305_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + + return i2c_smbus_read_byte_data(client, EMC2305_REG_FAN_DRIVE(channel)); +} + +static int emc2305_set_pwm(struct device *dev, long val, int channel) +{ + struct emc2305_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + int ret; + + if (val < data->pwm_min[channel] || val > EMC2305_FAN_MAX) + return -EINVAL; + + ret = i2c_smbus_write_byte_data(client, EMC2305_REG_FAN_DRIVE(channel), val); + if (ret < 0) + return ret; + data->cdev_data[channel].cur_state = EMC2305_PWM_DUTY2STATE(val, data->max_state, + EMC2305_FAN_MAX); + return 0; +} + +static int emc2305_set_single_tz(struct device *dev, int idx) +{ + struct emc2305_data *data = dev_get_drvdata(dev); + long pwm; + int i, cdev_idx, ret; + + cdev_idx = (idx) ? idx - 1 : 0; + pwm = data->pwm_min[cdev_idx]; + + data->cdev_data[cdev_idx].cdev = + thermal_cooling_device_register(emc2305_fan_name[idx], data, + &emc2305_cooling_ops); + + if (IS_ERR(data->cdev_data[cdev_idx].cdev)) { + dev_err(dev, "Failed to register cooling device %s\n", emc2305_fan_name[idx]); + return PTR_ERR(data->cdev_data[cdev_idx].cdev); + } + /* Set minimal PWM speed. */ + if (data->pwm_separate) { + ret = emc2305_set_pwm(dev, pwm, cdev_idx); + if (ret < 0) + return ret; + } else { + for (i = 0; i < data->pwm_num; i++) { + ret = emc2305_set_pwm(dev, pwm, i); + if (ret < 0) + return ret; + } + } + data->cdev_data[cdev_idx].cur_state = + EMC2305_PWM_DUTY2STATE(data->pwm_min[cdev_idx], data->max_state, + EMC2305_FAN_MAX); + data->cdev_data[cdev_idx].last_hwmon_state = + EMC2305_PWM_DUTY2STATE(data->pwm_min[cdev_idx], data->max_state, + EMC2305_FAN_MAX); + return 0; +} + +static int emc2305_set_tz(struct device *dev) +{ + struct emc2305_data *data = dev_get_drvdata(dev); + int i, ret; + + if (!data->pwm_separate) + return emc2305_set_single_tz(dev, 0); + + for (i = 0; i < data->pwm_num; i++) { + ret = emc2305_set_single_tz(dev, i + 1); + if (ret) + goto thermal_cooling_device_register_fail; + } + return 0; + +thermal_cooling_device_register_fail: + emc2305_unset_tz(dev); + return ret; +} + +static void emc2305_unset_tz(struct device *dev) +{ + struct emc2305_data *data = dev_get_drvdata(dev); + int i; + + /* Unregister cooling device. */ + for (i = 0; i < EMC2305_PWM_MAX; i++) + if (data->cdev_data[i].cdev) + thermal_cooling_device_unregister(data->cdev_data[i].cdev); +} + +static umode_t +emc2305_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr, int channel) +{ + int max_channel = emc2305_get_max_channel(data); + + /* Don't show channels which are not physically connected. */ + if (channel >= max_channel) + return 0; + switch (type) { + case hwmon_fan: + switch (attr) { + case hwmon_fan_input: + return 0444; + case hwmon_fan_fault: + return 0444; + default: + break; + } + break; + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_input: + return 0644; + default: + break; + } + break; + default: + break; + } + + return 0; +}; + +static int +emc2305_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long val) +{ + struct emc2305_data *data = dev_get_drvdata(dev); + int cdev_idx; + + switch (type) { + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_input: + /* If thermal is configured - handle PWM limit setting. */ + if (IS_REACHABLE(CONFIG_THERMAL)) { + if (data->pwm_separate) + cdev_idx = channel; + else + cdev_idx = 0; + data->cdev_data[cdev_idx].last_hwmon_state = + EMC2305_PWM_DUTY2STATE(val, data->max_state, + EMC2305_FAN_MAX); + /* + * Update PWM only in case requested state is not less than the + * last thermal state. + */ + if (data->cdev_data[cdev_idx].last_hwmon_state >= + data->cdev_data[cdev_idx].last_thermal_state) + return emc2305_set_cur_state(data->cdev_data[cdev_idx].cdev, + data->cdev_data[cdev_idx].last_hwmon_state); + return 0; + } + return emc2305_set_pwm(dev, val, channel); + default: + break; + } + break; + default: + break; + } + + return -EOPNOTSUPP; +}; + +static int +emc2305_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val) +{ + int ret; + + switch (type) { + case hwmon_fan: + switch (attr) { + case hwmon_fan_input: + ret = emc2305_show_fan(dev, channel); + if (ret < 0) + return ret; + *val = ret; + return 0; + case hwmon_fan_fault: + ret = emc2305_show_fault(dev, channel); + if (ret < 0) + return ret; + *val = ret; + return 0; + default: + break; + } + break; + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_input: + ret = emc2305_show_pwm(dev, channel); + if (ret < 0) + return ret; + *val = ret; + return 0; + default: + break; + } + break; + default: + break; + } + + return -EOPNOTSUPP; +}; + +static const struct hwmon_ops emc2305_ops = { + .is_visible = emc2305_is_visible, + .read = emc2305_read, + .write = emc2305_write, +}; + +static const struct hwmon_channel_info *emc2305_info[] = { + HWMON_CHANNEL_INFO(fan, + HWMON_F_INPUT | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_FAULT), + HWMON_CHANNEL_INFO(pwm, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT), + NULL +}; + +static const struct hwmon_chip_info emc2305_chip_info = { + .ops = &emc2305_ops, + .info = emc2305_info, +}; + +static int emc2305_identify(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct emc2305_data *data = i2c_get_clientdata(client); + int ret; + + ret = i2c_smbus_read_byte_data(client, EMC2305_REG_PRODUCT_ID); + if (ret < 0) + return ret; + + switch (ret) { + case EMC2305: + data->pwm_num = 5; + break; + case EMC2303: + data->pwm_num = 3; + break; + case EMC2302: + data->pwm_num = 2; + break; + case EMC2301: + data->pwm_num = 1; + break; + default: + return -ENODEV; + } + + return 0; +} + +static int emc2305_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = client->adapter; + struct device *dev = &client->dev; + struct emc2305_data *data; + struct emc2305_platform_data *pdata; + int vendor, device; + int ret; + int i; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) + return -ENODEV; + + vendor = i2c_smbus_read_byte_data(client, EMC2305_REG_VENDOR); + if (vendor != EMC2305_VENDOR) + return -ENODEV; + + device = i2c_smbus_read_byte_data(client, EMC2305_REG_DEVICE); + if (device != EMC2305_DEVICE) + return -ENODEV; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + i2c_set_clientdata(client, data); + data->client = client; + + ret = emc2305_identify(dev); + if (ret) + return ret; + + pdata = dev_get_platdata(&client->dev); + if (pdata) { + if (!pdata->max_state || pdata->max_state > EMC2305_FAN_MAX_STATE) + return -EINVAL; + data->max_state = pdata->max_state; + /* + * Validate a number of active PWM channels. Note that + * configured number can be less than the actual maximum + * supported by the device. + */ + if (!pdata->pwm_num || pdata->pwm_num > EMC2305_PWM_MAX) + return -EINVAL; + data->pwm_num = pdata->pwm_num; + data->pwm_separate = pdata->pwm_separate; + for (i = 0; i < EMC2305_PWM_MAX; i++) + data->pwm_min[i] = pdata->pwm_min[i]; + } else { + data->max_state = EMC2305_FAN_MAX_STATE; + data->pwm_separate = false; + for (i = 0; i < EMC2305_PWM_MAX; i++) + data->pwm_min[i] = EMC2305_FAN_MIN; + } + + data->hwmon_dev = devm_hwmon_device_register_with_info(dev, "emc2305", data, + &emc2305_chip_info, NULL); + if (IS_ERR(data->hwmon_dev)) + return PTR_ERR(data->hwmon_dev); + + if (IS_REACHABLE(CONFIG_THERMAL)) { + ret = emc2305_set_tz(dev); + if (ret != 0) + return ret; + } + + for (i = 0; i < data->pwm_num; i++) { + ret = i2c_smbus_write_byte_data(client, EMC2305_REG_FAN_MIN_DRIVE(i), + data->pwm_min[i]); + if (ret < 0) + return ret; + } + + return 0; +} + +static int emc2305_remove(struct i2c_client *client) +{ + struct device *dev = &client->dev; + + if (IS_REACHABLE(CONFIG_THERMAL)) + emc2305_unset_tz(dev); + return 0; +} + +static struct i2c_driver emc2305_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "emc2305", + }, + .probe = emc2305_probe, + .remove = emc2305_remove, + .id_table = emc2305_ids, + .address_list = emc2305_normal_i2c, +}; + +module_i2c_driver(emc2305_driver); + +MODULE_AUTHOR("Nvidia"); +MODULE_DESCRIPTION("Microchip EMC2305 fan controller driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 4444a06981af66a49cf0cd08fec9759e8dd0a0fc Mon Sep 17 00:00:00 2001 From: Jiapeng Chong Date: Thu, 1 Sep 2022 10:23:32 +0800 Subject: hwmon: (emc2305) Remove unused including ./drivers/hwmon/emc2305.c: 14 linux/version.h not needed. Link: https://bugzilla.openanolis.cn/show_bug.cgi?id=2024 Reported-by: Abaci Robot Signed-off-by: Jiapeng Chong Link: https://lore.kernel.org/r/20220901022332.40248-1-jiapeng.chong@linux.alibaba.com Signed-off-by: Guenter Roeck --- drivers/hwmon/emc2305.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/emc2305.c b/drivers/hwmon/emc2305.c index 9f6546ea3d69..ee5ed24feab5 100644 --- a/drivers/hwmon/emc2305.c +++ b/drivers/hwmon/emc2305.c @@ -11,7 +11,6 @@ #include #include #include -#include static const unsigned short emc2305_normal_i2c[] = { 0x27, 0x2c, 0x2d, 0x2e, 0x2f, 0x4c, 0x4d, I2C_CLIENT_END }; -- cgit v1.2.3 From 0fed840c7562d9dcc5bf8a91744b079e952683d1 Mon Sep 17 00:00:00 2001 From: Alexandru Gagniuc Date: Tue, 6 Sep 2022 20:54:04 -0500 Subject: hwmon: (tps23861) reduce count of i2c transactions for port_status When reading the 'port_status' debugfs entry, some I2C registers were read more than once. This looks inefficient in an I2C trace. To reduce I2C traffic, update tps23861_port_status_show() to only read each register once. Indexing the port number from 0 instead of 1 also allows simplifying things a bit. Signed-off-by: Alexandru Gagniuc Link: https://lore.kernel.org/r/20220907015405.16547-1-mr.nuke.me@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/tps23861.c | 79 +++++++++++++++--------------------------------- 1 file changed, 24 insertions(+), 55 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/tps23861.c b/drivers/hwmon/tps23861.c index f7c59ff7ae8e..019009b71a90 100644 --- a/drivers/hwmon/tps23861.c +++ b/drivers/hwmon/tps23861.c @@ -372,29 +372,12 @@ static const struct hwmon_chip_info tps23861_chip_info = { .info = tps23861_info, }; -static char *tps23861_port_operating_mode(struct tps23861_data *data, int port) +static char *port_operating_mode_string(uint8_t mode_reg, unsigned int port) { - unsigned int regval; - int mode; + unsigned int mode = ~0; - regmap_read(data->regmap, OPERATING_MODE, ®val); - - switch (port) { - case 1: - mode = FIELD_GET(OPERATING_MODE_PORT_1_MASK, regval); - break; - case 2: - mode = FIELD_GET(OPERATING_MODE_PORT_2_MASK, regval); - break; - case 3: - mode = FIELD_GET(OPERATING_MODE_PORT_3_MASK, regval); - break; - case 4: - mode = FIELD_GET(OPERATING_MODE_PORT_4_MASK, regval); - break; - default: - mode = -EINVAL; - } + if (port < TPS23861_NUM_PORTS) + mode = (mode_reg >> (2 * port)) & OPERATING_MODE_PORT_1_MASK; switch (mode) { case OPERATING_MODE_OFF: @@ -410,15 +393,9 @@ static char *tps23861_port_operating_mode(struct tps23861_data *data, int port) } } -static char *tps23861_port_detect_status(struct tps23861_data *data, int port) +static char *port_detect_status_string(uint8_t status_reg) { - unsigned int regval; - - regmap_read(data->regmap, - PORT_1_STATUS + (port - 1), - ®val); - - switch (FIELD_GET(PORT_STATUS_DETECT_MASK, regval)) { + switch (FIELD_GET(PORT_STATUS_DETECT_MASK, status_reg)) { case PORT_DETECT_UNKNOWN: return "Unknown device"; case PORT_DETECT_SHORT: @@ -448,15 +425,9 @@ static char *tps23861_port_detect_status(struct tps23861_data *data, int port) } } -static char *tps23861_port_class_status(struct tps23861_data *data, int port) +static char *port_class_status_string(uint8_t status_reg) { - unsigned int regval; - - regmap_read(data->regmap, - PORT_1_STATUS + (port - 1), - ®val); - - switch (FIELD_GET(PORT_STATUS_CLASS_MASK, regval)) { + switch (FIELD_GET(PORT_STATUS_CLASS_MASK, status_reg)) { case PORT_CLASS_UNKNOWN: return "Unknown"; case PORT_CLASS_RESERVED: @@ -479,16 +450,9 @@ static char *tps23861_port_class_status(struct tps23861_data *data, int port) } } -static char *tps23861_port_poe_plus_status(struct tps23861_data *data, int port) +static char *port_poe_plus_status_string(uint8_t poe_plus, unsigned int port) { - unsigned int regval; - - regmap_read(data->regmap, POE_PLUS, ®val); - - if (BIT(port + 3) & regval) - return "Yes"; - else - return "No"; + return (BIT(port + 4) & poe_plus) ? "Yes" : "No"; } static int tps23861_port_resistance(struct tps23861_data *data, int port) @@ -497,7 +461,7 @@ static int tps23861_port_resistance(struct tps23861_data *data, int port) __le16 regval; regmap_bulk_read(data->regmap, - PORT_1_RESISTANCE_LSB + PORT_N_RESISTANCE_LSB_OFFSET * (port - 1), + PORT_1_RESISTANCE_LSB + PORT_N_RESISTANCE_LSB_OFFSET * port, ®val, 2); @@ -517,14 +481,19 @@ static int tps23861_port_resistance(struct tps23861_data *data, int port) static int tps23861_port_status_show(struct seq_file *s, void *data) { struct tps23861_data *priv = s->private; - int i; - - for (i = 1; i < TPS23861_NUM_PORTS + 1; i++) { - seq_printf(s, "Port: \t\t%d\n", i); - seq_printf(s, "Operating mode: %s\n", tps23861_port_operating_mode(priv, i)); - seq_printf(s, "Detected: \t%s\n", tps23861_port_detect_status(priv, i)); - seq_printf(s, "Class: \t\t%s\n", tps23861_port_class_status(priv, i)); - seq_printf(s, "PoE Plus: \t%s\n", tps23861_port_poe_plus_status(priv, i)); + unsigned int i, mode, poe_plus, status; + + regmap_read(priv->regmap, OPERATING_MODE, &mode); + regmap_read(priv->regmap, POE_PLUS, &poe_plus); + + for (i = 0; i < TPS23861_NUM_PORTS; i++) { + regmap_read(priv->regmap, PORT_1_STATUS + i, &status); + + seq_printf(s, "Port: \t\t%d\n", i + 1); + seq_printf(s, "Operating mode: %s\n", port_operating_mode_string(mode, i)); + seq_printf(s, "Detected: \t%s\n", port_detect_status_string(status)); + seq_printf(s, "Class: \t\t%s\n", port_class_status_string(status)); + seq_printf(s, "PoE Plus: \t%s\n", port_poe_plus_status_string(poe_plus, i)); seq_printf(s, "Resistance: \t%d\n", tps23861_port_resistance(priv, i)); seq_putc(s, '\n'); } -- cgit v1.2.3 From 856361b397439157f7f60fd21efb522bb11f537a Mon Sep 17 00:00:00 2001 From: Alexandru Gagniuc Date: Tue, 6 Sep 2022 20:54:05 -0500 Subject: hwmon: (tps23861) create unique debugfs directory per device On systems with more than one tps23861, creating the debugfs directory for additional devices fails with debugfs: Directory 'tps23861' with parent '/' already present! To resolve this, include the hwmon device name in the directory name. Since the name is unique, this guarantees that the debugfs directory is unique. Signed-off-by: Alexandru Gagniuc Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20220907015405.16547-2-mr.nuke.me@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/tps23861.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/tps23861.c b/drivers/hwmon/tps23861.c index 019009b71a90..a3d7d6b75a26 100644 --- a/drivers/hwmon/tps23861.c +++ b/drivers/hwmon/tps23861.c @@ -503,9 +503,17 @@ static int tps23861_port_status_show(struct seq_file *s, void *data) DEFINE_SHOW_ATTRIBUTE(tps23861_port_status); -static void tps23861_init_debugfs(struct tps23861_data *data) +static void tps23861_init_debugfs(struct tps23861_data *data, + struct device *hwmon_dev) { - data->debugfs_dir = debugfs_create_dir(data->client->name, NULL); + const char *debugfs_name; + + debugfs_name = devm_kasprintf(&data->client->dev, GFP_KERNEL, "%s-%s", + data->client->name, dev_name(hwmon_dev)); + if (!debugfs_name) + return; + + data->debugfs_dir = debugfs_create_dir(debugfs_name, NULL); debugfs_create_file("port_status", 0400, @@ -554,7 +562,7 @@ static int tps23861_probe(struct i2c_client *client) if (IS_ERR(hwmon_dev)) return PTR_ERR(hwmon_dev); - tps23861_init_debugfs(data); + tps23861_init_debugfs(data, hwmon_dev); return 0; } -- cgit v1.2.3 From aed80bb91de89d1d484f659056d740047c54e065 Mon Sep 17 00:00:00 2001 From: Aleksa Savic Date: Wed, 7 Sep 2022 12:07:39 +0200 Subject: hwmon: (aquacomputer_d5next) Add support for Aquacomputer High Flow Next Extend aquacomputer_d5next driver to expose various hardware sensors of the Aquacomputer High Flow Next flow sensor, which communicates through a proprietary USB HID protocol. The High Flow Next exposes +5V voltages, water quality, conductivity and flow readings. A temperature sensor can be connected to it, in which case it provides its reading and an estimation of the dissipated/absorbed power in the liquid cooling loop. Additionally, serial number and firmware version are exposed through debugfs. Registry offsets were discovered and tested by users on Github [1] [2]. [1] https://github.com/aleksamagicka/aquacomputer_d5next-hwmon/issues/8 [2] https://github.com/aleksamagicka/aquacomputer_d5next-hwmon/pull/34 Signed-off-by: Aleksa Savic Link: https://lore.kernel.org/r/20220907100739.806571-1-savicaleksa83@gmail.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/aquacomputer_d5next.rst | 5 ++ drivers/hwmon/Kconfig | 6 +- drivers/hwmon/aquacomputer_d5next.c | 91 ++++++++++++++++++++++++++++- 3 files changed, 96 insertions(+), 6 deletions(-) (limited to 'drivers/hwmon') diff --git a/Documentation/hwmon/aquacomputer_d5next.rst b/Documentation/hwmon/aquacomputer_d5next.rst index b63a78d47624..e238533b5fe0 100644 --- a/Documentation/hwmon/aquacomputer_d5next.rst +++ b/Documentation/hwmon/aquacomputer_d5next.rst @@ -10,6 +10,7 @@ Supported devices: * Aquacomputer Farbwerk 360 RGB controller * Aquacomputer Octo fan controller * Aquacomputer Quadro fan controller +* Aquacomputer High Flow Next sensor Author: Aleksa Savic @@ -43,6 +44,10 @@ voltage and current. The Farbwerk and Farbwerk 360 expose four temperature sensors. Additionally, sixteen virtual temperature sensors of the Farbwerk 360 are exposed. +The High Flow Next exposes +5V voltages, water quality, conductivity and flow readings. +A temperature sensor can be connected to it, in which case it provides its reading +and an estimation of the dissipated/absorbed power in the liquid cooling loop. + Depending on the device, not all sysfs and debugfs entries will be available. Writing to virtual temperature sensors is not currently supported. diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index d32f1705915c..f604f599a341 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -257,14 +257,14 @@ config SENSORS_AHT10 will be called aht10. config SENSORS_AQUACOMPUTER_D5NEXT - tristate "Aquacomputer D5 Next, Octo, Quadro, Farbwerk, and Farbwerk 360" + tristate "Aquacomputer D5 Next, Octo, Quadro, Farbwerk, Farbwerk 360, High Flow Next" depends on USB_HID select CRC16 help If you say yes here you get support for sensors and fans of the Aquacomputer D5 Next watercooling pump, Octo and Quadro fan - controllers, Farbwerk and Farbwerk 360 RGB controllers, where - available. + controllers, Farbwerk and Farbwerk 360 RGB controllers, High Flow + Next sensor, where available. This driver can also be built as a module. If so, the module will be called aquacomputer_d5next. diff --git a/drivers/hwmon/aquacomputer_d5next.c b/drivers/hwmon/aquacomputer_d5next.c index 77cc8f50b8af..c51a2678f0eb 100644 --- a/drivers/hwmon/aquacomputer_d5next.c +++ b/drivers/hwmon/aquacomputer_d5next.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0+ /* * hwmon driver for Aquacomputer devices (D5 Next, Farbwerk, Farbwerk 360, Octo, - * Quadro) + * Quadro, High Flow Next) * * Aquacomputer devices send HID reports (with ID 0x01) every second to report * sensor values. @@ -26,15 +26,17 @@ #define USB_PRODUCT_ID_D5NEXT 0xf00e #define USB_PRODUCT_ID_FARBWERK360 0xf010 #define USB_PRODUCT_ID_OCTO 0xf011 +#define USB_PRODUCT_ID_HIGHFLOWNEXT 0xf012 -enum kinds { d5next, farbwerk, farbwerk360, octo, quadro }; +enum kinds { d5next, farbwerk, farbwerk360, octo, quadro, highflownext }; static const char *const aqc_device_names[] = { [d5next] = "d5next", [farbwerk] = "farbwerk", [farbwerk360] = "farbwerk360", [octo] = "octo", - [quadro] = "quadro" + [quadro] = "quadro", + [highflownext] = "highflownext" }; #define DRIVER_NAME "aquacomputer_d5next" @@ -120,6 +122,16 @@ static u8 quadro_sensor_fan_offsets[] = { 0x70, 0x7D, 0x8A, 0x97 }; /* Fan speed registers in Quadro control report (from 0-100%) */ static u16 quadro_ctrl_fan_offsets[] = { 0x37, 0x8c, 0xe1, 0x136 }; +/* Register offsets for the High Flow Next */ +#define HIGHFLOWNEXT_NUM_SENSORS 2 +#define HIGHFLOWNEXT_SENSOR_START 85 +#define HIGHFLOWNEXT_FLOW 81 +#define HIGHFLOWNEXT_WATER_QUALITY 89 +#define HIGHFLOWNEXT_POWER 91 +#define HIGHFLOWNEXT_CONDUCTIVITY 95 +#define HIGHFLOWNEXT_5V_VOLTAGE 97 +#define HIGHFLOWNEXT_5V_VOLTAGE_USB 99 + /* Labels for D5 Next */ static const char *const label_d5next_temp[] = { "Coolant temp" @@ -228,6 +240,27 @@ static const char *const label_quadro_speeds[] = { "Flow speed [dL/h]" }; +/* Labels for High Flow Next */ +static const char *const label_highflownext_temp_sensors[] = { + "Coolant temp", + "External sensor" +}; + +static const char *const label_highflownext_fan_speed[] = { + "Flow [dL/h]", + "Water quality [%]", + "Conductivity [nS/cm]", +}; + +static const char *const label_highflownext_power[] = { + "Dissipated power", +}; + +static const char *const label_highflownext_voltage[] = { + "+5V voltage", + "+5V USB voltage" +}; + struct aqc_data { struct hid_device *hdev; struct device *hwmon_dev; @@ -390,6 +423,11 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3 break; case hwmon_fan: switch (priv->kind) { + case highflownext: + /* Special case to support flow sensor, water quality and conductivity */ + if (channel < 3) + return 0444; + break; case quadro: /* Special case to support flow sensor */ if (channel < priv->num_fans + 1) @@ -402,6 +440,18 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3 } break; case hwmon_power: + switch (priv->kind) { + case highflownext: + /* Special case to support one power sensor */ + if (channel == 0) + return 0444; + break; + default: + if (channel < priv->num_fans) + return 0444; + break; + } + break; case hwmon_curr: if (channel < priv->num_fans) return 0444; @@ -413,6 +463,11 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3 if (channel < priv->num_fans + 2) return 0444; break; + case highflownext: + /* Special case to support two voltage sensors */ + if (channel < 2) + return 0444; + break; default: if (channel < priv->num_fans) return 0444; @@ -679,6 +734,22 @@ static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8 case quadro: priv->speed_input[4] = get_unaligned_be16(data + priv->flow_sensor_offset); break; + case highflownext: + /* If external temp sensor is not connected, its power reading is also N/A */ + if (priv->temp_input[1] == -ENODATA) + priv->power_input[0] = -ENODATA; + else + priv->power_input[0] = + get_unaligned_be16(data + HIGHFLOWNEXT_POWER) * 1000000; + + priv->voltage_input[0] = get_unaligned_be16(data + HIGHFLOWNEXT_5V_VOLTAGE) * 10; + priv->voltage_input[1] = + get_unaligned_be16(data + HIGHFLOWNEXT_5V_VOLTAGE_USB) * 10; + + priv->speed_input[0] = get_unaligned_be16(data + HIGHFLOWNEXT_FLOW); + priv->speed_input[1] = get_unaligned_be16(data + HIGHFLOWNEXT_WATER_QUALITY); + priv->speed_input[2] = get_unaligned_be16(data + HIGHFLOWNEXT_CONDUCTIVITY); + break; default: break; } @@ -851,6 +922,19 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id) priv->voltage_label = label_fan_voltage; priv->current_label = label_fan_current; break; + case USB_PRODUCT_ID_HIGHFLOWNEXT: + priv->kind = highflownext; + + priv->num_fans = 0; + priv->num_temp_sensors = HIGHFLOWNEXT_NUM_SENSORS; + priv->temp_sensor_start_offset = HIGHFLOWNEXT_SENSOR_START; + priv->power_cycle_count_offset = QUADRO_POWER_CYCLES; + + priv->temp_label = label_highflownext_temp_sensors; + priv->speed_label = label_highflownext_fan_speed; + priv->power_label = label_highflownext_power; + priv->voltage_label = label_highflownext_voltage; + break; default: break; } @@ -907,6 +991,7 @@ static const struct hid_device_id aqc_table[] = { { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_FARBWERK360) }, { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_OCTO) }, { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_QUADRO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_HIGHFLOWNEXT) }, { } }; -- cgit v1.2.3 From 493372f5d3df9905087a2ce9f8b5a2dca5af889f Mon Sep 17 00:00:00 2001 From: Eliav Farber Date: Thu, 8 Sep 2022 15:24:37 +0000 Subject: hwmon: (mr75203) skip reset-control deassert for SOCs that don't support it Don't fail the probe function and don't deassert the reset controller if a "reset" property doesn't exist in the device tree. Change is done for SOCs that don't support a reset controller. Signed-off-by: Eliav Farber Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220908152449.35457-10-farbere@amazon.com Signed-off-by: Guenter Roeck --- drivers/hwmon/mr75203.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/mr75203.c b/drivers/hwmon/mr75203.c index 9259779cc2df..3cc359e33677 100644 --- a/drivers/hwmon/mr75203.c +++ b/drivers/hwmon/mr75203.c @@ -541,14 +541,17 @@ static int mr75203_probe(struct platform_device *pdev) return ret; } - pvt->rst = devm_reset_control_get_exclusive(dev, NULL); + pvt->rst = devm_reset_control_get_optional_exclusive(dev, NULL); if (IS_ERR(pvt->rst)) return dev_err_probe(dev, PTR_ERR(pvt->rst), "failed to get reset control\n"); - ret = pvt_reset_control_deassert(dev, pvt); - if (ret) - return dev_err_probe(dev, ret, "cannot deassert reset control\n"); + if (pvt->rst) { + ret = pvt_reset_control_deassert(dev, pvt); + if (ret) + return dev_err_probe(dev, ret, + "cannot deassert reset control\n"); + } ret = regmap_read(pvt->c_map, PVT_IP_CONFIG, &val); if(ret < 0) -- cgit v1.2.3 From a31d53598c036480fe7df3220742b1bf480278a5 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 14 Sep 2022 03:20:56 -0700 Subject: hwmon: (emc2305) Remove unnecessary range check Static analyzers report: drivers/hwmon/emc2305.c:194 emc2305_set_cur_state() warn: impossible condition '(val > 255) => (0-255 > 255)' 'val' is u8 and thus can never be larger than 255. In theory the operation calculating 'val' could result in a value larger than 255, but this won't happen because its parameter has already been range checked and it is guaranteed that the result never exceeds 255. Remove the unnecessary value check. Cc: Michael Shych Signed-off-by: Guenter Roeck --- drivers/hwmon/emc2305.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/emc2305.c b/drivers/hwmon/emc2305.c index ee5ed24feab5..bb32172e07e3 100644 --- a/drivers/hwmon/emc2305.c +++ b/drivers/hwmon/emc2305.c @@ -191,8 +191,6 @@ static int emc2305_set_cur_state(struct thermal_cooling_device *cdev, unsigned l state = max_t(unsigned long, state, data->cdev_data[cdev_idx].last_hwmon_state); val = EMC2305_PWM_STATE2DUTY(state, data->max_state, EMC2305_FAN_MAX); - if (val > EMC2305_FAN_MAX) - return -EINVAL; data->cdev_data[cdev_idx].cur_state = state; if (data->pwm_separate) { -- cgit v1.2.3 From 887b22ec07e56c680c7e5c4b6e2f47b7b28a48fc Mon Sep 17 00:00:00 2001 From: Aleksandr Mezin Date: Sun, 18 Sep 2022 17:55:06 +0600 Subject: hwmon: (nzxt-smart2) add another USB ID No known differences from already supported devices. Signed-off-by: Aleksandr Mezin Link: https://lore.kernel.org/r/20220918115506.61870-1-mezin.alexander@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/nzxt-smart2.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/nzxt-smart2.c b/drivers/hwmon/nzxt-smart2.c index dd892ff5a3e8..533f38b0b4e9 100644 --- a/drivers/hwmon/nzxt-smart2.c +++ b/drivers/hwmon/nzxt-smart2.c @@ -787,6 +787,7 @@ static void nzxt_smart2_hid_remove(struct hid_device *hdev) static const struct hid_device_id nzxt_smart2_hid_id_table[] = { { HID_USB_DEVICE(0x1e71, 0x2006) }, /* NZXT Smart Device V2 */ { HID_USB_DEVICE(0x1e71, 0x200d) }, /* NZXT Smart Device V2 */ + { HID_USB_DEVICE(0x1e71, 0x200f) }, /* NZXT Smart Device V2 */ { HID_USB_DEVICE(0x1e71, 0x2009) }, /* NZXT RGB & Fan Controller */ { HID_USB_DEVICE(0x1e71, 0x200e) }, /* NZXT RGB & Fan Controller */ { HID_USB_DEVICE(0x1e71, 0x2010) }, /* NZXT RGB & Fan Controller */ -- cgit v1.2.3 From b7f5ac92fe18cd920b8d3f7e56a83ae16f7ecfbc Mon Sep 17 00:00:00 2001 From: Eliav Farber Date: Thu, 8 Sep 2022 15:24:40 +0000 Subject: hwmon: (mr75203) add VM active channel support Add active channel support per voltage monitor. The number of active channels is read from the device-tree. When absent in device-tree, all channels are assumed to be used. This shall be useful to expose sysfs only for inputs that are connected to a voltage source. Setting number of active channels to 0, means that entire VM sensor is not used. Signed-off-by: Eliav Farber Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220908152449.35457-13-farbere@amazon.com Signed-off-by: Guenter Roeck --- drivers/hwmon/mr75203.c | 121 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 99 insertions(+), 22 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/mr75203.c b/drivers/hwmon/mr75203.c index 3cc359e33677..0eb17122c40f 100644 --- a/drivers/hwmon/mr75203.c +++ b/drivers/hwmon/mr75203.c @@ -30,6 +30,8 @@ #define CH_NUM_MSK GENMASK(31, 24) #define CH_NUM_SFT 24 +#define VM_NUM_MAX (VM_NUM_MSK >> VM_NUM_SFT) + /* Macro Common Register */ #define CLK_SYNTH 0x00 #define CLK_SYNTH_LO_SFT 0 @@ -106,6 +108,31 @@ #define PVT_N_CONST 90 #define PVT_R_CONST 245805 +/** + * struct voltage_device - VM single input parameters. + * @vm_map: Map channel number to VM index. + * @ch_map: Map channel number to channel index. + * + * The structure provides mapping between channel-number (0..N-1) to VM-index + * (0..num_vm-1) and channel-index (0..ch_num-1) where N = num_vm * ch_num. + */ +struct voltage_device { + u32 vm_map; + u32 ch_map; +}; + +/** + * struct voltage_channels - VM channel count. + * @total: Total number of channels in all VMs. + * @max: Maximum number of channels among all VMs. + * + * The structure provides channel count information across all VMs. + */ +struct voltage_channels { + u32 total; + u8 max; +}; + struct pvt_device { struct regmap *c_map; struct regmap *t_map; @@ -113,12 +140,12 @@ struct pvt_device { struct regmap *v_map; struct clk *clk; struct reset_control *rst; + struct voltage_device *vd; + struct voltage_channels vm_channels; u32 t_num; u32 p_num; u32 v_num; - u32 c_num; u32 ip_freq; - u8 *vm_idx; }; static umode_t pvt_is_visible(const void *data, enum hwmon_sensor_types type, @@ -184,11 +211,11 @@ static int pvt_read_in(struct device *dev, u32 attr, int channel, long *val) u32 n, stat; int ret; - if (channel >= pvt->v_num * pvt->c_num) + if (channel >= pvt->vm_channels.total) return -EINVAL; - vm_idx = pvt->vm_idx[channel / pvt->c_num]; - ch_idx = channel % pvt->c_num; + vm_idx = pvt->vd[channel].vm_map; + ch_idx = pvt->vd[channel].ch_map; switch (attr) { case hwmon_in_input: @@ -388,7 +415,7 @@ static int pvt_init(struct pvt_device *pvt) if (ret) return ret; - val = (BIT(pvt->c_num) - 1) | VM_CH_INIT | + val = (BIT(pvt->vm_channels.max) - 1) | VM_CH_INIT | IP_POLL << SDIF_ADDR_SFT | SDIF_WRN_W | SDIF_PROG; ret = regmap_write(v_map, SDIF_W, val); if (ret < 0) @@ -513,6 +540,60 @@ static int pvt_reset_control_deassert(struct device *dev, struct pvt_device *pvt return devm_add_action_or_reset(dev, pvt_reset_control_assert, pvt); } +static int pvt_get_active_channel(struct device *dev, struct pvt_device *pvt, + u32 vm_num, u32 ch_num, u8 *vm_idx) +{ + u8 vm_active_ch[VM_NUM_MAX]; + int ret, i, j, k; + + ret = device_property_read_u8_array(dev, "moortec,vm-active-channels", + vm_active_ch, vm_num); + if (ret) { + /* + * Incase "moortec,vm-active-channels" property is not defined, + * we assume each VM sensor has all of its channels active. + */ + memset(vm_active_ch, ch_num, vm_num); + pvt->vm_channels.max = ch_num; + pvt->vm_channels.total = ch_num * vm_num; + } else { + for (i = 0; i < vm_num; i++) { + if (vm_active_ch[i] > ch_num) { + dev_err(dev, "invalid active channels: %u\n", + vm_active_ch[i]); + return -EINVAL; + } + + pvt->vm_channels.total += vm_active_ch[i]; + + if (vm_active_ch[i] > pvt->vm_channels.max) + pvt->vm_channels.max = vm_active_ch[i]; + } + } + + /* + * Map between the channel-number to VM-index and channel-index. + * Example - 3 VMs, "moortec,vm_active_ch" = <5 2 4>: + * vm_map = [0 0 0 0 0 1 1 2 2 2 2] + * ch_map = [0 1 2 3 4 0 1 0 1 2 3] + */ + pvt->vd = devm_kcalloc(dev, pvt->vm_channels.total, sizeof(*pvt->vd), + GFP_KERNEL); + if (!pvt->vd) + return -ENOMEM; + + k = 0; + for (i = 0; i < vm_num; i++) { + for (j = 0; j < vm_active_ch[i]; j++) { + pvt->vd[k].vm_map = vm_idx[i]; + pvt->vd[k].ch_map = j; + k++; + } + } + + return 0; +} + static int mr75203_probe(struct platform_device *pdev) { u32 ts_num, vm_num, pd_num, ch_num, val, index, i; @@ -564,7 +645,6 @@ static int mr75203_probe(struct platform_device *pdev) pvt->t_num = ts_num; pvt->p_num = pd_num; pvt->v_num = vm_num; - pvt->c_num = ch_num; val = 0; if (ts_num) val++; @@ -601,44 +681,41 @@ static int mr75203_probe(struct platform_device *pdev) } if (vm_num) { - u32 total_ch; + u8 vm_idx[VM_NUM_MAX]; ret = pvt_get_regmap(pdev, "vm", pvt); if (ret) return ret; - pvt->vm_idx = devm_kcalloc(dev, vm_num, sizeof(*pvt->vm_idx), - GFP_KERNEL); - if (!pvt->vm_idx) - return -ENOMEM; - - ret = device_property_read_u8_array(dev, "intel,vm-map", - pvt->vm_idx, vm_num); + ret = device_property_read_u8_array(dev, "intel,vm-map", vm_idx, + vm_num); if (ret) { /* * Incase intel,vm-map property is not defined, we * assume incremental channel numbers. */ for (i = 0; i < vm_num; i++) - pvt->vm_idx[i] = i; + vm_idx[i] = i; } else { for (i = 0; i < vm_num; i++) - if (pvt->vm_idx[i] >= vm_num || - pvt->vm_idx[i] == 0xff) { + if (vm_idx[i] >= vm_num || vm_idx[i] == 0xff) { pvt->v_num = i; vm_num = i; break; } } - total_ch = ch_num * vm_num; - in_config = devm_kcalloc(dev, total_ch + 1, + ret = pvt_get_active_channel(dev, pvt, vm_num, ch_num, vm_idx); + if (ret) + return ret; + + in_config = devm_kcalloc(dev, pvt->vm_channels.total + 1, sizeof(*in_config), GFP_KERNEL); if (!in_config) return -ENOMEM; - memset32(in_config, HWMON_I_INPUT, total_ch); - in_config[total_ch] = 0; + memset32(in_config, HWMON_I_INPUT, pvt->vm_channels.total); + in_config[pvt->vm_channels.total] = 0; pvt_in.config = in_config; pvt_info[index++] = &pvt_in; -- cgit v1.2.3 From 430c0d7ff56b7e324eb36c490d513c0ef9c99860 Mon Sep 17 00:00:00 2001 From: Eliav Farber Date: Thu, 8 Sep 2022 15:24:42 +0000 Subject: hwmon: (mr75203) add VM pre-scaler x2 support Add support for mr76006 pre-scaler which provides divide-by-2 scaling of the input voltage, so that it can be presented to the VM for measurement within its range (the VM input range is limited from -0.1V to 1V). The driver reads from the device-tree all the channels that use the mr76006 pre-scaler and multiplies the voltage result by a factor of 2, to represent to the user with the actual voltage input source. Channels that are not in the device-tree are multiplied by a factor of 1. Signed-off-by: Eliav Farber Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220908152449.35457-15-farbere@amazon.com [groeck: Addressed conflicts against commit d59eacaac953] Signed-off-by: Guenter Roeck --- drivers/hwmon/mr75203.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 2 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/mr75203.c b/drivers/hwmon/mr75203.c index 0eb17122c40f..c603132dbc85 100644 --- a/drivers/hwmon/mr75203.c +++ b/drivers/hwmon/mr75203.c @@ -17,6 +17,7 @@ #include #include #include +#include #include /* PVT Common register */ @@ -108,17 +109,24 @@ #define PVT_N_CONST 90 #define PVT_R_CONST 245805 +#define PRE_SCALER_X1 1 +#define PRE_SCALER_X2 2 + /** * struct voltage_device - VM single input parameters. * @vm_map: Map channel number to VM index. * @ch_map: Map channel number to channel index. + * @pre_scaler: Pre scaler value (1 or 2) used to normalize the voltage output + * result. * * The structure provides mapping between channel-number (0..N-1) to VM-index * (0..num_vm-1) and channel-index (0..ch_num-1) where N = num_vm * ch_num. + * It also provides normalization factor for the VM equation. */ struct voltage_device { u32 vm_map; u32 ch_map; + u32 pre_scaler; }; /** @@ -207,8 +215,8 @@ static int pvt_read_in(struct device *dev, u32 attr, int channel, long *val) { struct pvt_device *pvt = dev_get_drvdata(dev); struct regmap *v_map = pvt->v_map; + u32 n, stat, pre_scaler; u8 vm_idx, ch_idx; - u32 n, stat; int ret; if (channel >= pvt->vm_channels.total) @@ -231,6 +239,7 @@ static int pvt_read_in(struct device *dev, u32 attr, int channel, long *val) return ret; n &= SAMPLE_DATA_MSK; + pre_scaler = pvt->vd[channel].pre_scaler; /* * Convert the N bitstream count into voltage. * To support negative voltage calculation for 64bit machines @@ -242,7 +251,8 @@ static int pvt_read_in(struct device *dev, u32 attr, int channel, long *val) * BIT(x) may not be used instead of (1 << x) because it's * unsigned. */ - *val = (PVT_N_CONST * (long)n - PVT_R_CONST) / (1 << PVT_CONV_BITS); + *val = pre_scaler * (PVT_N_CONST * (long)n - PVT_R_CONST) / + (1 << PVT_CONV_BITS); return 0; default: @@ -594,6 +604,43 @@ static int pvt_get_active_channel(struct device *dev, struct pvt_device *pvt, return 0; } +static int pvt_get_pre_scaler(struct device *dev, struct pvt_device *pvt) +{ + u8 *pre_scaler_ch_list; + int i, ret, num_ch; + u32 channel; + + /* Set default pre-scaler value to be 1. */ + for (i = 0; i < pvt->vm_channels.total; i++) + pvt->vd[i].pre_scaler = PRE_SCALER_X1; + + /* Get number of channels configured in "moortec,vm-pre-scaler-x2". */ + num_ch = device_property_count_u8(dev, "moortec,vm-pre-scaler-x2"); + if (num_ch <= 0) + return 0; + + pre_scaler_ch_list = kcalloc(num_ch, sizeof(*pre_scaler_ch_list), + GFP_KERNEL); + if (!pre_scaler_ch_list) + return -ENOMEM; + + /* Get list of all channels that have pre-scaler of 2. */ + ret = device_property_read_u8_array(dev, "moortec,vm-pre-scaler-x2", + pre_scaler_ch_list, num_ch); + if (ret) + goto out; + + for (i = 0; i < num_ch; i++) { + channel = pre_scaler_ch_list[i]; + pvt->vd[channel].pre_scaler = PRE_SCALER_X2; + } + +out: + kfree(pre_scaler_ch_list); + + return ret; +} + static int mr75203_probe(struct platform_device *pdev) { u32 ts_num, vm_num, pd_num, ch_num, val, index, i; @@ -709,6 +756,10 @@ static int mr75203_probe(struct platform_device *pdev) if (ret) return ret; + ret = pvt_get_pre_scaler(dev, pvt); + if (ret) + return ret; + in_config = devm_kcalloc(dev, pvt->vm_channels.total + 1, sizeof(*in_config), GFP_KERNEL); if (!in_config) -- cgit v1.2.3 From 94c025b6f735b895285a74f2de263c773a08982b Mon Sep 17 00:00:00 2001 From: Eliav Farber Date: Thu, 8 Sep 2022 15:24:43 +0000 Subject: hwmon: (mr75203) modify the temperature equation according to series 5 datasheet Modify the equation and coefficients used to convert the digital output to temperature according to series 5 of the Moortec Embedded Temperature Sensor (METS) datasheet: T = G + H * (n / cal5 - 0.5) + J * F Where: *) G = 60, H = 200, cal5 = 4094, J = -0.1. *) F = frequency clock in MHz. *) n is the digital output. In code, the G, H and J coefficients are multiplied by a factor of 1000 to get the temperature in milli-Celsius. Final result is clamped in case it exceeds min/max thresholds. Change is done since it is unclear where the current equation and coefficients came from. Signed-off-by: Eliav Farber Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220908152449.35457-16-farbere@amazon.com Signed-off-by: Guenter Roeck --- drivers/hwmon/mr75203.c | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/mr75203.c b/drivers/hwmon/mr75203.c index c603132dbc85..5ee1666bc131 100644 --- a/drivers/hwmon/mr75203.c +++ b/drivers/hwmon/mr75203.c @@ -102,13 +102,19 @@ #define PVT_POLL_DELAY_US 20 #define PVT_POLL_TIMEOUT_US 20000 -#define PVT_H_CONST 100000 -#define PVT_CAL5_CONST 2047 -#define PVT_G_CONST 40000 #define PVT_CONV_BITS 10 #define PVT_N_CONST 90 #define PVT_R_CONST 245805 +#define PVT_TEMP_MIN_mC -40000 +#define PVT_TEMP_MAX_mC 125000 + +/* Temperature coefficients for series 5 */ +#define PVT_SERIES5_H_CONST 200000 +#define PVT_SERIES5_G_CONST 60000 +#define PVT_SERIES5_J_CONST -100 +#define PVT_SERIES5_CAL5_CONST 4094 + #define PRE_SCALER_X1 1 #define PRE_SCALER_X2 2 @@ -174,13 +180,26 @@ static umode_t pvt_is_visible(const void *data, enum hwmon_sensor_types type, return 0; } +static long pvt_calc_temp(struct pvt_device *pvt, u32 nbs) +{ + /* + * Convert the register value to degrees centigrade temperature: + * T = G + H * (n / cal5 - 0.5) + J * F + */ + s64 tmp = PVT_SERIES5_G_CONST + + PVT_SERIES5_H_CONST * (s64)nbs / PVT_SERIES5_CAL5_CONST - + PVT_SERIES5_H_CONST / 2 + + PVT_SERIES5_J_CONST * (s64)pvt->ip_freq / HZ_PER_MHZ; + + return clamp_val(tmp, PVT_TEMP_MIN_mC, PVT_TEMP_MAX_mC); +} + static int pvt_read_temp(struct device *dev, u32 attr, int channel, long *val) { struct pvt_device *pvt = dev_get_drvdata(dev); struct regmap *t_map = pvt->t_map; u32 stat, nbs; int ret; - u64 tmp; switch (attr) { case hwmon_temp_input: @@ -201,9 +220,7 @@ static int pvt_read_temp(struct device *dev, u32 attr, int channel, long *val) * Convert the register value to * degrees centigrade temperature */ - tmp = nbs * PVT_H_CONST; - do_div(tmp, PVT_CAL5_CONST); - *val = tmp - PVT_G_CONST - pvt->ip_freq; + *val = pvt_calc_temp(pvt, nbs); return 0; default: @@ -327,7 +344,7 @@ static int pvt_init(struct pvt_device *pvt) (key >> 1) << CLK_SYNTH_HI_SFT | (key >> 1) << CLK_SYNTH_HOLD_SFT | CLK_SYNTH_EN; - pvt->ip_freq = sys_freq * 100 / (key + 2); + pvt->ip_freq = clk_get_rate(pvt->clk) / (key + 2); if (t_num) { ret = regmap_write(t_map, SDIF_SMPL_CTRL, 0x0); -- cgit v1.2.3 From 331ed050c11faf6a0beb065a79a1ac52e2fd0467 Mon Sep 17 00:00:00 2001 From: Peter Robinson Date: Fri, 16 Sep 2022 13:09:36 +0100 Subject: hwmon: (aspeed-pwm-tacho) Add dependency on ARCH_ASPEED The SENSORS_ASPEED is part of the Aspeed silicon so it makes sense to depend on ARCH_ASPEED and for compile testing. Signed-off-by: Peter Robinson Link: https://lore.kernel.org/r/20220916120936.372591-1-pbrobinson@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index f604f599a341..96545763a52e 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -393,6 +393,7 @@ config SENSORS_ASB100 config SENSORS_ASPEED tristate "ASPEED AST2400/AST2500 PWM and Fan tach driver" + depends on ARCH_ASPEED || COMPILE_TEST depends on THERMAL || THERMAL=n select REGMAP help -- cgit v1.2.3 From 3b12ca798e022192fb451e9e5ef1bb147f21c413 Mon Sep 17 00:00:00 2001 From: Eliav Farber Date: Thu, 8 Sep 2022 15:24:45 +0000 Subject: hwmon: (mr75203) add support for series 6 temperature equation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current equation used in code is aligned to series 5: T = G + H * (n / cal5 - 0.5) + J * F Where: G = 60, H = 200, cal5 = 4094, J = -0.1, F = frequency clock in MHz Series 6 has a slightly different equation: T = G + H * (n / cal5 - 0.5) and a different set of coefficients: G = 57.4, H = 249.4, cal5 = 4096 This change supports equation and coefficients for both series. (for series 6, J is set to 0). The series is determined according to “moortec,ts-series” property in the device tree. If absent, series 5 is assumed to be the default. Signed-off-by: Eliav Farber Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220908152449.35457-18-farbere@amazon.com Signed-off-by: Guenter Roeck --- drivers/hwmon/mr75203.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 62 insertions(+), 4 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/mr75203.c b/drivers/hwmon/mr75203.c index 5ee1666bc131..ce00349ce1d7 100644 --- a/drivers/hwmon/mr75203.c +++ b/drivers/hwmon/mr75203.c @@ -115,6 +115,15 @@ #define PVT_SERIES5_J_CONST -100 #define PVT_SERIES5_CAL5_CONST 4094 +/* Temperature coefficients for series 6 */ +#define PVT_SERIES6_H_CONST 249400 +#define PVT_SERIES6_G_CONST 57400 +#define PVT_SERIES6_J_CONST 0 +#define PVT_SERIES6_CAL5_CONST 4096 + +#define TEMPERATURE_SENSOR_SERIES_5 5 +#define TEMPERATURE_SENSOR_SERIES_6 6 + #define PRE_SCALER_X1 1 #define PRE_SCALER_X2 2 @@ -147,6 +156,13 @@ struct voltage_channels { u8 max; }; +struct temp_coeff { + u32 h; + u32 g; + u32 cal5; + s32 j; +}; + struct pvt_device { struct regmap *c_map; struct regmap *t_map; @@ -156,6 +172,7 @@ struct pvt_device { struct reset_control *rst; struct voltage_device *vd; struct voltage_channels vm_channels; + struct temp_coeff ts_coeff; u32 t_num; u32 p_num; u32 v_num; @@ -186,10 +203,12 @@ static long pvt_calc_temp(struct pvt_device *pvt, u32 nbs) * Convert the register value to degrees centigrade temperature: * T = G + H * (n / cal5 - 0.5) + J * F */ - s64 tmp = PVT_SERIES5_G_CONST + - PVT_SERIES5_H_CONST * (s64)nbs / PVT_SERIES5_CAL5_CONST - - PVT_SERIES5_H_CONST / 2 + - PVT_SERIES5_J_CONST * (s64)pvt->ip_freq / HZ_PER_MHZ; + struct temp_coeff *ts_coeff = &pvt->ts_coeff; + + s64 tmp = ts_coeff->g + + ts_coeff->h * (s64)nbs / ts_coeff->cal5 - + ts_coeff->h / 2 + + ts_coeff->j * (s64)pvt->ip_freq / HZ_PER_MHZ; return clamp_val(tmp, PVT_TEMP_MIN_mC, PVT_TEMP_MAX_mC); } @@ -658,6 +677,41 @@ out: return ret; } +static int pvt_set_temp_coeff(struct device *dev, struct pvt_device *pvt) +{ + struct temp_coeff *ts_coeff = &pvt->ts_coeff; + u32 series; + int ret; + + /* Incase ts-series property is not defined, use default 5. */ + ret = device_property_read_u32(dev, "moortec,ts-series", &series); + if (ret) + series = TEMPERATURE_SENSOR_SERIES_5; + + switch (series) { + case TEMPERATURE_SENSOR_SERIES_5: + ts_coeff->h = PVT_SERIES5_H_CONST; + ts_coeff->g = PVT_SERIES5_G_CONST; + ts_coeff->j = PVT_SERIES5_J_CONST; + ts_coeff->cal5 = PVT_SERIES5_CAL5_CONST; + break; + case TEMPERATURE_SENSOR_SERIES_6: + ts_coeff->h = PVT_SERIES6_H_CONST; + ts_coeff->g = PVT_SERIES6_G_CONST; + ts_coeff->j = PVT_SERIES6_J_CONST; + ts_coeff->cal5 = PVT_SERIES6_CAL5_CONST; + break; + default: + dev_err(dev, "invalid temperature sensor series (%u)\n", + series); + return -EINVAL; + } + + dev_dbg(dev, "temperature sensor series = %u\n", series); + + return 0; +} + static int mr75203_probe(struct platform_device *pdev) { u32 ts_num, vm_num, pd_num, ch_num, val, index, i; @@ -728,6 +782,10 @@ static int mr75203_probe(struct platform_device *pdev) if (ret) return ret; + ret = pvt_set_temp_coeff(dev, pvt); + if (ret) + return ret; + temp_config = devm_kcalloc(dev, ts_num + 1, sizeof(*temp_config), GFP_KERNEL); if (!temp_config) -- cgit v1.2.3 From 27937d6f8eda4ec8179384764fc5a658d4a6060c Mon Sep 17 00:00:00 2001 From: Eliav Farber Date: Thu, 8 Sep 2022 15:24:47 +0000 Subject: hwmon: (mr75203) parse temperature coefficients from device-tree Use thermal coefficients from the device tree if they exist. Otherwise, use default values according to the series (5 or 6). All coefficients can be used or only part of them. The coefficients shall be used for fine tuning the default values since coefficients can vary between product and product. Signed-off-by: Eliav Farber Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220908152449.35457-20-farbere@amazon.com Signed-off-by: Guenter Roeck --- drivers/hwmon/mr75203.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/mr75203.c b/drivers/hwmon/mr75203.c index ce00349ce1d7..e3960fc7198f 100644 --- a/drivers/hwmon/mr75203.c +++ b/drivers/hwmon/mr75203.c @@ -709,6 +709,15 @@ static int pvt_set_temp_coeff(struct device *dev, struct pvt_device *pvt) dev_dbg(dev, "temperature sensor series = %u\n", series); + /* Override ts-coeff-h/g/j/cal5 if they are defined. */ + device_property_read_u32(dev, "moortec,ts-coeff-h", &ts_coeff->h); + device_property_read_u32(dev, "moortec,ts-coeff-g", &ts_coeff->g); + device_property_read_u32(dev, "moortec,ts-coeff-j", &ts_coeff->j); + device_property_read_u32(dev, "moortec,ts-coeff-cal5", &ts_coeff->cal5); + + dev_dbg(dev, "ts-coeff: h = %u, g = %u, j = %d, cal5 = %u\n", + ts_coeff->h, ts_coeff->g, ts_coeff->j, ts_coeff->cal5); + return 0; } -- cgit v1.2.3 From a4dd0b80b461a4cf2238013a43ff1bd6077c8056 Mon Sep 17 00:00:00 2001 From: Eliav Farber Date: Thu, 8 Sep 2022 15:24:48 +0000 Subject: hwmon: (mr75203) add debugfs to read and write temperature coefficients This change adds debugfs to read and write temperature sensor coefficients - g, h, j and cal5. The coefficients can vary between product and product, so it can be very useful to be able to modify them on the fly during the calibration process. e.g.: cat /sys/kernel/debug/940f23d0000.pvt/ts_coeff_cal5 4096 echo 83000 > sys/kernel/debug/940f23d0000.pvt/ts_coeff_g Signed-off-by: Eliav Farber Link: https://lore.kernel.org/r/20220908152449.35457-21-farbere@amazon.com Signed-off-by: Guenter Roeck --- drivers/hwmon/mr75203.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/mr75203.c b/drivers/hwmon/mr75203.c index e3960fc7198f..c9f72e1e8a68 100644 --- a/drivers/hwmon/mr75203.c +++ b/drivers/hwmon/mr75203.c @@ -9,6 +9,7 @@ */ #include #include +#include #include #include #include @@ -170,6 +171,7 @@ struct pvt_device { struct regmap *v_map; struct clk *clk; struct reset_control *rst; + struct dentry *dbgfs_dir; struct voltage_device *vd; struct voltage_channels vm_channels; struct temp_coeff ts_coeff; @@ -179,6 +181,64 @@ struct pvt_device { u32 ip_freq; }; +static ssize_t pvt_ts_coeff_j_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct pvt_device *pvt = file->private_data; + unsigned int len; + char buf[13]; + + len = scnprintf(buf, sizeof(buf), "%d\n", pvt->ts_coeff.j); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t pvt_ts_coeff_j_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct pvt_device *pvt = file->private_data; + int ret; + + ret = kstrtos32_from_user(user_buf, count, 0, &pvt->ts_coeff.j); + if (ret) + return ret; + + return count; +} + +static const struct file_operations pvt_ts_coeff_j_fops = { + .read = pvt_ts_coeff_j_read, + .write = pvt_ts_coeff_j_write, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static void devm_pvt_ts_dbgfs_remove(void *data) +{ + struct pvt_device *pvt = (struct pvt_device *)data; + + debugfs_remove_recursive(pvt->dbgfs_dir); + pvt->dbgfs_dir = NULL; +} + +static int pvt_ts_dbgfs_create(struct pvt_device *pvt, struct device *dev) +{ + pvt->dbgfs_dir = debugfs_create_dir(dev_name(dev), NULL); + + debugfs_create_u32("ts_coeff_h", 0644, pvt->dbgfs_dir, + &pvt->ts_coeff.h); + debugfs_create_u32("ts_coeff_g", 0644, pvt->dbgfs_dir, + &pvt->ts_coeff.g); + debugfs_create_u32("ts_coeff_cal5", 0644, pvt->dbgfs_dir, + &pvt->ts_coeff.cal5); + debugfs_create_file("ts_coeff_j", 0644, pvt->dbgfs_dir, pvt, + &pvt_ts_coeff_j_fops); + + return devm_add_action_or_reset(dev, devm_pvt_ts_dbgfs_remove, pvt); +} + static umode_t pvt_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr, int channel) { @@ -803,6 +863,8 @@ static int mr75203_probe(struct platform_device *pdev) memset32(temp_config, HWMON_T_INPUT, ts_num); pvt_temp.config = temp_config; pvt_info[index++] = &pvt_temp; + + pvt_ts_dbgfs_create(pvt, dev); } if (pd_num) { -- cgit v1.2.3 From 0cb15e8ae0f8e7f00f2da726541bfa44718fb955 Mon Sep 17 00:00:00 2001 From: Eliav Farber Date: Thu, 8 Sep 2022 15:24:49 +0000 Subject: hwmon: (mr75203) fix coding style space errors Fix: "ERROR: space required before the open parenthesis '('" All of the errors were introduced before this series of patches. Signed-off-by: Eliav Farber Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20220908152449.35457-22-farbere@amazon.com Signed-off-by: Guenter Roeck --- drivers/hwmon/mr75203.c | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/mr75203.c b/drivers/hwmon/mr75203.c index c9f72e1e8a68..667c764e0c2c 100644 --- a/drivers/hwmon/mr75203.c +++ b/drivers/hwmon/mr75203.c @@ -290,7 +290,7 @@ static int pvt_read_temp(struct device *dev, u32 attr, int channel, long *val) return ret; ret = regmap_read(t_map, SDIF_DATA(channel), &nbs); - if(ret < 0) + if (ret < 0) return ret; nbs &= SAMPLE_DATA_MSK; @@ -331,7 +331,7 @@ static int pvt_read_in(struct device *dev, u32 attr, int channel, long *val) return ret; ret = regmap_read(v_map, VM_SDIF_DATA(vm_idx, ch_idx), &n); - if(ret < 0) + if (ret < 0) return ret; n &= SAMPLE_DATA_MSK; @@ -427,19 +427,19 @@ static int pvt_init(struct pvt_device *pvt) if (t_num) { ret = regmap_write(t_map, SDIF_SMPL_CTRL, 0x0); - if(ret < 0) + if (ret < 0) return ret; ret = regmap_write(t_map, SDIF_HALT, 0x0); - if(ret < 0) + if (ret < 0) return ret; ret = regmap_write(t_map, CLK_SYNTH, clk_synth); - if(ret < 0) + if (ret < 0) return ret; ret = regmap_write(t_map, SDIF_DISABLE, 0x0); - if(ret < 0) + if (ret < 0) return ret; ret = regmap_read_poll_timeout(t_map, SDIF_STAT, @@ -452,7 +452,7 @@ static int pvt_init(struct pvt_device *pvt) val = CFG0_MODE_2 | CFG0_PARALLEL_OUT | CFG0_12_BIT | IP_CFG << SDIF_ADDR_SFT | SDIF_WRN_W | SDIF_PROG; ret = regmap_write(t_map, SDIF_W, val); - if(ret < 0) + if (ret < 0) return ret; ret = regmap_read_poll_timeout(t_map, SDIF_STAT, @@ -465,7 +465,7 @@ static int pvt_init(struct pvt_device *pvt) val = POWER_DELAY_CYCLE_256 | IP_TMR << SDIF_ADDR_SFT | SDIF_WRN_W | SDIF_PROG; ret = regmap_write(t_map, SDIF_W, val); - if(ret < 0) + if (ret < 0) return ret; ret = regmap_read_poll_timeout(t_map, SDIF_STAT, @@ -479,39 +479,39 @@ static int pvt_init(struct pvt_device *pvt) IP_CTRL << SDIF_ADDR_SFT | SDIF_WRN_W | SDIF_PROG; ret = regmap_write(t_map, SDIF_W, val); - if(ret < 0) + if (ret < 0) return ret; } if (p_num) { ret = regmap_write(p_map, SDIF_HALT, 0x0); - if(ret < 0) + if (ret < 0) return ret; ret = regmap_write(p_map, SDIF_DISABLE, BIT(p_num) - 1); - if(ret < 0) + if (ret < 0) return ret; ret = regmap_write(p_map, CLK_SYNTH, clk_synth); - if(ret < 0) + if (ret < 0) return ret; } if (v_num) { ret = regmap_write(v_map, SDIF_SMPL_CTRL, 0x0); - if(ret < 0) + if (ret < 0) return ret; ret = regmap_write(v_map, SDIF_HALT, 0x0); - if(ret < 0) + if (ret < 0) return ret; ret = regmap_write(v_map, CLK_SYNTH, clk_synth); - if(ret < 0) + if (ret < 0) return ret; ret = regmap_write(v_map, SDIF_DISABLE, 0x0); - if(ret < 0) + if (ret < 0) return ret; ret = regmap_read_poll_timeout(v_map, SDIF_STAT, @@ -538,7 +538,7 @@ static int pvt_init(struct pvt_device *pvt) CFG1_14_BIT | IP_CFG << SDIF_ADDR_SFT | SDIF_WRN_W | SDIF_PROG; ret = regmap_write(v_map, SDIF_W, val); - if(ret < 0) + if (ret < 0) return ret; ret = regmap_read_poll_timeout(v_map, SDIF_STAT, @@ -551,7 +551,7 @@ static int pvt_init(struct pvt_device *pvt) val = POWER_DELAY_CYCLE_64 | IP_TMR << SDIF_ADDR_SFT | SDIF_WRN_W | SDIF_PROG; ret = regmap_write(v_map, SDIF_W, val); - if(ret < 0) + if (ret < 0) return ret; ret = regmap_read_poll_timeout(v_map, SDIF_STAT, @@ -565,7 +565,7 @@ static int pvt_init(struct pvt_device *pvt) IP_CTRL << SDIF_ADDR_SFT | SDIF_WRN_W | SDIF_PROG; ret = regmap_write(v_map, SDIF_W, val); - if(ret < 0) + if (ret < 0) return ret; } @@ -822,7 +822,7 @@ static int mr75203_probe(struct platform_device *pdev) } ret = regmap_read(pvt->c_map, PVT_IP_CONFIG, &val); - if(ret < 0) + if (ret < 0) return ret; ts_num = (val & TS_NUM_MSK) >> TS_NUM_SFT; -- cgit v1.2.3 From 38b04ad02d0da956b697ff93c5c23e21cdb4cddc Mon Sep 17 00:00:00 2001 From: Ibrahim Tilki Date: Sat, 10 Sep 2022 20:19:42 +0300 Subject: drivers: hwmon: Add max31760 fan speed controller driver MAX31760 is a precision fan speed controller with nonvolatile lookup table. Device has one internal and one external temperature sensor support. Controls two fans and measures their speeds. Generates hardware alerts when programmable max and critical temperatures are exceeded. Signed-off-by: Ibrahim Tilki Reviewed-by: Nurettin Bolucu Link: https://lore.kernel.org/r/20220910171945.48088-2-Ibrahim.Tilki@analog.com Signed-off-by: Guenter Roeck --- drivers/hwmon/Kconfig | 12 + drivers/hwmon/Makefile | 1 + drivers/hwmon/max31760.c | 596 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 609 insertions(+) create mode 100644 drivers/hwmon/max31760.c (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 96545763a52e..5695b266abcf 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1067,6 +1067,18 @@ config SENSORS_MAX31730 This driver can also be built as a module. If so, the module will be called max31730. +config SENSORS_MAX31760 + tristate "MAX31760 fan speed controller" + depends on I2C + select REGMAP_I2C + help + Support for the Analog Devices MAX31760 Precision Fan-Speed + Controller. MAX31760 integrates temperature sensing along with + precision PWM fan control. + + This driver can also be built as a module. If so, the module + will be called max31760. + config SENSORS_MAX6620 tristate "Maxim MAX6620 fan controller" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 8b7a590091dd..11d076cad8a2 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -140,6 +140,7 @@ 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_MAX31760) += max31760.o obj-$(CONFIG_SENSORS_MAX6620) += max6620.o obj-$(CONFIG_SENSORS_MAX6621) += max6621.o obj-$(CONFIG_SENSORS_MAX6639) += max6639.o diff --git a/drivers/hwmon/max31760.c b/drivers/hwmon/max31760.c new file mode 100644 index 000000000000..06d5f39dc33d --- /dev/null +++ b/drivers/hwmon/max31760.c @@ -0,0 +1,596 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include + +#define REG_CR1 0x00 +#define CR1_HYST BIT(5) +#define CR1_DRV GENMASK(4, 3) +#define CR1_TEMP_SRC GENMASK(1, 0) +#define REG_CR2 0x01 +#define CR2_STBY BIT(7) +#define CR2_ALERTS BIT(6) +#define CR2_DFC BIT(0) +#define REG_CR3 0x02 +#define REG_PWMR 0x50 +#define REG_PWMV 0x51 +#define REG_STATUS 0x5A +#define STATUS_ALARM_CRIT(ch) BIT(2 + 2 * (ch)) +#define STATUS_ALARM_MAX(ch) BIT(3 + 2 * (ch)) +#define STATUS_RDFA BIT(6) + +#define REG_TACH(ch) (0x52 + (ch) * 2) +#define REG_TEMP_INPUT(ch) (0x56 + (ch) * 2) +#define REG_TEMP_MAX(ch) (0x06 + (ch) * 2) +#define REG_TEMP_CRIT(ch) (0x0A + (ch) * 2) + +#define TEMP11_FROM_REG(reg) ((reg) / 32 * 125) +#define TEMP11_TO_REG(val) (DIV_ROUND_CLOSEST(clamp_val((val), -128000, \ + 127875), 125) * 32) + +#define LUT_SIZE 48 + +#define REG_LUT(index) (0x20 + (index)) + +struct max31760_state { + struct regmap *regmap; + + struct lut_attribute { + char name[24]; + struct sensor_device_attribute sda; + } lut[LUT_SIZE]; + + struct attribute *attrs[LUT_SIZE + 2]; + struct attribute_group group; + const struct attribute_group *groups[2]; +}; + +static bool max31760_volatile_reg(struct device *dev, unsigned int reg) +{ + return reg > 0x50; +} + +static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x5B, + .cache_type = REGCACHE_RBTREE, + .volatile_reg = max31760_volatile_reg, +}; + +static const int max31760_pwm_freq[] = {33, 150, 1500, 25000}; + +static int tach_to_rpm(u16 tach) +{ + if (tach == 0) + tach = 1; + + return 60 * 100000 / tach / 2; +} + +static int max31760_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct max31760_state *state = dev_get_drvdata(dev); + unsigned int regval; + unsigned int reg_temp; + s16 temp; + u8 reg[2]; + int ret; + + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_fault: + ret = regmap_read(state->regmap, REG_STATUS, ®val); + if (ret) + return ret; + + *val = FIELD_GET(STATUS_RDFA, regval); + + return 0; + case hwmon_temp_max_alarm: + ret = regmap_read(state->regmap, REG_STATUS, ®val); + if (ret) + return ret; + + if (channel) + *val = FIELD_GET(STATUS_ALARM_MAX(1), regval); + else + *val = FIELD_GET(STATUS_ALARM_MAX(0), regval); + + return 0; + case hwmon_temp_crit_alarm: + ret = regmap_read(state->regmap, REG_STATUS, ®val); + if (ret) + return ret; + + if (channel) + *val = FIELD_GET(STATUS_ALARM_CRIT(1), regval); + else + *val = FIELD_GET(STATUS_ALARM_CRIT(0), regval); + + return 0; + case hwmon_temp_input: + reg_temp = REG_TEMP_INPUT(channel); + break; + case hwmon_temp_max: + reg_temp = REG_TEMP_MAX(channel); + break; + case hwmon_temp_crit: + reg_temp = REG_TEMP_CRIT(channel); + break; + default: + return -EOPNOTSUPP; + } + + ret = regmap_bulk_read(state->regmap, reg_temp, reg, 2); + if (ret) + return ret; + + temp = (reg[0] << 8) | reg[1]; + + *val = TEMP11_FROM_REG(temp); + + return 0; + case hwmon_fan: + switch (attr) { + case hwmon_fan_input: + ret = regmap_bulk_read(state->regmap, REG_TACH(channel), reg, 2); + if (ret) + return ret; + + *val = tach_to_rpm(reg[0] * 256 + reg[1]); + + return 0; + case hwmon_fan_fault: + ret = regmap_read(state->regmap, REG_STATUS, ®val); + if (ret) + return ret; + + if (channel) + *val = FIELD_GET(BIT(1), regval); + else + *val = FIELD_GET(BIT(0), regval); + + return 0; + case hwmon_fan_enable: + ret = regmap_read(state->regmap, REG_CR3, ®val); + if (ret) + return ret; + + if (channel) + *val = FIELD_GET(BIT(1), regval); + else + *val = FIELD_GET(BIT(0), regval); + + return 0; + default: + return -EOPNOTSUPP; + } + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_input: + ret = regmap_read(state->regmap, REG_PWMV, ®val); + if (ret) + return ret; + + *val = regval; + + return 0; + case hwmon_pwm_freq: + ret = regmap_read(state->regmap, REG_CR1, ®val); + if (ret) + return ret; + + regval = FIELD_GET(CR1_DRV, regval); + if (regval >= ARRAY_SIZE(max31760_pwm_freq)) + return -EINVAL; + + *val = max31760_pwm_freq[regval]; + + return 0; + case hwmon_pwm_enable: + ret = regmap_read(state->regmap, REG_CR2, ®val); + if (ret) + return ret; + + *val = 2 - FIELD_GET(CR2_DFC, regval); + + return 0; + case hwmon_pwm_auto_channels_temp: + ret = regmap_read(state->regmap, REG_CR1, ®val); + if (ret) + return ret; + + switch (FIELD_GET(CR1_TEMP_SRC, regval)) { + case 0: + *val = 2; + break; + case 1: + *val = 1; + break; + case 2: + case 3: + *val = 3; + break; + default: + return -EINVAL; + } + + return 0; + default: + return -EOPNOTSUPP; + } + default: + return -EOPNOTSUPP; + } +} + +static int max31760_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct max31760_state *state = dev_get_drvdata(dev); + unsigned int pwm_index; + unsigned int reg_temp; + int temp; + u8 reg_val[2]; + + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_max: + reg_temp = REG_TEMP_MAX(channel); + break; + case hwmon_temp_crit: + reg_temp = REG_TEMP_CRIT(channel); + break; + default: + return -EOPNOTSUPP; + } + + temp = TEMP11_TO_REG(val); + reg_val[0] = temp >> 8; + reg_val[1] = temp & 0xFF; + + return regmap_bulk_write(state->regmap, reg_temp, reg_val, 2); + case hwmon_fan: + switch (attr) { + case hwmon_fan_enable: + if (val == 0) + return regmap_clear_bits(state->regmap, REG_CR3, BIT(channel)); + + if (val == 1) + return regmap_set_bits(state->regmap, REG_CR3, BIT(channel)); + + return -EINVAL; + default: + return -EOPNOTSUPP; + } + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_input: + if (val < 0 || val > 255) + return -EINVAL; + + return regmap_write(state->regmap, REG_PWMR, val); + case hwmon_pwm_enable: + if (val == 1) + return regmap_set_bits(state->regmap, REG_CR2, CR2_DFC); + + if (val == 2) + return regmap_clear_bits(state->regmap, REG_CR2, CR2_DFC); + + return -EINVAL; + case hwmon_pwm_freq: + pwm_index = find_closest(val, max31760_pwm_freq, + ARRAY_SIZE(max31760_pwm_freq)); + + return regmap_update_bits(state->regmap, + REG_CR1, CR1_DRV, + FIELD_PREP(CR1_DRV, pwm_index)); + case hwmon_pwm_auto_channels_temp: + switch (val) { + case 1: + break; + case 2: + val = 0; + break; + case 3: + val = 2; + break; + default: + return -EINVAL; + } + + return regmap_update_bits(state->regmap, REG_CR1, CR1_TEMP_SRC, val); + default: + return -EOPNOTSUPP; + } + default: + return -EOPNOTSUPP; + } +} + +static const struct hwmon_channel_info *max31760_info[] = { + HWMON_CHANNEL_INFO(chip, + HWMON_C_REGISTER_TZ), + HWMON_CHANNEL_INFO(fan, + HWMON_F_INPUT | HWMON_F_FAULT | HWMON_F_ENABLE, + HWMON_F_INPUT | HWMON_F_FAULT | HWMON_F_ENABLE), + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT | + HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | + HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | HWMON_T_LABEL), + HWMON_CHANNEL_INFO(pwm, + HWMON_PWM_ENABLE | HWMON_PWM_FREQ | HWMON_PWM_INPUT | + HWMON_PWM_AUTO_CHANNELS_TEMP), + NULL +}; + +static umode_t max31760_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_max_alarm: + case hwmon_temp_crit_alarm: + case hwmon_temp_fault: + case hwmon_temp_label: + return 0444; + case hwmon_temp_max: + case hwmon_temp_crit: + return 0644; + default: + return 0; + } + case hwmon_fan: + switch (attr) { + case hwmon_fan_input: + case hwmon_fan_fault: + return 0444; + case hwmon_fan_enable: + return 0644; + default: + return 0; + } + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_enable: + case hwmon_pwm_input: + case hwmon_pwm_freq: + case hwmon_pwm_auto_channels_temp: + return 0644; + default: + return 0; + } + default: + return 0; + } +} + +static int max31760_read_string(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) +{ + switch (type) { + case hwmon_temp: + if (attr != hwmon_temp_label) + return -EOPNOTSUPP; + + *str = channel ? "local" : "remote"; + + return 0; + default: + return -EOPNOTSUPP; + } +} + +static const struct hwmon_ops max31760_hwmon_ops = { + .is_visible = max31760_is_visible, + .read = max31760_read, + .write = max31760_write, + .read_string = max31760_read_string +}; + +static const struct hwmon_chip_info max31760_chip_info = { + .ops = &max31760_hwmon_ops, + .info = max31760_info, +}; + +static ssize_t lut_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + struct max31760_state *state = dev_get_drvdata(dev); + int ret; + unsigned int regval; + + ret = regmap_read(state->regmap, REG_LUT(sda->index), ®val); + if (ret) + return ret; + + return sysfs_emit(buf, "%d\n", regval); +} + +static ssize_t lut_store(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr); + struct max31760_state *state = dev_get_drvdata(dev); + int ret; + u8 pwm; + + ret = kstrtou8(buf, 10, &pwm); + if (ret) + return ret; + + ret = regmap_write(state->regmap, REG_LUT(sda->index), pwm); + if (ret) + return ret; + + return count; +} + +static ssize_t pwm1_auto_point_temp_hyst_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct max31760_state *state = dev_get_drvdata(dev); + unsigned int regval; + int ret; + + ret = regmap_read(state->regmap, REG_CR1, ®val); + if (ret) + return ret; + + return sysfs_emit(buf, "%d\n", (1 + (int)FIELD_GET(CR1_HYST, regval)) * 2000); +} + +static ssize_t pwm1_auto_point_temp_hyst_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct max31760_state *state = dev_get_drvdata(dev); + unsigned int hyst; + int ret; + + ret = kstrtou32(buf, 10, &hyst); + if (ret) + return ret; + + if (hyst < 3000) + ret = regmap_clear_bits(state->regmap, REG_CR1, CR1_HYST); + else + ret = regmap_set_bits(state->regmap, REG_CR1, CR1_HYST); + + if (ret) + return ret; + + return count; +} + +static DEVICE_ATTR_RW(pwm1_auto_point_temp_hyst); + +static void max31760_create_lut_nodes(struct max31760_state *state) +{ + int i; + struct sensor_device_attribute *sda; + struct lut_attribute *lut; + + for (i = 0; i < LUT_SIZE; ++i) { + lut = &state->lut[i]; + sda = &lut->sda; + + snprintf(lut->name, sizeof(lut->name), + "pwm1_auto_point%d_pwm", i + 1); + + sda->dev_attr.attr.mode = 0644; + sda->index = i; + sda->dev_attr.show = lut_show; + sda->dev_attr.store = lut_store; + sda->dev_attr.attr.name = lut->name; + + sysfs_attr_init(&sda->dev_attr.attr); + + state->attrs[i] = &sda->dev_attr.attr; + } + + state->attrs[i] = &dev_attr_pwm1_auto_point_temp_hyst.attr; + + state->group.attrs = state->attrs; + state->groups[0] = &state->group; +} + +static int max31760_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct max31760_state *state; + struct device *hwmon_dev; + int ret; + + state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); + if (!state) + return -ENOMEM; + + state->regmap = devm_regmap_init_i2c(client, ®map_config); + if (IS_ERR(state->regmap)) + return dev_err_probe(dev, + PTR_ERR(state->regmap), + "regmap initialization failed\n"); + + dev_set_drvdata(dev, state); + + /* Set alert output to comparator mode */ + ret = regmap_set_bits(state->regmap, REG_CR2, CR2_ALERTS); + if (ret) + return dev_err_probe(dev, ret, "cannot write register\n"); + + max31760_create_lut_nodes(state); + + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, + state, + &max31760_chip_info, + state->groups); + + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct of_device_id max31760_of_match[] = { + {.compatible = "adi,max31760"}, + { } +}; +MODULE_DEVICE_TABLE(of, max31760_of_match); + +static const struct i2c_device_id max31760_id[] = { + {"max31760"}, + { } +}; +MODULE_DEVICE_TABLE(i2c, max31760_id); + +static int max31760_suspend(struct device *dev) +{ + struct max31760_state *state = dev_get_drvdata(dev); + + return regmap_set_bits(state->regmap, REG_CR2, CR2_STBY); +} + +static int max31760_resume(struct device *dev) +{ + struct max31760_state *state = dev_get_drvdata(dev); + + return regmap_clear_bits(state->regmap, REG_CR2, CR2_STBY); +} + +static DEFINE_SIMPLE_DEV_PM_OPS(max31760_pm_ops, max31760_suspend, + max31760_resume); + +static struct i2c_driver max31760_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "max31760", + .of_match_table = max31760_of_match, + .pm = pm_ptr(&max31760_pm_ops) + }, + .probe_new = max31760_probe, + .id_table = max31760_id +}; +module_i2c_driver(max31760_driver); + +MODULE_AUTHOR("Ibrahim Tilki "); +MODULE_DESCRIPTION("Analog Devices MAX31760 Fan Speed Controller"); +MODULE_SOFTDEP("pre: regmap_i2c"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From cc842bd57e779ee242f6bcc88149dec4542ea563 Mon Sep 17 00:00:00 2001 From: Duke Du Date: Mon, 12 Sep 2022 11:01:08 +0800 Subject: hwmon: (pmbus) Add driver for the TEXAS TPS546D24 Buck Converter. Add the pmbus driver for TEXAS tps546d24 Buck Converter. The vout mode of tps546d24 supported relative data format, which is not supported by the PMBus core. Signed-off-by: Duke Du Link: https://lore.kernel.org/r/1662951668-9849-1-git-send-email-Duke.Du@quantatw.com [groeck: Add __maybe_unused to tps546d24_of_match declaration] Signed-off-by: Guenter Roeck --- Documentation/hwmon/index.rst | 1 + Documentation/hwmon/tps546d24.rst | 35 +++++++++++++++++++ MAINTAINERS | 7 ++++ drivers/hwmon/pmbus/Kconfig | 9 +++++ drivers/hwmon/pmbus/Makefile | 1 + drivers/hwmon/pmbus/tps546d24.c | 71 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 124 insertions(+) create mode 100644 Documentation/hwmon/tps546d24.rst create mode 100644 drivers/hwmon/pmbus/tps546d24.c (limited to 'drivers/hwmon') diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index b29d7ac794e5..c1d11cf13eef 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -206,6 +206,7 @@ Hardware Monitoring Kernel Drivers tps23861 tps40422 tps53679 + tps546d24 twl4030-madc-hwmon ucd9000 ucd9200 diff --git a/Documentation/hwmon/tps546d24.rst b/Documentation/hwmon/tps546d24.rst new file mode 100644 index 000000000000..97adb8a30fc0 --- /dev/null +++ b/Documentation/hwmon/tps546d24.rst @@ -0,0 +1,35 @@ +.. SPDX-License-Identifier: GPL-2.0-only + +Kernel driver tps546d24 +======================= + +Supported chips: + + * TI TPS546D24 + + Prefix: 'tps546d24' + + Addresses scanned: - + + Datasheet: https://www.ti.com/lit/gpn/tps546d24 + +Author: Duke Du + + +Description +----------- + +The TPS546D24A is a highly integrated, non-isolated DC/DC converter capable +of high frequency operation and 40-A current output from a 7-mm x 5-mm +package. + +Two, three, and four TPS546D24A devices can be interconnected +to provide up to 160 A on a single output. The device has an option to +overdrive the internal 5-V LDO with an external 5-V supply via the VDD5 +pin to improve efficiency and reduce power dissipation of the converter. + + +Platform data support +--------------------- + +The driver supports standard PMBus driver platform data. diff --git a/MAINTAINERS b/MAINTAINERS index e252ea3f9380..7f40ac1735ae 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20611,6 +20611,13 @@ Q: https://patchwork.kernel.org/project/linux-integrity/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/jarkko/linux-tpmdd.git F: drivers/char/tpm/ +TPS546D24 DRIVER +M: Duke Du +L: linux-hwmon@vger.kernel.org +S: Maintained +F: Documentation/hwmon/tps546d24.rst +F: drivers/hwmon/pmbus/tps546d24.c + TRACING M: Steven Rostedt M: Ingo Molnar diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 951e4a9ff2d6..89668af67206 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -397,6 +397,15 @@ config SENSORS_TPS53679 This driver can also be built as a module. If so, the module will be called tps53679. +config SENSORS_TPS546D24 + tristate "TPS546D24" + help + If you say yes here you get hardware monitoring support for TEXAS + TPS546D24. + + This driver can also be built as a module. If so, the module will + be called tps546d24 + config SENSORS_UCD9000 tristate "TI UCD90120, UCD90124, UCD90160, UCD90320, UCD9090, UCD90910" help diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile index e2fe86f98965..0002dbe22d52 100644 --- a/drivers/hwmon/pmbus/Makefile +++ b/drivers/hwmon/pmbus/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_SENSORS_Q54SJ108A2) += q54sj108a2.o obj-$(CONFIG_SENSORS_STPDDC60) += stpddc60.o obj-$(CONFIG_SENSORS_TPS40422) += tps40422.o obj-$(CONFIG_SENSORS_TPS53679) += tps53679.o +obj-$(CONFIG_SENSORS_TPS546D24) += tps546d24.o obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o obj-$(CONFIG_SENSORS_XDPE122) += xdpe12284.o diff --git a/drivers/hwmon/pmbus/tps546d24.c b/drivers/hwmon/pmbus/tps546d24.c new file mode 100644 index 000000000000..435f94304ad8 --- /dev/null +++ b/drivers/hwmon/pmbus/tps546d24.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Hardware monitoring driver for TEXAS TPS546D24 buck converter + */ + +#include +#include +#include +#include +#include +#include +#include "pmbus.h" + +static struct pmbus_driver_info tps546d24_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = linear, + .format[PSC_TEMPERATURE] = linear, + .format[PSC_CURRENT_OUT] = linear, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN + | PMBUS_HAVE_IOUT | PMBUS_HAVE_VOUT + | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP, +}; + +static int tps546d24_probe(struct i2c_client *client) +{ + int reg; + + reg = i2c_smbus_read_byte_data(client, PMBUS_VOUT_MODE); + if (reg < 0) + return reg; + + if (reg & 0x80) { + int err; + + err = i2c_smbus_write_byte_data(client, PMBUS_VOUT_MODE, reg & 0x7f); + if (err < 0) + return err; + } + return pmbus_do_probe(client, &tps546d24_info); +} + +static const struct i2c_device_id tps546d24_id[] = { + {"tps546d24", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, tps546d24_id); + +static const struct of_device_id __maybe_unused tps546d24_of_match[] = { + {.compatible = "ti,tps546d24"}, + {} +}; +MODULE_DEVICE_TABLE(of, tps546d24_of_match); + +/* This is the driver that will be inserted */ +static struct i2c_driver tps546d24_driver = { + .driver = { + .name = "tps546d24", + .of_match_table = of_match_ptr(tps546d24_of_match), + }, + .probe_new = tps546d24_probe, + .id_table = tps546d24_id, +}; + +module_i2c_driver(tps546d24_driver); + +MODULE_AUTHOR("Duke Du "); +MODULE_DESCRIPTION("PMBus driver for TI tps546d24"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); -- cgit v1.2.3 From bf10ccad068088f4e31f4d4266356d1d24cf5734 Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Wed, 14 Sep 2022 17:31:33 +0200 Subject: hwmon: (pwm-fan) Refactor fan power on/off In preparation for dynamically switching regulator, split the power on and power off sequence into separate functions. Signed-off-by: Alexander Stein Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20220914153137.613982-2-alexander.stein@ew.tq-group.com Signed-off-by: Guenter Roeck --- drivers/hwmon/pwm-fan.c | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c index 06fd1d75101d..c8a7926d39e7 100644 --- a/drivers/hwmon/pwm-fan.c +++ b/drivers/hwmon/pwm-fan.c @@ -82,23 +82,47 @@ static void sample_timer(struct timer_list *t) mod_timer(&ctx->rpm_timer, jiffies + HZ); } -static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm) +static int pwm_fan_power_on(struct pwm_fan_ctx *ctx) { + struct pwm_state *state = &ctx->pwm_state; unsigned long period; - int ret = 0; + int ret; + + period = state->period; + state->duty_cycle = DIV_ROUND_UP(ctx->pwm_value * (period - 1), MAX_PWM); + state->enabled = true; + ret = pwm_apply_state(ctx->pwm, state); + + return ret; +} + +static int pwm_fan_power_off(struct pwm_fan_ctx *ctx) +{ struct pwm_state *state = &ctx->pwm_state; + state->enabled = false; + state->duty_cycle = 0; + pwm_apply_state(ctx->pwm, state); + + return 0; +} + +static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm) +{ + int ret = 0; + mutex_lock(&ctx->lock); if (ctx->pwm_value == pwm) goto exit_set_pwm_err; - period = state->period; - state->duty_cycle = DIV_ROUND_UP(pwm * (period - 1), MAX_PWM); - state->enabled = pwm ? true : false; + if (pwm > 0) + ret = pwm_fan_power_on(ctx); + else + ret = pwm_fan_power_off(ctx); - ret = pwm_apply_state(ctx->pwm, state); if (!ret) ctx->pwm_value = pwm; + exit_set_pwm_err: mutex_unlock(&ctx->lock); return ret; -- cgit v1.2.3 From b77f0c7680a4df2a550be5e0158204de1aa3a3ce Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Wed, 14 Sep 2022 17:31:34 +0200 Subject: hwmon: (pwm-fan) Simplify enable/disable check Instead of comparing the current to the new pwm duty to decide whether to enable the PWM, use a dedicated flag. Also apply the new PWM duty in any case. This is a preparation to enable/disable the regulator dynamically. Signed-off-by: Alexander Stein Link: https://lore.kernel.org/r/20220914153137.613982-3-alexander.stein@ew.tq-group.com Signed-off-by: Guenter Roeck --- drivers/hwmon/pwm-fan.c | 43 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 9 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c index c8a7926d39e7..01412c71deb3 100644 --- a/drivers/hwmon/pwm-fan.c +++ b/drivers/hwmon/pwm-fan.c @@ -29,10 +29,13 @@ struct pwm_fan_tach { }; struct pwm_fan_ctx { + struct device *dev; + struct mutex lock; struct pwm_device *pwm; struct pwm_state pwm_state; struct regulator *reg_en; + bool enabled; int tach_count; struct pwm_fan_tach *tachs; @@ -85,13 +88,19 @@ static void sample_timer(struct timer_list *t) static int pwm_fan_power_on(struct pwm_fan_ctx *ctx) { struct pwm_state *state = &ctx->pwm_state; - unsigned long period; int ret; - period = state->period; - state->duty_cycle = DIV_ROUND_UP(ctx->pwm_value * (period - 1), MAX_PWM); + if (ctx->enabled) + return 0; + state->enabled = true; ret = pwm_apply_state(ctx->pwm, state); + if (ret) { + dev_err(ctx->dev, "failed to enable PWM\n"); + return ret; + } + + ctx->enabled = true; return ret; } @@ -99,27 +108,42 @@ static int pwm_fan_power_on(struct pwm_fan_ctx *ctx) static int pwm_fan_power_off(struct pwm_fan_ctx *ctx) { struct pwm_state *state = &ctx->pwm_state; + int ret; + + if (!ctx->enabled) + return 0; state->enabled = false; state->duty_cycle = 0; - pwm_apply_state(ctx->pwm, state); + ret = pwm_apply_state(ctx->pwm, state); + if (ret) { + dev_err(ctx->dev, "failed to disable PWM\n"); + return ret; + } + + ctx->enabled = false; return 0; } static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm) { + struct pwm_state *state = &ctx->pwm_state; + unsigned long period; int ret = 0; mutex_lock(&ctx->lock); - if (ctx->pwm_value == pwm) - goto exit_set_pwm_err; - if (pwm > 0) + if (pwm > 0) { + period = state->period; + state->duty_cycle = DIV_ROUND_UP(pwm * (period - 1), MAX_PWM); + ret = pwm_apply_state(ctx->pwm, state); + if (ret) + goto exit_set_pwm_err; ret = pwm_fan_power_on(ctx); - else + } else { ret = pwm_fan_power_off(ctx); - + } if (!ret) ctx->pwm_value = pwm; @@ -326,6 +350,7 @@ static int pwm_fan_probe(struct platform_device *pdev) mutex_init(&ctx->lock); + ctx->dev = &pdev->dev; ctx->pwm = devm_pwm_get(dev, NULL); if (IS_ERR(ctx->pwm)) return dev_err_probe(dev, PTR_ERR(ctx->pwm), "Could not get PWM\n"); -- cgit v1.2.3 From 9bf3aa60808803d56cb34bdff336e85c87c8026d Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Wed, 14 Sep 2022 17:31:35 +0200 Subject: hwmon: (pwm-fan) Add dedicated power switch function This handles enabling/disabling the regulator in a single function, while keeping the enables/disabled balanced. This is a preparation when regulator is switched from different code paths. Signed-off-by: Alexander Stein Link: https://lore.kernel.org/r/20220914153137.613982-4-alexander.stein@ew.tq-group.com Signed-off-by: Guenter Roeck --- drivers/hwmon/pwm-fan.c | 46 +++++++++++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 15 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c index 01412c71deb3..92c5b7f5ddd6 100644 --- a/drivers/hwmon/pwm-fan.c +++ b/drivers/hwmon/pwm-fan.c @@ -35,6 +35,7 @@ struct pwm_fan_ctx { struct pwm_device *pwm; struct pwm_state pwm_state; struct regulator *reg_en; + bool regulator_enabled; bool enabled; int tach_count; @@ -85,6 +86,25 @@ static void sample_timer(struct timer_list *t) mod_timer(&ctx->rpm_timer, jiffies + HZ); } +static int pwm_fan_switch_power(struct pwm_fan_ctx *ctx, bool on) +{ + int ret = 0; + + if (!ctx->reg_en) + return ret; + + if (!ctx->regulator_enabled && on) { + ret = regulator_enable(ctx->reg_en); + if (ret == 0) + ctx->regulator_enabled = true; + } else if (ctx->regulator_enabled && !on) { + ret = regulator_disable(ctx->reg_en); + if (ret == 0) + ctx->regulator_enabled = false; + } + return ret; +} + static int pwm_fan_power_on(struct pwm_fan_ctx *ctx) { struct pwm_state *state = &ctx->pwm_state; @@ -320,7 +340,7 @@ static int pwm_fan_of_get_cooling_data(struct device *dev, static void pwm_fan_regulator_disable(void *data) { - regulator_disable(data); + pwm_fan_switch_power(data, false); } static void pwm_fan_pwm_disable(void *__ctx) @@ -364,13 +384,13 @@ static int pwm_fan_probe(struct platform_device *pdev) ctx->reg_en = NULL; } else { - ret = regulator_enable(ctx->reg_en); + ret = pwm_fan_switch_power(ctx, true); if (ret) { dev_err(dev, "Failed to enable fan supply: %d\n", ret); return ret; } ret = devm_add_action_or_reset(dev, pwm_fan_regulator_disable, - ctx->reg_en); + ctx); if (ret) return ret; } @@ -516,12 +536,10 @@ static int pwm_fan_disable(struct device *dev) return ret; } - if (ctx->reg_en) { - ret = regulator_disable(ctx->reg_en); - if (ret) { - dev_err(dev, "Failed to disable fan supply: %d\n", ret); - return ret; - } + ret = pwm_fan_switch_power(ctx, false); + if (ret) { + dev_err(dev, "Failed to disable fan supply: %d\n", ret); + return ret; } return 0; @@ -543,12 +561,10 @@ static int pwm_fan_resume(struct device *dev) struct pwm_fan_ctx *ctx = dev_get_drvdata(dev); int ret; - if (ctx->reg_en) { - ret = regulator_enable(ctx->reg_en); - if (ret) { - dev_err(dev, "Failed to enable fan supply: %d\n", ret); - return ret; - } + ret = pwm_fan_switch_power(ctx, true); + if (ret) { + dev_err(dev, "Failed to enable fan supply: %d\n", ret); + return ret; } if (ctx->pwm_value == 0) -- cgit v1.2.3 From 9db6e7f500541b797acc207c790024f6179e2e6c Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Wed, 14 Sep 2022 17:31:36 +0200 Subject: hwmon: (pwm-fan) split __set_pwm into locked/unlocked functions Regular calls to set_pwm don't hold the mutex, but the upcoming update_enable support needs to call set_pwm with the mutex being held. So provide the previous behavior in set_pwm (handling the lock), while adding __set_pwm which assumes the lock being held. Signed-off-by: Alexander Stein Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20220914153137.613982-5-alexander.stein@ew.tq-group.com Signed-off-by: Guenter Roeck --- drivers/hwmon/pwm-fan.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c index 92c5b7f5ddd6..12ef3b3dbe22 100644 --- a/drivers/hwmon/pwm-fan.c +++ b/drivers/hwmon/pwm-fan.c @@ -152,14 +152,12 @@ static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm) unsigned long period; int ret = 0; - mutex_lock(&ctx->lock); - if (pwm > 0) { period = state->period; state->duty_cycle = DIV_ROUND_UP(pwm * (period - 1), MAX_PWM); ret = pwm_apply_state(ctx->pwm, state); if (ret) - goto exit_set_pwm_err; + return ret; ret = pwm_fan_power_on(ctx); } else { ret = pwm_fan_power_off(ctx); @@ -167,8 +165,17 @@ static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm) if (!ret) ctx->pwm_value = pwm; -exit_set_pwm_err: + return ret; +} + +static int set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm) +{ + int ret; + + mutex_lock(&ctx->lock); + ret = __set_pwm(ctx, pwm); mutex_unlock(&ctx->lock); + return ret; } @@ -192,7 +199,7 @@ static int pwm_fan_write(struct device *dev, enum hwmon_sensor_types type, if (val < 0 || val > MAX_PWM) return -EINVAL; - ret = __set_pwm(ctx, val); + ret = set_pwm(ctx, val); if (ret) return ret; @@ -280,7 +287,7 @@ pwm_fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) if (state == ctx->pwm_fan_state) return 0; - ret = __set_pwm(ctx, ctx->pwm_fan_cooling_levels[state]); + ret = set_pwm(ctx, ctx->pwm_fan_cooling_levels[state]); if (ret) { dev_err(&cdev->device, "Cannot set pwm!\n"); return ret; @@ -398,7 +405,7 @@ static int pwm_fan_probe(struct platform_device *pdev) pwm_init_state(ctx->pwm, &ctx->pwm_state); /* - * __set_pwm assumes that MAX_PWM * (period - 1) fits into an unsigned + * set_pwm assumes that MAX_PWM * (period - 1) fits into an unsigned * long. Check this here to prevent the fan running at a too low * frequency. */ @@ -408,7 +415,7 @@ static int pwm_fan_probe(struct platform_device *pdev) } /* Set duty cycle to maximum allowed and enable PWM output */ - ret = __set_pwm(ctx, MAX_PWM); + ret = set_pwm(ctx, MAX_PWM); if (ret) { dev_err(dev, "Failed to configure PWM: %d\n", ret); return ret; -- cgit v1.2.3 From b99152d4f04b379e31db6c1a2dda6b272c92039b Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Wed, 14 Sep 2022 17:31:37 +0200 Subject: hwmon: (pwm-fan) Switch regulator dynamically This adds the enable attribute which is used to select if zero PWM duty means to switch off regulator and PWM or to keep them enabled but at inactive PWM output level. Depending on the select enable mode, turn off the regulator and PWM if the PWM duty is zero, or keep them enabled. This is especially important for fan using inverted PWM signal polarity. Having regulator supplied and PWM disabled, some PWM controllers provide the active, rather than inactive signal. With this change the shutdown as well as suspend/resume paths require modifcations as well. Signed-off-by: Alexander Stein Link: https://lore.kernel.org/r/20220914153137.613982-6-alexander.stein@ew.tq-group.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/pwm-fan.rst | 12 +++ drivers/hwmon/pwm-fan.c | 210 +++++++++++++++++++++++++++------------- 2 files changed, 154 insertions(+), 68 deletions(-) (limited to 'drivers/hwmon') diff --git a/Documentation/hwmon/pwm-fan.rst b/Documentation/hwmon/pwm-fan.rst index 82fe96742fee..f77998b204ef 100644 --- a/Documentation/hwmon/pwm-fan.rst +++ b/Documentation/hwmon/pwm-fan.rst @@ -18,3 +18,15 @@ the hwmon's sysfs interface. The fan rotation speed returned via the optional 'fan1_input' is extrapolated from the sampled interrupts from the tachometer signal within 1 second. + +The driver provides the following sensor accesses in sysfs: + +=============== ======= ======================================================= +fan1_input ro fan tachometer speed in RPM +pwm1_enable rw keep enable mode, defines behaviour when pwm1=0 + 0 -> disable pwm and regulator + 1 -> enable pwm; if pwm==0, disable pwm, keep regulator enabled + 2 -> enable pwm; if pwm==0, keep pwm and regulator enabled + 3 -> enable pwm; if pwm==0, disable pwm and regulator +pwm1 rw relative speed (0-255), 255=max. speed. +=============== ======= ======================================================= diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c index 12ef3b3dbe22..498128eb81f1 100644 --- a/drivers/hwmon/pwm-fan.c +++ b/drivers/hwmon/pwm-fan.c @@ -28,6 +28,13 @@ struct pwm_fan_tach { u8 pulses_per_revolution; }; +enum pwm_fan_enable_mode { + pwm_off_reg_off, + pwm_disable_reg_enable, + pwm_enable_reg_enable, + pwm_disable_reg_disable, +}; + struct pwm_fan_ctx { struct device *dev; @@ -35,6 +42,7 @@ struct pwm_fan_ctx { struct pwm_device *pwm; struct pwm_state pwm_state; struct regulator *reg_en; + enum pwm_fan_enable_mode enable_mode; bool regulator_enabled; bool enabled; @@ -86,6 +94,29 @@ static void sample_timer(struct timer_list *t) mod_timer(&ctx->rpm_timer, jiffies + HZ); } +static void pwm_fan_enable_mode_2_state(int enable_mode, + struct pwm_state *state, + bool *enable_regulator) +{ + switch (enable_mode) { + case pwm_disable_reg_enable: + /* disable pwm, keep regulator enabled */ + state->enabled = false; + *enable_regulator = true; + break; + case pwm_enable_reg_enable: + /* keep pwm and regulator enabled */ + state->enabled = true; + *enable_regulator = true; + break; + case pwm_off_reg_off: + case pwm_disable_reg_disable: + /* disable pwm and regulator */ + state->enabled = false; + *enable_regulator = false; + } +} + static int pwm_fan_switch_power(struct pwm_fan_ctx *ctx, bool on) { int ret = 0; @@ -113,26 +144,41 @@ static int pwm_fan_power_on(struct pwm_fan_ctx *ctx) if (ctx->enabled) return 0; + ret = pwm_fan_switch_power(ctx, true); + if (ret < 0) { + dev_err(ctx->dev, "failed to enable power supply\n"); + return ret; + } + state->enabled = true; ret = pwm_apply_state(ctx->pwm, state); if (ret) { dev_err(ctx->dev, "failed to enable PWM\n"); - return ret; + goto disable_regulator; } ctx->enabled = true; + return 0; + +disable_regulator: + pwm_fan_switch_power(ctx, false); return ret; } static int pwm_fan_power_off(struct pwm_fan_ctx *ctx) { struct pwm_state *state = &ctx->pwm_state; + bool enable_regulator = false; int ret; if (!ctx->enabled) return 0; + pwm_fan_enable_mode_2_state(ctx->enable_mode, + state, + &enable_regulator); + state->enabled = false; state->duty_cycle = 0; ret = pwm_apply_state(ctx->pwm, state); @@ -141,6 +187,8 @@ static int pwm_fan_power_off(struct pwm_fan_ctx *ctx) return ret; } + pwm_fan_switch_power(ctx, enable_regulator); + ctx->enabled = false; return 0; @@ -153,6 +201,10 @@ static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm) int ret = 0; if (pwm > 0) { + if (ctx->enable_mode == pwm_off_reg_off) + /* pwm-fan hard disabled */ + return 0; + period = state->period; state->duty_cycle = DIV_ROUND_UP(pwm * (period - 1), MAX_PWM); ret = pwm_apply_state(ctx->pwm, state); @@ -190,20 +242,76 @@ static void pwm_fan_update_state(struct pwm_fan_ctx *ctx, unsigned long pwm) ctx->pwm_fan_state = i; } +static int pwm_fan_update_enable(struct pwm_fan_ctx *ctx, long val) +{ + int ret = 0; + int old_val; + + mutex_lock(&ctx->lock); + + if (ctx->enable_mode == val) + goto out; + + old_val = ctx->enable_mode; + ctx->enable_mode = val; + + if (val == 0) { + /* Disable pwm-fan unconditionally */ + ret = __set_pwm(ctx, 0); + if (ret) + ctx->enable_mode = old_val; + pwm_fan_update_state(ctx, 0); + } else { + /* + * Change PWM and/or regulator state if currently disabled + * Nothing to do if currently enabled + */ + if (!ctx->enabled) { + struct pwm_state *state = &ctx->pwm_state; + bool enable_regulator = false; + + state->duty_cycle = 0; + pwm_fan_enable_mode_2_state(val, + state, + &enable_regulator); + + pwm_apply_state(ctx->pwm, state); + pwm_fan_switch_power(ctx, enable_regulator); + pwm_fan_update_state(ctx, 0); + } + } +out: + mutex_unlock(&ctx->lock); + + return ret; +} + static int pwm_fan_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long val) { struct pwm_fan_ctx *ctx = dev_get_drvdata(dev); int ret; - if (val < 0 || val > MAX_PWM) - return -EINVAL; + switch (attr) { + case hwmon_pwm_input: + if (val < 0 || val > MAX_PWM) + return -EINVAL; + ret = set_pwm(ctx, val); + if (ret) + return ret; + pwm_fan_update_state(ctx, val); + break; + case hwmon_pwm_enable: + if (val < 0 || val > 3) + ret = -EINVAL; + else + ret = pwm_fan_update_enable(ctx, val); - ret = set_pwm(ctx, val); - if (ret) return ret; + default: + return -EOPNOTSUPP; + } - pwm_fan_update_state(ctx, val); return 0; } @@ -214,9 +322,15 @@ static int pwm_fan_read(struct device *dev, enum hwmon_sensor_types type, switch (type) { case hwmon_pwm: - *val = ctx->pwm_value; - return 0; - + switch (attr) { + case hwmon_pwm_input: + *val = ctx->pwm_value; + return 0; + case hwmon_pwm_enable: + *val = ctx->enable_mode; + return 0; + } + return -EOPNOTSUPP; case hwmon_fan: *val = ctx->tachs[channel].rpm; return 0; @@ -345,18 +459,14 @@ static int pwm_fan_of_get_cooling_data(struct device *dev, return 0; } -static void pwm_fan_regulator_disable(void *data) -{ - pwm_fan_switch_power(data, false); -} - -static void pwm_fan_pwm_disable(void *__ctx) +static void pwm_fan_cleanup(void *__ctx) { struct pwm_fan_ctx *ctx = __ctx; - ctx->pwm_state.enabled = false; - pwm_apply_state(ctx->pwm, &ctx->pwm_state); del_timer_sync(&ctx->rpm_timer); + /* Switch off everything */ + ctx->enable_mode = pwm_disable_reg_disable; + pwm_fan_power_off(ctx); } static int pwm_fan_probe(struct platform_device *pdev) @@ -390,16 +500,6 @@ static int pwm_fan_probe(struct platform_device *pdev) return PTR_ERR(ctx->reg_en); ctx->reg_en = NULL; - } else { - ret = pwm_fan_switch_power(ctx, true); - if (ret) { - dev_err(dev, "Failed to enable fan supply: %d\n", ret); - return ret; - } - ret = devm_add_action_or_reset(dev, pwm_fan_regulator_disable, - ctx); - if (ret) - return ret; } pwm_init_state(ctx->pwm, &ctx->pwm_state); @@ -414,14 +514,19 @@ static int pwm_fan_probe(struct platform_device *pdev) return -EINVAL; } - /* Set duty cycle to maximum allowed and enable PWM output */ + ctx->enable_mode = pwm_disable_reg_enable; + + /* + * Set duty cycle to maximum allowed and enable PWM output as well as + * the regulator. In case of error nothing is changed + */ ret = set_pwm(ctx, MAX_PWM); if (ret) { dev_err(dev, "Failed to configure PWM: %d\n", ret); return ret; } timer_setup(&ctx->rpm_timer, sample_timer, 0); - ret = devm_add_action_or_reset(dev, pwm_fan_pwm_disable, ctx); + ret = devm_add_action_or_reset(dev, pwm_fan_cleanup, ctx); if (ret) return ret; @@ -453,7 +558,7 @@ static int pwm_fan_probe(struct platform_device *pdev) if (!channels) return -ENOMEM; - channels[0] = HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT); + channels[0] = HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT | HWMON_PWM_ENABLE); for (i = 0; i < ctx->tach_count; i++) { struct pwm_fan_tach *tach = &ctx->tachs[i]; @@ -527,57 +632,26 @@ static int pwm_fan_probe(struct platform_device *pdev) return 0; } -static int pwm_fan_disable(struct device *dev) -{ - struct pwm_fan_ctx *ctx = dev_get_drvdata(dev); - int ret; - - if (ctx->pwm_value) { - /* keep ctx->pwm_state unmodified for pwm_fan_resume() */ - struct pwm_state state = ctx->pwm_state; - - state.duty_cycle = 0; - state.enabled = false; - ret = pwm_apply_state(ctx->pwm, &state); - if (ret < 0) - return ret; - } - - ret = pwm_fan_switch_power(ctx, false); - if (ret) { - dev_err(dev, "Failed to disable fan supply: %d\n", ret); - return ret; - } - - return 0; -} - static void pwm_fan_shutdown(struct platform_device *pdev) { - pwm_fan_disable(&pdev->dev); + struct pwm_fan_ctx *ctx = platform_get_drvdata(pdev); + + pwm_fan_cleanup(ctx); } #ifdef CONFIG_PM_SLEEP static int pwm_fan_suspend(struct device *dev) { - return pwm_fan_disable(dev); + struct pwm_fan_ctx *ctx = dev_get_drvdata(dev); + + return pwm_fan_power_off(ctx); } static int pwm_fan_resume(struct device *dev) { struct pwm_fan_ctx *ctx = dev_get_drvdata(dev); - int ret; - - ret = pwm_fan_switch_power(ctx, true); - if (ret) { - dev_err(dev, "Failed to enable fan supply: %d\n", ret); - return ret; - } - - if (ctx->pwm_value == 0) - return 0; - return pwm_apply_state(ctx->pwm, &ctx->pwm_state); + return set_pwm(ctx, ctx->pwm_value); } #endif -- cgit v1.2.3 From 02e0500553890dd11bb2d20d9b865d0af7a6ec22 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 19 Sep 2022 12:31:54 +0200 Subject: hwmon: (pc87360) Introduce a #define for the driver name and use it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make use of the cpp symbol DRIVER_NAME to set the driver's name and also as name for devm_request_region(). While at it add a module alias using the new define. This is a preparation for the next cleanup commit that removes a cyclic dependency between pc87360_driver (which references pc87360_probe in .probe) and pc87360_probe() (which used pc87360_driver.driver.name). Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20220919103155.795151-2-u.kleine-koenig@pengutronix.de Signed-off-by: Guenter Roeck --- drivers/hwmon/pc87360.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/pc87360.c b/drivers/hwmon/pc87360.c index 0828436a1f6c..b912926c8b18 100644 --- a/drivers/hwmon/pc87360.c +++ b/drivers/hwmon/pc87360.c @@ -35,6 +35,8 @@ #include #include +#define DRIVER_NAME "pc87360" + static u8 devid; static struct platform_device *pdev; static unsigned short extra_isa[3]; @@ -228,10 +230,9 @@ static struct pc87360_data *pc87360_update_device(struct device *dev); /* * Driver data */ - static struct platform_driver pc87360_driver = { .driver = { - .name = "pc87360", + .name = DRIVER_NAME, }, .probe = pc87360_probe, .remove = pc87360_remove, @@ -1239,7 +1240,7 @@ static int pc87360_probe(struct platform_device *pdev) data->address[i] = extra_isa[i]; if (data->address[i] && !devm_request_region(dev, extra_isa[i], PC87360_EXTENT, - pc87360_driver.driver.name)) { + DRIVER_NAME)) { dev_err(dev, "Region 0x%x-0x%x already in use!\n", extra_isa[i], extra_isa[i]+PC87360_EXTENT-1); @@ -1781,6 +1782,7 @@ static void __exit pc87360_exit(void) MODULE_AUTHOR("Jean Delvare "); MODULE_DESCRIPTION("PC8736x hardware monitor"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRIVER_NAME); module_init(pc87360_init); module_exit(pc87360_exit); -- cgit v1.2.3 From 070affa898d2afc64a9633880a520ad21c8cf5cc Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 19 Sep 2022 12:31:55 +0200 Subject: hwmon: (pc87360) Reorder symbols to get rid of a few forward declarations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Declarations for static symbols are useless repetition unless there are cyclic dependencies. Reorder the functions and variables to get rid of 6 forward declarations. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20220919103155.795151-3-u.kleine-koenig@pengutronix.de Signed-off-by: Guenter Roeck --- drivers/hwmon/pc87360.c | 1539 ++++++++++++++++++++++++----------------------- 1 file changed, 777 insertions(+), 762 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/pc87360.c b/drivers/hwmon/pc87360.c index b912926c8b18..a4adc8bd531f 100644 --- a/drivers/hwmon/pc87360.c +++ b/drivers/hwmon/pc87360.c @@ -37,6 +37,16 @@ #define DRIVER_NAME "pc87360" +/* (temp & vin) channel conversion status register flags (pdf sec.11.5.12) */ +#define CHAN_CNVRTD 0x80 /* new data ready */ +#define CHAN_ENA 0x01 /* enabled channel (temp or vin) */ +#define CHAN_ALM_ENA 0x10 /* propagate to alarms-reg ?? (chk val!) */ +#define CHAN_READY (CHAN_ENA|CHAN_CNVRTD) /* sample ready mask */ + +#define TEMP_OTS_OE 0x20 /* OTS Output Enable */ +#define VIN_RW1C_MASK (CHAN_READY|CHAN_ALM_MAX|CHAN_ALM_MIN) /* 0x87 */ +#define TEMP_RW1C_MASK (VIN_RW1C_MASK|TEMP_ALM_CRIT|TEMP_FAULT) /* 0xCF */ + static u8 devid; static struct platform_device *pdev; static unsigned short extra_isa[3]; @@ -213,207 +223,205 @@ struct pc87360_data { }; /* - * Functions declaration + * ldi is the logical device index + * bank is for voltages and temperatures only */ - -static int pc87360_probe(struct platform_device *pdev); -static int pc87360_remove(struct platform_device *pdev); - static int pc87360_read_value(struct pc87360_data *data, u8 ldi, u8 bank, - u8 reg); -static void pc87360_write_value(struct pc87360_data *data, u8 ldi, u8 bank, - u8 reg, u8 value); -static void pc87360_init_device(struct platform_device *pdev, - int use_thermistors); -static struct pc87360_data *pc87360_update_device(struct device *dev); - -/* - * Driver data - */ -static struct platform_driver pc87360_driver = { - .driver = { - .name = DRIVER_NAME, - }, - .probe = pc87360_probe, - .remove = pc87360_remove, -}; + u8 reg) +{ + int res; -/* - * Sysfs stuff - */ + mutex_lock(&(data->lock)); + if (bank != NO_BANK) + outb_p(bank, data->address[ldi] + PC87365_REG_BANK); + res = inb_p(data->address[ldi] + reg); + mutex_unlock(&(data->lock)); -static ssize_t fan_input_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", FAN_FROM_REG(data->fan[attr->index], - FAN_DIV_FROM_REG(data->fan_status[attr->index]))); -} -static ssize_t fan_min_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", FAN_FROM_REG(data->fan_min[attr->index], - FAN_DIV_FROM_REG(data->fan_status[attr->index]))); -} -static ssize_t fan_div_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", - FAN_DIV_FROM_REG(data->fan_status[attr->index])); + return res; } -static ssize_t fan_status_show(struct device *dev, - struct device_attribute *devattr, char *buf) + +static void pc87360_write_value(struct pc87360_data *data, u8 ldi, u8 bank, + u8 reg, u8 value) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", - FAN_STATUS_FROM_REG(data->fan_status[attr->index])); + mutex_lock(&(data->lock)); + if (bank != NO_BANK) + outb_p(bank, data->address[ldi] + PC87365_REG_BANK); + outb_p(value, data->address[ldi] + reg); + mutex_unlock(&(data->lock)); } -static ssize_t fan_min_store(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) + +static void pc87360_autodiv(struct device *dev, int nr) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = dev_get_drvdata(dev); - long fan_min; - int err; - - err = kstrtol(buf, 10, &fan_min); - if (err) - return err; - - mutex_lock(&data->update_lock); - fan_min = FAN_TO_REG(fan_min, - FAN_DIV_FROM_REG(data->fan_status[attr->index])); + u8 old_min = data->fan_min[nr]; - /* If it wouldn't fit, change clock divisor */ - while (fan_min > 255 - && (data->fan_status[attr->index] & 0x60) != 0x60) { - fan_min >>= 1; - data->fan[attr->index] >>= 1; - data->fan_status[attr->index] += 0x20; + /* Increase clock divider if needed and possible */ + if ((data->fan_status[nr] & 0x04) /* overflow flag */ + || (data->fan[nr] >= 224)) { /* next to overflow */ + if ((data->fan_status[nr] & 0x60) != 0x60) { + data->fan_status[nr] += 0x20; + data->fan_min[nr] >>= 1; + data->fan[nr] >>= 1; + dev_dbg(dev, + "Increasing clock divider to %d for fan %d\n", + FAN_DIV_FROM_REG(data->fan_status[nr]), nr + 1); + } + } else { + /* Decrease clock divider if possible */ + while (!(data->fan_min[nr] & 0x80) /* min "nails" divider */ + && data->fan[nr] < 85 /* bad accuracy */ + && (data->fan_status[nr] & 0x60) != 0x00) { + data->fan_status[nr] -= 0x20; + data->fan_min[nr] <<= 1; + data->fan[nr] <<= 1; + dev_dbg(dev, + "Decreasing clock divider to %d for fan %d\n", + FAN_DIV_FROM_REG(data->fan_status[nr]), + nr + 1); + } } - data->fan_min[attr->index] = fan_min > 255 ? 255 : fan_min; - pc87360_write_value(data, LD_FAN, NO_BANK, - PC87360_REG_FAN_MIN(attr->index), - data->fan_min[attr->index]); - - /* Write new divider, preserve alarm bits */ - pc87360_write_value(data, LD_FAN, NO_BANK, - PC87360_REG_FAN_STATUS(attr->index), - data->fan_status[attr->index] & 0xF9); - mutex_unlock(&data->update_lock); - - return count; -} - -static struct sensor_device_attribute fan_input[] = { - SENSOR_ATTR_RO(fan1_input, fan_input, 0), - SENSOR_ATTR_RO(fan2_input, fan_input, 1), - SENSOR_ATTR_RO(fan3_input, fan_input, 2), -}; -static struct sensor_device_attribute fan_status[] = { - SENSOR_ATTR_RO(fan1_status, fan_status, 0), - SENSOR_ATTR_RO(fan2_status, fan_status, 1), - SENSOR_ATTR_RO(fan3_status, fan_status, 2), -}; -static struct sensor_device_attribute fan_div[] = { - SENSOR_ATTR_RO(fan1_div, fan_div, 0), - SENSOR_ATTR_RO(fan2_div, fan_div, 1), - SENSOR_ATTR_RO(fan3_div, fan_div, 2), -}; -static struct sensor_device_attribute fan_min[] = { - SENSOR_ATTR_RW(fan1_min, fan_min, 0), - SENSOR_ATTR_RW(fan2_min, fan_min, 1), - SENSOR_ATTR_RW(fan3_min, fan_min, 2), -}; -#define FAN_UNIT_ATTRS(X) \ -{ &fan_input[X].dev_attr.attr, \ - &fan_status[X].dev_attr.attr, \ - &fan_div[X].dev_attr.attr, \ - &fan_min[X].dev_attr.attr, \ - NULL \ + /* Write new fan min if it changed */ + if (old_min != data->fan_min[nr]) { + pc87360_write_value(data, LD_FAN, NO_BANK, + PC87360_REG_FAN_MIN(nr), + data->fan_min[nr]); + } } -static ssize_t pwm_show(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", - PWM_FROM_REG(data->pwm[attr->index], - FAN_CONFIG_INVERT(data->fan_conf, - attr->index))); -} -static ssize_t pwm_store(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) +static struct pc87360_data *pc87360_update_device(struct device *dev) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = dev_get_drvdata(dev); - long val; - int err; - - err = kstrtol(buf, 10, &val); - if (err) - return err; + u8 i; mutex_lock(&data->update_lock); - data->pwm[attr->index] = PWM_TO_REG(val, - FAN_CONFIG_INVERT(data->fan_conf, attr->index)); - pc87360_write_value(data, LD_FAN, NO_BANK, PC87360_REG_PWM(attr->index), - data->pwm[attr->index]); - mutex_unlock(&data->update_lock); - return count; -} - -static struct sensor_device_attribute pwm[] = { - SENSOR_ATTR_RW(pwm1, pwm, 0), - SENSOR_ATTR_RW(pwm2, pwm, 1), - SENSOR_ATTR_RW(pwm3, pwm, 2), -}; -static struct attribute *pc8736x_fan_attr[][5] = { - FAN_UNIT_ATTRS(0), - FAN_UNIT_ATTRS(1), - FAN_UNIT_ATTRS(2) -}; + if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) { + dev_dbg(dev, "Data update\n"); -static const struct attribute_group pc8736x_fan_attr_group[] = { - { .attrs = pc8736x_fan_attr[0], }, - { .attrs = pc8736x_fan_attr[1], }, - { .attrs = pc8736x_fan_attr[2], }, -}; + /* Fans */ + for (i = 0; i < data->fannr; i++) { + if (FAN_CONFIG_MONITOR(data->fan_conf, i)) { + data->fan_status[i] = + pc87360_read_value(data, LD_FAN, + NO_BANK, PC87360_REG_FAN_STATUS(i)); + data->fan[i] = pc87360_read_value(data, LD_FAN, + NO_BANK, PC87360_REG_FAN(i)); + data->fan_min[i] = pc87360_read_value(data, + LD_FAN, NO_BANK, + PC87360_REG_FAN_MIN(i)); + /* Change clock divider if needed */ + pc87360_autodiv(dev, i); + /* Clear bits and write new divider */ + pc87360_write_value(data, LD_FAN, NO_BANK, + PC87360_REG_FAN_STATUS(i), + data->fan_status[i]); + } + if (FAN_CONFIG_CONTROL(data->fan_conf, i)) + data->pwm[i] = pc87360_read_value(data, LD_FAN, + NO_BANK, PC87360_REG_PWM(i)); + } -static ssize_t in_input_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", IN_FROM_REG(data->in[attr->index], - data->in_vref)); -} -static ssize_t in_min_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[attr->index], - data->in_vref)); -} -static ssize_t in_max_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + /* Voltages */ + for (i = 0; i < data->innr; i++) { + data->in_status[i] = pc87360_read_value(data, LD_IN, i, + PC87365_REG_IN_STATUS); + /* Clear bits */ + pc87360_write_value(data, LD_IN, i, + PC87365_REG_IN_STATUS, + data->in_status[i]); + if ((data->in_status[i] & CHAN_READY) == CHAN_READY) { + data->in[i] = pc87360_read_value(data, LD_IN, + i, PC87365_REG_IN); + } + if (data->in_status[i] & CHAN_ENA) { + data->in_min[i] = pc87360_read_value(data, + LD_IN, i, + PC87365_REG_IN_MIN); + data->in_max[i] = pc87360_read_value(data, + LD_IN, i, + PC87365_REG_IN_MAX); + if (i >= 11) + data->in_crit[i-11] = + pc87360_read_value(data, LD_IN, + i, PC87365_REG_TEMP_CRIT); + } + } + if (data->innr) { + data->in_alarms = pc87360_read_value(data, LD_IN, + NO_BANK, PC87365_REG_IN_ALARMS1) + | ((pc87360_read_value(data, LD_IN, + NO_BANK, PC87365_REG_IN_ALARMS2) + & 0x07) << 8); + data->vid = (data->vid_conf & 0xE0) ? + pc87360_read_value(data, LD_IN, + NO_BANK, PC87365_REG_VID) : 0x1F; + } + + /* Temperatures */ + for (i = 0; i < data->tempnr; i++) { + data->temp_status[i] = pc87360_read_value(data, + LD_TEMP, i, + PC87365_REG_TEMP_STATUS); + /* Clear bits */ + pc87360_write_value(data, LD_TEMP, i, + PC87365_REG_TEMP_STATUS, + data->temp_status[i]); + if ((data->temp_status[i] & CHAN_READY) == CHAN_READY) { + data->temp[i] = pc87360_read_value(data, + LD_TEMP, i, + PC87365_REG_TEMP); + } + if (data->temp_status[i] & CHAN_ENA) { + data->temp_min[i] = pc87360_read_value(data, + LD_TEMP, i, + PC87365_REG_TEMP_MIN); + data->temp_max[i] = pc87360_read_value(data, + LD_TEMP, i, + PC87365_REG_TEMP_MAX); + data->temp_crit[i] = pc87360_read_value(data, + LD_TEMP, i, + PC87365_REG_TEMP_CRIT); + } + } + if (data->tempnr) { + data->temp_alarms = pc87360_read_value(data, LD_TEMP, + NO_BANK, PC87365_REG_TEMP_ALARMS) + & 0x3F; + } + + data->last_updated = jiffies; + data->valid = true; + } + + mutex_unlock(&data->update_lock); + + return data; +} + +static ssize_t in_input_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[attr->index], + return sprintf(buf, "%u\n", IN_FROM_REG(data->in[attr->index], data->in_vref)); } + +static struct sensor_device_attribute in_input[] = { + SENSOR_ATTR_RO(in0_input, in_input, 0), + SENSOR_ATTR_RO(in1_input, in_input, 1), + SENSOR_ATTR_RO(in2_input, in_input, 2), + SENSOR_ATTR_RO(in3_input, in_input, 3), + SENSOR_ATTR_RO(in4_input, in_input, 4), + SENSOR_ATTR_RO(in5_input, in_input, 5), + SENSOR_ATTR_RO(in6_input, in_input, 6), + SENSOR_ATTR_RO(in7_input, in_input, 7), + SENSOR_ATTR_RO(in8_input, in_input, 8), + SENSOR_ATTR_RO(in9_input, in_input, 9), + SENSOR_ATTR_RO(in10_input, in_input, 10), +}; + static ssize_t in_status_show(struct device *dev, struct device_attribute *devattr, char *buf) { @@ -421,6 +429,30 @@ static ssize_t in_status_show(struct device *dev, struct pc87360_data *data = pc87360_update_device(dev); return sprintf(buf, "%u\n", data->in_status[attr->index]); } + +static struct sensor_device_attribute in_status[] = { + SENSOR_ATTR_RO(in0_status, in_status, 0), + SENSOR_ATTR_RO(in1_status, in_status, 1), + SENSOR_ATTR_RO(in2_status, in_status, 2), + SENSOR_ATTR_RO(in3_status, in_status, 3), + SENSOR_ATTR_RO(in4_status, in_status, 4), + SENSOR_ATTR_RO(in5_status, in_status, 5), + SENSOR_ATTR_RO(in6_status, in_status, 6), + SENSOR_ATTR_RO(in7_status, in_status, 7), + SENSOR_ATTR_RO(in8_status, in_status, 8), + SENSOR_ATTR_RO(in9_status, in_status, 9), + SENSOR_ATTR_RO(in10_status, in_status, 10), +}; + +static ssize_t in_min_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87360_data *data = pc87360_update_device(dev); + return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[attr->index], + data->in_vref)); +} + static ssize_t in_min_store(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) @@ -441,6 +473,30 @@ static ssize_t in_min_store(struct device *dev, mutex_unlock(&data->update_lock); return count; } + +static struct sensor_device_attribute in_min[] = { + SENSOR_ATTR_RW(in0_min, in_min, 0), + SENSOR_ATTR_RW(in1_min, in_min, 1), + SENSOR_ATTR_RW(in2_min, in_min, 2), + SENSOR_ATTR_RW(in3_min, in_min, 3), + SENSOR_ATTR_RW(in4_min, in_min, 4), + SENSOR_ATTR_RW(in5_min, in_min, 5), + SENSOR_ATTR_RW(in6_min, in_min, 6), + SENSOR_ATTR_RW(in7_min, in_min, 7), + SENSOR_ATTR_RW(in8_min, in_min, 8), + SENSOR_ATTR_RW(in9_min, in_min, 9), + SENSOR_ATTR_RW(in10_min, in_min, 10), +}; + +static ssize_t in_max_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87360_data *data = pc87360_update_device(dev); + return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[attr->index], + data->in_vref)); +} + static ssize_t in_max_store(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) @@ -463,45 +519,6 @@ static ssize_t in_max_store(struct device *dev, return count; } -static struct sensor_device_attribute in_input[] = { - SENSOR_ATTR_RO(in0_input, in_input, 0), - SENSOR_ATTR_RO(in1_input, in_input, 1), - SENSOR_ATTR_RO(in2_input, in_input, 2), - SENSOR_ATTR_RO(in3_input, in_input, 3), - SENSOR_ATTR_RO(in4_input, in_input, 4), - SENSOR_ATTR_RO(in5_input, in_input, 5), - SENSOR_ATTR_RO(in6_input, in_input, 6), - SENSOR_ATTR_RO(in7_input, in_input, 7), - SENSOR_ATTR_RO(in8_input, in_input, 8), - SENSOR_ATTR_RO(in9_input, in_input, 9), - SENSOR_ATTR_RO(in10_input, in_input, 10), -}; -static struct sensor_device_attribute in_status[] = { - SENSOR_ATTR_RO(in0_status, in_status, 0), - SENSOR_ATTR_RO(in1_status, in_status, 1), - SENSOR_ATTR_RO(in2_status, in_status, 2), - SENSOR_ATTR_RO(in3_status, in_status, 3), - SENSOR_ATTR_RO(in4_status, in_status, 4), - SENSOR_ATTR_RO(in5_status, in_status, 5), - SENSOR_ATTR_RO(in6_status, in_status, 6), - SENSOR_ATTR_RO(in7_status, in_status, 7), - SENSOR_ATTR_RO(in8_status, in_status, 8), - SENSOR_ATTR_RO(in9_status, in_status, 9), - SENSOR_ATTR_RO(in10_status, in_status, 10), -}; -static struct sensor_device_attribute in_min[] = { - SENSOR_ATTR_RW(in0_min, in_min, 0), - SENSOR_ATTR_RW(in1_min, in_min, 1), - SENSOR_ATTR_RW(in2_min, in_min, 2), - SENSOR_ATTR_RW(in3_min, in_min, 3), - SENSOR_ATTR_RW(in4_min, in_min, 4), - SENSOR_ATTR_RW(in5_min, in_min, 5), - SENSOR_ATTR_RW(in6_min, in_min, 6), - SENSOR_ATTR_RW(in7_min, in_min, 7), - SENSOR_ATTR_RW(in8_min, in_min, 8), - SENSOR_ATTR_RW(in9_min, in_min, 9), - SENSOR_ATTR_RW(in10_min, in_min, 10), -}; static struct sensor_device_attribute in_max[] = { SENSOR_ATTR_RW(in0_max, in_max, 0), SENSOR_ATTR_RW(in1_max, in_max, 1), @@ -535,14 +552,6 @@ static ssize_t in_min_alarm_show(struct device *dev, return sprintf(buf, "%u\n", !!(data->in_status[nr] & CHAN_ALM_MIN)); } -static ssize_t in_max_alarm_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct pc87360_data *data = pc87360_update_device(dev); - unsigned nr = to_sensor_dev_attr(devattr)->index; - - return sprintf(buf, "%u\n", !!(data->in_status[nr] & CHAN_ALM_MAX)); -} static struct sensor_device_attribute in_min_alarm[] = { SENSOR_ATTR_RO(in0_min_alarm, in_min_alarm, 0), @@ -557,6 +566,16 @@ static struct sensor_device_attribute in_min_alarm[] = { SENSOR_ATTR_RO(in9_min_alarm, in_min_alarm, 9), SENSOR_ATTR_RO(in10_min_alarm, in_min_alarm, 10), }; + +static ssize_t in_max_alarm_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct pc87360_data *data = pc87360_update_device(dev); + unsigned nr = to_sensor_dev_attr(devattr)->index; + + return sprintf(buf, "%u\n", !!(data->in_status[nr] & CHAN_ALM_MAX)); +} + static struct sensor_device_attribute in_max_alarm[] = { SENSOR_ATTR_RO(in0_max_alarm, in_max_alarm, 0), SENSOR_ATTR_RO(in1_max_alarm, in_max_alarm, 1), @@ -593,6 +612,7 @@ static ssize_t vrm_show(struct device *dev, struct device_attribute *attr, struct pc87360_data *data = dev_get_drvdata(dev); return sprintf(buf, "%u\n", data->vrm); } + static ssize_t vrm_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -649,37 +669,39 @@ static ssize_t therm_input_show(struct device *dev, return sprintf(buf, "%u\n", IN_FROM_REG(data->in[attr->index], data->in_vref)); } -static ssize_t therm_min_show(struct device *dev, - struct device_attribute *devattr, char *buf) + +/* + * the +11 term below reflects the fact that VLM units 11,12,13 are + * used in the chip to measure voltage across the thermistors + */ +static struct sensor_device_attribute therm_input[] = { + SENSOR_ATTR_RO(temp4_input, therm_input, 0 + 11), + SENSOR_ATTR_RO(temp5_input, therm_input, 1 + 11), + SENSOR_ATTR_RO(temp6_input, therm_input, 2 + 11), +}; + +static ssize_t therm_status_show(struct device *dev, + struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[attr->index], - data->in_vref)); + return sprintf(buf, "%u\n", data->in_status[attr->index]); } -static ssize_t therm_max_show(struct device *dev, + +static struct sensor_device_attribute therm_status[] = { + SENSOR_ATTR_RO(temp4_status, therm_status, 0 + 11), + SENSOR_ATTR_RO(temp5_status, therm_status, 1 + 11), + SENSOR_ATTR_RO(temp6_status, therm_status, 2 + 11), +}; + +static ssize_t therm_min_show(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[attr->index], - data->in_vref)); -} -static ssize_t therm_crit_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", IN_FROM_REG(data->in_crit[attr->index-11], + return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[attr->index], data->in_vref)); } -static ssize_t therm_status_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", data->in_status[attr->index]); -} static ssize_t therm_min_store(struct device *dev, struct device_attribute *devattr, @@ -702,14 +724,29 @@ static ssize_t therm_min_store(struct device *dev, return count; } -static ssize_t therm_max_store(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct pc87360_data *data = dev_get_drvdata(dev); - long val; - int err; +static struct sensor_device_attribute therm_min[] = { + SENSOR_ATTR_RW(temp4_min, therm_min, 0 + 11), + SENSOR_ATTR_RW(temp5_min, therm_min, 1 + 11), + SENSOR_ATTR_RW(temp6_min, therm_min, 2 + 11), +}; + +static ssize_t therm_max_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87360_data *data = pc87360_update_device(dev); + return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[attr->index], + data->in_vref)); +} + +static ssize_t therm_max_store(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87360_data *data = dev_get_drvdata(dev); + long val; + int err; err = kstrtol(buf, 10, &val); if (err) @@ -722,6 +759,22 @@ static ssize_t therm_max_store(struct device *dev, mutex_unlock(&data->update_lock); return count; } + +static struct sensor_device_attribute therm_max[] = { + SENSOR_ATTR_RW(temp4_max, therm_max, 0 + 11), + SENSOR_ATTR_RW(temp5_max, therm_max, 1 + 11), + SENSOR_ATTR_RW(temp6_max, therm_max, 2 + 11), +}; + +static ssize_t therm_crit_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87360_data *data = pc87360_update_device(dev); + return sprintf(buf, "%u\n", IN_FROM_REG(data->in_crit[attr->index-11], + data->in_vref)); +} + static ssize_t therm_crit_store(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) @@ -743,30 +796,6 @@ static ssize_t therm_crit_store(struct device *dev, return count; } -/* - * the +11 term below reflects the fact that VLM units 11,12,13 are - * used in the chip to measure voltage across the thermistors - */ -static struct sensor_device_attribute therm_input[] = { - SENSOR_ATTR_RO(temp4_input, therm_input, 0 + 11), - SENSOR_ATTR_RO(temp5_input, therm_input, 1 + 11), - SENSOR_ATTR_RO(temp6_input, therm_input, 2 + 11), -}; -static struct sensor_device_attribute therm_status[] = { - SENSOR_ATTR_RO(temp4_status, therm_status, 0 + 11), - SENSOR_ATTR_RO(temp5_status, therm_status, 1 + 11), - SENSOR_ATTR_RO(temp6_status, therm_status, 2 + 11), -}; -static struct sensor_device_attribute therm_min[] = { - SENSOR_ATTR_RW(temp4_min, therm_min, 0 + 11), - SENSOR_ATTR_RW(temp5_min, therm_min, 1 + 11), - SENSOR_ATTR_RW(temp6_min, therm_min, 2 + 11), -}; -static struct sensor_device_attribute therm_max[] = { - SENSOR_ATTR_RW(temp4_max, therm_max, 0 + 11), - SENSOR_ATTR_RW(temp5_max, therm_max, 1 + 11), - SENSOR_ATTR_RW(temp6_max, therm_max, 2 + 11), -}; static struct sensor_device_attribute therm_crit[] = { SENSOR_ATTR_RW(temp4_crit, therm_crit, 0 + 11), SENSOR_ATTR_RW(temp5_crit, therm_crit, 1 + 11), @@ -777,7 +806,6 @@ static struct sensor_device_attribute therm_crit[] = { * show_therm_min/max_alarm() reads data from the per-channel voltage * status register (sec 11.5.12) */ - static ssize_t therm_min_alarm_show(struct device *dev, struct device_attribute *devattr, char *buf) @@ -787,6 +815,13 @@ static ssize_t therm_min_alarm_show(struct device *dev, return sprintf(buf, "%u\n", !!(data->in_status[nr] & CHAN_ALM_MIN)); } + +static struct sensor_device_attribute therm_min_alarm[] = { + SENSOR_ATTR_RO(temp4_min_alarm, therm_min_alarm, 0 + 11), + SENSOR_ATTR_RO(temp5_min_alarm, therm_min_alarm, 1 + 11), + SENSOR_ATTR_RO(temp6_min_alarm, therm_min_alarm, 2 + 11), +}; + static ssize_t therm_max_alarm_show(struct device *dev, struct device_attribute *devattr, char *buf) @@ -796,6 +831,13 @@ static ssize_t therm_max_alarm_show(struct device *dev, return sprintf(buf, "%u\n", !!(data->in_status[nr] & CHAN_ALM_MAX)); } + +static struct sensor_device_attribute therm_max_alarm[] = { + SENSOR_ATTR_RO(temp4_max_alarm, therm_max_alarm, 0 + 11), + SENSOR_ATTR_RO(temp5_max_alarm, therm_max_alarm, 1 + 11), + SENSOR_ATTR_RO(temp6_max_alarm, therm_max_alarm, 2 + 11), +}; + static ssize_t therm_crit_alarm_show(struct device *dev, struct device_attribute *devattr, char *buf) @@ -806,16 +848,6 @@ static ssize_t therm_crit_alarm_show(struct device *dev, return sprintf(buf, "%u\n", !!(data->in_status[nr] & TEMP_ALM_CRIT)); } -static struct sensor_device_attribute therm_min_alarm[] = { - SENSOR_ATTR_RO(temp4_min_alarm, therm_min_alarm, 0 + 11), - SENSOR_ATTR_RO(temp5_min_alarm, therm_min_alarm, 1 + 11), - SENSOR_ATTR_RO(temp6_min_alarm, therm_min_alarm, 2 + 11), -}; -static struct sensor_device_attribute therm_max_alarm[] = { - SENSOR_ATTR_RO(temp4_max_alarm, therm_max_alarm, 0 + 11), - SENSOR_ATTR_RO(temp5_max_alarm, therm_max_alarm, 1 + 11), - SENSOR_ATTR_RO(temp6_max_alarm, therm_max_alarm, 2 + 11), -}; static struct sensor_device_attribute therm_crit_alarm[] = { SENSOR_ATTR_RO(temp4_crit_alarm, therm_crit_alarm, 0 + 11), SENSOR_ATTR_RO(temp5_crit_alarm, therm_crit_alarm, 1 + 11), @@ -850,37 +882,32 @@ static ssize_t temp_input_show(struct device *dev, return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[attr->index])); } -static ssize_t temp_min_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_min[attr->index])); -} +static struct sensor_device_attribute temp_input[] = { + SENSOR_ATTR_RO(temp1_input, temp_input, 0), + SENSOR_ATTR_RO(temp2_input, temp_input, 1), + SENSOR_ATTR_RO(temp3_input, temp_input, 2), +}; -static ssize_t temp_max_show(struct device *dev, - struct device_attribute *devattr, char *buf) +static ssize_t temp_status_show(struct device *dev, + struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[attr->index])); + return sprintf(buf, "%d\n", data->temp_status[attr->index]); } -static ssize_t temp_crit_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%d\n", - TEMP_FROM_REG(data->temp_crit[attr->index])); -} +static struct sensor_device_attribute temp_status[] = { + SENSOR_ATTR_RO(temp1_status, temp_status, 0), + SENSOR_ATTR_RO(temp2_status, temp_status, 1), + SENSOR_ATTR_RO(temp3_status, temp_status, 2), +}; -static ssize_t temp_status_show(struct device *dev, - struct device_attribute *devattr, char *buf) +static ssize_t temp_min_show(struct device *dev, + struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%d\n", data->temp_status[attr->index]); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_min[attr->index])); } static ssize_t temp_min_store(struct device *dev, @@ -904,6 +931,20 @@ static ssize_t temp_min_store(struct device *dev, return count; } +static struct sensor_device_attribute temp_min[] = { + SENSOR_ATTR_RW(temp1_min, temp_min, 0), + SENSOR_ATTR_RW(temp2_min, temp_min, 1), + SENSOR_ATTR_RW(temp3_min, temp_min, 2), +}; + +static ssize_t temp_max_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87360_data *data = pc87360_update_device(dev); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[attr->index])); +} + static ssize_t temp_max_store(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) @@ -925,6 +966,21 @@ static ssize_t temp_max_store(struct device *dev, return count; } +static struct sensor_device_attribute temp_max[] = { + SENSOR_ATTR_RW(temp1_max, temp_max, 0), + SENSOR_ATTR_RW(temp2_max, temp_max, 1), + SENSOR_ATTR_RW(temp3_max, temp_max, 2), +}; + +static ssize_t temp_crit_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87360_data *data = pc87360_update_device(dev); + return sprintf(buf, "%d\n", + TEMP_FROM_REG(data->temp_crit[attr->index])); +} + static ssize_t temp_crit_store(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) @@ -946,47 +1002,17 @@ static ssize_t temp_crit_store(struct device *dev, return count; } -static struct sensor_device_attribute temp_input[] = { - SENSOR_ATTR_RO(temp1_input, temp_input, 0), - SENSOR_ATTR_RO(temp2_input, temp_input, 1), - SENSOR_ATTR_RO(temp3_input, temp_input, 2), -}; -static struct sensor_device_attribute temp_status[] = { - SENSOR_ATTR_RO(temp1_status, temp_status, 0), - SENSOR_ATTR_RO(temp2_status, temp_status, 1), - SENSOR_ATTR_RO(temp3_status, temp_status, 2), -}; -static struct sensor_device_attribute temp_min[] = { - SENSOR_ATTR_RW(temp1_min, temp_min, 0), - SENSOR_ATTR_RW(temp2_min, temp_min, 1), - SENSOR_ATTR_RW(temp3_min, temp_min, 2), -}; -static struct sensor_device_attribute temp_max[] = { - SENSOR_ATTR_RW(temp1_max, temp_max, 0), - SENSOR_ATTR_RW(temp2_max, temp_max, 1), - SENSOR_ATTR_RW(temp3_max, temp_max, 2), -}; static struct sensor_device_attribute temp_crit[] = { SENSOR_ATTR_RW(temp1_crit, temp_crit, 0), SENSOR_ATTR_RW(temp2_crit, temp_crit, 1), SENSOR_ATTR_RW(temp3_crit, temp_crit, 2), }; -static ssize_t alarms_temp_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", data->temp_alarms); -} - -static DEVICE_ATTR_RO(alarms_temp); - /* - * show_temp_min/max_alarm() reads data from the per-channel status + * temp_min/max_alarm_show() reads data from the per-channel status * register (sec 12.3.7), not the temp event status registers (sec * 12.3.2) that show_temp_alarm() reads (via data->temp_alarms) */ - static ssize_t temp_min_alarm_show(struct device *dev, struct device_attribute *devattr, char *buf) @@ -997,6 +1023,12 @@ static ssize_t temp_min_alarm_show(struct device *dev, return sprintf(buf, "%u\n", !!(data->temp_status[nr] & CHAN_ALM_MIN)); } +static struct sensor_device_attribute temp_min_alarm[] = { + SENSOR_ATTR_RO(temp1_min_alarm, temp_min_alarm, 0), + SENSOR_ATTR_RO(temp2_min_alarm, temp_min_alarm, 1), + SENSOR_ATTR_RO(temp3_min_alarm, temp_min_alarm, 2), +}; + static ssize_t temp_max_alarm_show(struct device *dev, struct device_attribute *devattr, char *buf) @@ -1007,6 +1039,12 @@ static ssize_t temp_max_alarm_show(struct device *dev, return sprintf(buf, "%u\n", !!(data->temp_status[nr] & CHAN_ALM_MAX)); } +static struct sensor_device_attribute temp_max_alarm[] = { + SENSOR_ATTR_RO(temp1_max_alarm, temp_max_alarm, 0), + SENSOR_ATTR_RO(temp2_max_alarm, temp_max_alarm, 1), + SENSOR_ATTR_RO(temp3_max_alarm, temp_max_alarm, 2), +}; + static ssize_t temp_crit_alarm_show(struct device *dev, struct device_attribute *devattr, char *buf) @@ -1017,18 +1055,6 @@ static ssize_t temp_crit_alarm_show(struct device *dev, return sprintf(buf, "%u\n", !!(data->temp_status[nr] & TEMP_ALM_CRIT)); } -static struct sensor_device_attribute temp_min_alarm[] = { - SENSOR_ATTR_RO(temp1_min_alarm, temp_min_alarm, 0), - SENSOR_ATTR_RO(temp2_min_alarm, temp_min_alarm, 1), - SENSOR_ATTR_RO(temp3_min_alarm, temp_min_alarm, 2), -}; - -static struct sensor_device_attribute temp_max_alarm[] = { - SENSOR_ATTR_RO(temp1_max_alarm, temp_max_alarm, 0), - SENSOR_ATTR_RO(temp2_max_alarm, temp_max_alarm, 1), - SENSOR_ATTR_RO(temp3_max_alarm, temp_max_alarm, 2), -}; - static struct sensor_device_attribute temp_crit_alarm[] = { SENSOR_ATTR_RO(temp1_crit_alarm, temp_crit_alarm, 0), SENSOR_ATTR_RO(temp2_crit_alarm, temp_crit_alarm, 1), @@ -1044,6 +1070,7 @@ static ssize_t temp_fault_show(struct device *dev, return sprintf(buf, "%u\n", !!(data->temp_status[nr] & TEMP_FAULT)); } + static struct sensor_device_attribute temp_fault[] = { SENSOR_ATTR_RO(temp1_fault, temp_fault, 0), SENSOR_ATTR_RO(temp2_fault, temp_fault, 1), @@ -1075,323 +1102,195 @@ static const struct attribute_group pc8736x_temp_attr_group[] = { { .attrs = pc8736x_temp_attr[2] } }; -static ssize_t name_show(struct device *dev, - struct device_attribute *devattr, char *buf) +static ssize_t alarms_temp_show(struct device *dev, + struct device_attribute *attr, char *buf) { - struct pc87360_data *data = dev_get_drvdata(dev); - return sprintf(buf, "%s\n", data->name); + struct pc87360_data *data = pc87360_update_device(dev); + return sprintf(buf, "%u\n", data->temp_alarms); } -static DEVICE_ATTR_RO(name); +static DEVICE_ATTR_RO(alarms_temp); -/* - * Device detection, registration and update - */ +static ssize_t fan_input_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87360_data *data = pc87360_update_device(dev); + return sprintf(buf, "%u\n", FAN_FROM_REG(data->fan[attr->index], + FAN_DIV_FROM_REG(data->fan_status[attr->index]))); +} -static int __init pc87360_find(int sioaddr, u8 *devid, - unsigned short *addresses) +static struct sensor_device_attribute fan_input[] = { + SENSOR_ATTR_RO(fan1_input, fan_input, 0), + SENSOR_ATTR_RO(fan2_input, fan_input, 1), + SENSOR_ATTR_RO(fan3_input, fan_input, 2), +}; + +static ssize_t fan_status_show(struct device *dev, + struct device_attribute *devattr, char *buf) { - u16 val; - int i; - int nrdev; /* logical device count */ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87360_data *data = pc87360_update_device(dev); + return sprintf(buf, "%u\n", + FAN_STATUS_FROM_REG(data->fan_status[attr->index])); +} - /* No superio_enter */ +static struct sensor_device_attribute fan_status[] = { + SENSOR_ATTR_RO(fan1_status, fan_status, 0), + SENSOR_ATTR_RO(fan2_status, fan_status, 1), + SENSOR_ATTR_RO(fan3_status, fan_status, 2), +}; - /* Identify device */ - val = force_id ? force_id : superio_inb(sioaddr, DEVID); - switch (val) { - case 0xE1: /* PC87360 */ - case 0xE8: /* PC87363 */ - case 0xE4: /* PC87364 */ - nrdev = 1; - break; - case 0xE5: /* PC87365 */ - case 0xE9: /* PC87366 */ - nrdev = 3; - break; - default: - superio_exit(sioaddr); - return -ENODEV; - } - /* Remember the device id */ - *devid = val; - - for (i = 0; i < nrdev; i++) { - /* select logical device */ - superio_outb(sioaddr, DEV, logdev[i]); - - val = superio_inb(sioaddr, ACT); - if (!(val & 0x01)) { - pr_info("Device 0x%02x not activated\n", logdev[i]); - continue; - } - - val = (superio_inb(sioaddr, BASE) << 8) - | superio_inb(sioaddr, BASE + 1); - if (!val) { - pr_info("Base address not set for device 0x%02x\n", - logdev[i]); - continue; - } - - addresses[i] = val; - - if (i == 0) { /* Fans */ - confreg[0] = superio_inb(sioaddr, 0xF0); - confreg[1] = superio_inb(sioaddr, 0xF1); - - pr_debug("Fan %d: mon=%d ctrl=%d inv=%d\n", 1, - (confreg[0] >> 2) & 1, (confreg[0] >> 3) & 1, - (confreg[0] >> 4) & 1); - pr_debug("Fan %d: mon=%d ctrl=%d inv=%d\n", 2, - (confreg[0] >> 5) & 1, (confreg[0] >> 6) & 1, - (confreg[0] >> 7) & 1); - pr_debug("Fan %d: mon=%d ctrl=%d inv=%d\n", 3, - confreg[1] & 1, (confreg[1] >> 1) & 1, - (confreg[1] >> 2) & 1); - } else if (i == 1) { /* Voltages */ - /* Are we using thermistors? */ - if (*devid == 0xE9) { /* PC87366 */ - /* - * These registers are not logical-device - * specific, just that we won't need them if - * we don't use the VLM device - */ - confreg[2] = superio_inb(sioaddr, 0x2B); - confreg[3] = superio_inb(sioaddr, 0x25); - - if (confreg[2] & 0x40) { - pr_info("Using thermistors for temperature monitoring\n"); - } - if (confreg[3] & 0xE0) { - pr_info("VID inputs routed (mode %u)\n", - confreg[3] >> 5); - } - } - } - } - - superio_exit(sioaddr); - return 0; +static ssize_t fan_div_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87360_data *data = pc87360_update_device(dev); + return sprintf(buf, "%u\n", + FAN_DIV_FROM_REG(data->fan_status[attr->index])); } -static void pc87360_remove_files(struct device *dev) -{ - int i; +static struct sensor_device_attribute fan_div[] = { + SENSOR_ATTR_RO(fan1_div, fan_div, 0), + SENSOR_ATTR_RO(fan2_div, fan_div, 1), + SENSOR_ATTR_RO(fan3_div, fan_div, 2), +}; - device_remove_file(dev, &dev_attr_name); - device_remove_file(dev, &dev_attr_alarms_temp); - for (i = 0; i < ARRAY_SIZE(pc8736x_temp_attr_group); i++) - sysfs_remove_group(&dev->kobj, &pc8736x_temp_attr_group[i]); - for (i = 0; i < ARRAY_SIZE(pc8736x_fan_attr_group); i++) { - sysfs_remove_group(&pdev->dev.kobj, &pc8736x_fan_attr_group[i]); - device_remove_file(dev, &pwm[i].dev_attr); - } - sysfs_remove_group(&dev->kobj, &pc8736x_therm_group); - sysfs_remove_group(&dev->kobj, &pc8736x_vin_group); +static ssize_t fan_min_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87360_data *data = pc87360_update_device(dev); + return sprintf(buf, "%u\n", FAN_FROM_REG(data->fan_min[attr->index], + FAN_DIV_FROM_REG(data->fan_status[attr->index]))); } -static int pc87360_probe(struct platform_device *pdev) +static ssize_t fan_min_store(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) { - int i; - struct pc87360_data *data; - int err = 0; - const char *name; - int use_thermistors = 0; - struct device *dev = &pdev->dev; - - data = devm_kzalloc(dev, sizeof(struct pc87360_data), GFP_KERNEL); - if (!data) - return -ENOMEM; - - switch (devid) { - default: - name = "pc87360"; - data->fannr = 2; - break; - case 0xe8: - name = "pc87363"; - data->fannr = 2; - break; - case 0xe4: - name = "pc87364"; - data->fannr = 3; - break; - case 0xe5: - name = "pc87365"; - data->fannr = extra_isa[0] ? 3 : 0; - data->innr = extra_isa[1] ? 11 : 0; - data->tempnr = extra_isa[2] ? 2 : 0; - break; - case 0xe9: - name = "pc87366"; - data->fannr = extra_isa[0] ? 3 : 0; - data->innr = extra_isa[1] ? 14 : 0; - data->tempnr = extra_isa[2] ? 3 : 0; - break; - } - - data->name = name; - mutex_init(&data->lock); - mutex_init(&data->update_lock); - platform_set_drvdata(pdev, data); - - for (i = 0; i < LDNI_MAX; i++) { - data->address[i] = extra_isa[i]; - if (data->address[i] - && !devm_request_region(dev, extra_isa[i], PC87360_EXTENT, - DRIVER_NAME)) { - dev_err(dev, - "Region 0x%x-0x%x already in use!\n", - extra_isa[i], extra_isa[i]+PC87360_EXTENT-1); - return -EBUSY; - } - } - - /* Retrieve the fans configuration from Super-I/O space */ - if (data->fannr) - data->fan_conf = confreg[0] | (confreg[1] << 8); - - /* - * Use the correct reference voltage - * Unless both the VLM and the TMS logical devices agree to - * use an external Vref, the internal one is used. - */ - if (data->innr) { - i = pc87360_read_value(data, LD_IN, NO_BANK, - PC87365_REG_IN_CONFIG); - if (data->tempnr) { - i &= pc87360_read_value(data, LD_TEMP, NO_BANK, - PC87365_REG_TEMP_CONFIG); - } - data->in_vref = (i&0x02) ? 3025 : 2966; - dev_dbg(dev, "Using %s reference voltage\n", - (i&0x02) ? "external" : "internal"); - - data->vid_conf = confreg[3]; - data->vrm = vid_which_vrm(); - } - - /* Fan clock dividers may be needed before any data is read */ - for (i = 0; i < data->fannr; i++) { - if (FAN_CONFIG_MONITOR(data->fan_conf, i)) - data->fan_status[i] = pc87360_read_value(data, - LD_FAN, NO_BANK, - PC87360_REG_FAN_STATUS(i)); - } - - if (init > 0) { - if (devid == 0xe9 && data->address[1]) /* PC87366 */ - use_thermistors = confreg[2] & 0x40; - - pc87360_init_device(pdev, use_thermistors); - } - - /* Register all-or-nothing sysfs groups */ - - if (data->innr) { - err = sysfs_create_group(&dev->kobj, &pc8736x_vin_group); - if (err) - goto error; - } + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87360_data *data = dev_get_drvdata(dev); + long fan_min; + int err; - if (data->innr == 14) { - err = sysfs_create_group(&dev->kobj, &pc8736x_therm_group); - if (err) - goto error; - } + err = kstrtol(buf, 10, &fan_min); + if (err) + return err; - /* create device attr-files for varying sysfs groups */ + mutex_lock(&data->update_lock); + fan_min = FAN_TO_REG(fan_min, + FAN_DIV_FROM_REG(data->fan_status[attr->index])); - if (data->tempnr) { - for (i = 0; i < data->tempnr; i++) { - err = sysfs_create_group(&dev->kobj, - &pc8736x_temp_attr_group[i]); - if (err) - goto error; - } - err = device_create_file(dev, &dev_attr_alarms_temp); - if (err) - goto error; + /* If it wouldn't fit, change clock divisor */ + while (fan_min > 255 + && (data->fan_status[attr->index] & 0x60) != 0x60) { + fan_min >>= 1; + data->fan[attr->index] >>= 1; + data->fan_status[attr->index] += 0x20; } + data->fan_min[attr->index] = fan_min > 255 ? 255 : fan_min; + pc87360_write_value(data, LD_FAN, NO_BANK, + PC87360_REG_FAN_MIN(attr->index), + data->fan_min[attr->index]); - for (i = 0; i < data->fannr; i++) { - if (FAN_CONFIG_MONITOR(data->fan_conf, i)) { - err = sysfs_create_group(&dev->kobj, - &pc8736x_fan_attr_group[i]); - if (err) - goto error; - } - if (FAN_CONFIG_CONTROL(data->fan_conf, i)) { - err = device_create_file(dev, &pwm[i].dev_attr); - if (err) - goto error; - } - } + /* Write new divider, preserve alarm bits */ + pc87360_write_value(data, LD_FAN, NO_BANK, + PC87360_REG_FAN_STATUS(attr->index), + data->fan_status[attr->index] & 0xF9); + mutex_unlock(&data->update_lock); - err = device_create_file(dev, &dev_attr_name); - if (err) - goto error; + return count; +} - data->hwmon_dev = hwmon_device_register(dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto error; - } - return 0; +static struct sensor_device_attribute fan_min[] = { + SENSOR_ATTR_RW(fan1_min, fan_min, 0), + SENSOR_ATTR_RW(fan2_min, fan_min, 1), + SENSOR_ATTR_RW(fan3_min, fan_min, 2), +}; -error: - pc87360_remove_files(dev); - return err; +#define FAN_UNIT_ATTRS(X) \ +{ &fan_input[X].dev_attr.attr, \ + &fan_status[X].dev_attr.attr, \ + &fan_div[X].dev_attr.attr, \ + &fan_min[X].dev_attr.attr, \ + NULL \ } -static int pc87360_remove(struct platform_device *pdev) -{ - struct pc87360_data *data = platform_get_drvdata(pdev); +static struct attribute *pc8736x_fan_attr[][5] = { + FAN_UNIT_ATTRS(0), + FAN_UNIT_ATTRS(1), + FAN_UNIT_ATTRS(2) +}; - hwmon_device_unregister(data->hwmon_dev); - pc87360_remove_files(&pdev->dev); +static const struct attribute_group pc8736x_fan_attr_group[] = { + { .attrs = pc8736x_fan_attr[0], }, + { .attrs = pc8736x_fan_attr[1], }, + { .attrs = pc8736x_fan_attr[2], }, +}; - return 0; +static ssize_t pwm_show(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87360_data *data = pc87360_update_device(dev); + return sprintf(buf, "%u\n", + PWM_FROM_REG(data->pwm[attr->index], + FAN_CONFIG_INVERT(data->fan_conf, + attr->index))); } -/* - * ldi is the logical device index - * bank is for voltages and temperatures only - */ -static int pc87360_read_value(struct pc87360_data *data, u8 ldi, u8 bank, - u8 reg) +static ssize_t pwm_store(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) { - int res; + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87360_data *data = dev_get_drvdata(dev); + long val; + int err; - mutex_lock(&(data->lock)); - if (bank != NO_BANK) - outb_p(bank, data->address[ldi] + PC87365_REG_BANK); - res = inb_p(data->address[ldi] + reg); - mutex_unlock(&(data->lock)); + err = kstrtol(buf, 10, &val); + if (err) + return err; - return res; + mutex_lock(&data->update_lock); + data->pwm[attr->index] = PWM_TO_REG(val, + FAN_CONFIG_INVERT(data->fan_conf, attr->index)); + pc87360_write_value(data, LD_FAN, NO_BANK, PC87360_REG_PWM(attr->index), + data->pwm[attr->index]); + mutex_unlock(&data->update_lock); + return count; } -static void pc87360_write_value(struct pc87360_data *data, u8 ldi, u8 bank, - u8 reg, u8 value) +static struct sensor_device_attribute pwm[] = { + SENSOR_ATTR_RW(pwm1, pwm, 0), + SENSOR_ATTR_RW(pwm2, pwm, 1), + SENSOR_ATTR_RW(pwm3, pwm, 2), +}; + +static ssize_t name_show(struct device *dev, + struct device_attribute *devattr, char *buf) { - mutex_lock(&(data->lock)); - if (bank != NO_BANK) - outb_p(bank, data->address[ldi] + PC87365_REG_BANK); - outb_p(value, data->address[ldi] + reg); - mutex_unlock(&(data->lock)); + struct pc87360_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%s\n", data->name); } -/* (temp & vin) channel conversion status register flags (pdf sec.11.5.12) */ -#define CHAN_CNVRTD 0x80 /* new data ready */ -#define CHAN_ENA 0x01 /* enabled channel (temp or vin) */ -#define CHAN_ALM_ENA 0x10 /* propagate to alarms-reg ?? (chk val!) */ -#define CHAN_READY (CHAN_ENA|CHAN_CNVRTD) /* sample ready mask */ +static DEVICE_ATTR_RO(name); -#define TEMP_OTS_OE 0x20 /* OTS Output Enable */ -#define VIN_RW1C_MASK (CHAN_READY|CHAN_ALM_MAX|CHAN_ALM_MIN) /* 0x87 */ -#define TEMP_RW1C_MASK (VIN_RW1C_MASK|TEMP_ALM_CRIT|TEMP_FAULT) /* 0xCF */ +static void pc87360_remove_files(struct device *dev) +{ + int i; + + device_remove_file(dev, &dev_attr_name); + device_remove_file(dev, &dev_attr_alarms_temp); + for (i = 0; i < ARRAY_SIZE(pc8736x_temp_attr_group); i++) + sysfs_remove_group(&dev->kobj, &pc8736x_temp_attr_group[i]); + for (i = 0; i < ARRAY_SIZE(pc8736x_fan_attr_group); i++) { + sysfs_remove_group(&pdev->dev.kobj, &pc8736x_fan_attr_group[i]); + device_remove_file(dev, &pwm[i].dev_attr); + } + sysfs_remove_group(&dev->kobj, &pc8736x_therm_group); + sysfs_remove_group(&dev->kobj, &pc8736x_vin_group); +} static void pc87360_init_device(struct platform_device *pdev, int use_thermistors) @@ -1531,155 +1430,272 @@ static void pc87360_init_device(struct platform_device *pdev, pc87360_write_value(data, LD_TEMP, NO_BANK, 0xE, 0x05); } } -} - -static void pc87360_autodiv(struct device *dev, int nr) -{ - struct pc87360_data *data = dev_get_drvdata(dev); - u8 old_min = data->fan_min[nr]; +} + +static int pc87360_probe(struct platform_device *pdev) +{ + int i; + struct pc87360_data *data; + int err = 0; + const char *name; + int use_thermistors = 0; + struct device *dev = &pdev->dev; + + data = devm_kzalloc(dev, sizeof(struct pc87360_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + switch (devid) { + default: + name = "pc87360"; + data->fannr = 2; + break; + case 0xe8: + name = "pc87363"; + data->fannr = 2; + break; + case 0xe4: + name = "pc87364"; + data->fannr = 3; + break; + case 0xe5: + name = "pc87365"; + data->fannr = extra_isa[0] ? 3 : 0; + data->innr = extra_isa[1] ? 11 : 0; + data->tempnr = extra_isa[2] ? 2 : 0; + break; + case 0xe9: + name = "pc87366"; + data->fannr = extra_isa[0] ? 3 : 0; + data->innr = extra_isa[1] ? 14 : 0; + data->tempnr = extra_isa[2] ? 3 : 0; + break; + } + + data->name = name; + mutex_init(&data->lock); + mutex_init(&data->update_lock); + platform_set_drvdata(pdev, data); + + for (i = 0; i < LDNI_MAX; i++) { + data->address[i] = extra_isa[i]; + if (data->address[i] + && !devm_request_region(dev, extra_isa[i], PC87360_EXTENT, + DRIVER_NAME)) { + dev_err(dev, + "Region 0x%x-0x%x already in use!\n", + extra_isa[i], extra_isa[i]+PC87360_EXTENT-1); + return -EBUSY; + } + } + + /* Retrieve the fans configuration from Super-I/O space */ + if (data->fannr) + data->fan_conf = confreg[0] | (confreg[1] << 8); + + /* + * Use the correct reference voltage + * Unless both the VLM and the TMS logical devices agree to + * use an external Vref, the internal one is used. + */ + if (data->innr) { + i = pc87360_read_value(data, LD_IN, NO_BANK, + PC87365_REG_IN_CONFIG); + if (data->tempnr) { + i &= pc87360_read_value(data, LD_TEMP, NO_BANK, + PC87365_REG_TEMP_CONFIG); + } + data->in_vref = (i&0x02) ? 3025 : 2966; + dev_dbg(dev, "Using %s reference voltage\n", + (i&0x02) ? "external" : "internal"); + + data->vid_conf = confreg[3]; + data->vrm = vid_which_vrm(); + } + + /* Fan clock dividers may be needed before any data is read */ + for (i = 0; i < data->fannr; i++) { + if (FAN_CONFIG_MONITOR(data->fan_conf, i)) + data->fan_status[i] = pc87360_read_value(data, + LD_FAN, NO_BANK, + PC87360_REG_FAN_STATUS(i)); + } + + if (init > 0) { + if (devid == 0xe9 && data->address[1]) /* PC87366 */ + use_thermistors = confreg[2] & 0x40; + + pc87360_init_device(pdev, use_thermistors); + } + + /* Register all-or-nothing sysfs groups */ + + if (data->innr) { + err = sysfs_create_group(&dev->kobj, &pc8736x_vin_group); + if (err) + goto error; + } + + if (data->innr == 14) { + err = sysfs_create_group(&dev->kobj, &pc8736x_therm_group); + if (err) + goto error; + } + + /* create device attr-files for varying sysfs groups */ + + if (data->tempnr) { + for (i = 0; i < data->tempnr; i++) { + err = sysfs_create_group(&dev->kobj, + &pc8736x_temp_attr_group[i]); + if (err) + goto error; + } + err = device_create_file(dev, &dev_attr_alarms_temp); + if (err) + goto error; + } - /* Increase clock divider if needed and possible */ - if ((data->fan_status[nr] & 0x04) /* overflow flag */ - || (data->fan[nr] >= 224)) { /* next to overflow */ - if ((data->fan_status[nr] & 0x60) != 0x60) { - data->fan_status[nr] += 0x20; - data->fan_min[nr] >>= 1; - data->fan[nr] >>= 1; - dev_dbg(dev, - "Increasing clock divider to %d for fan %d\n", - FAN_DIV_FROM_REG(data->fan_status[nr]), nr + 1); + for (i = 0; i < data->fannr; i++) { + if (FAN_CONFIG_MONITOR(data->fan_conf, i)) { + err = sysfs_create_group(&dev->kobj, + &pc8736x_fan_attr_group[i]); + if (err) + goto error; } - } else { - /* Decrease clock divider if possible */ - while (!(data->fan_min[nr] & 0x80) /* min "nails" divider */ - && data->fan[nr] < 85 /* bad accuracy */ - && (data->fan_status[nr] & 0x60) != 0x00) { - data->fan_status[nr] -= 0x20; - data->fan_min[nr] <<= 1; - data->fan[nr] <<= 1; - dev_dbg(dev, - "Decreasing clock divider to %d for fan %d\n", - FAN_DIV_FROM_REG(data->fan_status[nr]), - nr + 1); + if (FAN_CONFIG_CONTROL(data->fan_conf, i)) { + err = device_create_file(dev, &pwm[i].dev_attr); + if (err) + goto error; } } - /* Write new fan min if it changed */ - if (old_min != data->fan_min[nr]) { - pc87360_write_value(data, LD_FAN, NO_BANK, - PC87360_REG_FAN_MIN(nr), - data->fan_min[nr]); + err = device_create_file(dev, &dev_attr_name); + if (err) + goto error; + + data->hwmon_dev = hwmon_device_register(dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + goto error; } + return 0; + +error: + pc87360_remove_files(dev); + return err; } -static struct pc87360_data *pc87360_update_device(struct device *dev) +static int pc87360_remove(struct platform_device *pdev) { - struct pc87360_data *data = dev_get_drvdata(dev); - u8 i; + struct pc87360_data *data = platform_get_drvdata(pdev); - mutex_lock(&data->update_lock); + hwmon_device_unregister(data->hwmon_dev); + pc87360_remove_files(&pdev->dev); - if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) { - dev_dbg(dev, "Data update\n"); + return 0; +} - /* Fans */ - for (i = 0; i < data->fannr; i++) { - if (FAN_CONFIG_MONITOR(data->fan_conf, i)) { - data->fan_status[i] = - pc87360_read_value(data, LD_FAN, - NO_BANK, PC87360_REG_FAN_STATUS(i)); - data->fan[i] = pc87360_read_value(data, LD_FAN, - NO_BANK, PC87360_REG_FAN(i)); - data->fan_min[i] = pc87360_read_value(data, - LD_FAN, NO_BANK, - PC87360_REG_FAN_MIN(i)); - /* Change clock divider if needed */ - pc87360_autodiv(dev, i); - /* Clear bits and write new divider */ - pc87360_write_value(data, LD_FAN, NO_BANK, - PC87360_REG_FAN_STATUS(i), - data->fan_status[i]); - } - if (FAN_CONFIG_CONTROL(data->fan_conf, i)) - data->pwm[i] = pc87360_read_value(data, LD_FAN, - NO_BANK, PC87360_REG_PWM(i)); - } +/* + * Driver data + */ +static struct platform_driver pc87360_driver = { + .driver = { + .name = DRIVER_NAME, + }, + .probe = pc87360_probe, + .remove = pc87360_remove, +}; - /* Voltages */ - for (i = 0; i < data->innr; i++) { - data->in_status[i] = pc87360_read_value(data, LD_IN, i, - PC87365_REG_IN_STATUS); - /* Clear bits */ - pc87360_write_value(data, LD_IN, i, - PC87365_REG_IN_STATUS, - data->in_status[i]); - if ((data->in_status[i] & CHAN_READY) == CHAN_READY) { - data->in[i] = pc87360_read_value(data, LD_IN, - i, PC87365_REG_IN); - } - if (data->in_status[i] & CHAN_ENA) { - data->in_min[i] = pc87360_read_value(data, - LD_IN, i, - PC87365_REG_IN_MIN); - data->in_max[i] = pc87360_read_value(data, - LD_IN, i, - PC87365_REG_IN_MAX); - if (i >= 11) - data->in_crit[i-11] = - pc87360_read_value(data, LD_IN, - i, PC87365_REG_TEMP_CRIT); - } +/* + * Device detection, registration and update + */ + +static int __init pc87360_find(int sioaddr, u8 *devid, + unsigned short *addresses) +{ + u16 val; + int i; + int nrdev; /* logical device count */ + + /* No superio_enter */ + + /* Identify device */ + val = force_id ? force_id : superio_inb(sioaddr, DEVID); + switch (val) { + case 0xE1: /* PC87360 */ + case 0xE8: /* PC87363 */ + case 0xE4: /* PC87364 */ + nrdev = 1; + break; + case 0xE5: /* PC87365 */ + case 0xE9: /* PC87366 */ + nrdev = 3; + break; + default: + superio_exit(sioaddr); + return -ENODEV; + } + /* Remember the device id */ + *devid = val; + + for (i = 0; i < nrdev; i++) { + /* select logical device */ + superio_outb(sioaddr, DEV, logdev[i]); + + val = superio_inb(sioaddr, ACT); + if (!(val & 0x01)) { + pr_info("Device 0x%02x not activated\n", logdev[i]); + continue; } - if (data->innr) { - data->in_alarms = pc87360_read_value(data, LD_IN, - NO_BANK, PC87365_REG_IN_ALARMS1) - | ((pc87360_read_value(data, LD_IN, - NO_BANK, PC87365_REG_IN_ALARMS2) - & 0x07) << 8); - data->vid = (data->vid_conf & 0xE0) ? - pc87360_read_value(data, LD_IN, - NO_BANK, PC87365_REG_VID) : 0x1F; + + val = (superio_inb(sioaddr, BASE) << 8) + | superio_inb(sioaddr, BASE + 1); + if (!val) { + pr_info("Base address not set for device 0x%02x\n", + logdev[i]); + continue; } - /* Temperatures */ - for (i = 0; i < data->tempnr; i++) { - data->temp_status[i] = pc87360_read_value(data, - LD_TEMP, i, - PC87365_REG_TEMP_STATUS); - /* Clear bits */ - pc87360_write_value(data, LD_TEMP, i, - PC87365_REG_TEMP_STATUS, - data->temp_status[i]); - if ((data->temp_status[i] & CHAN_READY) == CHAN_READY) { - data->temp[i] = pc87360_read_value(data, - LD_TEMP, i, - PC87365_REG_TEMP); - } - if (data->temp_status[i] & CHAN_ENA) { - data->temp_min[i] = pc87360_read_value(data, - LD_TEMP, i, - PC87365_REG_TEMP_MIN); - data->temp_max[i] = pc87360_read_value(data, - LD_TEMP, i, - PC87365_REG_TEMP_MAX); - data->temp_crit[i] = pc87360_read_value(data, - LD_TEMP, i, - PC87365_REG_TEMP_CRIT); + addresses[i] = val; + + if (i == 0) { /* Fans */ + confreg[0] = superio_inb(sioaddr, 0xF0); + confreg[1] = superio_inb(sioaddr, 0xF1); + + pr_debug("Fan %d: mon=%d ctrl=%d inv=%d\n", 1, + (confreg[0] >> 2) & 1, (confreg[0] >> 3) & 1, + (confreg[0] >> 4) & 1); + pr_debug("Fan %d: mon=%d ctrl=%d inv=%d\n", 2, + (confreg[0] >> 5) & 1, (confreg[0] >> 6) & 1, + (confreg[0] >> 7) & 1); + pr_debug("Fan %d: mon=%d ctrl=%d inv=%d\n", 3, + confreg[1] & 1, (confreg[1] >> 1) & 1, + (confreg[1] >> 2) & 1); + } else if (i == 1) { /* Voltages */ + /* Are we using thermistors? */ + if (*devid == 0xE9) { /* PC87366 */ + /* + * These registers are not logical-device + * specific, just that we won't need them if + * we don't use the VLM device + */ + confreg[2] = superio_inb(sioaddr, 0x2B); + confreg[3] = superio_inb(sioaddr, 0x25); + + if (confreg[2] & 0x40) { + pr_info("Using thermistors for temperature monitoring\n"); + } + if (confreg[3] & 0xE0) { + pr_info("VID inputs routed (mode %u)\n", + confreg[3] >> 5); + } } } - if (data->tempnr) { - data->temp_alarms = pc87360_read_value(data, LD_TEMP, - NO_BANK, PC87365_REG_TEMP_ALARMS) - & 0x3F; - } - - data->last_updated = jiffies; - data->valid = true; } - mutex_unlock(&data->update_lock); - - return data; + superio_exit(sioaddr); + return 0; } static int __init pc87360_device_add(unsigned short address) @@ -1778,7 +1794,6 @@ static void __exit pc87360_exit(void) platform_driver_unregister(&pc87360_driver); } - MODULE_AUTHOR("Jean Delvare "); MODULE_DESCRIPTION("PC8736x hardware monitor"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 6c7c469c8074e2bb20c41592f28b81925988b1a7 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 20 Sep 2022 15:56:16 +0200 Subject: hwmon: (vt8231) Introduce a #define for the driver name and use it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make use of the cpp symbol DRIVER_NAME to set the driver's name and use it instead of all explicit usages of the same string. Also make use of it instead of vt8231_driver.driver.name which breaks a cyclic dependency between vt8231_probe() and vt8231_driver that in the next commit allows to drop some forward declarations. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20220920135617.1046361-1-u.kleine-koenig@pengutronix.de Signed-off-by: Guenter Roeck --- drivers/hwmon/vt8231.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/vt8231.c b/drivers/hwmon/vt8231.c index 03275ac8ba72..d84f7db74889 100644 --- a/drivers/hwmon/vt8231.c +++ b/drivers/hwmon/vt8231.c @@ -38,6 +38,8 @@ static struct platform_device *pdev; #define VT8231_BASE_REG 0x70 #define VT8231_ENABLE_REG 0x74 +#define DRIVER_NAME "vt8231" + /* * The VT8231 registers * @@ -753,7 +755,7 @@ static const struct attribute_group vt8231_group = { static struct platform_driver vt8231_driver = { .driver = { - .name = "vt8231", + .name = DRIVER_NAME, }, .probe = vt8231_probe, .remove = vt8231_remove, @@ -770,7 +772,7 @@ static int vt8231_pci_probe(struct pci_dev *dev, const struct pci_device_id *id); static struct pci_driver vt8231_pci_driver = { - .name = "vt8231", + .name = DRIVER_NAME, .id_table = vt8231_pci_ids, .probe = vt8231_pci_probe, }; @@ -784,7 +786,7 @@ static int vt8231_probe(struct platform_device *pdev) /* Reserve the ISA region */ res = platform_get_resource(pdev, IORESOURCE_IO, 0); if (!devm_request_region(&pdev->dev, res->start, VT8231_EXTENT, - vt8231_driver.driver.name)) { + DRIVER_NAME)) { dev_err(&pdev->dev, "Region 0x%lx-0x%lx already in use!\n", (unsigned long)res->start, (unsigned long)res->end); return -ENODEV; @@ -796,7 +798,7 @@ static int vt8231_probe(struct platform_device *pdev) platform_set_drvdata(pdev, data); data->addr = res->start; - data->name = "vt8231"; + data->name = DRIVER_NAME; mutex_init(&data->update_lock); vt8231_init_device(data); @@ -942,7 +944,7 @@ static int vt8231_device_add(unsigned short address) struct resource res = { .start = address, .end = address + VT8231_EXTENT - 1, - .name = "vt8231", + .name = DRIVER_NAME, .flags = IORESOURCE_IO, }; int err; @@ -951,7 +953,7 @@ static int vt8231_device_add(unsigned short address) if (err) goto exit; - pdev = platform_device_alloc("vt8231", address); + pdev = platform_device_alloc(DRIVER_NAME, address); if (!pdev) { err = -ENOMEM; pr_err("Device allocation failed\n"); -- cgit v1.2.3 From ac387b0cb34b35e098dee52ef3856fa4afe35ab2 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 20 Sep 2022 15:56:17 +0200 Subject: hwmon: (vt8231) Reorder symbols to get rid of a few forward declarations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Declarations for static symbols are useless repetition unless there are cyclic dependencies. Reorder the functions and variables to get rid of 5 forward declarations. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20220920135617.1046361-2-u.kleine-koenig@pengutronix.de Signed-off-by: Guenter Roeck --- drivers/hwmon/vt8231.c | 188 ++++++++++++++++++++++++------------------------- 1 file changed, 91 insertions(+), 97 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/vt8231.c b/drivers/hwmon/vt8231.c index d84f7db74889..3b7f8922b0d5 100644 --- a/drivers/hwmon/vt8231.c +++ b/drivers/hwmon/vt8231.c @@ -164,10 +164,6 @@ struct vt8231_data { }; static struct pci_dev *s_bridge; -static int vt8231_probe(struct platform_device *pdev); -static int vt8231_remove(struct platform_device *pdev); -static struct vt8231_data *vt8231_update_device(struct device *dev); -static void vt8231_init_device(struct vt8231_data *data); static inline int vt8231_read_value(struct vt8231_data *data, u8 reg) { @@ -180,6 +176,74 @@ static inline void vt8231_write_value(struct vt8231_data *data, u8 reg, outb_p(value, data->addr + reg); } +static struct vt8231_data *vt8231_update_device(struct device *dev) +{ + struct vt8231_data *data = dev_get_drvdata(dev); + int i; + u16 low; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + for (i = 0; i < 6; i++) { + if (ISVOLT(i, data->uch_config)) { + data->in[i] = vt8231_read_value(data, + regvolt[i]); + data->in_min[i] = vt8231_read_value(data, + regvoltmin[i]); + data->in_max[i] = vt8231_read_value(data, + regvoltmax[i]); + } + } + for (i = 0; i < 2; i++) { + data->fan[i] = vt8231_read_value(data, + VT8231_REG_FAN(i)); + data->fan_min[i] = vt8231_read_value(data, + VT8231_REG_FAN_MIN(i)); + } + + low = vt8231_read_value(data, VT8231_REG_TEMP_LOW01); + low = (low >> 6) | ((low & 0x30) >> 2) + | (vt8231_read_value(data, VT8231_REG_TEMP_LOW25) << 4); + for (i = 0; i < 6; i++) { + if (ISTEMP(i, data->uch_config)) { + data->temp[i] = (vt8231_read_value(data, + regtemp[i]) << 2) + | ((low >> (2 * i)) & 0x03); + data->temp_max[i] = vt8231_read_value(data, + regtempmax[i]); + data->temp_min[i] = vt8231_read_value(data, + regtempmin[i]); + } + } + + i = vt8231_read_value(data, VT8231_REG_FANDIV); + data->fan_div[0] = (i >> 4) & 0x03; + data->fan_div[1] = i >> 6; + data->alarms = vt8231_read_value(data, VT8231_REG_ALARM1) | + (vt8231_read_value(data, VT8231_REG_ALARM2) << 8); + + /* Set alarm flags correctly */ + if (!data->fan[0] && data->fan_min[0]) + data->alarms |= 0x40; + else if (data->fan[0] && !data->fan_min[0]) + data->alarms &= ~0x40; + + if (!data->fan[1] && data->fan_min[1]) + data->alarms |= 0x80; + else if (data->fan[1] && !data->fan_min[1]) + data->alarms &= ~0x80; + + data->last_updated = jiffies; + data->valid = true; + } + + mutex_unlock(&data->update_lock); + + return data; +} + /* following are the sysfs callback functions */ static ssize_t in_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -753,29 +817,11 @@ static const struct attribute_group vt8231_group = { .attrs = vt8231_attributes, }; -static struct platform_driver vt8231_driver = { - .driver = { - .name = DRIVER_NAME, - }, - .probe = vt8231_probe, - .remove = vt8231_remove, -}; - -static const struct pci_device_id vt8231_pci_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8231_4) }, - { 0, } -}; - -MODULE_DEVICE_TABLE(pci, vt8231_pci_ids); - -static int vt8231_pci_probe(struct pci_dev *dev, - const struct pci_device_id *id); - -static struct pci_driver vt8231_pci_driver = { - .name = DRIVER_NAME, - .id_table = vt8231_pci_ids, - .probe = vt8231_pci_probe, -}; +static void vt8231_init_device(struct vt8231_data *data) +{ + vt8231_write_value(data, VT8231_REG_TEMP1_CONFIG, 0); + vt8231_write_value(data, VT8231_REG_TEMP2_CONFIG, 0); +} static int vt8231_probe(struct platform_device *pdev) { @@ -865,79 +911,21 @@ static int vt8231_remove(struct platform_device *pdev) return 0; } -static void vt8231_init_device(struct vt8231_data *data) -{ - vt8231_write_value(data, VT8231_REG_TEMP1_CONFIG, 0); - vt8231_write_value(data, VT8231_REG_TEMP2_CONFIG, 0); -} - -static struct vt8231_data *vt8231_update_device(struct device *dev) -{ - struct vt8231_data *data = dev_get_drvdata(dev); - int i; - u16 low; - - mutex_lock(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ + HZ / 2) - || !data->valid) { - for (i = 0; i < 6; i++) { - if (ISVOLT(i, data->uch_config)) { - data->in[i] = vt8231_read_value(data, - regvolt[i]); - data->in_min[i] = vt8231_read_value(data, - regvoltmin[i]); - data->in_max[i] = vt8231_read_value(data, - regvoltmax[i]); - } - } - for (i = 0; i < 2; i++) { - data->fan[i] = vt8231_read_value(data, - VT8231_REG_FAN(i)); - data->fan_min[i] = vt8231_read_value(data, - VT8231_REG_FAN_MIN(i)); - } - low = vt8231_read_value(data, VT8231_REG_TEMP_LOW01); - low = (low >> 6) | ((low & 0x30) >> 2) - | (vt8231_read_value(data, VT8231_REG_TEMP_LOW25) << 4); - for (i = 0; i < 6; i++) { - if (ISTEMP(i, data->uch_config)) { - data->temp[i] = (vt8231_read_value(data, - regtemp[i]) << 2) - | ((low >> (2 * i)) & 0x03); - data->temp_max[i] = vt8231_read_value(data, - regtempmax[i]); - data->temp_min[i] = vt8231_read_value(data, - regtempmin[i]); - } - } - - i = vt8231_read_value(data, VT8231_REG_FANDIV); - data->fan_div[0] = (i >> 4) & 0x03; - data->fan_div[1] = i >> 6; - data->alarms = vt8231_read_value(data, VT8231_REG_ALARM1) | - (vt8231_read_value(data, VT8231_REG_ALARM2) << 8); - - /* Set alarm flags correctly */ - if (!data->fan[0] && data->fan_min[0]) - data->alarms |= 0x40; - else if (data->fan[0] && !data->fan_min[0]) - data->alarms &= ~0x40; - - if (!data->fan[1] && data->fan_min[1]) - data->alarms |= 0x80; - else if (data->fan[1] && !data->fan_min[1]) - data->alarms &= ~0x80; - - data->last_updated = jiffies; - data->valid = true; - } +static struct platform_driver vt8231_driver = { + .driver = { + .name = DRIVER_NAME, + }, + .probe = vt8231_probe, + .remove = vt8231_remove, +}; - mutex_unlock(&data->update_lock); +static const struct pci_device_id vt8231_pci_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8231_4) }, + { 0, } +}; - return data; -} +MODULE_DEVICE_TABLE(pci, vt8231_pci_ids); static int vt8231_device_add(unsigned short address) { @@ -1042,6 +1030,12 @@ exit: return -ENODEV; } +static struct pci_driver vt8231_pci_driver = { + .name = DRIVER_NAME, + .id_table = vt8231_pci_ids, + .probe = vt8231_pci_probe, +}; + static int __init sm_vt8231_init(void) { return pci_register_driver(&vt8231_pci_driver); -- cgit v1.2.3 From 903882c767051a17c1535b2a820e2931ee44b8fb Mon Sep 17 00:00:00 2001 From: Eliav Farber Date: Wed, 21 Sep 2022 12:17:23 +0000 Subject: hwmon: (mr75203) fix undefined reference to `__divdi3' Fix build error on 32-bit machines. Fixes: 94c025b6f735 ("hwmon: (mr75203) modify the temperature equation according to series 5 datasheet") Signed-off-by: Eliav Farber Reported-by: kernel test robot Tested-by: Sudip Mukherjee Link: https://lore.kernel.org/r/20220921121723.6726-1-farbere@amazon.com Signed-off-by: Guenter Roeck --- drivers/hwmon/mr75203.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/mr75203.c b/drivers/hwmon/mr75203.c index 667c764e0c2c..01f63dc1a250 100644 --- a/drivers/hwmon/mr75203.c +++ b/drivers/hwmon/mr75203.c @@ -266,9 +266,9 @@ static long pvt_calc_temp(struct pvt_device *pvt, u32 nbs) struct temp_coeff *ts_coeff = &pvt->ts_coeff; s64 tmp = ts_coeff->g + - ts_coeff->h * (s64)nbs / ts_coeff->cal5 - + div_s64(ts_coeff->h * (s64)nbs, ts_coeff->cal5) - ts_coeff->h / 2 + - ts_coeff->j * (s64)pvt->ip_freq / HZ_PER_MHZ; + div_s64(ts_coeff->j * (s64)pvt->ip_freq, HZ_PER_MHZ); return clamp_val(tmp, PVT_TEMP_MIN_mC, PVT_TEMP_MAX_mC); } -- cgit v1.2.3 From 0dee25ebc7d315d9ee785ea5f457ae980979ea89 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 8 Aug 2022 08:06:40 +0200 Subject: hwmon: Make use of devm_clk_get_enabled() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Several drivers manually register a devm handler to disable their clk. Convert them to devm_clk_get_enabled(). Acked-by: Guenter Roeck Reviewed-by: Nuno Sá Acked-by: Jonathan Cameron Signed-off-by: Uwe Kleine-König Signed-off-by: Guenter Roeck --- drivers/hwmon/axi-fan-control.c | 15 +-------------- drivers/hwmon/ltc2947-core.c | 17 +---------------- drivers/hwmon/mr75203.c | 26 +------------------------- 3 files changed, 3 insertions(+), 55 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/axi-fan-control.c b/drivers/hwmon/axi-fan-control.c index 96c4a5c45291..6724e0dd3088 100644 --- a/drivers/hwmon/axi-fan-control.c +++ b/drivers/hwmon/axi-fan-control.c @@ -394,11 +394,6 @@ static int axi_fan_control_init(struct axi_fan_control_data *ctl, return ret; } -static void axi_fan_control_clk_disable(void *clk) -{ - clk_disable_unprepare(clk); -} - static const struct hwmon_channel_info *axi_fan_control_info[] = { HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT), HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_FAULT | HWMON_F_LABEL), @@ -478,20 +473,12 @@ static int axi_fan_control_probe(struct platform_device *pdev) if (IS_ERR(ctl->base)) return PTR_ERR(ctl->base); - clk = devm_clk_get(&pdev->dev, NULL); + clk = devm_clk_get_enabled(&pdev->dev, NULL); if (IS_ERR(clk)) { dev_err(&pdev->dev, "clk_get failed with %ld\n", PTR_ERR(clk)); return PTR_ERR(clk); } - ret = clk_prepare_enable(clk); - if (ret) - return ret; - - ret = devm_add_action_or_reset(&pdev->dev, axi_fan_control_clk_disable, clk); - if (ret) - return ret; - ctl->clk_rate = clk_get_rate(clk); if (!ctl->clk_rate) return -EINVAL; diff --git a/drivers/hwmon/ltc2947-core.c b/drivers/hwmon/ltc2947-core.c index 5423466de697..626f5bf2c9c7 100644 --- a/drivers/hwmon/ltc2947-core.c +++ b/drivers/hwmon/ltc2947-core.c @@ -956,13 +956,6 @@ static struct attribute *ltc2947_attrs[] = { }; ATTRIBUTE_GROUPS(ltc2947); -static void ltc2947_clk_disable(void *data) -{ - struct clk *extclk = data; - - clk_disable_unprepare(extclk); -} - static int ltc2947_setup(struct ltc2947_data *st) { int ret; @@ -989,7 +982,7 @@ static int ltc2947_setup(struct ltc2947_data *st) return ret; /* check external clock presence */ - extclk = devm_clk_get_optional(st->dev, NULL); + extclk = devm_clk_get_optional_enabled(st->dev, NULL); if (IS_ERR(extclk)) return dev_err_probe(st->dev, PTR_ERR(extclk), "Failed to get external clock\n"); @@ -1007,14 +1000,6 @@ static int ltc2947_setup(struct ltc2947_data *st) return -EINVAL; } - ret = clk_prepare_enable(extclk); - if (ret) - return ret; - - ret = devm_add_action_or_reset(st->dev, ltc2947_clk_disable, - extclk); - if (ret) - return ret; /* as in table 1 of the datasheet */ if (rate_hz >= LTC2947_CLK_MIN && rate_hz <= 1000000) pre = 0; diff --git a/drivers/hwmon/mr75203.c b/drivers/hwmon/mr75203.c index 01f63dc1a250..394a4c7e46ab 100644 --- a/drivers/hwmon/mr75203.c +++ b/drivers/hwmon/mr75203.c @@ -610,24 +610,6 @@ static int pvt_get_regmap(struct platform_device *pdev, char *reg_name, return 0; } -static void pvt_clk_disable(void *data) -{ - struct pvt_device *pvt = data; - - clk_disable_unprepare(pvt->clk); -} - -static int pvt_clk_enable(struct device *dev, struct pvt_device *pvt) -{ - int ret; - - ret = clk_prepare_enable(pvt->clk); - if (ret) - return ret; - - return devm_add_action_or_reset(dev, pvt_clk_disable, pvt); -} - static void pvt_reset_control_assert(void *data) { struct pvt_device *pvt = data; @@ -799,16 +781,10 @@ static int mr75203_probe(struct platform_device *pdev) if (ret) return ret; - pvt->clk = devm_clk_get(dev, NULL); + pvt->clk = devm_clk_get_enabled(dev, NULL); if (IS_ERR(pvt->clk)) return dev_err_probe(dev, PTR_ERR(pvt->clk), "failed to get clock\n"); - ret = pvt_clk_enable(dev, pvt); - if (ret) { - dev_err(dev, "failed to enable clock\n"); - return ret; - } - pvt->rst = devm_reset_control_get_optional_exclusive(dev, NULL); if (IS_ERR(pvt->rst)) return dev_err_probe(dev, PTR_ERR(pvt->rst), -- cgit v1.2.3 From 847a3b04b32ee36b9049d48da23d70ee83cf6d0a Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 22 Sep 2022 09:48:59 +0200 Subject: hwmon: (sis5595) Introduce a #define for the driver name and use it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make use of the cpp symbol DRIVER_NAME to set the driver's name and use it instead of all explicit usages of the same string. Also make use of it instead of sis5595_driver.driver.name which breaks a cyclic dependency between sis5595_probe() and sis5595_driver that in the next commit allows to drop some forward declarations. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20220922074900.2763331-1-u.kleine-koenig@pengutronix.de Signed-off-by: Guenter Roeck --- drivers/hwmon/sis5595.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/sis5595.c b/drivers/hwmon/sis5595.c index 018cb5a7651f..013f87da6fff 100644 --- a/drivers/hwmon/sis5595.c +++ b/drivers/hwmon/sis5595.c @@ -37,6 +37,7 @@ * 735 0008 0735 */ +#define DRIVER_NAME "sis5595" #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include @@ -201,7 +202,7 @@ static void sis5595_init_device(struct sis5595_data *data); static struct platform_driver sis5595_driver = { .driver = { - .name = "sis5595", + .name = DRIVER_NAME, }, .probe = sis5595_probe, .remove = sis5595_remove, @@ -580,7 +581,7 @@ static int sis5595_probe(struct platform_device *pdev) /* Reserve the ISA region */ res = platform_get_resource(pdev, IORESOURCE_IO, 0); if (!devm_request_region(&pdev->dev, res->start, SIS5595_EXTENT, - sis5595_driver.driver.name)) + DRIVER_NAME)) return -EBUSY; data = devm_kzalloc(&pdev->dev, sizeof(struct sis5595_data), @@ -591,7 +592,7 @@ static int sis5595_probe(struct platform_device *pdev) mutex_init(&data->lock); mutex_init(&data->update_lock); data->addr = res->start; - data->name = "sis5595"; + data->name = DRIVER_NAME; platform_set_drvdata(pdev, data); /* @@ -764,7 +765,7 @@ static int sis5595_device_add(unsigned short address) struct resource res = { .start = address, .end = address + SIS5595_EXTENT - 1, - .name = "sis5595", + .name = DRIVER_NAME, .flags = IORESOURCE_IO, }; int err; @@ -773,7 +774,7 @@ static int sis5595_device_add(unsigned short address) if (err) goto exit; - pdev = platform_device_alloc("sis5595", address); + pdev = platform_device_alloc(DRIVER_NAME, address); if (!pdev) { err = -ENOMEM; pr_err("Device allocation failed\n"); @@ -886,7 +887,7 @@ exit: } static struct pci_driver sis5595_pci_driver = { - .name = "sis5595", + .name = DRIVER_NAME, .id_table = sis5595_pci_ids, .probe = sis5595_pci_probe, }; -- cgit v1.2.3 From 1b2f9b1e6dd36a64b84f65078322109ffc453f0c Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 22 Sep 2022 09:49:00 +0200 Subject: hwmon: (sis5595) Reorder symbols to get rid of a few forward declarations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Declarations for static symbols are useless repetition unless there are cyclic dependencies. Reorder the functions and variables to get rid of 6 forward declarations. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20220922074900.2763331-2-u.kleine-koenig@pengutronix.de Signed-off-by: Guenter Roeck --- drivers/hwmon/sis5595.c | 176 +++++++++++++++++++++++------------------------- 1 file changed, 84 insertions(+), 92 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/sis5595.c b/drivers/hwmon/sis5595.c index 013f87da6fff..b0b05fd12221 100644 --- a/drivers/hwmon/sis5595.c +++ b/drivers/hwmon/sis5595.c @@ -192,21 +192,75 @@ struct sis5595_data { static struct pci_dev *s_bridge; /* pointer to the (only) sis5595 */ -static int sis5595_probe(struct platform_device *pdev); -static int sis5595_remove(struct platform_device *pdev); +/* ISA access must be locked explicitly. */ +static int sis5595_read_value(struct sis5595_data *data, u8 reg) +{ + int res; -static int sis5595_read_value(struct sis5595_data *data, u8 reg); -static void sis5595_write_value(struct sis5595_data *data, u8 reg, u8 value); -static struct sis5595_data *sis5595_update_device(struct device *dev); -static void sis5595_init_device(struct sis5595_data *data); + mutex_lock(&data->lock); + outb_p(reg, data->addr + SIS5595_ADDR_REG_OFFSET); + res = inb_p(data->addr + SIS5595_DATA_REG_OFFSET); + mutex_unlock(&data->lock); + return res; +} -static struct platform_driver sis5595_driver = { - .driver = { - .name = DRIVER_NAME, - }, - .probe = sis5595_probe, - .remove = sis5595_remove, -}; +static void sis5595_write_value(struct sis5595_data *data, u8 reg, u8 value) +{ + mutex_lock(&data->lock); + outb_p(reg, data->addr + SIS5595_ADDR_REG_OFFSET); + outb_p(value, data->addr + SIS5595_DATA_REG_OFFSET); + mutex_unlock(&data->lock); +} + +static struct sis5595_data *sis5595_update_device(struct device *dev) +{ + struct sis5595_data *data = dev_get_drvdata(dev); + int i; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + + for (i = 0; i <= data->maxins; i++) { + data->in[i] = + sis5595_read_value(data, SIS5595_REG_IN(i)); + data->in_min[i] = + sis5595_read_value(data, + SIS5595_REG_IN_MIN(i)); + data->in_max[i] = + sis5595_read_value(data, + SIS5595_REG_IN_MAX(i)); + } + for (i = 0; i < 2; i++) { + data->fan[i] = + sis5595_read_value(data, SIS5595_REG_FAN(i)); + data->fan_min[i] = + sis5595_read_value(data, + SIS5595_REG_FAN_MIN(i)); + } + if (data->maxins == 3) { + data->temp = + sis5595_read_value(data, SIS5595_REG_TEMP); + data->temp_over = + sis5595_read_value(data, SIS5595_REG_TEMP_OVER); + data->temp_hyst = + sis5595_read_value(data, SIS5595_REG_TEMP_HYST); + } + i = sis5595_read_value(data, SIS5595_REG_FANDIV); + data->fan_div[0] = (i >> 4) & 0x03; + data->fan_div[1] = i >> 6; + data->alarms = + sis5595_read_value(data, SIS5595_REG_ALARM1) | + (sis5595_read_value(data, SIS5595_REG_ALARM2) << 8); + data->last_updated = jiffies; + data->valid = true; + } + + mutex_unlock(&data->update_lock); + + return data; +} /* 4 Voltages */ static ssize_t in_show(struct device *dev, struct device_attribute *da, @@ -569,6 +623,15 @@ static const struct attribute_group sis5595_group_temp1 = { .attrs = sis5595_attributes_temp1, }; +/* Called when we have found a new SIS5595. */ +static void sis5595_init_device(struct sis5595_data *data) +{ + u8 config = sis5595_read_value(data, SIS5595_REG_CONFIG); + if (!(config & 0x01)) + sis5595_write_value(data, SIS5595_REG_CONFIG, + (config & 0xf7) | 0x01); +} + /* This is called when the module is loaded */ static int sis5595_probe(struct platform_device *pdev) { @@ -658,85 +721,6 @@ static int sis5595_remove(struct platform_device *pdev) return 0; } -/* ISA access must be locked explicitly. */ -static int sis5595_read_value(struct sis5595_data *data, u8 reg) -{ - int res; - - mutex_lock(&data->lock); - outb_p(reg, data->addr + SIS5595_ADDR_REG_OFFSET); - res = inb_p(data->addr + SIS5595_DATA_REG_OFFSET); - mutex_unlock(&data->lock); - return res; -} - -static void sis5595_write_value(struct sis5595_data *data, u8 reg, u8 value) -{ - mutex_lock(&data->lock); - outb_p(reg, data->addr + SIS5595_ADDR_REG_OFFSET); - outb_p(value, data->addr + SIS5595_DATA_REG_OFFSET); - mutex_unlock(&data->lock); -} - -/* Called when we have found a new SIS5595. */ -static void sis5595_init_device(struct sis5595_data *data) -{ - u8 config = sis5595_read_value(data, SIS5595_REG_CONFIG); - if (!(config & 0x01)) - sis5595_write_value(data, SIS5595_REG_CONFIG, - (config & 0xf7) | 0x01); -} - -static struct sis5595_data *sis5595_update_device(struct device *dev) -{ - struct sis5595_data *data = dev_get_drvdata(dev); - int i; - - mutex_lock(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ + HZ / 2) - || !data->valid) { - - for (i = 0; i <= data->maxins; i++) { - data->in[i] = - sis5595_read_value(data, SIS5595_REG_IN(i)); - data->in_min[i] = - sis5595_read_value(data, - SIS5595_REG_IN_MIN(i)); - data->in_max[i] = - sis5595_read_value(data, - SIS5595_REG_IN_MAX(i)); - } - for (i = 0; i < 2; i++) { - data->fan[i] = - sis5595_read_value(data, SIS5595_REG_FAN(i)); - data->fan_min[i] = - sis5595_read_value(data, - SIS5595_REG_FAN_MIN(i)); - } - if (data->maxins == 3) { - data->temp = - sis5595_read_value(data, SIS5595_REG_TEMP); - data->temp_over = - sis5595_read_value(data, SIS5595_REG_TEMP_OVER); - data->temp_hyst = - sis5595_read_value(data, SIS5595_REG_TEMP_HYST); - } - i = sis5595_read_value(data, SIS5595_REG_FANDIV); - data->fan_div[0] = (i >> 4) & 0x03; - data->fan_div[1] = i >> 6; - data->alarms = - sis5595_read_value(data, SIS5595_REG_ALARM1) | - (sis5595_read_value(data, SIS5595_REG_ALARM2) << 8); - data->last_updated = jiffies; - data->valid = true; - } - - mutex_unlock(&data->update_lock); - - return data; -} - static const struct pci_device_id sis5595_pci_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503) }, { 0, } @@ -801,6 +785,14 @@ exit: return err; } +static struct platform_driver sis5595_driver = { + .driver = { + .name = DRIVER_NAME, + }, + .probe = sis5595_probe, + .remove = sis5595_remove, +}; + static int sis5595_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) { -- cgit v1.2.3 From f9c0cf8f26de367c58e48b02b1cdb9c377626e6f Mon Sep 17 00:00:00 2001 From: "Jason A. Donenfeld" Date: Sat, 24 Sep 2022 12:11:51 +0200 Subject: hwmon: (sht4x) do not overflow clamping operation on 32-bit platforms On 32-bit platforms, long is 32 bits, so (long)UINT_MAX is less than (long)SHT4X_MIN_POLL_INTERVAL, which means the clamping operation is bogus. Fix this by clamping at INT_MAX, so that the upperbound is the same on all platforms. Signed-off-by: Jason A. Donenfeld Link: https://lore.kernel.org/r/20220924101151.4168414-1-Jason@zx2c4.com Signed-off-by: Guenter Roeck --- drivers/hwmon/sht4x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/sht4x.c b/drivers/hwmon/sht4x.c index c19df3ade48e..13ac2d8f22c7 100644 --- a/drivers/hwmon/sht4x.c +++ b/drivers/hwmon/sht4x.c @@ -129,7 +129,7 @@ unlock: static ssize_t sht4x_interval_write(struct sht4x_data *data, long val) { - data->update_interval = clamp_val(val, SHT4X_MIN_POLL_INTERVAL, UINT_MAX); + data->update_interval = clamp_val(val, SHT4X_MIN_POLL_INTERVAL, INT_MAX); return 0; } -- cgit v1.2.3 From 8887516f01066375d70162a978b3fd9d73695878 Mon Sep 17 00:00:00 2001 From: Li Zhong Date: Fri, 23 Sep 2022 17:17:51 -0700 Subject: hwmon: (adm9240) fix data race in adm9240_fan_read In adm9240_read() adm9240_fan_read() adm9240_write_fan_div(), it assumes that the caller of adm9240_write_fan_div() must hold data->update_lock. Otherwise, it may cause data races when data is updated by other threads. Signed-off-by: Li Zhong Link: https://lore.kernel.org/r/20220924001751.1726369-1-floridsleeves@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/adm9240.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/adm9240.c b/drivers/hwmon/adm9240.c index 483cd757abd3..40e3558d3709 100644 --- a/drivers/hwmon/adm9240.c +++ b/drivers/hwmon/adm9240.c @@ -501,17 +501,23 @@ static int adm9240_fan_read(struct device *dev, u32 attr, int channel, long *val switch (attr) { case hwmon_fan_input: + mutex_lock(&data->update_lock); err = regmap_read(data->regmap, ADM9240_REG_FAN(channel), ®val); - if (err < 0) + if (err < 0) { + mutex_unlock(&data->update_lock); return err; + } if (regval == 255 && data->fan_div[channel] < 3) { /* adjust fan clock divider on overflow */ err = adm9240_write_fan_div(data, channel, ++data->fan_div[channel]); - if (err) + if (err) { + mutex_unlock(&data->update_lock); return err; + } } *val = FAN_FROM_REG(regval, BIT(data->fan_div[channel])); + mutex_unlock(&data->update_lock); break; case hwmon_fan_div: *val = BIT(data->fan_div[channel]); -- cgit v1.2.3 From 50e52c1fc5cecfac52287a2227d8883b32963876 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Sat, 24 Sep 2022 15:57:37 +0200 Subject: hwmon: (via686a) Introduce a #define for the driver name and use it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make use of the cpp symbol DRIVER_NAME to set the driver's name and use it instead of all explicit usages of the same string. Also make use of it instead of sis5595_driver.driver.name which breaks a cyclic dependency between sis5595_probe() and sis5595_driver that in the next commit allows to drop some forward declarations. For an amd64 allyesconfig this even reduces the size of the driver by 3 bytes. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20220924135738.234051-1-u.kleine-koenig@pengutronix.de Signed-off-by: Guenter Roeck --- drivers/hwmon/via686a.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/via686a.c b/drivers/hwmon/via686a.c index 55634110c2f9..b17121881235 100644 --- a/drivers/hwmon/via686a.c +++ b/drivers/hwmon/via686a.c @@ -34,6 +34,8 @@ #include #include +#define DRIVER_NAME "via686a" + /* * If force_addr is set to anything different from 0, we forcibly enable * the device at the given address. @@ -656,7 +658,7 @@ static const struct attribute_group via686a_group = { static struct platform_driver via686a_driver = { .driver = { - .name = "via686a", + .name = DRIVER_NAME, }, .probe = via686a_probe, .remove = via686a_remove, @@ -672,7 +674,7 @@ static int via686a_probe(struct platform_device *pdev) /* Reserve the ISA region */ res = platform_get_resource(pdev, IORESOURCE_IO, 0); if (!devm_request_region(&pdev->dev, res->start, VIA686A_EXTENT, - via686a_driver.driver.name)) { + DRIVER_NAME)) { dev_err(&pdev->dev, "Region 0x%lx-0x%lx already in use!\n", (unsigned long)res->start, (unsigned long)res->end); return -ENODEV; @@ -685,7 +687,7 @@ static int via686a_probe(struct platform_device *pdev) platform_set_drvdata(pdev, data); data->addr = res->start; - data->name = "via686a"; + data->name = DRIVER_NAME; mutex_init(&data->update_lock); /* Initialize the VIA686A chip */ @@ -819,7 +821,7 @@ static int via686a_device_add(unsigned short address) struct resource res = { .start = address, .end = address + VIA686A_EXTENT - 1, - .name = "via686a", + .name = DRIVER_NAME, .flags = IORESOURCE_IO, }; int err; @@ -828,7 +830,7 @@ static int via686a_device_add(unsigned short address) if (err) goto exit; - pdev = platform_device_alloc("via686a", address); + pdev = platform_device_alloc(DRIVER_NAME, address); if (!pdev) { err = -ENOMEM; pr_err("Device allocation failed\n"); @@ -918,7 +920,7 @@ exit: } static struct pci_driver via686a_pci_driver = { - .name = "via686a", + .name = DRIVER_NAME, .id_table = via686a_pci_ids, .probe = via686a_pci_probe, }; -- cgit v1.2.3 From 984fed5686e172e9dfc405b36b7477cfb54733fe Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Sat, 24 Sep 2022 15:57:38 +0200 Subject: hwmon: (via686a) Reorder symbols to get rid of a few forward declarations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Declarations for static symbols are useless repetition unless there are cyclic dependencies. Reorder the functions and variables to get rid of 4 forward declarations. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20220924135738.234051-2-u.kleine-koenig@pengutronix.de Signed-off-by: Guenter Roeck --- drivers/hwmon/via686a.c | 194 +++++++++++++++++++++++------------------------- 1 file changed, 94 insertions(+), 100 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/via686a.c b/drivers/hwmon/via686a.c index b17121881235..37d7374896f6 100644 --- a/drivers/hwmon/via686a.c +++ b/drivers/hwmon/via686a.c @@ -323,9 +323,6 @@ struct via686a_data { static struct pci_dev *s_bridge; /* pointer to the (only) via686a */ -static int via686a_probe(struct platform_device *pdev); -static int via686a_remove(struct platform_device *pdev); - static inline int via686a_read_value(struct via686a_data *data, u8 reg) { return inb_p(data->addr + reg); @@ -337,8 +334,76 @@ static inline void via686a_write_value(struct via686a_data *data, u8 reg, outb_p(value, data->addr + reg); } -static struct via686a_data *via686a_update_device(struct device *dev); -static void via686a_init_device(struct via686a_data *data); +static void via686a_update_fan_div(struct via686a_data *data) +{ + int reg = via686a_read_value(data, VIA686A_REG_FANDIV); + data->fan_div[0] = (reg >> 4) & 0x03; + data->fan_div[1] = reg >> 6; +} + +static struct via686a_data *via686a_update_device(struct device *dev) +{ + struct via686a_data *data = dev_get_drvdata(dev); + int i; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + for (i = 0; i <= 4; i++) { + data->in[i] = + via686a_read_value(data, VIA686A_REG_IN(i)); + data->in_min[i] = via686a_read_value(data, + VIA686A_REG_IN_MIN + (i)); + data->in_max[i] = + via686a_read_value(data, VIA686A_REG_IN_MAX(i)); + } + for (i = 1; i <= 2; i++) { + data->fan[i - 1] = + via686a_read_value(data, VIA686A_REG_FAN(i)); + data->fan_min[i - 1] = via686a_read_value(data, + VIA686A_REG_FAN_MIN(i)); + } + for (i = 0; i <= 2; i++) { + data->temp[i] = via686a_read_value(data, + VIA686A_REG_TEMP[i]) << 2; + data->temp_over[i] = + via686a_read_value(data, + VIA686A_REG_TEMP_OVER[i]); + data->temp_hyst[i] = + via686a_read_value(data, + VIA686A_REG_TEMP_HYST[i]); + } + /* + * add in lower 2 bits + * temp1 uses bits 7-6 of VIA686A_REG_TEMP_LOW1 + * temp2 uses bits 5-4 of VIA686A_REG_TEMP_LOW23 + * temp3 uses bits 7-6 of VIA686A_REG_TEMP_LOW23 + */ + data->temp[0] |= (via686a_read_value(data, + VIA686A_REG_TEMP_LOW1) + & 0xc0) >> 6; + data->temp[1] |= + (via686a_read_value(data, VIA686A_REG_TEMP_LOW23) & + 0x30) >> 4; + data->temp[2] |= + (via686a_read_value(data, VIA686A_REG_TEMP_LOW23) & + 0xc0) >> 6; + + via686a_update_fan_div(data); + data->alarms = + via686a_read_value(data, + VIA686A_REG_ALARM1) | + (via686a_read_value(data, VIA686A_REG_ALARM2) << 8); + data->last_updated = jiffies; + data->valid = true; + } + + mutex_unlock(&data->update_lock); + + return data; +} /* following are the sysfs callback functions */ @@ -656,13 +721,23 @@ static const struct attribute_group via686a_group = { .attrs = via686a_attributes, }; -static struct platform_driver via686a_driver = { - .driver = { - .name = DRIVER_NAME, - }, - .probe = via686a_probe, - .remove = via686a_remove, -}; +static void via686a_init_device(struct via686a_data *data) +{ + u8 reg; + + /* Start monitoring */ + reg = via686a_read_value(data, VIA686A_REG_CONFIG); + via686a_write_value(data, VIA686A_REG_CONFIG, (reg | 0x01) & 0x7F); + + /* Configure temp interrupt mode for continuous-interrupt operation */ + reg = via686a_read_value(data, VIA686A_REG_TEMP_MODE); + via686a_write_value(data, VIA686A_REG_TEMP_MODE, + (reg & ~VIA686A_TEMP_MODE_MASK) + | VIA686A_TEMP_MODE_CONTINUOUS); + + /* Pre-read fan clock divisor values */ + via686a_update_fan_div(data); +} /* This is called when the module is loaded */ static int via686a_probe(struct platform_device *pdev) @@ -721,94 +796,13 @@ static int via686a_remove(struct platform_device *pdev) return 0; } -static void via686a_update_fan_div(struct via686a_data *data) -{ - int reg = via686a_read_value(data, VIA686A_REG_FANDIV); - data->fan_div[0] = (reg >> 4) & 0x03; - data->fan_div[1] = reg >> 6; -} - -static void via686a_init_device(struct via686a_data *data) -{ - u8 reg; - - /* Start monitoring */ - reg = via686a_read_value(data, VIA686A_REG_CONFIG); - via686a_write_value(data, VIA686A_REG_CONFIG, (reg | 0x01) & 0x7F); - - /* Configure temp interrupt mode for continuous-interrupt operation */ - reg = via686a_read_value(data, VIA686A_REG_TEMP_MODE); - via686a_write_value(data, VIA686A_REG_TEMP_MODE, - (reg & ~VIA686A_TEMP_MODE_MASK) - | VIA686A_TEMP_MODE_CONTINUOUS); - - /* Pre-read fan clock divisor values */ - via686a_update_fan_div(data); -} - -static struct via686a_data *via686a_update_device(struct device *dev) -{ - struct via686a_data *data = dev_get_drvdata(dev); - int i; - - mutex_lock(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ + HZ / 2) - || !data->valid) { - for (i = 0; i <= 4; i++) { - data->in[i] = - via686a_read_value(data, VIA686A_REG_IN(i)); - data->in_min[i] = via686a_read_value(data, - VIA686A_REG_IN_MIN - (i)); - data->in_max[i] = - via686a_read_value(data, VIA686A_REG_IN_MAX(i)); - } - for (i = 1; i <= 2; i++) { - data->fan[i - 1] = - via686a_read_value(data, VIA686A_REG_FAN(i)); - data->fan_min[i - 1] = via686a_read_value(data, - VIA686A_REG_FAN_MIN(i)); - } - for (i = 0; i <= 2; i++) { - data->temp[i] = via686a_read_value(data, - VIA686A_REG_TEMP[i]) << 2; - data->temp_over[i] = - via686a_read_value(data, - VIA686A_REG_TEMP_OVER[i]); - data->temp_hyst[i] = - via686a_read_value(data, - VIA686A_REG_TEMP_HYST[i]); - } - /* - * add in lower 2 bits - * temp1 uses bits 7-6 of VIA686A_REG_TEMP_LOW1 - * temp2 uses bits 5-4 of VIA686A_REG_TEMP_LOW23 - * temp3 uses bits 7-6 of VIA686A_REG_TEMP_LOW23 - */ - data->temp[0] |= (via686a_read_value(data, - VIA686A_REG_TEMP_LOW1) - & 0xc0) >> 6; - data->temp[1] |= - (via686a_read_value(data, VIA686A_REG_TEMP_LOW23) & - 0x30) >> 4; - data->temp[2] |= - (via686a_read_value(data, VIA686A_REG_TEMP_LOW23) & - 0xc0) >> 6; - - via686a_update_fan_div(data); - data->alarms = - via686a_read_value(data, - VIA686A_REG_ALARM1) | - (via686a_read_value(data, VIA686A_REG_ALARM2) << 8); - data->last_updated = jiffies; - data->valid = true; - } - - mutex_unlock(&data->update_lock); - - return data; -} +static struct platform_driver via686a_driver = { + .driver = { + .name = DRIVER_NAME, + }, + .probe = via686a_probe, + .remove = via686a_remove, +}; static const struct pci_device_id via686a_pci_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_4) }, -- cgit v1.2.3 From df9ec2dae094225190527f9406ad3ce8983baa1f Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Sat, 24 Sep 2022 23:28:52 +0200 Subject: hwmon: (f71882fg) Reorder symbols to get rid of a few forward declarations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Declarations for static symbols are useless code repetition (unless there are cyclic dependencies). Reorder some functions and variables which allows to get rid of 42 forward declarations. Signed-off-by: Uwe Kleine-König Signed-off-by: Guenter Roeck --- drivers/hwmon/f71882fg.c | 3039 ++++++++++++++++++++++------------------------ 1 file changed, 1469 insertions(+), 1570 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c index 19b6c643059a..70121482a617 100644 --- a/drivers/hwmon/f71882fg.c +++ b/drivers/hwmon/f71882fg.c @@ -237,13 +237,6 @@ static const char f71882fg_nr_temps[] = { static struct platform_device *f71882fg_pdev; -/* Super-I/O Function prototypes */ -static inline int superio_inb(int base, int reg); -static inline int superio_inw(int base, int reg); -static inline int superio_enter(int base); -static inline void superio_select(int base, int ld); -static inline void superio_exit(int base); - struct f71882fg_sio_data { enum chips type; }; @@ -292,968 +285,116 @@ struct f71882fg_data { s8 pwm_auto_point_temp[4][4]; }; -/* Sysfs in */ -static ssize_t show_in(struct device *dev, struct device_attribute *devattr, - char *buf); -static ssize_t show_in_max(struct device *dev, struct device_attribute - *devattr, char *buf); -static ssize_t store_in_max(struct device *dev, struct device_attribute - *devattr, const char *buf, size_t count); -static ssize_t show_in_beep(struct device *dev, struct device_attribute - *devattr, char *buf); -static ssize_t store_in_beep(struct device *dev, struct device_attribute - *devattr, const char *buf, size_t count); -static ssize_t show_in_alarm(struct device *dev, struct device_attribute - *devattr, char *buf); -/* Sysfs Fan */ -static ssize_t show_fan(struct device *dev, struct device_attribute *devattr, - char *buf); -static ssize_t show_fan_full_speed(struct device *dev, - struct device_attribute *devattr, char *buf); -static ssize_t store_fan_full_speed(struct device *dev, - struct device_attribute *devattr, const char *buf, size_t count); -static ssize_t show_fan_beep(struct device *dev, struct device_attribute - *devattr, char *buf); -static ssize_t store_fan_beep(struct device *dev, struct device_attribute - *devattr, const char *buf, size_t count); -static ssize_t show_fan_alarm(struct device *dev, struct device_attribute - *devattr, char *buf); -/* Sysfs Temp */ -static ssize_t show_temp(struct device *dev, struct device_attribute - *devattr, char *buf); -static ssize_t show_temp_max(struct device *dev, struct device_attribute - *devattr, char *buf); -static ssize_t store_temp_max(struct device *dev, struct device_attribute - *devattr, const char *buf, size_t count); -static ssize_t show_temp_max_hyst(struct device *dev, struct device_attribute - *devattr, char *buf); -static ssize_t store_temp_max_hyst(struct device *dev, struct device_attribute - *devattr, const char *buf, size_t count); -static ssize_t show_temp_crit(struct device *dev, struct device_attribute - *devattr, char *buf); -static ssize_t store_temp_crit(struct device *dev, struct device_attribute - *devattr, const char *buf, size_t count); -static ssize_t show_temp_crit_hyst(struct device *dev, struct device_attribute - *devattr, char *buf); -static ssize_t show_temp_type(struct device *dev, struct device_attribute - *devattr, char *buf); -static ssize_t show_temp_beep(struct device *dev, struct device_attribute - *devattr, char *buf); -static ssize_t store_temp_beep(struct device *dev, struct device_attribute - *devattr, const char *buf, size_t count); -static ssize_t show_temp_alarm(struct device *dev, struct device_attribute - *devattr, char *buf); -static ssize_t show_temp_fault(struct device *dev, struct device_attribute - *devattr, char *buf); -/* PWM and Auto point control */ -static ssize_t show_pwm(struct device *dev, struct device_attribute *devattr, - char *buf); -static ssize_t store_pwm(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count); -static ssize_t show_simple_pwm(struct device *dev, - struct device_attribute *devattr, char *buf); -static ssize_t store_simple_pwm(struct device *dev, - struct device_attribute *devattr, const char *buf, size_t count); -static ssize_t show_pwm_enable(struct device *dev, - struct device_attribute *devattr, char *buf); -static ssize_t store_pwm_enable(struct device *dev, - struct device_attribute *devattr, const char *buf, size_t count); -static ssize_t show_pwm_interpolate(struct device *dev, - struct device_attribute *devattr, char *buf); -static ssize_t store_pwm_interpolate(struct device *dev, - struct device_attribute *devattr, const char *buf, size_t count); -static ssize_t show_pwm_auto_point_channel(struct device *dev, - struct device_attribute *devattr, char *buf); -static ssize_t store_pwm_auto_point_channel(struct device *dev, - struct device_attribute *devattr, const char *buf, size_t count); -static ssize_t show_pwm_auto_point_temp_hyst(struct device *dev, - struct device_attribute *devattr, char *buf); -static ssize_t store_pwm_auto_point_temp_hyst(struct device *dev, - struct device_attribute *devattr, const char *buf, size_t count); -static ssize_t show_pwm_auto_point_pwm(struct device *dev, - struct device_attribute *devattr, char *buf); -static ssize_t store_pwm_auto_point_pwm(struct device *dev, - struct device_attribute *devattr, const char *buf, size_t count); -static ssize_t show_pwm_auto_point_temp(struct device *dev, - struct device_attribute *devattr, char *buf); -static ssize_t store_pwm_auto_point_temp(struct device *dev, - struct device_attribute *devattr, const char *buf, size_t count); -/* Sysfs misc */ -static ssize_t name_show(struct device *dev, struct device_attribute *devattr, - char *buf); +static u8 f71882fg_read8(struct f71882fg_data *data, u8 reg) +{ + u8 val; -static int f71882fg_probe(struct platform_device *pdev); -static int f71882fg_remove(struct platform_device *pdev); + outb(reg, data->addr + ADDR_REG_OFFSET); + val = inb(data->addr + DATA_REG_OFFSET); -static struct platform_driver f71882fg_driver = { - .driver = { - .name = DRVNAME, - }, - .probe = f71882fg_probe, - .remove = f71882fg_remove, -}; + return val; +} -static DEVICE_ATTR_RO(name); +static u16 f71882fg_read16(struct f71882fg_data *data, u8 reg) +{ + u16 val; -/* - * Temp attr for the f71858fg, the f71858fg is special as it has its - * temperature indexes start at 0 (the others start at 1) - */ -static struct sensor_device_attribute_2 f71858fg_temp_attr[] = { - SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0), - SENSOR_ATTR_2(temp1_max, S_IRUGO|S_IWUSR, show_temp_max, - store_temp_max, 0, 0), - SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst, - store_temp_max_hyst, 0, 0), - SENSOR_ATTR_2(temp1_max_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 0), - SENSOR_ATTR_2(temp1_crit, S_IRUGO|S_IWUSR, show_temp_crit, - store_temp_crit, 0, 0), - SENSOR_ATTR_2(temp1_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, - 0, 0), - SENSOR_ATTR_2(temp1_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 4), - SENSOR_ATTR_2(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0, 0), - SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0, 1), - SENSOR_ATTR_2(temp2_max, S_IRUGO|S_IWUSR, show_temp_max, - store_temp_max, 0, 1), - SENSOR_ATTR_2(temp2_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst, - store_temp_max_hyst, 0, 1), - SENSOR_ATTR_2(temp2_max_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 1), - SENSOR_ATTR_2(temp2_crit, S_IRUGO|S_IWUSR, show_temp_crit, - store_temp_crit, 0, 1), - SENSOR_ATTR_2(temp2_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, - 0, 1), - SENSOR_ATTR_2(temp2_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 5), - SENSOR_ATTR_2(temp2_fault, S_IRUGO, show_temp_fault, NULL, 0, 1), - SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0, 2), - SENSOR_ATTR_2(temp3_max, S_IRUGO|S_IWUSR, show_temp_max, - store_temp_max, 0, 2), - SENSOR_ATTR_2(temp3_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst, - store_temp_max_hyst, 0, 2), - SENSOR_ATTR_2(temp3_max_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 2), - SENSOR_ATTR_2(temp3_crit, S_IRUGO|S_IWUSR, show_temp_crit, - store_temp_crit, 0, 2), - SENSOR_ATTR_2(temp3_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, - 0, 2), - SENSOR_ATTR_2(temp3_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 6), - SENSOR_ATTR_2(temp3_fault, S_IRUGO, show_temp_fault, NULL, 0, 2), -}; + val = f71882fg_read8(data, reg) << 8; + val |= f71882fg_read8(data, reg + 1); -/* Temp attr for the standard models */ -static struct sensor_device_attribute_2 fxxxx_temp_attr[3][9] = { { - SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 1), - SENSOR_ATTR_2(temp1_max, S_IRUGO|S_IWUSR, show_temp_max, - store_temp_max, 0, 1), - SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst, - store_temp_max_hyst, 0, 1), - /* - * Should really be temp1_max_alarm, but older versions did not handle - * the max and crit alarms separately and lm_sensors v2 depends on the - * presence of temp#_alarm files. The same goes for temp2/3 _alarm. - */ - SENSOR_ATTR_2(temp1_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 1), - SENSOR_ATTR_2(temp1_crit, S_IRUGO|S_IWUSR, show_temp_crit, - store_temp_crit, 0, 1), - SENSOR_ATTR_2(temp1_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, - 0, 1), - SENSOR_ATTR_2(temp1_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 5), - SENSOR_ATTR_2(temp1_type, S_IRUGO, show_temp_type, NULL, 0, 1), - SENSOR_ATTR_2(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0, 1), -}, { - SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0, 2), - SENSOR_ATTR_2(temp2_max, S_IRUGO|S_IWUSR, show_temp_max, - store_temp_max, 0, 2), - SENSOR_ATTR_2(temp2_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst, - store_temp_max_hyst, 0, 2), - /* Should be temp2_max_alarm, see temp1_alarm note */ - SENSOR_ATTR_2(temp2_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 2), - SENSOR_ATTR_2(temp2_crit, S_IRUGO|S_IWUSR, show_temp_crit, - store_temp_crit, 0, 2), - SENSOR_ATTR_2(temp2_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, - 0, 2), - SENSOR_ATTR_2(temp2_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 6), - SENSOR_ATTR_2(temp2_type, S_IRUGO, show_temp_type, NULL, 0, 2), - SENSOR_ATTR_2(temp2_fault, S_IRUGO, show_temp_fault, NULL, 0, 2), -}, { - SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0, 3), - SENSOR_ATTR_2(temp3_max, S_IRUGO|S_IWUSR, show_temp_max, - store_temp_max, 0, 3), - SENSOR_ATTR_2(temp3_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst, - store_temp_max_hyst, 0, 3), - /* Should be temp3_max_alarm, see temp1_alarm note */ - SENSOR_ATTR_2(temp3_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 3), - SENSOR_ATTR_2(temp3_crit, S_IRUGO|S_IWUSR, show_temp_crit, - store_temp_crit, 0, 3), - SENSOR_ATTR_2(temp3_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, - 0, 3), - SENSOR_ATTR_2(temp3_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 7), - SENSOR_ATTR_2(temp3_type, S_IRUGO, show_temp_type, NULL, 0, 3), - SENSOR_ATTR_2(temp3_fault, S_IRUGO, show_temp_fault, NULL, 0, 3), -} }; + return val; +} -/* Temp attr for models which can beep on temp alarm */ -static struct sensor_device_attribute_2 fxxxx_temp_beep_attr[3][2] = { { - SENSOR_ATTR_2(temp1_max_beep, S_IRUGO|S_IWUSR, show_temp_beep, - store_temp_beep, 0, 1), - SENSOR_ATTR_2(temp1_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep, - store_temp_beep, 0, 5), -}, { - SENSOR_ATTR_2(temp2_max_beep, S_IRUGO|S_IWUSR, show_temp_beep, - store_temp_beep, 0, 2), - SENSOR_ATTR_2(temp2_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep, - store_temp_beep, 0, 6), -}, { - SENSOR_ATTR_2(temp3_max_beep, S_IRUGO|S_IWUSR, show_temp_beep, - store_temp_beep, 0, 3), - SENSOR_ATTR_2(temp3_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep, - store_temp_beep, 0, 7), -} }; +static inline int fan_from_reg(u16 reg) +{ + return reg ? (1500000 / reg) : 0; +} -static struct sensor_device_attribute_2 f81866_temp_beep_attr[3][2] = { { - SENSOR_ATTR_2(temp1_max_beep, S_IRUGO|S_IWUSR, show_temp_beep, - store_temp_beep, 0, 0), - SENSOR_ATTR_2(temp1_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep, - store_temp_beep, 0, 4), -}, { - SENSOR_ATTR_2(temp2_max_beep, S_IRUGO|S_IWUSR, show_temp_beep, - store_temp_beep, 0, 1), - SENSOR_ATTR_2(temp2_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep, - store_temp_beep, 0, 5), -}, { - SENSOR_ATTR_2(temp3_max_beep, S_IRUGO|S_IWUSR, show_temp_beep, - store_temp_beep, 0, 2), - SENSOR_ATTR_2(temp3_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep, - store_temp_beep, 0, 6), -} }; +static inline u16 fan_to_reg(int fan) +{ + return fan ? (1500000 / fan) : 0; +} -/* - * Temp attr for the f8000 - * Note on the f8000 temp_ovt (crit) is used as max, and temp_high (max) - * is used as hysteresis value to clear alarms - * Also like the f71858fg its temperature indexes start at 0 - */ -static struct sensor_device_attribute_2 f8000_temp_attr[] = { - SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0), - SENSOR_ATTR_2(temp1_max, S_IRUGO|S_IWUSR, show_temp_crit, - store_temp_crit, 0, 0), - SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO|S_IWUSR, show_temp_max, - store_temp_max, 0, 0), - SENSOR_ATTR_2(temp1_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 4), - SENSOR_ATTR_2(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0, 0), - SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0, 1), - SENSOR_ATTR_2(temp2_max, S_IRUGO|S_IWUSR, show_temp_crit, - store_temp_crit, 0, 1), - SENSOR_ATTR_2(temp2_max_hyst, S_IRUGO|S_IWUSR, show_temp_max, - store_temp_max, 0, 1), - SENSOR_ATTR_2(temp2_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 5), - SENSOR_ATTR_2(temp2_fault, S_IRUGO, show_temp_fault, NULL, 0, 1), - SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0, 2), - SENSOR_ATTR_2(temp3_max, S_IRUGO|S_IWUSR, show_temp_crit, - store_temp_crit, 0, 2), - SENSOR_ATTR_2(temp3_max_hyst, S_IRUGO|S_IWUSR, show_temp_max, - store_temp_max, 0, 2), - SENSOR_ATTR_2(temp3_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 6), - SENSOR_ATTR_2(temp3_fault, S_IRUGO, show_temp_fault, NULL, 0, 2), -}; +static void f71882fg_write8(struct f71882fg_data *data, u8 reg, u8 val) +{ + outb(reg, data->addr + ADDR_REG_OFFSET); + outb(val, data->addr + DATA_REG_OFFSET); +} -/* in attr for all models */ -static struct sensor_device_attribute_2 fxxxx_in_attr[] = { - SENSOR_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0), - SENSOR_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 0, 1), - SENSOR_ATTR_2(in2_input, S_IRUGO, show_in, NULL, 0, 2), - SENSOR_ATTR_2(in3_input, S_IRUGO, show_in, NULL, 0, 3), - SENSOR_ATTR_2(in4_input, S_IRUGO, show_in, NULL, 0, 4), - SENSOR_ATTR_2(in5_input, S_IRUGO, show_in, NULL, 0, 5), - SENSOR_ATTR_2(in6_input, S_IRUGO, show_in, NULL, 0, 6), - SENSOR_ATTR_2(in7_input, S_IRUGO, show_in, NULL, 0, 7), - SENSOR_ATTR_2(in8_input, S_IRUGO, show_in, NULL, 0, 8), - SENSOR_ATTR_2(in9_input, S_IRUGO, show_in, NULL, 0, 9), - SENSOR_ATTR_2(in10_input, S_IRUGO, show_in, NULL, 0, 10), -}; +static void f71882fg_write16(struct f71882fg_data *data, u8 reg, u16 val) +{ + f71882fg_write8(data, reg, val >> 8); + f71882fg_write8(data, reg + 1, val & 0xff); +} -/* For models with in1 alarm capability */ -static struct sensor_device_attribute_2 fxxxx_in1_alarm_attr[] = { - SENSOR_ATTR_2(in1_max, S_IRUGO|S_IWUSR, show_in_max, store_in_max, - 0, 1), - SENSOR_ATTR_2(in1_beep, S_IRUGO|S_IWUSR, show_in_beep, store_in_beep, - 0, 1), - SENSOR_ATTR_2(in1_alarm, S_IRUGO, show_in_alarm, NULL, 0, 1), -}; +static u16 f71882fg_read_temp(struct f71882fg_data *data, int nr) +{ + if (data->type == f71858fg) + return f71882fg_read16(data, F71882FG_REG_TEMP(nr)); + else + return f71882fg_read8(data, F71882FG_REG_TEMP(nr)); +} -/* Fan / PWM attr common to all models */ -static struct sensor_device_attribute_2 fxxxx_fan_attr[4][6] = { { - SENSOR_ATTR_2(fan1_input, S_IRUGO, show_fan, NULL, 0, 0), - SENSOR_ATTR_2(fan1_full_speed, S_IRUGO|S_IWUSR, - show_fan_full_speed, - store_fan_full_speed, 0, 0), - SENSOR_ATTR_2(fan1_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 0), - SENSOR_ATTR_2(pwm1, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 0), - SENSOR_ATTR_2(pwm1_enable, S_IRUGO|S_IWUSR, show_pwm_enable, - store_pwm_enable, 0, 0), - SENSOR_ATTR_2(pwm1_interpolate, S_IRUGO|S_IWUSR, - show_pwm_interpolate, store_pwm_interpolate, 0, 0), -}, { - SENSOR_ATTR_2(fan2_input, S_IRUGO, show_fan, NULL, 0, 1), - SENSOR_ATTR_2(fan2_full_speed, S_IRUGO|S_IWUSR, - show_fan_full_speed, - store_fan_full_speed, 0, 1), - SENSOR_ATTR_2(fan2_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 1), - SENSOR_ATTR_2(pwm2, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 1), - SENSOR_ATTR_2(pwm2_enable, S_IRUGO|S_IWUSR, show_pwm_enable, - store_pwm_enable, 0, 1), - SENSOR_ATTR_2(pwm2_interpolate, S_IRUGO|S_IWUSR, - show_pwm_interpolate, store_pwm_interpolate, 0, 1), -}, { - SENSOR_ATTR_2(fan3_input, S_IRUGO, show_fan, NULL, 0, 2), - SENSOR_ATTR_2(fan3_full_speed, S_IRUGO|S_IWUSR, - show_fan_full_speed, - store_fan_full_speed, 0, 2), - SENSOR_ATTR_2(fan3_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 2), - SENSOR_ATTR_2(pwm3, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 2), - SENSOR_ATTR_2(pwm3_enable, S_IRUGO|S_IWUSR, show_pwm_enable, - store_pwm_enable, 0, 2), - SENSOR_ATTR_2(pwm3_interpolate, S_IRUGO|S_IWUSR, - show_pwm_interpolate, store_pwm_interpolate, 0, 2), -}, { - SENSOR_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 0, 3), - SENSOR_ATTR_2(fan4_full_speed, S_IRUGO|S_IWUSR, - show_fan_full_speed, - store_fan_full_speed, 0, 3), - SENSOR_ATTR_2(fan4_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 3), - SENSOR_ATTR_2(pwm4, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 3), - SENSOR_ATTR_2(pwm4_enable, S_IRUGO|S_IWUSR, show_pwm_enable, - store_pwm_enable, 0, 3), - SENSOR_ATTR_2(pwm4_interpolate, S_IRUGO|S_IWUSR, - show_pwm_interpolate, store_pwm_interpolate, 0, 3), -} }; +static struct f71882fg_data *f71882fg_update_device(struct device *dev) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + int nr_fans = f71882fg_nr_fans[data->type]; + int nr_temps = f71882fg_nr_temps[data->type]; + int nr, reg, point; -/* Attr for the third fan of the f71808a, which only has manual pwm */ -static struct sensor_device_attribute_2 f71808a_fan3_attr[] = { - SENSOR_ATTR_2(fan3_input, S_IRUGO, show_fan, NULL, 0, 2), - SENSOR_ATTR_2(fan3_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 2), - SENSOR_ATTR_2(pwm3, S_IRUGO|S_IWUSR, - show_simple_pwm, store_simple_pwm, 0, 2), -}; + mutex_lock(&data->update_lock); -/* Attr for models which can beep on Fan alarm */ -static struct sensor_device_attribute_2 fxxxx_fan_beep_attr[] = { - SENSOR_ATTR_2(fan1_beep, S_IRUGO|S_IWUSR, show_fan_beep, - store_fan_beep, 0, 0), - SENSOR_ATTR_2(fan2_beep, S_IRUGO|S_IWUSR, show_fan_beep, - store_fan_beep, 0, 1), - SENSOR_ATTR_2(fan3_beep, S_IRUGO|S_IWUSR, show_fan_beep, - store_fan_beep, 0, 2), - SENSOR_ATTR_2(fan4_beep, S_IRUGO|S_IWUSR, show_fan_beep, - store_fan_beep, 0, 3), -}; + /* Update once every 60 seconds */ + if (time_after(jiffies, data->last_limits + 60 * HZ) || + !data->valid) { + if (f71882fg_has_in1_alarm[data->type]) { + if (data->type == f81866a) { + data->in1_max = + f71882fg_read8(data, + F81866_REG_IN1_HIGH); + data->in_beep = + f71882fg_read8(data, + F81866_REG_IN_BEEP); + } else { + data->in1_max = + f71882fg_read8(data, + F71882FG_REG_IN1_HIGH); + data->in_beep = + f71882fg_read8(data, + F71882FG_REG_IN_BEEP); + } + } -/* - * PWM attr for the f71862fg, fewer pwms and fewer zones per pwm than the - * standard models - */ -static struct sensor_device_attribute_2 f71862fg_auto_pwm_attr[3][7] = { { - SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_channel, - store_pwm_auto_point_channel, 0, 0), - SENSOR_ATTR_2(pwm1_auto_point1_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 1, 0), - SENSOR_ATTR_2(pwm1_auto_point2_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 4, 0), - SENSOR_ATTR_2(pwm1_auto_point1_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp, store_pwm_auto_point_temp, - 0, 0), - SENSOR_ATTR_2(pwm1_auto_point2_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp, store_pwm_auto_point_temp, - 3, 0), - SENSOR_ATTR_2(pwm1_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp_hyst, - store_pwm_auto_point_temp_hyst, - 0, 0), - SENSOR_ATTR_2(pwm1_auto_point2_temp_hyst, S_IRUGO, - show_pwm_auto_point_temp_hyst, NULL, 3, 0), -}, { - SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_channel, - store_pwm_auto_point_channel, 0, 1), - SENSOR_ATTR_2(pwm2_auto_point1_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 1, 1), - SENSOR_ATTR_2(pwm2_auto_point2_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 4, 1), - SENSOR_ATTR_2(pwm2_auto_point1_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp, store_pwm_auto_point_temp, - 0, 1), - SENSOR_ATTR_2(pwm2_auto_point2_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp, store_pwm_auto_point_temp, - 3, 1), - SENSOR_ATTR_2(pwm2_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp_hyst, - store_pwm_auto_point_temp_hyst, - 0, 1), - SENSOR_ATTR_2(pwm2_auto_point2_temp_hyst, S_IRUGO, - show_pwm_auto_point_temp_hyst, NULL, 3, 1), -}, { - SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_channel, - store_pwm_auto_point_channel, 0, 2), - SENSOR_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 1, 2), - SENSOR_ATTR_2(pwm3_auto_point2_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 4, 2), - SENSOR_ATTR_2(pwm3_auto_point1_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp, store_pwm_auto_point_temp, - 0, 2), - SENSOR_ATTR_2(pwm3_auto_point2_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp, store_pwm_auto_point_temp, - 3, 2), - SENSOR_ATTR_2(pwm3_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp_hyst, - store_pwm_auto_point_temp_hyst, - 0, 2), - SENSOR_ATTR_2(pwm3_auto_point2_temp_hyst, S_IRUGO, - show_pwm_auto_point_temp_hyst, NULL, 3, 2), -} }; + /* Get High & boundary temps*/ + for (nr = data->temp_start; nr < nr_temps + data->temp_start; + nr++) { + data->temp_ovt[nr] = f71882fg_read8(data, + F71882FG_REG_TEMP_OVT(nr)); + data->temp_high[nr] = f71882fg_read8(data, + F71882FG_REG_TEMP_HIGH(nr)); + } -/* - * PWM attr for the f71808e/f71869, almost identical to the f71862fg, but the - * pwm setting when the temperature is above the pwmX_auto_point1_temp can be - * programmed instead of being hardcoded to 0xff - */ -static struct sensor_device_attribute_2 f71869_auto_pwm_attr[3][8] = { { - SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_channel, - store_pwm_auto_point_channel, 0, 0), - SENSOR_ATTR_2(pwm1_auto_point1_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 0, 0), - SENSOR_ATTR_2(pwm1_auto_point2_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 1, 0), - SENSOR_ATTR_2(pwm1_auto_point3_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 4, 0), - SENSOR_ATTR_2(pwm1_auto_point1_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp, store_pwm_auto_point_temp, - 0, 0), - SENSOR_ATTR_2(pwm1_auto_point2_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp, store_pwm_auto_point_temp, - 3, 0), - SENSOR_ATTR_2(pwm1_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp_hyst, - store_pwm_auto_point_temp_hyst, - 0, 0), - SENSOR_ATTR_2(pwm1_auto_point2_temp_hyst, S_IRUGO, - show_pwm_auto_point_temp_hyst, NULL, 3, 0), -}, { - SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_channel, - store_pwm_auto_point_channel, 0, 1), - SENSOR_ATTR_2(pwm2_auto_point1_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 0, 1), - SENSOR_ATTR_2(pwm2_auto_point2_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 1, 1), - SENSOR_ATTR_2(pwm2_auto_point3_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 4, 1), - SENSOR_ATTR_2(pwm2_auto_point1_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp, store_pwm_auto_point_temp, - 0, 1), - SENSOR_ATTR_2(pwm2_auto_point2_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp, store_pwm_auto_point_temp, - 3, 1), - SENSOR_ATTR_2(pwm2_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp_hyst, - store_pwm_auto_point_temp_hyst, - 0, 1), - SENSOR_ATTR_2(pwm2_auto_point2_temp_hyst, S_IRUGO, - show_pwm_auto_point_temp_hyst, NULL, 3, 1), -}, { - SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_channel, - store_pwm_auto_point_channel, 0, 2), - SENSOR_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 0, 2), - SENSOR_ATTR_2(pwm3_auto_point2_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 1, 2), - SENSOR_ATTR_2(pwm3_auto_point3_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 4, 2), - SENSOR_ATTR_2(pwm3_auto_point1_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp, store_pwm_auto_point_temp, - 0, 2), - SENSOR_ATTR_2(pwm3_auto_point2_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp, store_pwm_auto_point_temp, - 3, 2), - SENSOR_ATTR_2(pwm3_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp_hyst, - store_pwm_auto_point_temp_hyst, - 0, 2), - SENSOR_ATTR_2(pwm3_auto_point2_temp_hyst, S_IRUGO, - show_pwm_auto_point_temp_hyst, NULL, 3, 2), -} }; + if (data->type != f8000) { + data->temp_hyst[0] = f71882fg_read8(data, + F71882FG_REG_TEMP_HYST(0)); + data->temp_hyst[1] = f71882fg_read8(data, + F71882FG_REG_TEMP_HYST(1)); + } + /* All but the f71858fg / f8000 have this register */ + if ((data->type != f71858fg) && (data->type != f8000)) { + reg = f71882fg_read8(data, F71882FG_REG_TEMP_TYPE); + data->temp_type[1] = (reg & 0x02) ? 2 : 4; + data->temp_type[2] = (reg & 0x04) ? 2 : 4; + data->temp_type[3] = (reg & 0x08) ? 2 : 4; + } -/* PWM attr for the standard models */ -static struct sensor_device_attribute_2 fxxxx_auto_pwm_attr[4][14] = { { - SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_channel, - store_pwm_auto_point_channel, 0, 0), - SENSOR_ATTR_2(pwm1_auto_point1_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 0, 0), - SENSOR_ATTR_2(pwm1_auto_point2_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 1, 0), - SENSOR_ATTR_2(pwm1_auto_point3_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 2, 0), - SENSOR_ATTR_2(pwm1_auto_point4_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 3, 0), - SENSOR_ATTR_2(pwm1_auto_point5_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 4, 0), - SENSOR_ATTR_2(pwm1_auto_point1_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp, store_pwm_auto_point_temp, - 0, 0), - SENSOR_ATTR_2(pwm1_auto_point2_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp, store_pwm_auto_point_temp, - 1, 0), - SENSOR_ATTR_2(pwm1_auto_point3_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp, store_pwm_auto_point_temp, - 2, 0), - SENSOR_ATTR_2(pwm1_auto_point4_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp, store_pwm_auto_point_temp, - 3, 0), - SENSOR_ATTR_2(pwm1_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp_hyst, - store_pwm_auto_point_temp_hyst, - 0, 0), - SENSOR_ATTR_2(pwm1_auto_point2_temp_hyst, S_IRUGO, - show_pwm_auto_point_temp_hyst, NULL, 1, 0), - SENSOR_ATTR_2(pwm1_auto_point3_temp_hyst, S_IRUGO, - show_pwm_auto_point_temp_hyst, NULL, 2, 0), - SENSOR_ATTR_2(pwm1_auto_point4_temp_hyst, S_IRUGO, - show_pwm_auto_point_temp_hyst, NULL, 3, 0), -}, { - SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_channel, - store_pwm_auto_point_channel, 0, 1), - SENSOR_ATTR_2(pwm2_auto_point1_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 0, 1), - SENSOR_ATTR_2(pwm2_auto_point2_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 1, 1), - SENSOR_ATTR_2(pwm2_auto_point3_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 2, 1), - SENSOR_ATTR_2(pwm2_auto_point4_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 3, 1), - SENSOR_ATTR_2(pwm2_auto_point5_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 4, 1), - SENSOR_ATTR_2(pwm2_auto_point1_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp, store_pwm_auto_point_temp, - 0, 1), - SENSOR_ATTR_2(pwm2_auto_point2_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp, store_pwm_auto_point_temp, - 1, 1), - SENSOR_ATTR_2(pwm2_auto_point3_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp, store_pwm_auto_point_temp, - 2, 1), - SENSOR_ATTR_2(pwm2_auto_point4_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp, store_pwm_auto_point_temp, - 3, 1), - SENSOR_ATTR_2(pwm2_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp_hyst, - store_pwm_auto_point_temp_hyst, - 0, 1), - SENSOR_ATTR_2(pwm2_auto_point2_temp_hyst, S_IRUGO, - show_pwm_auto_point_temp_hyst, NULL, 1, 1), - SENSOR_ATTR_2(pwm2_auto_point3_temp_hyst, S_IRUGO, - show_pwm_auto_point_temp_hyst, NULL, 2, 1), - SENSOR_ATTR_2(pwm2_auto_point4_temp_hyst, S_IRUGO, - show_pwm_auto_point_temp_hyst, NULL, 3, 1), -}, { - SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_channel, - store_pwm_auto_point_channel, 0, 2), - SENSOR_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 0, 2), - SENSOR_ATTR_2(pwm3_auto_point2_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 1, 2), - SENSOR_ATTR_2(pwm3_auto_point3_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 2, 2), - SENSOR_ATTR_2(pwm3_auto_point4_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 3, 2), - SENSOR_ATTR_2(pwm3_auto_point5_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 4, 2), - SENSOR_ATTR_2(pwm3_auto_point1_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp, store_pwm_auto_point_temp, - 0, 2), - SENSOR_ATTR_2(pwm3_auto_point2_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp, store_pwm_auto_point_temp, - 1, 2), - SENSOR_ATTR_2(pwm3_auto_point3_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp, store_pwm_auto_point_temp, - 2, 2), - SENSOR_ATTR_2(pwm3_auto_point4_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp, store_pwm_auto_point_temp, - 3, 2), - SENSOR_ATTR_2(pwm3_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp_hyst, - store_pwm_auto_point_temp_hyst, - 0, 2), - SENSOR_ATTR_2(pwm3_auto_point2_temp_hyst, S_IRUGO, - show_pwm_auto_point_temp_hyst, NULL, 1, 2), - SENSOR_ATTR_2(pwm3_auto_point3_temp_hyst, S_IRUGO, - show_pwm_auto_point_temp_hyst, NULL, 2, 2), - SENSOR_ATTR_2(pwm3_auto_point4_temp_hyst, S_IRUGO, - show_pwm_auto_point_temp_hyst, NULL, 3, 2), -}, { - SENSOR_ATTR_2(pwm4_auto_channels_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_channel, - store_pwm_auto_point_channel, 0, 3), - SENSOR_ATTR_2(pwm4_auto_point1_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 0, 3), - SENSOR_ATTR_2(pwm4_auto_point2_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 1, 3), - SENSOR_ATTR_2(pwm4_auto_point3_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 2, 3), - SENSOR_ATTR_2(pwm4_auto_point4_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 3, 3), - SENSOR_ATTR_2(pwm4_auto_point5_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 4, 3), - SENSOR_ATTR_2(pwm4_auto_point1_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp, store_pwm_auto_point_temp, - 0, 3), - SENSOR_ATTR_2(pwm4_auto_point2_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp, store_pwm_auto_point_temp, - 1, 3), - SENSOR_ATTR_2(pwm4_auto_point3_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp, store_pwm_auto_point_temp, - 2, 3), - SENSOR_ATTR_2(pwm4_auto_point4_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp, store_pwm_auto_point_temp, - 3, 3), - SENSOR_ATTR_2(pwm4_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp_hyst, - store_pwm_auto_point_temp_hyst, - 0, 3), - SENSOR_ATTR_2(pwm4_auto_point2_temp_hyst, S_IRUGO, - show_pwm_auto_point_temp_hyst, NULL, 1, 3), - SENSOR_ATTR_2(pwm4_auto_point3_temp_hyst, S_IRUGO, - show_pwm_auto_point_temp_hyst, NULL, 2, 3), - SENSOR_ATTR_2(pwm4_auto_point4_temp_hyst, S_IRUGO, - show_pwm_auto_point_temp_hyst, NULL, 3, 3), -} }; + if (f71882fg_fan_has_beep[data->type]) + data->fan_beep = f71882fg_read8(data, + F71882FG_REG_FAN_BEEP); -/* Fan attr specific to the f8000 (4th fan input can only measure speed) */ -static struct sensor_device_attribute_2 f8000_fan_attr[] = { - SENSOR_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 0, 3), -}; - -/* - * PWM attr for the f8000, zones mapped to temp instead of to pwm! - * Also the register block at offset A0 maps to TEMP1 (so our temp2, as the - * F8000 starts counting temps at 0), B0 maps the TEMP2 and C0 maps to TEMP0 - */ -static struct sensor_device_attribute_2 f8000_auto_pwm_attr[3][14] = { { - SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_channel, - store_pwm_auto_point_channel, 0, 0), - SENSOR_ATTR_2(temp1_auto_point1_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 0, 2), - SENSOR_ATTR_2(temp1_auto_point2_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 1, 2), - SENSOR_ATTR_2(temp1_auto_point3_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 2, 2), - SENSOR_ATTR_2(temp1_auto_point4_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 3, 2), - SENSOR_ATTR_2(temp1_auto_point5_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 4, 2), - SENSOR_ATTR_2(temp1_auto_point1_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp, store_pwm_auto_point_temp, - 0, 2), - SENSOR_ATTR_2(temp1_auto_point2_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp, store_pwm_auto_point_temp, - 1, 2), - SENSOR_ATTR_2(temp1_auto_point3_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp, store_pwm_auto_point_temp, - 2, 2), - SENSOR_ATTR_2(temp1_auto_point4_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp, store_pwm_auto_point_temp, - 3, 2), - SENSOR_ATTR_2(temp1_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp_hyst, - store_pwm_auto_point_temp_hyst, - 0, 2), - SENSOR_ATTR_2(temp1_auto_point2_temp_hyst, S_IRUGO, - show_pwm_auto_point_temp_hyst, NULL, 1, 2), - SENSOR_ATTR_2(temp1_auto_point3_temp_hyst, S_IRUGO, - show_pwm_auto_point_temp_hyst, NULL, 2, 2), - SENSOR_ATTR_2(temp1_auto_point4_temp_hyst, S_IRUGO, - show_pwm_auto_point_temp_hyst, NULL, 3, 2), -}, { - SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_channel, - store_pwm_auto_point_channel, 0, 1), - SENSOR_ATTR_2(temp2_auto_point1_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 0, 0), - SENSOR_ATTR_2(temp2_auto_point2_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 1, 0), - SENSOR_ATTR_2(temp2_auto_point3_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 2, 0), - SENSOR_ATTR_2(temp2_auto_point4_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 3, 0), - SENSOR_ATTR_2(temp2_auto_point5_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 4, 0), - SENSOR_ATTR_2(temp2_auto_point1_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp, store_pwm_auto_point_temp, - 0, 0), - SENSOR_ATTR_2(temp2_auto_point2_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp, store_pwm_auto_point_temp, - 1, 0), - SENSOR_ATTR_2(temp2_auto_point3_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp, store_pwm_auto_point_temp, - 2, 0), - SENSOR_ATTR_2(temp2_auto_point4_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp, store_pwm_auto_point_temp, - 3, 0), - SENSOR_ATTR_2(temp2_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp_hyst, - store_pwm_auto_point_temp_hyst, - 0, 0), - SENSOR_ATTR_2(temp2_auto_point2_temp_hyst, S_IRUGO, - show_pwm_auto_point_temp_hyst, NULL, 1, 0), - SENSOR_ATTR_2(temp2_auto_point3_temp_hyst, S_IRUGO, - show_pwm_auto_point_temp_hyst, NULL, 2, 0), - SENSOR_ATTR_2(temp2_auto_point4_temp_hyst, S_IRUGO, - show_pwm_auto_point_temp_hyst, NULL, 3, 0), -}, { - SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_channel, - store_pwm_auto_point_channel, 0, 2), - SENSOR_ATTR_2(temp3_auto_point1_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 0, 1), - SENSOR_ATTR_2(temp3_auto_point2_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 1, 1), - SENSOR_ATTR_2(temp3_auto_point3_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 2, 1), - SENSOR_ATTR_2(temp3_auto_point4_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 3, 1), - SENSOR_ATTR_2(temp3_auto_point5_pwm, S_IRUGO|S_IWUSR, - show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, - 4, 1), - SENSOR_ATTR_2(temp3_auto_point1_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp, store_pwm_auto_point_temp, - 0, 1), - SENSOR_ATTR_2(temp3_auto_point2_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp, store_pwm_auto_point_temp, - 1, 1), - SENSOR_ATTR_2(temp3_auto_point3_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp, store_pwm_auto_point_temp, - 2, 1), - SENSOR_ATTR_2(temp3_auto_point4_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp, store_pwm_auto_point_temp, - 3, 1), - SENSOR_ATTR_2(temp3_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, - show_pwm_auto_point_temp_hyst, - store_pwm_auto_point_temp_hyst, - 0, 1), - SENSOR_ATTR_2(temp3_auto_point2_temp_hyst, S_IRUGO, - show_pwm_auto_point_temp_hyst, NULL, 1, 1), - SENSOR_ATTR_2(temp3_auto_point3_temp_hyst, S_IRUGO, - show_pwm_auto_point_temp_hyst, NULL, 2, 1), - SENSOR_ATTR_2(temp3_auto_point4_temp_hyst, S_IRUGO, - show_pwm_auto_point_temp_hyst, NULL, 3, 1), -} }; - -/* Super I/O functions */ -static inline int superio_inb(int base, int reg) -{ - outb(reg, base); - return inb(base + 1); -} - -static int superio_inw(int base, int reg) -{ - int val; - val = superio_inb(base, reg) << 8; - val |= superio_inb(base, reg + 1); - return val; -} - -static inline int superio_enter(int base) -{ - /* Don't step on other drivers' I/O space by accident */ - if (!request_muxed_region(base, 2, DRVNAME)) { - pr_err("I/O address 0x%04x already in use\n", base); - return -EBUSY; - } - - /* according to the datasheet the key must be send twice! */ - outb(SIO_UNLOCK_KEY, base); - outb(SIO_UNLOCK_KEY, base); - - return 0; -} - -static inline void superio_select(int base, int ld) -{ - outb(SIO_REG_LDSEL, base); - outb(ld, base + 1); -} - -static inline void superio_exit(int base) -{ - outb(SIO_LOCK_KEY, base); - release_region(base, 2); -} - -static inline int fan_from_reg(u16 reg) -{ - return reg ? (1500000 / reg) : 0; -} - -static inline u16 fan_to_reg(int fan) -{ - return fan ? (1500000 / fan) : 0; -} - -static u8 f71882fg_read8(struct f71882fg_data *data, u8 reg) -{ - u8 val; - - outb(reg, data->addr + ADDR_REG_OFFSET); - val = inb(data->addr + DATA_REG_OFFSET); - - return val; -} - -static u16 f71882fg_read16(struct f71882fg_data *data, u8 reg) -{ - u16 val; - - val = f71882fg_read8(data, reg) << 8; - val |= f71882fg_read8(data, reg + 1); - - return val; -} - -static void f71882fg_write8(struct f71882fg_data *data, u8 reg, u8 val) -{ - outb(reg, data->addr + ADDR_REG_OFFSET); - outb(val, data->addr + DATA_REG_OFFSET); -} - -static void f71882fg_write16(struct f71882fg_data *data, u8 reg, u16 val) -{ - f71882fg_write8(data, reg, val >> 8); - f71882fg_write8(data, reg + 1, val & 0xff); -} - -static u16 f71882fg_read_temp(struct f71882fg_data *data, int nr) -{ - if (data->type == f71858fg) - return f71882fg_read16(data, F71882FG_REG_TEMP(nr)); - else - return f71882fg_read8(data, F71882FG_REG_TEMP(nr)); -} - -static struct f71882fg_data *f71882fg_update_device(struct device *dev) -{ - struct f71882fg_data *data = dev_get_drvdata(dev); - int nr_fans = f71882fg_nr_fans[data->type]; - int nr_temps = f71882fg_nr_temps[data->type]; - int nr, reg, point; - - mutex_lock(&data->update_lock); - - /* Update once every 60 seconds */ - if (time_after(jiffies, data->last_limits + 60 * HZ) || - !data->valid) { - if (f71882fg_has_in1_alarm[data->type]) { - if (data->type == f81866a) { - data->in1_max = - f71882fg_read8(data, - F81866_REG_IN1_HIGH); - data->in_beep = - f71882fg_read8(data, - F81866_REG_IN_BEEP); - } else { - data->in1_max = - f71882fg_read8(data, - F71882FG_REG_IN1_HIGH); - data->in_beep = - f71882fg_read8(data, - F71882FG_REG_IN_BEEP); - } - } - - /* Get High & boundary temps*/ - for (nr = data->temp_start; nr < nr_temps + data->temp_start; - nr++) { - data->temp_ovt[nr] = f71882fg_read8(data, - F71882FG_REG_TEMP_OVT(nr)); - data->temp_high[nr] = f71882fg_read8(data, - F71882FG_REG_TEMP_HIGH(nr)); - } - - if (data->type != f8000) { - data->temp_hyst[0] = f71882fg_read8(data, - F71882FG_REG_TEMP_HYST(0)); - data->temp_hyst[1] = f71882fg_read8(data, - F71882FG_REG_TEMP_HYST(1)); - } - /* All but the f71858fg / f8000 have this register */ - if ((data->type != f71858fg) && (data->type != f8000)) { - reg = f71882fg_read8(data, F71882FG_REG_TEMP_TYPE); - data->temp_type[1] = (reg & 0x02) ? 2 : 4; - data->temp_type[2] = (reg & 0x04) ? 2 : 4; - data->temp_type[3] = (reg & 0x08) ? 2 : 4; - } - - if (f71882fg_fan_has_beep[data->type]) - data->fan_beep = f71882fg_read8(data, - F71882FG_REG_FAN_BEEP); - - if (f71882fg_temp_has_beep[data->type]) - data->temp_beep = f71882fg_read8(data, - F71882FG_REG_TEMP_BEEP); + if (f71882fg_temp_has_beep[data->type]) + data->temp_beep = f71882fg_read8(data, + F71882FG_REG_TEMP_BEEP); data->pwm_enable = f71882fg_read8(data, F71882FG_REG_PWM_ENABLE); @@ -1369,225 +510,43 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev) return data; } -/* Sysfs Interface */ -static ssize_t show_fan(struct device *dev, struct device_attribute *devattr, +static ssize_t name_show(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%s\n", f71882fg_names[data->type]); +} + +static DEVICE_ATTR_RO(name); + +static ssize_t show_temp(struct device *dev, struct device_attribute *devattr, char *buf) { struct f71882fg_data *data = f71882fg_update_device(dev); int nr = to_sensor_dev_attr_2(devattr)->index; - int speed = fan_from_reg(data->fan[nr]); + int sign, temp; - if (speed == FAN_MIN_DETECT) - speed = 0; + if (data->type == f71858fg) { + /* TEMP_TABLE_SEL 1 or 3 ? */ + if (data->temp_config & 1) { + sign = data->temp[nr] & 0x0001; + temp = (data->temp[nr] >> 5) & 0x7ff; + } else { + sign = data->temp[nr] & 0x8000; + temp = (data->temp[nr] >> 5) & 0x3ff; + } + temp *= 125; + if (sign) + temp -= 128000; + } else { + temp = ((s8)data->temp[nr]) * 1000; + } - return sprintf(buf, "%d\n", speed); + return sprintf(buf, "%d\n", temp); } -static ssize_t show_fan_full_speed(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; - int speed = fan_from_reg(data->fan_full_speed[nr]); - return sprintf(buf, "%d\n", speed); -} - -static ssize_t store_fan_full_speed(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) -{ - struct f71882fg_data *data = dev_get_drvdata(dev); - int err, nr = to_sensor_dev_attr_2(devattr)->index; - long val; - - err = kstrtol(buf, 10, &val); - if (err) - return err; - - val = clamp_val(val, 23, 1500000); - val = fan_to_reg(val); - - mutex_lock(&data->update_lock); - f71882fg_write16(data, F71882FG_REG_FAN_FULL_SPEED(nr), val); - data->fan_full_speed[nr] = val; - mutex_unlock(&data->update_lock); - - return count; -} - -static ssize_t show_fan_beep(struct device *dev, struct device_attribute - *devattr, char *buf) -{ - struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; - - if (data->fan_beep & (1 << nr)) - return sprintf(buf, "1\n"); - else - return sprintf(buf, "0\n"); -} - -static ssize_t store_fan_beep(struct device *dev, struct device_attribute - *devattr, const char *buf, size_t count) -{ - struct f71882fg_data *data = dev_get_drvdata(dev); - int err, nr = to_sensor_dev_attr_2(devattr)->index; - unsigned long val; - - err = kstrtoul(buf, 10, &val); - if (err) - return err; - - mutex_lock(&data->update_lock); - data->fan_beep = f71882fg_read8(data, F71882FG_REG_FAN_BEEP); - if (val) - data->fan_beep |= 1 << nr; - else - data->fan_beep &= ~(1 << nr); - - f71882fg_write8(data, F71882FG_REG_FAN_BEEP, data->fan_beep); - mutex_unlock(&data->update_lock); - - return count; -} - -static ssize_t show_fan_alarm(struct device *dev, struct device_attribute - *devattr, char *buf) -{ - struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; - - if (data->fan_status & (1 << nr)) - return sprintf(buf, "1\n"); - else - return sprintf(buf, "0\n"); -} - -static ssize_t show_in(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; - - return sprintf(buf, "%d\n", data->in[nr] * 8); -} - -static ssize_t show_in_max(struct device *dev, struct device_attribute - *devattr, char *buf) -{ - struct f71882fg_data *data = f71882fg_update_device(dev); - - return sprintf(buf, "%d\n", data->in1_max * 8); -} - -static ssize_t store_in_max(struct device *dev, struct device_attribute - *devattr, const char *buf, size_t count) -{ - struct f71882fg_data *data = dev_get_drvdata(dev); - int err; - long val; - - err = kstrtol(buf, 10, &val); - if (err) - return err; - - val /= 8; - val = clamp_val(val, 0, 255); - - mutex_lock(&data->update_lock); - if (data->type == f81866a) - f71882fg_write8(data, F81866_REG_IN1_HIGH, val); - else - f71882fg_write8(data, F71882FG_REG_IN1_HIGH, val); - data->in1_max = val; - mutex_unlock(&data->update_lock); - - return count; -} - -static ssize_t show_in_beep(struct device *dev, struct device_attribute - *devattr, char *buf) -{ - struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; - - if (data->in_beep & (1 << nr)) - return sprintf(buf, "1\n"); - else - return sprintf(buf, "0\n"); -} - -static ssize_t store_in_beep(struct device *dev, struct device_attribute - *devattr, const char *buf, size_t count) -{ - struct f71882fg_data *data = dev_get_drvdata(dev); - int err, nr = to_sensor_dev_attr_2(devattr)->index; - unsigned long val; - - err = kstrtoul(buf, 10, &val); - if (err) - return err; - - mutex_lock(&data->update_lock); - if (data->type == f81866a) - data->in_beep = f71882fg_read8(data, F81866_REG_IN_BEEP); - else - data->in_beep = f71882fg_read8(data, F71882FG_REG_IN_BEEP); - - if (val) - data->in_beep |= 1 << nr; - else - data->in_beep &= ~(1 << nr); - - if (data->type == f81866a) - f71882fg_write8(data, F81866_REG_IN_BEEP, data->in_beep); - else - f71882fg_write8(data, F71882FG_REG_IN_BEEP, data->in_beep); - mutex_unlock(&data->update_lock); - - return count; -} - -static ssize_t show_in_alarm(struct device *dev, struct device_attribute - *devattr, char *buf) -{ - struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; - - if (data->in_status & (1 << nr)) - return sprintf(buf, "1\n"); - else - return sprintf(buf, "0\n"); -} - -static ssize_t show_temp(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; - int sign, temp; - - if (data->type == f71858fg) { - /* TEMP_TABLE_SEL 1 or 3 ? */ - if (data->temp_config & 1) { - sign = data->temp[nr] & 0x0001; - temp = (data->temp[nr] >> 5) & 0x7ff; - } else { - sign = data->temp[nr] & 0x8000; - temp = (data->temp[nr] >> 5) & 0x3ff; - } - temp *= 125; - if (sign) - temp -= 128000; - } else { - temp = ((s8)data->temp[nr]) * 1000; - } - - return sprintf(buf, "%d\n", temp); -} - -static ssize_t show_temp_max(struct device *dev, struct device_attribute - *devattr, char *buf) +static ssize_t show_temp_max(struct device *dev, struct device_attribute + *devattr, char *buf) { struct f71882fg_data *data = f71882fg_update_device(dev); int nr = to_sensor_dev_attr_2(devattr)->index; @@ -1670,6 +629,18 @@ static ssize_t store_temp_max_hyst(struct device *dev, struct device_attribute return ret; } +static ssize_t show_temp_alarm(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + + if (data->temp_status & (1 << nr)) + return sprintf(buf, "1\n"); + else + return sprintf(buf, "0\n"); +} + static ssize_t show_temp_crit(struct device *dev, struct device_attribute *devattr, char *buf) { @@ -1719,161 +690,455 @@ static ssize_t show_temp_crit_hyst(struct device *dev, struct device_attribute return sprintf(buf, "%d\n", temp_crit_hyst); } -static ssize_t show_temp_type(struct device *dev, struct device_attribute - *devattr, char *buf) -{ - struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; - - return sprintf(buf, "%d\n", data->temp_type[nr]); -} - -static ssize_t show_temp_beep(struct device *dev, struct device_attribute +static ssize_t show_temp_fault(struct device *dev, struct device_attribute *devattr, char *buf) { struct f71882fg_data *data = f71882fg_update_device(dev); int nr = to_sensor_dev_attr_2(devattr)->index; - if (data->temp_beep & (1 << nr)) + if (data->temp_diode_open & (1 << nr)) return sprintf(buf, "1\n"); else return sprintf(buf, "0\n"); } -static ssize_t store_temp_beep(struct device *dev, struct device_attribute - *devattr, const char *buf, size_t count) -{ - struct f71882fg_data *data = dev_get_drvdata(dev); - int err, nr = to_sensor_dev_attr_2(devattr)->index; - unsigned long val; - - err = kstrtoul(buf, 10, &val); - if (err) - return err; - - mutex_lock(&data->update_lock); - data->temp_beep = f71882fg_read8(data, F71882FG_REG_TEMP_BEEP); - if (val) - data->temp_beep |= 1 << nr; - else - data->temp_beep &= ~(1 << nr); - - f71882fg_write8(data, F71882FG_REG_TEMP_BEEP, data->temp_beep); - mutex_unlock(&data->update_lock); - - return count; -} - -static ssize_t show_temp_alarm(struct device *dev, struct device_attribute - *devattr, char *buf) -{ - struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; - - if (data->temp_status & (1 << nr)) - return sprintf(buf, "1\n"); - else - return sprintf(buf, "0\n"); -} +/* + * Temp attr for the f71858fg, the f71858fg is special as it has its + * temperature indexes start at 0 (the others start at 1) + */ +static struct sensor_device_attribute_2 f71858fg_temp_attr[] = { + SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0), + SENSOR_ATTR_2(temp1_max, S_IRUGO|S_IWUSR, show_temp_max, + store_temp_max, 0, 0), + SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst, + store_temp_max_hyst, 0, 0), + SENSOR_ATTR_2(temp1_max_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 0), + SENSOR_ATTR_2(temp1_crit, S_IRUGO|S_IWUSR, show_temp_crit, + store_temp_crit, 0, 0), + SENSOR_ATTR_2(temp1_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, + 0, 0), + SENSOR_ATTR_2(temp1_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 4), + SENSOR_ATTR_2(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0, 0), + SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0, 1), + SENSOR_ATTR_2(temp2_max, S_IRUGO|S_IWUSR, show_temp_max, + store_temp_max, 0, 1), + SENSOR_ATTR_2(temp2_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst, + store_temp_max_hyst, 0, 1), + SENSOR_ATTR_2(temp2_max_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 1), + SENSOR_ATTR_2(temp2_crit, S_IRUGO|S_IWUSR, show_temp_crit, + store_temp_crit, 0, 1), + SENSOR_ATTR_2(temp2_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, + 0, 1), + SENSOR_ATTR_2(temp2_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 5), + SENSOR_ATTR_2(temp2_fault, S_IRUGO, show_temp_fault, NULL, 0, 1), + SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0, 2), + SENSOR_ATTR_2(temp3_max, S_IRUGO|S_IWUSR, show_temp_max, + store_temp_max, 0, 2), + SENSOR_ATTR_2(temp3_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst, + store_temp_max_hyst, 0, 2), + SENSOR_ATTR_2(temp3_max_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 2), + SENSOR_ATTR_2(temp3_crit, S_IRUGO|S_IWUSR, show_temp_crit, + store_temp_crit, 0, 2), + SENSOR_ATTR_2(temp3_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, + 0, 2), + SENSOR_ATTR_2(temp3_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 6), + SENSOR_ATTR_2(temp3_fault, S_IRUGO, show_temp_fault, NULL, 0, 2), +}; -static ssize_t show_temp_fault(struct device *dev, struct device_attribute +static ssize_t show_temp_type(struct device *dev, struct device_attribute *devattr, char *buf) { struct f71882fg_data *data = f71882fg_update_device(dev); int nr = to_sensor_dev_attr_2(devattr)->index; - if (data->temp_diode_open & (1 << nr)) - return sprintf(buf, "1\n"); - else - return sprintf(buf, "0\n"); + return sprintf(buf, "%d\n", data->temp_type[nr]); } -static ssize_t show_pwm(struct device *dev, - struct device_attribute *devattr, char *buf) +/* Temp attr for the standard models */ +static struct sensor_device_attribute_2 fxxxx_temp_attr[3][9] = { { + SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 1), + SENSOR_ATTR_2(temp1_max, S_IRUGO|S_IWUSR, show_temp_max, + store_temp_max, 0, 1), + SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst, + store_temp_max_hyst, 0, 1), + /* + * Should really be temp1_max_alarm, but older versions did not handle + * the max and crit alarms separately and lm_sensors v2 depends on the + * presence of temp#_alarm files. The same goes for temp2/3 _alarm. + */ + SENSOR_ATTR_2(temp1_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 1), + SENSOR_ATTR_2(temp1_crit, S_IRUGO|S_IWUSR, show_temp_crit, + store_temp_crit, 0, 1), + SENSOR_ATTR_2(temp1_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, + 0, 1), + SENSOR_ATTR_2(temp1_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 5), + SENSOR_ATTR_2(temp1_type, S_IRUGO, show_temp_type, NULL, 0, 1), + SENSOR_ATTR_2(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0, 1), +}, { + SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0, 2), + SENSOR_ATTR_2(temp2_max, S_IRUGO|S_IWUSR, show_temp_max, + store_temp_max, 0, 2), + SENSOR_ATTR_2(temp2_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst, + store_temp_max_hyst, 0, 2), + /* Should be temp2_max_alarm, see temp1_alarm note */ + SENSOR_ATTR_2(temp2_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 2), + SENSOR_ATTR_2(temp2_crit, S_IRUGO|S_IWUSR, show_temp_crit, + store_temp_crit, 0, 2), + SENSOR_ATTR_2(temp2_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, + 0, 2), + SENSOR_ATTR_2(temp2_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 6), + SENSOR_ATTR_2(temp2_type, S_IRUGO, show_temp_type, NULL, 0, 2), + SENSOR_ATTR_2(temp2_fault, S_IRUGO, show_temp_fault, NULL, 0, 2), +}, { + SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0, 3), + SENSOR_ATTR_2(temp3_max, S_IRUGO|S_IWUSR, show_temp_max, + store_temp_max, 0, 3), + SENSOR_ATTR_2(temp3_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst, + store_temp_max_hyst, 0, 3), + /* Should be temp3_max_alarm, see temp1_alarm note */ + SENSOR_ATTR_2(temp3_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 3), + SENSOR_ATTR_2(temp3_crit, S_IRUGO|S_IWUSR, show_temp_crit, + store_temp_crit, 0, 3), + SENSOR_ATTR_2(temp3_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, + 0, 3), + SENSOR_ATTR_2(temp3_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 7), + SENSOR_ATTR_2(temp3_type, S_IRUGO, show_temp_type, NULL, 0, 3), + SENSOR_ATTR_2(temp3_fault, S_IRUGO, show_temp_fault, NULL, 0, 3), +} }; + +static ssize_t show_temp_beep(struct device *dev, struct device_attribute + *devattr, char *buf) { struct f71882fg_data *data = f71882fg_update_device(dev); - int val, nr = to_sensor_dev_attr_2(devattr)->index; - mutex_lock(&data->update_lock); - if (data->pwm_enable & (1 << (2 * nr))) - /* PWM mode */ - val = data->pwm[nr]; - else { - /* RPM mode */ - val = 255 * fan_from_reg(data->fan_target[nr]) - / fan_from_reg(data->fan_full_speed[nr]); - } - mutex_unlock(&data->update_lock); - return sprintf(buf, "%d\n", val); + int nr = to_sensor_dev_attr_2(devattr)->index; + + if (data->temp_beep & (1 << nr)) + return sprintf(buf, "1\n"); + else + return sprintf(buf, "0\n"); } -static ssize_t store_pwm(struct device *dev, - struct device_attribute *devattr, const char *buf, - size_t count) +static ssize_t store_temp_beep(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count) { struct f71882fg_data *data = dev_get_drvdata(dev); int err, nr = to_sensor_dev_attr_2(devattr)->index; - long val; + unsigned long val; - err = kstrtol(buf, 10, &val); + err = kstrtoul(buf, 10, &val); if (err) return err; - val = clamp_val(val, 0, 255); - mutex_lock(&data->update_lock); - data->pwm_enable = f71882fg_read8(data, F71882FG_REG_PWM_ENABLE); - if ((data->type == f8000 && ((data->pwm_enable >> 2 * nr) & 3) != 2) || - (data->type != f8000 && !((data->pwm_enable >> 2 * nr) & 2))) { - count = -EROFS; - goto leave; - } - if (data->pwm_enable & (1 << (2 * nr))) { - /* PWM mode */ - f71882fg_write8(data, F71882FG_REG_PWM(nr), val); - data->pwm[nr] = val; - } else { - /* RPM mode */ - int target, full_speed; - full_speed = f71882fg_read16(data, - F71882FG_REG_FAN_FULL_SPEED(nr)); - target = fan_to_reg(val * fan_from_reg(full_speed) / 255); - f71882fg_write16(data, F71882FG_REG_FAN_TARGET(nr), target); - data->fan_target[nr] = target; - data->fan_full_speed[nr] = full_speed; - } -leave: + data->temp_beep = f71882fg_read8(data, F71882FG_REG_TEMP_BEEP); + if (val) + data->temp_beep |= 1 << nr; + else + data->temp_beep &= ~(1 << nr); + + f71882fg_write8(data, F71882FG_REG_TEMP_BEEP, data->temp_beep); mutex_unlock(&data->update_lock); return count; } -static ssize_t show_simple_pwm(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct f71882fg_data *data = f71882fg_update_device(dev); - int val, nr = to_sensor_dev_attr_2(devattr)->index; +/* Temp attr for models which can beep on temp alarm */ +static struct sensor_device_attribute_2 fxxxx_temp_beep_attr[3][2] = { { + SENSOR_ATTR_2(temp1_max_beep, S_IRUGO|S_IWUSR, show_temp_beep, + store_temp_beep, 0, 1), + SENSOR_ATTR_2(temp1_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep, + store_temp_beep, 0, 5), +}, { + SENSOR_ATTR_2(temp2_max_beep, S_IRUGO|S_IWUSR, show_temp_beep, + store_temp_beep, 0, 2), + SENSOR_ATTR_2(temp2_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep, + store_temp_beep, 0, 6), +}, { + SENSOR_ATTR_2(temp3_max_beep, S_IRUGO|S_IWUSR, show_temp_beep, + store_temp_beep, 0, 3), + SENSOR_ATTR_2(temp3_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep, + store_temp_beep, 0, 7), +} }; - val = data->pwm[nr]; - return sprintf(buf, "%d\n", val); -} +static struct sensor_device_attribute_2 f81866_temp_beep_attr[3][2] = { { + SENSOR_ATTR_2(temp1_max_beep, S_IRUGO|S_IWUSR, show_temp_beep, + store_temp_beep, 0, 0), + SENSOR_ATTR_2(temp1_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep, + store_temp_beep, 0, 4), +}, { + SENSOR_ATTR_2(temp2_max_beep, S_IRUGO|S_IWUSR, show_temp_beep, + store_temp_beep, 0, 1), + SENSOR_ATTR_2(temp2_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep, + store_temp_beep, 0, 5), +}, { + SENSOR_ATTR_2(temp3_max_beep, S_IRUGO|S_IWUSR, show_temp_beep, + store_temp_beep, 0, 2), + SENSOR_ATTR_2(temp3_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep, + store_temp_beep, 0, 6), +} }; -static ssize_t store_simple_pwm(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) -{ - struct f71882fg_data *data = dev_get_drvdata(dev); - int err, nr = to_sensor_dev_attr_2(devattr)->index; - long val; +/* + * Temp attr for the f8000 + * Note on the f8000 temp_ovt (crit) is used as max, and temp_high (max) + * is used as hysteresis value to clear alarms + * Also like the f71858fg its temperature indexes start at 0 + */ +static struct sensor_device_attribute_2 f8000_temp_attr[] = { + SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0), + SENSOR_ATTR_2(temp1_max, S_IRUGO|S_IWUSR, show_temp_crit, + store_temp_crit, 0, 0), + SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO|S_IWUSR, show_temp_max, + store_temp_max, 0, 0), + SENSOR_ATTR_2(temp1_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 4), + SENSOR_ATTR_2(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0, 0), + SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0, 1), + SENSOR_ATTR_2(temp2_max, S_IRUGO|S_IWUSR, show_temp_crit, + store_temp_crit, 0, 1), + SENSOR_ATTR_2(temp2_max_hyst, S_IRUGO|S_IWUSR, show_temp_max, + store_temp_max, 0, 1), + SENSOR_ATTR_2(temp2_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 5), + SENSOR_ATTR_2(temp2_fault, S_IRUGO, show_temp_fault, NULL, 0, 1), + SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0, 2), + SENSOR_ATTR_2(temp3_max, S_IRUGO|S_IWUSR, show_temp_crit, + store_temp_crit, 0, 2), + SENSOR_ATTR_2(temp3_max_hyst, S_IRUGO|S_IWUSR, show_temp_max, + store_temp_max, 0, 2), + SENSOR_ATTR_2(temp3_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 6), + SENSOR_ATTR_2(temp3_fault, S_IRUGO, show_temp_fault, NULL, 0, 2), +}; - err = kstrtol(buf, 10, &val); - if (err) - return err; +static ssize_t show_in(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; - val = clamp_val(val, 0, 255); + return sprintf(buf, "%d\n", data->in[nr] * 8); +} - mutex_lock(&data->update_lock); - f71882fg_write8(data, F71882FG_REG_PWM(nr), val); - data->pwm[nr] = val; +/* in attr for all models */ +static struct sensor_device_attribute_2 fxxxx_in_attr[] = { + SENSOR_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0), + SENSOR_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 0, 1), + SENSOR_ATTR_2(in2_input, S_IRUGO, show_in, NULL, 0, 2), + SENSOR_ATTR_2(in3_input, S_IRUGO, show_in, NULL, 0, 3), + SENSOR_ATTR_2(in4_input, S_IRUGO, show_in, NULL, 0, 4), + SENSOR_ATTR_2(in5_input, S_IRUGO, show_in, NULL, 0, 5), + SENSOR_ATTR_2(in6_input, S_IRUGO, show_in, NULL, 0, 6), + SENSOR_ATTR_2(in7_input, S_IRUGO, show_in, NULL, 0, 7), + SENSOR_ATTR_2(in8_input, S_IRUGO, show_in, NULL, 0, 8), + SENSOR_ATTR_2(in9_input, S_IRUGO, show_in, NULL, 0, 9), + SENSOR_ATTR_2(in10_input, S_IRUGO, show_in, NULL, 0, 10), +}; + +static ssize_t show_in_max(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + + return sprintf(buf, "%d\n", data->in1_max * 8); +} + +static ssize_t store_in_max(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + int err; + long val; + + err = kstrtol(buf, 10, &val); + if (err) + return err; + + val /= 8; + val = clamp_val(val, 0, 255); + + mutex_lock(&data->update_lock); + if (data->type == f81866a) + f71882fg_write8(data, F81866_REG_IN1_HIGH, val); + else + f71882fg_write8(data, F71882FG_REG_IN1_HIGH, val); + data->in1_max = val; + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_in_beep(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + + if (data->in_beep & (1 << nr)) + return sprintf(buf, "1\n"); + else + return sprintf(buf, "0\n"); +} + +static ssize_t store_in_beep(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + int err, nr = to_sensor_dev_attr_2(devattr)->index; + unsigned long val; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + + mutex_lock(&data->update_lock); + if (data->type == f81866a) + data->in_beep = f71882fg_read8(data, F81866_REG_IN_BEEP); + else + data->in_beep = f71882fg_read8(data, F71882FG_REG_IN_BEEP); + + if (val) + data->in_beep |= 1 << nr; + else + data->in_beep &= ~(1 << nr); + + if (data->type == f81866a) + f71882fg_write8(data, F81866_REG_IN_BEEP, data->in_beep); + else + f71882fg_write8(data, F71882FG_REG_IN_BEEP, data->in_beep); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_in_alarm(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + + if (data->in_status & (1 << nr)) + return sprintf(buf, "1\n"); + else + return sprintf(buf, "0\n"); +} + +/* For models with in1 alarm capability */ +static struct sensor_device_attribute_2 fxxxx_in1_alarm_attr[] = { + SENSOR_ATTR_2(in1_max, S_IRUGO|S_IWUSR, show_in_max, store_in_max, + 0, 1), + SENSOR_ATTR_2(in1_beep, S_IRUGO|S_IWUSR, show_in_beep, store_in_beep, + 0, 1), + SENSOR_ATTR_2(in1_alarm, S_IRUGO, show_in_alarm, NULL, 0, 1), +}; + +static ssize_t show_fan(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + int speed = fan_from_reg(data->fan[nr]); + + if (speed == FAN_MIN_DETECT) + speed = 0; + + return sprintf(buf, "%d\n", speed); +} + +static ssize_t show_fan_full_speed(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + int speed = fan_from_reg(data->fan_full_speed[nr]); + return sprintf(buf, "%d\n", speed); +} + +static ssize_t store_fan_full_speed(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + int err, nr = to_sensor_dev_attr_2(devattr)->index; + long val; + + err = kstrtol(buf, 10, &val); + if (err) + return err; + + val = clamp_val(val, 23, 1500000); + val = fan_to_reg(val); + + mutex_lock(&data->update_lock); + f71882fg_write16(data, F71882FG_REG_FAN_FULL_SPEED(nr), val); + data->fan_full_speed[nr] = val; + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_fan_alarm(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + + if (data->fan_status & (1 << nr)) + return sprintf(buf, "1\n"); + else + return sprintf(buf, "0\n"); +} + +static ssize_t show_pwm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int val, nr = to_sensor_dev_attr_2(devattr)->index; + mutex_lock(&data->update_lock); + if (data->pwm_enable & (1 << (2 * nr))) + /* PWM mode */ + val = data->pwm[nr]; + else { + /* RPM mode */ + val = 255 * fan_from_reg(data->fan_target[nr]) + / fan_from_reg(data->fan_full_speed[nr]); + } + mutex_unlock(&data->update_lock); + return sprintf(buf, "%d\n", val); +} + +static ssize_t store_pwm(struct device *dev, + struct device_attribute *devattr, const char *buf, + size_t count) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + int err, nr = to_sensor_dev_attr_2(devattr)->index; + long val; + + err = kstrtol(buf, 10, &val); + if (err) + return err; + + val = clamp_val(val, 0, 255); + + mutex_lock(&data->update_lock); + data->pwm_enable = f71882fg_read8(data, F71882FG_REG_PWM_ENABLE); + if ((data->type == f8000 && ((data->pwm_enable >> 2 * nr) & 3) != 2) || + (data->type != f8000 && !((data->pwm_enable >> 2 * nr) & 2))) { + count = -EROFS; + goto leave; + } + if (data->pwm_enable & (1 << (2 * nr))) { + /* PWM mode */ + f71882fg_write8(data, F71882FG_REG_PWM(nr), val); + data->pwm[nr] = val; + } else { + /* RPM mode */ + int target, full_speed; + full_speed = f71882fg_read16(data, + F71882FG_REG_FAN_FULL_SPEED(nr)); + target = fan_to_reg(val * fan_from_reg(full_speed) / 255); + f71882fg_write16(data, F71882FG_REG_FAN_TARGET(nr), target); + data->fan_target[nr] = target; + data->fan_full_speed[nr] = full_speed; + } +leave: mutex_unlock(&data->update_lock); return count; @@ -1961,252 +1226,878 @@ leave: return count; } -static ssize_t show_pwm_auto_point_pwm(struct device *dev, - struct device_attribute *devattr, - char *buf) +static ssize_t show_pwm_interpolate(struct device *dev, + struct device_attribute *devattr, char *buf) { int result; struct f71882fg_data *data = f71882fg_update_device(dev); - int pwm = to_sensor_dev_attr_2(devattr)->index; - int point = to_sensor_dev_attr_2(devattr)->nr; + int nr = to_sensor_dev_attr_2(devattr)->index; - mutex_lock(&data->update_lock); - if (data->pwm_enable & (1 << (2 * pwm))) { - /* PWM mode */ - result = data->pwm_auto_point_pwm[pwm][point]; - } else { - /* RPM mode */ - result = 32 * 255 / (32 + data->pwm_auto_point_pwm[pwm][point]); - } - mutex_unlock(&data->update_lock); + result = (data->pwm_auto_point_mapping[nr] >> 4) & 1; return sprintf(buf, "%d\n", result); } -static ssize_t store_pwm_auto_point_pwm(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) +static ssize_t store_pwm_interpolate(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) { struct f71882fg_data *data = dev_get_drvdata(dev); - int err, pwm = to_sensor_dev_attr_2(devattr)->index; - int point = to_sensor_dev_attr_2(devattr)->nr; - long val; + int err, nr = to_sensor_dev_attr_2(devattr)->index; + unsigned long val; - err = kstrtol(buf, 10, &val); + err = kstrtoul(buf, 10, &val); + if (err) + return err; + + mutex_lock(&data->update_lock); + data->pwm_auto_point_mapping[nr] = + f71882fg_read8(data, F71882FG_REG_POINT_MAPPING(nr)); + if (val) + val = data->pwm_auto_point_mapping[nr] | (1 << 4); + else + val = data->pwm_auto_point_mapping[nr] & (~(1 << 4)); + f71882fg_write8(data, F71882FG_REG_POINT_MAPPING(nr), val); + data->pwm_auto_point_mapping[nr] = val; + mutex_unlock(&data->update_lock); + + return count; +} + +/* Fan / PWM attr common to all models */ +static struct sensor_device_attribute_2 fxxxx_fan_attr[4][6] = { { + SENSOR_ATTR_2(fan1_input, S_IRUGO, show_fan, NULL, 0, 0), + SENSOR_ATTR_2(fan1_full_speed, S_IRUGO|S_IWUSR, + show_fan_full_speed, + store_fan_full_speed, 0, 0), + SENSOR_ATTR_2(fan1_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 0), + SENSOR_ATTR_2(pwm1, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 0), + SENSOR_ATTR_2(pwm1_enable, S_IRUGO|S_IWUSR, show_pwm_enable, + store_pwm_enable, 0, 0), + SENSOR_ATTR_2(pwm1_interpolate, S_IRUGO|S_IWUSR, + show_pwm_interpolate, store_pwm_interpolate, 0, 0), +}, { + SENSOR_ATTR_2(fan2_input, S_IRUGO, show_fan, NULL, 0, 1), + SENSOR_ATTR_2(fan2_full_speed, S_IRUGO|S_IWUSR, + show_fan_full_speed, + store_fan_full_speed, 0, 1), + SENSOR_ATTR_2(fan2_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 1), + SENSOR_ATTR_2(pwm2, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 1), + SENSOR_ATTR_2(pwm2_enable, S_IRUGO|S_IWUSR, show_pwm_enable, + store_pwm_enable, 0, 1), + SENSOR_ATTR_2(pwm2_interpolate, S_IRUGO|S_IWUSR, + show_pwm_interpolate, store_pwm_interpolate, 0, 1), +}, { + SENSOR_ATTR_2(fan3_input, S_IRUGO, show_fan, NULL, 0, 2), + SENSOR_ATTR_2(fan3_full_speed, S_IRUGO|S_IWUSR, + show_fan_full_speed, + store_fan_full_speed, 0, 2), + SENSOR_ATTR_2(fan3_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 2), + SENSOR_ATTR_2(pwm3, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 2), + SENSOR_ATTR_2(pwm3_enable, S_IRUGO|S_IWUSR, show_pwm_enable, + store_pwm_enable, 0, 2), + SENSOR_ATTR_2(pwm3_interpolate, S_IRUGO|S_IWUSR, + show_pwm_interpolate, store_pwm_interpolate, 0, 2), +}, { + SENSOR_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 0, 3), + SENSOR_ATTR_2(fan4_full_speed, S_IRUGO|S_IWUSR, + show_fan_full_speed, + store_fan_full_speed, 0, 3), + SENSOR_ATTR_2(fan4_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 3), + SENSOR_ATTR_2(pwm4, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 3), + SENSOR_ATTR_2(pwm4_enable, S_IRUGO|S_IWUSR, show_pwm_enable, + store_pwm_enable, 0, 3), + SENSOR_ATTR_2(pwm4_interpolate, S_IRUGO|S_IWUSR, + show_pwm_interpolate, store_pwm_interpolate, 0, 3), +} }; + +static ssize_t show_simple_pwm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int val, nr = to_sensor_dev_attr_2(devattr)->index; + + val = data->pwm[nr]; + return sprintf(buf, "%d\n", val); +} + +static ssize_t store_simple_pwm(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + int err, nr = to_sensor_dev_attr_2(devattr)->index; + long val; + + err = kstrtol(buf, 10, &val); if (err) return err; val = clamp_val(val, 0, 255); mutex_lock(&data->update_lock); - data->pwm_enable = f71882fg_read8(data, F71882FG_REG_PWM_ENABLE); - if (data->pwm_enable & (1 << (2 * pwm))) { - /* PWM mode */ - } else { - /* RPM mode */ - if (val < 29) /* Prevent negative numbers */ - val = 255; - else - val = (255 - val) * 32 / val; - } - f71882fg_write8(data, F71882FG_REG_POINT_PWM(pwm, point), val); - data->pwm_auto_point_pwm[pwm][point] = val; + f71882fg_write8(data, F71882FG_REG_PWM(nr), val); + data->pwm[nr] = val; mutex_unlock(&data->update_lock); return count; } -static ssize_t show_pwm_auto_point_temp_hyst(struct device *dev, - struct device_attribute *devattr, - char *buf) +/* Attr for the third fan of the f71808a, which only has manual pwm */ +static struct sensor_device_attribute_2 f71808a_fan3_attr[] = { + SENSOR_ATTR_2(fan3_input, S_IRUGO, show_fan, NULL, 0, 2), + SENSOR_ATTR_2(fan3_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 2), + SENSOR_ATTR_2(pwm3, S_IRUGO|S_IWUSR, + show_simple_pwm, store_simple_pwm, 0, 2), +}; + +static ssize_t show_fan_beep(struct device *dev, struct device_attribute + *devattr, char *buf) { - int result = 0; struct f71882fg_data *data = f71882fg_update_device(dev); int nr = to_sensor_dev_attr_2(devattr)->index; - int point = to_sensor_dev_attr_2(devattr)->nr; + + if (data->fan_beep & (1 << nr)) + return sprintf(buf, "1\n"); + else + return sprintf(buf, "0\n"); +} + +static ssize_t store_fan_beep(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + int err, nr = to_sensor_dev_attr_2(devattr)->index; + unsigned long val; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; mutex_lock(&data->update_lock); - if (nr & 1) - result = data->pwm_auto_point_hyst[nr / 2] >> 4; + data->fan_beep = f71882fg_read8(data, F71882FG_REG_FAN_BEEP); + if (val) + data->fan_beep |= 1 << nr; else - result = data->pwm_auto_point_hyst[nr / 2] & 0x0f; - result = 1000 * (data->pwm_auto_point_temp[nr][point] - result); + data->fan_beep &= ~(1 << nr); + + f71882fg_write8(data, F71882FG_REG_FAN_BEEP, data->fan_beep); mutex_unlock(&data->update_lock); + return count; +} + +/* Attr for models which can beep on Fan alarm */ +static struct sensor_device_attribute_2 fxxxx_fan_beep_attr[] = { + SENSOR_ATTR_2(fan1_beep, S_IRUGO|S_IWUSR, show_fan_beep, + store_fan_beep, 0, 0), + SENSOR_ATTR_2(fan2_beep, S_IRUGO|S_IWUSR, show_fan_beep, + store_fan_beep, 0, 1), + SENSOR_ATTR_2(fan3_beep, S_IRUGO|S_IWUSR, show_fan_beep, + store_fan_beep, 0, 2), + SENSOR_ATTR_2(fan4_beep, S_IRUGO|S_IWUSR, show_fan_beep, + store_fan_beep, 0, 3), +}; + +static ssize_t show_pwm_auto_point_channel(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + int result; + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + + result = 1 << ((data->pwm_auto_point_mapping[nr] & 3) - + data->temp_start); + return sprintf(buf, "%d\n", result); } -static ssize_t store_pwm_auto_point_temp_hyst(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) +static ssize_t store_pwm_auto_point_channel(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) { struct f71882fg_data *data = dev_get_drvdata(dev); int err, nr = to_sensor_dev_attr_2(devattr)->index; - int point = to_sensor_dev_attr_2(devattr)->nr; - u8 reg; long val; err = kstrtol(buf, 10, &val); if (err) return err; - val /= 1000; - + switch (val) { + case 1: + val = 0; + break; + case 2: + val = 1; + break; + case 4: + val = 2; + break; + default: + return -EINVAL; + } + val += data->temp_start; mutex_lock(&data->update_lock); - data->pwm_auto_point_temp[nr][point] = - f71882fg_read8(data, F71882FG_REG_POINT_TEMP(nr, point)); - val = clamp_val(val, data->pwm_auto_point_temp[nr][point] - 15, - data->pwm_auto_point_temp[nr][point]); - val = data->pwm_auto_point_temp[nr][point] - val; - - reg = f71882fg_read8(data, F71882FG_REG_FAN_HYST(nr / 2)); - if (nr & 1) - reg = (reg & 0x0f) | (val << 4); - else - reg = (reg & 0xf0) | val; - - f71882fg_write8(data, F71882FG_REG_FAN_HYST(nr / 2), reg); - data->pwm_auto_point_hyst[nr / 2] = reg; + data->pwm_auto_point_mapping[nr] = + f71882fg_read8(data, F71882FG_REG_POINT_MAPPING(nr)); + val = (data->pwm_auto_point_mapping[nr] & 0xfc) | val; + f71882fg_write8(data, F71882FG_REG_POINT_MAPPING(nr), val); + data->pwm_auto_point_mapping[nr] = val; mutex_unlock(&data->update_lock); return count; } -static ssize_t show_pwm_interpolate(struct device *dev, - struct device_attribute *devattr, char *buf) +static ssize_t show_pwm_auto_point_pwm(struct device *dev, + struct device_attribute *devattr, + char *buf) { int result; struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; + int pwm = to_sensor_dev_attr_2(devattr)->index; + int point = to_sensor_dev_attr_2(devattr)->nr; + + mutex_lock(&data->update_lock); + if (data->pwm_enable & (1 << (2 * pwm))) { + /* PWM mode */ + result = data->pwm_auto_point_pwm[pwm][point]; + } else { + /* RPM mode */ + result = 32 * 255 / (32 + data->pwm_auto_point_pwm[pwm][point]); + } + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%d\n", result); +} + +static ssize_t store_pwm_auto_point_pwm(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + int err, pwm = to_sensor_dev_attr_2(devattr)->index; + int point = to_sensor_dev_attr_2(devattr)->nr; + long val; + + err = kstrtol(buf, 10, &val); + if (err) + return err; + + val = clamp_val(val, 0, 255); + + mutex_lock(&data->update_lock); + data->pwm_enable = f71882fg_read8(data, F71882FG_REG_PWM_ENABLE); + if (data->pwm_enable & (1 << (2 * pwm))) { + /* PWM mode */ + } else { + /* RPM mode */ + if (val < 29) /* Prevent negative numbers */ + val = 255; + else + val = (255 - val) * 32 / val; + } + f71882fg_write8(data, F71882FG_REG_POINT_PWM(pwm, point), val); + data->pwm_auto_point_pwm[pwm][point] = val; + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_pwm_auto_point_temp(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + int result; + struct f71882fg_data *data = f71882fg_update_device(dev); + int pwm = to_sensor_dev_attr_2(devattr)->index; + int point = to_sensor_dev_attr_2(devattr)->nr; + + result = data->pwm_auto_point_temp[pwm][point]; + return sprintf(buf, "%d\n", 1000 * result); +} + +static ssize_t store_pwm_auto_point_temp(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + int err, pwm = to_sensor_dev_attr_2(devattr)->index; + int point = to_sensor_dev_attr_2(devattr)->nr; + long val; + + err = kstrtol(buf, 10, &val); + if (err) + return err; + + val /= 1000; + + if (data->auto_point_temp_signed) + val = clamp_val(val, -128, 127); + else + val = clamp_val(val, 0, 127); + + mutex_lock(&data->update_lock); + f71882fg_write8(data, F71882FG_REG_POINT_TEMP(pwm, point), val); + data->pwm_auto_point_temp[pwm][point] = val; + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_pwm_auto_point_temp_hyst(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + int result = 0; + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + int point = to_sensor_dev_attr_2(devattr)->nr; + + mutex_lock(&data->update_lock); + if (nr & 1) + result = data->pwm_auto_point_hyst[nr / 2] >> 4; + else + result = data->pwm_auto_point_hyst[nr / 2] & 0x0f; + result = 1000 * (data->pwm_auto_point_temp[nr][point] - result); + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%d\n", result); +} + +static ssize_t store_pwm_auto_point_temp_hyst(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + int err, nr = to_sensor_dev_attr_2(devattr)->index; + int point = to_sensor_dev_attr_2(devattr)->nr; + u8 reg; + long val; + + err = kstrtol(buf, 10, &val); + if (err) + return err; + + val /= 1000; + + mutex_lock(&data->update_lock); + data->pwm_auto_point_temp[nr][point] = + f71882fg_read8(data, F71882FG_REG_POINT_TEMP(nr, point)); + val = clamp_val(val, data->pwm_auto_point_temp[nr][point] - 15, + data->pwm_auto_point_temp[nr][point]); + val = data->pwm_auto_point_temp[nr][point] - val; + + reg = f71882fg_read8(data, F71882FG_REG_FAN_HYST(nr / 2)); + if (nr & 1) + reg = (reg & 0x0f) | (val << 4); + else + reg = (reg & 0xf0) | val; + + f71882fg_write8(data, F71882FG_REG_FAN_HYST(nr / 2), reg); + data->pwm_auto_point_hyst[nr / 2] = reg; + mutex_unlock(&data->update_lock); + + return count; +} + +/* + * PWM attr for the f71862fg, fewer pwms and fewer zones per pwm than the + * standard models + */ +static struct sensor_device_attribute_2 f71862fg_auto_pwm_attr[3][7] = { { + SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 0), + SENSOR_ATTR_2(pwm1_auto_point1_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 1, 0), + SENSOR_ATTR_2(pwm1_auto_point2_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 4, 0), + SENSOR_ATTR_2(pwm1_auto_point1_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 0, 0), + SENSOR_ATTR_2(pwm1_auto_point2_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 3, 0), + SENSOR_ATTR_2(pwm1_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp_hyst, + store_pwm_auto_point_temp_hyst, + 0, 0), + SENSOR_ATTR_2(pwm1_auto_point2_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 3, 0), +}, { + SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 1), + SENSOR_ATTR_2(pwm2_auto_point1_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 1, 1), + SENSOR_ATTR_2(pwm2_auto_point2_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 4, 1), + SENSOR_ATTR_2(pwm2_auto_point1_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 0, 1), + SENSOR_ATTR_2(pwm2_auto_point2_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 3, 1), + SENSOR_ATTR_2(pwm2_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp_hyst, + store_pwm_auto_point_temp_hyst, + 0, 1), + SENSOR_ATTR_2(pwm2_auto_point2_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 3, 1), +}, { + SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 2), + SENSOR_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 1, 2), + SENSOR_ATTR_2(pwm3_auto_point2_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 4, 2), + SENSOR_ATTR_2(pwm3_auto_point1_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 0, 2), + SENSOR_ATTR_2(pwm3_auto_point2_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 3, 2), + SENSOR_ATTR_2(pwm3_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp_hyst, + store_pwm_auto_point_temp_hyst, + 0, 2), + SENSOR_ATTR_2(pwm3_auto_point2_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 3, 2), +} }; + +/* + * PWM attr for the f71808e/f71869, almost identical to the f71862fg, but the + * pwm setting when the temperature is above the pwmX_auto_point1_temp can be + * programmed instead of being hardcoded to 0xff + */ +static struct sensor_device_attribute_2 f71869_auto_pwm_attr[3][8] = { { + SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 0), + SENSOR_ATTR_2(pwm1_auto_point1_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 0, 0), + SENSOR_ATTR_2(pwm1_auto_point2_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 1, 0), + SENSOR_ATTR_2(pwm1_auto_point3_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 4, 0), + SENSOR_ATTR_2(pwm1_auto_point1_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 0, 0), + SENSOR_ATTR_2(pwm1_auto_point2_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 3, 0), + SENSOR_ATTR_2(pwm1_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp_hyst, + store_pwm_auto_point_temp_hyst, + 0, 0), + SENSOR_ATTR_2(pwm1_auto_point2_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 3, 0), +}, { + SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 1), + SENSOR_ATTR_2(pwm2_auto_point1_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 0, 1), + SENSOR_ATTR_2(pwm2_auto_point2_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 1, 1), + SENSOR_ATTR_2(pwm2_auto_point3_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 4, 1), + SENSOR_ATTR_2(pwm2_auto_point1_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 0, 1), + SENSOR_ATTR_2(pwm2_auto_point2_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 3, 1), + SENSOR_ATTR_2(pwm2_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp_hyst, + store_pwm_auto_point_temp_hyst, + 0, 1), + SENSOR_ATTR_2(pwm2_auto_point2_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 3, 1), +}, { + SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 2), + SENSOR_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 0, 2), + SENSOR_ATTR_2(pwm3_auto_point2_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 1, 2), + SENSOR_ATTR_2(pwm3_auto_point3_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 4, 2), + SENSOR_ATTR_2(pwm3_auto_point1_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 0, 2), + SENSOR_ATTR_2(pwm3_auto_point2_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 3, 2), + SENSOR_ATTR_2(pwm3_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp_hyst, + store_pwm_auto_point_temp_hyst, + 0, 2), + SENSOR_ATTR_2(pwm3_auto_point2_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 3, 2), +} }; + +/* PWM attr for the standard models */ +static struct sensor_device_attribute_2 fxxxx_auto_pwm_attr[4][14] = { { + SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 0), + SENSOR_ATTR_2(pwm1_auto_point1_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 0, 0), + SENSOR_ATTR_2(pwm1_auto_point2_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 1, 0), + SENSOR_ATTR_2(pwm1_auto_point3_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 2, 0), + SENSOR_ATTR_2(pwm1_auto_point4_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 3, 0), + SENSOR_ATTR_2(pwm1_auto_point5_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 4, 0), + SENSOR_ATTR_2(pwm1_auto_point1_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 0, 0), + SENSOR_ATTR_2(pwm1_auto_point2_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 1, 0), + SENSOR_ATTR_2(pwm1_auto_point3_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 2, 0), + SENSOR_ATTR_2(pwm1_auto_point4_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 3, 0), + SENSOR_ATTR_2(pwm1_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp_hyst, + store_pwm_auto_point_temp_hyst, + 0, 0), + SENSOR_ATTR_2(pwm1_auto_point2_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 1, 0), + SENSOR_ATTR_2(pwm1_auto_point3_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 2, 0), + SENSOR_ATTR_2(pwm1_auto_point4_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 3, 0), +}, { + SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 1), + SENSOR_ATTR_2(pwm2_auto_point1_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 0, 1), + SENSOR_ATTR_2(pwm2_auto_point2_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 1, 1), + SENSOR_ATTR_2(pwm2_auto_point3_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 2, 1), + SENSOR_ATTR_2(pwm2_auto_point4_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 3, 1), + SENSOR_ATTR_2(pwm2_auto_point5_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 4, 1), + SENSOR_ATTR_2(pwm2_auto_point1_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 0, 1), + SENSOR_ATTR_2(pwm2_auto_point2_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 1, 1), + SENSOR_ATTR_2(pwm2_auto_point3_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 2, 1), + SENSOR_ATTR_2(pwm2_auto_point4_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 3, 1), + SENSOR_ATTR_2(pwm2_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp_hyst, + store_pwm_auto_point_temp_hyst, + 0, 1), + SENSOR_ATTR_2(pwm2_auto_point2_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 1, 1), + SENSOR_ATTR_2(pwm2_auto_point3_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 2, 1), + SENSOR_ATTR_2(pwm2_auto_point4_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 3, 1), +}, { + SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 2), + SENSOR_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 0, 2), + SENSOR_ATTR_2(pwm3_auto_point2_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 1, 2), + SENSOR_ATTR_2(pwm3_auto_point3_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 2, 2), + SENSOR_ATTR_2(pwm3_auto_point4_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 3, 2), + SENSOR_ATTR_2(pwm3_auto_point5_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 4, 2), + SENSOR_ATTR_2(pwm3_auto_point1_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 0, 2), + SENSOR_ATTR_2(pwm3_auto_point2_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 1, 2), + SENSOR_ATTR_2(pwm3_auto_point3_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 2, 2), + SENSOR_ATTR_2(pwm3_auto_point4_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 3, 2), + SENSOR_ATTR_2(pwm3_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp_hyst, + store_pwm_auto_point_temp_hyst, + 0, 2), + SENSOR_ATTR_2(pwm3_auto_point2_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 1, 2), + SENSOR_ATTR_2(pwm3_auto_point3_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 2, 2), + SENSOR_ATTR_2(pwm3_auto_point4_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 3, 2), +}, { + SENSOR_ATTR_2(pwm4_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 3), + SENSOR_ATTR_2(pwm4_auto_point1_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 0, 3), + SENSOR_ATTR_2(pwm4_auto_point2_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 1, 3), + SENSOR_ATTR_2(pwm4_auto_point3_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 2, 3), + SENSOR_ATTR_2(pwm4_auto_point4_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 3, 3), + SENSOR_ATTR_2(pwm4_auto_point5_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 4, 3), + SENSOR_ATTR_2(pwm4_auto_point1_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 0, 3), + SENSOR_ATTR_2(pwm4_auto_point2_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 1, 3), + SENSOR_ATTR_2(pwm4_auto_point3_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 2, 3), + SENSOR_ATTR_2(pwm4_auto_point4_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 3, 3), + SENSOR_ATTR_2(pwm4_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp_hyst, + store_pwm_auto_point_temp_hyst, + 0, 3), + SENSOR_ATTR_2(pwm4_auto_point2_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 1, 3), + SENSOR_ATTR_2(pwm4_auto_point3_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 2, 3), + SENSOR_ATTR_2(pwm4_auto_point4_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 3, 3), +} }; - result = (data->pwm_auto_point_mapping[nr] >> 4) & 1; +/* Fan attr specific to the f8000 (4th fan input can only measure speed) */ +static struct sensor_device_attribute_2 f8000_fan_attr[] = { + SENSOR_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 0, 3), +}; - return sprintf(buf, "%d\n", result); -} +/* + * PWM attr for the f8000, zones mapped to temp instead of to pwm! + * Also the register block at offset A0 maps to TEMP1 (so our temp2, as the + * F8000 starts counting temps at 0), B0 maps the TEMP2 and C0 maps to TEMP0 + */ +static struct sensor_device_attribute_2 f8000_auto_pwm_attr[3][14] = { { + SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 0), + SENSOR_ATTR_2(temp1_auto_point1_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 0, 2), + SENSOR_ATTR_2(temp1_auto_point2_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 1, 2), + SENSOR_ATTR_2(temp1_auto_point3_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 2, 2), + SENSOR_ATTR_2(temp1_auto_point4_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 3, 2), + SENSOR_ATTR_2(temp1_auto_point5_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 4, 2), + SENSOR_ATTR_2(temp1_auto_point1_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 0, 2), + SENSOR_ATTR_2(temp1_auto_point2_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 1, 2), + SENSOR_ATTR_2(temp1_auto_point3_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 2, 2), + SENSOR_ATTR_2(temp1_auto_point4_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 3, 2), + SENSOR_ATTR_2(temp1_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp_hyst, + store_pwm_auto_point_temp_hyst, + 0, 2), + SENSOR_ATTR_2(temp1_auto_point2_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 1, 2), + SENSOR_ATTR_2(temp1_auto_point3_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 2, 2), + SENSOR_ATTR_2(temp1_auto_point4_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 3, 2), +}, { + SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 1), + SENSOR_ATTR_2(temp2_auto_point1_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 0, 0), + SENSOR_ATTR_2(temp2_auto_point2_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 1, 0), + SENSOR_ATTR_2(temp2_auto_point3_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 2, 0), + SENSOR_ATTR_2(temp2_auto_point4_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 3, 0), + SENSOR_ATTR_2(temp2_auto_point5_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 4, 0), + SENSOR_ATTR_2(temp2_auto_point1_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 0, 0), + SENSOR_ATTR_2(temp2_auto_point2_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 1, 0), + SENSOR_ATTR_2(temp2_auto_point3_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 2, 0), + SENSOR_ATTR_2(temp2_auto_point4_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 3, 0), + SENSOR_ATTR_2(temp2_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp_hyst, + store_pwm_auto_point_temp_hyst, + 0, 0), + SENSOR_ATTR_2(temp2_auto_point2_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 1, 0), + SENSOR_ATTR_2(temp2_auto_point3_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 2, 0), + SENSOR_ATTR_2(temp2_auto_point4_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 3, 0), +}, { + SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 2), + SENSOR_ATTR_2(temp3_auto_point1_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 0, 1), + SENSOR_ATTR_2(temp3_auto_point2_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 1, 1), + SENSOR_ATTR_2(temp3_auto_point3_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 2, 1), + SENSOR_ATTR_2(temp3_auto_point4_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 3, 1), + SENSOR_ATTR_2(temp3_auto_point5_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 4, 1), + SENSOR_ATTR_2(temp3_auto_point1_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 0, 1), + SENSOR_ATTR_2(temp3_auto_point2_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 1, 1), + SENSOR_ATTR_2(temp3_auto_point3_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 2, 1), + SENSOR_ATTR_2(temp3_auto_point4_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 3, 1), + SENSOR_ATTR_2(temp3_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp_hyst, + store_pwm_auto_point_temp_hyst, + 0, 1), + SENSOR_ATTR_2(temp3_auto_point2_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 1, 1), + SENSOR_ATTR_2(temp3_auto_point3_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 2, 1), + SENSOR_ATTR_2(temp3_auto_point4_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 3, 1), +} }; -static ssize_t store_pwm_interpolate(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) +/* Super I/O functions */ +static inline int superio_inb(int base, int reg) { - struct f71882fg_data *data = dev_get_drvdata(dev); - int err, nr = to_sensor_dev_attr_2(devattr)->index; - unsigned long val; - - err = kstrtoul(buf, 10, &val); - if (err) - return err; - - mutex_lock(&data->update_lock); - data->pwm_auto_point_mapping[nr] = - f71882fg_read8(data, F71882FG_REG_POINT_MAPPING(nr)); - if (val) - val = data->pwm_auto_point_mapping[nr] | (1 << 4); - else - val = data->pwm_auto_point_mapping[nr] & (~(1 << 4)); - f71882fg_write8(data, F71882FG_REG_POINT_MAPPING(nr), val); - data->pwm_auto_point_mapping[nr] = val; - mutex_unlock(&data->update_lock); - - return count; + outb(reg, base); + return inb(base + 1); } -static ssize_t show_pwm_auto_point_channel(struct device *dev, - struct device_attribute *devattr, - char *buf) +static int superio_inw(int base, int reg) { - int result; - struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr_2(devattr)->index; - - result = 1 << ((data->pwm_auto_point_mapping[nr] & 3) - - data->temp_start); - - return sprintf(buf, "%d\n", result); + int val; + val = superio_inb(base, reg) << 8; + val |= superio_inb(base, reg + 1); + return val; } -static ssize_t store_pwm_auto_point_channel(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) +static inline int superio_enter(int base) { - struct f71882fg_data *data = dev_get_drvdata(dev); - int err, nr = to_sensor_dev_attr_2(devattr)->index; - long val; - - err = kstrtol(buf, 10, &val); - if (err) - return err; - - switch (val) { - case 1: - val = 0; - break; - case 2: - val = 1; - break; - case 4: - val = 2; - break; - default: - return -EINVAL; + /* Don't step on other drivers' I/O space by accident */ + if (!request_muxed_region(base, 2, DRVNAME)) { + pr_err("I/O address 0x%04x already in use\n", base); + return -EBUSY; } - val += data->temp_start; - mutex_lock(&data->update_lock); - data->pwm_auto_point_mapping[nr] = - f71882fg_read8(data, F71882FG_REG_POINT_MAPPING(nr)); - val = (data->pwm_auto_point_mapping[nr] & 0xfc) | val; - f71882fg_write8(data, F71882FG_REG_POINT_MAPPING(nr), val); - data->pwm_auto_point_mapping[nr] = val; - mutex_unlock(&data->update_lock); - - return count; -} - -static ssize_t show_pwm_auto_point_temp(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - int result; - struct f71882fg_data *data = f71882fg_update_device(dev); - int pwm = to_sensor_dev_attr_2(devattr)->index; - int point = to_sensor_dev_attr_2(devattr)->nr; - - result = data->pwm_auto_point_temp[pwm][point]; - return sprintf(buf, "%d\n", 1000 * result); -} - -static ssize_t store_pwm_auto_point_temp(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) -{ - struct f71882fg_data *data = dev_get_drvdata(dev); - int err, pwm = to_sensor_dev_attr_2(devattr)->index; - int point = to_sensor_dev_attr_2(devattr)->nr; - long val; - - err = kstrtol(buf, 10, &val); - if (err) - return err; - - val /= 1000; - - if (data->auto_point_temp_signed) - val = clamp_val(val, -128, 127); - else - val = clamp_val(val, 0, 127); - mutex_lock(&data->update_lock); - f71882fg_write8(data, F71882FG_REG_POINT_TEMP(pwm, point), val); - data->pwm_auto_point_temp[pwm][point] = val; - mutex_unlock(&data->update_lock); + /* according to the datasheet the key must be send twice! */ + outb(SIO_UNLOCK_KEY, base); + outb(SIO_UNLOCK_KEY, base); - return count; + return 0; } -static ssize_t name_show(struct device *dev, struct device_attribute *devattr, - char *buf) +static inline void superio_select(int base, int ld) { - struct f71882fg_data *data = dev_get_drvdata(dev); - return sprintf(buf, "%s\n", f71882fg_names[data->type]); + outb(SIO_REG_LDSEL, base); + outb(ld, base + 1); +} + +static inline void superio_exit(int base) +{ + outb(SIO_LOCK_KEY, base); + release_region(base, 2); } static int f71882fg_create_sysfs_files(struct platform_device *pdev, @@ -2329,6 +2220,119 @@ static int f71882fg_create_fan_sysfs_files( return err; } +static int f71882fg_remove(struct platform_device *pdev) +{ + struct f71882fg_data *data = platform_get_drvdata(pdev); + int nr_fans = f71882fg_nr_fans[data->type]; + int nr_temps = f71882fg_nr_temps[data->type]; + int i; + u8 start_reg = f71882fg_read8(data, F71882FG_REG_START); + + if (data->hwmon_dev) + hwmon_device_unregister(data->hwmon_dev); + + device_remove_file(&pdev->dev, &dev_attr_name); + + if (start_reg & 0x01) { + switch (data->type) { + case f71858fg: + if (data->temp_config & 0x10) + f71882fg_remove_sysfs_files(pdev, + f8000_temp_attr, + ARRAY_SIZE(f8000_temp_attr)); + else + f71882fg_remove_sysfs_files(pdev, + f71858fg_temp_attr, + ARRAY_SIZE(f71858fg_temp_attr)); + break; + case f8000: + f71882fg_remove_sysfs_files(pdev, + f8000_temp_attr, + ARRAY_SIZE(f8000_temp_attr)); + break; + case f81866a: + f71882fg_remove_sysfs_files(pdev, + f71858fg_temp_attr, + ARRAY_SIZE(f71858fg_temp_attr)); + break; + default: + f71882fg_remove_sysfs_files(pdev, + &fxxxx_temp_attr[0][0], + ARRAY_SIZE(fxxxx_temp_attr[0]) * nr_temps); + } + if (f71882fg_temp_has_beep[data->type]) { + if (data->type == f81866a) + f71882fg_remove_sysfs_files(pdev, + &f81866_temp_beep_attr[0][0], + ARRAY_SIZE(f81866_temp_beep_attr[0]) + * nr_temps); + else + f71882fg_remove_sysfs_files(pdev, + &fxxxx_temp_beep_attr[0][0], + ARRAY_SIZE(fxxxx_temp_beep_attr[0]) + * nr_temps); + } + + for (i = 0; i < F71882FG_MAX_INS; i++) { + if (f71882fg_has_in[data->type][i]) { + device_remove_file(&pdev->dev, + &fxxxx_in_attr[i].dev_attr); + } + } + if (f71882fg_has_in1_alarm[data->type]) { + f71882fg_remove_sysfs_files(pdev, + fxxxx_in1_alarm_attr, + ARRAY_SIZE(fxxxx_in1_alarm_attr)); + } + } + + if (start_reg & 0x02) { + f71882fg_remove_sysfs_files(pdev, &fxxxx_fan_attr[0][0], + ARRAY_SIZE(fxxxx_fan_attr[0]) * nr_fans); + + if (f71882fg_fan_has_beep[data->type]) { + f71882fg_remove_sysfs_files(pdev, + fxxxx_fan_beep_attr, nr_fans); + } + + switch (data->type) { + case f71808a: + f71882fg_remove_sysfs_files(pdev, + &fxxxx_auto_pwm_attr[0][0], + ARRAY_SIZE(fxxxx_auto_pwm_attr[0]) * nr_fans); + f71882fg_remove_sysfs_files(pdev, + f71808a_fan3_attr, + ARRAY_SIZE(f71808a_fan3_attr)); + break; + case f71862fg: + f71882fg_remove_sysfs_files(pdev, + &f71862fg_auto_pwm_attr[0][0], + ARRAY_SIZE(f71862fg_auto_pwm_attr[0]) * + nr_fans); + break; + case f71808e: + case f71869: + f71882fg_remove_sysfs_files(pdev, + &f71869_auto_pwm_attr[0][0], + ARRAY_SIZE(f71869_auto_pwm_attr[0]) * nr_fans); + break; + case f8000: + f71882fg_remove_sysfs_files(pdev, + f8000_fan_attr, + ARRAY_SIZE(f8000_fan_attr)); + f71882fg_remove_sysfs_files(pdev, + &f8000_auto_pwm_attr[0][0], + ARRAY_SIZE(f8000_auto_pwm_attr[0]) * nr_fans); + break; + default: + f71882fg_remove_sysfs_files(pdev, + &fxxxx_auto_pwm_attr[0][0], + ARRAY_SIZE(fxxxx_auto_pwm_attr[0]) * nr_fans); + } + } + return 0; +} + static int f71882fg_probe(struct platform_device *pdev) { struct f71882fg_data *data; @@ -2502,119 +2506,6 @@ exit_unregister_sysfs: return err; /* f71882fg_remove() also frees our data */ } -static int f71882fg_remove(struct platform_device *pdev) -{ - struct f71882fg_data *data = platform_get_drvdata(pdev); - int nr_fans = f71882fg_nr_fans[data->type]; - int nr_temps = f71882fg_nr_temps[data->type]; - int i; - u8 start_reg = f71882fg_read8(data, F71882FG_REG_START); - - if (data->hwmon_dev) - hwmon_device_unregister(data->hwmon_dev); - - device_remove_file(&pdev->dev, &dev_attr_name); - - if (start_reg & 0x01) { - switch (data->type) { - case f71858fg: - if (data->temp_config & 0x10) - f71882fg_remove_sysfs_files(pdev, - f8000_temp_attr, - ARRAY_SIZE(f8000_temp_attr)); - else - f71882fg_remove_sysfs_files(pdev, - f71858fg_temp_attr, - ARRAY_SIZE(f71858fg_temp_attr)); - break; - case f8000: - f71882fg_remove_sysfs_files(pdev, - f8000_temp_attr, - ARRAY_SIZE(f8000_temp_attr)); - break; - case f81866a: - f71882fg_remove_sysfs_files(pdev, - f71858fg_temp_attr, - ARRAY_SIZE(f71858fg_temp_attr)); - break; - default: - f71882fg_remove_sysfs_files(pdev, - &fxxxx_temp_attr[0][0], - ARRAY_SIZE(fxxxx_temp_attr[0]) * nr_temps); - } - if (f71882fg_temp_has_beep[data->type]) { - if (data->type == f81866a) - f71882fg_remove_sysfs_files(pdev, - &f81866_temp_beep_attr[0][0], - ARRAY_SIZE(f81866_temp_beep_attr[0]) - * nr_temps); - else - f71882fg_remove_sysfs_files(pdev, - &fxxxx_temp_beep_attr[0][0], - ARRAY_SIZE(fxxxx_temp_beep_attr[0]) - * nr_temps); - } - - for (i = 0; i < F71882FG_MAX_INS; i++) { - if (f71882fg_has_in[data->type][i]) { - device_remove_file(&pdev->dev, - &fxxxx_in_attr[i].dev_attr); - } - } - if (f71882fg_has_in1_alarm[data->type]) { - f71882fg_remove_sysfs_files(pdev, - fxxxx_in1_alarm_attr, - ARRAY_SIZE(fxxxx_in1_alarm_attr)); - } - } - - if (start_reg & 0x02) { - f71882fg_remove_sysfs_files(pdev, &fxxxx_fan_attr[0][0], - ARRAY_SIZE(fxxxx_fan_attr[0]) * nr_fans); - - if (f71882fg_fan_has_beep[data->type]) { - f71882fg_remove_sysfs_files(pdev, - fxxxx_fan_beep_attr, nr_fans); - } - - switch (data->type) { - case f71808a: - f71882fg_remove_sysfs_files(pdev, - &fxxxx_auto_pwm_attr[0][0], - ARRAY_SIZE(fxxxx_auto_pwm_attr[0]) * nr_fans); - f71882fg_remove_sysfs_files(pdev, - f71808a_fan3_attr, - ARRAY_SIZE(f71808a_fan3_attr)); - break; - case f71862fg: - f71882fg_remove_sysfs_files(pdev, - &f71862fg_auto_pwm_attr[0][0], - ARRAY_SIZE(f71862fg_auto_pwm_attr[0]) * - nr_fans); - break; - case f71808e: - case f71869: - f71882fg_remove_sysfs_files(pdev, - &f71869_auto_pwm_attr[0][0], - ARRAY_SIZE(f71869_auto_pwm_attr[0]) * nr_fans); - break; - case f8000: - f71882fg_remove_sysfs_files(pdev, - f8000_fan_attr, - ARRAY_SIZE(f8000_fan_attr)); - f71882fg_remove_sysfs_files(pdev, - &f8000_auto_pwm_attr[0][0], - ARRAY_SIZE(f8000_auto_pwm_attr[0]) * nr_fans); - break; - default: - f71882fg_remove_sysfs_files(pdev, - &fxxxx_auto_pwm_attr[0][0], - ARRAY_SIZE(fxxxx_auto_pwm_attr[0]) * nr_fans); - } - } - return 0; -} - static int __init f71882fg_find(int sioaddr, struct f71882fg_sio_data *sio_data) { u16 devid; @@ -2760,6 +2651,14 @@ exit_device_put: return err; } +static struct platform_driver f71882fg_driver = { + .driver = { + .name = DRVNAME, + }, + .probe = f71882fg_probe, + .remove = f71882fg_remove, +}; + static int __init f71882fg_init(void) { int err; -- cgit v1.2.3 From 38e776290efa5e4c32c2a71d23da7d9907086e93 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 25 Sep 2022 18:27:42 +0100 Subject: hwmon: (abitguru) Switch to DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr() These newer PM macros allow the compiler to see what code it can remove if !CONFIG_PM_SLEEP. This allows the removal of messy #ifdef barriers. Signed-off-by: Jonathan Cameron Cc: Hans de Goede Link: https://lore.kernel.org/r/20220925172759.3573439-2-jic23@kernel.org Signed-off-by: Guenter Roeck --- drivers/hwmon/abituguru.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/abituguru.c b/drivers/hwmon/abituguru.c index 681f0623868f..a7cae6568155 100644 --- a/drivers/hwmon/abituguru.c +++ b/drivers/hwmon/abituguru.c @@ -1504,7 +1504,6 @@ LEAVE_UPDATE: return NULL; } -#ifdef CONFIG_PM_SLEEP static int abituguru_suspend(struct device *dev) { struct abituguru_data *data = dev_get_drvdata(dev); @@ -1526,16 +1525,12 @@ static int abituguru_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(abituguru_pm, abituguru_suspend, abituguru_resume); -#define ABIT_UGURU_PM (&abituguru_pm) -#else -#define ABIT_UGURU_PM NULL -#endif /* CONFIG_PM */ +static DEFINE_SIMPLE_DEV_PM_OPS(abituguru_pm, abituguru_suspend, abituguru_resume); static struct platform_driver abituguru_driver = { .driver = { .name = ABIT_UGURU_NAME, - .pm = ABIT_UGURU_PM, + .pm = pm_sleep_ptr(&abituguru_pm), }, .probe = abituguru_probe, .remove = abituguru_remove, -- cgit v1.2.3 From e7045a14fa1459f4fd56c3b6944382a00586cc89 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 25 Sep 2022 18:27:43 +0100 Subject: hwmon: (abitguru3) Switch to DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr() These newer PM macros allow the compiler to see what code it can remove if !CONFIG_PM_SLEEP. This allows the removal of messy #ifdef barriers whilst achieving the same result. Signed-off-by: Jonathan Cameron Cc: Hans de Goede Link: https://lore.kernel.org/r/20220925172759.3573439-3-jic23@kernel.org Signed-off-by: Guenter Roeck --- drivers/hwmon/abituguru3.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/abituguru3.c b/drivers/hwmon/abituguru3.c index 8229ad30c909..afb21f73032d 100644 --- a/drivers/hwmon/abituguru3.c +++ b/drivers/hwmon/abituguru3.c @@ -1127,7 +1127,6 @@ LEAVE_UPDATE: return NULL; } -#ifdef CONFIG_PM_SLEEP static int abituguru3_suspend(struct device *dev) { struct abituguru3_data *data = dev_get_drvdata(dev); @@ -1146,16 +1145,12 @@ static int abituguru3_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(abituguru3_pm, abituguru3_suspend, abituguru3_resume); -#define ABIT_UGURU3_PM (&abituguru3_pm) -#else -#define ABIT_UGURU3_PM NULL -#endif /* CONFIG_PM */ +static DEFINE_SIMPLE_DEV_PM_OPS(abituguru3_pm, abituguru3_suspend, abituguru3_resume); static struct platform_driver abituguru3_driver = { .driver = { .name = ABIT_UGURU3_NAME, - .pm = ABIT_UGURU3_PM + .pm = pm_sleep_ptr(&abituguru3_pm), }, .probe = abituguru3_probe, .remove = abituguru3_remove, -- cgit v1.2.3 From 00f4095c967f00c5ad450569f66f0bb82cc97178 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 25 Sep 2022 18:27:44 +0100 Subject: hwmon: (acpi_power_meter) Switch to DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr() These newer PM macros allow the compiler to see what code it can remove if !CONFIG_PM_SLEEP. This allows the removal of messy #ifdef barriers whilst achieving the same result. Signed-off-by: Jonathan Cameron Cc: Rafael J. Wysocki Link: https://lore.kernel.org/r/20220925172759.3573439-4-jic23@kernel.org Signed-off-by: Guenter Roeck --- drivers/hwmon/acpi_power_meter.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c index d2545a1be9fc..6d02947409d5 100644 --- a/drivers/hwmon/acpi_power_meter.c +++ b/drivers/hwmon/acpi_power_meter.c @@ -927,8 +927,6 @@ static int acpi_power_meter_remove(struct acpi_device *device) return 0; } -#ifdef CONFIG_PM_SLEEP - static int acpi_power_meter_resume(struct device *dev) { struct acpi_power_meter_resource *resource; @@ -946,9 +944,8 @@ static int acpi_power_meter_resume(struct device *dev) return 0; } -#endif /* CONFIG_PM_SLEEP */ - -static SIMPLE_DEV_PM_OPS(acpi_power_meter_pm, NULL, acpi_power_meter_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(acpi_power_meter_pm, NULL, + acpi_power_meter_resume); static struct acpi_driver acpi_power_meter_driver = { .name = "power_meter", @@ -959,7 +956,7 @@ static struct acpi_driver acpi_power_meter_driver = { .remove = acpi_power_meter_remove, .notify = acpi_power_meter_notify, }, - .drv.pm = &acpi_power_meter_pm, + .drv.pm = pm_sleep_ptr(&acpi_power_meter_pm), }; /* Module init/exit routines */ -- cgit v1.2.3 From 29805956ee16b487dee57823d884df0bfe45ff69 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 25 Sep 2022 18:27:45 +0100 Subject: hwmon: (adt7x10) Switch to EXPORT_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr() These newer PM macros allow the compiler to see what code it can remove if !CONFIG_PM_SLEEP. This allows the removal of messy #ifdef barriers whilst achieving the same result. Signed-off-by: Jonathan Cameron Cc: Lars-Peter Clausen Link: https://lore.kernel.org/r/20220925172759.3573439-5-jic23@kernel.org Signed-off-by: Guenter Roeck --- drivers/hwmon/adt7310.c | 2 +- drivers/hwmon/adt7410.c | 2 +- drivers/hwmon/adt7x10.c | 7 +------ drivers/hwmon/adt7x10.h | 5 ----- 4 files changed, 3 insertions(+), 13 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/adt7310.c b/drivers/hwmon/adt7310.c index 1efc0bdcceab..067865f4887a 100644 --- a/drivers/hwmon/adt7310.c +++ b/drivers/hwmon/adt7310.c @@ -152,7 +152,7 @@ MODULE_DEVICE_TABLE(spi, adt7310_id); static struct spi_driver adt7310_driver = { .driver = { .name = "adt7310", - .pm = ADT7X10_DEV_PM_OPS, + .pm = pm_sleep_ptr(&adt7x10_dev_pm_ops), }, .probe = adt7310_spi_probe, .id_table = adt7310_id, diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c index aede5baca7b9..0cebf6777239 100644 --- a/drivers/hwmon/adt7410.c +++ b/drivers/hwmon/adt7410.c @@ -98,7 +98,7 @@ static struct i2c_driver adt7410_driver = { .class = I2C_CLASS_HWMON, .driver = { .name = "adt7410", - .pm = ADT7X10_DEV_PM_OPS, + .pm = pm_sleep_ptr(&adt7x10_dev_pm_ops), }, .probe_new = adt7410_i2c_probe, .id_table = adt7410_ids, diff --git a/drivers/hwmon/adt7x10.c b/drivers/hwmon/adt7x10.c index ce54bffab2ec..da67734edafd 100644 --- a/drivers/hwmon/adt7x10.c +++ b/drivers/hwmon/adt7x10.c @@ -397,8 +397,6 @@ int adt7x10_probe(struct device *dev, const char *name, int irq, } EXPORT_SYMBOL_GPL(adt7x10_probe); -#ifdef CONFIG_PM_SLEEP - static int adt7x10_suspend(struct device *dev) { struct adt7x10_data *data = dev_get_drvdata(dev); @@ -414,10 +412,7 @@ static int adt7x10_resume(struct device *dev) return regmap_write(data->regmap, ADT7X10_CONFIG, data->config); } -SIMPLE_DEV_PM_OPS(adt7x10_dev_pm_ops, adt7x10_suspend, adt7x10_resume); -EXPORT_SYMBOL_GPL(adt7x10_dev_pm_ops); - -#endif /* CONFIG_PM_SLEEP */ +EXPORT_SIMPLE_DEV_PM_OPS(adt7x10_dev_pm_ops, adt7x10_suspend, adt7x10_resume); MODULE_AUTHOR("Hartmut Knaack"); MODULE_DESCRIPTION("ADT7410/ADT7420, ADT7310/ADT7320 common code"); diff --git a/drivers/hwmon/adt7x10.h b/drivers/hwmon/adt7x10.h index ba22c32c8355..46caf3e21978 100644 --- a/drivers/hwmon/adt7x10.h +++ b/drivers/hwmon/adt7x10.h @@ -20,11 +20,6 @@ struct device; int adt7x10_probe(struct device *dev, const char *name, int irq, struct regmap *regmap); -#ifdef CONFIG_PM_SLEEP extern const struct dev_pm_ops adt7x10_dev_pm_ops; -#define ADT7X10_DEV_PM_OPS (&adt7x10_dev_pm_ops) -#else -#define ADT7X10_DEV_PM_OPS NULL -#endif #endif -- cgit v1.2.3 From 5e866400e9d5d68d52907cbd91082c1cad477355 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 25 Sep 2022 18:27:46 +0100 Subject: hwmon: (gpio-fan) Switch to DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr() These newer PM macros allow the compiler to see what code it can remove if !CONFIG_PM_SLEEP. This allows the removal of messy #ifdef barriers whilst achieving the same result. Signed-off-by: Jonathan Cameron Cc: Linus Walleij Link: https://lore.kernel.org/r/20220925172759.3573439-6-jic23@kernel.org [groeck: Drop #ifdef from struct gpio_fan_data] Signed-off-by: Guenter Roeck --- drivers/hwmon/gpio-fan.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c index fbf3f5a4ecb6..ba408942dbe7 100644 --- a/drivers/hwmon/gpio-fan.c +++ b/drivers/hwmon/gpio-fan.c @@ -37,9 +37,7 @@ struct gpio_fan_data { int num_speed; struct gpio_fan_speed *speed; int speed_index; -#ifdef CONFIG_PM_SLEEP int resume_speed; -#endif bool pwm_enable; struct gpio_desc *alarm_gpio; struct work_struct alarm_work; @@ -557,7 +555,6 @@ static void gpio_fan_shutdown(struct platform_device *pdev) set_fan_speed(fan_data, 0); } -#ifdef CONFIG_PM_SLEEP static int gpio_fan_suspend(struct device *dev) { struct gpio_fan_data *fan_data = dev_get_drvdata(dev); @@ -580,18 +577,14 @@ static int gpio_fan_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(gpio_fan_pm, gpio_fan_suspend, gpio_fan_resume); -#define GPIO_FAN_PM (&gpio_fan_pm) -#else -#define GPIO_FAN_PM NULL -#endif +static DEFINE_SIMPLE_DEV_PM_OPS(gpio_fan_pm, gpio_fan_suspend, gpio_fan_resume); static struct platform_driver gpio_fan_driver = { .probe = gpio_fan_probe, .shutdown = gpio_fan_shutdown, .driver = { .name = "gpio-fan", - .pm = GPIO_FAN_PM, + .pm = pm_sleep_ptr(&gpio_fan_pm), .of_match_table = of_match_ptr(of_gpio_fan_match), }, }; -- cgit v1.2.3 From 31b34d62081a31255e520b702cec2e44183b0acc Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 25 Sep 2022 18:27:47 +0100 Subject: hwmon: (it87) Switch to DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr() These newer PM macros allow the compiler to see what code it can remove if !CONFIG_PM_SLEEP. This allows the removal of __maybe_unused markings whilst achieving the same result. Signed-off-by: Jonathan Cameron Link: https://lore.kernel.org/r/20220925172759.3573439-7-jic23@kernel.org Signed-off-by: Guenter Roeck --- drivers/hwmon/it87.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 0e543dbe0a6b..7bd154ba351b 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -3179,7 +3179,7 @@ static int it87_probe(struct platform_device *pdev) return PTR_ERR_OR_ZERO(hwmon_dev); } -static void __maybe_unused it87_resume_sio(struct platform_device *pdev) +static void it87_resume_sio(struct platform_device *pdev) { struct it87_data *data = dev_get_drvdata(&pdev->dev); int err; @@ -3211,7 +3211,7 @@ static void __maybe_unused it87_resume_sio(struct platform_device *pdev) superio_exit(data->sioaddr); } -static int __maybe_unused it87_resume(struct device *dev) +static int it87_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct it87_data *data = dev_get_drvdata(dev); @@ -3238,12 +3238,12 @@ static int __maybe_unused it87_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(it87_dev_pm_ops, NULL, it87_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(it87_dev_pm_ops, NULL, it87_resume); static struct platform_driver it87_driver = { .driver = { .name = DRVNAME, - .pm = &it87_dev_pm_ops, + .pm = pm_sleep_ptr(&it87_dev_pm_ops), }, .probe = it87_probe, }; -- cgit v1.2.3 From d025007daaef3e468021c998e1de3f9bf3ba3476 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 25 Sep 2022 18:27:48 +0100 Subject: hwmon: (lm90) Switch to DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr() These newer PM macros allow the compiler to see what code it can remove if !CONFIG_PM_SLEEP. This allows the removal of __maybe_unused markings whilst achieving the same result. Signed-off-by: Jonathan Cameron Cc: Guenter Roeck Link: https://lore.kernel.org/r/20220925172759.3573439-8-jic23@kernel.org Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index c151c0bf43f2..db595f7d01f8 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -2956,7 +2956,7 @@ static void lm90_alert(struct i2c_client *client, enum i2c_alert_protocol type, } } -static int __maybe_unused lm90_suspend(struct device *dev) +static int lm90_suspend(struct device *dev) { struct lm90_data *data = dev_get_drvdata(dev); struct i2c_client *client = data->client; @@ -2967,7 +2967,7 @@ static int __maybe_unused lm90_suspend(struct device *dev) return 0; } -static int __maybe_unused lm90_resume(struct device *dev) +static int lm90_resume(struct device *dev) { struct lm90_data *data = dev_get_drvdata(dev); struct i2c_client *client = data->client; @@ -2978,14 +2978,14 @@ static int __maybe_unused lm90_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(lm90_pm_ops, lm90_suspend, lm90_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(lm90_pm_ops, lm90_suspend, lm90_resume); static struct i2c_driver lm90_driver = { .class = I2C_CLASS_HWMON, .driver = { .name = "lm90", .of_match_table = of_match_ptr(lm90_of_match), - .pm = &lm90_pm_ops, + .pm = pm_sleep_ptr(&lm90_pm_ops), }, .probe_new = lm90_probe, .alert = lm90_alert, -- cgit v1.2.3 From ca19f965429e8d79d688a4446fde3456f22a8c62 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 25 Sep 2022 18:27:49 +0100 Subject: hwmon: (ltc2947) Switch to EXPORT_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These newer PM macros allow the compiler to see what code it can remove if !CONFIG_PM_SLEEP. This allows the removal of __maybe_unused markings whilst achieving the same result. In this case it also lets the structure itself be removed. Signed-off-by: Jonathan Cameron Cc: Nuno Sá Link: https://lore.kernel.org/r/20220925172759.3573439-9-jic23@kernel.org Signed-off-by: Guenter Roeck --- drivers/hwmon/ltc2947-core.c | 7 +++---- drivers/hwmon/ltc2947-i2c.c | 2 +- drivers/hwmon/ltc2947-spi.c | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/ltc2947-core.c b/drivers/hwmon/ltc2947-core.c index 626f5bf2c9c7..7404e974762f 100644 --- a/drivers/hwmon/ltc2947-core.c +++ b/drivers/hwmon/ltc2947-core.c @@ -1120,7 +1120,7 @@ int ltc2947_core_probe(struct regmap *map, const char *name) } EXPORT_SYMBOL_GPL(ltc2947_core_probe); -static int __maybe_unused ltc2947_resume(struct device *dev) +static int ltc2947_resume(struct device *dev) { struct ltc2947_data *st = dev_get_drvdata(dev); u32 ctrl = 0; @@ -1149,7 +1149,7 @@ static int __maybe_unused ltc2947_resume(struct device *dev) LTC2947_CONT_MODE_MASK, LTC2947_CONT_MODE(1)); } -static int __maybe_unused ltc2947_suspend(struct device *dev) +static int ltc2947_suspend(struct device *dev) { struct ltc2947_data *st = dev_get_drvdata(dev); @@ -1157,8 +1157,7 @@ static int __maybe_unused ltc2947_suspend(struct device *dev) LTC2947_SHUTDOWN_MASK, 1); } -SIMPLE_DEV_PM_OPS(ltc2947_pm_ops, ltc2947_suspend, ltc2947_resume); -EXPORT_SYMBOL_GPL(ltc2947_pm_ops); +EXPORT_SIMPLE_DEV_PM_OPS(ltc2947_pm_ops, ltc2947_suspend, ltc2947_resume); const struct of_device_id ltc2947_of_match[] = { { .compatible = "adi,ltc2947" }, diff --git a/drivers/hwmon/ltc2947-i2c.c b/drivers/hwmon/ltc2947-i2c.c index ad0dfd3efbf8..96852bc8a964 100644 --- a/drivers/hwmon/ltc2947-i2c.c +++ b/drivers/hwmon/ltc2947-i2c.c @@ -36,7 +36,7 @@ static struct i2c_driver ltc2947_driver = { .driver = { .name = "ltc2947", .of_match_table = ltc2947_of_match, - .pm = <c2947_pm_ops, + .pm = pm_sleep_ptr(<c2947_pm_ops), }, .probe_new = ltc2947_probe, .id_table = ltc2947_id, diff --git a/drivers/hwmon/ltc2947-spi.c b/drivers/hwmon/ltc2947-spi.c index c24ca569db1b..a33be110098c 100644 --- a/drivers/hwmon/ltc2947-spi.c +++ b/drivers/hwmon/ltc2947-spi.c @@ -38,7 +38,7 @@ static struct spi_driver ltc2947_driver = { .driver = { .name = "ltc2947", .of_match_table = ltc2947_of_match, - .pm = <c2947_pm_ops, + .pm = pm_sleep_ptr(<c2947_pm_ops), }, .probe = ltc2947_probe, .id_table = ltc2947_id, -- cgit v1.2.3 From 80294537171703284e18ada3fc213c94b54e6b03 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 25 Sep 2022 18:27:50 +0100 Subject: hwmon: (max31722) Switch to DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr() These newer PM macros allow the compiler to see what code it can remove if !CONFIG_PM_SLEEP. This allows the removal of __maybe_unused markings whilst achieving the same result. Signed-off-by: Jonathan Cameron Link: https://lore.kernel.org/r/20220925172759.3573439-10-jic23@kernel.org Signed-off-by: Guenter Roeck --- drivers/hwmon/max31722.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/max31722.c b/drivers/hwmon/max31722.c index 93e048ee4955..9a31ef388396 100644 --- a/drivers/hwmon/max31722.c +++ b/drivers/hwmon/max31722.c @@ -113,7 +113,7 @@ static void max31722_remove(struct spi_device *spi) dev_warn(&spi->dev, "Failed to put device in stand-by mode\n"); } -static int __maybe_unused max31722_suspend(struct device *dev) +static int max31722_suspend(struct device *dev) { struct spi_device *spi_device = to_spi_device(dev); struct max31722_data *data = spi_get_drvdata(spi_device); @@ -121,7 +121,7 @@ static int __maybe_unused max31722_suspend(struct device *dev) return max31722_set_mode(data, MAX31722_MODE_STANDBY); } -static int __maybe_unused max31722_resume(struct device *dev) +static int max31722_resume(struct device *dev) { struct spi_device *spi_device = to_spi_device(dev); struct max31722_data *data = spi_get_drvdata(spi_device); @@ -129,7 +129,7 @@ static int __maybe_unused max31722_resume(struct device *dev) return max31722_set_mode(data, MAX31722_MODE_CONTINUOUS); } -static SIMPLE_DEV_PM_OPS(max31722_pm_ops, max31722_suspend, max31722_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(max31722_pm_ops, max31722_suspend, max31722_resume); static const struct spi_device_id max31722_spi_id[] = { {"max31722", 0}, @@ -141,7 +141,7 @@ MODULE_DEVICE_TABLE(spi, max31722_spi_id); static struct spi_driver max31722_driver = { .driver = { .name = "max31722", - .pm = &max31722_pm_ops, + .pm = pm_sleep_ptr(&max31722_pm_ops), }, .probe = max31722_probe, .remove = max31722_remove, -- cgit v1.2.3 From 5ce951abc5037f3dea54021e3368a6842c15fe7b Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 25 Sep 2022 18:27:51 +0100 Subject: hwmon: (max31730) witch to DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr() These newer PM macros allow the compiler to see what code it can remove if !CONFIG_PM_SLEEP. This allows the removal of __maybe_unused markings whilst achieving the same result. Signed-off-by: Jonathan Cameron Cc: Guenter Roeck Link: https://lore.kernel.org/r/20220925172759.3573439-11-jic23@kernel.org Signed-off-by: Guenter Roeck --- drivers/hwmon/max31730.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/max31730.c b/drivers/hwmon/max31730.c index 9bdff881f59c..746a767c9fc6 100644 --- a/drivers/hwmon/max31730.c +++ b/drivers/hwmon/max31730.c @@ -404,28 +404,28 @@ static int max31730_detect(struct i2c_client *client, return 0; } -static int __maybe_unused max31730_suspend(struct device *dev) +static int 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) +static int 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 DEFINE_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, + .pm = pm_sleep_ptr(&max31730_pm_ops), }, .probe_new = max31730_probe, .id_table = max31730_ids, -- cgit v1.2.3 From 77563092fe1e58fa84ed6543e0a8f99e03915f4b Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 25 Sep 2022 18:27:52 +0100 Subject: hwmon: (max6639) Switch to DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr() These newer PM macros allow the compiler to see what code it can remove if !CONFIG_PM_SLEEP. This allows the removal of #ifdef guards whilst achieving the same result. Signed-off-by: Jonathan Cameron Cc: Roland Stigge Link: https://lore.kernel.org/r/20220925172759.3573439-12-jic23@kernel.org Signed-off-by: Guenter Roeck --- drivers/hwmon/max6639.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/max6639.c b/drivers/hwmon/max6639.c index 936861131d74..9b895402c80d 100644 --- a/drivers/hwmon/max6639.c +++ b/drivers/hwmon/max6639.c @@ -571,7 +571,6 @@ static int max6639_probe(struct i2c_client *client) return PTR_ERR_OR_ZERO(hwmon_dev); } -#ifdef CONFIG_PM_SLEEP static int max6639_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -609,7 +608,6 @@ static int max6639_resume(struct device *dev) return i2c_smbus_write_byte_data(client, MAX6639_REG_GCONFIG, ret & ~MAX6639_GCONFIG_STANDBY); } -#endif /* CONFIG_PM_SLEEP */ static const struct i2c_device_id max6639_id[] = { {"max6639", 0}, @@ -618,13 +616,13 @@ static const struct i2c_device_id max6639_id[] = { MODULE_DEVICE_TABLE(i2c, max6639_id); -static SIMPLE_DEV_PM_OPS(max6639_pm_ops, max6639_suspend, max6639_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(max6639_pm_ops, max6639_suspend, max6639_resume); static struct i2c_driver max6639_driver = { .class = I2C_CLASS_HWMON, .driver = { .name = "max6639", - .pm = &max6639_pm_ops, + .pm = pm_sleep_ptr(&max6639_pm_ops), }, .probe_new = max6639_probe, .id_table = max6639_id, -- cgit v1.2.3 From 8de7295c207fcf78f61b6c7e00ef45554a0ebd22 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 25 Sep 2022 18:27:53 +0100 Subject: hwmon: (nct6775) Switch to DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These newer PM macros allow the compiler to see what code it can remove if !CONFIG_PM_SLEEP. This allows the removal of __maybe_unused markings whilst achieving the same result. Signed-off-by: Jonathan Cameron Cc: Zoltán Kővágó Link: https://lore.kernel.org/r/20220925172759.3573439-13-jic23@kernel.org Signed-off-by: Guenter Roeck --- drivers/hwmon/nct6775-platform.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/nct6775-platform.c b/drivers/hwmon/nct6775-platform.c index 41c97cfacfb8..b34783784213 100644 --- a/drivers/hwmon/nct6775-platform.c +++ b/drivers/hwmon/nct6775-platform.c @@ -355,7 +355,7 @@ static void nct6791_enable_io_mapping(struct nct6775_sio_data *sio_data) } } -static int __maybe_unused nct6775_suspend(struct device *dev) +static int nct6775_suspend(struct device *dev) { int err; u16 tmp; @@ -386,7 +386,7 @@ out: return err; } -static int __maybe_unused nct6775_resume(struct device *dev) +static int nct6775_resume(struct device *dev) { struct nct6775_data *data = dev_get_drvdata(dev); struct nct6775_sio_data *sio_data = dev_get_platdata(dev); @@ -467,7 +467,7 @@ abort: return err; } -static SIMPLE_DEV_PM_OPS(nct6775_dev_pm_ops, nct6775_suspend, nct6775_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(nct6775_dev_pm_ops, nct6775_suspend, nct6775_resume); static void nct6775_check_fan_inputs(struct nct6775_data *data, struct nct6775_sio_data *sio_data) @@ -934,7 +934,7 @@ static int nct6775_platform_probe(struct platform_device *pdev) static struct platform_driver nct6775_driver = { .driver = { .name = DRVNAME, - .pm = &nct6775_dev_pm_ops, + .pm = pm_sleep_ptr(&nct6775_dev_pm_ops), }, .probe = nct6775_platform_probe, }; -- cgit v1.2.3 From 1839391bdedb23d0e07e9030e18466172818d824 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 25 Sep 2022 18:27:54 +0100 Subject: hwmon: (pwm-fan) Switch to DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr() These newer PM macros allow the compiler to see what code it can remove if !CONFIG_PM_SLEEP. This allows the removal of #ifdef guards whilst achieving the same result. Signed-off-by: Jonathan Cameron Link: https://lore.kernel.org/r/20220925172759.3573439-14-jic23@kernel.org Signed-off-by: Guenter Roeck --- drivers/hwmon/pwm-fan.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c index 498128eb81f1..dc3d9a22d917 100644 --- a/drivers/hwmon/pwm-fan.c +++ b/drivers/hwmon/pwm-fan.c @@ -639,7 +639,6 @@ static void pwm_fan_shutdown(struct platform_device *pdev) pwm_fan_cleanup(ctx); } -#ifdef CONFIG_PM_SLEEP static int pwm_fan_suspend(struct device *dev) { struct pwm_fan_ctx *ctx = dev_get_drvdata(dev); @@ -653,9 +652,8 @@ static int pwm_fan_resume(struct device *dev) return set_pwm(ctx, ctx->pwm_value); } -#endif -static SIMPLE_DEV_PM_OPS(pwm_fan_pm, pwm_fan_suspend, pwm_fan_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(pwm_fan_pm, pwm_fan_suspend, pwm_fan_resume); static const struct of_device_id of_pwm_fan_match[] = { { .compatible = "pwm-fan", }, @@ -668,7 +666,7 @@ static struct platform_driver pwm_fan_driver = { .shutdown = pwm_fan_shutdown, .driver = { .name = "pwm-fan", - .pm = &pwm_fan_pm, + .pm = pm_sleep_ptr(&pwm_fan_pm), .of_match_table = of_pwm_fan_match, }, }; -- cgit v1.2.3 From 73568f92d365d55c5261dda83ef9ec9944929f2e Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 25 Sep 2022 18:27:55 +0100 Subject: hwmon: (tmp102) Switch to DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr() These newer PM macros allow the compiler to see what code it can remove if !CONFIG_PM_SLEEP. This allows the removal of #ifdef guards whilst achieving the same result. Signed-off-by: Jonathan Cameron Link: https://lore.kernel.org/r/20220925172759.3573439-15-jic23@kernel.org Signed-off-by: Guenter Roeck --- drivers/hwmon/tmp102.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/tmp102.c b/drivers/hwmon/tmp102.c index e867a0c2e539..2bf496a62206 100644 --- a/drivers/hwmon/tmp102.c +++ b/drivers/hwmon/tmp102.c @@ -260,7 +260,6 @@ static int tmp102_probe(struct i2c_client *client) return 0; } -#ifdef CONFIG_PM_SLEEP static int tmp102_suspend(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -283,9 +282,8 @@ static int tmp102_resume(struct device *dev) return err; } -#endif /* CONFIG_PM */ -static SIMPLE_DEV_PM_OPS(tmp102_dev_pm_ops, tmp102_suspend, tmp102_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(tmp102_dev_pm_ops, tmp102_suspend, tmp102_resume); static const struct i2c_device_id tmp102_id[] = { { "tmp102", 0 }, @@ -302,7 +300,7 @@ MODULE_DEVICE_TABLE(of, tmp102_of_match); static struct i2c_driver tmp102_driver = { .driver.name = DRIVER_NAME, .driver.of_match_table = of_match_ptr(tmp102_of_match), - .driver.pm = &tmp102_dev_pm_ops, + .driver.pm = pm_sleep_ptr(&tmp102_dev_pm_ops), .probe_new = tmp102_probe, .id_table = tmp102_id, }; -- cgit v1.2.3 From a158b4ea194a3a0232fcfac723ac5923a01bfa1a Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 25 Sep 2022 18:27:56 +0100 Subject: hwmon: (tmp103) Switch to DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr() These newer PM macros allow the compiler to see what code it can remove if !CONFIG_PM_SLEEP. This allows the removal of __maybe_unused markings whilst achieving the same result. Signed-off-by: Jonathan Cameron Link: https://lore.kernel.org/r/20220925172759.3573439-16-jic23@kernel.org Signed-off-by: Guenter Roeck --- drivers/hwmon/tmp103.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/tmp103.c b/drivers/hwmon/tmp103.c index 5cab4436aa77..56d5cbf36a45 100644 --- a/drivers/hwmon/tmp103.c +++ b/drivers/hwmon/tmp103.c @@ -178,7 +178,7 @@ static int tmp103_probe(struct i2c_client *client) return PTR_ERR_OR_ZERO(hwmon_dev); } -static int __maybe_unused tmp103_suspend(struct device *dev) +static int tmp103_suspend(struct device *dev) { struct regmap *regmap = dev_get_drvdata(dev); @@ -186,7 +186,7 @@ static int __maybe_unused tmp103_suspend(struct device *dev) TMP103_CONF_SD_MASK, 0); } -static int __maybe_unused tmp103_resume(struct device *dev) +static int tmp103_resume(struct device *dev) { struct regmap *regmap = dev_get_drvdata(dev); @@ -194,7 +194,7 @@ static int __maybe_unused tmp103_resume(struct device *dev) TMP103_CONF_SD_MASK, TMP103_CONF_SD); } -static SIMPLE_DEV_PM_OPS(tmp103_dev_pm_ops, tmp103_suspend, tmp103_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(tmp103_dev_pm_ops, tmp103_suspend, tmp103_resume); static const struct i2c_device_id tmp103_id[] = { { "tmp103", 0 }, @@ -212,7 +212,7 @@ static struct i2c_driver tmp103_driver = { .driver = { .name = "tmp103", .of_match_table = of_match_ptr(tmp103_of_match), - .pm = &tmp103_dev_pm_ops, + .pm = pm_sleep_ptr(&tmp103_dev_pm_ops), }, .probe_new = tmp103_probe, .id_table = tmp103_id, -- cgit v1.2.3 From 1efe2b254fe149d1b4ded4c3630f6a048add0269 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 25 Sep 2022 18:27:57 +0100 Subject: hwmon: (tmp108) Switch to DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr() These newer PM macros allow the compiler to see what code it can remove if !CONFIG_PM_SLEEP. This allows the removal of __maybe_unused markings whilst achieving the same result. Signed-off-by: Jonathan Cameron Link: https://lore.kernel.org/r/20220925172759.3573439-17-jic23@kernel.org Signed-off-by: Guenter Roeck --- drivers/hwmon/tmp108.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/tmp108.c b/drivers/hwmon/tmp108.c index 5435664c3f6e..acb4ba750b09 100644 --- a/drivers/hwmon/tmp108.c +++ b/drivers/hwmon/tmp108.c @@ -390,7 +390,7 @@ static int tmp108_probe(struct i2c_client *client) return PTR_ERR_OR_ZERO(hwmon_dev); } -static int __maybe_unused tmp108_suspend(struct device *dev) +static int tmp108_suspend(struct device *dev) { struct tmp108 *tmp108 = dev_get_drvdata(dev); @@ -398,7 +398,7 @@ static int __maybe_unused tmp108_suspend(struct device *dev) TMP108_CONF_MODE_MASK, TMP108_MODE_SHUTDOWN); } -static int __maybe_unused tmp108_resume(struct device *dev) +static int tmp108_resume(struct device *dev) { struct tmp108 *tmp108 = dev_get_drvdata(dev); int err; @@ -410,7 +410,7 @@ static int __maybe_unused tmp108_resume(struct device *dev) return err; } -static SIMPLE_DEV_PM_OPS(tmp108_dev_pm_ops, tmp108_suspend, tmp108_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(tmp108_dev_pm_ops, tmp108_suspend, tmp108_resume); static const struct i2c_device_id tmp108_i2c_ids[] = { { "tmp108", 0 }, @@ -429,7 +429,7 @@ MODULE_DEVICE_TABLE(of, tmp108_of_ids); static struct i2c_driver tmp108_driver = { .driver = { .name = DRIVER_NAME, - .pm = &tmp108_dev_pm_ops, + .pm = pm_sleep_ptr(&tmp108_dev_pm_ops), .of_match_table = of_match_ptr(tmp108_of_ids), }, .probe_new = tmp108_probe, -- cgit v1.2.3 From 655231d4b958cfe80f92ff8ae5c2d80d125b3c24 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 25 Sep 2022 18:27:58 +0100 Subject: hwmon: (w83627ehf) Switch to DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr() These newer PM macros allow the compiler to see what code it can remove if !CONFIG_PM_SLEEP. This allows the removal of __maybe_unused markings whilst achieving the same result. Signed-off-by: Jonathan Cameron Link: https://lore.kernel.org/r/20220925172759.3573439-18-jic23@kernel.org Signed-off-by: Guenter Roeck --- drivers/hwmon/w83627ehf.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c index af89b32a93a5..939d4c35e713 100644 --- a/drivers/hwmon/w83627ehf.c +++ b/drivers/hwmon/w83627ehf.c @@ -1944,7 +1944,7 @@ static int __init w83627ehf_probe(struct platform_device *pdev) return PTR_ERR_OR_ZERO(hwmon_dev); } -static int __maybe_unused w83627ehf_suspend(struct device *dev) +static int w83627ehf_suspend(struct device *dev) { struct w83627ehf_data *data = w83627ehf_update_device(dev); @@ -1955,7 +1955,7 @@ static int __maybe_unused w83627ehf_suspend(struct device *dev) return 0; } -static int __maybe_unused w83627ehf_resume(struct device *dev) +static int w83627ehf_resume(struct device *dev) { struct w83627ehf_data *data = dev_get_drvdata(dev); int i; @@ -2010,12 +2010,12 @@ static int __maybe_unused w83627ehf_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(w83627ehf_dev_pm_ops, w83627ehf_suspend, w83627ehf_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(w83627ehf_dev_pm_ops, w83627ehf_suspend, w83627ehf_resume); static struct platform_driver w83627ehf_driver = { .driver = { .name = DRVNAME, - .pm = &w83627ehf_dev_pm_ops, + .pm = pm_sleep_ptr(&w83627ehf_dev_pm_ops), }, }; -- cgit v1.2.3 From 2d5604c822e91c3eebaa0f9d0691acb954071ef1 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 25 Sep 2022 18:27:59 +0100 Subject: hwmon: (ina3221) Use DEFINE_RUNTIME_DEV_PM_OPS() and pm_ptr() These new macros allow the compiler to see all the functions even if !CONFIG_PM* and remove the structures and functions if unused. This removes the need for __maybe_unused markings. Signed-off-by: Jonathan Cameron Cc: Ninad Malwade Link: https://lore.kernel.org/r/20220925172759.3573439-19-jic23@kernel.org Signed-off-by: Guenter Roeck --- drivers/hwmon/ina3221.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/ina3221.c b/drivers/hwmon/ina3221.c index 58d3828e2ec0..8ef2631fc312 100644 --- a/drivers/hwmon/ina3221.c +++ b/drivers/hwmon/ina3221.c @@ -930,7 +930,7 @@ static int ina3221_remove(struct i2c_client *client) return 0; } -static int __maybe_unused ina3221_suspend(struct device *dev) +static int ina3221_suspend(struct device *dev) { struct ina3221_data *ina = dev_get_drvdata(dev); int ret; @@ -953,7 +953,7 @@ static int __maybe_unused ina3221_suspend(struct device *dev) return 0; } -static int __maybe_unused ina3221_resume(struct device *dev) +static int ina3221_resume(struct device *dev) { struct ina3221_data *ina = dev_get_drvdata(dev); int ret; @@ -996,11 +996,8 @@ static int __maybe_unused ina3221_resume(struct device *dev) return 0; } -static const struct dev_pm_ops ina3221_pm = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) - SET_RUNTIME_PM_OPS(ina3221_suspend, ina3221_resume, NULL) -}; +static DEFINE_RUNTIME_DEV_PM_OPS(ina3221_pm, ina3221_suspend, ina3221_resume, + NULL); static const struct of_device_id ina3221_of_match_table[] = { { .compatible = "ti,ina3221", }, @@ -1020,7 +1017,7 @@ static struct i2c_driver ina3221_i2c_driver = { .driver = { .name = INA3221_DRIVER_NAME, .of_match_table = ina3221_of_match_table, - .pm = &ina3221_pm, + .pm = pm_ptr(&ina3221_pm), }, .id_table = ina3221_ids, }; -- cgit v1.2.3 From 760bda91cb4f5f778a6193b20c22726209579164 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 26 Sep 2022 17:39:46 +0200 Subject: hwmon: w83627hf: Reorder symbols to get rid of a few forward declarations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Declarations for static symbols are useless code repetition (unless there are cyclic dependencies). Reorder some functions and variables which allows to get rid of 7 forward declarations. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20220926153946.1478260-1-u.kleine-koenig@pengutronix.de Signed-off-by: Guenter Roeck --- drivers/hwmon/w83627hf.c | 1788 +++++++++++++++++++++++----------------------- 1 file changed, 896 insertions(+), 892 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/w83627hf.c b/drivers/hwmon/w83627hf.c index 9be277156ed2..b638d672ac45 100644 --- a/drivers/hwmon/w83627hf.c +++ b/drivers/hwmon/w83627hf.c @@ -389,14 +389,184 @@ struct w83627hf_data { #endif }; -static int w83627hf_probe(struct platform_device *pdev); -static int w83627hf_remove(struct platform_device *pdev); +/* Registers 0x50-0x5f are banked */ +static inline void w83627hf_set_bank(struct w83627hf_data *data, u16 reg) +{ + if ((reg & 0x00f0) == 0x50) { + outb_p(W83781D_REG_BANK, data->addr + W83781D_ADDR_REG_OFFSET); + outb_p(reg >> 8, data->addr + W83781D_DATA_REG_OFFSET); + } +} + +/* Not strictly necessary, but play it safe for now */ +static inline void w83627hf_reset_bank(struct w83627hf_data *data, u16 reg) +{ + if (reg & 0xff00) { + outb_p(W83781D_REG_BANK, data->addr + W83781D_ADDR_REG_OFFSET); + outb_p(0, data->addr + W83781D_DATA_REG_OFFSET); + } +} + +static int w83627hf_read_value(struct w83627hf_data *data, u16 reg) +{ + int res, word_sized; + + mutex_lock(&data->lock); + word_sized = (((reg & 0xff00) == 0x100) + || ((reg & 0xff00) == 0x200)) + && (((reg & 0x00ff) == 0x50) + || ((reg & 0x00ff) == 0x53) + || ((reg & 0x00ff) == 0x55)); + w83627hf_set_bank(data, reg); + outb_p(reg & 0xff, data->addr + W83781D_ADDR_REG_OFFSET); + res = inb_p(data->addr + W83781D_DATA_REG_OFFSET); + if (word_sized) { + outb_p((reg & 0xff) + 1, + data->addr + W83781D_ADDR_REG_OFFSET); + res = + (res << 8) + inb_p(data->addr + + W83781D_DATA_REG_OFFSET); + } + w83627hf_reset_bank(data, reg); + mutex_unlock(&data->lock); + return res; +} + +static int w83627hf_write_value(struct w83627hf_data *data, u16 reg, u16 value) +{ + int word_sized; + + mutex_lock(&data->lock); + word_sized = (((reg & 0xff00) == 0x100) + || ((reg & 0xff00) == 0x200)) + && (((reg & 0x00ff) == 0x53) + || ((reg & 0x00ff) == 0x55)); + w83627hf_set_bank(data, reg); + outb_p(reg & 0xff, data->addr + W83781D_ADDR_REG_OFFSET); + if (word_sized) { + outb_p(value >> 8, + data->addr + W83781D_DATA_REG_OFFSET); + outb_p((reg & 0xff) + 1, + data->addr + W83781D_ADDR_REG_OFFSET); + } + outb_p(value & 0xff, + data->addr + W83781D_DATA_REG_OFFSET); + w83627hf_reset_bank(data, reg); + mutex_unlock(&data->lock); + return 0; +} + +static void w83627hf_update_fan_div(struct w83627hf_data *data) +{ + int reg; + + reg = w83627hf_read_value(data, W83781D_REG_VID_FANDIV); + data->fan_div[0] = (reg >> 4) & 0x03; + data->fan_div[1] = (reg >> 6) & 0x03; + if (data->type != w83697hf) { + data->fan_div[2] = (w83627hf_read_value(data, + W83781D_REG_PIN) >> 6) & 0x03; + } + reg = w83627hf_read_value(data, W83781D_REG_VBAT); + data->fan_div[0] |= (reg >> 3) & 0x04; + data->fan_div[1] |= (reg >> 4) & 0x04; + if (data->type != w83697hf) + data->fan_div[2] |= (reg >> 5) & 0x04; +} + +static struct w83627hf_data *w83627hf_update_device(struct device *dev) +{ + struct w83627hf_data *data = dev_get_drvdata(dev); + int i, num_temps = (data->type == w83697hf) ? 2 : 3; + int num_pwms = (data->type == w83697hf) ? 2 : 3; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + for (i = 0; i <= 8; i++) { + /* skip missing sensors */ + if (((data->type == w83697hf) && (i == 1)) || + ((data->type != w83627hf && data->type != w83697hf) + && (i == 5 || i == 6))) + continue; + data->in[i] = + w83627hf_read_value(data, W83781D_REG_IN(i)); + data->in_min[i] = + w83627hf_read_value(data, + W83781D_REG_IN_MIN(i)); + data->in_max[i] = + w83627hf_read_value(data, + W83781D_REG_IN_MAX(i)); + } + for (i = 0; i <= 2; i++) { + data->fan[i] = + w83627hf_read_value(data, W83627HF_REG_FAN(i)); + data->fan_min[i] = + w83627hf_read_value(data, + W83627HF_REG_FAN_MIN(i)); + } + for (i = 0; i <= 2; i++) { + u8 tmp = w83627hf_read_value(data, + W836X7HF_REG_PWM(data->type, i)); + /* bits 0-3 are reserved in 627THF */ + if (data->type == w83627thf) + tmp &= 0xf0; + data->pwm[i] = tmp; + if (i == 1 && + (data->type == w83627hf || data->type == w83697hf)) + break; + } + if (data->type == w83627hf) { + u8 tmp = w83627hf_read_value(data, + W83627HF_REG_PWM_FREQ); + data->pwm_freq[0] = tmp & 0x07; + data->pwm_freq[1] = (tmp >> 4) & 0x07; + } else if (data->type != w83627thf) { + for (i = 1; i <= 3; i++) { + data->pwm_freq[i - 1] = + w83627hf_read_value(data, + W83637HF_REG_PWM_FREQ[i - 1]); + if (i == 2 && (data->type == w83697hf)) + break; + } + } + if (data->type != w83627hf) { + for (i = 0; i < num_pwms; i++) { + u8 tmp = w83627hf_read_value(data, + W83627THF_REG_PWM_ENABLE[i]); + data->pwm_enable[i] = + ((tmp >> W83627THF_PWM_ENABLE_SHIFT[i]) + & 0x03) + 1; + } + } + for (i = 0; i < num_temps; i++) { + data->temp[i] = w83627hf_read_value( + data, w83627hf_reg_temp[i]); + data->temp_max[i] = w83627hf_read_value( + data, w83627hf_reg_temp_over[i]); + data->temp_max_hyst[i] = w83627hf_read_value( + data, w83627hf_reg_temp_hyst[i]); + } + + w83627hf_update_fan_div(data); + + data->alarms = + w83627hf_read_value(data, W83781D_REG_ALARM1) | + (w83627hf_read_value(data, W83781D_REG_ALARM2) << 8) | + (w83627hf_read_value(data, W83781D_REG_ALARM3) << 16); + i = w83627hf_read_value(data, W83781D_REG_BEEP_INTS2); + data->beep_mask = (i << 8) | + w83627hf_read_value(data, W83781D_REG_BEEP_INTS1) | + w83627hf_read_value(data, W83781D_REG_BEEP_INTS3) << 16; + data->last_updated = jiffies; + data->valid = true; + } + + mutex_unlock(&data->update_lock); -static int w83627hf_read_value(struct w83627hf_data *data, u16 reg); -static int w83627hf_write_value(struct w83627hf_data *data, u16 reg, u16 value); -static void w83627hf_update_fan_div(struct w83627hf_data *data); -static struct w83627hf_data *w83627hf_update_device(struct device *dev); -static void w83627hf_init_device(struct platform_device *pdev); + return data; +} #ifdef CONFIG_PM static int w83627hf_suspend(struct device *dev) @@ -464,99 +634,171 @@ static const struct dev_pm_ops w83627hf_dev_pm_ops = { #define W83627HF_DEV_PM_OPS NULL #endif /* CONFIG_PM */ -static struct platform_driver w83627hf_driver = { - .driver = { - .name = DRVNAME, - .pm = W83627HF_DEV_PM_OPS, - }, - .probe = w83627hf_probe, - .remove = w83627hf_remove, -}; - -static ssize_t -in_input_show(struct device *dev, struct device_attribute *devattr, char *buf) -{ - int nr = to_sensor_dev_attr(devattr)->index; - struct w83627hf_data *data = w83627hf_update_device(dev); - return sprintf(buf, "%ld\n", (long)IN_FROM_REG(data->in[nr])); -} -static ssize_t -in_min_show(struct device *dev, struct device_attribute *devattr, char *buf) -{ - int nr = to_sensor_dev_attr(devattr)->index; - struct w83627hf_data *data = w83627hf_update_device(dev); - return sprintf(buf, "%ld\n", (long)IN_FROM_REG(data->in_min[nr])); -} -static ssize_t -in_max_show(struct device *dev, struct device_attribute *devattr, char *buf) -{ - int nr = to_sensor_dev_attr(devattr)->index; - struct w83627hf_data *data = w83627hf_update_device(dev); - return sprintf(buf, "%ld\n", (long)IN_FROM_REG(data->in_max[nr])); -} -static ssize_t -in_min_store(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) +static int w83627thf_read_gpio5(struct platform_device *pdev) { - int nr = to_sensor_dev_attr(devattr)->index; - struct w83627hf_data *data = dev_get_drvdata(dev); - long val; - int err; - - err = kstrtol(buf, 10, &val); - if (err) - return err; + struct w83627hf_sio_data *sio_data = dev_get_platdata(&pdev->dev); + int res = 0xff, sel; - mutex_lock(&data->update_lock); - data->in_min[nr] = IN_TO_REG(val); - w83627hf_write_value(data, W83781D_REG_IN_MIN(nr), data->in_min[nr]); - mutex_unlock(&data->update_lock); - return count; -} -static ssize_t -in_max_store(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) -{ - int nr = to_sensor_dev_attr(devattr)->index; - struct w83627hf_data *data = dev_get_drvdata(dev); - long val; - int err; + if (superio_enter(sio_data)) { + /* + * Some other driver reserved the address space for itself. + * We don't want to fail driver instantiation because of that, + * so display a warning and keep going. + */ + dev_warn(&pdev->dev, + "Can not read VID data: Failed to enable SuperIO access\n"); + return res; + } - err = kstrtol(buf, 10, &val); - if (err) - return err; + superio_select(sio_data, W83627HF_LD_GPIO5); - mutex_lock(&data->update_lock); - data->in_max[nr] = IN_TO_REG(val); - w83627hf_write_value(data, W83781D_REG_IN_MAX(nr), data->in_max[nr]); - mutex_unlock(&data->update_lock); - return count; -} + res = 0xff; -static SENSOR_DEVICE_ATTR_RO(in1_input, in_input, 1); -static SENSOR_DEVICE_ATTR_RW(in1_min, in_min, 1); -static SENSOR_DEVICE_ATTR_RW(in1_max, in_max, 1); -static SENSOR_DEVICE_ATTR_RO(in2_input, in_input, 2); -static SENSOR_DEVICE_ATTR_RW(in2_min, in_min, 2); -static SENSOR_DEVICE_ATTR_RW(in2_max, in_max, 2); -static SENSOR_DEVICE_ATTR_RO(in3_input, in_input, 3); -static SENSOR_DEVICE_ATTR_RW(in3_min, in_min, 3); -static SENSOR_DEVICE_ATTR_RW(in3_max, in_max, 3); -static SENSOR_DEVICE_ATTR_RO(in4_input, in_input, 4); -static SENSOR_DEVICE_ATTR_RW(in4_min, in_min, 4); -static SENSOR_DEVICE_ATTR_RW(in4_max, in_max, 4); -static SENSOR_DEVICE_ATTR_RO(in5_input, in_input, 5); -static SENSOR_DEVICE_ATTR_RW(in5_min, in_min, 5); -static SENSOR_DEVICE_ATTR_RW(in5_max, in_max, 5); -static SENSOR_DEVICE_ATTR_RO(in6_input, in_input, 6); -static SENSOR_DEVICE_ATTR_RW(in6_min, in_min, 6); -static SENSOR_DEVICE_ATTR_RW(in6_max, in_max, 6); -static SENSOR_DEVICE_ATTR_RO(in7_input, in_input, 7); -static SENSOR_DEVICE_ATTR_RW(in7_min, in_min, 7); -static SENSOR_DEVICE_ATTR_RW(in7_max, in_max, 7); -static SENSOR_DEVICE_ATTR_RO(in8_input, in_input, 8); -static SENSOR_DEVICE_ATTR_RW(in8_min, in_min, 8); -static SENSOR_DEVICE_ATTR_RW(in8_max, in_max, 8); + /* Make sure these GPIO pins are enabled */ + if (!(superio_inb(sio_data, W83627THF_GPIO5_EN) & (1<<3))) { + dev_dbg(&pdev->dev, "GPIO5 disabled, no VID function\n"); + goto exit; + } + + /* + * Make sure the pins are configured for input + * There must be at least five (VRM 9), and possibly 6 (VRM 10) + */ + sel = superio_inb(sio_data, W83627THF_GPIO5_IOSR) & 0x3f; + if ((sel & 0x1f) != 0x1f) { + dev_dbg(&pdev->dev, "GPIO5 not configured for VID " + "function\n"); + goto exit; + } + + dev_info(&pdev->dev, "Reading VID from GPIO5\n"); + res = superio_inb(sio_data, W83627THF_GPIO5_DR) & sel; + +exit: + superio_exit(sio_data); + return res; +} + +static int w83687thf_read_vid(struct platform_device *pdev) +{ + struct w83627hf_sio_data *sio_data = dev_get_platdata(&pdev->dev); + int res = 0xff; + + if (superio_enter(sio_data)) { + /* + * Some other driver reserved the address space for itself. + * We don't want to fail driver instantiation because of that, + * so display a warning and keep going. + */ + dev_warn(&pdev->dev, + "Can not read VID data: Failed to enable SuperIO access\n"); + return res; + } + + superio_select(sio_data, W83627HF_LD_HWM); + + /* Make sure these GPIO pins are enabled */ + if (!(superio_inb(sio_data, W83687THF_VID_EN) & (1 << 2))) { + dev_dbg(&pdev->dev, "VID disabled, no VID function\n"); + goto exit; + } + + /* Make sure the pins are configured for input */ + if (!(superio_inb(sio_data, W83687THF_VID_CFG) & (1 << 4))) { + dev_dbg(&pdev->dev, "VID configured as output, " + "no VID function\n"); + goto exit; + } + + res = superio_inb(sio_data, W83687THF_VID_DATA) & 0x3f; + +exit: + superio_exit(sio_data); + return res; +} + +static void w83627hf_init_device(struct platform_device *pdev) +{ + struct w83627hf_data *data = platform_get_drvdata(pdev); + int i; + enum chips type = data->type; + u8 tmp; + + /* Minimize conflicts with other winbond i2c-only clients... */ + /* disable i2c subclients... how to disable main i2c client?? */ + /* force i2c address to relatively uncommon address */ + if (type == w83627hf) { + w83627hf_write_value(data, W83781D_REG_I2C_SUBADDR, 0x89); + w83627hf_write_value(data, W83781D_REG_I2C_ADDR, force_i2c); + } + + /* Read VID only once */ + if (type == w83627hf || type == w83637hf) { + int lo = w83627hf_read_value(data, W83781D_REG_VID_FANDIV); + int hi = w83627hf_read_value(data, W83781D_REG_CHIPID); + data->vid = (lo & 0x0f) | ((hi & 0x01) << 4); + } else if (type == w83627thf) { + data->vid = w83627thf_read_gpio5(pdev); + } else if (type == w83687thf) { + data->vid = w83687thf_read_vid(pdev); + } + + /* Read VRM & OVT Config only once */ + if (type == w83627thf || type == w83637hf || type == w83687thf) { + data->vrm_ovt = + w83627hf_read_value(data, W83627THF_REG_VRM_OVT_CFG); + } + + tmp = w83627hf_read_value(data, W83781D_REG_SCFG1); + for (i = 1; i <= 3; i++) { + if (!(tmp & BIT_SCFG1[i - 1])) { + data->sens[i - 1] = 4; + } else { + if (w83627hf_read_value + (data, + W83781D_REG_SCFG2) & BIT_SCFG2[i - 1]) + data->sens[i - 1] = 1; + else + data->sens[i - 1] = 2; + } + if ((type == w83697hf) && (i == 2)) + break; + } + + if(init) { + /* Enable temp2 */ + tmp = w83627hf_read_value(data, W83627HF_REG_TEMP2_CONFIG); + if (tmp & 0x01) { + dev_warn(&pdev->dev, "Enabling temp2, readings " + "might not make sense\n"); + w83627hf_write_value(data, W83627HF_REG_TEMP2_CONFIG, + tmp & 0xfe); + } + + /* Enable temp3 */ + if (type != w83697hf) { + tmp = w83627hf_read_value(data, + W83627HF_REG_TEMP3_CONFIG); + if (tmp & 0x01) { + dev_warn(&pdev->dev, "Enabling temp3, " + "readings might not make sense\n"); + w83627hf_write_value(data, + W83627HF_REG_TEMP3_CONFIG, tmp & 0xfe); + } + } + } + + /* Start monitoring */ + w83627hf_write_value(data, W83781D_REG_CONFIG, + (w83627hf_read_value(data, + W83781D_REG_CONFIG) & 0xf7) + | 0x01); + + /* Enable VBAT monitoring if needed */ + tmp = w83627hf_read_value(data, W83781D_REG_VBAT); + if (!(tmp & 0x01)) + w83627hf_write_value(data, W83781D_REG_VBAT, tmp | 0x01); +} /* use a different set of functions for in0 */ static ssize_t show_in_0(struct w83627hf_data *data, char *buf, u8 reg) @@ -582,6 +824,7 @@ static ssize_t in0_input_show(struct device *dev, struct w83627hf_data *data = w83627hf_update_device(dev); return show_in_0(data, buf, data->in[0]); } +static DEVICE_ATTR_RO(in0_input); static ssize_t in0_min_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -590,13 +833,6 @@ static ssize_t in0_min_show(struct device *dev, struct device_attribute *attr, return show_in_0(data, buf, data->in_min[0]); } -static ssize_t in0_max_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct w83627hf_data *data = w83627hf_update_device(dev); - return show_in_0(data, buf, data->in_max[0]); -} - static ssize_t in0_min_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -627,6 +863,15 @@ static ssize_t in0_min_store(struct device *dev, return count; } +static DEVICE_ATTR_RW(in0_min); + +static ssize_t in0_max_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct w83627hf_data *data = w83627hf_update_device(dev); + return show_in_0(data, buf, data->in_max[0]); +} + static ssize_t in0_max_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -657,97 +902,129 @@ static ssize_t in0_max_store(struct device *dev, return count; } -static DEVICE_ATTR_RO(in0_input); -static DEVICE_ATTR_RW(in0_min); static DEVICE_ATTR_RW(in0_max); static ssize_t -fan_input_show(struct device *dev, struct device_attribute *devattr, - char *buf) +alarm_show(struct device *dev, struct device_attribute *attr, char *buf) { - int nr = to_sensor_dev_attr(devattr)->index; struct w83627hf_data *data = w83627hf_update_device(dev); - return sprintf(buf, "%ld\n", FAN_FROM_REG(data->fan[nr], - (long)DIV_FROM_REG(data->fan_div[nr]))); + int bitnr = to_sensor_dev_attr(attr)->index; + return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1); } + +static SENSOR_DEVICE_ATTR_RO(in0_alarm, alarm, 0); +static SENSOR_DEVICE_ATTR_RO(in1_alarm, alarm, 1); +static SENSOR_DEVICE_ATTR_RO(in2_alarm, alarm, 2); +static SENSOR_DEVICE_ATTR_RO(in3_alarm, alarm, 3); +static SENSOR_DEVICE_ATTR_RO(in4_alarm, alarm, 8); +static SENSOR_DEVICE_ATTR_RO(in5_alarm, alarm, 9); +static SENSOR_DEVICE_ATTR_RO(in6_alarm, alarm, 10); +static SENSOR_DEVICE_ATTR_RO(in7_alarm, alarm, 16); +static SENSOR_DEVICE_ATTR_RO(in8_alarm, alarm, 17); +static SENSOR_DEVICE_ATTR_RO(fan1_alarm, alarm, 6); +static SENSOR_DEVICE_ATTR_RO(fan2_alarm, alarm, 7); +static SENSOR_DEVICE_ATTR_RO(fan3_alarm, alarm, 11); +static SENSOR_DEVICE_ATTR_RO(temp1_alarm, alarm, 4); +static SENSOR_DEVICE_ATTR_RO(temp2_alarm, alarm, 5); +static SENSOR_DEVICE_ATTR_RO(temp3_alarm, alarm, 13); + static ssize_t -fan_min_show(struct device *dev, struct device_attribute *devattr, char *buf) +beep_show(struct device *dev, struct device_attribute *attr, char *buf) { - int nr = to_sensor_dev_attr(devattr)->index; struct w83627hf_data *data = w83627hf_update_device(dev); - return sprintf(buf, "%ld\n", FAN_FROM_REG(data->fan_min[nr], - (long)DIV_FROM_REG(data->fan_div[nr]))); + int bitnr = to_sensor_dev_attr(attr)->index; + return sprintf(buf, "%u\n", (data->beep_mask >> bitnr) & 1); } + static ssize_t -fan_min_store(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) +beep_store(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) { - int nr = to_sensor_dev_attr(devattr)->index; struct w83627hf_data *data = dev_get_drvdata(dev); - unsigned long val; + int bitnr = to_sensor_dev_attr(attr)->index; + u8 reg; + unsigned long bit; int err; - err = kstrtoul(buf, 10, &val); + err = kstrtoul(buf, 10, &bit); if (err) return err; - mutex_lock(&data->update_lock); - data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr])); - w83627hf_write_value(data, W83627HF_REG_FAN_MIN(nr), - data->fan_min[nr]); - - mutex_unlock(&data->update_lock); - return count; -} - -static SENSOR_DEVICE_ATTR_RO(fan1_input, fan_input, 0); -static SENSOR_DEVICE_ATTR_RW(fan1_min, fan_min, 0); -static SENSOR_DEVICE_ATTR_RO(fan2_input, fan_input, 1); -static SENSOR_DEVICE_ATTR_RW(fan2_min, fan_min, 1); -static SENSOR_DEVICE_ATTR_RO(fan3_input, fan_input, 2); -static SENSOR_DEVICE_ATTR_RW(fan3_min, fan_min, 2); + if (bit & ~1) + return -EINVAL; -static ssize_t -temp_show(struct device *dev, struct device_attribute *devattr, char *buf) -{ - int nr = to_sensor_dev_attr(devattr)->index; - struct w83627hf_data *data = w83627hf_update_device(dev); + mutex_lock(&data->update_lock); + if (bit) + data->beep_mask |= (1 << bitnr); + else + data->beep_mask &= ~(1 << bitnr); - u16 tmp = data->temp[nr]; - return sprintf(buf, "%ld\n", (nr) ? (long) LM75_TEMP_FROM_REG(tmp) - : (long) TEMP_FROM_REG(tmp)); + if (bitnr < 8) { + reg = w83627hf_read_value(data, W83781D_REG_BEEP_INTS1); + if (bit) + reg |= (1 << bitnr); + else + reg &= ~(1 << bitnr); + w83627hf_write_value(data, W83781D_REG_BEEP_INTS1, reg); + } else if (bitnr < 16) { + reg = w83627hf_read_value(data, W83781D_REG_BEEP_INTS2); + if (bit) + reg |= (1 << (bitnr - 8)); + else + reg &= ~(1 << (bitnr - 8)); + w83627hf_write_value(data, W83781D_REG_BEEP_INTS2, reg); + } else { + reg = w83627hf_read_value(data, W83781D_REG_BEEP_INTS3); + if (bit) + reg |= (1 << (bitnr - 16)); + else + reg &= ~(1 << (bitnr - 16)); + w83627hf_write_value(data, W83781D_REG_BEEP_INTS3, reg); + } + mutex_unlock(&data->update_lock); + + return count; } +static SENSOR_DEVICE_ATTR_RW(in0_beep, beep, 0); +static SENSOR_DEVICE_ATTR_RW(in1_beep, beep, 1); +static SENSOR_DEVICE_ATTR_RW(in2_beep, beep, 2); +static SENSOR_DEVICE_ATTR_RW(in3_beep, beep, 3); +static SENSOR_DEVICE_ATTR_RW(in4_beep, beep, 8); +static SENSOR_DEVICE_ATTR_RW(in5_beep, beep, 9); +static SENSOR_DEVICE_ATTR_RW(in6_beep, beep, 10); +static SENSOR_DEVICE_ATTR_RW(in7_beep, beep, 16); +static SENSOR_DEVICE_ATTR_RW(in8_beep, beep, 17); +static SENSOR_DEVICE_ATTR_RW(fan1_beep, beep, 6); +static SENSOR_DEVICE_ATTR_RW(fan2_beep, beep, 7); +static SENSOR_DEVICE_ATTR_RW(fan3_beep, beep, 11); +static SENSOR_DEVICE_ATTR_RW(temp1_beep, beep, 4); +static SENSOR_DEVICE_ATTR_RW(temp2_beep, beep, 5); +static SENSOR_DEVICE_ATTR_RW(temp3_beep, beep, 13); +static SENSOR_DEVICE_ATTR_RW(beep_enable, beep, 15); + static ssize_t -temp_max_show(struct device *dev, struct device_attribute *devattr, char *buf) +in_input_show(struct device *dev, struct device_attribute *devattr, char *buf) { int nr = to_sensor_dev_attr(devattr)->index; struct w83627hf_data *data = w83627hf_update_device(dev); - - u16 tmp = data->temp_max[nr]; - return sprintf(buf, "%ld\n", (nr) ? (long) LM75_TEMP_FROM_REG(tmp) - : (long) TEMP_FROM_REG(tmp)); + return sprintf(buf, "%ld\n", (long)IN_FROM_REG(data->in[nr])); } static ssize_t -temp_max_hyst_show(struct device *dev, struct device_attribute *devattr, - char *buf) +in_min_show(struct device *dev, struct device_attribute *devattr, char *buf) { int nr = to_sensor_dev_attr(devattr)->index; struct w83627hf_data *data = w83627hf_update_device(dev); - - u16 tmp = data->temp_max_hyst[nr]; - return sprintf(buf, "%ld\n", (nr) ? (long) LM75_TEMP_FROM_REG(tmp) - : (long) TEMP_FROM_REG(tmp)); + return sprintf(buf, "%ld\n", (long)IN_FROM_REG(data->in_min[nr])); } static ssize_t -temp_max_store(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) +in_min_store(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) { int nr = to_sensor_dev_attr(devattr)->index; struct w83627hf_data *data = dev_get_drvdata(dev); - u16 tmp; long val; int err; @@ -755,21 +1032,27 @@ temp_max_store(struct device *dev, struct device_attribute *devattr, if (err) return err; - tmp = (nr) ? LM75_TEMP_TO_REG(val) : TEMP_TO_REG(val); mutex_lock(&data->update_lock); - data->temp_max[nr] = tmp; - w83627hf_write_value(data, w83627hf_reg_temp_over[nr], tmp); + data->in_min[nr] = IN_TO_REG(val); + w83627hf_write_value(data, W83781D_REG_IN_MIN(nr), data->in_min[nr]); mutex_unlock(&data->update_lock); return count; } static ssize_t -temp_max_hyst_store(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) +in_max_show(struct device *dev, struct device_attribute *devattr, char *buf) +{ + int nr = to_sensor_dev_attr(devattr)->index; + struct w83627hf_data *data = w83627hf_update_device(dev); + return sprintf(buf, "%ld\n", (long)IN_FROM_REG(data->in_max[nr])); +} + +static ssize_t +in_max_store(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) { int nr = to_sensor_dev_attr(devattr)->index; struct w83627hf_data *data = dev_get_drvdata(dev); - u16 tmp; long val; int err; @@ -777,42 +1060,62 @@ temp_max_hyst_store(struct device *dev, struct device_attribute *devattr, if (err) return err; - tmp = (nr) ? LM75_TEMP_TO_REG(val) : TEMP_TO_REG(val); mutex_lock(&data->update_lock); - data->temp_max_hyst[nr] = tmp; - w83627hf_write_value(data, w83627hf_reg_temp_hyst[nr], tmp); + data->in_max[nr] = IN_TO_REG(val); + w83627hf_write_value(data, W83781D_REG_IN_MAX(nr), data->in_max[nr]); mutex_unlock(&data->update_lock); return count; } -static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, 0); -static SENSOR_DEVICE_ATTR_RW(temp1_max, temp_max, 0); -static SENSOR_DEVICE_ATTR_RW(temp1_max_hyst, temp_max_hyst, 0); -static SENSOR_DEVICE_ATTR_RO(temp2_input, temp, 1); -static SENSOR_DEVICE_ATTR_RW(temp2_max, temp_max, 1); -static SENSOR_DEVICE_ATTR_RW(temp2_max_hyst, temp_max_hyst, 1); -static SENSOR_DEVICE_ATTR_RO(temp3_input, temp, 2); -static SENSOR_DEVICE_ATTR_RW(temp3_max, temp_max, 2); -static SENSOR_DEVICE_ATTR_RW(temp3_max_hyst, temp_max_hyst, 2); +static SENSOR_DEVICE_ATTR_RO(in1_input, in_input, 1); +static SENSOR_DEVICE_ATTR_RW(in1_min, in_min, 1); +static SENSOR_DEVICE_ATTR_RW(in1_max, in_max, 1); +static SENSOR_DEVICE_ATTR_RO(in2_input, in_input, 2); +static SENSOR_DEVICE_ATTR_RW(in2_min, in_min, 2); +static SENSOR_DEVICE_ATTR_RW(in2_max, in_max, 2); +static SENSOR_DEVICE_ATTR_RO(in3_input, in_input, 3); +static SENSOR_DEVICE_ATTR_RW(in3_min, in_min, 3); +static SENSOR_DEVICE_ATTR_RW(in3_max, in_max, 3); +static SENSOR_DEVICE_ATTR_RO(in4_input, in_input, 4); +static SENSOR_DEVICE_ATTR_RW(in4_min, in_min, 4); +static SENSOR_DEVICE_ATTR_RW(in4_max, in_max, 4); +static SENSOR_DEVICE_ATTR_RO(in5_input, in_input, 5); +static SENSOR_DEVICE_ATTR_RW(in5_min, in_min, 5); +static SENSOR_DEVICE_ATTR_RW(in5_max, in_max, 5); +static SENSOR_DEVICE_ATTR_RO(in6_input, in_input, 6); +static SENSOR_DEVICE_ATTR_RW(in6_min, in_min, 6); +static SENSOR_DEVICE_ATTR_RW(in6_max, in_max, 6); +static SENSOR_DEVICE_ATTR_RO(in7_input, in_input, 7); +static SENSOR_DEVICE_ATTR_RW(in7_min, in_min, 7); +static SENSOR_DEVICE_ATTR_RW(in7_max, in_max, 7); +static SENSOR_DEVICE_ATTR_RO(in8_input, in_input, 8); +static SENSOR_DEVICE_ATTR_RW(in8_min, in_min, 8); +static SENSOR_DEVICE_ATTR_RW(in8_max, in_max, 8); static ssize_t -cpu0_vid_show(struct device *dev, struct device_attribute *attr, char *buf) +fan_input_show(struct device *dev, struct device_attribute *devattr, + char *buf) { + int nr = to_sensor_dev_attr(devattr)->index; struct w83627hf_data *data = w83627hf_update_device(dev); - return sprintf(buf, "%ld\n", (long) vid_from_reg(data->vid, data->vrm)); + return sprintf(buf, "%ld\n", FAN_FROM_REG(data->fan[nr], + (long)DIV_FROM_REG(data->fan_div[nr]))); } -static DEVICE_ATTR_RO(cpu0_vid); static ssize_t -vrm_show(struct device *dev, struct device_attribute *attr, char *buf) +fan_min_show(struct device *dev, struct device_attribute *devattr, char *buf) { - struct w83627hf_data *data = dev_get_drvdata(dev); - return sprintf(buf, "%ld\n", (long) data->vrm); + int nr = to_sensor_dev_attr(devattr)->index; + struct w83627hf_data *data = w83627hf_update_device(dev); + return sprintf(buf, "%ld\n", FAN_FROM_REG(data->fan_min[nr], + (long)DIV_FROM_REG(data->fan_div[nr]))); } + static ssize_t -vrm_store(struct device *dev, struct device_attribute *attr, const char *buf, - size_t count) +fan_min_store(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) { + int nr = to_sensor_dev_attr(devattr)->index; struct w83627hf_data *data = dev_get_drvdata(dev); unsigned long val; int err; @@ -821,58 +1124,45 @@ vrm_store(struct device *dev, struct device_attribute *attr, const char *buf, if (err) return err; - if (val > 255) - return -EINVAL; - data->vrm = val; + mutex_lock(&data->update_lock); + data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr])); + w83627hf_write_value(data, W83627HF_REG_FAN_MIN(nr), + data->fan_min[nr]); + mutex_unlock(&data->update_lock); return count; } -static DEVICE_ATTR_RW(vrm); - -static ssize_t -alarms_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct w83627hf_data *data = w83627hf_update_device(dev); - return sprintf(buf, "%ld\n", (long) data->alarms); -} -static DEVICE_ATTR_RO(alarms); -static ssize_t -alarm_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct w83627hf_data *data = w83627hf_update_device(dev); - int bitnr = to_sensor_dev_attr(attr)->index; - return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1); -} -static SENSOR_DEVICE_ATTR_RO(in0_alarm, alarm, 0); -static SENSOR_DEVICE_ATTR_RO(in1_alarm, alarm, 1); -static SENSOR_DEVICE_ATTR_RO(in2_alarm, alarm, 2); -static SENSOR_DEVICE_ATTR_RO(in3_alarm, alarm, 3); -static SENSOR_DEVICE_ATTR_RO(in4_alarm, alarm, 8); -static SENSOR_DEVICE_ATTR_RO(in5_alarm, alarm, 9); -static SENSOR_DEVICE_ATTR_RO(in6_alarm, alarm, 10); -static SENSOR_DEVICE_ATTR_RO(in7_alarm, alarm, 16); -static SENSOR_DEVICE_ATTR_RO(in8_alarm, alarm, 17); -static SENSOR_DEVICE_ATTR_RO(fan1_alarm, alarm, 6); -static SENSOR_DEVICE_ATTR_RO(fan2_alarm, alarm, 7); -static SENSOR_DEVICE_ATTR_RO(fan3_alarm, alarm, 11); -static SENSOR_DEVICE_ATTR_RO(temp1_alarm, alarm, 4); -static SENSOR_DEVICE_ATTR_RO(temp2_alarm, alarm, 5); -static SENSOR_DEVICE_ATTR_RO(temp3_alarm, alarm, 13); +static SENSOR_DEVICE_ATTR_RO(fan1_input, fan_input, 0); +static SENSOR_DEVICE_ATTR_RW(fan1_min, fan_min, 0); +static SENSOR_DEVICE_ATTR_RO(fan2_input, fan_input, 1); +static SENSOR_DEVICE_ATTR_RW(fan2_min, fan_min, 1); +static SENSOR_DEVICE_ATTR_RO(fan3_input, fan_input, 2); +static SENSOR_DEVICE_ATTR_RW(fan3_min, fan_min, 2); static ssize_t -beep_mask_show(struct device *dev, struct device_attribute *attr, char *buf) +fan_div_show(struct device *dev, struct device_attribute *devattr, char *buf) { + int nr = to_sensor_dev_attr(devattr)->index; struct w83627hf_data *data = w83627hf_update_device(dev); return sprintf(buf, "%ld\n", - (long)BEEP_MASK_FROM_REG(data->beep_mask)); + (long) DIV_FROM_REG(data->fan_div[nr])); } +/* + * Note: we save and restore the fan minimum here, because its value is + * determined in part by the fan divisor. This follows the principle of + * least surprise; the user doesn't expect the fan minimum to change just + * because the divisor changed. + */ static ssize_t -beep_mask_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +fan_div_store(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) { + int nr = to_sensor_dev_attr(devattr)->index; struct w83627hf_data *data = dev_get_drvdata(dev); + unsigned long min; + u8 reg; unsigned long val; int err; @@ -882,289 +1172,121 @@ beep_mask_store(struct device *dev, struct device_attribute *attr, mutex_lock(&data->update_lock); - /* preserve beep enable */ - data->beep_mask = (data->beep_mask & 0x8000) - | BEEP_MASK_TO_REG(val); - w83627hf_write_value(data, W83781D_REG_BEEP_INTS1, - data->beep_mask & 0xff); - w83627hf_write_value(data, W83781D_REG_BEEP_INTS3, - ((data->beep_mask) >> 16) & 0xff); - w83627hf_write_value(data, W83781D_REG_BEEP_INTS2, - (data->beep_mask >> 8) & 0xff); + /* Save fan_min */ + min = FAN_FROM_REG(data->fan_min[nr], + DIV_FROM_REG(data->fan_div[nr])); + + data->fan_div[nr] = DIV_TO_REG(val); + + reg = (w83627hf_read_value(data, nr==2 ? W83781D_REG_PIN : W83781D_REG_VID_FANDIV) + & (nr==0 ? 0xcf : 0x3f)) + | ((data->fan_div[nr] & 0x03) << (nr==0 ? 4 : 6)); + w83627hf_write_value(data, nr==2 ? W83781D_REG_PIN : W83781D_REG_VID_FANDIV, reg); + + reg = (w83627hf_read_value(data, W83781D_REG_VBAT) + & ~(1 << (5 + nr))) + | ((data->fan_div[nr] & 0x04) << (3 + nr)); + w83627hf_write_value(data, W83781D_REG_VBAT, reg); + + /* Restore fan_min */ + data->fan_min[nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr])); + w83627hf_write_value(data, W83627HF_REG_FAN_MIN(nr), data->fan_min[nr]); mutex_unlock(&data->update_lock); return count; } -static DEVICE_ATTR_RW(beep_mask); +static SENSOR_DEVICE_ATTR_RW(fan1_div, fan_div, 0); +static SENSOR_DEVICE_ATTR_RW(fan2_div, fan_div, 1); +static SENSOR_DEVICE_ATTR_RW(fan3_div, fan_div, 2); static ssize_t -beep_show(struct device *dev, struct device_attribute *attr, char *buf) +temp_show(struct device *dev, struct device_attribute *devattr, char *buf) { + int nr = to_sensor_dev_attr(devattr)->index; struct w83627hf_data *data = w83627hf_update_device(dev); - int bitnr = to_sensor_dev_attr(attr)->index; - return sprintf(buf, "%u\n", (data->beep_mask >> bitnr) & 1); + + u16 tmp = data->temp[nr]; + return sprintf(buf, "%ld\n", (nr) ? (long) LM75_TEMP_FROM_REG(tmp) + : (long) TEMP_FROM_REG(tmp)); } static ssize_t -beep_store(struct device *dev, struct device_attribute *attr, const char *buf, - size_t count) -{ - struct w83627hf_data *data = dev_get_drvdata(dev); - int bitnr = to_sensor_dev_attr(attr)->index; - u8 reg; - unsigned long bit; - int err; - - err = kstrtoul(buf, 10, &bit); - if (err) - return err; - - if (bit & ~1) - return -EINVAL; - - mutex_lock(&data->update_lock); - if (bit) - data->beep_mask |= (1 << bitnr); - else - data->beep_mask &= ~(1 << bitnr); - - if (bitnr < 8) { - reg = w83627hf_read_value(data, W83781D_REG_BEEP_INTS1); - if (bit) - reg |= (1 << bitnr); - else - reg &= ~(1 << bitnr); - w83627hf_write_value(data, W83781D_REG_BEEP_INTS1, reg); - } else if (bitnr < 16) { - reg = w83627hf_read_value(data, W83781D_REG_BEEP_INTS2); - if (bit) - reg |= (1 << (bitnr - 8)); - else - reg &= ~(1 << (bitnr - 8)); - w83627hf_write_value(data, W83781D_REG_BEEP_INTS2, reg); - } else { - reg = w83627hf_read_value(data, W83781D_REG_BEEP_INTS3); - if (bit) - reg |= (1 << (bitnr - 16)); - else - reg &= ~(1 << (bitnr - 16)); - w83627hf_write_value(data, W83781D_REG_BEEP_INTS3, reg); - } - mutex_unlock(&data->update_lock); - - return count; -} - -static SENSOR_DEVICE_ATTR_RW(in0_beep, beep, 0); -static SENSOR_DEVICE_ATTR_RW(in1_beep, beep, 1); -static SENSOR_DEVICE_ATTR_RW(in2_beep, beep, 2); -static SENSOR_DEVICE_ATTR_RW(in3_beep, beep, 3); -static SENSOR_DEVICE_ATTR_RW(in4_beep, beep, 8); -static SENSOR_DEVICE_ATTR_RW(in5_beep, beep, 9); -static SENSOR_DEVICE_ATTR_RW(in6_beep, beep, 10); -static SENSOR_DEVICE_ATTR_RW(in7_beep, beep, 16); -static SENSOR_DEVICE_ATTR_RW(in8_beep, beep, 17); -static SENSOR_DEVICE_ATTR_RW(fan1_beep, beep, 6); -static SENSOR_DEVICE_ATTR_RW(fan2_beep, beep, 7); -static SENSOR_DEVICE_ATTR_RW(fan3_beep, beep, 11); -static SENSOR_DEVICE_ATTR_RW(temp1_beep, beep, 4); -static SENSOR_DEVICE_ATTR_RW(temp2_beep, beep, 5); -static SENSOR_DEVICE_ATTR_RW(temp3_beep, beep, 13); -static SENSOR_DEVICE_ATTR_RW(beep_enable, beep, 15); - -static ssize_t -fan_div_show(struct device *dev, struct device_attribute *devattr, char *buf) +temp_max_show(struct device *dev, struct device_attribute *devattr, char *buf) { int nr = to_sensor_dev_attr(devattr)->index; struct w83627hf_data *data = w83627hf_update_device(dev); - return sprintf(buf, "%ld\n", - (long) DIV_FROM_REG(data->fan_div[nr])); -} -/* - * Note: we save and restore the fan minimum here, because its value is - * determined in part by the fan divisor. This follows the principle of - * least surprise; the user doesn't expect the fan minimum to change just - * because the divisor changed. - */ -static ssize_t -fan_div_store(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) -{ - int nr = to_sensor_dev_attr(devattr)->index; - struct w83627hf_data *data = dev_get_drvdata(dev); - unsigned long min; - u8 reg; - unsigned long val; - int err; - - err = kstrtoul(buf, 10, &val); - if (err) - return err; - - mutex_lock(&data->update_lock); - - /* Save fan_min */ - min = FAN_FROM_REG(data->fan_min[nr], - DIV_FROM_REG(data->fan_div[nr])); - - data->fan_div[nr] = DIV_TO_REG(val); - - reg = (w83627hf_read_value(data, nr==2 ? W83781D_REG_PIN : W83781D_REG_VID_FANDIV) - & (nr==0 ? 0xcf : 0x3f)) - | ((data->fan_div[nr] & 0x03) << (nr==0 ? 4 : 6)); - w83627hf_write_value(data, nr==2 ? W83781D_REG_PIN : W83781D_REG_VID_FANDIV, reg); - reg = (w83627hf_read_value(data, W83781D_REG_VBAT) - & ~(1 << (5 + nr))) - | ((data->fan_div[nr] & 0x04) << (3 + nr)); - w83627hf_write_value(data, W83781D_REG_VBAT, reg); - - /* Restore fan_min */ - data->fan_min[nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr])); - w83627hf_write_value(data, W83627HF_REG_FAN_MIN(nr), data->fan_min[nr]); - - mutex_unlock(&data->update_lock); - return count; -} - -static SENSOR_DEVICE_ATTR_RW(fan1_div, fan_div, 0); -static SENSOR_DEVICE_ATTR_RW(fan2_div, fan_div, 1); -static SENSOR_DEVICE_ATTR_RW(fan3_div, fan_div, 2); - -static ssize_t -pwm_show(struct device *dev, struct device_attribute *devattr, char *buf) -{ - int nr = to_sensor_dev_attr(devattr)->index; - struct w83627hf_data *data = w83627hf_update_device(dev); - return sprintf(buf, "%ld\n", (long) data->pwm[nr]); + u16 tmp = data->temp_max[nr]; + return sprintf(buf, "%ld\n", (nr) ? (long) LM75_TEMP_FROM_REG(tmp) + : (long) TEMP_FROM_REG(tmp)); } static ssize_t -pwm_store(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) +temp_max_store(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) { int nr = to_sensor_dev_attr(devattr)->index; struct w83627hf_data *data = dev_get_drvdata(dev); - unsigned long val; + u16 tmp; + long val; int err; - err = kstrtoul(buf, 10, &val); + err = kstrtol(buf, 10, &val); if (err) return err; + tmp = (nr) ? LM75_TEMP_TO_REG(val) : TEMP_TO_REG(val); mutex_lock(&data->update_lock); - - if (data->type == w83627thf) { - /* bits 0-3 are reserved in 627THF */ - data->pwm[nr] = PWM_TO_REG(val) & 0xf0; - w83627hf_write_value(data, - W836X7HF_REG_PWM(data->type, nr), - data->pwm[nr] | - (w83627hf_read_value(data, - W836X7HF_REG_PWM(data->type, nr)) & 0x0f)); - } else { - data->pwm[nr] = PWM_TO_REG(val); - w83627hf_write_value(data, - W836X7HF_REG_PWM(data->type, nr), - data->pwm[nr]); - } - + data->temp_max[nr] = tmp; + w83627hf_write_value(data, w83627hf_reg_temp_over[nr], tmp); mutex_unlock(&data->update_lock); return count; } -static SENSOR_DEVICE_ATTR_RW(pwm1, pwm, 0); -static SENSOR_DEVICE_ATTR_RW(pwm2, pwm, 1); -static SENSOR_DEVICE_ATTR_RW(pwm3, pwm, 2); - static ssize_t -pwm_enable_show(struct device *dev, struct device_attribute *devattr, - char *buf) +temp_max_hyst_show(struct device *dev, struct device_attribute *devattr, + char *buf) { int nr = to_sensor_dev_attr(devattr)->index; struct w83627hf_data *data = w83627hf_update_device(dev); - return sprintf(buf, "%d\n", data->pwm_enable[nr]); -} -static ssize_t -pwm_enable_store(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) -{ - int nr = to_sensor_dev_attr(devattr)->index; - struct w83627hf_data *data = dev_get_drvdata(dev); - u8 reg; - unsigned long val; - int err; - - err = kstrtoul(buf, 10, &val); - if (err) - return err; - - if (!val || val > 3) /* modes 1, 2 and 3 are supported */ - return -EINVAL; - mutex_lock(&data->update_lock); - data->pwm_enable[nr] = val; - reg = w83627hf_read_value(data, W83627THF_REG_PWM_ENABLE[nr]); - reg &= ~(0x03 << W83627THF_PWM_ENABLE_SHIFT[nr]); - reg |= (val - 1) << W83627THF_PWM_ENABLE_SHIFT[nr]; - w83627hf_write_value(data, W83627THF_REG_PWM_ENABLE[nr], reg); - mutex_unlock(&data->update_lock); - return count; -} - -static SENSOR_DEVICE_ATTR_RW(pwm1_enable, pwm_enable, 0); -static SENSOR_DEVICE_ATTR_RW(pwm2_enable, pwm_enable, 1); -static SENSOR_DEVICE_ATTR_RW(pwm3_enable, pwm_enable, 2); - -static ssize_t -pwm_freq_show(struct device *dev, struct device_attribute *devattr, char *buf) -{ - int nr = to_sensor_dev_attr(devattr)->index; - struct w83627hf_data *data = w83627hf_update_device(dev); - if (data->type == w83627hf) - return sprintf(buf, "%ld\n", - pwm_freq_from_reg_627hf(data->pwm_freq[nr])); - else - return sprintf(buf, "%ld\n", - pwm_freq_from_reg(data->pwm_freq[nr])); + u16 tmp = data->temp_max_hyst[nr]; + return sprintf(buf, "%ld\n", (nr) ? (long) LM75_TEMP_FROM_REG(tmp) + : (long) TEMP_FROM_REG(tmp)); } static ssize_t -pwm_freq_store(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) +temp_max_hyst_store(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) { int nr = to_sensor_dev_attr(devattr)->index; struct w83627hf_data *data = dev_get_drvdata(dev); - static const u8 mask[]={0xF8, 0x8F}; - unsigned long val; + u16 tmp; + long val; int err; - err = kstrtoul(buf, 10, &val); + err = kstrtol(buf, 10, &val); if (err) return err; + tmp = (nr) ? LM75_TEMP_TO_REG(val) : TEMP_TO_REG(val); mutex_lock(&data->update_lock); - - if (data->type == w83627hf) { - data->pwm_freq[nr] = pwm_freq_to_reg_627hf(val); - w83627hf_write_value(data, W83627HF_REG_PWM_FREQ, - (data->pwm_freq[nr] << (nr*4)) | - (w83627hf_read_value(data, - W83627HF_REG_PWM_FREQ) & mask[nr])); - } else { - data->pwm_freq[nr] = pwm_freq_to_reg(val); - w83627hf_write_value(data, W83637HF_REG_PWM_FREQ[nr], - data->pwm_freq[nr]); - } - + data->temp_max_hyst[nr] = tmp; + w83627hf_write_value(data, w83627hf_reg_temp_hyst[nr], tmp); mutex_unlock(&data->update_lock); return count; } -static SENSOR_DEVICE_ATTR_RW(pwm1_freq, pwm_freq, 0); -static SENSOR_DEVICE_ATTR_RW(pwm2_freq, pwm_freq, 1); -static SENSOR_DEVICE_ATTR_RW(pwm3_freq, pwm_freq, 2); +static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, 0); +static SENSOR_DEVICE_ATTR_RW(temp1_max, temp_max, 0); +static SENSOR_DEVICE_ATTR_RW(temp1_max_hyst, temp_max_hyst, 0); +static SENSOR_DEVICE_ATTR_RO(temp2_input, temp, 1); +static SENSOR_DEVICE_ATTR_RW(temp2_max, temp_max, 1); +static SENSOR_DEVICE_ATTR_RW(temp2_max_hyst, temp_max_hyst, 1); +static SENSOR_DEVICE_ATTR_RO(temp3_input, temp, 2); +static SENSOR_DEVICE_ATTR_RW(temp3_max, temp_max, 2); +static SENSOR_DEVICE_ATTR_RW(temp3_max_hyst, temp_max_hyst, 2); static ssize_t temp_type_show(struct device *dev, struct device_attribute *devattr, @@ -1236,81 +1358,12 @@ static SENSOR_DEVICE_ATTR_RW(temp2_type, temp_type, 1); static SENSOR_DEVICE_ATTR_RW(temp3_type, temp_type, 2); static ssize_t -name_show(struct device *dev, struct device_attribute *devattr, char *buf) +alarms_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct w83627hf_data *data = dev_get_drvdata(dev); - - return sprintf(buf, "%s\n", data->name); -} -static DEVICE_ATTR_RO(name); - -static int __init w83627hf_find(int sioaddr, unsigned short *addr, - struct w83627hf_sio_data *sio_data) -{ - int err; - u16 val; - - static __initconst char *const names[] = { - "W83627HF", - "W83627THF", - "W83697HF", - "W83637HF", - "W83687THF", - }; - - sio_data->sioaddr = sioaddr; - err = superio_enter(sio_data); - if (err) - return err; - - err = -ENODEV; - val = force_id ? force_id : superio_inb(sio_data, DEVID); - switch (val) { - case W627_DEVID: - sio_data->type = w83627hf; - break; - case W627THF_DEVID: - sio_data->type = w83627thf; - break; - case W697_DEVID: - sio_data->type = w83697hf; - break; - case W637_DEVID: - sio_data->type = w83637hf; - break; - case W687THF_DEVID: - sio_data->type = w83687thf; - break; - case 0xff: /* No device at all */ - goto exit; - default: - pr_debug(DRVNAME ": Unsupported chip (DEVID=0x%02x)\n", val); - goto exit; - } - - superio_select(sio_data, W83627HF_LD_HWM); - val = (superio_inb(sio_data, WINB_BASE_REG) << 8) | - superio_inb(sio_data, WINB_BASE_REG + 1); - *addr = val & WINB_ALIGNMENT; - if (*addr == 0) { - pr_warn("Base address not set, skipping\n"); - goto exit; - } - - val = superio_inb(sio_data, WINB_ACT_REG); - if (!(val & 0x01)) { - pr_warn("Enabling HWM logical device\n"); - superio_outb(sio_data, WINB_ACT_REG, val | 0x01); - } - - err = 0; - pr_info(DRVNAME ": Found %s chip at %#x\n", - names[sio_data->type], *addr); - - exit: - superio_exit(sio_data); - return err; + struct w83627hf_data *data = w83627hf_update_device(dev); + return sprintf(buf, "%ld\n", (long) data->alarms); } +static DEVICE_ATTR_RO(alarms); #define VIN_UNIT_ATTRS(_X_) \ &sensor_dev_attr_in##_X_##_input.dev_attr.attr, \ @@ -1334,6 +1387,100 @@ static int __init w83627hf_find(int sioaddr, unsigned short *addr, &sensor_dev_attr_temp##_X_##_alarm.dev_attr.attr, \ &sensor_dev_attr_temp##_X_##_beep.dev_attr.attr +static ssize_t +beep_mask_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct w83627hf_data *data = w83627hf_update_device(dev); + return sprintf(buf, "%ld\n", + (long)BEEP_MASK_FROM_REG(data->beep_mask)); +} + +static ssize_t +beep_mask_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct w83627hf_data *data = dev_get_drvdata(dev); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + + mutex_lock(&data->update_lock); + + /* preserve beep enable */ + data->beep_mask = (data->beep_mask & 0x8000) + | BEEP_MASK_TO_REG(val); + w83627hf_write_value(data, W83781D_REG_BEEP_INTS1, + data->beep_mask & 0xff); + w83627hf_write_value(data, W83781D_REG_BEEP_INTS3, + ((data->beep_mask) >> 16) & 0xff); + w83627hf_write_value(data, W83781D_REG_BEEP_INTS2, + (data->beep_mask >> 8) & 0xff); + + mutex_unlock(&data->update_lock); + return count; +} + +static DEVICE_ATTR_RW(beep_mask); + +static ssize_t +pwm_show(struct device *dev, struct device_attribute *devattr, char *buf) +{ + int nr = to_sensor_dev_attr(devattr)->index; + struct w83627hf_data *data = w83627hf_update_device(dev); + return sprintf(buf, "%ld\n", (long) data->pwm[nr]); +} + +static ssize_t +pwm_store(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + int nr = to_sensor_dev_attr(devattr)->index; + struct w83627hf_data *data = dev_get_drvdata(dev); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + + mutex_lock(&data->update_lock); + + if (data->type == w83627thf) { + /* bits 0-3 are reserved in 627THF */ + data->pwm[nr] = PWM_TO_REG(val) & 0xf0; + w83627hf_write_value(data, + W836X7HF_REG_PWM(data->type, nr), + data->pwm[nr] | + (w83627hf_read_value(data, + W836X7HF_REG_PWM(data->type, nr)) & 0x0f)); + } else { + data->pwm[nr] = PWM_TO_REG(val); + w83627hf_write_value(data, + W836X7HF_REG_PWM(data->type, nr), + data->pwm[nr]); + } + + mutex_unlock(&data->update_lock); + return count; +} + +static SENSOR_DEVICE_ATTR_RW(pwm1, pwm, 0); +static SENSOR_DEVICE_ATTR_RW(pwm2, pwm, 1); +static SENSOR_DEVICE_ATTR_RW(pwm3, pwm, 2); + +static ssize_t +name_show(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct w83627hf_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", data->name); +} + +static DEVICE_ATTR_RO(name); + static struct attribute *w83627hf_attributes[] = { &dev_attr_in0_input.attr, &dev_attr_in0_min.attr, @@ -1366,60 +1513,185 @@ static const struct attribute_group w83627hf_group = { .attrs = w83627hf_attributes, }; -static struct attribute *w83627hf_attributes_opt[] = { - VIN_UNIT_ATTRS(1), - VIN_UNIT_ATTRS(5), - VIN_UNIT_ATTRS(6), +static ssize_t +pwm_freq_show(struct device *dev, struct device_attribute *devattr, char *buf) +{ + int nr = to_sensor_dev_attr(devattr)->index; + struct w83627hf_data *data = w83627hf_update_device(dev); + if (data->type == w83627hf) + return sprintf(buf, "%ld\n", + pwm_freq_from_reg_627hf(data->pwm_freq[nr])); + else + return sprintf(buf, "%ld\n", + pwm_freq_from_reg(data->pwm_freq[nr])); +} - FAN_UNIT_ATTRS(3), - TEMP_UNIT_ATTRS(3), - &sensor_dev_attr_pwm3.dev_attr.attr, +static ssize_t +pwm_freq_store(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + int nr = to_sensor_dev_attr(devattr)->index; + struct w83627hf_data *data = dev_get_drvdata(dev); + static const u8 mask[]={0xF8, 0x8F}; + unsigned long val; + int err; - &sensor_dev_attr_pwm1_freq.dev_attr.attr, - &sensor_dev_attr_pwm2_freq.dev_attr.attr, - &sensor_dev_attr_pwm3_freq.dev_attr.attr, + err = kstrtoul(buf, 10, &val); + if (err) + return err; - &sensor_dev_attr_pwm1_enable.dev_attr.attr, - &sensor_dev_attr_pwm2_enable.dev_attr.attr, - &sensor_dev_attr_pwm3_enable.dev_attr.attr, + mutex_lock(&data->update_lock); - NULL -}; + if (data->type == w83627hf) { + data->pwm_freq[nr] = pwm_freq_to_reg_627hf(val); + w83627hf_write_value(data, W83627HF_REG_PWM_FREQ, + (data->pwm_freq[nr] << (nr*4)) | + (w83627hf_read_value(data, + W83627HF_REG_PWM_FREQ) & mask[nr])); + } else { + data->pwm_freq[nr] = pwm_freq_to_reg(val); + w83627hf_write_value(data, W83637HF_REG_PWM_FREQ[nr], + data->pwm_freq[nr]); + } -static const struct attribute_group w83627hf_group_opt = { - .attrs = w83627hf_attributes_opt, -}; + mutex_unlock(&data->update_lock); + return count; +} -static int w83627hf_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct w83627hf_sio_data *sio_data = dev_get_platdata(dev); - struct w83627hf_data *data; - struct resource *res; - int err, i; +static SENSOR_DEVICE_ATTR_RW(pwm1_freq, pwm_freq, 0); +static SENSOR_DEVICE_ATTR_RW(pwm2_freq, pwm_freq, 1); +static SENSOR_DEVICE_ATTR_RW(pwm3_freq, pwm_freq, 2); - static const char *names[] = { - "w83627hf", - "w83627thf", - "w83697hf", - "w83637hf", - "w83687thf", - }; +static ssize_t +cpu0_vid_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct w83627hf_data *data = w83627hf_update_device(dev); + return sprintf(buf, "%ld\n", (long) vid_from_reg(data->vid, data->vrm)); +} - res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (!devm_request_region(dev, res->start, WINB_REGION_SIZE, DRVNAME)) { - dev_err(dev, "Failed to request region 0x%lx-0x%lx\n", - (unsigned long)res->start, - (unsigned long)(res->start + WINB_REGION_SIZE - 1)); - return -EBUSY; - } +static DEVICE_ATTR_RO(cpu0_vid); - data = devm_kzalloc(dev, sizeof(struct w83627hf_data), GFP_KERNEL); - if (!data) - return -ENOMEM; +static ssize_t +vrm_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct w83627hf_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%ld\n", (long) data->vrm); +} - data->addr = res->start; - data->type = sio_data->type; +static ssize_t +vrm_store(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + struct w83627hf_data *data = dev_get_drvdata(dev); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + + if (val > 255) + return -EINVAL; + data->vrm = val; + + return count; +} + +static DEVICE_ATTR_RW(vrm); + +static ssize_t +pwm_enable_show(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + int nr = to_sensor_dev_attr(devattr)->index; + struct w83627hf_data *data = w83627hf_update_device(dev); + return sprintf(buf, "%d\n", data->pwm_enable[nr]); +} + +static ssize_t +pwm_enable_store(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + int nr = to_sensor_dev_attr(devattr)->index; + struct w83627hf_data *data = dev_get_drvdata(dev); + u8 reg; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + + if (!val || val > 3) /* modes 1, 2 and 3 are supported */ + return -EINVAL; + mutex_lock(&data->update_lock); + data->pwm_enable[nr] = val; + reg = w83627hf_read_value(data, W83627THF_REG_PWM_ENABLE[nr]); + reg &= ~(0x03 << W83627THF_PWM_ENABLE_SHIFT[nr]); + reg |= (val - 1) << W83627THF_PWM_ENABLE_SHIFT[nr]; + w83627hf_write_value(data, W83627THF_REG_PWM_ENABLE[nr], reg); + mutex_unlock(&data->update_lock); + return count; +} + +static SENSOR_DEVICE_ATTR_RW(pwm1_enable, pwm_enable, 0); +static SENSOR_DEVICE_ATTR_RW(pwm2_enable, pwm_enable, 1); +static SENSOR_DEVICE_ATTR_RW(pwm3_enable, pwm_enable, 2); + +static struct attribute *w83627hf_attributes_opt[] = { + VIN_UNIT_ATTRS(1), + VIN_UNIT_ATTRS(5), + VIN_UNIT_ATTRS(6), + + FAN_UNIT_ATTRS(3), + TEMP_UNIT_ATTRS(3), + &sensor_dev_attr_pwm3.dev_attr.attr, + + &sensor_dev_attr_pwm1_freq.dev_attr.attr, + &sensor_dev_attr_pwm2_freq.dev_attr.attr, + &sensor_dev_attr_pwm3_freq.dev_attr.attr, + + &sensor_dev_attr_pwm1_enable.dev_attr.attr, + &sensor_dev_attr_pwm2_enable.dev_attr.attr, + &sensor_dev_attr_pwm3_enable.dev_attr.attr, + + NULL +}; + +static const struct attribute_group w83627hf_group_opt = { + .attrs = w83627hf_attributes_opt, +}; + +static int w83627hf_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct w83627hf_sio_data *sio_data = dev_get_platdata(dev); + struct w83627hf_data *data; + struct resource *res; + int err, i; + + static const char *names[] = { + "w83627hf", + "w83627thf", + "w83697hf", + "w83637hf", + "w83687thf", + }; + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!devm_request_region(dev, res->start, WINB_REGION_SIZE, DRVNAME)) { + dev_err(dev, "Failed to request region 0x%lx-0x%lx\n", + (unsigned long)res->start, + (unsigned long)(res->start + WINB_REGION_SIZE - 1)); + return -EBUSY; + } + + data = devm_kzalloc(dev, sizeof(struct w83627hf_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->addr = res->start; + data->type = sio_data->type; data->name = names[sio_data->type]; mutex_init(&data->lock); mutex_init(&data->update_lock); @@ -1568,349 +1840,81 @@ static int w83627hf_remove(struct platform_device *pdev) return 0; } -/* Registers 0x50-0x5f are banked */ -static inline void w83627hf_set_bank(struct w83627hf_data *data, u16 reg) -{ - if ((reg & 0x00f0) == 0x50) { - outb_p(W83781D_REG_BANK, data->addr + W83781D_ADDR_REG_OFFSET); - outb_p(reg >> 8, data->addr + W83781D_DATA_REG_OFFSET); - } -} - -/* Not strictly necessary, but play it safe for now */ -static inline void w83627hf_reset_bank(struct w83627hf_data *data, u16 reg) -{ - if (reg & 0xff00) { - outb_p(W83781D_REG_BANK, data->addr + W83781D_ADDR_REG_OFFSET); - outb_p(0, data->addr + W83781D_DATA_REG_OFFSET); - } -} - -static int w83627hf_read_value(struct w83627hf_data *data, u16 reg) -{ - int res, word_sized; - - mutex_lock(&data->lock); - word_sized = (((reg & 0xff00) == 0x100) - || ((reg & 0xff00) == 0x200)) - && (((reg & 0x00ff) == 0x50) - || ((reg & 0x00ff) == 0x53) - || ((reg & 0x00ff) == 0x55)); - w83627hf_set_bank(data, reg); - outb_p(reg & 0xff, data->addr + W83781D_ADDR_REG_OFFSET); - res = inb_p(data->addr + W83781D_DATA_REG_OFFSET); - if (word_sized) { - outb_p((reg & 0xff) + 1, - data->addr + W83781D_ADDR_REG_OFFSET); - res = - (res << 8) + inb_p(data->addr + - W83781D_DATA_REG_OFFSET); - } - w83627hf_reset_bank(data, reg); - mutex_unlock(&data->lock); - return res; -} +static struct platform_driver w83627hf_driver = { + .driver = { + .name = DRVNAME, + .pm = W83627HF_DEV_PM_OPS, + }, + .probe = w83627hf_probe, + .remove = w83627hf_remove, +}; -static int w83627thf_read_gpio5(struct platform_device *pdev) +static int __init w83627hf_find(int sioaddr, unsigned short *addr, + struct w83627hf_sio_data *sio_data) { - struct w83627hf_sio_data *sio_data = dev_get_platdata(&pdev->dev); - int res = 0xff, sel; - - if (superio_enter(sio_data)) { - /* - * Some other driver reserved the address space for itself. - * We don't want to fail driver instantiation because of that, - * so display a warning and keep going. - */ - dev_warn(&pdev->dev, - "Can not read VID data: Failed to enable SuperIO access\n"); - return res; - } + int err; + u16 val; - superio_select(sio_data, W83627HF_LD_GPIO5); + static __initconst char *const names[] = { + "W83627HF", + "W83627THF", + "W83697HF", + "W83637HF", + "W83687THF", + }; - res = 0xff; + sio_data->sioaddr = sioaddr; + err = superio_enter(sio_data); + if (err) + return err; - /* Make sure these GPIO pins are enabled */ - if (!(superio_inb(sio_data, W83627THF_GPIO5_EN) & (1<<3))) { - dev_dbg(&pdev->dev, "GPIO5 disabled, no VID function\n"); + err = -ENODEV; + val = force_id ? force_id : superio_inb(sio_data, DEVID); + switch (val) { + case W627_DEVID: + sio_data->type = w83627hf; + break; + case W627THF_DEVID: + sio_data->type = w83627thf; + break; + case W697_DEVID: + sio_data->type = w83697hf; + break; + case W637_DEVID: + sio_data->type = w83637hf; + break; + case W687THF_DEVID: + sio_data->type = w83687thf; + break; + case 0xff: /* No device at all */ goto exit; - } - - /* - * Make sure the pins are configured for input - * There must be at least five (VRM 9), and possibly 6 (VRM 10) - */ - sel = superio_inb(sio_data, W83627THF_GPIO5_IOSR) & 0x3f; - if ((sel & 0x1f) != 0x1f) { - dev_dbg(&pdev->dev, "GPIO5 not configured for VID " - "function\n"); + default: + pr_debug(DRVNAME ": Unsupported chip (DEVID=0x%02x)\n", val); goto exit; } - dev_info(&pdev->dev, "Reading VID from GPIO5\n"); - res = superio_inb(sio_data, W83627THF_GPIO5_DR) & sel; - -exit: - superio_exit(sio_data); - return res; -} - -static int w83687thf_read_vid(struct platform_device *pdev) -{ - struct w83627hf_sio_data *sio_data = dev_get_platdata(&pdev->dev); - int res = 0xff; - - if (superio_enter(sio_data)) { - /* - * Some other driver reserved the address space for itself. - * We don't want to fail driver instantiation because of that, - * so display a warning and keep going. - */ - dev_warn(&pdev->dev, - "Can not read VID data: Failed to enable SuperIO access\n"); - return res; - } - superio_select(sio_data, W83627HF_LD_HWM); - - /* Make sure these GPIO pins are enabled */ - if (!(superio_inb(sio_data, W83687THF_VID_EN) & (1 << 2))) { - dev_dbg(&pdev->dev, "VID disabled, no VID function\n"); + val = (superio_inb(sio_data, WINB_BASE_REG) << 8) | + superio_inb(sio_data, WINB_BASE_REG + 1); + *addr = val & WINB_ALIGNMENT; + if (*addr == 0) { + pr_warn("Base address not set, skipping\n"); goto exit; } - /* Make sure the pins are configured for input */ - if (!(superio_inb(sio_data, W83687THF_VID_CFG) & (1 << 4))) { - dev_dbg(&pdev->dev, "VID configured as output, " - "no VID function\n"); - goto exit; + val = superio_inb(sio_data, WINB_ACT_REG); + if (!(val & 0x01)) { + pr_warn("Enabling HWM logical device\n"); + superio_outb(sio_data, WINB_ACT_REG, val | 0x01); } - res = superio_inb(sio_data, W83687THF_VID_DATA) & 0x3f; + err = 0; + pr_info(DRVNAME ": Found %s chip at %#x\n", + names[sio_data->type], *addr); -exit: + exit: superio_exit(sio_data); - return res; -} - -static int w83627hf_write_value(struct w83627hf_data *data, u16 reg, u16 value) -{ - int word_sized; - - mutex_lock(&data->lock); - word_sized = (((reg & 0xff00) == 0x100) - || ((reg & 0xff00) == 0x200)) - && (((reg & 0x00ff) == 0x53) - || ((reg & 0x00ff) == 0x55)); - w83627hf_set_bank(data, reg); - outb_p(reg & 0xff, data->addr + W83781D_ADDR_REG_OFFSET); - if (word_sized) { - outb_p(value >> 8, - data->addr + W83781D_DATA_REG_OFFSET); - outb_p((reg & 0xff) + 1, - data->addr + W83781D_ADDR_REG_OFFSET); - } - outb_p(value & 0xff, - data->addr + W83781D_DATA_REG_OFFSET); - w83627hf_reset_bank(data, reg); - mutex_unlock(&data->lock); - return 0; -} - -static void w83627hf_init_device(struct platform_device *pdev) -{ - struct w83627hf_data *data = platform_get_drvdata(pdev); - int i; - enum chips type = data->type; - u8 tmp; - - /* Minimize conflicts with other winbond i2c-only clients... */ - /* disable i2c subclients... how to disable main i2c client?? */ - /* force i2c address to relatively uncommon address */ - if (type == w83627hf) { - w83627hf_write_value(data, W83781D_REG_I2C_SUBADDR, 0x89); - w83627hf_write_value(data, W83781D_REG_I2C_ADDR, force_i2c); - } - - /* Read VID only once */ - if (type == w83627hf || type == w83637hf) { - int lo = w83627hf_read_value(data, W83781D_REG_VID_FANDIV); - int hi = w83627hf_read_value(data, W83781D_REG_CHIPID); - data->vid = (lo & 0x0f) | ((hi & 0x01) << 4); - } else if (type == w83627thf) { - data->vid = w83627thf_read_gpio5(pdev); - } else if (type == w83687thf) { - data->vid = w83687thf_read_vid(pdev); - } - - /* Read VRM & OVT Config only once */ - if (type == w83627thf || type == w83637hf || type == w83687thf) { - data->vrm_ovt = - w83627hf_read_value(data, W83627THF_REG_VRM_OVT_CFG); - } - - tmp = w83627hf_read_value(data, W83781D_REG_SCFG1); - for (i = 1; i <= 3; i++) { - if (!(tmp & BIT_SCFG1[i - 1])) { - data->sens[i - 1] = 4; - } else { - if (w83627hf_read_value - (data, - W83781D_REG_SCFG2) & BIT_SCFG2[i - 1]) - data->sens[i - 1] = 1; - else - data->sens[i - 1] = 2; - } - if ((type == w83697hf) && (i == 2)) - break; - } - - if(init) { - /* Enable temp2 */ - tmp = w83627hf_read_value(data, W83627HF_REG_TEMP2_CONFIG); - if (tmp & 0x01) { - dev_warn(&pdev->dev, "Enabling temp2, readings " - "might not make sense\n"); - w83627hf_write_value(data, W83627HF_REG_TEMP2_CONFIG, - tmp & 0xfe); - } - - /* Enable temp3 */ - if (type != w83697hf) { - tmp = w83627hf_read_value(data, - W83627HF_REG_TEMP3_CONFIG); - if (tmp & 0x01) { - dev_warn(&pdev->dev, "Enabling temp3, " - "readings might not make sense\n"); - w83627hf_write_value(data, - W83627HF_REG_TEMP3_CONFIG, tmp & 0xfe); - } - } - } - - /* Start monitoring */ - w83627hf_write_value(data, W83781D_REG_CONFIG, - (w83627hf_read_value(data, - W83781D_REG_CONFIG) & 0xf7) - | 0x01); - - /* Enable VBAT monitoring if needed */ - tmp = w83627hf_read_value(data, W83781D_REG_VBAT); - if (!(tmp & 0x01)) - w83627hf_write_value(data, W83781D_REG_VBAT, tmp | 0x01); -} - -static void w83627hf_update_fan_div(struct w83627hf_data *data) -{ - int reg; - - reg = w83627hf_read_value(data, W83781D_REG_VID_FANDIV); - data->fan_div[0] = (reg >> 4) & 0x03; - data->fan_div[1] = (reg >> 6) & 0x03; - if (data->type != w83697hf) { - data->fan_div[2] = (w83627hf_read_value(data, - W83781D_REG_PIN) >> 6) & 0x03; - } - reg = w83627hf_read_value(data, W83781D_REG_VBAT); - data->fan_div[0] |= (reg >> 3) & 0x04; - data->fan_div[1] |= (reg >> 4) & 0x04; - if (data->type != w83697hf) - data->fan_div[2] |= (reg >> 5) & 0x04; -} - -static struct w83627hf_data *w83627hf_update_device(struct device *dev) -{ - struct w83627hf_data *data = dev_get_drvdata(dev); - int i, num_temps = (data->type == w83697hf) ? 2 : 3; - int num_pwms = (data->type == w83697hf) ? 2 : 3; - - mutex_lock(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ + HZ / 2) - || !data->valid) { - for (i = 0; i <= 8; i++) { - /* skip missing sensors */ - if (((data->type == w83697hf) && (i == 1)) || - ((data->type != w83627hf && data->type != w83697hf) - && (i == 5 || i == 6))) - continue; - data->in[i] = - w83627hf_read_value(data, W83781D_REG_IN(i)); - data->in_min[i] = - w83627hf_read_value(data, - W83781D_REG_IN_MIN(i)); - data->in_max[i] = - w83627hf_read_value(data, - W83781D_REG_IN_MAX(i)); - } - for (i = 0; i <= 2; i++) { - data->fan[i] = - w83627hf_read_value(data, W83627HF_REG_FAN(i)); - data->fan_min[i] = - w83627hf_read_value(data, - W83627HF_REG_FAN_MIN(i)); - } - for (i = 0; i <= 2; i++) { - u8 tmp = w83627hf_read_value(data, - W836X7HF_REG_PWM(data->type, i)); - /* bits 0-3 are reserved in 627THF */ - if (data->type == w83627thf) - tmp &= 0xf0; - data->pwm[i] = tmp; - if (i == 1 && - (data->type == w83627hf || data->type == w83697hf)) - break; - } - if (data->type == w83627hf) { - u8 tmp = w83627hf_read_value(data, - W83627HF_REG_PWM_FREQ); - data->pwm_freq[0] = tmp & 0x07; - data->pwm_freq[1] = (tmp >> 4) & 0x07; - } else if (data->type != w83627thf) { - for (i = 1; i <= 3; i++) { - data->pwm_freq[i - 1] = - w83627hf_read_value(data, - W83637HF_REG_PWM_FREQ[i - 1]); - if (i == 2 && (data->type == w83697hf)) - break; - } - } - if (data->type != w83627hf) { - for (i = 0; i < num_pwms; i++) { - u8 tmp = w83627hf_read_value(data, - W83627THF_REG_PWM_ENABLE[i]); - data->pwm_enable[i] = - ((tmp >> W83627THF_PWM_ENABLE_SHIFT[i]) - & 0x03) + 1; - } - } - for (i = 0; i < num_temps; i++) { - data->temp[i] = w83627hf_read_value( - data, w83627hf_reg_temp[i]); - data->temp_max[i] = w83627hf_read_value( - data, w83627hf_reg_temp_over[i]); - data->temp_max_hyst[i] = w83627hf_read_value( - data, w83627hf_reg_temp_hyst[i]); - } - - w83627hf_update_fan_div(data); - - data->alarms = - w83627hf_read_value(data, W83781D_REG_ALARM1) | - (w83627hf_read_value(data, W83781D_REG_ALARM2) << 8) | - (w83627hf_read_value(data, W83781D_REG_ALARM3) << 16); - i = w83627hf_read_value(data, W83781D_REG_BEEP_INTS2); - data->beep_mask = (i << 8) | - w83627hf_read_value(data, W83781D_REG_BEEP_INTS1) | - w83627hf_read_value(data, W83781D_REG_BEEP_INTS3) << 16; - data->last_updated = jiffies; - data->valid = true; - } - - mutex_unlock(&data->update_lock); - - return data; + return err; } static int __init w83627hf_device_add(unsigned short address, -- cgit v1.2.3 From 1e4aa3e18dac1ef3a07a9d0e2662208644bd93de Mon Sep 17 00:00:00 2001 From: Zeng Heng Date: Tue, 27 Sep 2022 19:43:52 +0800 Subject: hwmon: (nct6683) remove unused variable in nct6683_create_attr_group When enable 'unused-but-set-variable' compile warning option, it would raise warning as below: drivers/hwmon/nct6683.c:415:9: warning: variable 'j' set but not used [-Wunused-but-set-variable] Variable 'j' in nct6683_create_attr_group is unused, so remove it and simplify the 'for' loop. Signed-off-by: Zeng Heng Link: https://lore.kernel.org/r/20220927114352.2498079-1-zengheng4@huawei.com Signed-off-by: Guenter Roeck --- drivers/hwmon/nct6683.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/nct6683.c b/drivers/hwmon/nct6683.c index 6a9f420e7d32..a872f783e9cc 100644 --- a/drivers/hwmon/nct6683.c +++ b/drivers/hwmon/nct6683.c @@ -412,7 +412,7 @@ nct6683_create_attr_group(struct device *dev, struct sensor_device_attr_u *su; struct attribute_group *group; struct attribute **attrs; - int i, j, count; + int i, count; if (repeat <= 0) return ERR_PTR(-EINVAL); @@ -443,7 +443,7 @@ nct6683_create_attr_group(struct device *dev, for (i = 0; i < repeat; i++) { t = tg->templates; - for (j = 0; *t != NULL; j++) { + while (*t) { snprintf(su->name, sizeof(su->name), (*t)->dev_attr.attr.name, tg->base + i); if ((*t)->s2) { -- cgit v1.2.3 From 525dd5aed67a2f4f7278116fb92a24e6a53e2622 Mon Sep 17 00:00:00 2001 From: Oleksandr Shamray Date: Thu, 29 Sep 2022 15:16:42 +0300 Subject: hwmon: (pmbus/mp2888) Fix sensors readouts for MPS Multi-phase mp2888 controller Fix scale factors for reading MPS Multi-phase mp2888 controller. Fixed sensors: - PIN/POUT: based on vendor documentation, set bscale factor 0.5W/LSB - IOUT: based on vendor documentation, set scale factor 0.25 A/LSB Fixes: e4db7719d037 ("hwmon: (pmbus) Add support for MPS Multi-phase mp2888 controller") Signed-off-by: Oleksandr Shamray Reviewed-by: Vadim Pasternak Link: https://lore.kernel.org/r/20220929121642.63051-1-oleksandrs@nvidia.com Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/mp2888.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/pmbus/mp2888.c b/drivers/hwmon/pmbus/mp2888.c index 8ecd4adfef40..24e5194706cf 100644 --- a/drivers/hwmon/pmbus/mp2888.c +++ b/drivers/hwmon/pmbus/mp2888.c @@ -34,7 +34,7 @@ struct mp2888_data { int curr_sense_gain; }; -#define to_mp2888_data(x) container_of(x, struct mp2888_data, info) +#define to_mp2888_data(x) container_of(x, struct mp2888_data, info) static int mp2888_read_byte_data(struct i2c_client *client, int page, int reg) { @@ -109,7 +109,7 @@ mp2888_read_phase(struct i2c_client *client, struct mp2888_data *data, int page, * - Kcs is the DrMOS current sense gain of power stage, which is obtained from the * register MP2888_MFR_VR_CONFIG1, bits 13-12 with the following selection of DrMOS * (data->curr_sense_gain): - * 00b - 5µA/A, 01b - 8.5µA/A, 10b - 9.7µA/A, 11b - 10µA/A. + * 00b - 8.5µA/A, 01b - 9.7µA/A, 1b - 10µA/A, 11b - 5µA/A. * - Rcs is the internal phase current sense resistor. This parameter depends on hardware * assembly. By default it is set to 1kΩ. In case of different assembly, user should * scale this parameter by dividing it by Rcs. @@ -118,10 +118,9 @@ mp2888_read_phase(struct i2c_client *client, struct mp2888_data *data, int page, * because sampling of current occurrence of bit weight has a big deviation, especially for * light load. */ - ret = DIV_ROUND_CLOSEST(ret * 100 - 9800, data->curr_sense_gain); - ret = (data->phase_curr_resolution) ? ret * 2 : ret; + ret = DIV_ROUND_CLOSEST(ret * 200 - 19600, data->curr_sense_gain); /* Scale according to total current resolution. */ - ret = (data->total_curr_resolution) ? ret * 8 : ret * 4; + ret = (data->total_curr_resolution) ? ret * 2 : ret; return ret; } @@ -212,7 +211,7 @@ static int mp2888_read_word_data(struct i2c_client *client, int page, int phase, ret = pmbus_read_word_data(client, page, phase, reg); if (ret < 0) return ret; - ret = data->total_curr_resolution ? ret * 2 : ret; + ret = data->total_curr_resolution ? ret : DIV_ROUND_CLOSEST(ret, 2); break; case PMBUS_POUT_OP_WARN_LIMIT: ret = pmbus_read_word_data(client, page, phase, reg); @@ -223,7 +222,7 @@ static int mp2888_read_word_data(struct i2c_client *client, int page, int phase, * set 1. Actual power is reported with 0.5W or 1W respectively resolution. Scaling * is needed to match both. */ - ret = data->total_curr_resolution ? ret * 4 : ret * 2; + ret = data->total_curr_resolution ? ret * 2 : ret; break; /* * The below registers are not implemented by device or implemented not according to the -- cgit v1.2.3 From 0cf46a653bdae56683fece68dc50340f7520e6c4 Mon Sep 17 00:00:00 2001 From: Wilken Gottwalt Date: Sun, 2 Oct 2022 17:45:53 +0000 Subject: hwmon: (corsair-psu) add USB id of new revision of the HX1000i psu Also updates the documentation accordingly. Signed-off-by: Wilken Gottwalt Link: https://lore.kernel.org/r/YznOUQ7Pijedu0NW@monster.localdomain Signed-off-by: Guenter Roeck --- Documentation/hwmon/corsair-psu.rst | 2 +- drivers/hwmon/corsair-psu.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/hwmon') diff --git a/Documentation/hwmon/corsair-psu.rst b/Documentation/hwmon/corsair-psu.rst index c3a76305c587..3c1b164eb3c0 100644 --- a/Documentation/hwmon/corsair-psu.rst +++ b/Documentation/hwmon/corsair-psu.rst @@ -15,7 +15,7 @@ Supported devices: Corsair HX850i - Corsair HX1000i + Corsair HX1000i (revision 1 and 2) Corsair HX1200i diff --git a/drivers/hwmon/corsair-psu.c b/drivers/hwmon/corsair-psu.c index c99e4c6afc2d..345d883ab044 100644 --- a/drivers/hwmon/corsair-psu.c +++ b/drivers/hwmon/corsair-psu.c @@ -813,13 +813,14 @@ static const struct hid_device_id corsairpsu_idtable[] = { { HID_USB_DEVICE(0x1b1c, 0x1c04) }, /* Corsair HX650i */ { HID_USB_DEVICE(0x1b1c, 0x1c05) }, /* Corsair HX750i */ { HID_USB_DEVICE(0x1b1c, 0x1c06) }, /* Corsair HX850i */ - { HID_USB_DEVICE(0x1b1c, 0x1c07) }, /* Corsair HX1000i */ + { HID_USB_DEVICE(0x1b1c, 0x1c07) }, /* Corsair HX1000i revision 1 */ { HID_USB_DEVICE(0x1b1c, 0x1c08) }, /* Corsair HX1200i */ { HID_USB_DEVICE(0x1b1c, 0x1c09) }, /* Corsair RM550i */ { HID_USB_DEVICE(0x1b1c, 0x1c0a) }, /* Corsair RM650i */ { HID_USB_DEVICE(0x1b1c, 0x1c0b) }, /* Corsair RM750i */ { HID_USB_DEVICE(0x1b1c, 0x1c0c) }, /* Corsair RM850i */ { HID_USB_DEVICE(0x1b1c, 0x1c0d) }, /* Corsair RM1000i */ + { HID_USB_DEVICE(0x1b1c, 0x1c1e) }, /* Corsaur HX1000i revision 2 */ { }, }; MODULE_DEVICE_TABLE(hid, corsairpsu_idtable); -- cgit v1.2.3