diff options
Diffstat (limited to 'drivers/hwmon')
26 files changed, 913 insertions, 624 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 1b1d64493909..9d28fcf7cd2a 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -700,6 +700,16 @@ config SENSORS_MC13783_ADC help Support for the A/D converter on MC13783 and MC13892 PMIC. +config SENSORS_MC33XS2410 + tristate "MC33XS2410 HWMON support" + depends on PWM_MC33XS2410 + help + If you say yes here you get hardware monitoring support for + MC33XS2410. + + This driver can also be built as a module. If so, the module + will be called mc33xs2410_hwmon. + config SENSORS_FSCHMD tristate "Fujitsu Siemens Computers sensor chips" depends on (X86 || COMPILE_TEST) && I2C @@ -1905,16 +1915,6 @@ config SENSORS_SBTSI This driver can also be built as a module. If so, the module will be called sbtsi_temp. -config SENSORS_SBRMI - tristate "Emulated SB-RMI sensor" - depends on I2C - help - If you say yes here you get support for emulated RMI - sensors on AMD SoCs with APML interface connected to a BMC device. - - This driver can also be built as a module. If so, the module will - be called sbrmi. - config SENSORS_SHT15 tristate "Sensiron humidity and temperature sensors. SHT15 and compat." depends on GPIOLIB || COMPILE_TEST diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 48e5866c0c9a..cd8bc4752b4d 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -165,6 +165,7 @@ obj-$(CONFIG_SENSORS_MAX31790) += max31790.o obj-$(CONFIG_MAX31827) += max31827.o obj-$(CONFIG_SENSORS_MAX77705) += max77705-hwmon.o obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o +obj-$(CONFIG_SENSORS_MC33XS2410) += mc33xs2410_hwmon.o obj-$(CONFIG_SENSORS_MC34VR500) += mc34vr500.o obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o obj-$(CONFIG_SENSORS_TC654) += tc654.o diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c index 5f2541c11fe9..8cefa14e1633 100644 --- a/drivers/hwmon/adt7475.c +++ b/drivers/hwmon/adt7475.c @@ -1704,12 +1704,15 @@ static int adt7475_pwm_properties_parse_reference_args(struct fwnode_handle *fwn if (ret) return ret; - if (rargs.nargs != 4) { + if (rargs.nargs != 3 && rargs.nargs != 4) { fwnode_handle_put(rargs.fwnode); return -EINVAL; } - for (i = 0; i < 4; i++) + /* Let duty_cycle default to period */ + args[3] = rargs.args[1]; + + for (i = 0; i < rargs.nargs; i++) args[i] = rargs.args[i]; ret = _adt7475_pwm_properties_parse_args(args, cfg); @@ -1724,11 +1727,22 @@ static int adt7475_pwm_properties_parse_args(struct fwnode_handle *fwnode, { int ret; u32 args[4] = {}; + size_t n_vals = fwnode_property_count_u32(fwnode, "pwms"); + + if (n_vals != 3 && n_vals != 4) + return -EOVERFLOW; - ret = fwnode_property_read_u32_array(fwnode, "pwms", args, ARRAY_SIZE(args)); + ret = fwnode_property_read_u32_array(fwnode, "pwms", args, n_vals); if (ret) return ret; + /* + * If there are no item to define the duty_cycle, default it to the + * period. + */ + if (n_vals == 3) + args[3] = args[1]; + return _adt7475_pwm_properties_parse_args(args, cfg); } diff --git a/drivers/hwmon/amc6821.c b/drivers/hwmon/amc6821.c index 13a789cc85d2..d5f864b360b0 100644 --- a/drivers/hwmon/amc6821.c +++ b/drivers/hwmon/amc6821.c @@ -26,6 +26,7 @@ #include <linux/pwm.h> #include <linux/regmap.h> #include <linux/slab.h> +#include <linux/thermal.h> #include <dt-bindings/pwm/pwm.h> @@ -126,6 +127,10 @@ module_param(init, int, 0444); struct amc6821_data { struct regmap *regmap; struct mutex update_lock; + unsigned long fan_state; + unsigned long fan_max_state; + unsigned int *fan_cooling_levels; + enum pwm_polarity pwm_polarity; }; /* @@ -804,6 +809,65 @@ static const struct hwmon_chip_info amc6821_chip_info = { .info = amc6821_info, }; +static int amc6821_set_sw_dcy(struct amc6821_data *data, u8 duty_cycle) +{ + int ret; + + ret = regmap_write(data->regmap, AMC6821_REG_DCY, duty_cycle); + if (ret) + return ret; + + return regmap_update_bits(data->regmap, AMC6821_REG_CONF1, + AMC6821_CONF1_FDRC0 | AMC6821_CONF1_FDRC1, 0); +} + +static int amc6821_get_max_state(struct thermal_cooling_device *cdev, unsigned long *state) +{ + struct amc6821_data *data = cdev->devdata; + + if (!data) + return -EINVAL; + + *state = data->fan_max_state; + + return 0; +} + +static int amc6821_get_cur_state(struct thermal_cooling_device *cdev, unsigned long *state) +{ + struct amc6821_data *data = cdev->devdata; + + if (!data) + return -EINVAL; + + *state = data->fan_state; + + return 0; +} + +static int amc6821_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) +{ + struct amc6821_data *data = cdev->devdata; + int ret; + + if (!data || state > data->fan_max_state) + return -EINVAL; + + ret = amc6821_set_sw_dcy(data, data->fan_cooling_levels[state]); + if (ret) + return ret; + + data->fan_state = state; + + return 0; +} + +static const struct thermal_cooling_device_ops amc6821_cooling_ops = { + .get_max_state = amc6821_get_max_state, + .get_cur_state = amc6821_get_cur_state, + .set_cur_state = amc6821_set_cur_state, +}; + /* Return 0 if detection is successful, -ENODEV otherwise */ static int amc6821_detect(struct i2c_client *client, struct i2c_board_info *info) { @@ -848,11 +912,11 @@ static int amc6821_detect(struct i2c_client *client, struct i2c_board_info *info return 0; } -static enum pwm_polarity amc6821_pwm_polarity(struct i2c_client *client) +static enum pwm_polarity amc6821_pwm_polarity(struct i2c_client *client, + struct device_node *fan_np) { enum pwm_polarity polarity = PWM_POLARITY_NORMAL; struct of_phandle_args args; - struct device_node *fan_np; /* * For backward compatibility, the pwminv module parameter takes @@ -863,10 +927,6 @@ static enum pwm_polarity amc6821_pwm_polarity(struct i2c_client *client) if (pwminv > 0) return PWM_POLARITY_INVERSED; - fan_np = of_get_child_by_name(client->dev.of_node, "fan"); - if (!fan_np) - return PWM_POLARITY_NORMAL; - if (of_parse_phandle_with_args(fan_np, "pwms", "#pwm-cells", 0, &args)) goto out; of_node_put(args.np); @@ -877,10 +937,34 @@ static enum pwm_polarity amc6821_pwm_polarity(struct i2c_client *client) if (args.args[1] & PWM_POLARITY_INVERTED) polarity = PWM_POLARITY_INVERSED; out: - of_node_put(fan_np); return polarity; } +static int amc6821_of_fan_read_data(struct i2c_client *client, + struct amc6821_data *data, + struct device_node *fan_np) +{ + int num; + + data->pwm_polarity = amc6821_pwm_polarity(client, fan_np); + + num = of_property_count_u32_elems(fan_np, "cooling-levels"); + if (num <= 0) + return 0; + + data->fan_max_state = num - 1; + + data->fan_cooling_levels = devm_kcalloc(&client->dev, num, + sizeof(u32), + GFP_KERNEL); + + if (!data->fan_cooling_levels) + return -ENOMEM; + + return of_property_read_u32_array(fan_np, "cooling-levels", + data->fan_cooling_levels, num); +} + static int amc6821_init_client(struct i2c_client *client, struct amc6821_data *data) { struct regmap *regmap = data->regmap; @@ -902,7 +986,7 @@ static int amc6821_init_client(struct i2c_client *client, struct amc6821_data *d return err; regval = AMC6821_CONF1_START; - if (amc6821_pwm_polarity(client) == PWM_POLARITY_INVERSED) + if (data->pwm_polarity == PWM_POLARITY_INVERSED) regval |= AMC6821_CONF1_PWMINV; err = regmap_update_bits(regmap, AMC6821_REG_CONF1, @@ -911,6 +995,14 @@ static int amc6821_init_client(struct i2c_client *client, struct amc6821_data *d regval); if (err) return err; + + /* Software DCY-control mode with fan enabled when cooling-levels present */ + if (data->fan_cooling_levels) { + err = amc6821_set_sw_dcy(data, + data->fan_cooling_levels[data->fan_max_state]); + if (err) + return err; + } } return 0; } @@ -944,6 +1036,7 @@ static int amc6821_probe(struct i2c_client *client) struct amc6821_data *data; struct device *hwmon_dev; struct regmap *regmap; + struct device_node *fan_np __free(device_node) = NULL; int err; data = devm_kzalloc(dev, sizeof(struct amc6821_data), GFP_KERNEL); @@ -956,6 +1049,14 @@ static int amc6821_probe(struct i2c_client *client) "Failed to initialize regmap\n"); data->regmap = regmap; + fan_np = of_get_child_by_name(dev->of_node, "fan"); + if (fan_np) { + err = amc6821_of_fan_read_data(client, data, fan_np); + if (err) + return dev_err_probe(dev, err, + "Failed to read fan device tree data\n"); + } + err = amc6821_init_client(client, data); if (err) return err; @@ -970,7 +1071,15 @@ static int amc6821_probe(struct i2c_client *client) hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, &amc6821_chip_info, amc6821_groups); - return PTR_ERR_OR_ZERO(hwmon_dev); + if (IS_ERR(hwmon_dev)) + return dev_err_probe(dev, PTR_ERR(hwmon_dev), + "Failed to initialize hwmon\n"); + + if (IS_ENABLED(CONFIG_THERMAL) && fan_np && data->fan_cooling_levels) + return PTR_ERR_OR_ZERO(devm_thermal_of_cooling_device_register(dev, + fan_np, client->name, data, &amc6821_cooling_ops)); + + return 0; } static const struct i2c_device_id amc6821_id[] = { diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c index e0a95197c71b..4ac554731e98 100644 --- a/drivers/hwmon/asus-ec-sensors.c +++ b/drivers/hwmon/asus-ec-sensors.c @@ -165,7 +165,9 @@ enum board_family { family_amd_400_series, family_amd_500_series, family_amd_600_series, + family_amd_800_series, family_intel_300_series, + family_intel_400_series, family_intel_600_series }; @@ -259,6 +261,20 @@ static const struct ec_sensor_info sensors_family_amd_600[] = { EC_SENSOR("Water_Out", hwmon_temp, 1, 0x01, 0x01), }; +static const struct ec_sensor_info sensors_family_amd_800[] = { + [ec_sensor_temp_cpu] = EC_SENSOR("CPU", hwmon_temp, 1, 0x00, 0x30), + [ec_sensor_temp_cpu_package] = + EC_SENSOR("CPU Package", hwmon_temp, 1, 0x00, 0x31), + [ec_sensor_temp_mb] = + EC_SENSOR("Motherboard", hwmon_temp, 1, 0x00, 0x32), + [ec_sensor_temp_vrm] = + EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x33), + [ec_sensor_temp_t_sensor] = + EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x36), + [ec_sensor_fan_cpu_opt] = + EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xb0), +}; + static const struct ec_sensor_info sensors_family_intel_300[] = { [ec_sensor_temp_chipset] = EC_SENSOR("Chipset", hwmon_temp, 1, 0x00, 0x3a), @@ -279,6 +295,20 @@ static const struct ec_sensor_info sensors_family_intel_300[] = { EC_SENSOR("Water_Out", hwmon_temp, 1, 0x01, 0x01), }; +static const struct ec_sensor_info sensors_family_intel_400[] = { + [ec_sensor_temp_chipset] = + EC_SENSOR("Chipset", hwmon_temp, 1, 0x00, 0x3a), + [ec_sensor_temp_cpu] = EC_SENSOR("CPU", hwmon_temp, 1, 0x00, 0x3b), + [ec_sensor_temp_mb] = + EC_SENSOR("Motherboard", hwmon_temp, 1, 0x00, 0x3c), + [ec_sensor_temp_t_sensor] = + EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x3d), + [ec_sensor_temp_vrm] = EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x3e), + [ec_sensor_fan_cpu_opt] = + EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xb0), + [ec_sensor_fan_vrm_hs] = EC_SENSOR("VRM HS", hwmon_fan, 2, 0x00, 0xb2), +}; + static const struct ec_sensor_info sensors_family_intel_600[] = { [ec_sensor_temp_t_sensor] = EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x3d), @@ -362,6 +392,14 @@ static const struct ec_board_info board_info_pro_art_x670E_creator_wifi = { .family = family_amd_600_series, }; +static const struct ec_board_info board_info_pro_art_x870E_creator_wifi = { + .sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE | + SENSOR_TEMP_MB | SENSOR_TEMP_VRM | + SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CPU_OPT, + .mutex_path = ACPI_GLOBAL_LOCK_PSEUDO_PATH, + .family = family_amd_800_series, +}; + static const struct ec_board_info board_info_pro_art_b550_creator = { .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_T_SENSOR | @@ -498,6 +536,18 @@ static const struct ec_board_info board_info_strix_z390_f_gaming = { .family = family_intel_300_series, }; +static const struct ec_board_info board_info_strix_z490_f_gaming = { + .sensors = SENSOR_TEMP_CHIPSET | + SENSOR_TEMP_CPU | + SENSOR_TEMP_MB | + SENSOR_TEMP_T_SENSOR | + SENSOR_TEMP_VRM | + SENSOR_FAN_CPU_OPT | + SENSOR_FAN_VRM_HS, + .mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX, + .family = family_intel_400_series, +}; + static const struct ec_board_info board_info_strix_z690_a_gaming_wifi_d4 = { .sensors = SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM, .mutex_path = ASUS_HW_ACCESS_MUTEX_RMTW_ASMX, @@ -548,6 +598,8 @@ static const struct dmi_system_id dmi_table[] = { &board_info_pro_art_x570_creator_wifi), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ProArt X670E-CREATOR WIFI", &board_info_pro_art_x670E_creator_wifi), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ProArt X870E-CREATOR WIFI", + &board_info_pro_art_x870E_creator_wifi), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ProArt B550-CREATOR", &board_info_pro_art_b550_creator), DMI_EXACT_MATCH_ASUS_BOARD_NAME("Pro WS X570-ACE", @@ -586,6 +638,8 @@ static const struct dmi_system_id dmi_table[] = { &board_info_strix_x570_i_gaming), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX Z390-F GAMING", &board_info_strix_z390_f_gaming), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX Z490-F GAMING", + &board_info_strix_z490_f_gaming), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX Z690-A GAMING WIFI D4", &board_info_strix_z690_a_gaming_wifi_d4), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG ZENITH II EXTREME", @@ -1058,9 +1112,15 @@ static int asus_ec_probe(struct platform_device *pdev) case family_amd_600_series: ec_data->sensors_info = sensors_family_amd_600; break; + case family_amd_800_series: + ec_data->sensors_info = sensors_family_amd_800; + break; case family_intel_300_series: ec_data->sensors_info = sensors_family_intel_300; break; + case family_intel_400_series: + ec_data->sensors_info = sensors_family_intel_400; + break; case family_intel_600_series: ec_data->sensors_info = sensors_family_intel_600; break; diff --git a/drivers/hwmon/axi-fan-control.c b/drivers/hwmon/axi-fan-control.c index 35c862eb158b..b7bb325c3ad9 100644 --- a/drivers/hwmon/axi-fan-control.c +++ b/drivers/hwmon/axi-fan-control.c @@ -4,9 +4,9 @@ * * Copyright 2019 Analog Devices Inc. */ +#include <linux/adi-axi-common.h> #include <linux/bits.h> #include <linux/clk.h> -#include <linux/fpga/adi-axi-common.h> #include <linux/hwmon.h> #include <linux/hwmon-sysfs.h> #include <linux/interrupt.h> diff --git a/drivers/hwmon/corsair-cpro.c b/drivers/hwmon/corsair-cpro.c index e1a7f7aa7f80..b7b911f8359c 100644 --- a/drivers/hwmon/corsair-cpro.c +++ b/drivers/hwmon/corsair-cpro.c @@ -89,6 +89,7 @@ struct ccp_device { struct mutex mutex; /* whenever buffer is used, lock before send_usb_cmd */ u8 *cmd_buffer; u8 *buffer; + int buffer_recv_size; /* number of received bytes in buffer */ int target[6]; DECLARE_BITMAP(temp_cnct, NUM_TEMP_SENSORS); DECLARE_BITMAP(fan_cnct, NUM_FANS); @@ -146,6 +147,9 @@ static int send_usb_cmd(struct ccp_device *ccp, u8 command, u8 byte1, u8 byte2, if (!t) return -ETIMEDOUT; + if (ccp->buffer_recv_size != IN_BUFFER_SIZE) + return -EPROTO; + return ccp_get_errno(ccp); } @@ -157,6 +161,7 @@ static int ccp_raw_event(struct hid_device *hdev, struct hid_report *report, u8 spin_lock(&ccp->wait_input_report_lock); if (!completion_done(&ccp->wait_input_report)) { memcpy(ccp->buffer, data, min(IN_BUFFER_SIZE, size)); + ccp->buffer_recv_size = size; complete_all(&ccp->wait_input_report); } spin_unlock(&ccp->wait_input_report_lock); diff --git a/drivers/hwmon/corsair-psu.c b/drivers/hwmon/corsair-psu.c index f8f22b8a67cd..6b5c8f200780 100644 --- a/drivers/hwmon/corsair-psu.c +++ b/drivers/hwmon/corsair-psu.c @@ -885,6 +885,7 @@ static const struct hid_device_id corsairpsu_idtable[] = { { HID_USB_DEVICE(0x1b1c, 0x1c1e) }, /* Corsair HX1000i Series 2023 */ { HID_USB_DEVICE(0x1b1c, 0x1c1f) }, /* Corsair HX1500i Legacy and Series 2023 */ { HID_USB_DEVICE(0x1b1c, 0x1c23) }, /* Corsair HX1200i Series 2023 */ + { HID_USB_DEVICE(0x1b1c, 0x1c27) }, /* Corsair HX1200i Series 2025 */ { }, }; MODULE_DEVICE_TABLE(hid, corsairpsu_idtable); diff --git a/drivers/hwmon/emc2305.c b/drivers/hwmon/emc2305.c index 234c54956a4b..60809289f816 100644 --- a/drivers/hwmon/emc2305.c +++ b/drivers/hwmon/emc2305.c @@ -11,6 +11,9 @@ #include <linux/module.h> #include <linux/platform_data/emc2305.h> #include <linux/thermal.h> +#include <linux/pwm.h> +#include <linux/of_device.h> +#include <linux/util_macros.h> #define EMC2305_REG_DRIVE_FAIL_STATUS 0x27 #define EMC2305_REG_VENDOR 0xfe @@ -23,6 +26,12 @@ #define EMC2305_TACH_REGS_UNUSE_BITS 3 #define EMC2305_TACH_CNT_MULTIPLIER 0x02 #define EMC2305_TACH_RANGE_MIN 480 +#define EMC2305_DEFAULT_OUTPUT 0x0 +#define EMC2305_DEFAULT_POLARITY 0x0 +#define EMC2305_REG_POLARITY 0x2a +#define EMC2305_REG_DRIVE_PWM_OUT 0x2b +#define EMC2305_OPEN_DRAIN 0x0 +#define EMC2305_PUSH_PULL 0x1 #define EMC2305_PWM_DUTY2STATE(duty, max_state, pwm_max) \ DIV_ROUND_CLOSEST((duty) * (max_state), (pwm_max)) @@ -39,6 +48,9 @@ #define EMC2305_REG_FAN_MIN_DRIVE(n) (0x38 + 0x10 * (n)) #define EMC2305_REG_FAN_TACH(n) (0x3e + 0x10 * (n)) +/* Supported base PWM frequencies */ +static const unsigned int base_freq_table[] = { 2441, 4882, 19530, 26000 }; + enum emc230x_product_id { EMC2305 = 0x34, EMC2303 = 0x35, @@ -89,8 +101,11 @@ struct emc2305_cdev_data { * @hwmon_dev: hwmon device * @max_state: maximum cooling state of the cooling device * @pwm_num: number of PWM channels + * @pwm_output_mask: PWM output mask + * @pwm_polarity_mask: PWM polarity mask * @pwm_separate: separate PWM settings for every channel * @pwm_min: array of minimum PWM per channel + * @pwm_freq: array of PWM frequency per channel * @cdev_data: array of cooling devices data */ struct emc2305_data { @@ -98,8 +113,11 @@ struct emc2305_data { struct device *hwmon_dev; u8 max_state; u8 pwm_num; + u8 pwm_output_mask; + u8 pwm_polarity_mask; bool pwm_separate; u8 pwm_min[EMC2305_PWM_MAX]; + u16 pwm_freq[EMC2305_PWM_MAX]; struct emc2305_cdev_data cdev_data[EMC2305_PWM_MAX]; }; @@ -281,7 +299,7 @@ static int emc2305_set_pwm(struct device *dev, long val, int channel) return 0; } -static int emc2305_set_single_tz(struct device *dev, int idx) +static int emc2305_set_single_tz(struct device *dev, struct device_node *fan_node, int idx) { struct emc2305_data *data = dev_get_drvdata(dev); long pwm; @@ -291,7 +309,7 @@ static int emc2305_set_single_tz(struct device *dev, int idx) pwm = data->pwm_min[cdev_idx]; data->cdev_data[cdev_idx].cdev = - devm_thermal_of_cooling_device_register(dev, dev->of_node, + devm_thermal_of_cooling_device_register(dev, fan_node, emc2305_fan_name[idx], data, &emc2305_cooling_ops); @@ -299,6 +317,12 @@ static int emc2305_set_single_tz(struct device *dev, int idx) dev_err(dev, "Failed to register cooling device %s\n", emc2305_fan_name[idx]); return PTR_ERR(data->cdev_data[cdev_idx].cdev); } + + if (data->cdev_data[cdev_idx].cur_state > 0) + /* Update pwm when temperature is above trips */ + pwm = EMC2305_PWM_STATE2DUTY(data->cdev_data[cdev_idx].cur_state, + data->max_state, EMC2305_FAN_MAX); + /* Set minimal PWM speed. */ if (data->pwm_separate) { ret = emc2305_set_pwm(dev, pwm, cdev_idx); @@ -312,10 +336,10 @@ static int emc2305_set_single_tz(struct device *dev, int idx) } } data->cdev_data[cdev_idx].cur_state = - EMC2305_PWM_DUTY2STATE(data->pwm_min[cdev_idx], data->max_state, + EMC2305_PWM_DUTY2STATE(pwm, 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_PWM_DUTY2STATE(pwm, data->max_state, EMC2305_FAN_MAX); return 0; } @@ -326,10 +350,10 @@ static int emc2305_set_tz(struct device *dev) int i, ret; if (!data->pwm_separate) - return emc2305_set_single_tz(dev, 0); + return emc2305_set_single_tz(dev, dev->of_node, 0); for (i = 0; i < data->pwm_num; i++) { - ret = emc2305_set_single_tz(dev, i + 1); + ret = emc2305_set_single_tz(dev, dev->of_node, i + 1); if (ret) return ret; } @@ -511,15 +535,85 @@ static int emc2305_identify(struct device *dev) return 0; } +static int emc2305_of_parse_pwm_child(struct device *dev, + struct device_node *child, + struct emc2305_data *data) +{ u32 ch; + int ret; + struct of_phandle_args args; + + ret = of_property_read_u32(child, "reg", &ch); + if (ret) { + dev_err(dev, "missing reg property of %pOFn\n", child); + return ret; + } + + ret = of_parse_phandle_with_args(child, "pwms", "#pwm-cells", 0, &args); + + if (ret) + return ret; + + if (args.args_count > 0) { + data->pwm_freq[ch] = find_closest(args.args[0], base_freq_table, + ARRAY_SIZE(base_freq_table)); + } else { + data->pwm_freq[ch] = base_freq_table[3]; + } + + if (args.args_count > 1) { + if (args.args[1] == PWM_POLARITY_NORMAL || args.args[1] == PWM_POLARITY_INVERSED) + data->pwm_polarity_mask |= args.args[1] << ch; + else + dev_err(dev, "Wrong PWM polarity config provided: %d\n", args.args[0]); + } else { + data->pwm_polarity_mask |= PWM_POLARITY_NORMAL << ch; + } + + if (args.args_count > 2) { + if (args.args[2] == EMC2305_PUSH_PULL || args.args[2] <= EMC2305_OPEN_DRAIN) + data->pwm_output_mask |= args.args[2] << ch; + else + dev_err(dev, "Wrong PWM output config provided: %d\n", args.args[1]); + } else { + data->pwm_output_mask |= EMC2305_OPEN_DRAIN << ch; + } + + return 0; +} + +static int emc2305_probe_childs_from_dt(struct device *dev) +{ + struct emc2305_data *data = dev_get_drvdata(dev); + struct device_node *child; + int ret, count = 0; + + data->pwm_output_mask = 0x0; + data->pwm_polarity_mask = 0x0; + + for_each_child_of_node(dev->of_node, child) { + if (of_property_present(child, "reg")) { + ret = emc2305_of_parse_pwm_child(dev, child, data); + if (ret) { + of_node_put(child); + continue; + } + count++; + } + } + return count; +} + static int emc2305_probe(struct i2c_client *client) { struct i2c_adapter *adapter = client->adapter; struct device *dev = &client->dev; + struct device_node *child; struct emc2305_data *data; struct emc2305_platform_data *pdata; int vendor; int ret; int i; + int pwm_childs; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) return -ENODEV; @@ -539,22 +633,40 @@ static int emc2305_probe(struct i2c_client *client) if (ret) return ret; + pwm_childs = emc2305_probe_childs_from_dt(dev); + 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]; + + if (!pwm_childs) { + 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_output_mask = pdata->pwm_output_mask; + data->pwm_polarity_mask = pdata->pwm_polarity_mask; + data->pwm_separate = pdata->pwm_separate; + for (i = 0; i < EMC2305_PWM_MAX; i++) { + data->pwm_min[i] = pdata->pwm_min[i]; + data->pwm_freq[i] = pdata->pwm_freq[i]; + } + } else { + data->max_state = EMC2305_FAN_MAX_STATE; + data->pwm_separate = false; + data->pwm_output_mask = EMC2305_DEFAULT_OUTPUT; + data->pwm_polarity_mask = EMC2305_DEFAULT_POLARITY; + for (i = 0; i < EMC2305_PWM_MAX; i++) { + data->pwm_min[i] = EMC2305_FAN_MIN; + data->pwm_freq[i] = base_freq_table[3]; + } + } } else { data->max_state = EMC2305_FAN_MAX_STATE; data->pwm_separate = false; @@ -568,11 +680,32 @@ static int emc2305_probe(struct i2c_client *client) return PTR_ERR(data->hwmon_dev); if (IS_REACHABLE(CONFIG_THERMAL)) { - ret = emc2305_set_tz(dev); - if (ret != 0) - return ret; + /* Parse and check for the available PWM child nodes */ + if (pwm_childs > 0) { + i = 0; + for_each_child_of_node(dev->of_node, child) { + ret = emc2305_set_single_tz(dev, child, i); + if (ret != 0) + return ret; + i++; + } + } else { + ret = emc2305_set_tz(dev); + if (ret != 0) + return ret; + } } + ret = i2c_smbus_write_byte_data(client, EMC2305_REG_DRIVE_PWM_OUT, + data->pwm_output_mask); + if (ret < 0) + dev_err(dev, "Failed to configure pwm output, using default\n"); + + ret = i2c_smbus_write_byte_data(client, EMC2305_REG_POLARITY, + data->pwm_polarity_mask); + if (ret < 0) + dev_err(dev, "Failed to configure pwm polarity, using default\n"); + 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]); diff --git a/drivers/hwmon/ftsteutates.c b/drivers/hwmon/ftsteutates.c index a3a07662e491..8aeec16a7a90 100644 --- a/drivers/hwmon/ftsteutates.c +++ b/drivers/hwmon/ftsteutates.c @@ -423,13 +423,16 @@ static int fts_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, break; case hwmon_pwm: switch (attr) { - case hwmon_pwm_auto_channels_temp: - if (data->fan_source[channel] == FTS_FAN_SOURCE_INVALID) + case hwmon_pwm_auto_channels_temp: { + u8 fan_source = data->fan_source[channel]; + + if (fan_source == FTS_FAN_SOURCE_INVALID || fan_source >= BITS_PER_LONG) *val = 0; else - *val = BIT(data->fan_source[channel]); + *val = BIT(fan_source); return 0; + } default: break; } diff --git a/drivers/hwmon/gsc-hwmon.c b/drivers/hwmon/gsc-hwmon.c index 0f9af82cebec..105b9f9dbec3 100644 --- a/drivers/hwmon/gsc-hwmon.c +++ b/drivers/hwmon/gsc-hwmon.c @@ -64,7 +64,7 @@ static ssize_t pwm_auto_point_temp_show(struct device *dev, return ret; ret = regs[0] | regs[1] << 8; - return sprintf(buf, "%d\n", ret * 10); + return sprintf(buf, "%d\n", ret * 100); } static ssize_t pwm_auto_point_temp_store(struct device *dev, @@ -99,7 +99,7 @@ static ssize_t pwm_auto_point_pwm_show(struct device *dev, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - return sprintf(buf, "%d\n", 255 * (50 + (attr->index * 10))); + return sprintf(buf, "%d\n", 255 * (50 + (attr->index * 10)) / 100); } static SENSOR_DEVICE_ATTR_RO(pwm1_auto_point1_pwm, pwm_auto_point_pwm, 0); diff --git a/drivers/hwmon/ibmaem.c b/drivers/hwmon/ibmaem.c index 157e232aace0..daed437d34a4 100644 --- a/drivers/hwmon/ibmaem.c +++ b/drivers/hwmon/ibmaem.c @@ -349,7 +349,7 @@ static void aem_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data) static int aem_read_sensor(struct aem_data *data, u8 elt, u8 reg, void *buf, size_t size) { - int rs_size, res; + int rs_size; struct aem_read_sensor_req rs_req; /* Use preallocated rx buffer */ struct aem_read_sensor_resp *rs_resp = data->rs_resp; @@ -383,17 +383,12 @@ static int aem_read_sensor(struct aem_data *data, u8 elt, u8 reg, aem_send_message(ipmi); - res = wait_for_completion_timeout(&ipmi->read_complete, IPMI_TIMEOUT); - if (!res) { - res = -ETIMEDOUT; - goto out; - } + if (!wait_for_completion_timeout(&ipmi->read_complete, IPMI_TIMEOUT)) + return -ETIMEDOUT; if (ipmi->rx_result || ipmi->rx_msg_len != rs_size || - memcmp(&rs_resp->id, &system_x_id, sizeof(system_x_id))) { - res = -ENOENT; - goto out; - } + memcmp(&rs_resp->id, &system_x_id, sizeof(system_x_id))) + return -ENOENT; switch (size) { case 1: { @@ -417,10 +412,8 @@ static int aem_read_sensor(struct aem_data *data, u8 elt, u8 reg, break; } } - res = 0; -out: - return res; + return 0; } /* Update AEM energy registers */ @@ -491,7 +484,6 @@ static void aem_delete(struct aem_data *data) /* Retrieve version and module handle for an AEM1 instance */ static int aem_find_aem1_count(struct aem_ipmi_data *data) { - int res; struct aem_find_firmware_req ff_req; struct aem_find_firmware_resp ff_resp; @@ -508,8 +500,7 @@ static int aem_find_aem1_count(struct aem_ipmi_data *data) aem_send_message(data); - res = wait_for_completion_timeout(&data->read_complete, IPMI_TIMEOUT); - if (!res) + if (!wait_for_completion_timeout(&data->read_complete, IPMI_TIMEOUT)) return -ETIMEDOUT; if (data->rx_result || data->rx_msg_len != sizeof(ff_resp) || @@ -632,7 +623,6 @@ static int aem_find_aem2(struct aem_ipmi_data *data, struct aem_find_instance_resp *fi_resp, int instance_num) { - int res; struct aem_find_instance_req fi_req; fi_req.id = system_x_id; @@ -648,8 +638,7 @@ static int aem_find_aem2(struct aem_ipmi_data *data, aem_send_message(data); - res = wait_for_completion_timeout(&data->read_complete, IPMI_TIMEOUT); - if (!res) + if (!wait_for_completion_timeout(&data->read_complete, IPMI_TIMEOUT)) return -ETIMEDOUT; if (data->rx_result || data->rx_msg_len != sizeof(*fi_resp) || diff --git a/drivers/hwmon/ina238.c b/drivers/hwmon/ina238.c index a4a41742786b..5a394eeff676 100644 --- a/drivers/hwmon/ina238.c +++ b/drivers/hwmon/ina238.c @@ -6,6 +6,7 @@ * Copyright (C) 2021 Nathan Rossi <nathan.rossi@digi.com> */ +#include <linux/bitops.h> #include <linux/err.h> #include <linux/hwmon.h> #include <linux/i2c.h> @@ -41,7 +42,7 @@ #define INA238_CONFIG_ADCRANGE BIT(4) #define SQ52206_CONFIG_ADCRANGE_HIGH BIT(4) -#define SQ52206_CONFIG_ADCRANGE_LOW BIT(3) +#define SQ52206_CONFIG_ADCRANGE_LOW BIT(3) #define INA238_DIAG_ALERT_TMPOL BIT(7) #define INA238_DIAG_ALERT_SHNTOL BIT(6) @@ -97,16 +98,17 @@ * Power (mW) = 0.2 * register value * 20000 / rshunt / 4 * gain * (Specific for SQ52206) * Power (mW) = 0.24 * register value * 20000 / rshunt / 4 * gain - * Energy (mJ) = 16 * 0.24 * register value * 20000 / rshunt / 4 * gain + * Energy (uJ) = 16 * 0.24 * register value * 20000 / rshunt / 4 * gain * 1000 */ #define INA238_CALIBRATION_VALUE 16384 #define INA238_FIXED_SHUNT 20000 #define INA238_SHUNT_VOLTAGE_LSB 5 /* 5 uV/lsb */ #define INA238_BUS_VOLTAGE_LSB 3125 /* 3.125 mV/lsb */ -#define INA238_DIE_TEMP_LSB 1250000 /* 125.0000 mC/lsb */ +#define INA238_DIE_TEMP_LSB 1250000 /* 125.0000 mC/lsb */ #define SQ52206_BUS_VOLTAGE_LSB 3750 /* 3.75 mV/lsb */ #define SQ52206_DIE_TEMP_LSB 78125 /* 7.8125 mC/lsb */ +#define INA228_DIE_TEMP_LSB 78125 /* 7.8125 mC/lsb */ static const struct regmap_config ina238_regmap_config = { .max_register = INA238_REGISTERS, @@ -114,16 +116,17 @@ static const struct regmap_config ina238_regmap_config = { .val_bits = 16, }; -enum ina238_ids { ina238, ina237, sq52206 }; +enum ina238_ids { ina238, ina237, sq52206, ina228 }; struct ina238_config { + bool has_20bit_voltage_current; /* vshunt, vbus and current are 20-bit fields */ bool has_power_highest; /* chip detection power peak */ - bool has_energy; /* chip detection energy */ - u8 temp_shift; /* fixed parameters for temp calculate */ + bool has_energy; /* chip detection energy */ + u8 temp_shift; /* fixed parameters for temp calculate */ u32 power_calculate_factor; /* fixed parameters for power calculate */ - u16 config_default; /* Power-on default state */ + u16 config_default; /* Power-on default state */ int bus_voltage_lsb; /* use for temperature calculate, uV/lsb */ - int temp_lsb; /* use for temperature calculate */ + int temp_lsb; /* use for temperature calculate */ }; struct ina238_data { @@ -137,6 +140,7 @@ struct ina238_data { static const struct ina238_config ina238_config[] = { [ina238] = { + .has_20bit_voltage_current = false, .has_energy = false, .has_power_highest = false, .temp_shift = 4, @@ -146,6 +150,7 @@ static const struct ina238_config ina238_config[] = { .temp_lsb = INA238_DIE_TEMP_LSB, }, [ina237] = { + .has_20bit_voltage_current = false, .has_energy = false, .has_power_highest = false, .temp_shift = 4, @@ -155,6 +160,7 @@ static const struct ina238_config ina238_config[] = { .temp_lsb = INA238_DIE_TEMP_LSB, }, [sq52206] = { + .has_20bit_voltage_current = false, .has_energy = true, .has_power_highest = true, .temp_shift = 0, @@ -163,6 +169,16 @@ static const struct ina238_config ina238_config[] = { .bus_voltage_lsb = SQ52206_BUS_VOLTAGE_LSB, .temp_lsb = SQ52206_DIE_TEMP_LSB, }, + [ina228] = { + .has_20bit_voltage_current = true, + .has_energy = true, + .has_power_highest = false, + .temp_shift = 0, + .power_calculate_factor = 20, + .config_default = INA238_CONFIG_DEFAULT, + .bus_voltage_lsb = INA238_BUS_VOLTAGE_LSB, + .temp_lsb = INA228_DIE_TEMP_LSB, + }, }; static int ina238_read_reg24(const struct i2c_client *client, u8 reg, u32 *val) @@ -199,6 +215,65 @@ static int ina238_read_reg40(const struct i2c_client *client, u8 reg, u64 *val) return 0; } +static int ina238_read_field_s20(const struct i2c_client *client, u8 reg, s32 *val) +{ + u32 regval; + int err; + + err = ina238_read_reg24(client, reg, ®val); + if (err) + return err; + + /* bits 3-0 Reserved, always zero */ + regval >>= 4; + + *val = sign_extend32(regval, 19); + + return 0; +} + +static int ina228_read_shunt_voltage(struct device *dev, u32 attr, int channel, + long *val) +{ + struct ina238_data *data = dev_get_drvdata(dev); + int regval; + int err; + + err = ina238_read_field_s20(data->client, INA238_SHUNT_VOLTAGE, ®val); + if (err) + return err; + + /* + * gain of 1 -> LSB / 4 + * This field has 16 bit on ina238. ina228 adds another 4 bits of + * precision. ina238 conversion factors can still be applied when + * dividing by 16. + */ + *val = (regval * INA238_SHUNT_VOLTAGE_LSB) * data->gain / (1000 * 4) / 16; + return 0; +} + +static int ina228_read_bus_voltage(struct device *dev, u32 attr, int channel, + long *val) +{ + struct ina238_data *data = dev_get_drvdata(dev); + int regval; + int err; + + err = ina238_read_field_s20(data->client, INA238_BUS_VOLTAGE, ®val); + if (err) + return err; + + /* + * gain of 1 -> LSB / 4 + * This field has 16 bit on ina238. ina228 adds another 4 bits of + * precision. ina238 conversion factors can still be applied when + * dividing by 16. + */ + *val = (regval * data->config->bus_voltage_lsb) / 1000 / 16; + return 0; +} + static int ina238_read_in(struct device *dev, u32 attr, int channel, long *val) { @@ -211,6 +286,8 @@ static int ina238_read_in(struct device *dev, u32 attr, int channel, case 0: switch (attr) { case hwmon_in_input: + if (data->config->has_20bit_voltage_current) + return ina228_read_shunt_voltage(dev, attr, channel, val); reg = INA238_SHUNT_VOLTAGE; break; case hwmon_in_max: @@ -234,6 +311,8 @@ static int ina238_read_in(struct device *dev, u32 attr, int channel, case 1: switch (attr) { case hwmon_in_input: + if (data->config->has_20bit_voltage_current) + return ina228_read_bus_voltage(dev, attr, channel, val); reg = INA238_BUS_VOLTAGE; break; case hwmon_in_max: @@ -271,7 +350,7 @@ static int ina238_read_in(struct device *dev, u32 attr, int channel, if (channel == 0) /* gain of 1 -> LSB / 4 */ *val = (regval * INA238_SHUNT_VOLTAGE_LSB) * - data->gain / (1000 * 4); + data->gain / (1000 * 4); else *val = (regval * data->config->bus_voltage_lsb) / 1000; break; @@ -341,13 +420,25 @@ static int ina238_read_current(struct device *dev, u32 attr, long *val) switch (attr) { case hwmon_curr_input: - err = regmap_read(data->regmap, INA238_CURRENT, ®val); - if (err < 0) - return err; + if (data->config->has_20bit_voltage_current) { + err = ina238_read_field_s20(data->client, INA238_CURRENT, ®val); + if (err) + return err; + } else { + err = regmap_read(data->regmap, INA238_CURRENT, ®val); + if (err < 0) + return err; + /* sign-extend */ + regval = (s16)regval; + } /* Signed register, fixed 1mA current lsb. result in mA */ - *val = div_s64((s16)regval * INA238_FIXED_SHUNT * data->gain, + *val = div_s64((s64)regval * INA238_FIXED_SHUNT * data->gain, data->rshunt * 4); + + /* Account for 4 bit offset */ + if (data->config->has_20bit_voltage_current) + *val /= 16; break; default: return -EOPNOTSUPP; @@ -370,7 +461,7 @@ static int ina238_read_power(struct device *dev, u32 attr, long *val) return err; /* Fixed 1mA lsb, scaled by 1000000 to have result in uW */ - power = div_u64(regval * 1000ULL * INA238_FIXED_SHUNT * data->gain * + power = div_u64(regval * 1000ULL * INA238_FIXED_SHUNT * data->gain * data->config->power_calculate_factor, 4 * 100 * data->rshunt); /* Clamp value to maximum value of long */ *val = clamp_val(power, 0, LONG_MAX); @@ -381,7 +472,7 @@ static int ina238_read_power(struct device *dev, u32 attr, long *val) return err; /* Fixed 1mA lsb, scaled by 1000000 to have result in uW */ - power = div_u64(regval * 1000ULL * INA238_FIXED_SHUNT * data->gain * + power = div_u64(regval * 1000ULL * INA238_FIXED_SHUNT * data->gain * data->config->power_calculate_factor, 4 * 100 * data->rshunt); /* Clamp value to maximum value of long */ *val = clamp_val(power, 0, LONG_MAX); @@ -395,7 +486,7 @@ static int ina238_read_power(struct device *dev, u32 attr, long *val) * Truncated 24-bit compare register, lower 8-bits are * truncated. Same conversion to/from uW as POWER register. */ - power = div_u64((regval << 8) * 1000ULL * INA238_FIXED_SHUNT * data->gain * + power = div_u64((regval << 8) * 1000ULL * INA238_FIXED_SHUNT * data->gain * data->config->power_calculate_factor, 4 * 100 * data->rshunt); /* Clamp value to maximum value of long */ *val = clamp_val(power, 0, LONG_MAX); @@ -448,7 +539,7 @@ static int ina238_read_temp(struct device *dev, u32 attr, long *val) return err; /* Signed, result in mC */ *val = div_s64(((s64)((s16)regval) >> data->config->temp_shift) * - (s64)data->config->temp_lsb, 10000); + (s64)data->config->temp_lsb, 10000); break; case hwmon_temp_max: err = regmap_read(data->regmap, INA238_TEMP_LIMIT, ®val); @@ -456,7 +547,7 @@ static int ina238_read_temp(struct device *dev, u32 attr, long *val) return err; /* Signed, result in mC */ *val = div_s64(((s64)((s16)regval) >> data->config->temp_shift) * - (s64)data->config->temp_lsb, 10000); + (s64)data->config->temp_lsb, 10000); break; case hwmon_temp_max_alarm: err = regmap_read(data->regmap, INA238_DIAG_ALERT, ®val); @@ -500,9 +591,9 @@ static ssize_t energy1_input_show(struct device *dev, if (ret) return ret; - /* result in mJ */ - energy = div_u64(regval * INA238_FIXED_SHUNT * data->gain * 16 * - data->config->power_calculate_factor, 4 * 100 * data->rshunt); + /* result in uJ */ + energy = div_u64(regval * INA238_FIXED_SHUNT * data->gain * 16 * 10 * + data->config->power_calculate_factor, 4 * data->rshunt); return sysfs_emit(buf, "%llu\n", energy); } @@ -750,6 +841,7 @@ static int ina238_probe(struct i2c_client *client) } static const struct i2c_device_id ina238_id[] = { + { "ina228", ina228 }, { "ina237", ina237 }, { "ina238", ina238 }, { "sq52206", sq52206 }, @@ -759,6 +851,10 @@ MODULE_DEVICE_TABLE(i2c, ina238_id); static const struct of_device_id __maybe_unused ina238_of_match[] = { { + .compatible = "ti,ina228", + .data = (void *)ina228 + }, + { .compatible = "ti,ina237", .data = (void *)ina237 }, diff --git a/drivers/hwmon/ltc4282.c b/drivers/hwmon/ltc4282.c index 7f38d2696239..dbb30abcd343 100644 --- a/drivers/hwmon/ltc4282.c +++ b/drivers/hwmon/ltc4282.c @@ -177,13 +177,15 @@ static const unsigned int ltc4282_out_rates[] = { LTC4282_CLKOUT_CNV, LTC4282_CLKOUT_SYSTEM }; -static long ltc4282_round_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static int ltc4282_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { - int idx = find_closest(rate, ltc4282_out_rates, + int idx = find_closest(req->rate, ltc4282_out_rates, ARRAY_SIZE(ltc4282_out_rates)); - return ltc4282_out_rates[idx]; + req->rate = ltc4282_out_rates[idx]; + + return 0; } static unsigned long ltc4282_recalc_rate(struct clk_hw *hw, @@ -1124,7 +1126,7 @@ static ssize_t ltc4282_energy_show(struct device *dev, static const struct clk_ops ltc4282_ops = { .recalc_rate = ltc4282_recalc_rate, - .round_rate = ltc4282_round_rate, + .determine_rate = ltc4282_determine_rate, .set_rate = ltc4282_set_rate, .disable = ltc4282_disable, }; @@ -1512,13 +1514,6 @@ static int ltc4282_setup(struct ltc4282_state *st, struct device *dev) } if (device_property_read_bool(dev, "adi,fault-log-enable")) { - ret = regmap_set_bits(st->map, LTC4282_ADC_CTRL, - LTC4282_FAULT_LOG_EN_MASK); - if (ret) - return ret; - } - - if (device_property_read_bool(dev, "adi,fault-log-enable")) { ret = regmap_set_bits(st->map, LTC4282_ADC_CTRL, LTC4282_FAULT_LOG_EN_MASK); if (ret) return ret; @@ -1603,7 +1598,7 @@ static const struct hwmon_ops ltc4282_hwmon_ops = { .read_string = ltc4282_read_labels, }; -static const struct hwmon_chip_info ltc2947_chip_info = { +static const struct hwmon_chip_info ltc4282_chip_info = { .ops = <c4282_hwmon_ops, .info = ltc4282_info, }; @@ -1724,7 +1719,7 @@ static int ltc4282_probe(struct i2c_client *i2c) mutex_init(&st->lock); hwmon = devm_hwmon_device_register_with_info(dev, "ltc4282", st, - <c2947_chip_info, + <c4282_chip_info, ltc4282_groups); if (IS_ERR(hwmon)) return PTR_ERR(hwmon); diff --git a/drivers/hwmon/max31827.c b/drivers/hwmon/max31827.c index 48e8f8ba4d05..a31c7b655da1 100644 --- a/drivers/hwmon/max31827.c +++ b/drivers/hwmon/max31827.c @@ -445,7 +445,7 @@ static ssize_t temp1_resolution_show(struct device *dev, val = FIELD_GET(MAX31827_CONFIGURATION_RESOLUTION_MASK, val); - return scnprintf(buf, PAGE_SIZE, "%u\n", max31827_resolutions[val]); + return sysfs_emit(buf, "%u\n", max31827_resolutions[val]); } static ssize_t temp1_resolution_store(struct device *dev, diff --git a/drivers/hwmon/mc33xs2410_hwmon.c b/drivers/hwmon/mc33xs2410_hwmon.c new file mode 100644 index 000000000000..23eb90e33709 --- /dev/null +++ b/drivers/hwmon/mc33xs2410_hwmon.c @@ -0,0 +1,178 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 Liebherr-Electronics and Drives GmbH + */ + +#include <linux/auxiliary_bus.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/hwmon.h> +#include <linux/mc33xs2410.h> +#include <linux/module.h> + +/* ctrl registers */ + +#define MC33XS2410_TEMP_WT 0x29 +#define MC33XS2410_TEMP_WT_MASK GENMASK(7, 0) + +/* diag registers */ + +/* chan in { 1 ... 4 } */ +#define MC33XS2410_OUT_STA(chan) (0x02 + (chan) - 1) +#define MC33XS2410_OUT_STA_OTW BIT(8) + +#define MC33XS2410_TS_TEMP_DIE 0x26 +#define MC33XS2410_TS_TEMP_MASK GENMASK(9, 0) + +/* chan in { 1 ... 4 } */ +#define MC33XS2410_TS_TEMP(chan) (0x2f + (chan) - 1) + +static const struct hwmon_channel_info * const mc33xs2410_hwmon_info[] = { + HWMON_CHANNEL_INFO(temp, + HWMON_T_LABEL | HWMON_T_INPUT, + HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | + HWMON_T_ALARM, + HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | + HWMON_T_ALARM, + HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | + HWMON_T_ALARM, + HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | + HWMON_T_ALARM), + NULL, +}; + +static umode_t mc33xs2410_hwmon_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (attr) { + case hwmon_temp_input: + case hwmon_temp_alarm: + case hwmon_temp_label: + return 0444; + case hwmon_temp_max: + return 0644; + default: + return 0; + } +} + +static int mc33xs2410_hwmon_read(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct spi_device *spi = dev_get_drvdata(dev); + u16 reg_val; + int ret; + u8 reg; + + switch (attr) { + case hwmon_temp_input: + reg = (channel == 0) ? MC33XS2410_TS_TEMP_DIE : + MC33XS2410_TS_TEMP(channel); + ret = mc33xs2410_read_reg_diag(spi, reg, ®_val); + if (ret < 0) + return ret; + + /* LSB is 0.25 degree celsius */ + *val = FIELD_GET(MC33XS2410_TS_TEMP_MASK, reg_val) * 250 - 40000; + return 0; + case hwmon_temp_alarm: + ret = mc33xs2410_read_reg_diag(spi, MC33XS2410_OUT_STA(channel), + ®_val); + if (ret < 0) + return ret; + + *val = FIELD_GET(MC33XS2410_OUT_STA_OTW, reg_val); + return 0; + case hwmon_temp_max: + ret = mc33xs2410_read_reg_ctrl(spi, MC33XS2410_TEMP_WT, ®_val); + if (ret < 0) + return ret; + + /* LSB is 1 degree celsius */ + *val = FIELD_GET(MC33XS2410_TEMP_WT_MASK, reg_val) * 1000 - 40000; + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int mc33xs2410_hwmon_write(struct device *dev, + enum hwmon_sensor_types type, u32 attr, + int channel, long val) +{ + struct spi_device *spi = dev_get_drvdata(dev); + + switch (attr) { + case hwmon_temp_max: + val = clamp_val(val, -40000, 215000); + + /* LSB is 1 degree celsius */ + val = (val / 1000) + 40; + return mc33xs2410_modify_reg(spi, MC33XS2410_TEMP_WT, + MC33XS2410_TEMP_WT_MASK, val); + default: + return -EOPNOTSUPP; + } +} + +static const char *const mc33xs2410_temp_label[] = { + "Central die temperature", + "Channel 1 temperature", + "Channel 2 temperature", + "Channel 3 temperature", + "Channel 4 temperature", +}; + +static int mc33xs2410_read_string(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) +{ + *str = mc33xs2410_temp_label[channel]; + + return 0; +} + +static const struct hwmon_ops mc33xs2410_hwmon_hwmon_ops = { + .is_visible = mc33xs2410_hwmon_is_visible, + .read = mc33xs2410_hwmon_read, + .read_string = mc33xs2410_read_string, + .write = mc33xs2410_hwmon_write, +}; + +static const struct hwmon_chip_info mc33xs2410_hwmon_chip_info = { + .ops = &mc33xs2410_hwmon_hwmon_ops, + .info = mc33xs2410_hwmon_info, +}; + +static int mc33xs2410_hwmon_probe(struct auxiliary_device *adev, + const struct auxiliary_device_id *id) +{ + struct device *dev = &adev->dev; + struct spi_device *spi = container_of(dev->parent, struct spi_device, dev); + struct device *hwmon; + + hwmon = devm_hwmon_device_register_with_info(dev, NULL, spi, + &mc33xs2410_hwmon_chip_info, + NULL); + return PTR_ERR_OR_ZERO(hwmon); +} + +static const struct auxiliary_device_id mc33xs2410_hwmon_ids[] = { + { + .name = "pwm_mc33xs2410.hwmon", + }, + { } +}; +MODULE_DEVICE_TABLE(auxiliary, mc33xs2410_hwmon_ids); + +static struct auxiliary_driver mc33xs2410_hwmon_driver = { + .probe = mc33xs2410_hwmon_probe, + .id_table = mc33xs2410_hwmon_ids, +}; +module_auxiliary_driver(mc33xs2410_hwmon_driver); + +MODULE_DESCRIPTION("NXP MC33XS2410 hwmon driver"); +MODULE_AUTHOR("Dimitri Fedrau <dimitri.fedrau@liebherr.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/npcm750-pwm-fan.c b/drivers/hwmon/npcm750-pwm-fan.c index db3b551828eb..802c73def428 100644 --- a/drivers/hwmon/npcm750-pwm-fan.c +++ b/drivers/hwmon/npcm750-pwm-fan.c @@ -331,7 +331,7 @@ static void npcm7xx_fan_polling(struct timer_list *t) struct npcm7xx_pwm_fan_data *data; int i; - data = from_timer(data, t, fan_timer); + data = timer_container_of(data, t, fan_timer); /* * Polling two module per one round, diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c index 9486db249c64..b3694a4209b9 100644 --- a/drivers/hwmon/occ/common.c +++ b/drivers/hwmon/occ/common.c @@ -459,12 +459,10 @@ static ssize_t occ_show_power_1(struct device *dev, return sysfs_emit(buf, "%llu\n", val); } -static u64 occ_get_powr_avg(u64 *accum, u32 *samples) +static u64 occ_get_powr_avg(u64 accum, u32 samples) { - u64 divisor = get_unaligned_be32(samples); - - return (divisor == 0) ? 0 : - div64_u64(get_unaligned_be64(accum) * 1000000ULL, divisor); + return (samples == 0) ? 0 : + mul_u64_u32_div(accum, 1000000UL, samples); } static ssize_t occ_show_power_2(struct device *dev, @@ -489,8 +487,8 @@ static ssize_t occ_show_power_2(struct device *dev, get_unaligned_be32(&power->sensor_id), power->function_id, power->apss_channel); case 1: - val = occ_get_powr_avg(&power->accumulator, - &power->update_tag); + val = occ_get_powr_avg(get_unaligned_be64(&power->accumulator), + get_unaligned_be32(&power->update_tag)); break; case 2: val = (u64)get_unaligned_be32(&power->update_tag) * @@ -527,8 +525,8 @@ static ssize_t occ_show_power_a0(struct device *dev, return sysfs_emit(buf, "%u_system\n", get_unaligned_be32(&power->sensor_id)); case 1: - val = occ_get_powr_avg(&power->system.accumulator, - &power->system.update_tag); + val = occ_get_powr_avg(get_unaligned_be64(&power->system.accumulator), + get_unaligned_be32(&power->system.update_tag)); break; case 2: val = (u64)get_unaligned_be32(&power->system.update_tag) * @@ -541,8 +539,8 @@ static ssize_t occ_show_power_a0(struct device *dev, return sysfs_emit(buf, "%u_proc\n", get_unaligned_be32(&power->sensor_id)); case 5: - val = occ_get_powr_avg(&power->proc.accumulator, - &power->proc.update_tag); + val = occ_get_powr_avg(get_unaligned_be64(&power->proc.accumulator), + get_unaligned_be32(&power->proc.update_tag)); break; case 6: val = (u64)get_unaligned_be32(&power->proc.update_tag) * @@ -555,8 +553,8 @@ static ssize_t occ_show_power_a0(struct device *dev, return sysfs_emit(buf, "%u_vdd\n", get_unaligned_be32(&power->sensor_id)); case 9: - val = occ_get_powr_avg(&power->vdd.accumulator, - &power->vdd.update_tag); + val = occ_get_powr_avg(get_unaligned_be64(&power->vdd.accumulator), + get_unaligned_be32(&power->vdd.update_tag)); break; case 10: val = (u64)get_unaligned_be32(&power->vdd.update_tag) * @@ -569,8 +567,8 @@ static ssize_t occ_show_power_a0(struct device *dev, return sysfs_emit(buf, "%u_vdn\n", get_unaligned_be32(&power->sensor_id)); case 13: - val = occ_get_powr_avg(&power->vdn.accumulator, - &power->vdn.update_tag); + val = occ_get_powr_avg(get_unaligned_be64(&power->vdn.accumulator), + get_unaligned_be32(&power->vdn.update_tag)); break; case 14: val = (u64)get_unaligned_be32(&power->vdn.update_tag) * @@ -747,29 +745,30 @@ static ssize_t occ_show_extended(struct device *dev, } /* - * Some helper macros to make it easier to define an occ_attribute. Since these - * are dynamically allocated, we shouldn't use the existing kernel macros which + * A helper to make it easier to define an occ_attribute. Since these + * are dynamically allocated, we cannot use the existing kernel macros which * stringify the name argument. */ -#define ATTR_OCC(_name, _mode, _show, _store) { \ - .attr = { \ - .name = _name, \ - .mode = VERIFY_OCTAL_PERMISSIONS(_mode), \ - }, \ - .show = _show, \ - .store = _store, \ -} - -#define SENSOR_ATTR_OCC(_name, _mode, _show, _store, _nr, _index) { \ - .dev_attr = ATTR_OCC(_name, _mode, _show, _store), \ - .index = _index, \ - .nr = _nr, \ +static void occ_init_attribute(struct occ_attribute *attr, int mode, + ssize_t (*show)(struct device *dev, struct device_attribute *attr, char *buf), + ssize_t (*store)(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count), + int nr, int index, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vsnprintf(attr->name, sizeof(attr->name), fmt, args); + va_end(args); + + attr->sensor.dev_attr.attr.name = attr->name; + attr->sensor.dev_attr.attr.mode = mode; + attr->sensor.dev_attr.show = show; + attr->sensor.dev_attr.store = store; + attr->sensor.index = index; + attr->sensor.nr = nr; } -#define OCC_INIT_ATTR(_name, _mode, _show, _store, _nr, _index) \ - ((struct sensor_device_attribute_2) \ - SENSOR_ATTR_OCC(_name, _mode, _show, _store, _nr, _index)) - /* * Allocate and instatiate sensor_device_attribute_2s. It's most efficient to * use our own instead of the built-in hwmon attribute types. @@ -855,14 +854,15 @@ static int occ_setup_sensor_attrs(struct occ *occ) sensors->extended.num_sensors = 0; } - occ->attrs = devm_kzalloc(dev, sizeof(*occ->attrs) * num_attrs, + occ->attrs = devm_kcalloc(dev, num_attrs, sizeof(*occ->attrs), GFP_KERNEL); if (!occ->attrs) return -ENOMEM; /* null-terminated list */ - occ->group.attrs = devm_kzalloc(dev, sizeof(*occ->group.attrs) * - num_attrs + 1, GFP_KERNEL); + occ->group.attrs = devm_kcalloc(dev, num_attrs + 1, + sizeof(*occ->group.attrs), + GFP_KERNEL); if (!occ->group.attrs) return -ENOMEM; @@ -872,43 +872,33 @@ static int occ_setup_sensor_attrs(struct occ *occ) s = i + 1; temp = ((struct temp_sensor_2 *)sensors->temp.data) + i; - snprintf(attr->name, sizeof(attr->name), "temp%d_label", s); - attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_temp, NULL, - 0, i); + occ_init_attribute(attr, 0444, show_temp, NULL, + 0, i, "temp%d_label", s); attr++; if (sensors->temp.version == 2 && temp->fru_type == OCC_FRU_TYPE_VRM) { - snprintf(attr->name, sizeof(attr->name), - "temp%d_alarm", s); + occ_init_attribute(attr, 0444, show_temp, NULL, + 1, i, "temp%d_alarm", s); } else { - snprintf(attr->name, sizeof(attr->name), - "temp%d_input", s); + occ_init_attribute(attr, 0444, show_temp, NULL, + 1, i, "temp%d_input", s); } - attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_temp, NULL, - 1, i); attr++; if (sensors->temp.version > 1) { - snprintf(attr->name, sizeof(attr->name), - "temp%d_fru_type", s); - attr->sensor = OCC_INIT_ATTR(attr->name, 0444, - show_temp, NULL, 2, i); + occ_init_attribute(attr, 0444, show_temp, NULL, + 2, i, "temp%d_fru_type", s); attr++; - snprintf(attr->name, sizeof(attr->name), - "temp%d_fault", s); - attr->sensor = OCC_INIT_ATTR(attr->name, 0444, - show_temp, NULL, 3, i); + occ_init_attribute(attr, 0444, show_temp, NULL, + 3, i, "temp%d_fault", s); attr++; if (sensors->temp.version == 0x10) { - snprintf(attr->name, sizeof(attr->name), - "temp%d_max", s); - attr->sensor = OCC_INIT_ATTR(attr->name, 0444, - show_temp, NULL, - 4, i); + occ_init_attribute(attr, 0444, show_temp, NULL, + 4, i, "temp%d_max", s); attr++; } } @@ -917,14 +907,12 @@ static int occ_setup_sensor_attrs(struct occ *occ) for (i = 0; i < sensors->freq.num_sensors; ++i) { s = i + 1; - snprintf(attr->name, sizeof(attr->name), "freq%d_label", s); - attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_freq, NULL, - 0, i); + occ_init_attribute(attr, 0444, show_freq, NULL, + 0, i, "freq%d_label", s); attr++; - snprintf(attr->name, sizeof(attr->name), "freq%d_input", s); - attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_freq, NULL, - 1, i); + occ_init_attribute(attr, 0444, show_freq, NULL, + 1, i, "freq%d_input", s); attr++; } @@ -940,32 +928,24 @@ static int occ_setup_sensor_attrs(struct occ *occ) s = (i * 4) + 1; for (j = 0; j < 4; ++j) { - snprintf(attr->name, sizeof(attr->name), - "power%d_label", s); - attr->sensor = OCC_INIT_ATTR(attr->name, 0444, - show_power, NULL, - nr++, i); + occ_init_attribute(attr, 0444, show_power, + NULL, nr++, i, + "power%d_label", s); attr++; - snprintf(attr->name, sizeof(attr->name), - "power%d_average", s); - attr->sensor = OCC_INIT_ATTR(attr->name, 0444, - show_power, NULL, - nr++, i); + occ_init_attribute(attr, 0444, show_power, + NULL, nr++, i, + "power%d_average", s); attr++; - snprintf(attr->name, sizeof(attr->name), - "power%d_average_interval", s); - attr->sensor = OCC_INIT_ATTR(attr->name, 0444, - show_power, NULL, - nr++, i); + occ_init_attribute(attr, 0444, show_power, + NULL, nr++, i, + "power%d_average_interval", s); attr++; - snprintf(attr->name, sizeof(attr->name), - "power%d_input", s); - attr->sensor = OCC_INIT_ATTR(attr->name, 0444, - show_power, NULL, - nr++, i); + occ_init_attribute(attr, 0444, show_power, + NULL, nr++, i, + "power%d_input", s); attr++; s++; @@ -977,28 +957,20 @@ static int occ_setup_sensor_attrs(struct occ *occ) for (i = 0; i < sensors->power.num_sensors; ++i) { s = i + 1; - snprintf(attr->name, sizeof(attr->name), - "power%d_label", s); - attr->sensor = OCC_INIT_ATTR(attr->name, 0444, - show_power, NULL, 0, i); + occ_init_attribute(attr, 0444, show_power, NULL, + 0, i, "power%d_label", s); attr++; - snprintf(attr->name, sizeof(attr->name), - "power%d_average", s); - attr->sensor = OCC_INIT_ATTR(attr->name, 0444, - show_power, NULL, 1, i); + occ_init_attribute(attr, 0444, show_power, NULL, + 1, i, "power%d_average", s); attr++; - snprintf(attr->name, sizeof(attr->name), - "power%d_average_interval", s); - attr->sensor = OCC_INIT_ATTR(attr->name, 0444, - show_power, NULL, 2, i); + occ_init_attribute(attr, 0444, show_power, NULL, + 2, i, "power%d_average_interval", s); attr++; - snprintf(attr->name, sizeof(attr->name), - "power%d_input", s); - attr->sensor = OCC_INIT_ATTR(attr->name, 0444, - show_power, NULL, 3, i); + occ_init_attribute(attr, 0444, show_power, NULL, + 3, i, "power%d_input", s); attr++; } @@ -1006,56 +978,43 @@ static int occ_setup_sensor_attrs(struct occ *occ) } if (sensors->caps.num_sensors >= 1) { - snprintf(attr->name, sizeof(attr->name), "power%d_label", s); - attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL, - 0, 0); + occ_init_attribute(attr, 0444, show_caps, NULL, + 0, 0, "power%d_label", s); attr++; - snprintf(attr->name, sizeof(attr->name), "power%d_cap", s); - attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL, - 1, 0); + occ_init_attribute(attr, 0444, show_caps, NULL, + 1, 0, "power%d_cap", s); attr++; - snprintf(attr->name, sizeof(attr->name), "power%d_input", s); - attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL, - 2, 0); + occ_init_attribute(attr, 0444, show_caps, NULL, + 2, 0, "power%d_input", s); attr++; - snprintf(attr->name, sizeof(attr->name), - "power%d_cap_not_redundant", s); - attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL, - 3, 0); + occ_init_attribute(attr, 0444, show_caps, NULL, + 3, 0, "power%d_cap_not_redundant", s); attr++; - snprintf(attr->name, sizeof(attr->name), "power%d_cap_max", s); - attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL, - 4, 0); + occ_init_attribute(attr, 0444, show_caps, NULL, + 4, 0, "power%d_cap_max", s); attr++; - snprintf(attr->name, sizeof(attr->name), "power%d_cap_min", s); - attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL, - 5, 0); + occ_init_attribute(attr, 0444, show_caps, NULL, + 5, 0, "power%d_cap_min", s); attr++; - snprintf(attr->name, sizeof(attr->name), "power%d_cap_user", - s); - attr->sensor = OCC_INIT_ATTR(attr->name, 0644, show_caps, - occ_store_caps_user, 6, 0); + occ_init_attribute(attr, 0644, show_caps, occ_store_caps_user, + 6, 0, "power%d_cap_user", s); attr++; if (sensors->caps.version > 1) { - snprintf(attr->name, sizeof(attr->name), - "power%d_cap_user_source", s); - attr->sensor = OCC_INIT_ATTR(attr->name, 0444, - show_caps, NULL, 7, 0); + occ_init_attribute(attr, 0444, show_caps, NULL, + 7, 0, "power%d_cap_user_source", s); attr++; if (sensors->caps.version > 2) { - snprintf(attr->name, sizeof(attr->name), - "power%d_cap_min_soft", s); - attr->sensor = OCC_INIT_ATTR(attr->name, 0444, - show_caps, NULL, - 8, 0); + occ_init_attribute(attr, 0444, show_caps, NULL, + 8, 0, + "power%d_cap_min_soft", s); attr++; } } @@ -1064,19 +1023,16 @@ static int occ_setup_sensor_attrs(struct occ *occ) for (i = 0; i < sensors->extended.num_sensors; ++i) { s = i + 1; - snprintf(attr->name, sizeof(attr->name), "extn%d_label", s); - attr->sensor = OCC_INIT_ATTR(attr->name, 0444, - occ_show_extended, NULL, 0, i); + occ_init_attribute(attr, 0444, occ_show_extended, NULL, + 0, i, "extn%d_label", s); attr++; - snprintf(attr->name, sizeof(attr->name), "extn%d_flags", s); - attr->sensor = OCC_INIT_ATTR(attr->name, 0444, - occ_show_extended, NULL, 1, i); + occ_init_attribute(attr, 0444, occ_show_extended, NULL, + 1, i, "extn%d_flags", s); attr++; - snprintf(attr->name, sizeof(attr->name), "extn%d_input", s); - attr->sensor = OCC_INIT_ATTR(attr->name, 0444, - occ_show_extended, NULL, 2, i); + occ_init_attribute(attr, 0444, occ_show_extended, NULL, + 2, i, "extn%d_input", s); attr++; } diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 441f984a859d..55e492452ce8 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -67,6 +67,15 @@ config SENSORS_ADP1050 This driver can also be built as a module. If so, the module will be called adp1050. +config SENSORS_ADP1050_REGULATOR + bool "Regulator support for ADP1050 and compatibles" + depends on SENSORS_ADP1050 && REGULATOR + help + If you say yes here you get regulator support for Analog Devices + LTP8800-1A, LTP8800-4A, and LTP8800-2. LTP8800 is a family of DC/DC + µModule regulators that can provide microprocessor power from 54V + power distribution architecture. + config SENSORS_BEL_PFE tristate "Bel PFE Compatible Power Supplies" help diff --git a/drivers/hwmon/pmbus/adp1050.c b/drivers/hwmon/pmbus/adp1050.c index ef46c880b168..a73774f8da2d 100644 --- a/drivers/hwmon/pmbus/adp1050.c +++ b/drivers/hwmon/pmbus/adp1050.c @@ -11,6 +11,12 @@ #include "pmbus.h" +#if IS_ENABLED(CONFIG_SENSORS_ADP1050_REGULATOR) +static const struct regulator_desc adp1050_reg_desc[] = { + PMBUS_REGULATOR_ONE("vout"), +}; +#endif /* CONFIG_SENSORS_ADP1050_REGULATOR */ + static struct pmbus_driver_info adp1050_info = { .pages = 1, .format[PSC_VOLTAGE_IN] = linear, @@ -23,19 +29,79 @@ static struct pmbus_driver_info adp1050_info = { | PMBUS_HAVE_STATUS_TEMP, }; +static struct pmbus_driver_info adp1051_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = linear, + .format[PSC_CURRENT_IN] = linear, + .format[PSC_TEMPERATURE] = linear, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN + | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT + | PMBUS_HAVE_TEMP + | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT + | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_STATUS_TEMP, +}; + +static struct pmbus_driver_info adp1055_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = linear, + .format[PSC_CURRENT_IN] = linear, + .format[PSC_TEMPERATURE] = linear, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN + | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT + | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3 + | PMBUS_HAVE_POUT + | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT + | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_STATUS_TEMP, +}; + +static struct pmbus_driver_info ltp8800_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = linear, + .format[PSC_CURRENT_IN] = linear, + .format[PSC_TEMPERATURE] = linear, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN + | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT + | PMBUS_HAVE_TEMP + | PMBUS_HAVE_POUT + | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_STATUS_TEMP, +#if IS_ENABLED(CONFIG_SENSORS_ADP1050_REGULATOR) + .num_regulators = 1, + .reg_desc = adp1050_reg_desc, +#endif +}; + static int adp1050_probe(struct i2c_client *client) { - return pmbus_do_probe(client, &adp1050_info); + struct pmbus_driver_info *info; + + info = (struct pmbus_driver_info *)i2c_get_match_data(client); + if (!info) + return -ENODEV; + + return pmbus_do_probe(client, info); } static const struct i2c_device_id adp1050_id[] = { - {"adp1050"}, + { .name = "adp1050", .driver_data = (kernel_ulong_t)&adp1050_info }, + { .name = "adp1051", .driver_data = (kernel_ulong_t)&adp1051_info }, + { .name = "adp1055", .driver_data = (kernel_ulong_t)&adp1055_info }, + { .name = "ltp8800", .driver_data = (kernel_ulong_t)<p8800_info }, {} }; MODULE_DEVICE_TABLE(i2c, adp1050_id); static const struct of_device_id adp1050_of_match[] = { - { .compatible = "adi,adp1050"}, + { .compatible = "adi,adp1050", .data = &adp1050_info }, + { .compatible = "adi,adp1051", .data = &adp1051_info }, + { .compatible = "adi,adp1055", .data = &adp1055_info }, + { .compatible = "adi,ltp8800", .data = <p8800_info }, {} }; MODULE_DEVICE_TABLE(of, adp1050_of_match); diff --git a/drivers/hwmon/pmbus/isl68137.c b/drivers/hwmon/pmbus/isl68137.c index 2af921039309..c52c55d2e7f4 100644 --- a/drivers/hwmon/pmbus/isl68137.c +++ b/drivers/hwmon/pmbus/isl68137.c @@ -63,6 +63,7 @@ enum chips { raa228228, raa229001, raa229004, + raa229621, }; enum variants { @@ -465,6 +466,7 @@ static const struct i2c_device_id raa_dmpvr_id[] = { {"raa228228", raa_dmpvr2_2rail_nontc}, {"raa229001", raa_dmpvr2_2rail}, {"raa229004", raa_dmpvr2_2rail}, + {"raa229621", raa_dmpvr2_2rail}, {} }; @@ -512,6 +514,7 @@ static const struct of_device_id isl68137_of_match[] = { { .compatible = "renesas,raa228228", .data = (void *)raa_dmpvr2_2rail_nontc }, { .compatible = "renesas,raa229001", .data = (void *)raa_dmpvr2_2rail }, { .compatible = "renesas,raa229004", .data = (void *)raa_dmpvr2_2rail }, + { .compatible = "renesas,raa229621", .data = (void *)raa_dmpvr2_2rail }, { }, }; diff --git a/drivers/hwmon/pmbus/tps53679.c b/drivers/hwmon/pmbus/tps53679.c index 63524dff5e75..ca2bfa25eb04 100644 --- a/drivers/hwmon/pmbus/tps53679.c +++ b/drivers/hwmon/pmbus/tps53679.c @@ -16,7 +16,7 @@ #include "pmbus.h" enum chips { - tps53647, tps53667, tps53676, tps53679, tps53681, tps53688 + tps53647, tps53667, tps53676, tps53679, tps53681, tps53685, tps53688 }; #define TPS53647_PAGE_NUM 1 @@ -31,7 +31,8 @@ enum chips { #define TPS53679_PROT_VR13_5MV 0x07 /* VR13.0 mode, 5-mV DAC */ #define TPS53679_PAGE_NUM 2 -#define TPS53681_DEVICE_ID 0x81 +#define TPS53681_DEVICE_ID "\x81" +#define TPS53685_DEVICE_ID "TIShP" #define TPS53681_PMBUS_REVISION 0x33 @@ -86,10 +87,12 @@ static int tps53679_identify_phases(struct i2c_client *client, } static int tps53679_identify_chip(struct i2c_client *client, - u8 revision, u16 id) + u8 revision, char *id) { u8 buf[I2C_SMBUS_BLOCK_MAX]; int ret; + int buf_len; + int id_len; ret = pmbus_read_byte_data(client, 0, PMBUS_REVISION); if (ret < 0) @@ -102,8 +105,14 @@ static int tps53679_identify_chip(struct i2c_client *client, ret = i2c_smbus_read_block_data(client, PMBUS_IC_DEVICE_ID, buf); if (ret < 0) return ret; - if (ret != 1 || buf[0] != id) { - dev_err(&client->dev, "Unexpected device ID 0x%x\n", buf[0]); + + /* Adjust length if null terminator if present */ + buf_len = (buf[ret - 1] != '\x00' ? ret : ret - 1); + + id_len = strlen(id); + + if (buf_len != id_len || strncmp(id, buf, id_len)) { + dev_err(&client->dev, "Unexpected device ID: %*ph\n", ret, buf); return -ENODEV; } return 0; @@ -117,7 +126,7 @@ static int tps53679_identify_chip(struct i2c_client *client, */ static int tps53679_identify_multiphase(struct i2c_client *client, struct pmbus_driver_info *info, - int pmbus_rev, int device_id) + int pmbus_rev, char *device_id) { int ret; @@ -138,6 +147,16 @@ static int tps53679_identify(struct i2c_client *client, return tps53679_identify_mode(client, info); } +static int tps53685_identify(struct i2c_client *client, + struct pmbus_driver_info *info) +{ + info->func[1] |= PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | PMBUS_HAVE_PIN | + PMBUS_HAVE_STATUS_INPUT; + info->format[PSC_VOLTAGE_OUT] = linear; + return tps53679_identify_chip(client, TPS53681_PMBUS_REVISION, + TPS53685_DEVICE_ID); +} + static int tps53681_identify(struct i2c_client *client, struct pmbus_driver_info *info) { @@ -263,6 +282,10 @@ static int tps53679_probe(struct i2c_client *client) info->identify = tps53681_identify; info->read_word_data = tps53681_read_word_data; break; + case tps53685: + info->pages = TPS53679_PAGE_NUM; + info->identify = tps53685_identify; + break; default: return -ENODEV; } @@ -277,6 +300,7 @@ static const struct i2c_device_id tps53679_id[] = { {"tps53676", tps53676}, {"tps53679", tps53679}, {"tps53681", tps53681}, + {"tps53685", tps53685}, {"tps53688", tps53688}, {} }; @@ -289,6 +313,7 @@ static const struct of_device_id __maybe_unused tps53679_of_match[] = { {.compatible = "ti,tps53676", .data = (void *)tps53676}, {.compatible = "ti,tps53679", .data = (void *)tps53679}, {.compatible = "ti,tps53681", .data = (void *)tps53681}, + {.compatible = "ti,tps53685", .data = (void *)tps53685}, {.compatible = "ti,tps53688", .data = (void *)tps53688}, {} }; diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c index 2bc8cccb01fd..52d4000902d5 100644 --- a/drivers/hwmon/pmbus/ucd9000.c +++ b/drivers/hwmon/pmbus/ucd9000.c @@ -226,15 +226,15 @@ static int ucd9000_gpio_set(struct gpio_chip *gc, unsigned int offset, } if (value) { - if (ret & UCD9000_GPIO_CONFIG_STATUS) + if (ret & UCD9000_GPIO_CONFIG_OUT_VALUE) return 0; - ret |= UCD9000_GPIO_CONFIG_STATUS; + ret |= UCD9000_GPIO_CONFIG_OUT_VALUE; } else { - if (!(ret & UCD9000_GPIO_CONFIG_STATUS)) + if (!(ret & UCD9000_GPIO_CONFIG_OUT_VALUE)) return 0; - ret &= ~UCD9000_GPIO_CONFIG_STATUS; + ret &= ~UCD9000_GPIO_CONFIG_OUT_VALUE; } ret |= UCD9000_GPIO_CONFIG_ENABLE; diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c index 2df294793f6e..d0fe53451bdf 100644 --- a/drivers/hwmon/pwm-fan.c +++ b/drivers/hwmon/pwm-fan.c @@ -78,7 +78,7 @@ static irqreturn_t pulse_handler(int irq, void *dev_id) static void sample_timer(struct timer_list *t) { - struct pwm_fan_ctx *ctx = from_timer(ctx, t, rpm_timer); + struct pwm_fan_ctx *ctx = timer_container_of(ctx, t, rpm_timer); unsigned int delta = ktime_ms_delta(ktime_get(), ctx->sample_start); int i; diff --git a/drivers/hwmon/sbrmi.c b/drivers/hwmon/sbrmi.c deleted file mode 100644 index d48d8e5460ff..000000000000 --- a/drivers/hwmon/sbrmi.c +++ /dev/null @@ -1,357 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * sbrmi.c - hwmon driver for a SB-RMI mailbox - * compliant AMD SoC device. - * - * Copyright (C) 2020-2021 Advanced Micro Devices, Inc. - */ - -#include <linux/delay.h> -#include <linux/err.h> -#include <linux/hwmon.h> -#include <linux/i2c.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/mutex.h> -#include <linux/of.h> - -/* Do not allow setting negative power limit */ -#define SBRMI_PWR_MIN 0 -/* Mask for Status Register bit[1] */ -#define SW_ALERT_MASK 0x2 - -/* Software Interrupt for triggering */ -#define START_CMD 0x80 -#define TRIGGER_MAILBOX 0x01 - -/* - * SB-RMI supports soft mailbox service request to MP1 (power management - * firmware) through SBRMI inbound/outbound message registers. - * SB-RMI message IDs - */ -enum sbrmi_msg_id { - SBRMI_READ_PKG_PWR_CONSUMPTION = 0x1, - SBRMI_WRITE_PKG_PWR_LIMIT, - SBRMI_READ_PKG_PWR_LIMIT, - SBRMI_READ_PKG_MAX_PWR_LIMIT, -}; - -/* SB-RMI registers */ -enum sbrmi_reg { - SBRMI_CTRL = 0x01, - SBRMI_STATUS, - SBRMI_OUTBNDMSG0 = 0x30, - SBRMI_OUTBNDMSG1, - SBRMI_OUTBNDMSG2, - SBRMI_OUTBNDMSG3, - SBRMI_OUTBNDMSG4, - SBRMI_OUTBNDMSG5, - SBRMI_OUTBNDMSG6, - SBRMI_OUTBNDMSG7, - SBRMI_INBNDMSG0, - SBRMI_INBNDMSG1, - SBRMI_INBNDMSG2, - SBRMI_INBNDMSG3, - SBRMI_INBNDMSG4, - SBRMI_INBNDMSG5, - SBRMI_INBNDMSG6, - SBRMI_INBNDMSG7, - SBRMI_SW_INTERRUPT, -}; - -/* Each client has this additional data */ -struct sbrmi_data { - struct i2c_client *client; - struct mutex lock; - u32 pwr_limit_max; -}; - -struct sbrmi_mailbox_msg { - u8 cmd; - bool read; - u32 data_in; - u32 data_out; -}; - -static int sbrmi_enable_alert(struct i2c_client *client) -{ - int ctrl; - - /* - * Enable the SB-RMI Software alert status - * by writing 0 to bit 4 of Control register(0x1) - */ - ctrl = i2c_smbus_read_byte_data(client, SBRMI_CTRL); - if (ctrl < 0) - return ctrl; - - if (ctrl & 0x10) { - ctrl &= ~0x10; - return i2c_smbus_write_byte_data(client, - SBRMI_CTRL, ctrl); - } - - return 0; -} - -static int rmi_mailbox_xfer(struct sbrmi_data *data, - struct sbrmi_mailbox_msg *msg) -{ - int i, ret, retry = 10; - int sw_status; - u8 byte; - - mutex_lock(&data->lock); - - /* Indicate firmware a command is to be serviced */ - ret = i2c_smbus_write_byte_data(data->client, - SBRMI_INBNDMSG7, START_CMD); - if (ret < 0) - goto exit_unlock; - - /* Write the command to SBRMI::InBndMsg_inst0 */ - ret = i2c_smbus_write_byte_data(data->client, - SBRMI_INBNDMSG0, msg->cmd); - if (ret < 0) - goto exit_unlock; - - /* - * For both read and write the initiator (BMC) writes - * Command Data In[31:0] to SBRMI::InBndMsg_inst[4:1] - * SBRMI_x3C(MSB):SBRMI_x39(LSB) - */ - for (i = 0; i < 4; i++) { - byte = (msg->data_in >> i * 8) & 0xff; - ret = i2c_smbus_write_byte_data(data->client, - SBRMI_INBNDMSG1 + i, byte); - if (ret < 0) - goto exit_unlock; - } - - /* - * Write 0x01 to SBRMI::SoftwareInterrupt to notify firmware to - * perform the requested read or write command - */ - ret = i2c_smbus_write_byte_data(data->client, - SBRMI_SW_INTERRUPT, TRIGGER_MAILBOX); - if (ret < 0) - goto exit_unlock; - - /* - * Firmware will write SBRMI::Status[SwAlertSts]=1 to generate - * an ALERT (if enabled) to initiator (BMC) to indicate completion - * of the requested command - */ - do { - sw_status = i2c_smbus_read_byte_data(data->client, - SBRMI_STATUS); - if (sw_status < 0) { - ret = sw_status; - goto exit_unlock; - } - if (sw_status & SW_ALERT_MASK) - break; - usleep_range(50, 100); - } while (retry--); - - if (retry < 0) { - dev_err(&data->client->dev, - "Firmware fail to indicate command completion\n"); - ret = -EIO; - goto exit_unlock; - } - - /* - * For a read operation, the initiator (BMC) reads the firmware - * response Command Data Out[31:0] from SBRMI::OutBndMsg_inst[4:1] - * {SBRMI_x34(MSB):SBRMI_x31(LSB)}. - */ - if (msg->read) { - for (i = 0; i < 4; i++) { - ret = i2c_smbus_read_byte_data(data->client, - SBRMI_OUTBNDMSG1 + i); - if (ret < 0) - goto exit_unlock; - msg->data_out |= ret << i * 8; - } - } - - /* - * BMC must write 1'b1 to SBRMI::Status[SwAlertSts] to clear the - * ALERT to initiator - */ - ret = i2c_smbus_write_byte_data(data->client, SBRMI_STATUS, - sw_status | SW_ALERT_MASK); - -exit_unlock: - mutex_unlock(&data->lock); - return ret; -} - -static int sbrmi_read(struct device *dev, enum hwmon_sensor_types type, - u32 attr, int channel, long *val) -{ - struct sbrmi_data *data = dev_get_drvdata(dev); - struct sbrmi_mailbox_msg msg = { 0 }; - int ret; - - if (type != hwmon_power) - return -EINVAL; - - msg.read = true; - switch (attr) { - case hwmon_power_input: - msg.cmd = SBRMI_READ_PKG_PWR_CONSUMPTION; - ret = rmi_mailbox_xfer(data, &msg); - break; - case hwmon_power_cap: - msg.cmd = SBRMI_READ_PKG_PWR_LIMIT; - ret = rmi_mailbox_xfer(data, &msg); - break; - case hwmon_power_cap_max: - msg.data_out = data->pwr_limit_max; - ret = 0; - break; - default: - return -EINVAL; - } - if (ret < 0) - return ret; - /* hwmon power attributes are in microWatt */ - *val = (long)msg.data_out * 1000; - return ret; -} - -static int sbrmi_write(struct device *dev, enum hwmon_sensor_types type, - u32 attr, int channel, long val) -{ - struct sbrmi_data *data = dev_get_drvdata(dev); - struct sbrmi_mailbox_msg msg = { 0 }; - - if (type != hwmon_power && attr != hwmon_power_cap) - return -EINVAL; - /* - * hwmon power attributes are in microWatt - * mailbox read/write is in mWatt - */ - val /= 1000; - - val = clamp_val(val, SBRMI_PWR_MIN, data->pwr_limit_max); - - msg.cmd = SBRMI_WRITE_PKG_PWR_LIMIT; - msg.data_in = val; - msg.read = false; - - return rmi_mailbox_xfer(data, &msg); -} - -static umode_t sbrmi_is_visible(const void *data, - enum hwmon_sensor_types type, - u32 attr, int channel) -{ - switch (type) { - case hwmon_power: - switch (attr) { - case hwmon_power_input: - case hwmon_power_cap_max: - return 0444; - case hwmon_power_cap: - return 0644; - } - break; - default: - break; - } - return 0; -} - -static const struct hwmon_channel_info * const sbrmi_info[] = { - HWMON_CHANNEL_INFO(power, - HWMON_P_INPUT | HWMON_P_CAP | HWMON_P_CAP_MAX), - NULL -}; - -static const struct hwmon_ops sbrmi_hwmon_ops = { - .is_visible = sbrmi_is_visible, - .read = sbrmi_read, - .write = sbrmi_write, -}; - -static const struct hwmon_chip_info sbrmi_chip_info = { - .ops = &sbrmi_hwmon_ops, - .info = sbrmi_info, -}; - -static int sbrmi_get_max_pwr_limit(struct sbrmi_data *data) -{ - struct sbrmi_mailbox_msg msg = { 0 }; - int ret; - - msg.cmd = SBRMI_READ_PKG_MAX_PWR_LIMIT; - msg.read = true; - ret = rmi_mailbox_xfer(data, &msg); - if (ret < 0) - return ret; - data->pwr_limit_max = msg.data_out; - - return ret; -} - -static int sbrmi_probe(struct i2c_client *client) -{ - struct device *dev = &client->dev; - struct device *hwmon_dev; - struct sbrmi_data *data; - int ret; - - data = devm_kzalloc(dev, sizeof(struct sbrmi_data), GFP_KERNEL); - if (!data) - return -ENOMEM; - - data->client = client; - mutex_init(&data->lock); - - /* Enable alert for SB-RMI sequence */ - ret = sbrmi_enable_alert(client); - if (ret < 0) - return ret; - - /* Cache maximum power limit */ - ret = sbrmi_get_max_pwr_limit(data); - if (ret < 0) - return ret; - - hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, - &sbrmi_chip_info, NULL); - - return PTR_ERR_OR_ZERO(hwmon_dev); -} - -static const struct i2c_device_id sbrmi_id[] = { - {"sbrmi"}, - {} -}; -MODULE_DEVICE_TABLE(i2c, sbrmi_id); - -static const struct of_device_id __maybe_unused sbrmi_of_match[] = { - { - .compatible = "amd,sbrmi", - }, - { }, -}; -MODULE_DEVICE_TABLE(of, sbrmi_of_match); - -static struct i2c_driver sbrmi_driver = { - .driver = { - .name = "sbrmi", - .of_match_table = of_match_ptr(sbrmi_of_match), - }, - .probe = sbrmi_probe, - .id_table = sbrmi_id, -}; - -module_i2c_driver(sbrmi_driver); - -MODULE_AUTHOR("Akshay Gupta <akshay.gupta@amd.com>"); -MODULE_DESCRIPTION("Hwmon driver for AMD SB-RMI emulated sensor"); -MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c index 7d7d70afde65..a23edd35c19f 100644 --- a/drivers/hwmon/w83627ehf.c +++ b/drivers/hwmon/w83627ehf.c @@ -1448,7 +1448,8 @@ w83627ehf_do_read_temp(struct w83627ehf_data *data, u32 attr, return 0; case hwmon_temp_alarm: if (channel < 3) { - int bit[] = { 4, 5, 13 }; + static const int bit[] = { 4, 5, 13 }; + *val = (data->alarms >> bit[channel]) & 1; return 0; } @@ -1479,7 +1480,8 @@ w83627ehf_do_read_in(struct w83627ehf_data *data, u32 attr, return 0; case hwmon_in_alarm: if (channel < 10) { - int bit[] = { 0, 1, 2, 3, 8, 21, 20, 16, 17, 19 }; + static const int bit[] = { 0, 1, 2, 3, 8, 21, 20, 16, 17, 19 }; + *val = (data->alarms >> bit[channel]) & 1; return 0; } @@ -1507,7 +1509,8 @@ w83627ehf_do_read_fan(struct w83627ehf_data *data, u32 attr, return 0; case hwmon_fan_alarm: if (channel < 5) { - int bit[] = { 6, 7, 11, 10, 23 }; + static const int bit[] = { 6, 7, 11, 10, 23 }; + *val = (data->alarms >> bit[channel]) & 1; return 0; } |