diff options
Diffstat (limited to 'drivers/hwmon')
88 files changed, 1500 insertions, 337 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index dd376602f3f1..4cbaba15d86e 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -324,7 +324,7 @@ config SENSORS_K8TEMP config SENSORS_K10TEMP tristate "AMD Family 10h+ temperature sensor" - depends on X86 && PCI && AMD_NB + depends on X86 && PCI && AMD_NODE help If you say yes here you get support for the temperature sensor(s) inside your CPU. Supported are later revisions of @@ -413,7 +413,7 @@ config SENSORS_ASPEED will be called aspeed_pwm_tacho. config SENSORS_ASPEED_G6 - tristate "ASPEED g6 PWM and Fan tach driver" + tristate "ASPEED G6 PWM and Fan tach driver" depends on ARCH_ASPEED || COMPILE_TEST depends on PWM help @@ -421,7 +421,7 @@ config SENSORS_ASPEED_G6 controllers. This driver can also be built as a module. If so, the module - will be called aspeed_pwm_tacho. + will be called aspeed_g6_pwm_tach. config SENSORS_ATXP1 tristate "Attansic ATXP1 VID controller" @@ -1412,7 +1412,9 @@ config SENSORS_LM73 config SENSORS_LM75 tristate "National Semiconductor LM75 and compatibles" depends on I2C + depends on I3C || !I3C select REGMAP_I2C + select REGMAP_I3C if I3C help If you say yes here you get support for one common type of temperature sensor chip, with models including: @@ -1822,6 +1824,18 @@ config SENSORS_PWM_FAN This driver can also be built as a module. If so, the module will be called pwm-fan. +config SENSORS_QNAP_MCU_HWMON + tristate "QNAP MCU hardware monitoring" + depends on MFD_QNAP_MCU + depends on THERMAL || THERMAL=n + help + Say yes here to enable support for fan and temperature sensor + connected to a QNAP MCU, as found in a number of QNAP network + attached storage devices. + + This driver can also be built as a module. If so, the module + will be called qnap-mcu-hwmon. + config SENSORS_RASPBERRYPI_HWMON tristate "Raspberry Pi voltage monitor" depends on RASPBERRYPI_FIRMWARE || (COMPILE_TEST && !RASPBERRYPI_FIRMWARE) diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index b827b92f2a78..b7ef0f0562d3 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -189,6 +189,7 @@ obj-$(CONFIG_SENSORS_POWERZ) += powerz.o obj-$(CONFIG_SENSORS_POWR1220) += powr1220.o obj-$(CONFIG_SENSORS_PT5161L) += pt5161l.o obj-$(CONFIG_SENSORS_PWM_FAN) += pwm-fan.o +obj-$(CONFIG_SENSORS_QNAP_MCU_HWMON) += qnap-mcu-hwmon.o obj-$(CONFIG_SENSORS_RASPBERRYPI_HWMON) += raspberrypi-hwmon.o obj-$(CONFIG_SENSORS_SBTSI) += sbtsi_temp.o obj-$(CONFIG_SENSORS_SBRMI) += sbrmi.o diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c index 2f1c9d97ad21..44afb07409a4 100644 --- a/drivers/hwmon/acpi_power_meter.c +++ b/drivers/hwmon/acpi_power_meter.c @@ -84,6 +84,7 @@ struct acpi_power_meter_resource { u64 power; u64 cap; u64 avg_interval; + bool power_alarm; int sensors_valid; unsigned long sensors_last_updated; struct sensor_device_attribute sensors[NUM_SENSORS]; @@ -292,8 +293,8 @@ static ssize_t set_trip(struct device *dev, struct device_attribute *devattr, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct acpi_device *acpi_dev = to_acpi_device(dev); struct acpi_power_meter_resource *resource = acpi_dev->driver_data; + unsigned long temp, trip_bk; int res; - unsigned long temp; res = kstrtoul(buf, 10, &temp); if (res) @@ -301,13 +302,15 @@ static ssize_t set_trip(struct device *dev, struct device_attribute *devattr, temp = DIV_ROUND_CLOSEST(temp, 1000); - mutex_lock(&resource->lock); + guard(mutex)(&resource->lock); + + trip_bk = resource->trip[attr->index - 7]; resource->trip[attr->index - 7] = temp; res = set_acpi_trip(resource); - mutex_unlock(&resource->lock); - - if (res) + if (res) { + resource->trip[attr->index - 7] = trip_bk; return res; + } return count; } @@ -396,6 +399,9 @@ static ssize_t show_val(struct device *dev, struct acpi_device *acpi_dev = to_acpi_device(dev); struct acpi_power_meter_resource *resource = acpi_dev->driver_data; u64 val = 0; + int ret; + + guard(mutex)(&resource->lock); switch (attr->index) { case 0: @@ -423,10 +429,17 @@ static ssize_t show_val(struct device *dev, val = 0; break; case 6: - if (resource->power > resource->cap) - val = 1; - else - val = 0; + ret = update_meter(resource); + if (ret) + return ret; + /* need to update cap if not to support the notification. */ + if (!(resource->caps.flags & POWER_METER_CAN_NOTIFY)) { + ret = update_cap(resource); + if (ret) + return ret; + } + val = resource->power_alarm || resource->power > resource->cap; + resource->power_alarm = resource->power > resource->cap; break; case 7: case 8: @@ -682,7 +695,7 @@ static int setup_attrs(struct acpi_power_meter_resource *resource) /* _PMD method is optional. */ res = read_domain_devices(resource); - if (res != -ENODEV) + if (res && res != -ENODEV) return res; if (resource->caps.flags & POWER_METER_CAN_MEASURE) { @@ -847,12 +860,20 @@ static void acpi_power_meter_notify(struct acpi_device *device, u32 event) sysfs_notify(&device->dev.kobj, NULL, POWER_AVERAGE_NAME); break; case METER_NOTIFY_CAP: + mutex_lock(&resource->lock); + res = update_cap(resource); + if (res) + dev_err_once(&device->dev, "update cap failed when capping value is changed.\n"); + mutex_unlock(&resource->lock); sysfs_notify(&device->dev.kobj, NULL, POWER_CAP_NAME); break; case METER_NOTIFY_INTERVAL: sysfs_notify(&device->dev.kobj, NULL, POWER_AVG_INTERVAL_NAME); break; case METER_NOTIFY_CAPPING: + mutex_lock(&resource->lock); + resource->power_alarm = true; + mutex_unlock(&resource->lock); sysfs_notify(&device->dev.kobj, NULL, POWER_ALARM_NAME); dev_info(&device->dev, "Capping in progress.\n"); break; diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c index 9555366aeaf0..43e54dc513da 100644 --- a/drivers/hwmon/asus-ec-sensors.c +++ b/drivers/hwmon/asus-ec-sensors.c @@ -250,6 +250,8 @@ static const struct ec_sensor_info sensors_family_amd_600[] = { EC_SENSOR("Water_In", hwmon_temp, 1, 0x01, 0x00), [ec_sensor_temp_water_out] = EC_SENSOR("Water_Out", hwmon_temp, 1, 0x01, 0x01), + [ec_sensor_fan_cpu_opt] = + EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xb0), }; static const struct ec_sensor_info sensors_family_intel_300[] = { @@ -477,6 +479,15 @@ static const struct ec_board_info board_info_zenith_ii_extreme = { .family = family_amd_500_series, }; +static const struct ec_board_info board_info_tuf_gaming_x670e_plus = { + .sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE | + SENSOR_TEMP_MB | SENSOR_TEMP_VRM | + SENSOR_TEMP_WATER_IN | SENSOR_TEMP_WATER_OUT | + SENSOR_FAN_CPU_OPT, + .mutex_path = ACPI_GLOBAL_LOCK_PSEUDO_PATH, + .family = family_amd_600_series, +}; + #define DMI_EXACT_MATCH_ASUS_BOARD_NAME(name, board_info) \ { \ .matches = { \ @@ -538,6 +549,8 @@ static const struct dmi_system_id dmi_table[] = { &board_info_zenith_ii_extreme), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG ZENITH II EXTREME ALPHA", &board_info_zenith_ii_extreme), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("TUF GAMING X670E-PLUS", + &board_info_tuf_gaming_x670e_plus), {}, }; diff --git a/drivers/hwmon/asus_atk0110.c b/drivers/hwmon/asus_atk0110.c index 1dc7e24fe4c5..c80350e499e9 100644 --- a/drivers/hwmon/asus_atk0110.c +++ b/drivers/hwmon/asus_atk0110.c @@ -17,6 +17,7 @@ #include <linux/jiffies.h> #include <linux/err.h> #include <linux/acpi.h> +#include <linux/string_choices.h> #define ATK_HID "ATK0110" @@ -441,7 +442,7 @@ static void atk_print_sensor(struct atk_data *data, union acpi_object *obj) flags->integer.value, name->string.pointer, limit1->integer.value, limit2->integer.value, - enable->integer.value ? "enabled" : "disabled"); + str_enabled_disabled(enable->integer.value)); #endif } @@ -1074,8 +1075,7 @@ static int atk_ec_enabled(struct atk_data *data) err = -EIO; } else { err = (buf->value != 0); - dev_dbg(dev, "EC is %sabled\n", - err ? "en" : "dis"); + dev_dbg(dev, "EC is %s\n", str_enabled_disabled(err)); } ACPI_FREE(obj); @@ -1096,18 +1096,15 @@ static int atk_ec_ctl(struct atk_data *data, int enable) obj = atk_sitm(data, &sitm); if (IS_ERR(obj)) { - dev_err(dev, "Failed to %sable the EC\n", - enable ? "en" : "dis"); + dev_err(dev, "Failed to %s the EC\n", str_enable_disable(enable)); return PTR_ERR(obj); } ec_ret = (struct atk_acpi_ret_buffer *)obj->buffer.pointer; if (ec_ret->flags == 0) { - dev_err(dev, "Failed to %sable the EC\n", - enable ? "en" : "dis"); + dev_err(dev, "Failed to %s the EC\n", str_enable_disable(enable)); err = -EIO; } else { - dev_info(dev, "EC %sabled\n", - enable ? "en" : "dis"); + dev_info(dev, "EC %s\n", str_enabled_disabled(enable)); } ACPI_FREE(obj); diff --git a/drivers/hwmon/chipcap2.c b/drivers/hwmon/chipcap2.c index edf454474f11..9d071f7ca9d2 100644 --- a/drivers/hwmon/chipcap2.c +++ b/drivers/hwmon/chipcap2.c @@ -13,6 +13,7 @@ #include <linux/bitfield.h> #include <linux/bits.h> +#include <linux/cleanup.h> #include <linux/completion.h> #include <linux/delay.h> #include <linux/hwmon.h> @@ -556,55 +557,40 @@ static int cc2_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val) { struct cc2_data *data = dev_get_drvdata(dev); - int ret = 0; - mutex_lock(&data->dev_access_lock); + guard(mutex)(&data->dev_access_lock); switch (type) { case hwmon_temp: - ret = cc2_measurement(data, type, val); - break; + return cc2_measurement(data, type, val); case hwmon_humidity: switch (attr) { case hwmon_humidity_input: - ret = cc2_measurement(data, type, val); - break; + return cc2_measurement(data, type, val); case hwmon_humidity_min: - ret = cc2_get_reg_val(data, CC2_R_ALARM_L_ON, val); - break; + return cc2_get_reg_val(data, CC2_R_ALARM_L_ON, val); case hwmon_humidity_min_hyst: - ret = cc2_get_reg_val(data, CC2_R_ALARM_L_OFF, val); - break; + return cc2_get_reg_val(data, CC2_R_ALARM_L_OFF, val); case hwmon_humidity_max: - ret = cc2_get_reg_val(data, CC2_R_ALARM_H_ON, val); - break; + return cc2_get_reg_val(data, CC2_R_ALARM_H_ON, val); case hwmon_humidity_max_hyst: - ret = cc2_get_reg_val(data, CC2_R_ALARM_H_OFF, val); - break; + return cc2_get_reg_val(data, CC2_R_ALARM_H_OFF, val); case hwmon_humidity_min_alarm: - ret = cc2_humidity_min_alarm_status(data, val); - break; + return cc2_humidity_min_alarm_status(data, val); case hwmon_humidity_max_alarm: - ret = cc2_humidity_max_alarm_status(data, val); - break; + return cc2_humidity_max_alarm_status(data, val); default: - ret = -EOPNOTSUPP; + return -EOPNOTSUPP; } - break; default: - ret = -EOPNOTSUPP; + return -EOPNOTSUPP; } - - mutex_unlock(&data->dev_access_lock); - - return ret; } static int cc2_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long val) { struct cc2_data *data = dev_get_drvdata(dev); - int ret; u16 arg; u8 cmd; @@ -614,41 +600,28 @@ static int cc2_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, if (val < 0 || val > CC2_RH_MAX) return -EINVAL; - mutex_lock(&data->dev_access_lock); + guard(mutex)(&data->dev_access_lock); switch (attr) { case hwmon_humidity_min: cmd = CC2_W_ALARM_L_ON; arg = cc2_rh_to_reg(val); - ret = cc2_write_reg(data, cmd, arg); - break; - + return cc2_write_reg(data, cmd, arg); case hwmon_humidity_min_hyst: cmd = CC2_W_ALARM_L_OFF; arg = cc2_rh_to_reg(val); - ret = cc2_write_reg(data, cmd, arg); - break; - + return cc2_write_reg(data, cmd, arg); case hwmon_humidity_max: cmd = CC2_W_ALARM_H_ON; arg = cc2_rh_to_reg(val); - ret = cc2_write_reg(data, cmd, arg); - break; - + return cc2_write_reg(data, cmd, arg); case hwmon_humidity_max_hyst: cmd = CC2_W_ALARM_H_OFF; arg = cc2_rh_to_reg(val); - ret = cc2_write_reg(data, cmd, arg); - break; - + return cc2_write_reg(data, cmd, arg); default: - ret = -EOPNOTSUPP; - break; + return -EOPNOTSUPP; } - - mutex_unlock(&data->dev_access_lock); - - return ret; } static int cc2_request_ready_irq(struct cc2_data *data, struct device *dev) diff --git a/drivers/hwmon/dell-smm-hwmon.c b/drivers/hwmon/dell-smm-hwmon.c index f5bdf842040e..cd00adaad1b4 100644 --- a/drivers/hwmon/dell-smm-hwmon.c +++ b/drivers/hwmon/dell-smm-hwmon.c @@ -1545,6 +1545,14 @@ static const struct dmi_system_id i8k_whitelist_fan_control[] __initconst = { .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3], }, { + .ident = "Dell XPS 13 9370", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "XPS 13 9370"), + }, + .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_30A3_31A3], + }, + { .ident = "Dell Optiplex 7000", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), diff --git a/drivers/hwmon/drivetemp.c b/drivers/hwmon/drivetemp.c index 6bdd21aa005a..291d91f68646 100644 --- a/drivers/hwmon/drivetemp.c +++ b/drivers/hwmon/drivetemp.c @@ -165,6 +165,7 @@ static int drivetemp_scsi_command(struct drivetemp_data *st, { u8 scsi_cmd[MAX_COMMAND_SIZE]; enum req_op op; + int err; memset(scsi_cmd, 0, sizeof(scsi_cmd)); scsi_cmd[0] = ATA_16; @@ -192,8 +193,11 @@ static int drivetemp_scsi_command(struct drivetemp_data *st, scsi_cmd[12] = lba_high; scsi_cmd[14] = ata_command; - return scsi_execute_cmd(st->sdev, scsi_cmd, op, st->smartdata, - ATA_SECT_SIZE, HZ, 5, NULL); + err = scsi_execute_cmd(st->sdev, scsi_cmd, op, st->smartdata, + ATA_SECT_SIZE, 10 * HZ, 5, NULL); + if (err > 0) + err = -EIO; + return err; } static int drivetemp_ata_command(struct drivetemp_data *st, u8 feature, diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index 49b366254529..b7c0b1e3c23b 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -158,11 +158,6 @@ static umode_t hwmon_is_visible(const struct hwmon_ops *ops, /* Thermal zone handling */ -/* - * The complex conditional is necessary to avoid a cyclic dependency - * between hwmon and thermal_sys modules. - */ -#ifdef CONFIG_THERMAL_OF static int hwmon_thermal_get_temp(struct thermal_zone_device *tz, int *temp) { struct hwmon_thermal_data *tdata = thermal_zone_device_priv(tz); @@ -268,6 +263,9 @@ static int hwmon_thermal_register_sensors(struct device *dev) void *drvdata = dev_get_drvdata(dev); int i; + if (!IS_ENABLED(CONFIG_THERMAL_OF)) + return 0; + for (i = 1; info[i]; i++) { int j; @@ -296,6 +294,9 @@ static void hwmon_thermal_notify(struct device *dev, int index) struct hwmon_device *hwdev = to_hwmon_device(dev); struct hwmon_thermal_data *tzdata; + if (!IS_ENABLED(CONFIG_THERMAL_OF)) + return; + list_for_each_entry(tzdata, &hwdev->tzdata, node) { if (tzdata->index == index) { thermal_zone_device_update(tzdata->tzd, @@ -304,16 +305,6 @@ static void hwmon_thermal_notify(struct device *dev, int index) } } -#else -static int hwmon_thermal_register_sensors(struct device *dev) -{ - return 0; -} - -static void hwmon_thermal_notify(struct device *dev, int index) { } - -#endif /* IS_REACHABLE(CONFIG_THERMAL) && ... */ - static int hwmon_attr_base(enum hwmon_sensor_types type) { if (type == hwmon_in || type == hwmon_intrusion) @@ -1074,7 +1065,7 @@ hwmon_device_register_for_thermal(struct device *dev, const char *name, return __hwmon_device_register(dev, name, drvdata, NULL, NULL); } -EXPORT_SYMBOL_NS_GPL(hwmon_device_register_for_thermal, HWMON_THERMAL); +EXPORT_SYMBOL_NS_GPL(hwmon_device_register_for_thermal, "HWMON_THERMAL"); /** * hwmon_device_register - register w/ hwmon @@ -1179,6 +1170,12 @@ devm_hwmon_device_register_with_info(struct device *dev, const char *name, if (!dev) return ERR_PTR(-EINVAL); + if (!name) { + name = devm_hwmon_sanitize_name(dev, dev_name(dev)); + if (IS_ERR(name)) + return ERR_CAST(name); + } + ptr = devres_alloc(devm_hwmon_release, sizeof(*ptr), GFP_KERNEL); if (!ptr) return ERR_PTR(-ENOMEM); diff --git a/drivers/hwmon/intel-m10-bmc-hwmon.c b/drivers/hwmon/intel-m10-bmc-hwmon.c index e221f2c1f332..aa01a4bedc21 100644 --- a/drivers/hwmon/intel-m10-bmc-hwmon.c +++ b/drivers/hwmon/intel-m10-bmc-hwmon.c @@ -787,4 +787,4 @@ MODULE_DEVICE_TABLE(platform, intel_m10bmc_hwmon_ids); MODULE_AUTHOR("Intel Corporation"); MODULE_DESCRIPTION("Intel MAX 10 BMC hardware monitor"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(INTEL_M10_BMC_CORE); +MODULE_IMPORT_NS("INTEL_M10_BMC_CORE"); diff --git a/drivers/hwmon/isl28022.c b/drivers/hwmon/isl28022.c index f9edcfd164c2..3f9b4520b53e 100644 --- a/drivers/hwmon/isl28022.c +++ b/drivers/hwmon/isl28022.c @@ -486,7 +486,7 @@ static int isl28022_probe(struct i2c_client *client) } static const struct i2c_device_id isl28022_ids[] = { - { "isl28022", 0}, + { "isl28022" }, { /* LIST END */ } }; MODULE_DEVICE_TABLE(i2c, isl28022_ids); @@ -506,8 +506,7 @@ static struct i2c_driver isl28022_driver = { .id_table = isl28022_ids, }; -static int __init -isl28022_init(void) +static int __init isl28022_init(void) { int err; @@ -519,15 +518,13 @@ isl28022_init(void) debugfs_remove_recursive(isl28022_debugfs_root); return err; } +module_init(isl28022_init); -static void __exit -isl28022_exit(void) +static void __exit isl28022_exit(void) { i2c_del_driver(&isl28022_driver); debugfs_remove_recursive(isl28022_debugfs_root); } - -module_init(isl28022_init); module_exit(isl28022_exit); MODULE_AUTHOR("Carsten Spieß <mail@carsten-spiess.de>"); diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c index 7dc19c5d62ac..d0b4cc9a5011 100644 --- a/drivers/hwmon/k10temp.c +++ b/drivers/hwmon/k10temp.c @@ -20,7 +20,7 @@ #include <linux/module.h> #include <linux/pci.h> #include <linux/pci_ids.h> -#include <asm/amd_nb.h> +#include <asm/amd_node.h> #include <asm/processor.h> MODULE_DESCRIPTION("AMD Family 10h+ CPU core temperature monitor"); @@ -150,6 +150,11 @@ static void read_tempreg_nb_f15(struct pci_dev *pdev, u32 *regval) F15H_M60H_REPORTED_TEMP_CTRL_OFFSET, regval); } +static u16 amd_pci_dev_to_node_id(struct pci_dev *pdev) +{ + return PCI_SLOT(pdev->devfn) - AMD_NODE0_PCI_SLOT; +} + static void read_tempreg_nb_zen(struct pci_dev *pdev, u32 *regval) { if (amd_smn_read(amd_pci_dev_to_node_id(pdev), diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c index 2c2205aec7d4..d95a3c6c245c 100644 --- a/drivers/hwmon/lm75.c +++ b/drivers/hwmon/lm75.c @@ -11,6 +11,7 @@ #include <linux/slab.h> #include <linux/jiffies.h> #include <linux/i2c.h> +#include <linux/i3c/device.h> #include <linux/hwmon.h> #include <linux/err.h> #include <linux/of.h> @@ -38,6 +39,7 @@ enum lm75_type { /* keep sorted in alphabetical order */ max6626, max31725, mcp980x, + p3t1755, pct2075, stds75, stlm75, @@ -104,17 +106,15 @@ static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c, #define LM75_REG_MAX 0x03 #define PCT2075_REG_IDLE 0x04 -/* Each client has this additional data */ struct lm75_data { - struct i2c_client *client; struct regmap *regmap; - struct regulator *vs; u16 orig_conf; - u16 current_conf; u8 resolution; /* In bits, 9 to 16 */ unsigned int sample_time; /* In ms */ enum lm75_type kind; const struct lm75_params *params; + u8 reg_buf[1]; + u8 val_buf[3]; }; /*-----------------------------------------------------------------------*/ @@ -222,6 +222,13 @@ static const struct lm75_params device_params[] = { .default_resolution = 9, .default_sample_time = MSEC_PER_SEC / 18, }, + [p3t1755] = { + .clr_mask = 1 << 1 | 1 << 7, /* disable SMBAlert and one-shot */ + .default_resolution = 12, + .default_sample_time = 55, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 28, 55, 110, 220 }, + }, [pct2075] = { .default_resolution = 11, .default_sample_time = MSEC_PER_SEC / 10, @@ -276,6 +283,7 @@ static const struct lm75_params device_params[] = { .default_sample_time = 125, .num_sample_times = 4, .sample_times = (unsigned int []){ 125, 250, 1000, 4000 }, + .alarm = true, }, [tmp175] = { .set_mask = 3 << 5, /* 12-bit mode */ @@ -332,41 +340,11 @@ static inline long lm75_reg_to_mc(s16 temp, u8 resolution) return ((temp >> (16 - resolution)) * 1000) >> (resolution - 8); } -static int lm75_write_config(struct lm75_data *data, u16 set_mask, - u16 clr_mask) -{ - unsigned int value; - - clr_mask |= LM75_SHUTDOWN << (8 * data->params->config_reg_16bits); - value = data->current_conf & ~clr_mask; - value |= set_mask; - - if (data->current_conf != value) { - s32 err; - if (data->params->config_reg_16bits) - err = regmap_write(data->regmap, LM75_REG_CONF, value); - else - err = i2c_smbus_write_byte_data(data->client, - LM75_REG_CONF, - value); - if (err) - return err; - data->current_conf = value; - } - return 0; -} - -static int lm75_read_config(struct lm75_data *data) +static inline int lm75_write_config(struct lm75_data *data, u16 set_mask, + u16 clr_mask) { - int ret; - unsigned int status; - - if (data->params->config_reg_16bits) { - ret = regmap_read(data->regmap, LM75_REG_CONF, &status); - return ret ? ret : status; - } - - return i2c_smbus_read_byte_data(data->client, LM75_REG_CONF); + return regmap_update_bits(data->regmap, LM75_REG_CONF, + clr_mask | LM75_SHUTDOWN, set_mask); } static irqreturn_t lm75_alarm_handler(int irq, void *private) @@ -418,7 +396,8 @@ static int lm75_read(struct device *dev, enum hwmon_sensor_types type, if (attr == hwmon_temp_alarm) { switch (data->kind) { case as6200: - *val = (regval >> 5) & 0x1; + case tmp112: + *val = (regval >> 13) & 0x1; break; default: return -EINVAL; @@ -469,7 +448,6 @@ static int lm75_write_temp(struct device *dev, u32 attr, long temp) static int lm75_update_interval(struct device *dev, long val) { struct lm75_data *data = dev_get_drvdata(dev); - unsigned int reg; u8 index; s32 err; @@ -489,19 +467,14 @@ static int lm75_update_interval(struct device *dev, long val) break; case tmp112: case as6200: - err = regmap_read(data->regmap, LM75_REG_CONF, ®); - if (err < 0) - return err; - reg &= ~0x00c0; - reg |= (3 - index) << 6; - err = regmap_write(data->regmap, LM75_REG_CONF, reg); + err = regmap_update_bits(data->regmap, LM75_REG_CONF, + 0xc000, (3 - index) << 14); if (err < 0) return err; data->sample_time = data->params->sample_times[index]; break; case pct2075: - err = i2c_smbus_write_byte_data(data->client, PCT2075_REG_IDLE, - index + 1); + err = regmap_write(data->regmap, PCT2075_REG_IDLE, index + 1); if (err) return err; data->sample_time = data->params->sample_times[index]; @@ -598,6 +571,115 @@ static bool lm75_is_volatile_reg(struct device *dev, unsigned int reg) return reg == LM75_REG_TEMP || reg == LM75_REG_CONF; } +static int lm75_i2c_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + struct i2c_client *client = context; + struct lm75_data *data = i2c_get_clientdata(client); + int ret; + + if (reg == LM75_REG_CONF) { + if (!data->params->config_reg_16bits) + ret = i2c_smbus_read_byte_data(client, LM75_REG_CONF); + else + ret = i2c_smbus_read_word_data(client, LM75_REG_CONF); + } else { + ret = i2c_smbus_read_word_swapped(client, reg); + } + if (ret < 0) + return ret; + *val = ret; + return 0; +} + +static int lm75_i2c_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct i2c_client *client = context; + struct lm75_data *data = i2c_get_clientdata(client); + + if (reg == PCT2075_REG_IDLE || + (reg == LM75_REG_CONF && !data->params->config_reg_16bits)) + return i2c_smbus_write_byte_data(client, reg, val); + else if (reg == LM75_REG_CONF) + return i2c_smbus_write_word_data(client, reg, val); + return i2c_smbus_write_word_swapped(client, reg, val); +} + +static const struct regmap_bus lm75_i2c_regmap_bus = { + .reg_read = lm75_i2c_reg_read, + .reg_write = lm75_i2c_reg_write, +}; + +static int lm75_i3c_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + struct i3c_device *i3cdev = context; + struct lm75_data *data = i3cdev_get_drvdata(i3cdev); + struct i3c_priv_xfer xfers[] = { + { + .rnw = false, + .len = 1, + .data.out = data->reg_buf, + }, + { + .rnw = true, + .len = 2, + .data.out = data->val_buf, + }, + }; + int ret; + + data->reg_buf[0] = reg; + + if (reg == LM75_REG_CONF && !data->params->config_reg_16bits) + xfers[1].len--; + + ret = i3c_device_do_priv_xfers(i3cdev, xfers, 2); + if (ret < 0) + return ret; + + if (reg == LM75_REG_CONF && !data->params->config_reg_16bits) + *val = data->val_buf[0]; + else if (reg == LM75_REG_CONF) + *val = data->val_buf[0] | (data->val_buf[1] << 8); + else + *val = data->val_buf[1] | (data->val_buf[0] << 8); + + return 0; +} + +static int lm75_i3c_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct i3c_device *i3cdev = context; + struct lm75_data *data = i3cdev_get_drvdata(i3cdev); + struct i3c_priv_xfer xfers[] = { + { + .rnw = false, + .len = 3, + .data.out = data->val_buf, + }, + }; + + data->val_buf[0] = reg; + + if (reg == PCT2075_REG_IDLE || + (reg == LM75_REG_CONF && !data->params->config_reg_16bits)) { + xfers[0].len--; + data->val_buf[1] = val & 0xff; + } else if (reg == LM75_REG_CONF) { + data->val_buf[1] = val & 0xff; + data->val_buf[2] = (val >> 8) & 0xff; + } else { + data->val_buf[1] = (val >> 8) & 0xff; + data->val_buf[2] = val & 0xff; + } + + return i3c_device_do_priv_xfers(i3cdev, xfers, 1); +} + +static const struct regmap_bus lm75_i3c_regmap_bus = { + .reg_read = lm75_i3c_reg_read, + .reg_write = lm75_i3c_reg_write, +}; + static const struct regmap_config lm75_regmap_config = { .reg_bits = 8, .val_bits = 16, @@ -610,46 +692,33 @@ static const struct regmap_config lm75_regmap_config = { .use_single_write = true, }; -static void lm75_disable_regulator(void *data) -{ - struct lm75_data *lm75 = data; - - regulator_disable(lm75->vs); -} - static void lm75_remove(void *data) { struct lm75_data *lm75 = data; - struct i2c_client *client = lm75->client; - i2c_smbus_write_byte_data(client, LM75_REG_CONF, lm75->orig_conf); + regmap_write(lm75->regmap, LM75_REG_CONF, lm75->orig_conf); } -static int lm75_probe(struct i2c_client *client) +static int lm75_generic_probe(struct device *dev, const char *name, + enum lm75_type kind, int irq, struct regmap *regmap) { - struct device *dev = &client->dev; struct device *hwmon_dev; struct lm75_data *data; int status, err; - if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) - return -EIO; - data = devm_kzalloc(dev, sizeof(struct lm75_data), GFP_KERNEL); if (!data) return -ENOMEM; - data->client = client; - data->kind = (uintptr_t)i2c_get_match_data(client); + /* needed by custom regmap callbacks */ + dev_set_drvdata(dev, data); - data->vs = devm_regulator_get(dev, "vs"); - if (IS_ERR(data->vs)) - return PTR_ERR(data->vs); + data->kind = kind; + data->regmap = regmap; - data->regmap = devm_regmap_init_i2c(client, &lm75_regmap_config); - if (IS_ERR(data->regmap)) - return PTR_ERR(data->regmap); + err = devm_regulator_get_enable(dev, "vs"); + if (err) + return err; /* Set to LM75 resolution (9 bits, 1/2 degree C) and range. * Then tweak to be more precise when appropriate. @@ -661,25 +730,11 @@ static int lm75_probe(struct i2c_client *client) data->sample_time = data->params->default_sample_time; data->resolution = data->params->default_resolution; - /* Enable the power */ - err = regulator_enable(data->vs); - if (err) { - dev_err(dev, "failed to enable regulator: %d\n", err); - return err; - } - - err = devm_add_action_or_reset(dev, lm75_disable_regulator, data); + /* Cache original configuration */ + err = regmap_read(data->regmap, LM75_REG_CONF, &status); if (err) return err; - - /* Cache original configuration */ - status = lm75_read_config(data); - if (status < 0) { - dev_dbg(dev, "Can't read config? %d\n", status); - return status; - } data->orig_conf = status; - data->current_conf = status; err = lm75_write_config(data, data->params->set_mask, data->params->clr_mask); @@ -690,20 +745,19 @@ static int lm75_probe(struct i2c_client *client) if (err) return err; - hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, - data, &lm75_chip_info, - NULL); + hwmon_dev = devm_hwmon_device_register_with_info(dev, name, data, + &lm75_chip_info, NULL); if (IS_ERR(hwmon_dev)) return PTR_ERR(hwmon_dev); - if (client->irq) { + if (irq) { if (data->params->alarm) { err = devm_request_threaded_irq(dev, - client->irq, + irq, NULL, &lm75_alarm_handler, IRQF_ONESHOT, - client->name, + name, hwmon_dev); if (err) return err; @@ -713,12 +767,29 @@ static int lm75_probe(struct i2c_client *client) } } - dev_info(dev, "%s: sensor '%s'\n", dev_name(hwmon_dev), client->name); + dev_info(dev, "%s: sensor '%s'\n", dev_name(hwmon_dev), name); return 0; } -static const struct i2c_device_id lm75_ids[] = { +static int lm75_i2c_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct regmap *regmap; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) + return -EOPNOTSUPP; + + regmap = devm_regmap_init(dev, &lm75_i2c_regmap_bus, client, &lm75_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return lm75_generic_probe(dev, client->name, (uintptr_t)i2c_get_match_data(client), + client->irq, regmap); +} + +static const struct i2c_device_id lm75_i2c_ids[] = { { "adt75", adt75, }, { "as6200", as6200, }, { "at30ts74", at30ts74, }, @@ -734,6 +805,7 @@ static const struct i2c_device_id lm75_ids[] = { { "max31725", max31725, }, { "max31726", max31725, }, { "mcp980x", mcp980x, }, + { "p3t1755", p3t1755, }, { "pct2075", pct2075, }, { "stds75", stds75, }, { "stlm75", stlm75, }, @@ -750,7 +822,38 @@ static const struct i2c_device_id lm75_ids[] = { { "tmp1075", tmp1075, }, { /* LIST END */ } }; -MODULE_DEVICE_TABLE(i2c, lm75_ids); +MODULE_DEVICE_TABLE(i2c, lm75_i2c_ids); + +struct lm75_i3c_device { + enum lm75_type type; + const char *name; +}; + +static const struct lm75_i3c_device lm75_i3c_p3t1755 = { + .name = "p3t1755", + .type = p3t1755, +}; + +static const struct i3c_device_id lm75_i3c_ids[] = { + I3C_DEVICE(0x011b, 0x152a, &lm75_i3c_p3t1755), + { /* LIST END */ } +}; +MODULE_DEVICE_TABLE(i3c, lm75_i3c_ids); + +static int lm75_i3c_probe(struct i3c_device *i3cdev) +{ + struct device *dev = i3cdev_to_dev(i3cdev); + const struct lm75_i3c_device *id_data; + struct regmap *regmap; + + regmap = devm_regmap_init(dev, &lm75_i3c_regmap_bus, i3cdev, &lm75_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + id_data = i3c_device_match_id(i3cdev, lm75_i3c_ids)->data; + + return lm75_generic_probe(dev, id_data->name, id_data->type, 0, regmap); +} static const struct of_device_id __maybe_unused lm75_of_match[] = { { @@ -814,6 +917,10 @@ static const struct of_device_id __maybe_unused lm75_of_match[] = { .data = (void *)mcp980x }, { + .compatible = "nxp,p3t1755", + .data = (void *)p3t1755 + }, + { .compatible = "nxp,pct2075", .data = (void *)pct2075 }, @@ -972,32 +1079,16 @@ static int lm75_detect(struct i2c_client *new_client, #ifdef CONFIG_PM static int lm75_suspend(struct device *dev) { - int status; - struct i2c_client *client = to_i2c_client(dev); + struct lm75_data *data = dev_get_drvdata(dev); - status = i2c_smbus_read_byte_data(client, LM75_REG_CONF); - if (status < 0) { - dev_dbg(&client->dev, "Can't read config? %d\n", status); - return status; - } - status = status | LM75_SHUTDOWN; - i2c_smbus_write_byte_data(client, LM75_REG_CONF, status); - return 0; + return regmap_update_bits(data->regmap, LM75_REG_CONF, LM75_SHUTDOWN, LM75_SHUTDOWN); } static int lm75_resume(struct device *dev) { - int status; - struct i2c_client *client = to_i2c_client(dev); + struct lm75_data *data = dev_get_drvdata(dev); - status = i2c_smbus_read_byte_data(client, LM75_REG_CONF); - if (status < 0) { - dev_dbg(&client->dev, "Can't read config? %d\n", status); - return status; - } - status = status & ~LM75_SHUTDOWN; - i2c_smbus_write_byte_data(client, LM75_REG_CONF, status); - return 0; + return regmap_update_bits(data->regmap, LM75_REG_CONF, LM75_SHUTDOWN, 0); } static const struct dev_pm_ops lm75_dev_pm_ops = { @@ -1009,20 +1100,28 @@ static const struct dev_pm_ops lm75_dev_pm_ops = { #define LM75_DEV_PM_OPS NULL #endif /* CONFIG_PM */ -static struct i2c_driver lm75_driver = { +static struct i2c_driver lm75_i2c_driver = { .class = I2C_CLASS_HWMON, .driver = { .name = "lm75", .of_match_table = of_match_ptr(lm75_of_match), .pm = LM75_DEV_PM_OPS, }, - .probe = lm75_probe, - .id_table = lm75_ids, + .probe = lm75_i2c_probe, + .id_table = lm75_i2c_ids, .detect = lm75_detect, .address_list = normal_i2c, }; -module_i2c_driver(lm75_driver); +static struct i3c_driver lm75_i3c_driver = { + .driver = { + .name = "lm75_i3c", + }, + .probe = lm75_i3c_probe, + .id_table = lm75_i3c_ids, +}; + +module_i3c_i2c_driver(lm75_i3c_driver, &lm75_i2c_driver) MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>"); MODULE_DESCRIPTION("LM75 driver"); diff --git a/drivers/hwmon/ltc2991.c b/drivers/hwmon/ltc2991.c index 7ca139e4b6af..6d5d4cb846da 100644 --- a/drivers/hwmon/ltc2991.c +++ b/drivers/hwmon/ltc2991.c @@ -125,7 +125,7 @@ static int ltc2991_get_curr(struct ltc2991_state *st, u32 reg, int channel, /* Vx-Vy, 19.075uV/LSB */ *val = DIV_ROUND_CLOSEST(sign_extend32(reg_val, 14) * 19075, - st->r_sense_uohm[channel]); + (s32)st->r_sense_uohm[channel]); return 0; } diff --git a/drivers/hwmon/nct6683.c b/drivers/hwmon/nct6683.c index f71615e06a8f..416ac02e9f74 100644 --- a/drivers/hwmon/nct6683.c +++ b/drivers/hwmon/nct6683.c @@ -175,9 +175,11 @@ superio_exit(int ioreg) #define NCT6683_CUSTOMER_ID_MSI 0x201 #define NCT6683_CUSTOMER_ID_MSI2 0x200 #define NCT6683_CUSTOMER_ID_MSI3 0x207 +#define NCT6683_CUSTOMER_ID_MSI4 0x20d #define NCT6683_CUSTOMER_ID_ASROCK 0xe2c #define NCT6683_CUSTOMER_ID_ASROCK2 0xe1b #define NCT6683_CUSTOMER_ID_ASROCK3 0x1631 +#define NCT6683_CUSTOMER_ID_ASROCK4 0x163e #define NCT6683_REG_BUILD_YEAR 0x604 #define NCT6683_REG_BUILD_MONTH 0x605 @@ -1227,12 +1229,16 @@ static int nct6683_probe(struct platform_device *pdev) break; case NCT6683_CUSTOMER_ID_MSI3: break; + case NCT6683_CUSTOMER_ID_MSI4: + break; case NCT6683_CUSTOMER_ID_ASROCK: break; case NCT6683_CUSTOMER_ID_ASROCK2: break; case NCT6683_CUSTOMER_ID_ASROCK3: break; + case NCT6683_CUSTOMER_ID_ASROCK4: + break; default: if (!force) return -ENODEV; diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c index ee04795b98aa..fa3351351825 100644 --- a/drivers/hwmon/nct6775-core.c +++ b/drivers/hwmon/nct6775-core.c @@ -42,6 +42,9 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#undef DEFAULT_SYMBOL_NAMESPACE +#define DEFAULT_SYMBOL_NAMESPACE "HWMON_NCT6775" + #include <linux/module.h> #include <linux/init.h> #include <linux/slab.h> @@ -56,9 +59,6 @@ #include "lm75.h" #include "nct6775.h" -#undef DEFAULT_SYMBOL_NAMESPACE -#define DEFAULT_SYMBOL_NAMESPACE HWMON_NCT6775 - #define USE_ALTERNATE /* used to set data->name = nct6775_device_names[data->sio_kind] */ diff --git a/drivers/hwmon/nct6775-i2c.c b/drivers/hwmon/nct6775-i2c.c index aff69fa50461..ba71d776a291 100644 --- a/drivers/hwmon/nct6775-i2c.c +++ b/drivers/hwmon/nct6775-i2c.c @@ -184,4 +184,4 @@ module_i2c_driver(nct6775_i2c_driver); MODULE_AUTHOR("Zev Weiss <zev@bewilderbeest.net>"); MODULE_DESCRIPTION("I2C driver for NCT6775F and compatible chips"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(HWMON_NCT6775); +MODULE_IMPORT_NS("HWMON_NCT6775"); diff --git a/drivers/hwmon/nct6775-platform.c b/drivers/hwmon/nct6775-platform.c index 1218a3b449a8..0a040364b512 100644 --- a/drivers/hwmon/nct6775-platform.c +++ b/drivers/hwmon/nct6775-platform.c @@ -1622,7 +1622,7 @@ static void __exit sensors_nct6775_platform_exit(void) MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); MODULE_DESCRIPTION("Platform driver for NCT6775F and compatible chips"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(HWMON_NCT6775); +MODULE_IMPORT_NS("HWMON_NCT6775"); module_init(sensors_nct6775_platform_init); module_exit(sensors_nct6775_platform_exit); diff --git a/drivers/hwmon/occ/p9_sbe.c b/drivers/hwmon/occ/p9_sbe.c index 89761a9c8892..1e3749dfa598 100644 --- a/drivers/hwmon/occ/p9_sbe.c +++ b/drivers/hwmon/occ/p9_sbe.c @@ -30,7 +30,7 @@ struct p9_sbe_occ { #define to_p9_sbe_occ(x) container_of((x), struct p9_sbe_occ, occ) static ssize_t ffdc_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *battr, char *buf, loff_t pos, + const struct bin_attribute *battr, char *buf, loff_t pos, size_t count) { ssize_t rc = 0; @@ -48,7 +48,7 @@ static ssize_t ffdc_read(struct file *filp, struct kobject *kobj, return rc; } -static BIN_ATTR_RO(ffdc, OCC_MAX_RESP_WORDS * 4); +static const BIN_ATTR_RO(ffdc, OCC_MAX_RESP_WORDS * 4); static bool p9_sbe_occ_save_ffdc(struct p9_sbe_occ *ctx, const void *resp, size_t resp_len) diff --git a/drivers/hwmon/peci/cputemp.c b/drivers/hwmon/peci/cputemp.c index 5a682195b98f..c7112dbf008b 100644 --- a/drivers/hwmon/peci/cputemp.c +++ b/drivers/hwmon/peci/cputemp.c @@ -607,4 +607,4 @@ MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>"); MODULE_AUTHOR("Iwona Winiarska <iwona.winiarska@intel.com>"); MODULE_DESCRIPTION("PECI cputemp driver"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PECI_CPU); +MODULE_IMPORT_NS("PECI_CPU"); diff --git a/drivers/hwmon/peci/dimmtemp.c b/drivers/hwmon/peci/dimmtemp.c index 4a72e9712408..d6762259dd69 100644 --- a/drivers/hwmon/peci/dimmtemp.c +++ b/drivers/hwmon/peci/dimmtemp.c @@ -666,4 +666,4 @@ MODULE_AUTHOR("Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>"); MODULE_AUTHOR("Iwona Winiarska <iwona.winiarska@intel.com>"); MODULE_DESCRIPTION("PECI dimmtemp driver"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PECI_CPU); +MODULE_IMPORT_NS("PECI_CPU"); diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index f6d352841953..419469f40ba0 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -51,7 +51,7 @@ config SENSORS_ADM1275 tristate "Analog Devices ADM1275 and compatibles" help If you say yes here you get hardware monitoring support for Analog - Devices ADM1075, ADM1272, ADM1275, ADM1276, ADM1278, ADM1281, + Devices ADM1075, ADM1272, ADM1273, ADM1275, ADM1276, ADM1278, ADM1281, ADM1293, and ADM1294 Hot-Swap Controller and Digital Power Monitors. This driver can also be built as a module. If so, the module will @@ -85,6 +85,15 @@ config SENSORS_BPA_RS600 This driver can also be built as a module. If so, the module will be called bpa-rs600. +config SENSORS_CRPS + tristate "Intel Common Redundant Power Supply" + help + If you say yes here you get hardware monitoring support for the Intel + Common Redundant Power Supply. + + This driver can also be built as a module. If so, the module will + be called crps. + config SENSORS_DELTA_AHE50DC_FAN tristate "Delta AHE-50DC fan control module" help @@ -251,7 +260,7 @@ config SENSORS_MAX15301 tristate "Maxim MAX15301" help If you say yes here you get hardware monitoring support for Maxim - MAX15301, as well as for Flex BMR461. + MAX15301, MAX15303, as well as for Flex BMR461. This driver can also be built as a module. If so, the module will be called max15301. @@ -510,6 +519,23 @@ config SENSORS_TDA38640_REGULATOR If you say yes here you get regulator support for Infineon TDA38640 as regulator. +config SENSORS_TPS25990 + tristate "TI TPS25990" + help + If you say yes here you get hardware monitoring support for TI + TPS25990. + + This driver can also be built as a module. If so, the module will + be called tps25990. + +config SENSORS_TPS25990_REGULATOR + bool "Regulator support for TPS25990 and compatibles" + depends on SENSORS_TPS25990 && REGULATOR + default SENSORS_TPS25990 + help + If you say yes here you get regulator support for Texas Instruments + TPS25990. + config SENSORS_TPS40422 tristate "TI TPS40422" help diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile index d00bcc758b97..c7eb7739b7f8 100644 --- a/drivers/hwmon/pmbus/Makefile +++ b/drivers/hwmon/pmbus/Makefile @@ -51,6 +51,7 @@ obj-$(CONFIG_SENSORS_PXE1610) += pxe1610.o obj-$(CONFIG_SENSORS_Q54SJ108A2) += q54sj108a2.o obj-$(CONFIG_SENSORS_STPDDC60) += stpddc60.o obj-$(CONFIG_SENSORS_TDA38640) += tda38640.o +obj-$(CONFIG_SENSORS_TPS25990) += tps25990.o obj-$(CONFIG_SENSORS_TPS40422) += tps40422.o obj-$(CONFIG_SENSORS_TPS53679) += tps53679.o obj-$(CONFIG_SENSORS_TPS546D24) += tps546d24.o @@ -61,3 +62,4 @@ obj-$(CONFIG_SENSORS_XDPE122) += xdpe12284.o obj-$(CONFIG_SENSORS_XDPE152) += xdpe152c4.o obj-$(CONFIG_SENSORS_ZL6100) += zl6100.o obj-$(CONFIG_SENSORS_PIM4328) += pim4328.o +obj-$(CONFIG_SENSORS_CRPS) += crps.o diff --git a/drivers/hwmon/pmbus/acbel-fsg032.c b/drivers/hwmon/pmbus/acbel-fsg032.c index e0c55fd8f3a6..9f07fb4abaff 100644 --- a/drivers/hwmon/pmbus/acbel-fsg032.c +++ b/drivers/hwmon/pmbus/acbel-fsg032.c @@ -120,4 +120,4 @@ module_i2c_driver(acbel_fsg032_driver); MODULE_AUTHOR("Lakshmi Yadlapati"); MODULE_DESCRIPTION("PMBus driver for AcBel Power System power supplies"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/adm1266.c b/drivers/hwmon/pmbus/adm1266.c index 2c4d94cc8729..d90f8f80be8e 100644 --- a/drivers/hwmon/pmbus/adm1266.c +++ b/drivers/hwmon/pmbus/adm1266.c @@ -509,4 +509,4 @@ module_i2c_driver(adm1266_driver); MODULE_AUTHOR("Alexandru Tachici <alexandru.tachici@analog.com>"); MODULE_DESCRIPTION("PMBus driver for Analog Devices ADM1266"); MODULE_LICENSE("GPL v2"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c index 59ffc08289bd..7d175baa5de2 100644 --- a/drivers/hwmon/pmbus/adm1275.c +++ b/drivers/hwmon/pmbus/adm1275.c @@ -18,7 +18,7 @@ #include <linux/log2.h> #include "pmbus.h" -enum chips { adm1075, adm1272, adm1275, adm1276, adm1278, adm1281, adm1293, adm1294 }; +enum chips { adm1075, adm1272, adm1273, adm1275, adm1276, adm1278, adm1281, adm1293, adm1294 }; #define ADM1275_MFR_STATUS_IOUT_WARN2 BIT(0) #define ADM1293_MFR_STATUS_VAUX_UV_WARN BIT(5) @@ -479,6 +479,7 @@ static int adm1275_read_byte_data(struct i2c_client *client, int page, int reg) static const struct i2c_device_id adm1275_id[] = { { "adm1075", adm1075 }, { "adm1272", adm1272 }, + { "adm1273", adm1273 }, { "adm1275", adm1275 }, { "adm1276", adm1276 }, { "adm1278", adm1278 }, @@ -555,9 +556,9 @@ static int adm1275_probe(struct i2c_client *client) "Device mismatch: Configured %s, detected %s\n", client->name, mid->name); - if (mid->driver_data == adm1272 || mid->driver_data == adm1278 || - mid->driver_data == adm1281 || mid->driver_data == adm1293 || - mid->driver_data == adm1294) + if (mid->driver_data == adm1272 || mid->driver_data == adm1273 || + mid->driver_data == adm1278 || mid->driver_data == adm1281 || + mid->driver_data == adm1293 || mid->driver_data == adm1294) config_read_fn = i2c_smbus_read_word_data; else config_read_fn = i2c_smbus_read_byte_data; @@ -630,6 +631,7 @@ static int adm1275_probe(struct i2c_client *client) PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT; break; case adm1272: + case adm1273: data->have_vout = true; data->have_pin_max = true; data->have_temp_max = true; @@ -866,4 +868,4 @@ module_i2c_driver(adm1275_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for Analog Devices ADM1275 and compatibles"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/adp1050.c b/drivers/hwmon/pmbus/adp1050.c index 20f22730fc01..ef46c880b168 100644 --- a/drivers/hwmon/pmbus/adp1050.c +++ b/drivers/hwmon/pmbus/adp1050.c @@ -53,4 +53,4 @@ module_i2c_driver(adp1050_driver); MODULE_AUTHOR("Radu Sabau <radu.sabau@analog.com>"); MODULE_DESCRIPTION("Analog Devices ADP1050 HWMON PMBus Driver"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/bel-pfe.c b/drivers/hwmon/pmbus/bel-pfe.c index 7c5f4b10a7c1..ddf9d9a2958c 100644 --- a/drivers/hwmon/pmbus/bel-pfe.c +++ b/drivers/hwmon/pmbus/bel-pfe.c @@ -129,4 +129,4 @@ module_i2c_driver(pfe_pmbus_driver); MODULE_AUTHOR("Tao Ren <rentao.bupt@gmail.com>"); MODULE_DESCRIPTION("PMBus driver for BEL PFE Family Power Supplies"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/bpa-rs600.c b/drivers/hwmon/pmbus/bpa-rs600.c index 0dce26c35556..6c3875ba37a0 100644 --- a/drivers/hwmon/pmbus/bpa-rs600.c +++ b/drivers/hwmon/pmbus/bpa-rs600.c @@ -205,4 +205,4 @@ module_i2c_driver(bpa_rs600_driver); MODULE_AUTHOR("Chris Packham"); MODULE_DESCRIPTION("PMBus driver for BluTek BPA-RS600"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/crps.c b/drivers/hwmon/pmbus/crps.c new file mode 100644 index 000000000000..164b33fed312 --- /dev/null +++ b/drivers/hwmon/pmbus/crps.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2024 IBM Corp. + */ + +#include <linux/i2c.h> +#include <linux/of.h> +#include <linux/pmbus.h> + +#include "pmbus.h" + +static const struct i2c_device_id crps_id[] = { + { "intel_crps185" }, + {} +}; +MODULE_DEVICE_TABLE(i2c, crps_id); + +static struct pmbus_driver_info crps_info = { + .pages = 1, + /* PSU uses default linear data format. */ + .func[0] = PMBUS_HAVE_PIN | PMBUS_HAVE_IOUT | + PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_IIN | + PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT | + PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | + PMBUS_HAVE_STATUS_TEMP | + PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12, +}; + +static int crps_probe(struct i2c_client *client) +{ + int rc; + struct device *dev = &client->dev; + char buf[I2C_SMBUS_BLOCK_MAX + 2] = { 0 }; + + rc = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf); + if (rc < 0) + return dev_err_probe(dev, rc, "Failed to read PMBUS_MFR_MODEL\n"); + + if (rc != 7 || strncmp(buf, "03NK260", 7)) { + buf[rc] = '\0'; + return dev_err_probe(dev, -ENODEV, "Model '%s' not supported\n", buf); + } + + rc = pmbus_do_probe(client, &crps_info); + if (rc) + return dev_err_probe(dev, rc, "Failed to probe\n"); + + return 0; +} + +static const struct of_device_id crps_of_match[] = { + { + .compatible = "intel,crps185", + }, + {} +}; +MODULE_DEVICE_TABLE(of, crps_of_match); + +static struct i2c_driver crps_driver = { + .driver = { + .name = "crps", + .of_match_table = crps_of_match, + }, + .probe = crps_probe, + .id_table = crps_id, +}; + +module_i2c_driver(crps_driver); + +MODULE_AUTHOR("Ninad Palsule"); +MODULE_DESCRIPTION("PMBus driver for Intel Common Redundant power supplies"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/delta-ahe50dc-fan.c b/drivers/hwmon/pmbus/delta-ahe50dc-fan.c index 4dd3b6686d6a..3850eaea75da 100644 --- a/drivers/hwmon/pmbus/delta-ahe50dc-fan.c +++ b/drivers/hwmon/pmbus/delta-ahe50dc-fan.c @@ -127,4 +127,4 @@ module_i2c_driver(ahe50dc_fan_driver); MODULE_AUTHOR("Zev Weiss <zev@bewilderbeest.net>"); MODULE_DESCRIPTION("Driver for Delta AHE-50DC power shelf fan control module"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/dps920ab.c b/drivers/hwmon/pmbus/dps920ab.c index 04e0d598a6e5..325111a955e6 100644 --- a/drivers/hwmon/pmbus/dps920ab.c +++ b/drivers/hwmon/pmbus/dps920ab.c @@ -190,12 +190,19 @@ static const struct of_device_id __maybe_unused dps920ab_of_match[] = { MODULE_DEVICE_TABLE(of, dps920ab_of_match); +static const struct i2c_device_id dps920ab_device_id[] = { + { "dps920ab" }, + {} +}; +MODULE_DEVICE_TABLE(i2c, dps920ab_device_id); + static struct i2c_driver dps920ab_driver = { .driver = { .name = "dps920ab", .of_match_table = of_match_ptr(dps920ab_of_match), }, .probe = dps920ab_probe, + .id_table = dps920ab_device_id, }; module_i2c_driver(dps920ab_driver); @@ -203,4 +210,4 @@ module_i2c_driver(dps920ab_driver); MODULE_AUTHOR("Robert Marko <robert.marko@sartura.hr>"); MODULE_DESCRIPTION("PMBus driver for Delta DPS920AB PSU"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/fsp-3y.c b/drivers/hwmon/pmbus/fsp-3y.c index 72a7c261ef06..a4dc09e2ef75 100644 --- a/drivers/hwmon/pmbus/fsp-3y.c +++ b/drivers/hwmon/pmbus/fsp-3y.c @@ -291,4 +291,4 @@ module_i2c_driver(fsp3y_driver); MODULE_AUTHOR("Václav Kubernát"); MODULE_DESCRIPTION("PMBus driver for FSP/3Y-Power power supplies"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/ibm-cffps.c b/drivers/hwmon/pmbus/ibm-cffps.c index 1ba4c5e95820..d05ef7a968a9 100644 --- a/drivers/hwmon/pmbus/ibm-cffps.c +++ b/drivers/hwmon/pmbus/ibm-cffps.c @@ -614,4 +614,4 @@ module_i2c_driver(ibm_cffps_driver); MODULE_AUTHOR("Eddie James"); MODULE_DESCRIPTION("PMBus driver for IBM Common Form Factor power supplies"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/inspur-ipsps.c b/drivers/hwmon/pmbus/inspur-ipsps.c index 3e3cc9a0f116..074e0f164ee1 100644 --- a/drivers/hwmon/pmbus/inspur-ipsps.c +++ b/drivers/hwmon/pmbus/inspur-ipsps.c @@ -224,4 +224,4 @@ module_i2c_driver(ipsps_driver); MODULE_AUTHOR("John Wang"); MODULE_DESCRIPTION("PMBus driver for Inspur Power System power supplies"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/ir35221.c b/drivers/hwmon/pmbus/ir35221.c index 503be59c6c7f..46d8f334d49a 100644 --- a/drivers/hwmon/pmbus/ir35221.c +++ b/drivers/hwmon/pmbus/ir35221.c @@ -145,4 +145,4 @@ module_i2c_driver(ir35221_driver); MODULE_AUTHOR("Samuel Mendoza-Jonas <sam@mendozajonas.com"); MODULE_DESCRIPTION("PMBus driver for IR35221"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/ir36021.c b/drivers/hwmon/pmbus/ir36021.c index 5148c9187c9e..34ce15fc708b 100644 --- a/drivers/hwmon/pmbus/ir36021.c +++ b/drivers/hwmon/pmbus/ir36021.c @@ -76,4 +76,4 @@ module_i2c_driver(ir36021_driver); MODULE_AUTHOR("Chris Packham <chris.packham@alliedtelesis.co.nz>"); MODULE_DESCRIPTION("PMBus driver for Infineon IR36021"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/ir38064.c b/drivers/hwmon/pmbus/ir38064.c index d4bcc9c39774..7b4188e8bf48 100644 --- a/drivers/hwmon/pmbus/ir38064.c +++ b/drivers/hwmon/pmbus/ir38064.c @@ -87,4 +87,4 @@ module_i2c_driver(ir38064_driver); MODULE_AUTHOR("Maxim Sloyko <maxims@google.com>"); MODULE_DESCRIPTION("PMBus driver for Infineon IR38064 and compatible chips"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/irps5401.c b/drivers/hwmon/pmbus/irps5401.c index f0bdf55c95bf..43674c64841d 100644 --- a/drivers/hwmon/pmbus/irps5401.c +++ b/drivers/hwmon/pmbus/irps5401.c @@ -63,4 +63,4 @@ module_i2c_driver(irps5401_driver); MODULE_AUTHOR("Robert Hancock"); MODULE_DESCRIPTION("PMBus driver for Infineon IRPS5401"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/isl68137.c b/drivers/hwmon/pmbus/isl68137.c index 97cc951a13a4..2af921039309 100644 --- a/drivers/hwmon/pmbus/isl68137.c +++ b/drivers/hwmon/pmbus/isl68137.c @@ -532,4 +532,4 @@ module_i2c_driver(isl68137_driver); MODULE_AUTHOR("Maxim Sloyko <maxims@google.com>"); MODULE_DESCRIPTION("PMBus driver for Renesas digital multiphase voltage regulators"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/lm25066.c b/drivers/hwmon/pmbus/lm25066.c index c36c124d1a2d..40b0dda32ea6 100644 --- a/drivers/hwmon/pmbus/lm25066.c +++ b/drivers/hwmon/pmbus/lm25066.c @@ -569,4 +569,4 @@ module_i2c_driver(lm25066_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for LM25066 and compatible chips"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/lt7182s.c b/drivers/hwmon/pmbus/lt7182s.c index aebd97af2741..9d6d50f39bd6 100644 --- a/drivers/hwmon/pmbus/lt7182s.c +++ b/drivers/hwmon/pmbus/lt7182s.c @@ -192,4 +192,4 @@ module_i2c_driver(lt7182s_driver); MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); MODULE_DESCRIPTION("PMBus driver for Analog Devices LT7182S"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c index a6eb4d4b5487..4c306943383a 100644 --- a/drivers/hwmon/pmbus/ltc2978.c +++ b/drivers/hwmon/pmbus/ltc2978.c @@ -952,4 +952,4 @@ module_i2c_driver(ltc2978_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for LTC2978 and compatible chips"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/ltc3815.c b/drivers/hwmon/pmbus/ltc3815.c index f58a8cedb0d7..824c16a75e2c 100644 --- a/drivers/hwmon/pmbus/ltc3815.c +++ b/drivers/hwmon/pmbus/ltc3815.c @@ -208,4 +208,4 @@ module_i2c_driver(ltc3815_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for LTC3815"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/max15301.c b/drivers/hwmon/pmbus/max15301.c index f5367a7bc0f5..d5810b88ea8d 100644 --- a/drivers/hwmon/pmbus/max15301.c +++ b/drivers/hwmon/pmbus/max15301.c @@ -25,6 +25,7 @@ static const struct i2c_device_id max15301_id[] = { { "bmr461" }, { "max15301" }, + { "max15303" }, {} }; MODULE_DEVICE_TABLE(i2c, max15301_id); @@ -97,4 +98,4 @@ module_i2c_driver(max15301_driver); MODULE_AUTHOR("Erik Rosen <erik.rosen@metormote.com>"); MODULE_DESCRIPTION("PMBus driver for Maxim MAX15301"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/max16064.c b/drivers/hwmon/pmbus/max16064.c index 98e2b5dd5841..eb84915c2a83 100644 --- a/drivers/hwmon/pmbus/max16064.c +++ b/drivers/hwmon/pmbus/max16064.c @@ -111,4 +111,4 @@ module_i2c_driver(max16064_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for Maxim MAX16064"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/max16601.c b/drivers/hwmon/pmbus/max16601.c index 3ab219504600..d696e506aafb 100644 --- a/drivers/hwmon/pmbus/max16601.c +++ b/drivers/hwmon/pmbus/max16601.c @@ -366,4 +366,4 @@ module_i2c_driver(max16601_driver); MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); MODULE_DESCRIPTION("PMBus driver for Maxim MAX16601"); MODULE_LICENSE("GPL v2"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/max20730.c b/drivers/hwmon/pmbus/max20730.c index d56ec24764fd..95869d198ecf 100644 --- a/drivers/hwmon/pmbus/max20730.c +++ b/drivers/hwmon/pmbus/max20730.c @@ -787,4 +787,4 @@ module_i2c_driver(max20730_driver); MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); MODULE_DESCRIPTION("PMBus driver for Maxim MAX20710 / MAX20730 / MAX20734 / MAX20743"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/max20751.c b/drivers/hwmon/pmbus/max20751.c index 8f23c1eb559e..ac8c43122133 100644 --- a/drivers/hwmon/pmbus/max20751.c +++ b/drivers/hwmon/pmbus/max20751.c @@ -51,4 +51,4 @@ module_i2c_driver(max20751_driver); MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); MODULE_DESCRIPTION("PMBus driver for Maxim MAX20751"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/max31785.c b/drivers/hwmon/pmbus/max31785.c index 09218dba8965..1f94d38a1637 100644 --- a/drivers/hwmon/pmbus/max31785.c +++ b/drivers/hwmon/pmbus/max31785.c @@ -549,4 +549,4 @@ module_i2c_driver(max31785_driver); MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>"); MODULE_DESCRIPTION("PMBus driver for the Maxim MAX31785"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/max34440.c b/drivers/hwmon/pmbus/max34440.c index fe7f6b1b0985..c9dda33831ff 100644 --- a/drivers/hwmon/pmbus/max34440.c +++ b/drivers/hwmon/pmbus/max34440.c @@ -529,4 +529,4 @@ module_i2c_driver(max34440_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for Maxim MAX34440/MAX34441"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/max8688.c b/drivers/hwmon/pmbus/max8688.c index 5d5b6aeefa80..b3a2a7492bbf 100644 --- a/drivers/hwmon/pmbus/max8688.c +++ b/drivers/hwmon/pmbus/max8688.c @@ -191,4 +191,4 @@ module_i2c_driver(max8688_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for Maxim MAX8688"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/mp2856.c b/drivers/hwmon/pmbus/mp2856.c index 41bb86667091..e83c70a3583f 100644 --- a/drivers/hwmon/pmbus/mp2856.c +++ b/drivers/hwmon/pmbus/mp2856.c @@ -463,4 +463,4 @@ module_i2c_driver(mp2856_driver); MODULE_AUTHOR("Peter Yin <peter.yin@quantatw.com>"); MODULE_DESCRIPTION("PMBus driver for MPS MP2856/MP2857 device"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/mp2888.c b/drivers/hwmon/pmbus/mp2888.c index 3b45f126b611..772a623ca7d0 100644 --- a/drivers/hwmon/pmbus/mp2888.c +++ b/drivers/hwmon/pmbus/mp2888.c @@ -404,4 +404,4 @@ module_i2c_driver(mp2888_driver); MODULE_AUTHOR("Vadim Pasternak <vadimp@nvidia.com>"); MODULE_DESCRIPTION("PMBus driver for MPS MP2888 device"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/mp2891.c b/drivers/hwmon/pmbus/mp2891.c index 94ab4ae5fba0..f8f4c91ec23c 100644 --- a/drivers/hwmon/pmbus/mp2891.c +++ b/drivers/hwmon/pmbus/mp2891.c @@ -597,4 +597,4 @@ module_i2c_driver(mp2891_driver); MODULE_AUTHOR("Noah Wang <noahwang.wang@outlook.com>"); MODULE_DESCRIPTION("PMBus driver for MPS MP2891"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/mp2975.c b/drivers/hwmon/pmbus/mp2975.c index 280bb12f762c..c31982d85196 100644 --- a/drivers/hwmon/pmbus/mp2975.c +++ b/drivers/hwmon/pmbus/mp2975.c @@ -1101,4 +1101,4 @@ module_i2c_driver(mp2975_driver); MODULE_AUTHOR("Vadim Pasternak <vadimp@nvidia.com>"); MODULE_DESCRIPTION("PMBus driver for MPS MP2975 device"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/mp2993.c b/drivers/hwmon/pmbus/mp2993.c index 63691dac2281..81c84fc8ed47 100644 --- a/drivers/hwmon/pmbus/mp2993.c +++ b/drivers/hwmon/pmbus/mp2993.c @@ -258,4 +258,4 @@ module_i2c_driver(mp2993_driver); MODULE_AUTHOR("Noah Wang <noahwang.wang@outlook.com>"); MODULE_DESCRIPTION("PMBus driver for MPS MP2993"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/mp5023.c b/drivers/hwmon/pmbus/mp5023.c index 21acb7fd9a1a..c466d67e9a8f 100644 --- a/drivers/hwmon/pmbus/mp5023.c +++ b/drivers/hwmon/pmbus/mp5023.c @@ -64,4 +64,4 @@ module_i2c_driver(mp5023_driver); MODULE_AUTHOR("Howard Chiu <howard.chiu@quantatw.com>"); MODULE_DESCRIPTION("PMBus driver for MPS MP5023 HSC"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/mp5920.c b/drivers/hwmon/pmbus/mp5920.c index f6d7527ade7d..319ae2721bcf 100644 --- a/drivers/hwmon/pmbus/mp5920.c +++ b/drivers/hwmon/pmbus/mp5920.c @@ -87,4 +87,4 @@ MODULE_AUTHOR("Tony Ao <tony_ao@wiwynn.com>"); MODULE_AUTHOR("Alex Vdovydchenko <xzeol@yahoo.com>"); MODULE_DESCRIPTION("PMBus driver for MP5920 HSC"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/mp5990.c b/drivers/hwmon/pmbus/mp5990.c index 5d1d5eac89da..4ce381a39480 100644 --- a/drivers/hwmon/pmbus/mp5990.c +++ b/drivers/hwmon/pmbus/mp5990.c @@ -176,4 +176,4 @@ module_i2c_driver(mp5990_driver); MODULE_AUTHOR("Peter Yin <peter.yin@quantatw.com>"); MODULE_DESCRIPTION("PMBus driver for MP5990 HSC"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/mp9941.c b/drivers/hwmon/pmbus/mp9941.c index 8ab5fc4d4092..42ca6748777a 100644 --- a/drivers/hwmon/pmbus/mp9941.c +++ b/drivers/hwmon/pmbus/mp9941.c @@ -316,4 +316,4 @@ module_i2c_driver(mp9941_driver); MODULE_AUTHOR("Noah Wang <noahwang.wang@outlook.com>"); MODULE_DESCRIPTION("PMBus driver for MPS MP9941"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/mpq7932.c b/drivers/hwmon/pmbus/mpq7932.c index 2dcb6da853bd..c1e2d0cb2fd0 100644 --- a/drivers/hwmon/pmbus/mpq7932.c +++ b/drivers/hwmon/pmbus/mpq7932.c @@ -164,4 +164,4 @@ module_i2c_driver(mpq7932_regulator_driver); MODULE_AUTHOR("Saravanan Sekar <saravanan@linumiz.com>"); MODULE_DESCRIPTION("MPQ7932 PMIC regulator driver"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/mpq8785.c b/drivers/hwmon/pmbus/mpq8785.c index 0d16491cd770..331c274ca892 100644 --- a/drivers/hwmon/pmbus/mpq8785.c +++ b/drivers/hwmon/pmbus/mpq8785.c @@ -87,4 +87,4 @@ module_i2c_driver(mpq8785_driver); MODULE_AUTHOR("Charles Hsu <ythsu0511@gmail.com>"); MODULE_DESCRIPTION("PMBus driver for MPS MPQ8785"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/pim4328.c b/drivers/hwmon/pmbus/pim4328.c index 31d9ae06379a..aa98284bbdd8 100644 --- a/drivers/hwmon/pmbus/pim4328.c +++ b/drivers/hwmon/pmbus/pim4328.c @@ -230,4 +230,4 @@ module_i2c_driver(pim4328_driver); MODULE_AUTHOR("Erik Rosen <erik.rosen@metormote.com>"); MODULE_DESCRIPTION("PMBus driver for PIM4006, PIM4328, PIM4820 power interface modules"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/pli1209bc.c b/drivers/hwmon/pmbus/pli1209bc.c index 178e0cdb7887..569b61dc1a32 100644 --- a/drivers/hwmon/pmbus/pli1209bc.c +++ b/drivers/hwmon/pmbus/pli1209bc.c @@ -145,4 +145,4 @@ module_i2c_driver(pli1209bc_driver); MODULE_AUTHOR("Marcello Sylvester Bauer <sylv@sylv.io>"); MODULE_DESCRIPTION("PMBus driver for Vicor PLI1209BC"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/pm6764tr.c b/drivers/hwmon/pmbus/pm6764tr.c index 23f15b608dcf..c96c0aecb920 100644 --- a/drivers/hwmon/pmbus/pm6764tr.c +++ b/drivers/hwmon/pmbus/pm6764tr.c @@ -73,4 +73,4 @@ module_i2c_driver(pm6764tr_driver); MODULE_AUTHOR("Charles Hsu"); MODULE_DESCRIPTION("PMBus driver for ST PM6764TR"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/pmbus.c b/drivers/hwmon/pmbus/pmbus.c index ec40c5c59954..77cf268e7d2d 100644 --- a/drivers/hwmon/pmbus/pmbus.c +++ b/drivers/hwmon/pmbus/pmbus.c @@ -261,4 +261,4 @@ module_i2c_driver(pmbus_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("Generic PMBus driver"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h index d605412a3173..ddb19c9726d6 100644 --- a/drivers/hwmon/pmbus/pmbus.h +++ b/drivers/hwmon/pmbus/pmbus.h @@ -487,6 +487,8 @@ struct pmbus_driver_info { /* Regulator ops */ extern const struct regulator_ops pmbus_regulator_ops; +int pmbus_regulator_init_cb(struct regulator_dev *rdev, + struct regulator_config *config); /* Macros for filling in array of struct regulator_desc */ #define PMBUS_REGULATOR_STEP(_name, _id, _voltages, _step, _min_uV) \ @@ -501,6 +503,7 @@ extern const struct regulator_ops pmbus_regulator_ops; .n_voltages = _voltages, \ .uV_step = _step, \ .min_uV = _min_uV, \ + .init_cb = pmbus_regulator_init_cb, \ } #define PMBUS_REGULATOR(_name, _id) PMBUS_REGULATOR_STEP(_name, _id, 0, 0, 0) @@ -516,6 +519,7 @@ extern const struct regulator_ops pmbus_regulator_ops; .n_voltages = _voltages, \ .uV_step = _step, \ .min_uV = _min_uV, \ + .init_cb = pmbus_regulator_init_cb, \ } #define PMBUS_REGULATOR_ONE(_name) PMBUS_REGULATOR_STEP_ONE(_name, 0, 0, 0) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index a0109296a994..787683e83db6 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -31,6 +31,9 @@ #define PMBUS_ATTR_ALLOC_SIZE 32 #define PMBUS_NAME_SIZE 24 +static int wp = -1; +module_param(wp, int, 0444); + struct pmbus_sensor { struct pmbus_sensor *next; char name[PMBUS_NAME_SIZE]; /* sysfs sensor name */ @@ -150,7 +153,7 @@ void pmbus_clear_cache(struct i2c_client *client) for (sensor = data->sensors; sensor; sensor = sensor->next) sensor->data = -ENODATA; } -EXPORT_SYMBOL_NS_GPL(pmbus_clear_cache, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_clear_cache, "PMBUS"); void pmbus_set_update(struct i2c_client *client, u8 reg, bool update) { @@ -161,7 +164,7 @@ void pmbus_set_update(struct i2c_client *client, u8 reg, bool update) if (sensor->reg == reg) sensor->update = update; } -EXPORT_SYMBOL_NS_GPL(pmbus_set_update, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_set_update, "PMBUS"); /* Some chips need a delay between accesses. */ static void pmbus_wait(struct i2c_client *client) @@ -236,7 +239,7 @@ int pmbus_set_page(struct i2c_client *client, int page, int phase) return 0; } -EXPORT_SYMBOL_NS_GPL(pmbus_set_page, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_set_page, "PMBUS"); int pmbus_write_byte(struct i2c_client *client, int page, u8 value) { @@ -252,7 +255,7 @@ int pmbus_write_byte(struct i2c_client *client, int page, u8 value) return rv; } -EXPORT_SYMBOL_NS_GPL(pmbus_write_byte, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_write_byte, "PMBUS"); /* * _pmbus_write_byte() is similar to pmbus_write_byte(), but checks if @@ -287,7 +290,7 @@ int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg, return rv; } -EXPORT_SYMBOL_NS_GPL(pmbus_write_word_data, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_write_word_data, "PMBUS"); static int pmbus_write_virt_reg(struct i2c_client *client, int page, int reg, @@ -393,7 +396,7 @@ int pmbus_update_fan(struct i2c_client *client, int page, int id, return _pmbus_write_word_data(client, page, pmbus_fan_command_registers[id], command); } -EXPORT_SYMBOL_NS_GPL(pmbus_update_fan, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_update_fan, "PMBUS"); int pmbus_read_word_data(struct i2c_client *client, int page, int phase, u8 reg) { @@ -409,7 +412,7 @@ int pmbus_read_word_data(struct i2c_client *client, int page, int phase, u8 reg) return rv; } -EXPORT_SYMBOL_NS_GPL(pmbus_read_word_data, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_read_word_data, "PMBUS"); static int pmbus_read_virt_reg(struct i2c_client *client, int page, int reg) { @@ -472,7 +475,7 @@ int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg) return rv; } -EXPORT_SYMBOL_NS_GPL(pmbus_read_byte_data, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_read_byte_data, "PMBUS"); int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, u8 value) { @@ -488,7 +491,7 @@ int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, u8 value) return rv; } -EXPORT_SYMBOL_NS_GPL(pmbus_write_byte_data, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_write_byte_data, "PMBUS"); int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg, u8 mask, u8 value) @@ -507,7 +510,7 @@ int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg, return rv; } -EXPORT_SYMBOL_NS_GPL(pmbus_update_byte_data, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_update_byte_data, "PMBUS"); static int pmbus_read_block_data(struct i2c_client *client, int page, u8 reg, char *data_buf) @@ -578,14 +581,14 @@ int pmbus_get_fan_rate_device(struct i2c_client *client, int page, int id, { return pmbus_get_fan_rate(client, page, id, mode, false); } -EXPORT_SYMBOL_NS_GPL(pmbus_get_fan_rate_device, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_get_fan_rate_device, "PMBUS"); int pmbus_get_fan_rate_cached(struct i2c_client *client, int page, int id, enum pmbus_fan_mode mode) { return pmbus_get_fan_rate(client, page, id, mode, true); } -EXPORT_SYMBOL_NS_GPL(pmbus_get_fan_rate_cached, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_get_fan_rate_cached, "PMBUS"); static void pmbus_clear_fault_page(struct i2c_client *client, int page) { @@ -600,7 +603,7 @@ void pmbus_clear_faults(struct i2c_client *client) for (i = 0; i < data->info->pages; i++) pmbus_clear_fault_page(client, i); } -EXPORT_SYMBOL_NS_GPL(pmbus_clear_faults, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_clear_faults, "PMBUS"); static int pmbus_check_status_cml(struct i2c_client *client) { @@ -655,13 +658,13 @@ bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg) { return pmbus_check_register(client, _pmbus_read_byte_data, page, reg); } -EXPORT_SYMBOL_NS_GPL(pmbus_check_byte_register, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_check_byte_register, "PMBUS"); bool pmbus_check_word_register(struct i2c_client *client, int page, int reg) { return pmbus_check_register(client, __pmbus_read_word_data, page, reg); } -EXPORT_SYMBOL_NS_GPL(pmbus_check_word_register, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_check_word_register, "PMBUS"); static bool __maybe_unused pmbus_check_block_register(struct i2c_client *client, int page, int reg) @@ -685,7 +688,7 @@ const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client *client) return data->info; } -EXPORT_SYMBOL_NS_GPL(pmbus_get_driver_info, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_get_driver_info, "PMBUS"); static int pmbus_get_status(struct i2c_client *client, int page, int reg) { @@ -2665,6 +2668,56 @@ static void pmbus_remove_pec(void *dev) device_remove_file(dev, &dev_attr_pec); } +static void pmbus_init_wp(struct i2c_client *client, struct pmbus_data *data) +{ + int ret; + + switch (wp) { + case 0: + _pmbus_write_byte_data(client, -1, + PMBUS_WRITE_PROTECT, 0); + break; + + case 1: + _pmbus_write_byte_data(client, -1, + PMBUS_WRITE_PROTECT, PB_WP_VOUT); + break; + + case 2: + _pmbus_write_byte_data(client, -1, + PMBUS_WRITE_PROTECT, PB_WP_OP); + break; + + case 3: + _pmbus_write_byte_data(client, -1, + PMBUS_WRITE_PROTECT, PB_WP_ALL); + break; + + default: + /* Ignore the other values */ + break; + } + + ret = _pmbus_read_byte_data(client, -1, PMBUS_WRITE_PROTECT); + if (ret < 0) + return; + + switch (ret & PB_WP_ANY) { + case PB_WP_ALL: + data->flags |= PMBUS_OP_PROTECTED; + fallthrough; + case PB_WP_OP: + data->flags |= PMBUS_VOUT_PROTECTED; + fallthrough; + case PB_WP_VOUT: + data->flags |= PMBUS_WRITE_PROTECTED | PMBUS_SKIP_STATUS_CHECK; + break; + + default: + break; + } +} + static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, struct pmbus_driver_info *info) { @@ -2718,12 +2771,8 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, * faults, and we should not try it. Also, in that case, writes into * limit registers need to be disabled. */ - if (!(data->flags & PMBUS_NO_WRITE_PROTECT)) { - ret = _pmbus_read_byte_data(client, -1, PMBUS_WRITE_PROTECT); - - if (ret > 0 && (ret & PB_WP_ANY)) - data->flags |= PMBUS_WRITE_PROTECTED | PMBUS_SKIP_STATUS_CHECK; - } + if (!(data->flags & PMBUS_NO_WRITE_PROTECT)) + pmbus_init_wp(client, data); ret = i2c_smbus_read_byte_data(client, PMBUS_REVISION); if (ret >= 0) @@ -3183,8 +3232,12 @@ static int pmbus_regulator_list_voltage(struct regulator_dev *rdev, { struct device *dev = rdev_get_dev(rdev); struct i2c_client *client = to_i2c_client(dev->parent); + struct pmbus_data *data = i2c_get_clientdata(client); int val, low, high; + if (data->flags & PMBUS_VOUT_PROTECTED) + return 0; + if (selector >= rdev->desc->n_voltages || selector < rdev->desc->linear_min_sel) return -EINVAL; @@ -3217,7 +3270,23 @@ const struct regulator_ops pmbus_regulator_ops = { .set_voltage = pmbus_regulator_set_voltage, .list_voltage = pmbus_regulator_list_voltage, }; -EXPORT_SYMBOL_NS_GPL(pmbus_regulator_ops, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_regulator_ops, "PMBUS"); + +int pmbus_regulator_init_cb(struct regulator_dev *rdev, + struct regulator_config *config) +{ + struct pmbus_data *data = config->driver_data; + struct regulation_constraints *constraints = rdev->constraints; + + if (data->flags & PMBUS_OP_PROTECTED) + constraints->valid_ops_mask &= ~REGULATOR_CHANGE_STATUS; + + if (data->flags & PMBUS_VOUT_PROTECTED) + constraints->valid_ops_mask &= ~REGULATOR_CHANGE_VOLTAGE; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(pmbus_regulator_init_cb, "PMBUS"); static int pmbus_regulator_register(struct pmbus_data *data) { @@ -3465,11 +3534,11 @@ static int pmbus_init_debugfs(struct i2c_client *client, /* * Allocate the max possible entries we need. - * 6 entries device-specific + * 7 entries device-specific * 10 entries page-specific */ entries = devm_kcalloc(data->dev, - 6 + data->info->pages * 10, sizeof(*entries), + 7 + data->info->pages * 10, sizeof(*entries), GFP_KERNEL); if (!entries) return -ENOMEM; @@ -3482,6 +3551,15 @@ static int pmbus_init_debugfs(struct i2c_client *client, * assume that values of the following registers are the same for all * pages and report values only for page 0. */ + if (pmbus_check_byte_register(client, 0, PMBUS_REVISION)) { + entries[idx].client = client; + entries[idx].page = 0; + entries[idx].reg = PMBUS_REVISION; + debugfs_create_file("revision", 0444, data->debugfs, + &entries[idx++], + &pmbus_debugfs_ops); + } + if (pmbus_check_block_register(client, 0, PMBUS_MFR_ID)) { entries[idx].client = client; entries[idx].page = 0; @@ -3743,7 +3821,7 @@ int pmbus_do_probe(struct i2c_client *client, struct pmbus_driver_info *info) return 0; } -EXPORT_SYMBOL_NS_GPL(pmbus_do_probe, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_do_probe, "PMBUS"); struct dentry *pmbus_get_debugfs_dir(struct i2c_client *client) { @@ -3751,7 +3829,7 @@ struct dentry *pmbus_get_debugfs_dir(struct i2c_client *client) return data->debugfs; } -EXPORT_SYMBOL_NS_GPL(pmbus_get_debugfs_dir, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_get_debugfs_dir, "PMBUS"); int pmbus_lock_interruptible(struct i2c_client *client) { @@ -3759,7 +3837,7 @@ int pmbus_lock_interruptible(struct i2c_client *client) return mutex_lock_interruptible(&data->update_lock); } -EXPORT_SYMBOL_NS_GPL(pmbus_lock_interruptible, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_lock_interruptible, "PMBUS"); void pmbus_unlock(struct i2c_client *client) { @@ -3767,7 +3845,7 @@ void pmbus_unlock(struct i2c_client *client) mutex_unlock(&data->update_lock); } -EXPORT_SYMBOL_NS_GPL(pmbus_unlock, PMBUS); +EXPORT_SYMBOL_NS_GPL(pmbus_unlock, "PMBUS"); static int __init pmbus_core_init(void) { diff --git a/drivers/hwmon/pmbus/pxe1610.c b/drivers/hwmon/pmbus/pxe1610.c index 5ac476d3cdd2..6a4a978eca7e 100644 --- a/drivers/hwmon/pmbus/pxe1610.c +++ b/drivers/hwmon/pmbus/pxe1610.c @@ -148,4 +148,4 @@ module_i2c_driver(pxe1610_driver); MODULE_AUTHOR("Vijay Khemka <vijaykhemka@fb.com>"); MODULE_DESCRIPTION("PMBus driver for Infineon PXE1610, PXE1110 and PXM1310"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/q54sj108a2.c b/drivers/hwmon/pmbus/q54sj108a2.c index a235c1cdf4fe..4d7086d83aa3 100644 --- a/drivers/hwmon/pmbus/q54sj108a2.c +++ b/drivers/hwmon/pmbus/q54sj108a2.c @@ -421,4 +421,4 @@ module_i2c_driver(q54sj108a2_driver); MODULE_AUTHOR("Xiao.Ma <xiao.mx.ma@deltaww.com>"); MODULE_DESCRIPTION("PMBus driver for Delta Q54SJ108A2 series modules"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/stpddc60.c b/drivers/hwmon/pmbus/stpddc60.c index 34d0f06f4845..5cb905ed8ae5 100644 --- a/drivers/hwmon/pmbus/stpddc60.c +++ b/drivers/hwmon/pmbus/stpddc60.c @@ -246,4 +246,4 @@ module_i2c_driver(stpddc60_driver); MODULE_AUTHOR("Erik Rosen <erik.rosen@metormote.com>"); MODULE_DESCRIPTION("PMBus driver for ST STPDDC60"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/tda38640.c b/drivers/hwmon/pmbus/tda38640.c index 044d5fbdf9eb..07fe58c24485 100644 --- a/drivers/hwmon/pmbus/tda38640.c +++ b/drivers/hwmon/pmbus/tda38640.c @@ -221,4 +221,4 @@ module_i2c_driver(tda38640_driver); MODULE_AUTHOR("Patrick Rudolph <patrick.rudolph@9elements.com>"); MODULE_DESCRIPTION("PMBus driver for Infineon TDA38640"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/tps25990.c b/drivers/hwmon/pmbus/tps25990.c new file mode 100644 index 000000000000..0d2655e69549 --- /dev/null +++ b/drivers/hwmon/pmbus/tps25990.c @@ -0,0 +1,436 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2024 BayLibre, SAS. +// Author: Jerome Brunet <jbrunet@baylibre.com> + +#include <linux/bitfield.h> +#include <linux/debugfs.h> +#include <linux/err.h> +#include <linux/hwmon-sysfs.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> + +#include "pmbus.h" + +#define TPS25990_READ_VAUX 0xd0 +#define TPS25990_READ_VIN_MIN 0xd1 +#define TPS25990_READ_VIN_PEAK 0xd2 +#define TPS25990_READ_IIN_PEAK 0xd4 +#define TPS25990_READ_PIN_PEAK 0xd5 +#define TPS25990_READ_TEMP_AVG 0xd6 +#define TPS25990_READ_TEMP_PEAK 0xd7 +#define TPS25990_READ_VOUT_MIN 0xda +#define TPS25990_READ_VIN_AVG 0xdc +#define TPS25990_READ_VOUT_AVG 0xdd +#define TPS25990_READ_IIN_AVG 0xde +#define TPS25990_READ_PIN_AVG 0xdf +#define TPS25990_VIREF 0xe0 +#define TPS25990_PK_MIN_AVG 0xea +#define PK_MIN_AVG_RST_PEAK BIT(7) +#define PK_MIN_AVG_RST_AVG BIT(6) +#define PK_MIN_AVG_RST_MIN BIT(5) +#define PK_MIN_AVG_AVG_CNT GENMASK(2, 0) +#define TPS25990_MFR_WRITE_PROTECT 0xf8 +#define TPS25990_UNLOCKED BIT(7) + +#define TPS25990_8B_SHIFT 2 +#define TPS25990_VIN_OVF_NUM 525100 +#define TPS25990_VIN_OVF_DIV 10163 +#define TPS25990_VIN_OVF_OFF 155 +#define TPS25990_IIN_OCF_NUM 953800 +#define TPS25990_IIN_OCF_DIV 129278 +#define TPS25990_IIN_OCF_OFF 157 + +#define PK_MIN_AVG_RST_MASK (PK_MIN_AVG_RST_PEAK | \ + PK_MIN_AVG_RST_AVG | \ + PK_MIN_AVG_RST_MIN) + +/* + * Arbitrary default Rimon value: 1kOhm + * This correspond to an overcurrent limit of 55A, close to the specified limit + * of un-stacked TPS25990 and makes further calculation easier to setup in + * sensor.conf, if necessary + */ +#define TPS25990_DEFAULT_RIMON 1000000000 + +static void tps25990_set_m(int *m, u32 rimon) +{ + u64 val = ((u64)*m) * rimon; + + /* Make sure m fits the s32 type */ + *m = DIV_ROUND_CLOSEST_ULL(val, 1000000); +} + +static int tps25990_mfr_write_protect_set(struct i2c_client *client, + u8 protect) +{ + u8 val; + + switch (protect) { + case 0: + val = 0xa2; + break; + case PB_WP_ALL: + val = 0x0; + break; + default: + return -EINVAL; + } + + return pmbus_write_byte_data(client, -1, TPS25990_MFR_WRITE_PROTECT, + val); +} + +static int tps25990_mfr_write_protect_get(struct i2c_client *client) +{ + int ret = pmbus_read_byte_data(client, -1, TPS25990_MFR_WRITE_PROTECT); + + if (ret < 0) + return ret; + + return (ret & TPS25990_UNLOCKED) ? 0 : PB_WP_ALL; +} + +static int tps25990_read_word_data(struct i2c_client *client, + int page, int phase, int reg) +{ + int ret; + + switch (reg) { + case PMBUS_VIRT_READ_VIN_MAX: + ret = pmbus_read_word_data(client, page, phase, + TPS25990_READ_VIN_PEAK); + break; + + case PMBUS_VIRT_READ_VIN_MIN: + ret = pmbus_read_word_data(client, page, phase, + TPS25990_READ_VIN_MIN); + break; + + case PMBUS_VIRT_READ_VIN_AVG: + ret = pmbus_read_word_data(client, page, phase, + TPS25990_READ_VIN_AVG); + break; + + case PMBUS_VIRT_READ_VOUT_MIN: + ret = pmbus_read_word_data(client, page, phase, + TPS25990_READ_VOUT_MIN); + break; + + case PMBUS_VIRT_READ_VOUT_AVG: + ret = pmbus_read_word_data(client, page, phase, + TPS25990_READ_VOUT_AVG); + break; + + case PMBUS_VIRT_READ_IIN_AVG: + ret = pmbus_read_word_data(client, page, phase, + TPS25990_READ_IIN_AVG); + break; + + case PMBUS_VIRT_READ_IIN_MAX: + ret = pmbus_read_word_data(client, page, phase, + TPS25990_READ_IIN_PEAK); + break; + + case PMBUS_VIRT_READ_TEMP_AVG: + ret = pmbus_read_word_data(client, page, phase, + TPS25990_READ_TEMP_AVG); + break; + + case PMBUS_VIRT_READ_TEMP_MAX: + ret = pmbus_read_word_data(client, page, phase, + TPS25990_READ_TEMP_PEAK); + break; + + case PMBUS_VIRT_READ_PIN_AVG: + ret = pmbus_read_word_data(client, page, phase, + TPS25990_READ_PIN_AVG); + break; + + case PMBUS_VIRT_READ_PIN_MAX: + ret = pmbus_read_word_data(client, page, phase, + TPS25990_READ_PIN_PEAK); + break; + + case PMBUS_VIRT_READ_VMON: + ret = pmbus_read_word_data(client, page, phase, + TPS25990_READ_VAUX); + break; + + case PMBUS_VIN_UV_WARN_LIMIT: + case PMBUS_VIN_UV_FAULT_LIMIT: + case PMBUS_VIN_OV_WARN_LIMIT: + case PMBUS_VOUT_UV_WARN_LIMIT: + case PMBUS_IIN_OC_WARN_LIMIT: + case PMBUS_OT_WARN_LIMIT: + case PMBUS_OT_FAULT_LIMIT: + case PMBUS_PIN_OP_WARN_LIMIT: + /* + * These registers provide an 8 bits value instead of a + * 10bits one. Just shifting twice the register value is + * enough to make the sensor type conversion work, even + * if the datasheet provides different m, b and R for + * those. + */ + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + break; + ret <<= TPS25990_8B_SHIFT; + break; + + case PMBUS_VIN_OV_FAULT_LIMIT: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + break; + ret = DIV_ROUND_CLOSEST(ret * TPS25990_VIN_OVF_NUM, + TPS25990_VIN_OVF_DIV); + ret += TPS25990_VIN_OVF_OFF; + break; + + case PMBUS_IIN_OC_FAULT_LIMIT: + /* + * VIREF directly sets the over-current limit at which the eFuse + * will turn the FET off and trigger a fault. Expose it through + * this generic property instead of a manufacturer specific one. + */ + ret = pmbus_read_byte_data(client, page, TPS25990_VIREF); + if (ret < 0) + break; + ret = DIV_ROUND_CLOSEST(ret * TPS25990_IIN_OCF_NUM, + TPS25990_IIN_OCF_DIV); + ret += TPS25990_IIN_OCF_OFF; + break; + + case PMBUS_VIRT_SAMPLES: + ret = pmbus_read_byte_data(client, page, TPS25990_PK_MIN_AVG); + if (ret < 0) + break; + ret = 1 << FIELD_GET(PK_MIN_AVG_AVG_CNT, ret); + break; + + case PMBUS_VIRT_RESET_TEMP_HISTORY: + case PMBUS_VIRT_RESET_VIN_HISTORY: + case PMBUS_VIRT_RESET_IIN_HISTORY: + case PMBUS_VIRT_RESET_PIN_HISTORY: + case PMBUS_VIRT_RESET_VOUT_HISTORY: + ret = 0; + break; + + default: + ret = -ENODATA; + break; + } + + return ret; +} + +static int tps25990_write_word_data(struct i2c_client *client, + int page, int reg, u16 value) +{ + int ret; + + switch (reg) { + case PMBUS_VIN_UV_WARN_LIMIT: + case PMBUS_VIN_UV_FAULT_LIMIT: + case PMBUS_VIN_OV_WARN_LIMIT: + case PMBUS_VOUT_UV_WARN_LIMIT: + case PMBUS_IIN_OC_WARN_LIMIT: + case PMBUS_OT_WARN_LIMIT: + case PMBUS_OT_FAULT_LIMIT: + case PMBUS_PIN_OP_WARN_LIMIT: + value >>= TPS25990_8B_SHIFT; + value = clamp_val(value, 0, 0xff); + ret = pmbus_write_word_data(client, page, reg, value); + break; + + case PMBUS_VIN_OV_FAULT_LIMIT: + value -= TPS25990_VIN_OVF_OFF; + value = DIV_ROUND_CLOSEST(((unsigned int)value) * TPS25990_VIN_OVF_DIV, + TPS25990_VIN_OVF_NUM); + value = clamp_val(value, 0, 0xf); + ret = pmbus_write_word_data(client, page, reg, value); + break; + + case PMBUS_IIN_OC_FAULT_LIMIT: + value -= TPS25990_IIN_OCF_OFF; + value = DIV_ROUND_CLOSEST(((unsigned int)value) * TPS25990_IIN_OCF_DIV, + TPS25990_IIN_OCF_NUM); + value = clamp_val(value, 0, 0x3f); + ret = pmbus_write_byte_data(client, page, TPS25990_VIREF, value); + break; + + case PMBUS_VIRT_SAMPLES: + value = clamp_val(value, 1, 1 << PK_MIN_AVG_AVG_CNT); + value = ilog2(value); + ret = pmbus_update_byte_data(client, page, TPS25990_PK_MIN_AVG, + PK_MIN_AVG_AVG_CNT, + FIELD_PREP(PK_MIN_AVG_AVG_CNT, value)); + break; + + case PMBUS_VIRT_RESET_TEMP_HISTORY: + case PMBUS_VIRT_RESET_VIN_HISTORY: + case PMBUS_VIRT_RESET_IIN_HISTORY: + case PMBUS_VIRT_RESET_PIN_HISTORY: + case PMBUS_VIRT_RESET_VOUT_HISTORY: + /* + * TPS25990 has history resets based on MIN/AVG/PEAK instead of per + * sensor type. Exposing this quirk in hwmon is not desirable so + * reset MIN, AVG and PEAK together. Even is there effectively only + * one reset, which resets everything, expose the 5 entries so + * userspace is not required map a sensor type to another to trigger + * a reset + */ + ret = pmbus_update_byte_data(client, 0, TPS25990_PK_MIN_AVG, + PK_MIN_AVG_RST_MASK, + PK_MIN_AVG_RST_MASK); + break; + + default: + ret = -ENODATA; + break; + } + + return ret; +} + +static int tps25990_read_byte_data(struct i2c_client *client, + int page, int reg) +{ + int ret; + + switch (reg) { + case PMBUS_WRITE_PROTECT: + ret = tps25990_mfr_write_protect_get(client); + break; + + default: + ret = -ENODATA; + break; + } + + return ret; +} + +static int tps25990_write_byte_data(struct i2c_client *client, + int page, int reg, u8 byte) +{ + int ret; + + switch (reg) { + case PMBUS_WRITE_PROTECT: + ret = tps25990_mfr_write_protect_set(client, byte); + break; + + default: + ret = -ENODATA; + break; + } + + return ret; +} + +#if IS_ENABLED(CONFIG_SENSORS_TPS25990_REGULATOR) +static const struct regulator_desc tps25990_reg_desc[] = { + PMBUS_REGULATOR_ONE("vout"), +}; +#endif + +static const struct pmbus_driver_info tps25990_base_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = direct, + .m[PSC_VOLTAGE_IN] = 5251, + .b[PSC_VOLTAGE_IN] = 0, + .R[PSC_VOLTAGE_IN] = -2, + .format[PSC_VOLTAGE_OUT] = direct, + .m[PSC_VOLTAGE_OUT] = 5251, + .b[PSC_VOLTAGE_OUT] = 0, + .R[PSC_VOLTAGE_OUT] = -2, + .format[PSC_TEMPERATURE] = direct, + .m[PSC_TEMPERATURE] = 140, + .b[PSC_TEMPERATURE] = 32100, + .R[PSC_TEMPERATURE] = -2, + /* + * Current and Power measurement depends on the ohm value + * of Rimon. m is multiplied by 1000 below to have an integer + * and -3 is added to R to compensate. + */ + .format[PSC_CURRENT_IN] = direct, + .m[PSC_CURRENT_IN] = 9538, + .b[PSC_CURRENT_IN] = 0, + .R[PSC_CURRENT_IN] = -6, + .format[PSC_POWER] = direct, + .m[PSC_POWER] = 4901, + .b[PSC_POWER] = 0, + .R[PSC_POWER] = -7, + .func[0] = (PMBUS_HAVE_VIN | + PMBUS_HAVE_VOUT | + PMBUS_HAVE_VMON | + PMBUS_HAVE_IIN | + PMBUS_HAVE_PIN | + PMBUS_HAVE_TEMP | + PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_STATUS_INPUT | + PMBUS_HAVE_STATUS_TEMP | + PMBUS_HAVE_SAMPLES), + .read_word_data = tps25990_read_word_data, + .write_word_data = tps25990_write_word_data, + .read_byte_data = tps25990_read_byte_data, + .write_byte_data = tps25990_write_byte_data, + +#if IS_ENABLED(CONFIG_SENSORS_TPS25990_REGULATOR) + .reg_desc = tps25990_reg_desc, + .num_regulators = ARRAY_SIZE(tps25990_reg_desc), +#endif +}; + +static const struct i2c_device_id tps25990_i2c_id[] = { + { "tps25990" }, + {} +}; +MODULE_DEVICE_TABLE(i2c, tps25990_i2c_id); + +static const struct of_device_id tps25990_of_match[] = { + { .compatible = "ti,tps25990" }, + {} +}; +MODULE_DEVICE_TABLE(of, tps25990_of_match); + +static int tps25990_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct pmbus_driver_info *info; + u32 rimon = TPS25990_DEFAULT_RIMON; + int ret; + + ret = device_property_read_u32(dev, "ti,rimon-micro-ohms", &rimon); + if (ret < 0 && ret != -EINVAL) + return dev_err_probe(dev, ret, "failed to get rimon\n"); + + info = devm_kmemdup(dev, &tps25990_base_info, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + /* Adapt the current and power scale for each instance */ + tps25990_set_m(&info->m[PSC_CURRENT_IN], rimon); + tps25990_set_m(&info->m[PSC_POWER], rimon); + + return pmbus_do_probe(client, info); +} + +static struct i2c_driver tps25990_driver = { + .driver = { + .name = "tps25990", + .of_match_table = tps25990_of_match, + }, + .probe = tps25990_probe, + .id_table = tps25990_i2c_id, +}; +module_i2c_driver(tps25990_driver); + +MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_DESCRIPTION("PMBUS driver for TPS25990 eFuse"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/tps40422.c b/drivers/hwmon/pmbus/tps40422.c index d99b9850ea36..7c9fedaa068c 100644 --- a/drivers/hwmon/pmbus/tps40422.c +++ b/drivers/hwmon/pmbus/tps40422.c @@ -51,4 +51,4 @@ module_i2c_driver(tps40422_driver); MODULE_AUTHOR("Zhu Laiwen <richard.zhu@nsn.com>"); MODULE_DESCRIPTION("PMBus driver for TI TPS40422"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/tps53679.c b/drivers/hwmon/pmbus/tps53679.c index 5c9466244d70..63524dff5e75 100644 --- a/drivers/hwmon/pmbus/tps53679.c +++ b/drivers/hwmon/pmbus/tps53679.c @@ -308,4 +308,4 @@ module_i2c_driver(tps53679_driver); MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>"); MODULE_DESCRIPTION("PMBus driver for Texas Instruments TPS53679"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/tps546d24.c b/drivers/hwmon/pmbus/tps546d24.c index 520ca37269f7..44d7a6df1dbd 100644 --- a/drivers/hwmon/pmbus/tps546d24.c +++ b/drivers/hwmon/pmbus/tps546d24.c @@ -68,4 +68,4 @@ module_i2c_driver(tps546d24_driver); MODULE_AUTHOR("Duke Du <dukedu83@gmail.com>"); MODULE_DESCRIPTION("PMBus driver for TI tps546d24"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c index 5d3d1773bf52..9b0eadc81a2e 100644 --- a/drivers/hwmon/pmbus/ucd9000.c +++ b/drivers/hwmon/pmbus/ucd9000.c @@ -642,4 +642,4 @@ module_i2c_driver(ucd9000_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for TI UCD90xxx"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/ucd9200.c b/drivers/hwmon/pmbus/ucd9200.c index 7920d1c06df0..f68adaf4a110 100644 --- a/drivers/hwmon/pmbus/ucd9200.c +++ b/drivers/hwmon/pmbus/ucd9200.c @@ -209,4 +209,4 @@ module_i2c_driver(ucd9200_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for TI UCD922x, UCD924x"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/xdp710.c b/drivers/hwmon/pmbus/xdp710.c index dd107e83f612..660bbfe16e1e 100644 --- a/drivers/hwmon/pmbus/xdp710.c +++ b/drivers/hwmon/pmbus/xdp710.c @@ -128,4 +128,4 @@ module_i2c_driver(xdp710_driver); MODULE_AUTHOR("Peter Yin <peter.yin@quantatw.com>"); MODULE_DESCRIPTION("PMBus driver for XDP710 HSC"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/xdpe12284.c b/drivers/hwmon/pmbus/xdpe12284.c index facb1201aa43..f3aa6339d60d 100644 --- a/drivers/hwmon/pmbus/xdpe12284.c +++ b/drivers/hwmon/pmbus/xdpe12284.c @@ -194,4 +194,4 @@ module_i2c_driver(xdpe122_driver); MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>"); MODULE_DESCRIPTION("PMBus driver for Infineon XDPE122 family"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/xdpe152c4.c b/drivers/hwmon/pmbus/xdpe152c4.c index 7f3b31d4f033..67a3d5fe1daf 100644 --- a/drivers/hwmon/pmbus/xdpe152c4.c +++ b/drivers/hwmon/pmbus/xdpe152c4.c @@ -72,4 +72,4 @@ module_i2c_driver(xdpe152_driver); MODULE_AUTHOR("Greg Schwendimann <greg.schwendimann@infineon.com>"); MODULE_DESCRIPTION("PMBus driver for Infineon XDPE152 family"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pmbus/zl6100.c b/drivers/hwmon/pmbus/zl6100.c index 7920a16203e1..97be69630cfb 100644 --- a/drivers/hwmon/pmbus/zl6100.c +++ b/drivers/hwmon/pmbus/zl6100.c @@ -420,4 +420,4 @@ module_i2c_driver(zl6100_driver); MODULE_AUTHOR("Guenter Roeck"); MODULE_DESCRIPTION("PMBus driver for ZL6100 and compatibles"); MODULE_LICENSE("GPL"); -MODULE_IMPORT_NS(PMBUS); +MODULE_IMPORT_NS("PMBUS"); diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c index 53a1a968d00d..579d31bb9ac7 100644 --- a/drivers/hwmon/pwm-fan.c +++ b/drivers/hwmon/pwm-fan.c @@ -497,7 +497,7 @@ static int pwm_fan_probe(struct platform_device *pdev) struct device *hwmon; int ret; const struct hwmon_channel_info **channels; - u32 pwm_min_from_stopped = 0; + u32 initial_pwm, pwm_min_from_stopped = 0; u32 *fan_channel_config; int channel_count = 1; /* We always have a PWM channel. */ int i; @@ -545,11 +545,21 @@ static int pwm_fan_probe(struct platform_device *pdev) ctx->enable_mode = pwm_disable_reg_enable; + ret = pwm_fan_get_cooling_data(dev, ctx); + if (ret) + return ret; + + /* use maximum cooling level if provided */ + if (ctx->pwm_fan_cooling_levels) + initial_pwm = ctx->pwm_fan_cooling_levels[ctx->pwm_fan_max_state]; + else + initial_pwm = MAX_PWM; + /* * 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); + ret = set_pwm(ctx, initial_pwm); if (ret) { dev_err(dev, "Failed to configure PWM: %d\n", ret); return ret; @@ -638,16 +648,16 @@ static int pwm_fan_probe(struct platform_device *pdev) channels[1] = &ctx->fan_channel; } - ret = of_property_read_u32(dev->of_node, "fan-stop-to-start-percent", - &pwm_min_from_stopped); + ret = device_property_read_u32(dev, "fan-stop-to-start-percent", + &pwm_min_from_stopped); if (!ret && pwm_min_from_stopped) { ctx->pwm_duty_cycle_from_stopped = DIV_ROUND_UP_ULL(pwm_min_from_stopped * (ctx->pwm_state.period - 1), 100); } - ret = of_property_read_u32(dev->of_node, "fan-stop-to-start-us", - &ctx->pwm_usec_from_stopped); + ret = device_property_read_u32(dev, "fan-stop-to-start-us", + &ctx->pwm_usec_from_stopped); if (ret) ctx->pwm_usec_from_stopped = 250000; @@ -661,10 +671,6 @@ static int pwm_fan_probe(struct platform_device *pdev) return PTR_ERR(hwmon); } - ret = pwm_fan_get_cooling_data(dev, ctx); - if (ret) - return ret; - ctx->pwm_fan_state = ctx->pwm_fan_max_state; if (IS_ENABLED(CONFIG_THERMAL)) { cdev = devm_thermal_of_cooling_device_register(dev, diff --git a/drivers/hwmon/qnap-mcu-hwmon.c b/drivers/hwmon/qnap-mcu-hwmon.c new file mode 100644 index 000000000000..29057514739c --- /dev/null +++ b/drivers/hwmon/qnap-mcu-hwmon.c @@ -0,0 +1,364 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* + * Driver for hwmon elements found on QNAP-MCU devices + * + * Copyright (C) 2024 Heiko Stuebner <heiko@sntech.de> + */ + +#include <linux/fwnode.h> +#include <linux/hwmon.h> +#include <linux/mfd/qnap-mcu.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/thermal.h> + +struct qnap_mcu_hwmon { + struct qnap_mcu *mcu; + struct device *dev; + + unsigned int pwm_min; + unsigned int pwm_max; + + struct fwnode_handle *fan_node; + unsigned int fan_state; + unsigned int fan_max_state; + unsigned int *fan_cooling_levels; + + struct thermal_cooling_device *cdev; + struct hwmon_chip_info info; +}; + +static int qnap_mcu_hwmon_get_rpm(struct qnap_mcu_hwmon *hwm) +{ + static const u8 cmd[] = { '@', 'F', 'A' }; + u8 reply[6]; + int ret; + + /* poll the fan rpm */ + ret = qnap_mcu_exec(hwm->mcu, cmd, sizeof(cmd), reply, sizeof(reply)); + if (ret) + return ret; + + /* First 2 bytes must mirror the sent command */ + if (memcmp(cmd, reply, 2)) + return -EIO; + + return reply[4] * 30; +} + +static int qnap_mcu_hwmon_get_pwm(struct qnap_mcu_hwmon *hwm) +{ + static const u8 cmd[] = { '@', 'F', 'Z', '0' }; /* 0 = fan-id? */ + u8 reply[4]; + int ret; + + /* poll the fan pwm */ + ret = qnap_mcu_exec(hwm->mcu, cmd, sizeof(cmd), reply, sizeof(reply)); + if (ret) + return ret; + + /* First 3 bytes must mirror the sent command */ + if (memcmp(cmd, reply, 3)) + return -EIO; + + return reply[3]; +} + +static int qnap_mcu_hwmon_set_pwm(struct qnap_mcu_hwmon *hwm, u8 pwm) +{ + const u8 cmd[] = { '@', 'F', 'W', '0', pwm }; /* 0 = fan-id?, pwm 0-255 */ + + /* set the fan pwm */ + return qnap_mcu_exec_with_ack(hwm->mcu, cmd, sizeof(cmd)); +} + +static int qnap_mcu_hwmon_get_temp(struct qnap_mcu_hwmon *hwm) +{ + static const u8 cmd[] = { '@', 'T', '3' }; + u8 reply[4]; + int ret; + + /* poll the fan rpm */ + ret = qnap_mcu_exec(hwm->mcu, cmd, sizeof(cmd), reply, sizeof(reply)); + if (ret) + return ret; + + /* First bytes must mirror the sent command */ + if (memcmp(cmd, reply, sizeof(cmd))) + return -EIO; + + /* + * There is an unknown bit set in bit7. + * Bits [6:0] report the actual temperature as returned by the + * original qnap firmware-tools, so just drop bit7 for now. + */ + return (reply[3] & 0x7f) * 1000; +} + +static int qnap_mcu_hwmon_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct qnap_mcu_hwmon *hwm = dev_get_drvdata(dev); + + switch (attr) { + case hwmon_pwm_input: + if (val < 0 || val > 255) + return -EINVAL; + + if (val != 0) + val = clamp_val(val, hwm->pwm_min, hwm->pwm_max); + + return qnap_mcu_hwmon_set_pwm(hwm, val); + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int qnap_mcu_hwmon_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct qnap_mcu_hwmon *hwm = dev_get_drvdata(dev); + int ret; + + switch (type) { + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_input: + ret = qnap_mcu_hwmon_get_pwm(hwm); + if (ret < 0) + return ret; + + *val = ret; + return 0; + default: + return -EOPNOTSUPP; + } + case hwmon_fan: + ret = qnap_mcu_hwmon_get_rpm(hwm); + if (ret < 0) + return ret; + + *val = ret; + return 0; + case hwmon_temp: + ret = qnap_mcu_hwmon_get_temp(hwm); + if (ret < 0) + return ret; + + *val = ret; + return 0; + default: + return -EOPNOTSUPP; + } +} + +static umode_t qnap_mcu_hwmon_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_temp: + return 0444; + + case hwmon_pwm: + return 0644; + + case hwmon_fan: + return 0444; + + default: + return 0; + } +} + +static const struct hwmon_ops qnap_mcu_hwmon_hwmon_ops = { + .is_visible = qnap_mcu_hwmon_is_visible, + .read = qnap_mcu_hwmon_read, + .write = qnap_mcu_hwmon_write, +}; + +/* thermal cooling device callbacks */ +static int qnap_mcu_hwmon_get_max_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct qnap_mcu_hwmon *hwm = cdev->devdata; + + if (!hwm) + return -EINVAL; + + *state = hwm->fan_max_state; + + return 0; +} + +static int qnap_mcu_hwmon_get_cur_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct qnap_mcu_hwmon *hwm = cdev->devdata; + + if (!hwm) + return -EINVAL; + + *state = hwm->fan_state; + + return 0; +} + +static int qnap_mcu_hwmon_set_cur_state(struct thermal_cooling_device *cdev, + unsigned long state) +{ + struct qnap_mcu_hwmon *hwm = cdev->devdata; + int ret; + + if (!hwm || state > hwm->fan_max_state) + return -EINVAL; + + if (state == hwm->fan_state) + return 0; + + ret = qnap_mcu_hwmon_set_pwm(hwm, hwm->fan_cooling_levels[state]); + if (ret) + return ret; + + hwm->fan_state = state; + + return ret; +} + +static const struct thermal_cooling_device_ops qnap_mcu_hwmon_cooling_ops = { + .get_max_state = qnap_mcu_hwmon_get_max_state, + .get_cur_state = qnap_mcu_hwmon_get_cur_state, + .set_cur_state = qnap_mcu_hwmon_set_cur_state, +}; + +static void devm_fan_node_release(void *data) +{ + struct qnap_mcu_hwmon *hwm = data; + + fwnode_handle_put(hwm->fan_node); +} + +static int qnap_mcu_hwmon_get_cooling_data(struct device *dev, struct qnap_mcu_hwmon *hwm) +{ + struct fwnode_handle *fwnode; + int num, i, ret; + + fwnode = device_get_named_child_node(dev->parent, "fan-0"); + if (!fwnode) + return 0; + + /* if we found the fan-node, we're keeping it until device-unbind */ + hwm->fan_node = fwnode; + ret = devm_add_action_or_reset(dev, devm_fan_node_release, hwm); + if (ret) + return ret; + + num = fwnode_property_count_u32(fwnode, "cooling-levels"); + if (num <= 0) + return dev_err_probe(dev, num ? : -EINVAL, + "Failed to count elements in 'cooling-levels'\n"); + + hwm->fan_cooling_levels = devm_kcalloc(dev, num, sizeof(u32), + GFP_KERNEL); + if (!hwm->fan_cooling_levels) + return -ENOMEM; + + ret = fwnode_property_read_u32_array(fwnode, "cooling-levels", + hwm->fan_cooling_levels, num); + if (ret) + return dev_err_probe(dev, ret, "Failed to read 'cooling-levels'\n"); + + for (i = 0; i < num; i++) { + if (hwm->fan_cooling_levels[i] > hwm->pwm_max) + return dev_err_probe(dev, -EINVAL, "fan state[%d]:%d > %d\n", i, + hwm->fan_cooling_levels[i], hwm->pwm_max); + } + + hwm->fan_max_state = num - 1; + + return 0; +} + +static const struct hwmon_channel_info * const qnap_mcu_hwmon_channels[] = { + HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT), + HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT), + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), + NULL +}; + +static int qnap_mcu_hwmon_probe(struct platform_device *pdev) +{ + struct qnap_mcu *mcu = dev_get_drvdata(pdev->dev.parent); + const struct qnap_mcu_variant *variant = pdev->dev.platform_data; + struct qnap_mcu_hwmon *hwm; + struct thermal_cooling_device *cdev; + struct device *dev = &pdev->dev; + struct device *hwmon; + int ret; + + hwm = devm_kzalloc(dev, sizeof(*hwm), GFP_KERNEL); + if (!hwm) + return -ENOMEM; + + hwm->mcu = mcu; + hwm->dev = &pdev->dev; + hwm->pwm_min = variant->fan_pwm_min; + hwm->pwm_max = variant->fan_pwm_max; + + platform_set_drvdata(pdev, hwm); + + /* + * Set duty cycle to maximum allowed. + */ + ret = qnap_mcu_hwmon_set_pwm(hwm, hwm->pwm_max); + if (ret) + return ret; + + hwm->info.ops = &qnap_mcu_hwmon_hwmon_ops; + hwm->info.info = qnap_mcu_hwmon_channels; + + ret = qnap_mcu_hwmon_get_cooling_data(dev, hwm); + if (ret) + return ret; + + hwm->fan_state = hwm->fan_max_state; + + hwmon = devm_hwmon_device_register_with_info(dev, "qnapmcu", + hwm, &hwm->info, NULL); + if (IS_ERR(hwmon)) + return dev_err_probe(dev, PTR_ERR(hwmon), "Failed to register hwmon device\n"); + + /* + * Only register cooling device when we found cooling-levels. + * qnap_mcu_hwmon_get_cooling_data() will fail when reading malformed + * levels and only succeed with either no or correct cooling levels. + */ + if (IS_ENABLED(CONFIG_THERMAL) && hwm->fan_cooling_levels) { + cdev = devm_thermal_of_cooling_device_register(dev, + to_of_node(hwm->fan_node), "qnap-mcu-hwmon", + hwm, &qnap_mcu_hwmon_cooling_ops); + if (IS_ERR(cdev)) + return dev_err_probe(dev, PTR_ERR(cdev), + "Failed to register qnap-mcu-hwmon as cooling device\n"); + hwm->cdev = cdev; + } + + return 0; +} + +static struct platform_driver qnap_mcu_hwmon_driver = { + .probe = qnap_mcu_hwmon_probe, + .driver = { + .name = "qnap-mcu-hwmon", + }, +}; +module_platform_driver(qnap_mcu_hwmon_driver); + +MODULE_ALIAS("platform:qnap-mcu-hwmon"); +MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>"); +MODULE_DESCRIPTION("QNAP MCU hwmon driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/raspberrypi-hwmon.c b/drivers/hwmon/raspberrypi-hwmon.c index 10ef1e1f9458..a2938881ccd2 100644 --- a/drivers/hwmon/raspberrypi-hwmon.c +++ b/drivers/hwmon/raspberrypi-hwmon.c @@ -128,10 +128,32 @@ static int rpi_hwmon_probe(struct platform_device *pdev) return 0; } +static int rpi_hwmon_suspend(struct device *dev) +{ + struct rpi_hwmon_data *data = dev_get_drvdata(dev); + + cancel_delayed_work_sync(&data->get_values_poll_work); + + return 0; +} + +static int rpi_hwmon_resume(struct device *dev) +{ + struct rpi_hwmon_data *data = dev_get_drvdata(dev); + + get_values_poll(&data->get_values_poll_work.work); + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(rpi_hwmon_pm_ops, rpi_hwmon_suspend, + rpi_hwmon_resume); + static struct platform_driver rpi_hwmon_driver = { .probe = rpi_hwmon_probe, .driver = { .name = "raspberrypi-hwmon", + .pm = pm_ptr(&rpi_hwmon_pm_ops), }, }; module_platform_driver(rpi_hwmon_driver); diff --git a/drivers/hwmon/tmp108.c b/drivers/hwmon/tmp108.c index fbe673009126..a971ff628435 100644 --- a/drivers/hwmon/tmp108.c +++ b/drivers/hwmon/tmp108.c @@ -8,15 +8,15 @@ #include <linux/device.h> #include <linux/err.h> #include <linux/hwmon.h> -#include <linux/hwmon-sysfs.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/mutex.h> -#include <linux/of.h> #include <linux/i2c.h> #include <linux/i3c/device.h> #include <linux/init.h> #include <linux/jiffies.h> #include <linux/regmap.h> +#include <linux/regulator/consumer.h> #include <linux/slab.h> #define DRIVER_NAME "tmp108" @@ -331,6 +331,10 @@ static int tmp108_common_probe(struct device *dev, struct regmap *regmap, char * u32 config; int err; + err = devm_regulator_get_enable(dev, "vcc"); + if (err) + return dev_err_probe(dev, err, "Failed to enable regulator\n"); + tmp108 = devm_kzalloc(dev, sizeof(*tmp108), GFP_KERNEL); if (!tmp108) return -ENOMEM; @@ -417,25 +421,24 @@ static int tmp108_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(tmp108_dev_pm_ops, tmp108_suspend, tmp108_resume); static const struct i2c_device_id tmp108_i2c_ids[] = { + { "p3t1085" }, { "tmp108" }, { } }; MODULE_DEVICE_TABLE(i2c, tmp108_i2c_ids); -#ifdef CONFIG_OF static const struct of_device_id tmp108_of_ids[] = { { .compatible = "nxp,p3t1085", }, { .compatible = "ti,tmp108", }, {} }; MODULE_DEVICE_TABLE(of, tmp108_of_ids); -#endif static struct i2c_driver tmp108_driver = { .driver = { .name = DRIVER_NAME, .pm = pm_sleep_ptr(&tmp108_dev_pm_ops), - .of_match_table = of_match_ptr(tmp108_of_ids), + .of_match_table = tmp108_of_ids, }, .probe = tmp108_probe, .id_table = tmp108_i2c_ids, diff --git a/drivers/hwmon/tmp513.c b/drivers/hwmon/tmp513.c index 926d28cd3fab..5acbfd7d088d 100644 --- a/drivers/hwmon/tmp513.c +++ b/drivers/hwmon/tmp513.c @@ -182,7 +182,7 @@ struct tmp51x_data { struct regmap *regmap; }; -// Set the shift based on the gain 8=4, 4=3, 2=2, 1=1 +// Set the shift based on the gain: 8 -> 1, 4 -> 2, 2 -> 3, 1 -> 4 static inline u8 tmp51x_get_pga_shift(struct tmp51x_data *data) { return 5 - ffs(data->pga_gain); @@ -204,8 +204,11 @@ static int tmp51x_get_value(struct tmp51x_data *data, u8 reg, u8 pos, * 2's complement number shifted by one to four depending * on the pga gain setting. 1lsb = 10uV */ - *val = sign_extend32(regval, 17 - tmp51x_get_pga_shift(data)); - *val = DIV_ROUND_CLOSEST(*val * 10 * MILLI, data->shunt_uohms); + *val = sign_extend32(regval, + reg == TMP51X_SHUNT_CURRENT_RESULT ? + 16 - tmp51x_get_pga_shift(data) : 15); + *val = DIV_ROUND_CLOSEST(*val * 10 * (long)MILLI, (long)data->shunt_uohms); + break; case TMP51X_BUS_VOLTAGE_RESULT: case TMP51X_BUS_VOLTAGE_H_LIMIT: @@ -220,8 +223,8 @@ static int tmp51x_get_value(struct tmp51x_data *data, u8 reg, u8 pos, break; case TMP51X_BUS_CURRENT_RESULT: // Current = (ShuntVoltage * CalibrationRegister) / 4096 - *val = sign_extend32(regval, 16) * data->curr_lsb_ua; - *val = DIV_ROUND_CLOSEST(*val, MILLI); + *val = sign_extend32(regval, 15) * (long)data->curr_lsb_ua; + *val = DIV_ROUND_CLOSEST(*val, (long)MILLI); break; case TMP51X_LOCAL_TEMP_RESULT: case TMP51X_REMOTE_TEMP_RESULT_1: @@ -232,7 +235,7 @@ static int tmp51x_get_value(struct tmp51x_data *data, u8 reg, u8 pos, case TMP51X_REMOTE_TEMP_LIMIT_2: case TMP513_REMOTE_TEMP_LIMIT_3: // 1lsb = 0.0625 degrees centigrade - *val = sign_extend32(regval, 16) >> TMP51X_TEMP_SHIFT; + *val = sign_extend32(regval, 15) >> TMP51X_TEMP_SHIFT; *val = DIV_ROUND_CLOSEST(*val * 625, 10); break; case TMP51X_N_FACTOR_AND_HYST_1: @@ -261,7 +264,7 @@ static int tmp51x_set_value(struct tmp51x_data *data, u8 reg, long val) * The user enter current value and we convert it to * voltage. 1lsb = 10uV */ - val = DIV_ROUND_CLOSEST(val * data->shunt_uohms, 10 * MILLI); + val = DIV_ROUND_CLOSEST(val * (long)data->shunt_uohms, 10 * (long)MILLI); max_val = U16_MAX >> tmp51x_get_pga_shift(data); regval = clamp_val(val, -max_val, max_val); break; |