From 5997eb60f896830126bc1783465b678b110e2fdd Mon Sep 17 00:00:00 2001 From: Marius Zachmann Date: Mon, 13 May 2024 21:47:34 +0200 Subject: hwmon: (corsair-cpro) Add firmware and bootloader information Add support for reporting firmware and bootloader version using debugfs. Update documentation accordingly. Signed-off-by: Marius Zachmann Link: https://lore.kernel.org/r/20240513194734.43040-2-mail@mariuszachmann.de Signed-off-by: Guenter Roeck --- drivers/hwmon/corsair-cpro.c | 88 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/corsair-cpro.c b/drivers/hwmon/corsair-cpro.c index 3e63666a61bd..e3300f3f4da6 100644 --- a/drivers/hwmon/corsair-cpro.c +++ b/drivers/hwmon/corsair-cpro.c @@ -10,11 +10,13 @@ #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -28,6 +30,8 @@ #define LABEL_LENGTH 11 #define REQ_TIMEOUT 300 +#define CTL_GET_FW_VER 0x02 /* returns the firmware version in bytes 1-3 */ +#define CTL_GET_BL_VER 0x06 /* returns the bootloader version in bytes 1-2 */ #define CTL_GET_TMP_CNCT 0x10 /* * returns in bytes 1-4 for each temp sensor: * 0 not connected @@ -78,6 +82,7 @@ struct ccp_device { struct hid_device *hdev; struct device *hwmon_dev; + struct dentry *debugfs; /* For reinitializing the completion below */ spinlock_t wait_input_report_lock; struct completion wait_input_report; @@ -88,6 +93,8 @@ struct ccp_device { DECLARE_BITMAP(temp_cnct, NUM_TEMP_SENSORS); DECLARE_BITMAP(fan_cnct, NUM_FANS); char fan_label[6][LABEL_LENGTH]; + u8 firmware_ver[3]; + u8 bootloader_ver[2]; }; /* converts response error in buffer to errno */ @@ -496,6 +503,83 @@ static int get_temp_cnct(struct ccp_device *ccp) return 0; } +/* read firmware version */ +static int get_fw_version(struct ccp_device *ccp) +{ + int ret; + + ret = send_usb_cmd(ccp, CTL_GET_FW_VER, 0, 0, 0); + if (ret) { + hid_notice(ccp->hdev, "Failed to read firmware version.\n"); + return ret; + } + ccp->firmware_ver[0] = ccp->buffer[1]; + ccp->firmware_ver[1] = ccp->buffer[2]; + ccp->firmware_ver[2] = ccp->buffer[3]; + + return 0; +} + +/* read bootloader version */ +static int get_bl_version(struct ccp_device *ccp) +{ + int ret; + + ret = send_usb_cmd(ccp, CTL_GET_BL_VER, 0, 0, 0); + if (ret) { + hid_notice(ccp->hdev, "Failed to read bootloader version.\n"); + return ret; + } + ccp->bootloader_ver[0] = ccp->buffer[1]; + ccp->bootloader_ver[1] = ccp->buffer[2]; + + return 0; +} + +static int firmware_show(struct seq_file *seqf, void *unused) +{ + struct ccp_device *ccp = seqf->private; + + seq_printf(seqf, "%d.%d.%d\n", + ccp->firmware_ver[0], + ccp->firmware_ver[1], + ccp->firmware_ver[2]); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(firmware); + +static int bootloader_show(struct seq_file *seqf, void *unused) +{ + struct ccp_device *ccp = seqf->private; + + seq_printf(seqf, "%d.%d\n", + ccp->bootloader_ver[0], + ccp->bootloader_ver[1]); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(bootloader); + +static void ccp_debugfs_init(struct ccp_device *ccp) +{ + char name[32]; + int ret; + + scnprintf(name, sizeof(name), "corsaircpro-%s", dev_name(&ccp->hdev->dev)); + ccp->debugfs = debugfs_create_dir(name, NULL); + + ret = get_fw_version(ccp); + if (!ret) + debugfs_create_file("firmware_version", 0444, + ccp->debugfs, ccp, &firmware_fops); + + ret = get_bl_version(ccp); + if (!ret) + debugfs_create_file("bootloader_version", 0444, + ccp->debugfs, ccp, &bootloader_fops); +} + static int ccp_probe(struct hid_device *hdev, const struct hid_device_id *id) { struct ccp_device *ccp; @@ -542,6 +626,9 @@ static int ccp_probe(struct hid_device *hdev, const struct hid_device_id *id) ret = get_fan_cnct(ccp); if (ret) goto out_hw_close; + + ccp_debugfs_init(ccp); + ccp->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "corsaircpro", ccp, &ccp_chip_info, NULL); if (IS_ERR(ccp->hwmon_dev)) { @@ -562,6 +649,7 @@ static void ccp_remove(struct hid_device *hdev) { struct ccp_device *ccp = hid_get_drvdata(hdev); + debugfs_remove_recursive(ccp->debugfs); hwmon_device_unregister(ccp->hwmon_dev); hid_hw_close(hdev); hid_hw_stop(hdev); -- cgit From be7d9294a411aa116d9f5874bd0ad982bb3eb7cc Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 24 May 2024 15:38:16 -0700 Subject: hwmon: (nct6683) Display warning when enabling driver for unknown customer Enabling the driver for devices with unknown customer ID is at least somewhat risky, so add a warning to the kernel log. Signed-off-by: Guenter Roeck --- drivers/hwmon/nct6683.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/nct6683.c b/drivers/hwmon/nct6683.c index 0d016fedb9c2..f71615e06a8f 100644 --- a/drivers/hwmon/nct6683.c +++ b/drivers/hwmon/nct6683.c @@ -1236,6 +1236,8 @@ static int nct6683_probe(struct platform_device *pdev) default: if (!force) return -ENODEV; + dev_warn(dev, "Enabling support for unknown customer ID 0x%04x\n", data->customer_id); + break; } nct6683_init_device(data); -- cgit From b9c15c96ccb47ad860af2e075c5f3c90c4cd1730 Mon Sep 17 00:00:00 2001 From: Wilken Gottwalt Date: Fri, 24 May 2024 04:38:11 +0000 Subject: hwmon: corsair-psu: add USB id of HX1200i Series 2023 psu Add the usb id of the HX1200i Series 2023. Update the documentation accordingly. Also fix the version comments, there are no Series 2022 products. That are legacy or first version products going back many many years. Signed-off-by: Wilken Gottwalt Link: https://lore.kernel.org/r/ZlAZs4u0dU7JxtDf@monster.localdomain Signed-off-by: Guenter Roeck --- drivers/hwmon/corsair-psu.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/corsair-psu.c b/drivers/hwmon/corsair-psu.c index 2c7c92272fe3..f8f22b8a67cd 100644 --- a/drivers/hwmon/corsair-psu.c +++ b/drivers/hwmon/corsair-psu.c @@ -875,15 +875,16 @@ static const struct hid_device_id corsairpsu_idtable[] = { { HID_USB_DEVICE(0x1b1c, 0x1c04) }, /* Corsair HX650i */ { HID_USB_DEVICE(0x1b1c, 0x1c05) }, /* Corsair HX750i */ { HID_USB_DEVICE(0x1b1c, 0x1c06) }, /* Corsair HX850i */ - { HID_USB_DEVICE(0x1b1c, 0x1c07) }, /* Corsair HX1000i Series 2022 */ - { HID_USB_DEVICE(0x1b1c, 0x1c08) }, /* Corsair HX1200i */ + { HID_USB_DEVICE(0x1b1c, 0x1c07) }, /* Corsair HX1000i Legacy */ + { HID_USB_DEVICE(0x1b1c, 0x1c08) }, /* Corsair HX1200i Legacy */ { HID_USB_DEVICE(0x1b1c, 0x1c09) }, /* Corsair RM550i */ { HID_USB_DEVICE(0x1b1c, 0x1c0a) }, /* Corsair RM650i */ { HID_USB_DEVICE(0x1b1c, 0x1c0b) }, /* Corsair RM750i */ { HID_USB_DEVICE(0x1b1c, 0x1c0c) }, /* Corsair RM850i */ { HID_USB_DEVICE(0x1b1c, 0x1c0d) }, /* Corsair RM1000i */ { HID_USB_DEVICE(0x1b1c, 0x1c1e) }, /* Corsair HX1000i Series 2023 */ - { HID_USB_DEVICE(0x1b1c, 0x1c1f) }, /* Corsair HX1500i Series 2022 and 2023 */ + { HID_USB_DEVICE(0x1b1c, 0x1c1f) }, /* Corsair HX1500i Legacy and Series 2023 */ + { HID_USB_DEVICE(0x1b1c, 0x1c23) }, /* Corsair HX1200i Series 2023 */ { }, }; MODULE_DEVICE_TABLE(hid, corsairpsu_idtable); -- cgit From 246e6426a87ee4dbb8cd08c9b43323263b5ab76f Mon Sep 17 00:00:00 2001 From: Aleksandr Mezin Date: Fri, 24 May 2024 03:39:58 +0300 Subject: hwmon: (nzxt-smart2) Add support for another version of NZXT RGB & Fan Controller Add support for NZXT RGB & Fan Controller with USB ID 1e71:2020. Fan speed control reported to be working with existing userspace (hidraw) software, so it should be compatible. Fan channel count is the same. No known differences from already supported devices, at least regarding fan speed control and initialization. Signed-off-by: Aleksandr Mezin Link: https://lore.kernel.org/r/20240524004040.121044-1-mezin.alexander@gmail.com [groeck: Adjusted subject and description] Signed-off-by: Guenter Roeck --- drivers/hwmon/nzxt-smart2.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/nzxt-smart2.c b/drivers/hwmon/nzxt-smart2.c index 7aa586eb74be..df6fa72a6b59 100644 --- a/drivers/hwmon/nzxt-smart2.c +++ b/drivers/hwmon/nzxt-smart2.c @@ -799,6 +799,7 @@ static const struct hid_device_id nzxt_smart2_hid_id_table[] = { { HID_USB_DEVICE(0x1e71, 0x2010) }, /* NZXT RGB & Fan Controller */ { HID_USB_DEVICE(0x1e71, 0x2011) }, /* NZXT RGB & Fan Controller (6 RGB) */ { HID_USB_DEVICE(0x1e71, 0x2019) }, /* NZXT RGB & Fan Controller (6 RGB) */ + { HID_USB_DEVICE(0x1e71, 0x2020) }, /* NZXT RGB & Fan Controller (6 RGB) */ {}, }; -- cgit From 4ce6e8a859f0503d97aac6869bc3b1a24b15601d Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 28 May 2024 13:33:29 -0700 Subject: hwmon: Add PEC attribute support to hardware monitoring core Several hardware monitoring chips optionally support Packet Error Checking (PEC). For some chips, PEC support can be enabled simply by setting I2C_CLIENT_PEC in the i2c client data structure. Others require chip specific code to enable or disable PEC support. Introduce hwmon_chip_pec and HWMON_C_PEC to simplify adding configurable PEC support for hardware monitoring drivers. A driver can set HWMON_C_PEC in its chip information data to indicate PEC support. If a chip requires chip specific code to enable or disable PEC support, the driver only needs to implement support for the hwmon_chip_pec attribute to its write function. Packet Error Checking is only supported for SMBus devices. HWMON_C_PEC must therefore only be set by a driver if the parent device is an I2C device. Attempts to set HWMON_C_PEC on any other device type is not supported and rejected. The code calls i2c_check_functionality() to check if PEC is supported by the I2C/SMBus controller. This function is only available if CONFIG_I2C is enabled and reachable. For this reason, the added code needs to depend on reachability of CONFIG_I2C. Cc: Radu Sabau Acked-by: Nuno Sa Signed-off-by: Guenter Roeck --- drivers/hwmon/hwmon.c | 147 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 131 insertions(+), 16 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index 3b259c425ab7..8aee253b5829 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -309,6 +310,114 @@ static int hwmon_attr_base(enum hwmon_sensor_types type) return 1; } +#if IS_REACHABLE(CONFIG_I2C) + +/* + * PEC support + * + * The 'pec' attribute is attached to I2C client devices. It is only provided + * if the i2c controller supports PEC. + * + * The mutex ensures that PEC configuration between i2c device and the hardware + * is consistent. Use a single mutex because attribute writes are supposed to be + * rare, and maintaining a separate mutex for each hardware monitoring device + * would add substantial complexity to the driver for little if any gain. + * + * The hardware monitoring device is identified as child of the i2c client + * device. This assumes that only a single hardware monitoring device is + * attached to an i2c client device. + */ + +static DEFINE_MUTEX(hwmon_pec_mutex); + +static int hwmon_match_device(struct device *dev, void *data) +{ + return dev->class == &hwmon_class; +} + +static ssize_t pec_show(struct device *dev, struct device_attribute *dummy, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + + return sysfs_emit(buf, "%d\n", !!(client->flags & I2C_CLIENT_PEC)); +} + +static ssize_t pec_store(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct hwmon_device *hwdev; + struct device *hdev; + bool val; + int err; + + err = kstrtobool(buf, &val); + if (err < 0) + return err; + + hdev = device_find_child(dev, NULL, hwmon_match_device); + if (!hdev) + return -ENODEV; + + mutex_lock(&hwmon_pec_mutex); + + /* + * If there is no write function, we assume that chip specific + * handling is not required. + */ + hwdev = to_hwmon_device(hdev); + if (hwdev->chip->ops->write) { + err = hwdev->chip->ops->write(hdev, hwmon_chip, hwmon_chip_pec, 0, val); + if (err && err != -EOPNOTSUPP) + goto unlock; + } + + if (!val) + client->flags &= ~I2C_CLIENT_PEC; + else + client->flags |= I2C_CLIENT_PEC; + + err = count; +unlock: + mutex_unlock(&hwmon_pec_mutex); + put_device(hdev); + + return err; +} + +static DEVICE_ATTR_RW(pec); + +static void hwmon_remove_pec(void *dev) +{ + device_remove_file(dev, &dev_attr_pec); +} + +static int hwmon_pec_register(struct device *hdev) +{ + struct i2c_client *client = i2c_verify_client(hdev->parent); + int err; + + if (!client) + return -EINVAL; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_PEC)) + return 0; + + err = device_create_file(&client->dev, &dev_attr_pec); + if (err) + return err; + + return devm_add_action_or_reset(hdev, hwmon_remove_pec, &client->dev); +} + +#else /* CONFIG_I2C */ +static int hwmon_pec_register(struct device *hdev) +{ + return -EINVAL; +} +#endif /* CONFIG_I2C */ + /* sysfs attribute management */ static ssize_t hwmon_attr_show(struct device *dev, @@ -397,10 +506,6 @@ static struct attribute *hwmon_genattr(const void *drvdata, const char *name; bool is_string = is_string_attr(type, attr); - /* The attribute is invisible if there is no template string */ - if (!template) - return ERR_PTR(-ENOENT); - mode = ops->is_visible(drvdata, type, attr, index); if (!mode) return ERR_PTR(-ENOENT); @@ -712,8 +817,8 @@ static int hwmon_genattrs(const void *drvdata, attr = __ffs(attr_mask); attr_mask &= ~BIT(attr); - if (attr >= template_size) - return -EINVAL; + if (attr >= template_size || !templates[attr]) + continue; /* attribute is invisible */ a = hwmon_genattr(drvdata, info->type, attr, i, templates[attr], ops); if (IS_ERR(a)) { @@ -849,16 +954,26 @@ __hwmon_device_register(struct device *dev, const char *name, void *drvdata, INIT_LIST_HEAD(&hwdev->tzdata); if (hdev->of_node && chip && chip->ops->read && - chip->info[0]->type == hwmon_chip && - (chip->info[0]->config[0] & HWMON_C_REGISTER_TZ)) { - err = hwmon_thermal_register_sensors(hdev); - if (err) { - device_unregister(hdev); - /* - * Don't worry about hwdev; hwmon_dev_release(), called - * from device_unregister(), will free it. - */ - goto ida_remove; + chip->info[0]->type == hwmon_chip) { + u32 config = chip->info[0]->config[0]; + + if (config & HWMON_C_REGISTER_TZ) { + err = hwmon_thermal_register_sensors(hdev); + if (err) { + device_unregister(hdev); + /* + * Don't worry about hwdev; hwmon_dev_release(), + * called from device_unregister(), will free it. + */ + goto ida_remove; + } + } + if (config & HWMON_C_PEC) { + err = hwmon_pec_register(hdev); + if (err) { + device_unregister(hdev); + goto ida_remove; + } } } -- cgit From fcbb583f33af99e260aca2b5893b21ccf1021285 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 28 May 2024 13:42:42 -0700 Subject: hwmon: (lm90) Convert to use PEC support from hwmon core Replace driver specific PEC handling code with hardware monitoring core functionality. Cc: Radu Sabau Acked-by: Nuno Sa Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 56 ++-------------------------------------------------- 1 file changed, 2 insertions(+), 54 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index e0d7454a301c..40d9e21b528c 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -1270,42 +1270,6 @@ static int lm90_update_device(struct device *dev) return 0; } -/* pec used for devices with PEC support */ -static ssize_t pec_show(struct device *dev, struct device_attribute *dummy, - char *buf) -{ - struct i2c_client *client = to_i2c_client(dev); - - return sprintf(buf, "%d\n", !!(client->flags & I2C_CLIENT_PEC)); -} - -static ssize_t pec_store(struct device *dev, struct device_attribute *dummy, - const char *buf, size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - long val; - int err; - - err = kstrtol(buf, 10, &val); - if (err < 0) - return err; - - switch (val) { - case 0: - client->flags &= ~I2C_CLIENT_PEC; - break; - case 1: - client->flags |= I2C_CLIENT_PEC; - break; - default: - return -EINVAL; - } - - return count; -} - -static DEVICE_ATTR_RW(pec); - static int lm90_temp_get_resolution(struct lm90_data *data, int index) { switch (index) { @@ -2659,11 +2623,6 @@ static irqreturn_t lm90_irq_thread(int irq, void *dev_id) return IRQ_NONE; } -static void lm90_remove_pec(void *dev) -{ - device_remove_file(dev, &dev_attr_pec); -} - static int lm90_probe_channel_from_dt(struct i2c_client *client, struct device_node *child, struct lm90_data *data) @@ -2802,6 +2761,8 @@ static int lm90_probe(struct i2c_client *client) data->chip_config[0] |= HWMON_C_UPDATE_INTERVAL; if (data->flags & LM90_HAVE_FAULTQUEUE) data->chip_config[0] |= HWMON_C_TEMP_SAMPLES; + if (data->flags & (LM90_HAVE_PEC | LM90_HAVE_PARTIAL_PEC)) + data->chip_config[0] |= HWMON_C_PEC; data->info[1] = &data->temp_info; info = &data->temp_info; @@ -2878,19 +2839,6 @@ static int lm90_probe(struct i2c_client *client) return err; } - /* - * The 'pec' attribute is attached to the i2c device and thus created - * separately. - */ - if (data->flags & (LM90_HAVE_PEC | LM90_HAVE_PARTIAL_PEC)) { - err = device_create_file(dev, &dev_attr_pec); - if (err) - return err; - err = devm_add_action_or_reset(dev, lm90_remove_pec, dev); - if (err) - return err; - } - hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, &data->chip, NULL); -- cgit From 5124d9acf3af50bcc6d0958db4fecb3c2f13f8ed Mon Sep 17 00:00:00 2001 From: Radu Sabau Date: Fri, 31 May 2024 11:46:44 +0300 Subject: hwmon: (max31827) Add PEC support Add PEC support using the hwmon core infrastructure. Signed-off-by: Radu Sabau Reviewed-by: Nuno Sa Link: https://lore.kernel.org/r/20240531084645.12935-2-radu.sabau@analog.com [groeck: Adjusted subject; simplified description] Signed-off-by: Guenter Roeck --- drivers/hwmon/max31827.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/max31827.c b/drivers/hwmon/max31827.c index f8a13b30f100..4d89b6a7060b 100644 --- a/drivers/hwmon/max31827.c +++ b/drivers/hwmon/max31827.c @@ -24,6 +24,7 @@ #define MAX31827_CONFIGURATION_1SHOT_MASK BIT(0) #define MAX31827_CONFIGURATION_CNV_RATE_MASK GENMASK(3, 1) +#define MAX31827_CONFIGURATION_PEC_EN_MASK BIT(4) #define MAX31827_CONFIGURATION_TIMEOUT_MASK BIT(5) #define MAX31827_CONFIGURATION_RESOLUTION_MASK GENMASK(7, 6) #define MAX31827_CONFIGURATION_ALRM_POL_MASK BIT(8) @@ -382,7 +383,8 @@ static int max31827_write(struct device *dev, enum hwmon_sensor_types type, } case hwmon_chip: - if (attr == hwmon_chip_update_interval) { + switch (attr) { + case hwmon_chip_update_interval: if (!st->enable) return -EINVAL; @@ -410,14 +412,18 @@ static int max31827_write(struct device *dev, enum hwmon_sensor_types type, return ret; st->update_interval = val; - } - break; + return 0; + case hwmon_chip_pec: + return regmap_update_bits(st->regmap, MAX31827_CONFIGURATION_REG, + MAX31827_CONFIGURATION_PEC_EN_MASK, + val ? MAX31827_CONFIGURATION_PEC_EN_MASK : 0); + default: + return -EOPNOTSUPP; + } default: return -EOPNOTSUPP; } - - return 0; } static ssize_t temp1_resolution_show(struct device *dev, @@ -583,7 +589,7 @@ static const struct hwmon_channel_info *max31827_info[] = { HWMON_T_MIN_HYST | HWMON_T_MIN_ALARM | HWMON_T_MAX | HWMON_T_MAX_HYST | HWMON_T_MAX_ALARM), - HWMON_CHANNEL_INFO(chip, HWMON_C_UPDATE_INTERVAL), + HWMON_CHANNEL_INFO(chip, HWMON_C_UPDATE_INTERVAL | HWMON_C_PEC), NULL, }; -- cgit From 6ce402327a6fb714a9f40a0bb59bcbfe383839a5 Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Tue, 4 Jun 2024 18:43:43 +0200 Subject: hwmon: g672: add support for g761 Add support for g761 PWM Fan Controller. The g761 is a copy of the g763 with the only difference of supporting and internal clock. The internal clock is used if no clocks property is defined in device node and in such case the required bit is enabled and clock handling is skipped. The internal clock oscillator runs at 31KHz. Signed-off-by: Christian Marangi Link: https://lore.kernel.org/r/20240604164348.542-3-ansuelsmth@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/g762.c | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/g762.c b/drivers/hwmon/g762.c index af1228708e25..a00cf3245eec 100644 --- a/drivers/hwmon/g762.c +++ b/drivers/hwmon/g762.c @@ -69,6 +69,7 @@ enum g762_regs { #define G762_REG_FAN_CMD1_PWM_POLARITY 0x02 /* PWM polarity */ #define G762_REG_FAN_CMD1_PULSE_PER_REV 0x01 /* pulse per fan revolution */ +#define G761_REG_FAN_CMD2_FAN_CLOCK 0x20 /* choose internal clock*/ #define G762_REG_FAN_CMD2_GEAR_MODE_1 0x08 /* fan gear mode */ #define G762_REG_FAN_CMD2_GEAR_MODE_0 0x04 #define G762_REG_FAN_CMD2_FAN_STARTV_1 0x02 /* fan startup voltage */ @@ -115,6 +116,7 @@ enum g762_regs { struct g762_data { struct i2c_client *client; + bool internal_clock; struct clk *clk; /* update mutex */ @@ -566,6 +568,7 @@ static int do_set_fan_startv(struct device *dev, unsigned long val) #ifdef CONFIG_OF static const struct of_device_id g762_dt_match[] = { + { .compatible = "gmt,g761" }, { .compatible = "gmt,g762" }, { .compatible = "gmt,g763" }, { }, @@ -597,6 +600,21 @@ static int g762_of_clock_enable(struct i2c_client *client) if (!client->dev.of_node) return 0; + data = i2c_get_clientdata(client); + + /* + * Skip CLK detection and handling if we use internal clock. + * This is only valid for g761. + */ + data->internal_clock = of_device_is_compatible(client->dev.of_node, + "gmt,g761") && + !of_property_present(client->dev.of_node, + "clocks"); + if (data->internal_clock) { + do_set_clk_freq(&client->dev, 32768); + return 0; + } + clk = of_clk_get(client->dev.of_node, 0); if (IS_ERR(clk)) { dev_err(&client->dev, "failed to get clock\n"); @@ -616,7 +634,6 @@ static int g762_of_clock_enable(struct i2c_client *client) goto clk_unprep; } - data = i2c_get_clientdata(client); data->clk = clk; ret = devm_add_action(&client->dev, g762_of_clock_disable, data); @@ -1025,16 +1042,26 @@ ATTRIBUTE_GROUPS(g762); static inline int g762_fan_init(struct device *dev) { struct g762_data *data = g762_update_client(dev); + int ret; if (IS_ERR(data)) return PTR_ERR(data); + /* internal_clock can only be set with compatible g761 */ + if (data->internal_clock) + data->fan_cmd2 |= G761_REG_FAN_CMD2_FAN_CLOCK; + data->fan_cmd1 |= G762_REG_FAN_CMD1_DET_FAN_FAIL; data->fan_cmd1 |= G762_REG_FAN_CMD1_DET_FAN_OOC; data->valid = false; - return i2c_smbus_write_byte_data(data->client, G762_REG_FAN_CMD1, - data->fan_cmd1); + ret = i2c_smbus_write_byte_data(data->client, G762_REG_FAN_CMD1, + data->fan_cmd1); + if (ret) + return ret; + + return i2c_smbus_write_byte_data(data->client, G762_REG_FAN_CMD2, + data->fan_cmd2); } static int g762_probe(struct i2c_client *client) -- cgit From b1ea8f7a30a01d3e314cd889ac6bd9f41675d23f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 6 Jun 2024 16:25:15 +0200 Subject: hwmon: lm70: simplify with spi_get_device_match_data() Use spi_get_device_match_data() helper to simplify a bit the driver. Also kernel_ulong_t type is preferred for kernel code over uintptr_t (needed for the cast). Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240606142515.132504-1-krzysztof.kozlowski@linaro.org Signed-off-by: Guenter Roeck --- drivers/hwmon/lm70.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/lm70.c b/drivers/hwmon/lm70.c index 481e4e1f8f4f..0d5a250cb672 100644 --- a/drivers/hwmon/lm70.c +++ b/drivers/hwmon/lm70.c @@ -169,11 +169,7 @@ static int lm70_probe(struct spi_device *spi) struct lm70 *p_lm70; int chip; - if (dev_fwnode(&spi->dev)) - chip = (int)(uintptr_t)device_get_match_data(&spi->dev); - else - chip = spi_get_device_id(spi)->driver_data; - + chip = (kernel_ulong_t)spi_get_device_match_data(spi); /* signaling is SPI_MODE_0 */ if ((spi->mode & SPI_MODE_X_MASK) != SPI_MODE_0) -- cgit From 244f1664fe36471573ecb7eb1fb722f4d17e54bb Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 3 Apr 2024 15:36:03 -0500 Subject: hwmon: (ad7418) Remove use of i2c_match_id() The function i2c_match_id() is used to fetch the matching ID from the i2c_device_id table. This is often used to then retrieve the matching driver_data. This can be done in one step with the helper i2c_get_match_data(). This helper has a couple other benefits: * It doesn't need the i2c_device_id passed in so we do not need to have that forward declared, allowing us to remove those or move the i2c_device_id table down to its more natural spot with the other module info. * It also checks for device match data, which allows for OF and ACPI based probing. That means we do not have to manually check those first and can remove those checks. Signed-off-by: Andrew Davis Link: https://lore.kernel.org/r/20240403203633.914389-2-afd@ti.com Signed-off-by: Guenter Roeck --- drivers/hwmon/ad7418.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/ad7418.c b/drivers/hwmon/ad7418.c index 4829f83ff52e..7a132accdf8a 100644 --- a/drivers/hwmon/ad7418.c +++ b/drivers/hwmon/ad7418.c @@ -230,8 +230,6 @@ static void ad7418_init_client(struct i2c_client *client) } } -static const struct i2c_device_id ad7418_id[]; - static int ad7418_probe(struct i2c_client *client) { struct device *dev = &client->dev; @@ -252,10 +250,7 @@ static int ad7418_probe(struct i2c_client *client) mutex_init(&data->lock); data->client = client; - if (dev->of_node) - data->type = (uintptr_t)of_device_get_match_data(dev); - else - data->type = i2c_match_id(ad7418_id, client)->driver_data; + data->type = (uintptr_t)i2c_get_match_data(client); switch (data->type) { case ad7416: -- cgit From 884369b2c9f4897a09b97792f7b67d3ff9e11676 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 3 Apr 2024 15:36:04 -0500 Subject: hwmon: (adm1021) Remove use of i2c_match_id() The function i2c_match_id() is used to fetch the matching ID from the i2c_device_id table. This is often used to then retrieve the matching driver_data. This can be done in one step with the helper i2c_get_match_data(). This helper has a couple other benefits: * It doesn't need the i2c_device_id passed in so we do not need to have that forward declared, allowing us to remove those or move the i2c_device_id table down to its more natural spot with the other module info. * It also checks for device match data, which allows for OF and ACPI based probing. That means we do not have to manually check those first and can remove those checks. Signed-off-by: Andrew Davis Link: https://lore.kernel.org/r/20240403203633.914389-3-afd@ti.com Signed-off-by: Guenter Roeck --- drivers/hwmon/adm1021.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/adm1021.c b/drivers/hwmon/adm1021.c index 7c15398ebb37..87b42e0cfd2e 100644 --- a/drivers/hwmon/adm1021.c +++ b/drivers/hwmon/adm1021.c @@ -440,8 +440,6 @@ static void adm1021_init_client(struct i2c_client *client) i2c_smbus_write_byte_data(client, ADM1021_REG_CONV_RATE_W, 0x04); } -static const struct i2c_device_id adm1021_id[]; - static int adm1021_probe(struct i2c_client *client) { struct device *dev = &client->dev; @@ -453,7 +451,7 @@ static int adm1021_probe(struct i2c_client *client) return -ENOMEM; data->client = client; - data->type = i2c_match_id(adm1021_id, client)->driver_data; + data->type = (uintptr_t)i2c_get_match_data(client); mutex_init(&data->update_lock); /* Initialize the ADM1021 chip */ -- cgit From 0f1874f8474776e3af7ab4b2085bd9a1b8383783 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 3 Apr 2024 15:36:05 -0500 Subject: hwmon: (adm1031) Remove use of i2c_match_id() The function i2c_match_id() is used to fetch the matching ID from the i2c_device_id table. This is often used to then retrieve the matching driver_data. This can be done in one step with the helper i2c_get_match_data(). This helper has a couple other benefits: * It doesn't need the i2c_device_id passed in so we do not need to have that forward declared, allowing us to remove those or move the i2c_device_id table down to its more natural spot with the other module info. * It also checks for device match data, which allows for OF and ACPI based probing. That means we do not have to manually check those first and can remove those checks. Signed-off-by: Andrew Davis Link: https://lore.kernel.org/r/20240403203633.914389-4-afd@ti.com Signed-off-by: Guenter Roeck --- drivers/hwmon/adm1031.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/adm1031.c b/drivers/hwmon/adm1031.c index 88c7e0d62d08..343118532cdb 100644 --- a/drivers/hwmon/adm1031.c +++ b/drivers/hwmon/adm1031.c @@ -1021,8 +1021,6 @@ static void adm1031_init_client(struct i2c_client *client) data->update_interval = update_intervals[i]; } -static const struct i2c_device_id adm1031_id[]; - static int adm1031_probe(struct i2c_client *client) { struct device *dev = &client->dev; @@ -1035,7 +1033,7 @@ static int adm1031_probe(struct i2c_client *client) i2c_set_clientdata(client, data); data->client = client; - data->chip_type = i2c_match_id(adm1031_id, client)->driver_data; + data->chip_type = (uintptr_t)i2c_get_match_data(client); mutex_init(&data->update_lock); if (data->chip_type == adm1030) -- cgit From ec6755630773bbfdbf07abbab35ba78a581badfe Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 3 Apr 2024 15:36:06 -0500 Subject: hwmon: (ads7828) Remove use of i2c_match_id() The function i2c_match_id() is used to fetch the matching ID from the i2c_device_id table. This is often used to then retrieve the matching driver_data. This can be done in one step with the helper i2c_get_match_data(). This helper has a couple other benefits: * It doesn't need the i2c_device_id passed in so we do not need to have that forward declared, allowing us to remove those or move the i2c_device_id table down to its more natural spot with the other module info. * It also checks for device match data, which allows for OF and ACPI based probing. That means we do not have to manually check those first and can remove those checks. Signed-off-by: Andrew Davis Link: https://lore.kernel.org/r/20240403203633.914389-5-afd@ti.com Signed-off-by: Guenter Roeck --- drivers/hwmon/ads7828.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/ads7828.c b/drivers/hwmon/ads7828.c index 809e830f52a6..436637264056 100644 --- a/drivers/hwmon/ads7828.c +++ b/drivers/hwmon/ads7828.c @@ -99,8 +99,6 @@ static const struct regmap_config ads2830_regmap_config = { .val_bits = 8, }; -static const struct i2c_device_id ads7828_device_ids[]; - static int ads7828_probe(struct i2c_client *client) { struct device *dev = &client->dev; @@ -138,10 +136,7 @@ static int ads7828_probe(struct i2c_client *client) } } - if (client->dev.of_node) - chip = (uintptr_t)of_device_get_match_data(&client->dev); - else - chip = i2c_match_id(ads7828_device_ids, client)->driver_data; + chip = (uintptr_t)i2c_get_match_data(client); /* Bound Vref with min/max values */ vref_mv = clamp_val(vref_mv, ADS7828_EXT_VREF_MV_MIN, -- cgit From 732d2624cf9015988c152cb2758ec807907e7d1a Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 3 Apr 2024 15:36:07 -0500 Subject: hwmon: (adt7475) Remove use of i2c_match_id() The function i2c_match_id() is used to fetch the matching ID from the i2c_device_id table. This is often used to then retrieve the matching driver_data. This can be done in one step with the helper i2c_get_match_data(). This helper has a couple other benefits: * It doesn't need the i2c_device_id passed in so we do not need to have that forward declared, allowing us to remove those or move the i2c_device_id table down to its more natural spot with the other module info. * It also checks for device match data, which allows for OF and ACPI based probing. That means we do not have to manually check those first and can remove those checks. Signed-off-by: Andrew Davis Link: https://lore.kernel.org/r/20240403203633.914389-6-afd@ti.com Signed-off-by: Guenter Roeck --- drivers/hwmon/adt7475.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c index 4224ffb30483..5f78e6633018 100644 --- a/drivers/hwmon/adt7475.c +++ b/drivers/hwmon/adt7475.c @@ -1676,7 +1676,6 @@ static int adt7475_probe(struct i2c_client *client) struct device *hwmon_dev; int i, ret = 0, revision, group_num = 0; u8 config3; - const struct i2c_device_id *id = i2c_match_id(adt7475_id, client); data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); if (data == NULL) @@ -1686,10 +1685,7 @@ static int adt7475_probe(struct i2c_client *client) data->client = client; i2c_set_clientdata(client, data); - if (client->dev.of_node) - chip = (uintptr_t)of_device_get_match_data(&client->dev); - else - chip = id->driver_data; + chip = (uintptr_t)i2c_get_match_data(client); /* Initialize device-specific values */ switch (chip) { @@ -1717,7 +1713,7 @@ static int adt7475_probe(struct i2c_client *client) if (!(config3 & CONFIG3_SMBALERT)) data->has_pwm2 = 1; /* Meaning of this bit is inverted for the ADT7473-1 */ - if (id->driver_data == adt7473 && revision >= 1) + if (chip == adt7473 && revision >= 1) data->has_pwm2 = !data->has_pwm2; data->config4 = adt7475_read(REG_CONFIG4); @@ -1730,12 +1726,12 @@ static int adt7475_probe(struct i2c_client *client) * because 2 different pins (TACH4 and +2.5 Vin) can be used for * this function */ - if (id->driver_data == adt7490) { + if (chip == adt7490) { if ((data->config4 & CONFIG4_PINFUNC) == 0x1 && !(config3 & CONFIG3_THERM)) data->has_fan4 = 1; } - if (id->driver_data == adt7476 || id->driver_data == adt7490) { + if (chip == adt7476 || chip == adt7490) { if (!(config3 & CONFIG3_THERM) || (data->config4 & CONFIG4_PINFUNC) == 0x1) data->has_voltage |= (1 << 0); /* in0 */ @@ -1745,7 +1741,7 @@ static int adt7475_probe(struct i2c_client *client) * On the ADT7476, the +12V input pin may instead be used as VID5, * and VID pins may alternatively be used as GPIO */ - if (id->driver_data == adt7476) { + if (chip == adt7476) { u8 vid = adt7475_read(REG_VID); if (!(vid & VID_VIDSEL)) data->has_voltage |= (1 << 4); /* in4 */ @@ -1829,7 +1825,7 @@ static int adt7475_probe(struct i2c_client *client) } dev_info(&client->dev, "%s device, revision %d\n", - names[id->driver_data], revision); + names[chip], revision); if ((data->has_voltage & 0x11) || data->has_fan4 || data->has_pwm2) dev_info(&client->dev, "Optional features:%s%s%s%s%s\n", (data->has_voltage & (1 << 0)) ? " in0" : "", -- cgit From 941421c9eae2ead12583ec2a6af119e268746757 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 3 Apr 2024 15:36:08 -0500 Subject: hwmon: (aht10) Remove use of i2c_match_id() The function i2c_match_id() is used to fetch the matching ID from the i2c_device_id table. This is often used to then retrieve the matching driver_data. This can be done in one step with the helper i2c_get_match_data(). This helper has a couple other benefits: * It doesn't need the i2c_device_id passed in so we do not need to have that forward declared, allowing us to remove those or move the i2c_device_id table down to its more natural spot with the other module info. * It also checks for device match data, which allows for OF and ACPI based probing. That means we do not have to manually check those first and can remove those checks. Signed-off-by: Andrew Davis Link: https://lore.kernel.org/r/20240403203633.914389-7-afd@ti.com Signed-off-by: Guenter Roeck --- drivers/hwmon/aht10.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/aht10.c b/drivers/hwmon/aht10.c index f136bf3ff40a..312ef3e98754 100644 --- a/drivers/hwmon/aht10.c +++ b/drivers/hwmon/aht10.c @@ -331,8 +331,7 @@ static const struct hwmon_chip_info aht10_chip_info = { static int aht10_probe(struct i2c_client *client) { - const struct i2c_device_id *id = i2c_match_id(aht10_id, client); - enum aht10_variant variant = id->driver_data; + enum aht10_variant variant = (uintptr_t)i2c_get_match_data(client); struct device *device = &client->dev; struct device *hwmon_dev; struct aht10_data *data; -- cgit From 9072777d9388979d769afa6089926789746e83a8 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 3 Apr 2024 15:36:09 -0500 Subject: hwmon: (dme1737) Remove use of i2c_match_id() The function i2c_match_id() is used to fetch the matching ID from the i2c_device_id table. This is often used to then retrieve the matching driver_data. This can be done in one step with the helper i2c_get_match_data(). This helper has a couple other benefits: * It doesn't need the i2c_device_id passed in so we do not need to have that forward declared, allowing us to remove those or move the i2c_device_id table down to its more natural spot with the other module info. * It also checks for device match data, which allows for OF and ACPI based probing. That means we do not have to manually check those first and can remove those checks. Signed-off-by: Andrew Davis Link: https://lore.kernel.org/r/20240403203633.914389-8-afd@ti.com Signed-off-by: Guenter Roeck --- drivers/hwmon/dme1737.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/dme1737.c b/drivers/hwmon/dme1737.c index 3dcef221041d..1a9b28dc91e6 100644 --- a/drivers/hwmon/dme1737.c +++ b/drivers/hwmon/dme1737.c @@ -2461,8 +2461,6 @@ static int dme1737_i2c_detect(struct i2c_client *client, return 0; } -static const struct i2c_device_id dme1737_id[]; - static int dme1737_i2c_probe(struct i2c_client *client) { struct dme1737_data *data; @@ -2474,7 +2472,7 @@ static int dme1737_i2c_probe(struct i2c_client *client) return -ENOMEM; i2c_set_clientdata(client, data); - data->type = i2c_match_id(dme1737_id, client)->driver_data; + data->type = (uintptr_t)i2c_get_match_data(client); data->client = client; data->name = client->name; mutex_init(&data->update_lock); -- cgit From 698d692cd8854ccb6509633f0bdbea2a83c491b3 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 3 Apr 2024 15:36:10 -0500 Subject: hwmon: (ds1621) Remove use of i2c_match_id() The function i2c_match_id() is used to fetch the matching ID from the i2c_device_id table. This is often used to then retrieve the matching driver_data. This can be done in one step with the helper i2c_get_match_data(). This helper has a couple other benefits: * It doesn't need the i2c_device_id passed in so we do not need to have that forward declared, allowing us to remove those or move the i2c_device_id table down to its more natural spot with the other module info. * It also checks for device match data, which allows for OF and ACPI based probing. That means we do not have to manually check those first and can remove those checks. Signed-off-by: Andrew Davis Link: https://lore.kernel.org/r/20240403203633.914389-9-afd@ti.com Signed-off-by: Guenter Roeck --- drivers/hwmon/ds1621.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/ds1621.c b/drivers/hwmon/ds1621.c index bffbc8040171..42ec34cb8a5f 100644 --- a/drivers/hwmon/ds1621.c +++ b/drivers/hwmon/ds1621.c @@ -342,8 +342,6 @@ static const struct attribute_group ds1621_group = { }; __ATTRIBUTE_GROUPS(ds1621); -static const struct i2c_device_id ds1621_id[]; - static int ds1621_probe(struct i2c_client *client) { struct ds1621_data *data; @@ -356,7 +354,7 @@ static int ds1621_probe(struct i2c_client *client) mutex_init(&data->update_lock); - data->kind = i2c_match_id(ds1621_id, client)->driver_data; + data->kind = (uintptr_t)i2c_get_match_data(client); data->client = client; /* Initialize the DS1621 chip */ -- cgit From 72fdab6b402b47e5c6daf882283789be1380ef00 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 3 Apr 2024 15:36:11 -0500 Subject: hwmon: (f75375s) Remove use of i2c_match_id() The function i2c_match_id() is used to fetch the matching ID from the i2c_device_id table. This is often used to then retrieve the matching driver_data. This can be done in one step with the helper i2c_get_match_data(). This helper has a couple other benefits: * It doesn't need the i2c_device_id passed in so we do not need to have that forward declared, allowing us to remove those or move the i2c_device_id table down to its more natural spot with the other module info. * It also checks for device match data, which allows for OF and ACPI based probing. That means we do not have to manually check those first and can remove those checks. Signed-off-by: Andrew Davis Link: https://lore.kernel.org/r/20240403203633.914389-10-afd@ti.com Signed-off-by: Guenter Roeck --- drivers/hwmon/f75375s.c | 46 ++++++++++++++++++++-------------------------- 1 file changed, 20 insertions(+), 26 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/f75375s.c b/drivers/hwmon/f75375s.c index 8c572bb64f5d..7e867f132420 100644 --- a/drivers/hwmon/f75375s.c +++ b/drivers/hwmon/f75375s.c @@ -111,31 +111,6 @@ struct f75375_data { s8 temp_max_hyst[2]; }; -static int f75375_detect(struct i2c_client *client, - struct i2c_board_info *info); -static int f75375_probe(struct i2c_client *client); -static void f75375_remove(struct i2c_client *client); - -static const struct i2c_device_id f75375_id[] = { - { "f75373", f75373 }, - { "f75375", f75375 }, - { "f75387", f75387 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, f75375_id); - -static struct i2c_driver f75375_driver = { - .class = I2C_CLASS_HWMON, - .driver = { - .name = "f75375", - }, - .probe = f75375_probe, - .remove = f75375_remove, - .id_table = f75375_id, - .detect = f75375_detect, - .address_list = normal_i2c, -}; - static inline int f75375_read8(struct i2c_client *client, u8 reg) { return i2c_smbus_read_byte_data(client, reg); @@ -830,7 +805,7 @@ static int f75375_probe(struct i2c_client *client) i2c_set_clientdata(client, data); mutex_init(&data->update_lock); - data->kind = i2c_match_id(f75375_id, client)->driver_data; + data->kind = (uintptr_t)i2c_get_match_data(client); err = sysfs_create_group(&client->dev.kobj, &f75375_group); if (err) @@ -901,6 +876,25 @@ static int f75375_detect(struct i2c_client *client, return 0; } +static const struct i2c_device_id f75375_id[] = { + { "f75373", f75373 }, + { "f75375", f75375 }, + { "f75387", f75387 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, f75375_id); + +static struct i2c_driver f75375_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "f75375", + }, + .probe = f75375_probe, + .remove = f75375_remove, + .id_table = f75375_id, + .detect = f75375_detect, + .address_list = normal_i2c, +}; module_i2c_driver(f75375_driver); MODULE_AUTHOR("Riku Voipio"); -- cgit From 945e71d5aa32a3568c7f0d407197e26b2fc3df52 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 3 Apr 2024 15:36:12 -0500 Subject: hwmon: (fschmd) Remove use of i2c_match_id() The function i2c_match_id() is used to fetch the matching ID from the i2c_device_id table. This is often used to then retrieve the matching driver_data. This can be done in one step with the helper i2c_get_match_data(). This helper has a couple other benefits: * It doesn't need the i2c_device_id passed in so we do not need to have that forward declared, allowing us to remove those or move the i2c_device_id table down to its more natural spot with the other module info. * It also checks for device match data, which allows for OF and ACPI based probing. That means we do not have to manually check those first and can remove those checks. Signed-off-by: Andrew Davis Link: https://lore.kernel.org/r/20240403203633.914389-11-afd@ti.com Signed-off-by: Guenter Roeck --- drivers/hwmon/fschmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/fschmd.c b/drivers/hwmon/fschmd.c index b30512a705a7..1811f84d835e 100644 --- a/drivers/hwmon/fschmd.c +++ b/drivers/hwmon/fschmd.c @@ -1087,7 +1087,7 @@ static int fschmd_probe(struct i2c_client *client) "Heracles", "Heimdall", "Hades", "Syleus" }; static const int watchdog_minors[] = { WATCHDOG_MINOR, 212, 213, 214, 215 }; int i, err; - enum chips kind = i2c_match_id(fschmd_id, client)->driver_data; + enum chips kind = (uintptr_t)i2c_get_match_data(client); data = kzalloc(sizeof(struct fschmd_data), GFP_KERNEL); if (!data) -- cgit From 8b8396990303b2e723f06dff8d4cfc9d3d9ba865 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 3 Apr 2024 15:36:13 -0500 Subject: hwmon: (ina2xx) Remove use of i2c_match_id() The function i2c_match_id() is used to fetch the matching ID from the i2c_device_id table. This is often used to then retrieve the matching driver_data. This can be done in one step with the helper i2c_get_match_data(). This helper has a couple other benefits: * It doesn't need the i2c_device_id passed in so we do not need to have that forward declared, allowing us to remove those or move the i2c_device_id table down to its more natural spot with the other module info. * It also checks for device match data, which allows for OF and ACPI based probing. That means we do not have to manually check those first and can remove those checks. Signed-off-by: Andrew Davis Link: https://lore.kernel.org/r/20240403203633.914389-12-afd@ti.com Signed-off-by: Guenter Roeck --- drivers/hwmon/ina2xx.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index d8415d1f21fc..f8b5d972191e 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -612,8 +612,6 @@ static const struct attribute_group ina226_group = { .attrs = ina226_attrs, }; -static const struct i2c_device_id ina2xx_id[]; - static int ina2xx_probe(struct i2c_client *client) { struct device *dev = &client->dev; @@ -623,10 +621,7 @@ static int ina2xx_probe(struct i2c_client *client) int ret, group = 0; enum ina2xx_ids chip; - if (client->dev.of_node) - chip = (uintptr_t)of_device_get_match_data(&client->dev); - else - chip = i2c_match_id(ina2xx_id, client)->driver_data; + chip = (uintptr_t)i2c_get_match_data(client); data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) -- cgit From cbc9b40818a20f513e7d3783c38721dd69b793e5 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 3 Apr 2024 15:36:14 -0500 Subject: hwmon: (lm63) Remove use of i2c_match_id() The function i2c_match_id() is used to fetch the matching ID from the i2c_device_id table. This is often used to then retrieve the matching driver_data. This can be done in one step with the helper i2c_get_match_data(). This helper has a couple other benefits: * It doesn't need the i2c_device_id passed in so we do not need to have that forward declared, allowing us to remove those or move the i2c_device_id table down to its more natural spot with the other module info. * It also checks for device match data, which allows for OF and ACPI based probing. That means we do not have to manually check those first and can remove those checks. Signed-off-by: Andrew Davis Link: https://lore.kernel.org/r/20240403203633.914389-13-afd@ti.com Signed-off-by: Guenter Roeck --- drivers/hwmon/lm63.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/lm63.c b/drivers/hwmon/lm63.c index 0878a044dd8e..035176a98ce9 100644 --- a/drivers/hwmon/lm63.c +++ b/drivers/hwmon/lm63.c @@ -1104,10 +1104,7 @@ static int lm63_probe(struct i2c_client *client) mutex_init(&data->update_lock); /* Set the device type */ - if (client->dev.of_node) - data->kind = (uintptr_t)of_device_get_match_data(&client->dev); - else - data->kind = i2c_match_id(lm63_id, client)->driver_data; + data->kind = (uintptr_t)i2c_get_match_data(client); if (data->kind == lm64) data->temp2_offset = 16000; -- cgit From 5178911d5c13308a0c2e80e0055c04f36a515bbf Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 3 Apr 2024 15:36:15 -0500 Subject: hwmon: (lm75) Remove use of i2c_match_id() The function i2c_match_id() is used to fetch the matching ID from the i2c_device_id table. This is often used to then retrieve the matching driver_data. This can be done in one step with the helper i2c_get_match_data(). This helper has a couple other benefits: * It doesn't need the i2c_device_id passed in so we do not need to have that forward declared, allowing us to remove those or move the i2c_device_id table down to its more natural spot with the other module info. * It also checks for device match data, which allows for OF and ACPI based probing. That means we do not have to manually check those first and can remove those checks. Signed-off-by: Andrew Davis Link: https://lore.kernel.org/r/20240403203633.914389-14-afd@ti.com Signed-off-by: Guenter Roeck --- drivers/hwmon/lm75.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c index e00750718536..2c2205aec7d4 100644 --- a/drivers/hwmon/lm75.c +++ b/drivers/hwmon/lm75.c @@ -625,20 +625,12 @@ static void lm75_remove(void *data) i2c_smbus_write_byte_data(client, LM75_REG_CONF, lm75->orig_conf); } -static const struct i2c_device_id lm75_ids[]; - static int lm75_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct device *hwmon_dev; struct lm75_data *data; int status, err; - enum lm75_type kind; - - if (client->dev.of_node) - kind = (uintptr_t)of_device_get_match_data(&client->dev); - else - kind = i2c_match_id(lm75_ids, client)->driver_data; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) @@ -649,7 +641,7 @@ static int lm75_probe(struct i2c_client *client) return -ENOMEM; data->client = client; - data->kind = kind; + data->kind = (uintptr_t)i2c_get_match_data(client); data->vs = devm_regulator_get(dev, "vs"); if (IS_ERR(data->vs)) -- cgit From e49d1a1d330f341e045250dececbe7745638ca19 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 3 Apr 2024 15:36:16 -0500 Subject: hwmon: (lm78) Remove use of i2c_match_id() The function i2c_match_id() is used to fetch the matching ID from the i2c_device_id table. This is often used to then retrieve the matching driver_data. This can be done in one step with the helper i2c_get_match_data(). This helper has a couple other benefits: * It doesn't need the i2c_device_id passed in so we do not need to have that forward declared, allowing us to remove those or move the i2c_device_id table down to its more natural spot with the other module info. * It also checks for device match data, which allows for OF and ACPI based probing. That means we do not have to manually check those first and can remove those checks. Signed-off-by: Andrew Davis Link: https://lore.kernel.org/r/20240403203633.914389-15-afd@ti.com Signed-off-by: Guenter Roeck --- drivers/hwmon/lm78.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c index b739c354311b..8b53bb312069 100644 --- a/drivers/hwmon/lm78.c +++ b/drivers/hwmon/lm78.c @@ -627,8 +627,6 @@ static int lm78_i2c_detect(struct i2c_client *client, return -ENODEV; } -static const struct i2c_device_id lm78_i2c_id[]; - static int lm78_i2c_probe(struct i2c_client *client) { struct device *dev = &client->dev; @@ -640,7 +638,7 @@ static int lm78_i2c_probe(struct i2c_client *client) return -ENOMEM; data->client = client; - data->type = i2c_match_id(lm78_i2c_id, client)->driver_data; + data->type = (uintptr_t)i2c_get_match_data(client); /* Initialize the LM78 chip */ lm78_init_device(data); -- cgit From 9339bed921d001d954f08bbe29be63d29653de69 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 3 Apr 2024 15:36:17 -0500 Subject: hwmon: (lm83) Remove use of i2c_match_id() The function i2c_match_id() is used to fetch the matching ID from the i2c_device_id table. This is often used to then retrieve the matching driver_data. This can be done in one step with the helper i2c_get_match_data(). This helper has a couple other benefits: * It doesn't need the i2c_device_id passed in so we do not need to have that forward declared, allowing us to remove those or move the i2c_device_id table down to its more natural spot with the other module info. * It also checks for device match data, which allows for OF and ACPI based probing. That means we do not have to manually check those first and can remove those checks. Signed-off-by: Andrew Davis Link: https://lore.kernel.org/r/20240403203633.914389-16-afd@ti.com Signed-off-by: Guenter Roeck --- drivers/hwmon/lm83.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/lm83.c b/drivers/hwmon/lm83.c index b333c9bde4e6..f800fe2ef18b 100644 --- a/drivers/hwmon/lm83.c +++ b/drivers/hwmon/lm83.c @@ -417,13 +417,6 @@ static int lm83_detect(struct i2c_client *client, return 0; } -static const struct i2c_device_id lm83_id[] = { - { "lm83", lm83 }, - { "lm82", lm82 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, lm83_id); - static int lm83_probe(struct i2c_client *client) { struct device *dev = &client->dev; @@ -438,7 +431,7 @@ static int lm83_probe(struct i2c_client *client) if (IS_ERR(data->regmap)) return PTR_ERR(data->regmap); - data->type = i2c_match_id(lm83_id, client)->driver_data; + data->type = (uintptr_t)i2c_get_match_data(client); hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, &lm83_chip_info, NULL); @@ -449,6 +442,13 @@ static int lm83_probe(struct i2c_client *client) * Driver data (common to all clients) */ +static const struct i2c_device_id lm83_id[] = { + { "lm83", lm83 }, + { "lm82", lm82 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, lm83_id); + static struct i2c_driver lm83_driver = { .class = I2C_CLASS_HWMON, .driver = { -- cgit From 4cfd3ceeaaf506d367ffe2c80c2ffc39c4f0a4f9 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 3 Apr 2024 15:36:18 -0500 Subject: hwmon: (lm85) Remove use of i2c_match_id() The function i2c_match_id() is used to fetch the matching ID from the i2c_device_id table. This is often used to then retrieve the matching driver_data. This can be done in one step with the helper i2c_get_match_data(). This helper has a couple other benefits: * It doesn't need the i2c_device_id passed in so we do not need to have that forward declared, allowing us to remove those or move the i2c_device_id table down to its more natural spot with the other module info. * It also checks for device match data, which allows for OF and ACPI based probing. That means we do not have to manually check those first and can remove those checks. Signed-off-by: Andrew Davis Link: https://lore.kernel.org/r/20240403203633.914389-17-afd@ti.com Signed-off-by: Guenter Roeck --- drivers/hwmon/lm85.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c index 68c210002357..1c244ed75122 100644 --- a/drivers/hwmon/lm85.c +++ b/drivers/hwmon/lm85.c @@ -1544,8 +1544,6 @@ static int lm85_detect(struct i2c_client *client, struct i2c_board_info *info) return 0; } -static const struct i2c_device_id lm85_id[]; - static int lm85_probe(struct i2c_client *client) { struct device *dev = &client->dev; @@ -1558,10 +1556,7 @@ static int lm85_probe(struct i2c_client *client) return -ENOMEM; data->client = client; - if (client->dev.of_node) - data->type = (uintptr_t)of_device_get_match_data(&client->dev); - else - data->type = i2c_match_id(lm85_id, client)->driver_data; + data->type = (uintptr_t)i2c_get_match_data(client); mutex_init(&data->update_lock); /* Fill in the chip specific driver values */ -- cgit From 711e2e4f690dbd46d8cdc6be3b1491e95dfc4426 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 3 Apr 2024 15:36:19 -0500 Subject: hwmon: (lm90) Remove use of i2c_match_id() The function i2c_match_id() is used to fetch the matching ID from the i2c_device_id table. This is often used to then retrieve the matching driver_data. This can be done in one step with the helper i2c_get_match_data(). This helper has a couple other benefits: * It doesn't need the i2c_device_id passed in so we do not need to have that forward declared, allowing us to remove those or move the i2c_device_id table down to its more natural spot with the other module info. * It also checks for device match data, which allows for OF and ACPI based probing. That means we do not have to manually check those first and can remove those checks. Signed-off-by: Andrew Davis Link: https://lore.kernel.org/r/20240403203633.914389-18-afd@ti.com Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 40d9e21b528c..ca5c52b38c0f 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -2723,10 +2723,7 @@ static int lm90_probe(struct i2c_client *client) INIT_WORK(&data->report_work, lm90_report_alarms); /* Set the device type */ - if (client->dev.of_node) - data->kind = (uintptr_t)of_device_get_match_data(&client->dev); - else - data->kind = i2c_match_id(lm90_id, client)->driver_data; + data->kind = (uintptr_t)i2c_get_match_data(client); /* * Different devices have different alarm bits triggering the -- cgit From e2791bdfa67c2edf380905faffab71ac91ac6ca2 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 3 Apr 2024 15:36:20 -0500 Subject: hwmon: (lm95234) Remove use of i2c_match_id() The function i2c_match_id() is used to fetch the matching ID from the i2c_device_id table. This is often used to then retrieve the matching driver_data. This can be done in one step with the helper i2c_get_match_data(). This helper has a couple other benefits: * It doesn't need the i2c_device_id passed in so we do not need to have that forward declared, allowing us to remove those or move the i2c_device_id table down to its more natural spot with the other module info. * It also checks for device match data, which allows for OF and ACPI based probing. That means we do not have to manually check those first and can remove those checks. Signed-off-by: Andrew Davis Link: https://lore.kernel.org/r/20240403203633.914389-19-afd@ti.com Signed-off-by: Guenter Roeck --- drivers/hwmon/lm95234.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/lm95234.c b/drivers/hwmon/lm95234.c index 67b9d7636ee4..be00a374632d 100644 --- a/drivers/hwmon/lm95234.c +++ b/drivers/hwmon/lm95234.c @@ -677,10 +677,9 @@ static int lm95234_init_client(struct i2c_client *client) return 0; } -static const struct i2c_device_id lm95234_id[]; - static int lm95234_probe(struct i2c_client *client) { + enum chips type = (uintptr_t)i2c_get_match_data(client); struct device *dev = &client->dev; struct lm95234_data *data; struct device *hwmon_dev; @@ -699,7 +698,7 @@ static int lm95234_probe(struct i2c_client *client) return err; data->groups[0] = &lm95234_common_group; - if (i2c_match_id(lm95234_id, client)->driver_data == lm95234) + if (type == lm95234) data->groups[1] = &lm95234_group; hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, -- cgit From 5a71654b398e3471f0169c266a3587cf09e1200c Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 3 Apr 2024 15:36:21 -0500 Subject: hwmon: (max16065) Remove use of i2c_match_id() The function i2c_match_id() is used to fetch the matching ID from the i2c_device_id table. This is often used to then retrieve the matching driver_data. This can be done in one step with the helper i2c_get_match_data(). This helper has a couple other benefits: * It doesn't need the i2c_device_id passed in so we do not need to have that forward declared, allowing us to remove those or move the i2c_device_id table down to its more natural spot with the other module info. * It also checks for device match data, which allows for OF and ACPI based probing. That means we do not have to manually check those first and can remove those checks. Signed-off-by: Andrew Davis Link: https://lore.kernel.org/r/20240403203633.914389-20-afd@ti.com Signed-off-by: Guenter Roeck --- drivers/hwmon/max16065.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/max16065.c b/drivers/hwmon/max16065.c index aa38c45adc09..7ce9a89f93a0 100644 --- a/drivers/hwmon/max16065.c +++ b/drivers/hwmon/max16065.c @@ -493,8 +493,6 @@ static const struct attribute_group max16065_max_group = { .is_visible = max16065_secondary_is_visible, }; -static const struct i2c_device_id max16065_id[]; - static int max16065_probe(struct i2c_client *client) { struct i2c_adapter *adapter = client->adapter; @@ -505,7 +503,7 @@ static int max16065_probe(struct i2c_client *client) bool have_secondary; /* true if chip has secondary limits */ bool secondary_is_max = false; /* secondary limits reflect max */ int groups = 0; - const struct i2c_device_id *id = i2c_match_id(max16065_id, client); + enum chips chip = (uintptr_t)i2c_get_match_data(client); if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_READ_WORD_DATA)) @@ -518,9 +516,9 @@ static int max16065_probe(struct i2c_client *client) data->client = client; mutex_init(&data->update_lock); - data->num_adc = max16065_num_adc[id->driver_data]; - data->have_current = max16065_have_current[id->driver_data]; - have_secondary = max16065_have_secondary[id->driver_data]; + data->num_adc = max16065_num_adc[chip]; + data->have_current = max16065_have_current[chip]; + have_secondary = max16065_have_secondary[chip]; if (have_secondary) { val = i2c_smbus_read_byte_data(client, MAX16065_SW_ENABLE); -- cgit From 495e6a9cf5509ff6c9e42de9eaf78e35c766aed6 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 3 Apr 2024 15:36:22 -0500 Subject: hwmon: (max1668) Remove use of i2c_match_id() The function i2c_match_id() is used to fetch the matching ID from the i2c_device_id table. This is often used to then retrieve the matching driver_data. This can be done in one step with the helper i2c_get_match_data(). This helper has a couple other benefits: * It doesn't need the i2c_device_id passed in so we do not need to have that forward declared, allowing us to remove those or move the i2c_device_id table down to its more natural spot with the other module info. * It also checks for device match data, which allows for OF and ACPI based probing. That means we do not have to manually check those first and can remove those checks. Signed-off-by: Andrew Davis Link: https://lore.kernel.org/r/20240403203633.914389-21-afd@ti.com Signed-off-by: Guenter Roeck --- drivers/hwmon/max1668.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/max1668.c b/drivers/hwmon/max1668.c index c4a02edefbee..9fc583ebb11b 100644 --- a/drivers/hwmon/max1668.c +++ b/drivers/hwmon/max1668.c @@ -391,8 +391,6 @@ static int max1668_detect(struct i2c_client *client, return 0; } -static const struct i2c_device_id max1668_id[]; - static int max1668_probe(struct i2c_client *client) { struct i2c_adapter *adapter = client->adapter; @@ -408,7 +406,7 @@ static int max1668_probe(struct i2c_client *client) return -ENOMEM; data->client = client; - data->type = i2c_match_id(max1668_id, client)->driver_data; + data->type = (uintptr_t)i2c_get_match_data(client); mutex_init(&data->update_lock); /* sysfs hooks */ -- cgit From de5fb06ca77f0e4347f21d4e6dd583f00824619c Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 3 Apr 2024 15:36:23 -0500 Subject: hwmon: (max6697) Remove use of i2c_match_id() The function i2c_match_id() is used to fetch the matching ID from the i2c_device_id table. This is often used to then retrieve the matching driver_data. This can be done in one step with the helper i2c_get_match_data(). This helper has a couple other benefits: * It doesn't need the i2c_device_id passed in so we do not need to have that forward declared, allowing us to remove those or move the i2c_device_id table down to its more natural spot with the other module info. * It also checks for device match data, which allows for OF and ACPI based probing. That means we do not have to manually check those first and can remove those checks. Signed-off-by: Andrew Davis Link: https://lore.kernel.org/r/20240403203633.914389-22-afd@ti.com Signed-off-by: Guenter Roeck --- drivers/hwmon/max6697.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/max6697.c b/drivers/hwmon/max6697.c index d161ba0e7813..b28b7b9448aa 100644 --- a/drivers/hwmon/max6697.c +++ b/drivers/hwmon/max6697.c @@ -684,8 +684,6 @@ done: return 0; } -static const struct i2c_device_id max6697_id[]; - static int max6697_probe(struct i2c_client *client) { struct i2c_adapter *adapter = client->adapter; @@ -701,10 +699,7 @@ static int max6697_probe(struct i2c_client *client) if (!data) return -ENOMEM; - if (client->dev.of_node) - data->type = (uintptr_t)of_device_get_match_data(&client->dev); - else - data->type = i2c_match_id(max6697_id, client)->driver_data; + data->type = (uintptr_t)i2c_get_match_data(client); data->chip = &max6697_chip_data[data->type]; data->client = client; mutex_init(&data->update_lock); -- cgit From f1230f756a71c4bf4debf2972bc53922aad2784f Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 3 Apr 2024 15:36:24 -0500 Subject: hwmon: (mcp3021) Remove use of i2c_match_id() The function i2c_match_id() is used to fetch the matching ID from the i2c_device_id table. This is often used to then retrieve the matching driver_data. This can be done in one step with the helper i2c_get_match_data(). This helper has a couple other benefits: * It doesn't need the i2c_device_id passed in so we do not need to have that forward declared, allowing us to remove those or move the i2c_device_id table down to its more natural spot with the other module info. * It also checks for device match data, which allows for OF and ACPI based probing. That means we do not have to manually check those first and can remove those checks. Signed-off-by: Andrew Davis Link: https://lore.kernel.org/r/20240403203633.914389-23-afd@ti.com Signed-off-by: Guenter Roeck --- drivers/hwmon/mcp3021.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/mcp3021.c b/drivers/hwmon/mcp3021.c index 9814eaf24564..bcddf6804d3a 100644 --- a/drivers/hwmon/mcp3021.c +++ b/drivers/hwmon/mcp3021.c @@ -116,13 +116,12 @@ static const struct hwmon_chip_info mcp3021_chip_info = { .info = mcp3021_info, }; -static const struct i2c_device_id mcp3021_id[]; - static int mcp3021_probe(struct i2c_client *client) { struct mcp3021_data *data = NULL; struct device_node *np = client->dev.of_node; struct device *hwmon_dev; + enum chips type; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) return -ENODEV; @@ -149,7 +148,8 @@ static int mcp3021_probe(struct i2c_client *client) data->vdd = MCP3021_VDD_REF_DEFAULT; } - switch (i2c_match_id(mcp3021_id, client)->driver_data) { + type = (uintptr_t)i2c_get_match_data(client); + switch (type) { case mcp3021: data->sar_shift = MCP3021_SAR_SHIFT; data->sar_mask = MCP3021_SAR_MASK; -- cgit From 77944b479dd5421e4afd73cee4d7567f48f44b04 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 3 Apr 2024 15:36:25 -0500 Subject: hwmon: (powr1220) Remove use of i2c_match_id() The function i2c_match_id() is used to fetch the matching ID from the i2c_device_id table. This is often used to then retrieve the matching driver_data. This can be done in one step with the helper i2c_get_match_data(). This helper has a couple other benefits: * It doesn't need the i2c_device_id passed in so we do not need to have that forward declared, allowing us to remove those or move the i2c_device_id table down to its more natural spot with the other module info. * It also checks for device match data, which allows for OF and ACPI based probing. That means we do not have to manually check those first and can remove those checks. Signed-off-by: Andrew Davis Link: https://lore.kernel.org/r/20240403203633.914389-24-afd@ti.com Signed-off-by: Guenter Roeck --- drivers/hwmon/powr1220.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/powr1220.c b/drivers/hwmon/powr1220.c index 2388d0565e7e..5f9ca6543530 100644 --- a/drivers/hwmon/powr1220.c +++ b/drivers/hwmon/powr1220.c @@ -279,12 +279,11 @@ static const struct hwmon_chip_info powr1220_chip_info = { .info = powr1220_info, }; -static const struct i2c_device_id powr1220_ids[]; - static int powr1220_probe(struct i2c_client *client) { struct powr1220_data *data; struct device *hwmon_dev; + enum powr1xxx_chips chip; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; @@ -293,7 +292,8 @@ static int powr1220_probe(struct i2c_client *client) if (!data) return -ENOMEM; - switch (i2c_match_id(powr1220_ids, client)->driver_data) { + chip = (uintptr_t)i2c_get_match_data(client); + switch (chip) { case powr1014: data->max_channels = 10; break; -- cgit From f147dbd7ea582a32966e5b5ddf2127172ff146d7 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 3 Apr 2024 15:36:26 -0500 Subject: hwmon: (sht3x) Remove use of i2c_match_id() The function i2c_match_id() is used to fetch the matching ID from the i2c_device_id table. This is often used to then retrieve the matching driver_data. This can be done in one step with the helper i2c_get_match_data(). This helper has a couple other benefits: * It doesn't need the i2c_device_id passed in so we do not need to have that forward declared, allowing us to remove those or move the i2c_device_id table down to its more natural spot with the other module info. * It also checks for device match data, which allows for OF and ACPI based probing. That means we do not have to manually check those first and can remove those checks. Signed-off-by: Andrew Davis Link: https://lore.kernel.org/r/20240403203633.914389-25-afd@ti.com Signed-off-by: Guenter Roeck --- drivers/hwmon/sht3x.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/sht3x.c b/drivers/hwmon/sht3x.c index c0d02fbcdb76..650b0bcc2359 100644 --- a/drivers/hwmon/sht3x.c +++ b/drivers/hwmon/sht3x.c @@ -882,15 +882,6 @@ static const struct hwmon_chip_info sht3x_chip_info = { .info = sht3x_channel_info, }; -/* device ID table */ -static const struct i2c_device_id sht3x_ids[] = { - {"sht3x", sht3x}, - {"sts3x", sts3x}, - {} -}; - -MODULE_DEVICE_TABLE(i2c, sht3x_ids); - static int sht3x_probe(struct i2c_client *client) { int ret; @@ -920,7 +911,7 @@ static int sht3x_probe(struct i2c_client *client) data->mode = 0; data->last_update = jiffies - msecs_to_jiffies(3000); data->client = client; - data->chip_id = i2c_match_id(sht3x_ids, client)->driver_data; + data->chip_id = (uintptr_t)i2c_get_match_data(client); crc8_populate_msb(sht3x_crc8_table, SHT3X_CRC8_POLYNOMIAL); sht3x_select_command(data); @@ -963,6 +954,15 @@ static int sht3x_probe(struct i2c_client *client) return PTR_ERR_OR_ZERO(hwmon_dev); } +/* device ID table */ +static const struct i2c_device_id sht3x_ids[] = { + {"sht3x", sht3x}, + {"sts3x", sts3x}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, sht3x_ids); + static struct i2c_driver sht3x_i2c_driver = { .driver.name = "sht3x", .probe = sht3x_probe, -- cgit From 64306a47193f1d7956f03a4f2cd068fed9703475 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 3 Apr 2024 15:36:27 -0500 Subject: hwmon: (shtc1) Remove use of i2c_match_id() The function i2c_match_id() is used to fetch the matching ID from the i2c_device_id table. This is often used to then retrieve the matching driver_data. This can be done in one step with the helper i2c_get_match_data(). This helper has a couple other benefits: * It doesn't need the i2c_device_id passed in so we do not need to have that forward declared, allowing us to remove those or move the i2c_device_id table down to its more natural spot with the other module info. * It also checks for device match data, which allows for OF and ACPI based probing. That means we do not have to manually check those first and can remove those checks. Signed-off-by: Andrew Davis Link: https://lore.kernel.org/r/20240403203633.914389-26-afd@ti.com Signed-off-by: Guenter Roeck --- drivers/hwmon/shtc1.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/shtc1.c b/drivers/hwmon/shtc1.c index 1f96e94967ee..24a8ae092d00 100644 --- a/drivers/hwmon/shtc1.c +++ b/drivers/hwmon/shtc1.c @@ -186,8 +186,6 @@ static void shtc1_select_command(struct shtc1_data *data) } } -static const struct i2c_device_id shtc1_id[]; - static int shtc1_probe(struct i2c_client *client) { int ret; @@ -195,7 +193,7 @@ static int shtc1_probe(struct i2c_client *client) char id_reg_buf[2]; struct shtc1_data *data; struct device *hwmon_dev; - enum shtcx_chips chip = i2c_match_id(shtc1_id, client)->driver_data; + enum shtcx_chips chip = (uintptr_t)i2c_get_match_data(client); struct i2c_adapter *adap = client->adapter; struct device *dev = &client->dev; struct device_node *np = dev->of_node; -- cgit From 50ba2d3d48900f30c090467fc11a99a40dbbad11 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 3 Apr 2024 15:36:28 -0500 Subject: hwmon: (thmc50) Remove use of i2c_match_id() The function i2c_match_id() is used to fetch the matching ID from the i2c_device_id table. This is often used to then retrieve the matching driver_data. This can be done in one step with the helper i2c_get_match_data(). This helper has a couple other benefits: * It doesn't need the i2c_device_id passed in so we do not need to have that forward declared, allowing us to remove those or move the i2c_device_id table down to its more natural spot with the other module info. * It also checks for device match data, which allows for OF and ACPI based probing. That means we do not have to manually check those first and can remove those checks. Signed-off-by: Andrew Davis Link: https://lore.kernel.org/r/20240403203633.914389-27-afd@ti.com Signed-off-by: Guenter Roeck --- drivers/hwmon/thmc50.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/thmc50.c b/drivers/hwmon/thmc50.c index 68ba26bc9014..0cbdb91698b1 100644 --- a/drivers/hwmon/thmc50.c +++ b/drivers/hwmon/thmc50.c @@ -377,8 +377,6 @@ static void thmc50_init_client(struct thmc50_data *data) i2c_smbus_write_byte_data(client, THMC50_REG_CONF, config); } -static const struct i2c_device_id thmc50_id[]; - static int thmc50_probe(struct i2c_client *client) { struct device *dev = &client->dev; @@ -391,7 +389,7 @@ static int thmc50_probe(struct i2c_client *client) return -ENOMEM; data->client = client; - data->type = i2c_match_id(thmc50_id, client)->driver_data; + data->type = (uintptr_t)i2c_get_match_data(client); mutex_init(&data->update_lock); thmc50_init_client(data); -- cgit From 516d5765a359987949679a1a6b5e4a8620a1bfef Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 3 Apr 2024 15:36:29 -0500 Subject: hwmon: (tmp401) Remove use of i2c_match_id() The function i2c_match_id() is used to fetch the matching ID from the i2c_device_id table. This is often used to then retrieve the matching driver_data. This can be done in one step with the helper i2c_get_match_data(). This helper has a couple other benefits: * It doesn't need the i2c_device_id passed in so we do not need to have that forward declared, allowing us to remove those or move the i2c_device_id table down to its more natural spot with the other module info. * It also checks for device match data, which allows for OF and ACPI based probing. That means we do not have to manually check those first and can remove those checks. Signed-off-by: Andrew Davis Link: https://lore.kernel.org/r/20240403203633.914389-28-afd@ti.com Signed-off-by: Guenter Roeck --- drivers/hwmon/tmp401.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/tmp401.c b/drivers/hwmon/tmp401.c index df1b45a62e80..853dbe708ff5 100644 --- a/drivers/hwmon/tmp401.c +++ b/drivers/hwmon/tmp401.c @@ -693,7 +693,7 @@ static int tmp401_probe(struct i2c_client *client) data->client = client; mutex_init(&data->update_lock); - data->kind = i2c_match_id(tmp401_id, client)->driver_data; + data->kind = (uintptr_t)i2c_get_match_data(client); data->regmap = devm_regmap_init(dev, NULL, data, &tmp401_regmap_config); if (IS_ERR(data->regmap)) -- cgit From 8b6e514c59b063b93645424d902263ea936a8a40 Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 3 Apr 2024 15:36:30 -0500 Subject: hwmon: (tmp421) Remove use of i2c_match_id() The function i2c_match_id() is used to fetch the matching ID from the i2c_device_id table. This is often used to then retrieve the matching driver_data. This can be done in one step with the helper i2c_get_match_data(). This helper has a couple other benefits: * It doesn't need the i2c_device_id passed in so we do not need to have that forward declared, allowing us to remove those or move the i2c_device_id table down to its more natural spot with the other module info. * It also checks for device match data, which allows for OF and ACPI based probing. That means we do not have to manually check those first and can remove those checks. Signed-off-by: Andrew Davis Link: https://lore.kernel.org/r/20240403203633.914389-29-afd@ti.com Signed-off-by: Guenter Roeck --- drivers/hwmon/tmp421.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/tmp421.c b/drivers/hwmon/tmp421.c index 10b66c9ce045..7a6f9532e594 100644 --- a/drivers/hwmon/tmp421.c +++ b/drivers/hwmon/tmp421.c @@ -446,11 +446,7 @@ static int tmp421_probe(struct i2c_client *client) return -ENOMEM; mutex_init(&data->update_lock); - if (client->dev.of_node) - data->channels = (unsigned long) - of_device_get_match_data(&client->dev); - else - data->channels = i2c_match_id(tmp421_id, client)->driver_data; + data->channels = (unsigned long)i2c_get_match_data(client); data->client = client; for (i = 0; i < data->channels; i++) { -- cgit From 38a085f9f4ab2dd6ddd680fb8ff649711faf871e Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 3 Apr 2024 15:36:31 -0500 Subject: hwmon: (tmp464) Remove use of i2c_match_id() The function i2c_match_id() is used to fetch the matching ID from the i2c_device_id table. This is often used to then retrieve the matching driver_data. This can be done in one step with the helper i2c_get_match_data(). This helper has a couple other benefits: * It doesn't need the i2c_device_id passed in so we do not need to have that forward declared, allowing us to remove those or move the i2c_device_id table down to its more natural spot with the other module info. * It also checks for device match data, which allows for OF and ACPI based probing. That means we do not have to manually check those first and can remove those checks. Signed-off-by: Andrew Davis Link: https://lore.kernel.org/r/20240403203633.914389-30-afd@ti.com Signed-off-by: Guenter Roeck --- drivers/hwmon/tmp464.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/tmp464.c b/drivers/hwmon/tmp464.c index f58ca4c6acb6..3ee1137533d6 100644 --- a/drivers/hwmon/tmp464.c +++ b/drivers/hwmon/tmp464.c @@ -666,10 +666,7 @@ static int tmp464_probe(struct i2c_client *client) mutex_init(&data->update_lock); - if (dev->of_node) - data->channels = (int)(unsigned long)of_device_get_match_data(&client->dev); - else - data->channels = i2c_match_id(tmp464_id, client)->driver_data; + data->channels = (int)(unsigned long)i2c_get_match_data(client); data->regmap = devm_regmap_init_i2c(client, &tmp464_regmap_config); if (IS_ERR(data->regmap)) -- cgit From 00dcf379ffeb333510a532bbe7157df0162600bc Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 3 Apr 2024 15:36:32 -0500 Subject: hwmon: (w83781d) Remove use of i2c_match_id() The function i2c_match_id() is used to fetch the matching ID from the i2c_device_id table. This is often used to then retrieve the matching driver_data. This can be done in one step with the helper i2c_get_match_data(). This helper has a couple other benefits: * It doesn't need the i2c_device_id passed in so we do not need to have that forward declared, allowing us to remove those or move the i2c_device_id table down to its more natural spot with the other module info. * It also checks for device match data, which allows for OF and ACPI based probing. That means we do not have to manually check those first and can remove those checks. Signed-off-by: Andrew Davis Link: https://lore.kernel.org/r/20240403203633.914389-31-afd@ti.com Signed-off-by: Guenter Roeck --- drivers/hwmon/w83781d.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c index cba5ec432e6d..b7957c84d235 100644 --- a/drivers/hwmon/w83781d.c +++ b/drivers/hwmon/w83781d.c @@ -1192,8 +1192,6 @@ static void w83781d_remove_files(struct device *dev) sysfs_remove_group(&dev->kobj, &w83781d_group_other); } -static const struct i2c_device_id w83781d_ids[]; - static int w83781d_probe(struct i2c_client *client) { struct device *dev = &client->dev; @@ -1208,7 +1206,7 @@ static int w83781d_probe(struct i2c_client *client) mutex_init(&data->lock); mutex_init(&data->update_lock); - data->type = i2c_match_id(w83781d_ids, client)->driver_data; + data->type = (uintptr_t)i2c_get_match_data(client); data->client = client; /* attach secondary i2c lm75-like clients */ -- cgit From 55076454de05b2f5ce8f5ef459685e707a03703f Mon Sep 17 00:00:00 2001 From: Andrew Davis Date: Wed, 3 Apr 2024 15:36:33 -0500 Subject: hwmon: (w83795): Remove use of i2c_match_id() The function i2c_match_id() is used to fetch the matching ID from the i2c_device_id table. This is often used to then retrieve the matching driver_data. This can be done in one step with the helper i2c_get_match_data(). This helper has a couple other benefits: * It doesn't need the i2c_device_id passed in so we do not need to have that forward declared, allowing us to remove those or move the i2c_device_id table down to its more natural spot with the other module info. * It also checks for device match data, which allows for OF and ACPI based probing. That means we do not have to manually check those first and can remove those checks. Signed-off-by: Andrew Davis Link: https://lore.kernel.org/r/20240403203633.914389-32-afd@ti.com Signed-off-by: Guenter Roeck --- drivers/hwmon/w83795.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/w83795.c b/drivers/hwmon/w83795.c index c446e00db658..5174db69db5e 100644 --- a/drivers/hwmon/w83795.c +++ b/drivers/hwmon/w83795.c @@ -2134,8 +2134,6 @@ static void w83795_apply_temp_config(struct w83795_data *data, u8 config, } } -static const struct i2c_device_id w83795_id[]; - static int w83795_probe(struct i2c_client *client) { int i; @@ -2149,7 +2147,7 @@ static int w83795_probe(struct i2c_client *client) return -ENOMEM; i2c_set_clientdata(client, data); - data->chip_type = i2c_match_id(w83795_id, client)->driver_data; + data->chip_type = (uintptr_t)i2c_get_match_data(client); data->bank = i2c_smbus_read_byte_data(client, W83795_REG_BANKSEL); mutex_init(&data->update_lock); -- cgit From 234c07400eb4f5d475e4055b11b5ed69f91d3732 Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Thu, 4 Apr 2024 13:13:27 +0200 Subject: hwmon: (ltc2991) use device_for_each_child_node_scoped() Switch to the _scoped() version introduced in commit 365130fd47af ("device property: Introduce device_for_each_child_node_scoped()") to remove the need for manual calling of fwnode_handle_put() in the paths where the code exits the loop early. Signed-off-by: Javier Carrasco Link: https://lore.kernel.org/r/20240404-hwmon_device_for_each_child_node_scoped-v1-1-53997abde43c@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/ltc2991.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/ltc2991.c b/drivers/hwmon/ltc2991.c index 06750bb93c23..a01f887392f9 100644 --- a/drivers/hwmon/ltc2991.c +++ b/drivers/hwmon/ltc2991.c @@ -284,7 +284,6 @@ static const struct regmap_config ltc2991_regmap_config = { static int ltc2991_init(struct ltc2991_state *st, struct device *dev) { - struct fwnode_handle *child; int ret; u32 val, addr; u8 v5_v8_reg_data = 0, v1_v4_reg_data = 0; @@ -294,17 +293,13 @@ static int ltc2991_init(struct ltc2991_state *st, struct device *dev) return dev_err_probe(dev, ret, "failed to enable regulator\n"); - device_for_each_child_node(dev, child) { + device_for_each_child_node_scoped(dev, child) { ret = fwnode_property_read_u32(child, "reg", &addr); - if (ret < 0) { - fwnode_handle_put(child); + if (ret < 0) return ret; - } - if (addr > 3) { - fwnode_handle_put(child); + if (addr > 3) return -EINVAL; - } ret = fwnode_property_read_u32(child, "shunt-resistor-micro-ohms", -- cgit From 1ee123794106db34e0248890a8e3388db404108b Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Thu, 4 Apr 2024 13:13:28 +0200 Subject: hwmon: (gsc-hwmon) use device_for_each_child_node_scoped() Switch to the _scoped() version introduced in commit 365130fd47af ("device property: Introduce device_for_each_child_node_scoped()") to remove the need for manual calling of fwnode_handle_put() in the paths where the code exits the loop early. Signed-off-by: Javier Carrasco Link: https://lore.kernel.org/r/20240404-hwmon_device_for_each_child_node_scoped-v1-2-53997abde43c@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/gsc-hwmon.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/gsc-hwmon.c b/drivers/hwmon/gsc-hwmon.c index 1501ceb551e7..0f2147699c91 100644 --- a/drivers/hwmon/gsc-hwmon.c +++ b/drivers/hwmon/gsc-hwmon.c @@ -249,7 +249,6 @@ gsc_hwmon_get_devtree_pdata(struct device *dev) { struct gsc_hwmon_platform_data *pdata; struct gsc_hwmon_channel *ch; - struct fwnode_handle *child; struct device_node *fan; int nchannels; @@ -276,25 +275,21 @@ gsc_hwmon_get_devtree_pdata(struct device *dev) ch = pdata->channels; /* allocate structures for channels and count instances of each type */ - device_for_each_child_node(dev, child) { + device_for_each_child_node_scoped(dev, child) { if (fwnode_property_read_string(child, "label", &ch->name)) { dev_err(dev, "channel without label\n"); - fwnode_handle_put(child); return ERR_PTR(-EINVAL); } if (fwnode_property_read_u32(child, "reg", &ch->reg)) { dev_err(dev, "channel without reg\n"); - fwnode_handle_put(child); return ERR_PTR(-EINVAL); } if (fwnode_property_read_u32(child, "gw,mode", &ch->mode)) { dev_err(dev, "channel without mode\n"); - fwnode_handle_put(child); return ERR_PTR(-EINVAL); } if (ch->mode > mode_max) { dev_err(dev, "invalid channel mode\n"); - fwnode_handle_put(child); return ERR_PTR(-EINVAL); } -- cgit From fb7a4931ef141054970ed065151a879764553206 Mon Sep 17 00:00:00 2001 From: Jeff Johnson Date: Fri, 7 Jun 2024 15:27:59 -0700 Subject: hwmon: add missing MODULE_DESCRIPTION() macros make allmodconfig && make W=1 C=1 reports: WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/hwmon/asus_atk0110.o WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/hwmon/corsair-cpro.o WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/hwmon/mr75203.o Add all missing invocations of the MODULE_DESCRIPTION() macro. Signed-off-by: Jeff Johnson Link: https://lore.kernel.org/r/20240607-md-drivers-hwmon-v1-1-1ea6d6fe61e3@quicinc.com Signed-off-by: Guenter Roeck --- drivers/hwmon/asus_atk0110.c | 1 + drivers/hwmon/corsair-cpro.c | 1 + drivers/hwmon/mr75203.c | 1 + 3 files changed, 3 insertions(+) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/asus_atk0110.c b/drivers/hwmon/asus_atk0110.c index d778a2aaefec..3751c1e3eddd 100644 --- a/drivers/hwmon/asus_atk0110.c +++ b/drivers/hwmon/asus_atk0110.c @@ -1389,4 +1389,5 @@ static void __exit atk0110_exit(void) module_init(atk0110_init); module_exit(atk0110_exit); +MODULE_DESCRIPTION("ASUS ATK0110 driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/corsair-cpro.c b/drivers/hwmon/corsair-cpro.c index e3300f3f4da6..e1a7f7aa7f80 100644 --- a/drivers/hwmon/corsair-cpro.c +++ b/drivers/hwmon/corsair-cpro.c @@ -670,6 +670,7 @@ static struct hid_driver ccp_driver = { }; MODULE_DEVICE_TABLE(hid, ccp_devices); +MODULE_DESCRIPTION("Corsair Commander Pro controller driver"); MODULE_LICENSE("GPL"); static int __init ccp_init(void) diff --git a/drivers/hwmon/mr75203.c b/drivers/hwmon/mr75203.c index 50a8b9c3f94d..7848198f8996 100644 --- a/drivers/hwmon/mr75203.c +++ b/drivers/hwmon/mr75203.c @@ -925,4 +925,5 @@ static struct platform_driver moortec_pvt_driver = { }; module_platform_driver(moortec_pvt_driver); +MODULE_DESCRIPTION("Moortec Semiconductor MR75203 PVT Controller driver"); MODULE_LICENSE("GPL v2"); -- cgit From 09262e9814ca0851c937cab1f26df4ab207885a1 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 27 May 2024 10:49:10 -0700 Subject: hwmon: Add support for SPD5118 compliant temperature sensors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for SPD5118 (Jedec JESD300) compliant temperature sensors. Such sensors are typically found on DDR5 memory modules. Cc: René Rebe Cc: Thomas Weißschuh Reviewed-by: Thomas Weißschuh Tested-by: Thomas Weißschuh Tested-by: Stephen Horvath Tested-by: Armin Wolf Signed-off-by: Guenter Roeck --- drivers/hwmon/Kconfig | 12 ++ drivers/hwmon/Makefile | 1 + drivers/hwmon/spd5118.c | 481 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 494 insertions(+) create mode 100644 drivers/hwmon/spd5118.c (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index e14ae18a973b..7a84e7637b51 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -2181,6 +2181,18 @@ config SENSORS_INA3221 This driver can also be built as a module. If so, the module will be called ina3221. +config SENSORS_SPD5118 + tristate "SPD5118 Compliant Temperature Sensors" + depends on I2C + select REGMAP_I2C + help + If you say yes here you get support for SPD5118 (JEDEC JESD300) + compliant temperature sensors. Such sensors are found on DDR5 memory + modules. + + This driver can also be built as a module. If so, the module + will be called spd5118. + config SENSORS_TC74 tristate "Microchip TC74" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index e3f25475d1f0..6574ca67d761 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -207,6 +207,7 @@ obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o obj-$(CONFIG_SENSORS_SPARX5) += sparx5-temp.o +obj-$(CONFIG_SENSORS_SPD5118) += spd5118.o obj-$(CONFIG_SENSORS_STTS751) += stts751.o obj-$(CONFIG_SENSORS_SURFACE_FAN)+= surface_fan.o obj-$(CONFIG_SENSORS_SY7636A) += sy7636a-hwmon.o diff --git a/drivers/hwmon/spd5118.c b/drivers/hwmon/spd5118.c new file mode 100644 index 000000000000..d3fc0ae17743 --- /dev/null +++ b/drivers/hwmon/spd5118.c @@ -0,0 +1,481 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for Jedec 5118 compliant temperature sensors + * + * Derived from https://github.com/Steve-Tech/SPD5118-DKMS + * Originally from T/2 driver at https://t2sde.org/packages/linux + * Copyright (c) 2023 René Rebe, ExactCODE GmbH; Germany. + * + * Copyright (c) 2024 Guenter Roeck + * + * Inspired by ee1004.c and jc42.c. + * + * SPD5118 compliant temperature sensors are typically used on DDR5 + * memory modules. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Addresses to scan */ +static const unsigned short normal_i2c[] = { + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, I2C_CLIENT_END }; + +/* SPD5118 registers. */ +#define SPD5118_REG_TYPE 0x00 /* MR0:MR1 */ +#define SPD5118_REG_REVISION 0x02 /* MR2 */ +#define SPD5118_REG_VENDOR 0x03 /* MR3:MR4 */ +#define SPD5118_REG_CAPABILITY 0x05 /* MR5 */ +#define SPD5118_REG_I2C_LEGACY_MODE 0x0B /* MR11 */ +#define SPD5118_REG_TEMP_CLR 0x13 /* MR19 */ +#define SPD5118_REG_ERROR_CLR 0x14 /* MR20 */ +#define SPD5118_REG_TEMP_CONFIG 0x1A /* MR26 */ +#define SPD5118_REG_TEMP_MAX 0x1c /* MR28:MR29 */ +#define SPD5118_REG_TEMP_MIN 0x1e /* MR30:MR31 */ +#define SPD5118_REG_TEMP_CRIT 0x20 /* MR32:MR33 */ +#define SPD5118_REG_TEMP_LCRIT 0x22 /* MR34:MR35 */ +#define SPD5118_REG_TEMP 0x31 /* MR49:MR50 */ +#define SPD5118_REG_TEMP_STATUS 0x33 /* MR51 */ + +#define SPD5118_TEMP_STATUS_HIGH BIT(0) +#define SPD5118_TEMP_STATUS_LOW BIT(1) +#define SPD5118_TEMP_STATUS_CRIT BIT(2) +#define SPD5118_TEMP_STATUS_LCRIT BIT(3) + +#define SPD5118_CAP_TS_SUPPORT BIT(1) /* temperature sensor support */ + +#define SPD5118_TS_DISABLE BIT(0) /* temperature sensor disable */ + +/* Temperature unit in millicelsius */ +#define SPD5118_TEMP_UNIT (MILLIDEGREE_PER_DEGREE / 4) +/* Representable temperature range in millicelsius */ +#define SPD5118_TEMP_RANGE_MIN -256000 +#define SPD5118_TEMP_RANGE_MAX 255750 + +static int spd5118_temp_from_reg(u16 reg) +{ + int temp = sign_extend32(reg >> 2, 10); + + return temp * SPD5118_TEMP_UNIT; +} + +static u16 spd5118_temp_to_reg(long temp) +{ + temp = clamp_val(temp, SPD5118_TEMP_RANGE_MIN, SPD5118_TEMP_RANGE_MAX); + return (DIV_ROUND_CLOSEST(temp, SPD5118_TEMP_UNIT) & 0x7ff) << 2; +} + +static int spd5118_read_temp(struct regmap *regmap, u32 attr, long *val) +{ + int reg, err; + u8 regval[2]; + u16 temp; + + switch (attr) { + case hwmon_temp_input: + reg = SPD5118_REG_TEMP; + break; + case hwmon_temp_max: + reg = SPD5118_REG_TEMP_MAX; + break; + case hwmon_temp_min: + reg = SPD5118_REG_TEMP_MIN; + break; + case hwmon_temp_crit: + reg = SPD5118_REG_TEMP_CRIT; + break; + case hwmon_temp_lcrit: + reg = SPD5118_REG_TEMP_LCRIT; + break; + default: + return -EOPNOTSUPP; + } + + err = regmap_bulk_read(regmap, reg, regval, 2); + if (err) + return err; + + temp = (regval[1] << 8) | regval[0]; + + *val = spd5118_temp_from_reg(temp); + return 0; +} + +static int spd5118_read_alarm(struct regmap *regmap, u32 attr, long *val) +{ + unsigned int mask, regval; + int err; + + switch (attr) { + case hwmon_temp_max_alarm: + mask = SPD5118_TEMP_STATUS_HIGH; + break; + case hwmon_temp_min_alarm: + mask = SPD5118_TEMP_STATUS_LOW; + break; + case hwmon_temp_crit_alarm: + mask = SPD5118_TEMP_STATUS_CRIT; + break; + case hwmon_temp_lcrit_alarm: + mask = SPD5118_TEMP_STATUS_LCRIT; + break; + default: + return -EOPNOTSUPP; + } + + err = regmap_read(regmap, SPD5118_REG_TEMP_STATUS, ®val); + if (err < 0) + return err; + *val = !!(regval & mask); + if (*val) + return regmap_write(regmap, SPD5118_REG_TEMP_CLR, mask); + return 0; +} + +static int spd5118_read_enable(struct regmap *regmap, long *val) +{ + u32 regval; + int err; + + err = regmap_read(regmap, SPD5118_REG_TEMP_CONFIG, ®val); + if (err < 0) + return err; + *val = !(regval & SPD5118_TS_DISABLE); + return 0; +} + +static int spd5118_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct regmap *regmap = dev_get_drvdata(dev); + + if (type != hwmon_temp) + return -EOPNOTSUPP; + + switch (attr) { + case hwmon_temp_input: + case hwmon_temp_max: + case hwmon_temp_min: + case hwmon_temp_crit: + case hwmon_temp_lcrit: + return spd5118_read_temp(regmap, attr, val); + case hwmon_temp_max_alarm: + case hwmon_temp_min_alarm: + case hwmon_temp_crit_alarm: + case hwmon_temp_lcrit_alarm: + return spd5118_read_alarm(regmap, attr, val); + case hwmon_temp_enable: + return spd5118_read_enable(regmap, val); + default: + return -EOPNOTSUPP; + } +} + +static int spd5118_write_temp(struct regmap *regmap, u32 attr, long val) +{ + u8 regval[2]; + u16 temp; + int reg; + + switch (attr) { + case hwmon_temp_max: + reg = SPD5118_REG_TEMP_MAX; + break; + case hwmon_temp_min: + reg = SPD5118_REG_TEMP_MIN; + break; + case hwmon_temp_crit: + reg = SPD5118_REG_TEMP_CRIT; + break; + case hwmon_temp_lcrit: + reg = SPD5118_REG_TEMP_LCRIT; + break; + default: + return -EOPNOTSUPP; + } + + temp = spd5118_temp_to_reg(val); + regval[0] = temp & 0xff; + regval[1] = temp >> 8; + + return regmap_bulk_write(regmap, reg, regval, 2); +} + +static int spd5118_write_enable(struct regmap *regmap, long val) +{ + if (val && val != 1) + return -EINVAL; + + return regmap_update_bits(regmap, SPD5118_REG_TEMP_CONFIG, + SPD5118_TS_DISABLE, + val ? 0 : SPD5118_TS_DISABLE); +} + +static int spd5118_temp_write(struct regmap *regmap, u32 attr, long val) +{ + switch (attr) { + case hwmon_temp_max: + case hwmon_temp_min: + case hwmon_temp_crit: + case hwmon_temp_lcrit: + return spd5118_write_temp(regmap, attr, val); + case hwmon_temp_enable: + return spd5118_write_enable(regmap, val); + default: + return -EOPNOTSUPP; + } +} + +static int spd5118_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct regmap *regmap = dev_get_drvdata(dev); + + switch (type) { + case hwmon_temp: + return spd5118_temp_write(regmap, attr, val); + default: + return -EOPNOTSUPP; + } +} + +static umode_t spd5118_is_visible(const void *_data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + if (type != hwmon_temp) + return 0; + + switch (attr) { + case hwmon_temp_input: + return 0444; + case hwmon_temp_min: + case hwmon_temp_max: + case hwmon_temp_lcrit: + case hwmon_temp_crit: + case hwmon_temp_enable: + return 0644; + case hwmon_temp_min_alarm: + case hwmon_temp_max_alarm: + case hwmon_temp_crit_alarm: + case hwmon_temp_lcrit_alarm: + return 0444; + default: + return 0; + } +} + +static inline bool spd5118_parity8(u8 w) +{ + w ^= w >> 4; + return (0x6996 >> (w & 0xf)) & 1; +} + +/* + * Bank and vendor id are 8-bit fields with seven data bits and odd parity. + * Vendor IDs 0 and 0x7f are invalid. + * See Jedec standard JEP106BJ for details and a list of assigned vendor IDs. + */ +static bool spd5118_vendor_valid(u8 bank, u8 id) +{ + if (!spd5118_parity8(bank) || !spd5118_parity8(id)) + return false; + + id &= 0x7f; + return id && id != 0x7f; +} + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int spd5118_detect(struct i2c_client *client, struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + int regval; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA)) + return -ENODEV; + + regval = i2c_smbus_read_word_swapped(client, SPD5118_REG_TYPE); + if (regval != 0x5118) + return -ENODEV; + + regval = i2c_smbus_read_word_data(client, SPD5118_REG_VENDOR); + if (regval < 0 || !spd5118_vendor_valid(regval & 0xff, regval >> 8)) + return -ENODEV; + + regval = i2c_smbus_read_byte_data(client, SPD5118_REG_CAPABILITY); + if (regval < 0) + return -ENODEV; + if (!(regval & SPD5118_CAP_TS_SUPPORT) || (regval & 0xfc)) + return -ENODEV; + + regval = i2c_smbus_read_byte_data(client, SPD5118_REG_TEMP_CLR); + if (regval) + return -ENODEV; + regval = i2c_smbus_read_byte_data(client, SPD5118_REG_ERROR_CLR); + if (regval) + return -ENODEV; + + regval = i2c_smbus_read_byte_data(client, SPD5118_REG_REVISION); + if (regval < 0 || (regval & 0xc1)) + return -ENODEV; + + regval = i2c_smbus_read_byte_data(client, SPD5118_REG_TEMP_CONFIG); + if (regval < 0) + return -ENODEV; + if (regval & ~SPD5118_TS_DISABLE) + return -ENODEV; + + strscpy(info->type, "spd5118", I2C_NAME_SIZE); + return 0; +} + +static const struct hwmon_channel_info *spd5118_info[] = { + HWMON_CHANNEL_INFO(chip, + HWMON_C_REGISTER_TZ), + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | + HWMON_T_LCRIT | HWMON_T_LCRIT_ALARM | + HWMON_T_MIN | HWMON_T_MIN_ALARM | + HWMON_T_MAX | HWMON_T_MAX_ALARM | + HWMON_T_CRIT | HWMON_T_CRIT_ALARM | + HWMON_T_ENABLE), + NULL +}; + +static const struct hwmon_ops spd5118_hwmon_ops = { + .is_visible = spd5118_is_visible, + .read = spd5118_read, + .write = spd5118_write, +}; + +static const struct hwmon_chip_info spd5118_chip_info = { + .ops = &spd5118_hwmon_ops, + .info = spd5118_info, +}; + +static bool spd5118_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SPD5118_REG_TEMP_CLR: + case SPD5118_REG_TEMP_CONFIG: + case SPD5118_REG_TEMP_MAX: + case SPD5118_REG_TEMP_MAX + 1: + case SPD5118_REG_TEMP_MIN: + case SPD5118_REG_TEMP_MIN + 1: + case SPD5118_REG_TEMP_CRIT: + case SPD5118_REG_TEMP_CRIT + 1: + case SPD5118_REG_TEMP_LCRIT: + case SPD5118_REG_TEMP_LCRIT + 1: + return true; + default: + return false; + } +} + +static bool spd5118_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SPD5118_REG_TEMP_CLR: + case SPD5118_REG_ERROR_CLR: + case SPD5118_REG_TEMP: + case SPD5118_REG_TEMP + 1: + case SPD5118_REG_TEMP_STATUS: + return true; + default: + return false; + } +} + +static const struct regmap_config spd5118_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = SPD5118_REG_TEMP_STATUS, + .writeable_reg = spd5118_writeable_reg, + .volatile_reg = spd5118_volatile_reg, + .cache_type = REGCACHE_MAPLE, +}; + +static int spd5118_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + unsigned int regval, revision, vendor, bank; + struct device *hwmon_dev; + struct regmap *regmap; + int err; + + regmap = devm_regmap_init_i2c(client, &spd5118_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), "regmap init failed\n"); + + err = regmap_read(regmap, SPD5118_REG_CAPABILITY, ®val); + if (err) + return err; + if (!(regval & SPD5118_CAP_TS_SUPPORT)) + return -ENODEV; + + err = regmap_read(regmap, SPD5118_REG_REVISION, &revision); + if (err) + return err; + + err = regmap_read(regmap, SPD5118_REG_VENDOR, &bank); + if (err) + return err; + err = regmap_read(regmap, SPD5118_REG_VENDOR + 1, &vendor); + if (err) + return err; + if (!spd5118_vendor_valid(bank, vendor)) + return -ENODEV; + + hwmon_dev = devm_hwmon_device_register_with_info(dev, "spd5118", + regmap, &spd5118_chip_info, + NULL); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); + + /* + * From JESD300-5B + * MR2 bits [5:4]: Major revision, 1..4 + * MR2 bits [3:1]: Minor revision, 0..8? Probably a typo, assume 1..8 + */ + dev_info(dev, "DDR5 temperature sensor: vendor 0x%02x:0x%02x revision %d.%d\n", + bank & 0x7f, vendor, ((revision >> 4) & 0x03) + 1, ((revision >> 1) & 0x07) + 1); + + return 0; +} + +static const struct i2c_device_id spd5118_id[] = { + { "spd5118", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, spd5118_id); + +static const struct of_device_id spd5118_of_ids[] = { + { .compatible = "jedec,spd5118", }, + { } +}; +MODULE_DEVICE_TABLE(of, spd5118_of_ids); + +static struct i2c_driver spd5118_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "spd5118", + .of_match_table = spd5118_of_ids, + }, + .probe = spd5118_probe, + .id_table = spd5118_id, + .detect = spd5118_detect, + .address_list = normal_i2c, +}; + +module_i2c_driver(spd5118_driver); + +MODULE_AUTHOR("René Rebe "); +MODULE_AUTHOR("Guenter Roeck "); +MODULE_DESCRIPTION("SPD 5118 driver"); +MODULE_LICENSE("GPL"); -- cgit From d1b4c755081a50b4ce21cd03c38f9176aa41f21e Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 30 May 2024 11:44:11 -0700 Subject: hwmon: (spd5118) Add suspend/resume support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add suspend/resume support to ensure that limit and configuration registers are updated and synchronized after a suspend/resume cycle. Cc: Armin Wolf Cc: Stephen Horvath Reviewed-by: Thomas Weißschuh Tested-by: Stephen Horvath Tested-by: Armin Wolf Signed-off-by: Guenter Roeck --- drivers/hwmon/spd5118.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/spd5118.c b/drivers/hwmon/spd5118.c index d3fc0ae17743..d55c073ff5fd 100644 --- a/drivers/hwmon/spd5118.c +++ b/drivers/hwmon/spd5118.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -432,6 +433,8 @@ static int spd5118_probe(struct i2c_client *client) if (!spd5118_vendor_valid(bank, vendor)) return -ENODEV; + dev_set_drvdata(dev, regmap); + hwmon_dev = devm_hwmon_device_register_with_info(dev, "spd5118", regmap, &spd5118_chip_info, NULL); @@ -449,6 +452,41 @@ static int spd5118_probe(struct i2c_client *client) return 0; } +static int spd5118_suspend(struct device *dev) +{ + struct regmap *regmap = dev_get_drvdata(dev); + u32 regval; + int err; + + /* + * Make sure the configuration register in the regmap cache is current + * before bypassing it. + */ + err = regmap_read(regmap, SPD5118_REG_TEMP_CONFIG, ®val); + if (err < 0) + return err; + + regcache_cache_bypass(regmap, true); + regmap_update_bits(regmap, SPD5118_REG_TEMP_CONFIG, SPD5118_TS_DISABLE, + SPD5118_TS_DISABLE); + regcache_cache_bypass(regmap, false); + + regcache_cache_only(regmap, true); + regcache_mark_dirty(regmap); + + return 0; +} + +static int spd5118_resume(struct device *dev) +{ + struct regmap *regmap = dev_get_drvdata(dev); + + regcache_cache_only(regmap, false); + return regcache_sync(regmap); +} + +static DEFINE_SIMPLE_DEV_PM_OPS(spd5118_pm_ops, spd5118_suspend, spd5118_resume); + static const struct i2c_device_id spd5118_id[] = { { "spd5118", 0 }, { } @@ -466,6 +504,7 @@ static struct i2c_driver spd5118_driver = { .driver = { .name = "spd5118", .of_match_table = spd5118_of_ids, + .pm = pm_sleep_ptr(&spd5118_pm_ops), }, .probe = spd5118_probe, .id_table = spd5118_id, -- cgit From 25dff444c6cb122eed08e328ddc9698f3b3d2ca9 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 31 May 2024 09:55:37 -0700 Subject: hwmon: (spd5118) Add support for reading SPD data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for reading SPD NVMEM data from SPD5118 (Jedec JESD300) compliant memory modules. NVMEM write operation is not supported. NVMEM support is optional. If CONFIG_NVMEM is disabled, the driver will still instantiate but not provide NVMEM attribute files. Reviewed-by: Thomas Weißschuh Tested-by: Armin Wolf Signed-off-by: Guenter Roeck --- drivers/hwmon/spd5118.c | 146 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 142 insertions(+), 4 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/spd5118.c b/drivers/hwmon/spd5118.c index d55c073ff5fd..de6cf07f3007 100644 --- a/drivers/hwmon/spd5118.c +++ b/drivers/hwmon/spd5118.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include #include #include @@ -53,12 +55,30 @@ static const unsigned short normal_i2c[] = { #define SPD5118_TS_DISABLE BIT(0) /* temperature sensor disable */ +#define SPD5118_LEGACY_MODE_ADDR BIT(3) +#define SPD5118_LEGACY_PAGE_MASK GENMASK(2, 0) +#define SPD5118_LEGACY_MODE_MASK (SPD5118_LEGACY_MODE_ADDR | SPD5118_LEGACY_PAGE_MASK) + +#define SPD5118_NUM_PAGES 8 +#define SPD5118_PAGE_SIZE 128 +#define SPD5118_PAGE_SHIFT 7 +#define SPD5118_PAGE_MASK GENMASK(6, 0) +#define SPD5118_EEPROM_BASE 0x80 +#define SPD5118_EEPROM_SIZE (SPD5118_PAGE_SIZE * SPD5118_NUM_PAGES) + /* Temperature unit in millicelsius */ #define SPD5118_TEMP_UNIT (MILLIDEGREE_PER_DEGREE / 4) /* Representable temperature range in millicelsius */ #define SPD5118_TEMP_RANGE_MIN -256000 #define SPD5118_TEMP_RANGE_MAX 255750 +struct spd5118_data { + struct regmap *regmap; + struct mutex nvmem_lock; +}; + +/* hwmon */ + static int spd5118_temp_from_reg(u16 reg) { int temp = sign_extend32(reg >> 2, 10); @@ -360,9 +380,111 @@ static const struct hwmon_chip_info spd5118_chip_info = { .info = spd5118_info, }; +/* nvmem */ + +static int spd5118_nvmem_set_page(struct regmap *regmap, int page) +{ + unsigned int old_page; + int err; + + err = regmap_read(regmap, SPD5118_REG_I2C_LEGACY_MODE, &old_page); + if (err) + return err; + + if (page != (old_page & SPD5118_LEGACY_MODE_MASK)) { + /* Update page and explicitly select 1-byte addressing */ + err = regmap_update_bits(regmap, SPD5118_REG_I2C_LEGACY_MODE, + SPD5118_LEGACY_MODE_MASK, page); + if (err) + return err; + + /* Selected new NVMEM page, drop cached data */ + regcache_drop_region(regmap, SPD5118_EEPROM_BASE, 0xff); + } + + return 0; +} + +static ssize_t spd5118_nvmem_read_page(struct regmap *regmap, char *buf, + unsigned int offset, size_t count) +{ + int err; + + err = spd5118_nvmem_set_page(regmap, offset >> SPD5118_PAGE_SHIFT); + if (err) + return err; + + offset &= SPD5118_PAGE_MASK; + + /* Can't cross page boundaries */ + if (offset + count > SPD5118_PAGE_SIZE) + count = SPD5118_PAGE_SIZE - offset; + + err = regmap_bulk_read(regmap, SPD5118_EEPROM_BASE + offset, buf, count); + if (err) + return err; + + return count; +} + +static int spd5118_nvmem_read(void *priv, unsigned int off, void *val, size_t count) +{ + struct spd5118_data *data = priv; + char *buf = val; + int ret; + + if (unlikely(!count)) + return count; + + if (off + count > SPD5118_EEPROM_SIZE) + return -EINVAL; + + mutex_lock(&data->nvmem_lock); + + while (count) { + ret = spd5118_nvmem_read_page(data->regmap, buf, off, count); + if (ret < 0) { + mutex_unlock(&data->nvmem_lock); + return ret; + } + buf += ret; + off += ret; + count -= ret; + } + mutex_unlock(&data->nvmem_lock); + return 0; +} + +static int spd5118_nvmem_init(struct device *dev, struct spd5118_data *data) +{ + struct nvmem_config nvmem_config = { + .type = NVMEM_TYPE_EEPROM, + .name = dev_name(dev), + .id = NVMEM_DEVID_NONE, + .dev = dev, + .base_dev = dev, + .read_only = true, + .root_only = false, + .owner = THIS_MODULE, + .compat = true, + .reg_read = spd5118_nvmem_read, + .priv = data, + .stride = 1, + .word_size = 1, + .size = SPD5118_EEPROM_SIZE, + }; + struct nvmem_device *nvmem; + + nvmem = devm_nvmem_register(dev, &nvmem_config); + return PTR_ERR_OR_ZERO(nvmem); +} + +/* regmap */ + static bool spd5118_writeable_reg(struct device *dev, unsigned int reg) { switch (reg) { + case SPD5118_REG_I2C_LEGACY_MODE: case SPD5118_REG_TEMP_CLR: case SPD5118_REG_TEMP_CONFIG: case SPD5118_REG_TEMP_MAX: @@ -396,7 +518,7 @@ static bool spd5118_volatile_reg(struct device *dev, unsigned int reg) static const struct regmap_config spd5118_regmap_config = { .reg_bits = 8, .val_bits = 8, - .max_register = SPD5118_REG_TEMP_STATUS, + .max_register = 0xff, .writeable_reg = spd5118_writeable_reg, .volatile_reg = spd5118_volatile_reg, .cache_type = REGCACHE_MAPLE, @@ -406,10 +528,15 @@ static int spd5118_probe(struct i2c_client *client) { struct device *dev = &client->dev; unsigned int regval, revision, vendor, bank; + struct spd5118_data *data; struct device *hwmon_dev; struct regmap *regmap; int err; + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + regmap = devm_regmap_init_i2c(client, &spd5118_regmap_config); if (IS_ERR(regmap)) return dev_err_probe(dev, PTR_ERR(regmap), "regmap init failed\n"); @@ -433,7 +560,16 @@ static int spd5118_probe(struct i2c_client *client) if (!spd5118_vendor_valid(bank, vendor)) return -ENODEV; - dev_set_drvdata(dev, regmap); + data->regmap = regmap; + mutex_init(&data->nvmem_lock); + dev_set_drvdata(dev, data); + + err = spd5118_nvmem_init(dev, data); + /* Ignore if NVMEM support is disabled */ + if (err && err != -EOPNOTSUPP) { + dev_err_probe(dev, err, "failed to register nvmem\n"); + return err; + } hwmon_dev = devm_hwmon_device_register_with_info(dev, "spd5118", regmap, &spd5118_chip_info, @@ -454,7 +590,8 @@ static int spd5118_probe(struct i2c_client *client) static int spd5118_suspend(struct device *dev) { - struct regmap *regmap = dev_get_drvdata(dev); + struct spd5118_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; u32 regval; int err; @@ -479,7 +616,8 @@ static int spd5118_suspend(struct device *dev) static int spd5118_resume(struct device *dev) { - struct regmap *regmap = dev_get_drvdata(dev); + struct spd5118_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; regcache_cache_only(regmap, false); return regcache_sync(regmap); -- cgit From 9ecc129470bcd3505c972b91dc402b8538e9cd9c Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 2 Jun 2024 22:31:52 -0700 Subject: hwmon: (spd5118) Add configuration option for auto-detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With SPD5118 chip detection for the most part handled by the i2c-smbus core using DMI information, the spd5118 driver no longer needs to auto-detect spd5118 compliant chips. Auto-detection by the driver is still needed on systems with no DMI support or on systems with more than eight DIMMs and can not be removed entirely. However, it affects boot time and introduces the risk of mis-identifying chips. Add configuration option to be able to disable it on systems where chip detection is handled outside the driver. Cc: Wolfram Sang Reviewed-by: Thomas Weißschuh Tested-by: Armin Wolf Signed-off-by: Guenter Roeck --- drivers/hwmon/Kconfig | 19 +++++++++++++++++++ drivers/hwmon/spd5118.c | 4 ++-- 2 files changed, 21 insertions(+), 2 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 7a84e7637b51..d5eced417fc3 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -2193,6 +2193,25 @@ config SENSORS_SPD5118 This driver can also be built as a module. If so, the module will be called spd5118. +config SENSORS_SPD5118_DETECT + bool "Enable detect function" + depends on SENSORS_SPD5118 + default (!DMI || !X86) + help + If enabled, the driver auto-detects if a chip in the SPD address + range is compliant to the SPD51888 standard and auto-instantiates + if that is the case. If disabled, SPD5118 compliant devices have + to be instantiated by other means. On X86 systems with DMI support + this will typically be done from DMI DDR detection code in the + I2C SMBus subsystem. Devicetree based systems will instantiate + attached devices if the DIMMs are listed in the devicetree file. + + Disabling the detect function will speed up boot time and reduce + the risk of mis-detecting SPD5118 compliant devices. However, it + may result in missed DIMMs under some circumstances. + + If unsure, say Y. + config SENSORS_TC74 tristate "Microchip TC74" depends on I2C diff --git a/drivers/hwmon/spd5118.c b/drivers/hwmon/spd5118.c index de6cf07f3007..ac94a6779360 100644 --- a/drivers/hwmon/spd5118.c +++ b/drivers/hwmon/spd5118.c @@ -646,8 +646,8 @@ static struct i2c_driver spd5118_driver = { }, .probe = spd5118_probe, .id_table = spd5118_id, - .detect = spd5118_detect, - .address_list = normal_i2c, + .detect = IS_ENABLED(CONFIG_SENSORS_SPD5118_DETECT) ? spd5118_detect : NULL, + .address_list = IS_ENABLED(CONFIG_SENSORS_SPD5118_DETECT) ? normal_i2c : NULL, }; module_i2c_driver(spd5118_driver); -- cgit From 71ac69e08947a03925cbd5f7517cba358cda1eb3 Mon Sep 17 00:00:00 2001 From: Eugene Shalygin Date: Sat, 8 Jun 2024 10:50:05 +0200 Subject: hwmon: (asus-ec-sensors) add ProArt X670E-CREATOR WIFI Provided and tested by a user in a GitHub PR [1]. [1] https://github.com/zeule/asus-ec-sensors/pull/56 Signed-off-by: Eugene Shalygin Link: https://lore.kernel.org/r/20240608085146.572777-2-eugene.shalygin@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/asus-ec-sensors.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c index 36f9e38000d5..6bb8d7b1d219 100644 --- a/drivers/hwmon/asus-ec-sensors.c +++ b/drivers/hwmon/asus-ec-sensors.c @@ -322,6 +322,14 @@ static const struct ec_board_info board_info_pro_art_x570_creator_wifi = { .family = family_amd_500_series, }; +static const struct ec_board_info board_info_pro_art_x670E_creator_wifi = { + .sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE | + SENSOR_TEMP_MB | SENSOR_TEMP_VRM | + SENSOR_TEMP_T_SENSOR, + .mutex_path = ACPI_GLOBAL_LOCK_PSEUDO_PATH, + .family = family_amd_600_series, +}; + static const struct ec_board_info board_info_pro_art_b550_creator = { .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_T_SENSOR | @@ -486,6 +494,8 @@ static const struct dmi_system_id dmi_table[] = { &board_info_prime_x570_pro), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ProArt X570-CREATOR WIFI", &board_info_pro_art_x570_creator_wifi), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("ProArt X670E-CREATOR WIFI", + &board_info_pro_art_x670E_creator_wifi), DMI_EXACT_MATCH_ASUS_BOARD_NAME("ProArt B550-CREATOR", &board_info_pro_art_b550_creator), DMI_EXACT_MATCH_ASUS_BOARD_NAME("Pro WS X570-ACE", -- cgit From 138d45d96758db803b50761297f3331c9170bc87 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 7 Jun 2024 06:32:04 -0700 Subject: hwmon: (pmbus/max31827) Explain why enum chips must not start with 0 If a driver calls device_get_match_data(), the .data pointer in its id data structures must not be NULL/0 because device_get_match_data() returns NULL if an entry is not found. Explain that in a comment to avoid confusion why this is required in this driver but not in other drivers. Cc: Daniel Matyas Acked-by: Nuno Sa Signed-off-by: Guenter Roeck --- drivers/hwmon/max31827.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/max31827.c b/drivers/hwmon/max31827.c index 4d89b6a7060b..48e8f8ba4d05 100644 --- a/drivers/hwmon/max31827.c +++ b/drivers/hwmon/max31827.c @@ -47,6 +47,11 @@ #define MAX31827_M_DGR_TO_16_BIT(x) (((x) << 4) / 1000) #define MAX31827_DEVICE_ENABLE(x) ((x) ? 0xA : 0x0) +/* + * The enum passed in the .data pointer of struct of_device_id must + * start with a value != 0 since that is a requirement for using + * device_get_match_data(). + */ enum chips { max31827 = 1, max31828, max31829 }; enum max31827_cnv { -- cgit From e229c6e80f9d3e92880de66962f7448d7d00e7df Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 6 Jun 2024 09:36:38 -0700 Subject: hwmon: (pmbus/mp2856) Let enum chips start with index 0 Earlier it was assumed that the data pointer in of_device_id must not start with 0 (NULL) if i2c_get_match_data() is used. However, it turns out that this is perfectly fine as long as there is also an i2c_device_id array with the same data, which is used as fallback in that case. Let enum chips start with 0 to avoid confusion against other drivers where the enum starts with 0 and i2c_get_match_data() is used as well. While doing that, remove chip_id from struct mp2856_data since it is only used in the probe function, and typecast the result of i2c_get_match_data() to kernel_ulong_t to avoid the double typecast. Cc: Peter Yin Cc: Potin Lai Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/mp2856.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/pmbus/mp2856.c b/drivers/hwmon/pmbus/mp2856.c index 6969350f5d7d..41bb86667091 100644 --- a/drivers/hwmon/pmbus/mp2856.c +++ b/drivers/hwmon/pmbus/mp2856.c @@ -46,7 +46,7 @@ #define MP2856_PAGE_NUM 2 -enum chips { mp2856 = 1, mp2857 }; +enum chips { mp2856, mp2857 }; static const int mp2856_max_phases[][MP2856_PAGE_NUM] = { [mp2856] = { MP2856_MAX_PHASE_RAIL1, MP2856_MAX_PHASE_RAIL2 }, @@ -66,7 +66,6 @@ struct mp2856_data { int vout_format[MP2856_PAGE_NUM]; int curr_sense_gain[MP2856_PAGE_NUM]; int max_phases[MP2856_PAGE_NUM]; - enum chips chip_id; }; #define to_mp2856_data(x) container_of(x, struct mp2856_data, info) @@ -397,6 +396,7 @@ static int mp2856_probe(struct i2c_client *client) { struct pmbus_driver_info *info; struct mp2856_data *data; + enum chips chip_id; int ret; data = devm_kzalloc(&client->dev, sizeof(struct mp2856_data), @@ -404,9 +404,9 @@ static int mp2856_probe(struct i2c_client *client) if (!data) return -ENOMEM; - data->chip_id = (enum chips)(uintptr_t)i2c_get_match_data(client); + chip_id = (kernel_ulong_t)i2c_get_match_data(client); - memcpy(data->max_phases, mp2856_max_phases[data->chip_id], + memcpy(data->max_phases, mp2856_max_phases[chip_id], sizeof(data->max_phases)); memcpy(&data->info, &mp2856_info, sizeof(*info)); -- cgit From 22558934447999f38bb521a57279aa7ecf3f2d74 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 6 Jun 2024 09:23:44 -0700 Subject: hwmon: (nct6775) Let enum kinds start with index 0 Commit 10a0575ea09d ("hwmon: (nct6775-i2c) Use i2c_get_match_data()") introduced calling i2c_get_match_data() to the nct6775 driver. As part of that commit, enum kinds was changed to start with 1, based on Adjust the 'kinds' enum to not use 0, so that no match data can be distinguished from a valid enum value. The patch had to be fixed later with commit 2792fc8f8c83 ("hwmon: (nct6775-core) Explicitly initialize nct6775_device_names indexes") and commit efe86092ab31 ("hwmon: (nct6775-platform) Explicitly initialize nct6775_sio_names indexes"). Various patches submitted later show that the change from 0 to 1 is not really necessary. As it turns out, it is perfectly fine as long as there is an i2c_device_id array with the same data as in the of_device_id array. This data is used as fallback if the data pointer in struct of_device_id is NULL (0). Let enum chips start with 0 to avoid confusion against other drivers where the enum starts with 0 and i2c_get_match_data() is used as well. Signed-off-by: Guenter Roeck --- drivers/hwmon/nct6775.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/nct6775.h b/drivers/hwmon/nct6775.h index d31e7a030216..296eff99d003 100644 --- a/drivers/hwmon/nct6775.h +++ b/drivers/hwmon/nct6775.h @@ -4,7 +4,7 @@ #include -enum kinds { nct6106 = 1, nct6116, nct6775, nct6776, nct6779, nct6791, nct6792, +enum kinds { nct6106, nct6116, nct6775, nct6776, nct6779, nct6791, nct6792, nct6793, nct6795, nct6796, nct6797, nct6798, nct6799 }; enum pwm_enable { off, manual, thermal_cruise, speed_cruise, sf3, sf4 }; -- cgit From cbbb76e4b8bf2c938f75baf11272ca3536be9366 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 6 Jun 2024 09:16:40 -0700 Subject: hwmon: (pmbus/lm25066) Let enum chips start with index 0 Commit ac0c26bae662 ("hwmon: (lm25066) Use i2c_get_match_data()") changed enum chips to start with 1 instead of 0, under the assumption that the data pointer in of_device_id must not start with 0 (NULL) if i2c_get_match_data() is used. However, that is perfectly fine as long as there is also an i2c_device_id array with the same data which is used as fallback in that case. Let enum chips start with 0 to avoid confusion against other drivers where the enum starts with 0 and i2c_get_match_data() is used as well. Cc: Rob Herring Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/lm25066.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/pmbus/lm25066.c b/drivers/hwmon/pmbus/lm25066.c index cfffa4cdc0df..c36c124d1a2d 100644 --- a/drivers/hwmon/pmbus/lm25066.c +++ b/drivers/hwmon/pmbus/lm25066.c @@ -17,7 +17,7 @@ #include #include "pmbus.h" -enum chips { lm25056 = 1, lm25066, lm5064, lm5066, lm5066i }; +enum chips { lm25056, lm25066, lm5064, lm5066, lm5066i }; #define LM25066_READ_VAUX 0xd0 #define LM25066_MFR_READ_IIN 0xd1 -- cgit From ec5d234f191a3884e974fcb77694e83e984ffc68 Mon Sep 17 00:00:00 2001 From: Amna Waseem Date: Tue, 11 Jun 2024 11:36:26 +0200 Subject: hwmon: (ina2xx) Add device tree support to pass alert polarity The INA230 has an Alert pin which is asserted when the alert function selected in the Mask/Enable register exceeds the value programmed into the Alert Limit register. Assertion is based on the Alert Polarity Bit (APOL, bit 1 of the Mask/Enable register). It is default set to value 0 i.e Normal (active-low open collector). However, hardware can be designed in such a way that expects Alert pin to become active high if a user-defined threshold in Alert limit register has been exceeded. This patch adds a way to pass alert polarity value to the driver via device tree. Signed-off-by: Amna Waseem Link: https://lore.kernel.org/r/20240611-apol-ina2xx-fix-v4-2-8df1d2282fc5@axis.com Signed-off-by: Guenter Roeck --- drivers/hwmon/ina2xx.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index f8b5d972191e..9ab4205622e2 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -73,6 +73,11 @@ #define INA226_READ_AVG(reg) (((reg) & INA226_AVG_RD_MASK) >> 9) #define INA226_SHIFT_AVG(val) ((val) << 9) +#define INA226_ALERT_POLARITY_MASK 0x0002 +#define INA226_SHIFT_ALERT_POLARITY(val) ((val) << 1) +#define INA226_ALERT_POL_LOW 0 +#define INA226_ALERT_POL_HIGH 1 + /* bit number of alert functions in Mask/Enable Register */ #define INA226_SHUNT_OVER_VOLTAGE_BIT 15 #define INA226_SHUNT_UNDER_VOLTAGE_BIT 14 @@ -178,6 +183,14 @@ static u16 ina226_interval_to_reg(int interval) return INA226_SHIFT_AVG(avg_bits); } +static int ina2xx_set_alert_polarity(struct ina2xx_data *data, + unsigned long val) +{ + return regmap_update_bits(data->regmap, INA226_MASK_ENABLE, + INA226_ALERT_POLARITY_MASK, + INA226_SHIFT_ALERT_POLARITY(val)); +} + /* * Calibration register is set to the best value, which eliminates * truncation errors on calculating current register in hardware. @@ -654,6 +667,25 @@ static int ina2xx_probe(struct i2c_client *client) if (ret) return dev_err_probe(dev, ret, "failed to enable vs regulator\n"); + if (chip == ina226) { + if (of_property_read_bool(dev->of_node, "ti,alert-polarity-active-high")) { + ret = ina2xx_set_alert_polarity(data, + INA226_ALERT_POL_HIGH); + if (ret < 0) { + return dev_err_probe(dev, ret, + "failed to set alert polarity active high\n"); + } + } else { + /* Set default value i.e active low */ + ret = ina2xx_set_alert_polarity(data, + INA226_ALERT_POL_LOW); + if (ret < 0) { + return dev_err_probe(dev, ret, + "failed to set alert polarity active low\n"); + } + } + } + ret = ina2xx_init(data); if (ret < 0) { dev_err(dev, "error configuring the device: %d\n", ret); -- cgit From fc37348c907ba220b898ad383c0d5134a9a545c1 Mon Sep 17 00:00:00 2001 From: Noah Wang Date: Tue, 11 Jun 2024 18:14:15 +0800 Subject: hwmon: add MP2993 driver Add support for MPS VR controller mp2993. This driver exposes telemetry and limits value readings and writtings. Signed-off-by: Noah Wang Link: https://lore.kernel.org/r/SEYPR04MB648203DC017A514837DB1204FAC72@SEYPR04MB6482.apcprd04.prod.outlook.com Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/Kconfig | 9 ++ drivers/hwmon/pmbus/Makefile | 1 + drivers/hwmon/pmbus/mp2993.c | 261 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 271 insertions(+) create mode 100644 drivers/hwmon/pmbus/mp2993.c (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 08e82c457356..d875d31ce84c 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -346,6 +346,15 @@ config SENSORS_MP2975 This driver can also be built as a module. If so, the module will be called mp2975. +config SENSORS_MP2993 + tristate "MPS MP2993" + help + If you say yes here you get hardware monitoring support for MPS + MP2993 Dual Loop Digital Multi-Phase Controller. + + This driver can also be built as a module. If so, the module will + be called mp2993. + config SENSORS_MP2975_REGULATOR depends on SENSORS_MP2975 && REGULATOR bool "Regulator support for MPS MP2975" diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile index 2279b3327bbf..312d3f0c0540 100644 --- a/drivers/hwmon/pmbus/Makefile +++ b/drivers/hwmon/pmbus/Makefile @@ -37,6 +37,7 @@ obj-$(CONFIG_SENSORS_MAX8688) += max8688.o obj-$(CONFIG_SENSORS_MP2856) += mp2856.o obj-$(CONFIG_SENSORS_MP2888) += mp2888.o obj-$(CONFIG_SENSORS_MP2975) += mp2975.o +obj-$(CONFIG_SENSORS_MP2993) += mp2993.o obj-$(CONFIG_SENSORS_MP5023) += mp5023.o obj-$(CONFIG_SENSORS_MP5990) += mp5990.o obj-$(CONFIG_SENSORS_MPQ7932) += mpq7932.o diff --git a/drivers/hwmon/pmbus/mp2993.c b/drivers/hwmon/pmbus/mp2993.c new file mode 100644 index 000000000000..944593e13231 --- /dev/null +++ b/drivers/hwmon/pmbus/mp2993.c @@ -0,0 +1,261 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for MPS Multi-phase Digital VR Controllers(MP2993) + */ + +#include +#include +#include +#include "pmbus.h" + +#define MP2993_VOUT_OVUV_UINT 125 +#define MP2993_VOUT_OVUV_DIV 64 +#define MP2993_VIN_LIMIT_UINT 1 +#define MP2993_VIN_LIMIT_DIV 8 +#define MP2993_READ_VIN_UINT 1 +#define MP2993_READ_VIN_DIV 32 + +#define MP2993_PAGE_NUM 2 + +#define MP2993_RAIL1_FUNC (PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | \ + PMBUS_HAVE_IOUT | PMBUS_HAVE_POUT | \ + PMBUS_HAVE_TEMP | PMBUS_HAVE_PIN | \ + PMBUS_HAVE_IIN | \ + PMBUS_HAVE_STATUS_VOUT | \ + PMBUS_HAVE_STATUS_IOUT | \ + PMBUS_HAVE_STATUS_TEMP | \ + PMBUS_HAVE_STATUS_INPUT) + +#define MP2993_RAIL2_FUNC (PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | \ + PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP | \ + PMBUS_HAVE_STATUS_VOUT | \ + PMBUS_HAVE_STATUS_IOUT | \ + PMBUS_HAVE_STATUS_TEMP | \ + PMBUS_HAVE_STATUS_INPUT) + +/* Converts a linear11 data exponent to a specified value */ +static u16 mp2993_linear11_exponent_transfer(u16 word, u16 expect_exponent) +{ + s16 exponent, mantissa, target_exponent; + + exponent = ((s16)word) >> 11; + mantissa = ((s16)((word & 0x7ff) << 5)) >> 5; + target_exponent = (s16)((expect_exponent & 0x1f) << 11) >> 11; + + if (exponent > target_exponent) + mantissa = mantissa << (exponent - target_exponent); + else + mantissa = mantissa >> (target_exponent - exponent); + + return (mantissa & 0x7ff) | ((expect_exponent << 11) & 0xf800); +} + +static int +mp2993_set_vout_format(struct i2c_client *client, int page, int format) +{ + int ret; + + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page); + if (ret < 0) + return ret; + + return i2c_smbus_write_byte_data(client, PMBUS_VOUT_MODE, format); +} + +static int mp2993_identify(struct i2c_client *client, struct pmbus_driver_info *info) +{ + int ret; + + /* Set vout to direct format for rail1. */ + ret = mp2993_set_vout_format(client, 0, PB_VOUT_MODE_DIRECT); + if (ret < 0) + return ret; + + /* Set vout to direct format for rail2. */ + return mp2993_set_vout_format(client, 1, PB_VOUT_MODE_DIRECT); +} + +static int mp2993_read_word_data(struct i2c_client *client, int page, int phase, + int reg) +{ + int ret; + + switch (reg) { + case PMBUS_VOUT_OV_FAULT_LIMIT: + case PMBUS_VOUT_UV_FAULT_LIMIT: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + ret = DIV_ROUND_CLOSEST(ret * MP2993_VOUT_OVUV_UINT, MP2993_VOUT_OVUV_DIV); + break; + case PMBUS_OT_FAULT_LIMIT: + case PMBUS_OT_WARN_LIMIT: + /* + * The MP2993 ot fault limit value and ot warn limit value + * per rail are always the same, so only PMBUS_OT_FAULT_LIMIT + * and PMBUS_OT_WARN_LIMIT register in page 0 are defined to + * indicates the limit value. + */ + ret = pmbus_read_word_data(client, 0, phase, reg); + break; + case PMBUS_READ_VIN: + /* The MP2993 vin scale is (1/32V)/Lsb */ + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + ret = DIV_ROUND_CLOSEST((ret & GENMASK(9, 0)) * MP2993_READ_VIN_UINT, + MP2993_READ_VIN_DIV); + break; + case PMBUS_VIN_OV_FAULT_LIMIT: + case PMBUS_VIN_OV_WARN_LIMIT: + case PMBUS_VIN_UV_WARN_LIMIT: + case PMBUS_VIN_UV_FAULT_LIMIT: + /* The MP2993 vin limit scale is (1/8V)/Lsb */ + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + ret = DIV_ROUND_CLOSEST((ret & GENMASK(7, 0)) * MP2993_VIN_LIMIT_UINT, + MP2993_VIN_LIMIT_DIV); + break; + case PMBUS_READ_IOUT: + case PMBUS_READ_IIN: + case PMBUS_IIN_OC_WARN_LIMIT: + case PMBUS_IOUT_OC_FAULT_LIMIT: + case PMBUS_IOUT_OC_WARN_LIMIT: + case PMBUS_READ_VOUT: + case PMBUS_READ_PIN: + case PMBUS_READ_POUT: + case PMBUS_READ_TEMPERATURE_1: + ret = -ENODATA; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int mp2993_write_word_data(struct i2c_client *client, int page, int reg, + u16 word) +{ + int ret; + + switch (reg) { + case PMBUS_VOUT_OV_FAULT_LIMIT: + case PMBUS_VOUT_UV_FAULT_LIMIT: + ret = DIV_ROUND_CLOSEST(word * MP2993_VOUT_OVUV_DIV, MP2993_VOUT_OVUV_UINT); + ret = pmbus_write_word_data(client, 0, reg, ret); + break; + case PMBUS_OT_FAULT_LIMIT: + case PMBUS_OT_WARN_LIMIT: + /* + * The MP2993 ot fault limit value and ot warn limit value + * per rail are always the same, so only PMBUS_OT_FAULT_LIMIT + * and PMBUS_OT_WARN_LIMIT register in page 0 are defined to + * config the ot limit value. + */ + ret = pmbus_write_word_data(client, 0, reg, word); + break; + case PMBUS_VIN_OV_FAULT_LIMIT: + case PMBUS_VIN_OV_WARN_LIMIT: + case PMBUS_VIN_UV_WARN_LIMIT: + case PMBUS_VIN_UV_FAULT_LIMIT: + /* The MP2993 vin limit scale is (1/8V)/Lsb */ + ret = pmbus_write_word_data(client, 0, reg, + DIV_ROUND_CLOSEST(word * MP2993_VIN_LIMIT_DIV, + MP2993_VIN_LIMIT_UINT)); + break; + case PMBUS_IIN_OC_WARN_LIMIT: + /* + * The PMBUS_IIN_OC_WARN_LIMIT of MP2993 is linear11 format, + * and the exponent is a constant value(5'b00000), so the + * exponent of word parameter should be converted to 5'b00000. + */ + ret = pmbus_write_word_data(client, page, reg, + mp2993_linear11_exponent_transfer(word, 0x00)); + break; + // + case PMBUS_IOUT_OC_FAULT_LIMIT: + case PMBUS_IOUT_OC_WARN_LIMIT: + /* + * The PMBUS_IOUT_OC_FAULT_LIMIT and PMBUS_IOUT_OC_WARN_LIMIT + * of MP2993 can be regarded as linear11 format, and the + * exponent is a 5'b00001 or 5'b00000. To ensure a larger + * range of limit value, so the exponent of word parameter + * should be converted to 5'b00001. + */ + ret = pmbus_write_word_data(client, page, reg, + mp2993_linear11_exponent_transfer(word, 0x01)); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static struct pmbus_driver_info mp2993_info = { + .pages = MP2993_PAGE_NUM, + .format[PSC_VOLTAGE_IN] = direct, + .format[PSC_CURRENT_IN] = linear, + .format[PSC_CURRENT_OUT] = linear, + .format[PSC_TEMPERATURE] = direct, + .format[PSC_POWER] = linear, + .format[PSC_VOLTAGE_OUT] = direct, + + .m[PSC_VOLTAGE_OUT] = 1, + .R[PSC_VOLTAGE_OUT] = 3, + .b[PSC_VOLTAGE_OUT] = 0, + + .m[PSC_VOLTAGE_IN] = 1, + .R[PSC_VOLTAGE_IN] = 0, + .b[PSC_VOLTAGE_IN] = 0, + + .m[PSC_TEMPERATURE] = 1, + .R[PSC_TEMPERATURE] = 0, + .b[PSC_TEMPERATURE] = 0, + + .func[0] = MP2993_RAIL1_FUNC, + .func[1] = MP2993_RAIL2_FUNC, + .read_word_data = mp2993_read_word_data, + .write_word_data = mp2993_write_word_data, + .identify = mp2993_identify, +}; + +static int mp2993_probe(struct i2c_client *client) +{ + return pmbus_do_probe(client, &mp2993_info); +} + +static const struct i2c_device_id mp2993_id[] = { + {"mp2993", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, mp2993_id); + +static const struct of_device_id __maybe_unused mp2993_of_match[] = { + {.compatible = "mps,mp2993"}, + {} +}; +MODULE_DEVICE_TABLE(of, mp2993_of_match); + +static struct i2c_driver mp2993_driver = { + .driver = { + .name = "mp2993", + .of_match_table = mp2993_of_match, + }, + .probe = mp2993_probe, + .id_table = mp2993_id, +}; + +module_i2c_driver(mp2993_driver); + +MODULE_AUTHOR("Noah Wang "); +MODULE_DESCRIPTION("PMBus driver for MPS MP2993"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); -- cgit From dc5abc2ff0ee4279a6c0351674cd24745216c4db Mon Sep 17 00:00:00 2001 From: Noah Wang Date: Tue, 11 Jun 2024 18:14:17 +0800 Subject: hwmon: add MP9941 driver Add support for MPS step-down converter mp9941. This driver exposes telemetry and limit value readings and writtings. Signed-off-by: Noah Wang Link: https://lore.kernel.org/r/SEYPR04MB648294005D55F70736B519F6FAC72@SEYPR04MB6482.apcprd04.prod.outlook.com [groeck: Include bitfield.h (for FIELD_PREP) and bits.h (for GENMASK)] Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/Kconfig | 9 ++ drivers/hwmon/pmbus/Makefile | 1 + drivers/hwmon/pmbus/mp9941.c | 319 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 329 insertions(+) create mode 100644 drivers/hwmon/pmbus/mp9941.c (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index d875d31ce84c..7d32cfc19820 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -380,6 +380,15 @@ config SENSORS_MP5990 This driver can also be built as a module. If so, the module will be called mp5990. +config SENSORS_MP9941 + tristate "MPS MP9941" + help + If you say yes here you get hardware monitoring support for MPS + MP9941. + + This driver can also be built as a module. If so, the module will + be called mp9941. + config SENSORS_MPQ7932_REGULATOR bool "Regulator support for MPQ7932" depends on SENSORS_MPQ7932 && REGULATOR diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile index 312d3f0c0540..6c7177fde355 100644 --- a/drivers/hwmon/pmbus/Makefile +++ b/drivers/hwmon/pmbus/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_SENSORS_MP2975) += mp2975.o obj-$(CONFIG_SENSORS_MP2993) += mp2993.o obj-$(CONFIG_SENSORS_MP5023) += mp5023.o obj-$(CONFIG_SENSORS_MP5990) += mp5990.o +obj-$(CONFIG_SENSORS_MP9941) += mp9941.o obj-$(CONFIG_SENSORS_MPQ7932) += mpq7932.o obj-$(CONFIG_SENSORS_MPQ8785) += mpq8785.o obj-$(CONFIG_SENSORS_PLI1209BC) += pli1209bc.o diff --git a/drivers/hwmon/pmbus/mp9941.c b/drivers/hwmon/pmbus/mp9941.c new file mode 100644 index 000000000000..543955cfce67 --- /dev/null +++ b/drivers/hwmon/pmbus/mp9941.c @@ -0,0 +1,319 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for MPS Multi-phase Digital VR Controllers(MP9941) + */ + +#include +#include +#include +#include +#include +#include "pmbus.h" + +/* + * Vender specific registers. The MFR_ICC_MAX(0x02) is used to + * config the iin scale. The MFR_RESO_SET(0xC7) is used to + * config the vout format. The MFR_VR_MULTI_CONFIG_R1(0x0D) is + * used to identify the vout vid step. + */ +#define MFR_ICC_MAX 0x02 +#define MFR_RESO_SET 0xC7 +#define MFR_VR_MULTI_CONFIG_R1 0x0D + +#define MP9941_VIN_LIMIT_UINT 1 +#define MP9941_VIN_LIMIT_DIV 8 +#define MP9941_READ_VIN_UINT 1 +#define MP9941_READ_VIN_DIV 32 + +#define MP9941_PAGE_NUM 1 + +#define MP9941_RAIL1_FUNC (PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | \ + PMBUS_HAVE_IOUT | PMBUS_HAVE_POUT | \ + PMBUS_HAVE_TEMP | PMBUS_HAVE_PIN | \ + PMBUS_HAVE_IIN | \ + PMBUS_HAVE_STATUS_VOUT | \ + PMBUS_HAVE_STATUS_IOUT | \ + PMBUS_HAVE_STATUS_TEMP | \ + PMBUS_HAVE_STATUS_INPUT) + +struct mp9941_data { + struct pmbus_driver_info info; + int vid_resolution; +}; + +#define to_mp9941_data(x) container_of(x, struct mp9941_data, info) + +static int mp9941_set_vout_format(struct i2c_client *client) +{ + int ret; + + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0); + if (ret < 0) + return ret; + + ret = i2c_smbus_read_word_data(client, MFR_RESO_SET); + if (ret < 0) + return ret; + + /* + * page = 0, MFR_RESO_SET[7:6] defines the vout format + * 2'b11 set the vout format as direct + */ + ret = (ret & ~GENMASK(7, 6)) | FIELD_PREP(GENMASK(7, 6), 3); + + return i2c_smbus_write_word_data(client, MFR_RESO_SET, ret); +} + +static int +mp9941_identify_vid_resolution(struct i2c_client *client, struct pmbus_driver_info *info) +{ + struct mp9941_data *data = to_mp9941_data(info); + int ret; + + /* + * page = 2, MFR_VR_MULTI_CONFIG_R1[4:4] defines rail1 vid step value + * 1'b0 represents the vid step value is 10mV + * 1'b1 represents the vid step value is 5mV + */ + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 2); + if (ret < 0) + return ret; + + ret = i2c_smbus_read_word_data(client, MFR_VR_MULTI_CONFIG_R1); + if (ret < 0) + return ret; + + if (FIELD_GET(GENMASK(4, 4), ret)) + data->vid_resolution = 5; + else + data->vid_resolution = 10; + + return 0; +} + +static int mp9941_identify_iin_scale(struct i2c_client *client) +{ + int ret; + + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0); + if (ret < 0) + return ret; + + ret = i2c_smbus_read_word_data(client, MFR_RESO_SET); + if (ret < 0) + return ret; + + ret = (ret & ~GENMASK(3, 2)) | FIELD_PREP(GENMASK(3, 2), 0); + + ret = i2c_smbus_write_word_data(client, MFR_RESO_SET, ret); + if (ret < 0) + return ret; + + /* + * page = 2, MFR_ICC_MAX[15:13] defines the iin scale + * 3'b000 set the iout scale as 0.5A/Lsb + */ + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 2); + if (ret < 0) + return ret; + + ret = i2c_smbus_read_word_data(client, MFR_ICC_MAX); + if (ret < 0) + return ret; + + ret = (ret & ~GENMASK(15, 13)) | FIELD_PREP(GENMASK(15, 13), 0); + + return i2c_smbus_write_word_data(client, MFR_ICC_MAX, ret); +} + +static int mp9941_identify(struct i2c_client *client, struct pmbus_driver_info *info) +{ + int ret; + + ret = mp9941_identify_iin_scale(client); + if (ret < 0) + return ret; + + ret = mp9941_identify_vid_resolution(client, info); + if (ret < 0) + return ret; + + return mp9941_set_vout_format(client); +} + +static int mp9941_read_word_data(struct i2c_client *client, int page, int phase, + int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct mp9941_data *data = to_mp9941_data(info); + int ret; + + switch (reg) { + case PMBUS_READ_VIN: + /* The MP9941 vin scale is (1/32V)/Lsb */ + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + ret = DIV_ROUND_CLOSEST((ret & GENMASK(9, 0)) * MP9941_READ_VIN_UINT, + MP9941_READ_VIN_DIV); + break; + case PMBUS_READ_IIN: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + ret = ret & GENMASK(10, 0); + break; + case PMBUS_VIN_OV_FAULT_LIMIT: + /* The MP9941 vin ov limit scale is (1/8V)/Lsb */ + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + ret = DIV_ROUND_CLOSEST((ret & GENMASK(7, 0)) * MP9941_VIN_LIMIT_UINT, + MP9941_VIN_LIMIT_DIV); + break; + case PMBUS_IIN_OC_WARN_LIMIT: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + ret = ret & GENMASK(7, 0); + break; + case PMBUS_VOUT_UV_FAULT_LIMIT: + case PMBUS_MFR_VOUT_MIN: + case PMBUS_MFR_VOUT_MAX: + /* + * The vout scale is set to 1mV/Lsb(using r/m/b scale). + * But the vout uv limit and vout max/min scale is 1VID/Lsb, + * so the vout uv limit and vout max/min value should be + * multiplied by vid resolution. + */ + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + ret = ret * data->vid_resolution; + break; + case PMBUS_READ_IOUT: + case PMBUS_READ_POUT: + case PMBUS_READ_TEMPERATURE_1: + case PMBUS_READ_VOUT: + case PMBUS_READ_PIN: + case PMBUS_OT_FAULT_LIMIT: + case PMBUS_OT_WARN_LIMIT: + ret = -ENODATA; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int mp9941_write_word_data(struct i2c_client *client, int page, int reg, + u16 word) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct mp9941_data *data = to_mp9941_data(info); + int ret; + + switch (reg) { + case PMBUS_VIN_OV_FAULT_LIMIT: + /* The MP9941 vin ov limit scale is (1/8V)/Lsb */ + ret = pmbus_write_word_data(client, page, reg, + DIV_ROUND_CLOSEST(word * MP9941_VIN_LIMIT_DIV, + MP9941_VIN_LIMIT_UINT)); + break; + case PMBUS_VOUT_UV_FAULT_LIMIT: + case PMBUS_MFR_VOUT_MIN: + case PMBUS_MFR_VOUT_MAX: + ret = pmbus_write_word_data(client, page, reg, + DIV_ROUND_CLOSEST(word, data->vid_resolution)); + break; + case PMBUS_IIN_OC_WARN_LIMIT: + case PMBUS_OT_FAULT_LIMIT: + case PMBUS_OT_WARN_LIMIT: + ret = -ENODATA; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static const struct pmbus_driver_info mp9941_info = { + .pages = MP9941_PAGE_NUM, + .format[PSC_VOLTAGE_IN] = direct, + .format[PSC_CURRENT_IN] = direct, + .format[PSC_CURRENT_OUT] = linear, + .format[PSC_POWER] = linear, + .format[PSC_TEMPERATURE] = direct, + .format[PSC_VOLTAGE_OUT] = direct, + + .m[PSC_TEMPERATURE] = 1, + .R[PSC_TEMPERATURE] = 0, + .b[PSC_TEMPERATURE] = 0, + + .m[PSC_VOLTAGE_IN] = 1, + .R[PSC_VOLTAGE_IN] = 0, + .b[PSC_VOLTAGE_IN] = 0, + + .m[PSC_CURRENT_IN] = 2, + .R[PSC_CURRENT_IN] = 0, + .b[PSC_CURRENT_IN] = 0, + + .m[PSC_VOLTAGE_OUT] = 1, + .R[PSC_VOLTAGE_OUT] = 3, + .b[PSC_VOLTAGE_OUT] = 0, + + .func[0] = MP9941_RAIL1_FUNC, + .read_word_data = mp9941_read_word_data, + .write_word_data = mp9941_write_word_data, + .identify = mp9941_identify, +}; + +static int mp9941_probe(struct i2c_client *client) +{ + struct mp9941_data *data; + + data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + memcpy(&data->info, &mp9941_info, sizeof(mp9941_info)); + + return pmbus_do_probe(client, &data->info); +} + +static const struct i2c_device_id mp9941_id[] = { + {"mp9941", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, mp9941_id); + +static const struct of_device_id __maybe_unused mp9941_of_match[] = { + {.compatible = "mps,mp9941"}, + {} +}; +MODULE_DEVICE_TABLE(of, mp9941_of_match); + +static struct i2c_driver mp9941_driver = { + .driver = { + .name = "mp9941", + .of_match_table = mp9941_of_match, + }, + .probe = mp9941_probe, + .id_table = mp9941_id, +}; + +module_i2c_driver(mp9941_driver); + +MODULE_AUTHOR("Noah Wang "); +MODULE_DESCRIPTION("PMBus driver for MPS MP9941"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); -- cgit From 52115fc32905c0bacc244f1dace9a138518df0de Mon Sep 17 00:00:00 2001 From: Thomas Weißschuh Date: Fri, 14 Jun 2024 01:01:42 +0200 Subject: hwmon: (core) Make hwmon_class const MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that the driver core allows for struct class to be in read-only memory, mark hwmon_class as const. Signed-off-by: Thomas Weißschuh Link: https://lore.kernel.org/r/20240614-class-const-hwmon-v1-1-27b910d06a90@weissschuh.net Signed-off-by: Guenter Roeck --- drivers/hwmon/hwmon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index 8aee253b5829..a362080d41fa 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -137,7 +137,7 @@ static void hwmon_dev_release(struct device *dev) kfree(hwdev); } -static struct class hwmon_class = { +static const struct class hwmon_class = { .name = "hwmon", .dev_groups = hwmon_dev_attr_groups, .dev_release = hwmon_dev_release, -- cgit From 0f33272b60ed87a2609542a6851361f35b83c6ba Mon Sep 17 00:00:00 2001 From: Naresh Solanki Date: Fri, 14 Jun 2024 11:25:30 +0530 Subject: hwmon: (max6639) : Update hwmon init using info structure Update hwmon init with info instead of group. The hwmon info structure in more flexible to describe sensor attribute & easy to maintian. Signed-off-by: Naresh Solanki Link: https://lore.kernel.org/r/20240614055533.2735210-1-naresh.solanki@9elements.com [groeck: Replace clamp_val() with range check when writing pwmX_input] Signed-off-by: Guenter Roeck --- drivers/hwmon/max6639.c | 458 ++++++++++++++++++++++++++++++------------------ 1 file changed, 287 insertions(+), 171 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/max6639.c b/drivers/hwmon/max6639.c index cbb595fe47aa..254074598666 100644 --- a/drivers/hwmon/max6639.c +++ b/drivers/hwmon/max6639.c @@ -21,6 +21,7 @@ #include #include #include +#include /* Addresses to scan */ static const unsigned short normal_i2c[] = { 0x2c, 0x2e, 0x2f, I2C_CLIENT_END }; @@ -55,13 +56,17 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2e, 0x2f, I2C_CLIENT_END }; #define MAX6639_GCONFIG_PWM_FREQ_HI 0x08 #define MAX6639_FAN_CONFIG1_PWM 0x80 - +#define MAX6639_FAN_CONFIG3_FREQ_MASK 0x03 #define MAX6639_FAN_CONFIG3_THERM_FULL_SPEED 0x40 #define MAX6639_NUM_CHANNELS 2 static const int rpm_ranges[] = { 2000, 4000, 8000, 16000 }; +/* Supported PWM frequency */ +static const unsigned int freq_table[] = { 20, 33, 50, 100, 5000, 8333, 12500, + 25000 }; + #define FAN_FROM_REG(val, rpm_range) ((val) == 0 || (val) == 255 ? \ 0 : (rpm_ranges[rpm_range] * 30) / (val)) #define TEMP_LIMIT_TO_REG(val) clamp_val((val) / 1000, 0, 255) @@ -73,19 +78,16 @@ struct max6639_data { struct regmap *regmap; /* Register values initialized only once */ - u8 ppr; /* Pulses per rotation 0..3 for 1..4 ppr */ - u8 rpm_range; /* Index in above rpm_ranges table */ + u8 ppr[MAX6639_NUM_CHANNELS]; /* Pulses per rotation 0..3 for 1..4 ppr */ + u8 rpm_range[MAX6639_NUM_CHANNELS]; /* Index in above rpm_ranges table */ /* Optional regulator for FAN supply */ struct regulator *reg; }; -static ssize_t temp_input_show(struct device *dev, - struct device_attribute *dev_attr, char *buf) +static int max6639_temp_read_input(struct device *dev, int channel, long *temp) { - long temp; struct max6639_data *data = dev_get_drvdata(dev); - struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); unsigned int val; int res; @@ -93,251 +95,365 @@ static ssize_t temp_input_show(struct device *dev, * Lock isn't needed as MAX6639_REG_TEMP wpnt change for at least 250ms after reading * MAX6639_REG_TEMP_EXT */ - res = regmap_read(data->regmap, MAX6639_REG_TEMP_EXT(attr->index), &val); + res = regmap_read(data->regmap, MAX6639_REG_TEMP_EXT(channel), &val); if (res < 0) return res; - temp = val >> 5; - res = regmap_read(data->regmap, MAX6639_REG_TEMP(attr->index), &val); + *temp = val >> 5; + res = regmap_read(data->regmap, MAX6639_REG_TEMP(channel), &val); if (res < 0) return res; - temp |= val << 3; - temp *= 125; + *temp |= val << 3; + *temp *= 125; - return sprintf(buf, "%ld\n", temp); + return 0; } -static ssize_t temp_fault_show(struct device *dev, - struct device_attribute *dev_attr, char *buf) +static int max6639_temp_read_fault(struct device *dev, int channel, long *fault) { struct max6639_data *data = dev_get_drvdata(dev); - struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); unsigned int val; int res; - res = regmap_read(data->regmap, MAX6639_REG_TEMP_EXT(attr->index), &val); + res = regmap_read(data->regmap, MAX6639_REG_TEMP_EXT(channel), &val); if (res < 0) return res; - return sprintf(buf, "%d\n", val & 1); + *fault = val & 1; + + return 0; } -static ssize_t temp_max_show(struct device *dev, - struct device_attribute *dev_attr, char *buf) +static int max6639_temp_read_max(struct device *dev, int channel, long *max) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); struct max6639_data *data = dev_get_drvdata(dev); unsigned int val; int res; - res = regmap_read(data->regmap, MAX6639_REG_THERM_LIMIT(attr->index), &val); + res = regmap_read(data->regmap, MAX6639_REG_THERM_LIMIT(channel), &val); if (res < 0) return res; - return sprintf(buf, "%d\n", (val * 1000)); + *max = (long)val * 1000; + + return 0; } -static ssize_t temp_max_store(struct device *dev, - struct device_attribute *dev_attr, - const char *buf, size_t count) +static int max6639_temp_read_crit(struct device *dev, int channel, long *crit) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); struct max6639_data *data = dev_get_drvdata(dev); - unsigned long val; + unsigned int val; int res; - res = kstrtoul(buf, 10, &val); - if (res) + res = regmap_read(data->regmap, MAX6639_REG_ALERT_LIMIT(channel), &val); + if (res < 0) return res; - regmap_write(data->regmap, MAX6639_REG_THERM_LIMIT(attr->index), - TEMP_LIMIT_TO_REG(val)); - return count; + *crit = (long)val * 1000; + + return 0; } -static ssize_t temp_crit_show(struct device *dev, - struct device_attribute *dev_attr, char *buf) +static int max6639_temp_read_emergency(struct device *dev, int channel, long *emerg) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); struct max6639_data *data = dev_get_drvdata(dev); unsigned int val; int res; - res = regmap_read(data->regmap, MAX6639_REG_ALERT_LIMIT(attr->index), &val); + res = regmap_read(data->regmap, MAX6639_REG_OT_LIMIT(channel), &val); if (res < 0) return res; - return sprintf(buf, "%d\n", (val * 1000)); + *emerg = (long)val * 1000; + + return 0; } -static ssize_t temp_crit_store(struct device *dev, - struct device_attribute *dev_attr, - const char *buf, size_t count) +static int max6639_get_status(struct device *dev, unsigned int *status) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); struct max6639_data *data = dev_get_drvdata(dev); - unsigned long val; + unsigned int val; int res; - res = kstrtoul(buf, 10, &val); - if (res) + res = regmap_read(data->regmap, MAX6639_REG_STATUS, &val); + if (res < 0) return res; - regmap_write(data->regmap, MAX6639_REG_ALERT_LIMIT(attr->index), - TEMP_LIMIT_TO_REG(val)); - return count; + *status = val; + + return 0; } -static ssize_t temp_emergency_show(struct device *dev, - struct device_attribute *dev_attr, - char *buf) +static int max6639_temp_set_max(struct max6639_data *data, int channel, long val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); - struct max6639_data *data = dev_get_drvdata(dev); - unsigned int val; int res; - res = regmap_read(data->regmap, MAX6639_REG_OT_LIMIT(attr->index), &val); - if (res < 0) - return res; - - return sprintf(buf, "%d\n", (val * 1000)); + res = regmap_write(data->regmap, MAX6639_REG_THERM_LIMIT(channel), + TEMP_LIMIT_TO_REG(val)); + return res; } -static ssize_t temp_emergency_store(struct device *dev, - struct device_attribute *dev_attr, - const char *buf, size_t count) +static int max6639_temp_set_crit(struct max6639_data *data, int channel, long val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); - struct max6639_data *data = dev_get_drvdata(dev); - unsigned long val; int res; - res = kstrtoul(buf, 10, &val); - if (res) - return res; + res = regmap_write(data->regmap, MAX6639_REG_ALERT_LIMIT(channel), TEMP_LIMIT_TO_REG(val)); + + return res; +} + +static int max6639_temp_set_emergency(struct max6639_data *data, int channel, long val) +{ + int res; - regmap_write(data->regmap, MAX6639_REG_OT_LIMIT(attr->index), TEMP_LIMIT_TO_REG(val)); + res = regmap_write(data->regmap, MAX6639_REG_OT_LIMIT(channel), TEMP_LIMIT_TO_REG(val)); - return count; + return res; } -static ssize_t pwm_show(struct device *dev, struct device_attribute *dev_attr, - char *buf) +static int max6639_read_fan(struct device *dev, u32 attr, int channel, + long *fan_val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); struct max6639_data *data = dev_get_drvdata(dev); unsigned int val; int res; - res = regmap_read(data->regmap, MAX6639_REG_TARGTDUTY(attr->index), &val); - if (res < 0) - return res; + switch (attr) { + case hwmon_fan_input: + res = regmap_read(data->regmap, MAX6639_REG_FAN_CNT(channel), &val); + if (res < 0) + return res; + *fan_val = FAN_FROM_REG(val, data->rpm_range[channel]); + return 0; + case hwmon_fan_fault: + res = max6639_get_status(dev, &val); + if (res < 0) + return res; + *fan_val = !!(val & BIT(1 - channel)); + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int max6639_set_ppr(struct max6639_data *data, int channel, u8 ppr) +{ + /* Decrement the PPR value and shift left by 6 to match the register format */ + return regmap_write(data->regmap, MAX6639_REG_FAN_PPR(channel), ppr-- << 6); +} - return sprintf(buf, "%d\n", val * 255 / 120); +static umode_t max6639_fan_is_visible(const void *_data, u32 attr, int channel) +{ + switch (attr) { + case hwmon_fan_input: + case hwmon_fan_fault: + return 0444; + case hwmon_fan_pulses: + return 0644; + default: + return 0; + } } -static ssize_t pwm_store(struct device *dev, - struct device_attribute *dev_attr, const char *buf, - size_t count) +static int max6639_read_pwm(struct device *dev, u32 attr, int channel, + long *pwm_val) { - struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); struct max6639_data *data = dev_get_drvdata(dev); - unsigned long val; + unsigned int val; int res; - res = kstrtoul(buf, 10, &val); - if (res) - return res; + switch (attr) { + case hwmon_pwm_input: + res = regmap_read(data->regmap, MAX6639_REG_TARGTDUTY(channel), &val); + if (res < 0) + return res; + *pwm_val = val * 255 / 120; + return 0; + default: + return -EOPNOTSUPP; + } +} - val = clamp_val(val, 0, 255); +static int max6639_write_pwm(struct device *dev, u32 attr, int channel, + long val) +{ + struct max6639_data *data = dev_get_drvdata(dev); + int err; - regmap_write(data->regmap, MAX6639_REG_TARGTDUTY(attr->index), val * 120 / 255); + switch (attr) { + case hwmon_pwm_input: + if (val < 0 || val > 255) + return -EINVAL; + err = regmap_write(data->regmap, MAX6639_REG_TARGTDUTY(channel), + val * 120 / 255); + return err; + default: + return -EOPNOTSUPP; + } +} - return count; +static umode_t max6639_pwm_is_visible(const void *_data, u32 attr, int channel) +{ + switch (attr) { + case hwmon_pwm_input: + return 0644; + default: + return 0; + } } -static ssize_t fan_input_show(struct device *dev, - struct device_attribute *dev_attr, char *buf) +static int max6639_read_temp(struct device *dev, u32 attr, int channel, + long *val) { - struct max6639_data *data = dev_get_drvdata(dev); - struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); - unsigned int val; + unsigned int status; int res; - res = regmap_read(data->regmap, MAX6639_REG_FAN_CNT(attr->index), &val); - if (res < 0) + switch (attr) { + case hwmon_temp_input: + res = max6639_temp_read_input(dev, channel, val); return res; - - return sprintf(buf, "%d\n", FAN_FROM_REG(val, data->rpm_range)); + case hwmon_temp_fault: + res = max6639_temp_read_fault(dev, channel, val); + return res; + case hwmon_temp_max: + res = max6639_temp_read_max(dev, channel, val); + return res; + case hwmon_temp_crit: + res = max6639_temp_read_crit(dev, channel, val); + return res; + case hwmon_temp_emergency: + res = max6639_temp_read_emergency(dev, channel, val); + return res; + case hwmon_temp_max_alarm: + res = max6639_get_status(dev, &status); + if (res < 0) + return res; + *val = !!(status & BIT(3 - channel)); + return 0; + case hwmon_temp_crit_alarm: + res = max6639_get_status(dev, &status); + if (res < 0) + return res; + *val = !!(status & BIT(7 - channel)); + return 0; + case hwmon_temp_emergency_alarm: + res = max6639_get_status(dev, &status); + if (res < 0) + return res; + *val = !!(status & BIT(5 - channel)); + return 0; + default: + return -EOPNOTSUPP; + } } -static ssize_t alarm_show(struct device *dev, - struct device_attribute *dev_attr, char *buf) +static int max6639_write_temp(struct device *dev, u32 attr, int channel, + long val) { struct max6639_data *data = dev_get_drvdata(dev); - struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); - unsigned int val; - int res; - res = regmap_read(data->regmap, MAX6639_REG_STATUS, &val); - if (res < 0) - return res; + switch (attr) { + case hwmon_temp_max: + return max6639_temp_set_max(data, channel, val); + case hwmon_temp_crit: + return max6639_temp_set_crit(data, channel, val); + case hwmon_temp_emergency: + return max6639_temp_set_emergency(data, channel, val); + default: + return -EOPNOTSUPP; + } +} - return sprintf(buf, "%d\n", !!(val & (1 << attr->index))); +static umode_t max6639_temp_is_visible(const void *_data, u32 attr, int channel) +{ + switch (attr) { + case hwmon_temp_input: + case hwmon_temp_fault: + case hwmon_temp_max_alarm: + case hwmon_temp_crit_alarm: + case hwmon_temp_emergency_alarm: + return 0444; + case hwmon_temp_max: + case hwmon_temp_crit: + case hwmon_temp_emergency: + return 0644; + default: + return 0; + } } -static SENSOR_DEVICE_ATTR_RO(temp1_input, temp_input, 0); -static SENSOR_DEVICE_ATTR_RO(temp2_input, temp_input, 1); -static SENSOR_DEVICE_ATTR_RO(temp1_fault, temp_fault, 0); -static SENSOR_DEVICE_ATTR_RO(temp2_fault, temp_fault, 1); -static SENSOR_DEVICE_ATTR_RW(temp1_max, temp_max, 0); -static SENSOR_DEVICE_ATTR_RW(temp2_max, temp_max, 1); -static SENSOR_DEVICE_ATTR_RW(temp1_crit, temp_crit, 0); -static SENSOR_DEVICE_ATTR_RW(temp2_crit, temp_crit, 1); -static SENSOR_DEVICE_ATTR_RW(temp1_emergency, temp_emergency, 0); -static SENSOR_DEVICE_ATTR_RW(temp2_emergency, temp_emergency, 1); -static SENSOR_DEVICE_ATTR_RW(pwm1, pwm, 0); -static SENSOR_DEVICE_ATTR_RW(pwm2, pwm, 1); -static SENSOR_DEVICE_ATTR_RO(fan1_input, fan_input, 0); -static SENSOR_DEVICE_ATTR_RO(fan2_input, fan_input, 1); -static SENSOR_DEVICE_ATTR_RO(fan1_fault, alarm, 1); -static SENSOR_DEVICE_ATTR_RO(fan2_fault, alarm, 0); -static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, alarm, 3); -static SENSOR_DEVICE_ATTR_RO(temp2_max_alarm, alarm, 2); -static SENSOR_DEVICE_ATTR_RO(temp1_crit_alarm, alarm, 7); -static SENSOR_DEVICE_ATTR_RO(temp2_crit_alarm, alarm, 6); -static SENSOR_DEVICE_ATTR_RO(temp1_emergency_alarm, alarm, 5); -static SENSOR_DEVICE_ATTR_RO(temp2_emergency_alarm, alarm, 4); - - -static struct attribute *max6639_attrs[] = { - &sensor_dev_attr_temp1_input.dev_attr.attr, - &sensor_dev_attr_temp2_input.dev_attr.attr, - &sensor_dev_attr_temp1_fault.dev_attr.attr, - &sensor_dev_attr_temp2_fault.dev_attr.attr, - &sensor_dev_attr_temp1_max.dev_attr.attr, - &sensor_dev_attr_temp2_max.dev_attr.attr, - &sensor_dev_attr_temp1_crit.dev_attr.attr, - &sensor_dev_attr_temp2_crit.dev_attr.attr, - &sensor_dev_attr_temp1_emergency.dev_attr.attr, - &sensor_dev_attr_temp2_emergency.dev_attr.attr, - &sensor_dev_attr_pwm1.dev_attr.attr, - &sensor_dev_attr_pwm2.dev_attr.attr, - &sensor_dev_attr_fan1_input.dev_attr.attr, - &sensor_dev_attr_fan2_input.dev_attr.attr, - &sensor_dev_attr_fan1_fault.dev_attr.attr, - &sensor_dev_attr_fan2_fault.dev_attr.attr, - &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, - &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, - &sensor_dev_attr_temp1_emergency_alarm.dev_attr.attr, - &sensor_dev_attr_temp2_emergency_alarm.dev_attr.attr, +static int max6639_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + switch (type) { + case hwmon_fan: + return max6639_read_fan(dev, attr, channel, val); + case hwmon_pwm: + return max6639_read_pwm(dev, attr, channel, val); + case hwmon_temp: + return max6639_read_temp(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} + +static int max6639_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + switch (type) { + case hwmon_pwm: + return max6639_write_pwm(dev, attr, channel, val); + case hwmon_temp: + return max6639_write_temp(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} + +static umode_t max6639_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_fan: + return max6639_fan_is_visible(data, attr, channel); + case hwmon_pwm: + return max6639_pwm_is_visible(data, attr, channel); + case hwmon_temp: + return max6639_temp_is_visible(data, attr, channel); + default: + return 0; + } +} + +static const struct hwmon_channel_info * const max6639_info[] = { + HWMON_CHANNEL_INFO(fan, + HWMON_F_INPUT | HWMON_F_FAULT, + HWMON_F_INPUT | HWMON_F_FAULT), + HWMON_CHANNEL_INFO(pwm, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT), + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_FAULT | HWMON_T_MAX | HWMON_T_MAX_ALARM | + HWMON_T_CRIT | HWMON_T_CRIT_ALARM | HWMON_T_EMERGENCY | + HWMON_T_EMERGENCY_ALARM, + HWMON_T_INPUT | HWMON_T_FAULT | HWMON_T_MAX | HWMON_T_MAX_ALARM | + HWMON_T_CRIT | HWMON_T_CRIT_ALARM | HWMON_T_EMERGENCY | + HWMON_T_EMERGENCY_ALARM), NULL }; -ATTRIBUTE_GROUPS(max6639); + +static const struct hwmon_ops max6639_hwmon_ops = { + .is_visible = max6639_is_visible, + .read = max6639_read, + .write = max6639_write, +}; + +static const struct hwmon_chip_info max6639_chip_info = { + .ops = &max6639_hwmon_ops, + .info = max6639_info, +}; /* * returns respective index in rpm_ranges table @@ -355,11 +471,6 @@ static int rpm_range_to_reg(int range) return 1; /* default: 4000 RPM */ } -static int max6639_set_ppr(struct max6639_data *data, u8 channel, u8 ppr) -{ - return regmap_write(data->regmap, MAX6639_REG_FAN_PPR(channel), ppr << 6); -} - static int max6639_init_client(struct i2c_client *client, struct max6639_data *data) { @@ -380,30 +491,34 @@ static int max6639_init_client(struct i2c_client *client, ppr = max6639_info->ppr; else ppr = 2; - ppr -= 1; + + data->ppr[0] = ppr; + data->ppr[1] = ppr; if (max6639_info) rpm_range = rpm_range_to_reg(max6639_info->rpm_range); - data->rpm_range = rpm_range; + data->rpm_range[0] = rpm_range; + data->rpm_range[1] = rpm_range; for (i = 0; i < MAX6639_NUM_CHANNELS; i++) { - /* Set Fan pulse per revolution */ - err = max6639_set_ppr(data, i, ppr); + err = max6639_set_ppr(data, i, data->ppr[i]); if (err) return err; /* Fans config PWM, RPM */ err = regmap_write(data->regmap, MAX6639_REG_FAN_CONFIG1(i), - MAX6639_FAN_CONFIG1_PWM | rpm_range); + MAX6639_FAN_CONFIG1_PWM | data->rpm_range[i]); if (err) return err; /* Fans PWM polarity high by default */ - if (max6639_info && max6639_info->pwm_polarity == 0) - err = regmap_write(data->regmap, MAX6639_REG_FAN_CONFIG2a(i), 0x00); - else - err = regmap_write(data->regmap, MAX6639_REG_FAN_CONFIG2a(i), 0x02); + if (max6639_info) { + if (max6639_info->pwm_polarity == 0) + err = regmap_write(data->regmap, MAX6639_REG_FAN_CONFIG2a(i), 0x00); + else + err = regmap_write(data->regmap, MAX6639_REG_FAN_CONFIG2a(i), 0x02); + } if (err) return err; @@ -534,9 +649,10 @@ static int max6639_probe(struct i2c_client *client) if (err < 0) return err; - hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, - data, - max6639_groups); + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, + data, &max6639_chip_info, + NULL); + return PTR_ERR_OR_ZERO(hwmon_dev); } -- cgit From 842b4d98ae338522babab38347297f0932fa7afd Mon Sep 17 00:00:00 2001 From: Naresh Solanki Date: Fri, 14 Jun 2024 11:25:31 +0530 Subject: hwmon: (max6639) : Add additional hwmon attributes for fan and pwm Add additional attributes for fan & pwm i.e., fanY_pulses pwmY_freq Signed-off-by: Naresh Solanki Link: https://lore.kernel.org/r/20240614055533.2735210-2-naresh.solanki@9elements.com [groeck: Fix value range for fanX_pulses] Signed-off-by: Guenter Roeck --- drivers/hwmon/max6639.c | 90 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 86 insertions(+), 4 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/max6639.c b/drivers/hwmon/max6639.c index 254074598666..f54720d3d2ce 100644 --- a/drivers/hwmon/max6639.c +++ b/drivers/hwmon/max6639.c @@ -76,6 +76,7 @@ static const unsigned int freq_table[] = { 20, 33, 50, 100, 5000, 8333, 12500, */ struct max6639_data { struct regmap *regmap; + struct mutex update_lock; /* Register values initialized only once */ u8 ppr[MAX6639_NUM_CHANNELS]; /* Pulses per rotation 0..3 for 1..4 ppr */ @@ -232,6 +233,9 @@ static int max6639_read_fan(struct device *dev, u32 attr, int channel, return res; *fan_val = !!(val & BIT(1 - channel)); return 0; + case hwmon_fan_pulses: + *fan_val = data->ppr[channel]; + return 0; default: return -EOPNOTSUPP; } @@ -243,6 +247,33 @@ static int max6639_set_ppr(struct max6639_data *data, int channel, u8 ppr) return regmap_write(data->regmap, MAX6639_REG_FAN_PPR(channel), ppr-- << 6); } +static int max6639_write_fan(struct device *dev, u32 attr, int channel, + long val) +{ + struct max6639_data *data = dev_get_drvdata(dev); + int err; + + switch (attr) { + case hwmon_fan_pulses: + if (val <= 0 || val > 4) + return -EINVAL; + + mutex_lock(&data->update_lock); + /* Set Fan pulse per revolution */ + err = max6639_set_ppr(data, channel, val); + if (err < 0) { + mutex_unlock(&data->update_lock); + return err; + } + data->ppr[channel] = val; + + mutex_unlock(&data->update_lock); + return 0; + default: + return -EOPNOTSUPP; + } +} + static umode_t max6639_fan_is_visible(const void *_data, u32 attr, int channel) { switch (attr) { @@ -262,6 +293,7 @@ static int max6639_read_pwm(struct device *dev, u32 attr, int channel, struct max6639_data *data = dev_get_drvdata(dev); unsigned int val; int res; + u8 i; switch (attr) { case hwmon_pwm_input: @@ -270,6 +302,28 @@ static int max6639_read_pwm(struct device *dev, u32 attr, int channel, return res; *pwm_val = val * 255 / 120; return 0; + case hwmon_pwm_freq: + mutex_lock(&data->update_lock); + res = regmap_read(data->regmap, MAX6639_REG_FAN_CONFIG3(channel), &val); + if (res < 0) { + mutex_unlock(&data->update_lock); + return res; + } + i = val & MAX6639_FAN_CONFIG3_FREQ_MASK; + + res = regmap_read(data->regmap, MAX6639_REG_GCONFIG, &val); + if (res < 0) { + mutex_unlock(&data->update_lock); + return res; + } + + if (val & MAX6639_GCONFIG_PWM_FREQ_HI) + i |= 0x4; + i &= 0x7; + *pwm_val = freq_table[i]; + + mutex_unlock(&data->update_lock); + return 0; default: return -EOPNOTSUPP; } @@ -280,6 +334,7 @@ static int max6639_write_pwm(struct device *dev, u32 attr, int channel, { struct max6639_data *data = dev_get_drvdata(dev); int err; + u8 i; switch (attr) { case hwmon_pwm_input: @@ -288,6 +343,28 @@ static int max6639_write_pwm(struct device *dev, u32 attr, int channel, err = regmap_write(data->regmap, MAX6639_REG_TARGTDUTY(channel), val * 120 / 255); return err; + case hwmon_pwm_freq: + val = clamp_val(val, 0, 25000); + + i = find_closest(val, freq_table, ARRAY_SIZE(freq_table)); + + mutex_lock(&data->update_lock); + err = regmap_update_bits(data->regmap, MAX6639_REG_FAN_CONFIG3(channel), + MAX6639_FAN_CONFIG3_FREQ_MASK, i); + if (err < 0) { + mutex_unlock(&data->update_lock); + return err; + } + + if (i >> 2) + err = regmap_set_bits(data->regmap, MAX6639_REG_GCONFIG, + MAX6639_GCONFIG_PWM_FREQ_HI); + else + err = regmap_clear_bits(data->regmap, MAX6639_REG_GCONFIG, + MAX6639_GCONFIG_PWM_FREQ_HI); + + mutex_unlock(&data->update_lock); + return err; default: return -EOPNOTSUPP; } @@ -297,6 +374,7 @@ static umode_t max6639_pwm_is_visible(const void *_data, u32 attr, int channel) { switch (attr) { case hwmon_pwm_input: + case hwmon_pwm_freq: return 0644; default: return 0; @@ -402,6 +480,8 @@ static int max6639_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long val) { switch (type) { + case hwmon_fan: + return max6639_write_fan(dev, attr, channel, val); case hwmon_pwm: return max6639_write_pwm(dev, attr, channel, val); case hwmon_temp: @@ -429,11 +509,11 @@ static umode_t max6639_is_visible(const void *data, static const struct hwmon_channel_info * const max6639_info[] = { HWMON_CHANNEL_INFO(fan, - HWMON_F_INPUT | HWMON_F_FAULT, - HWMON_F_INPUT | HWMON_F_FAULT), + HWMON_F_INPUT | HWMON_F_FAULT | HWMON_F_PULSES, + HWMON_F_INPUT | HWMON_F_FAULT | HWMON_F_PULSES), HWMON_CHANNEL_INFO(pwm, - HWMON_PWM_INPUT, - HWMON_PWM_INPUT), + HWMON_PWM_INPUT | HWMON_PWM_FREQ, + HWMON_PWM_INPUT | HWMON_PWM_FREQ), HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_FAULT | HWMON_T_MAX | HWMON_T_MAX_ALARM | HWMON_T_CRIT | HWMON_T_CRIT_ALARM | HWMON_T_EMERGENCY | @@ -644,6 +724,8 @@ static int max6639_probe(struct i2c_client *client) } } + mutex_init(&data->update_lock); + /* Initialize the max6639 chip */ err = max6639_init_client(client, data); if (err < 0) -- cgit From 27972a41423291b0ca438fc3f0b104429bf270f2 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 18 Jun 2024 10:08:54 -0700 Subject: hwmon: (spd5118) Use regmap to implement paging Using regmap for paging significantly improves caching since the regmap cache no longer needs to be cleared after changing the page, so let's use it. Suggested-by: Armin Wolf Tested-by: Armin Wolf Signed-off-by: Guenter Roeck --- drivers/hwmon/spd5118.c | 47 ++++++++++++++++++----------------------------- 1 file changed, 18 insertions(+), 29 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/spd5118.c b/drivers/hwmon/spd5118.c index ac94a6779360..d405c5ef755d 100644 --- a/drivers/hwmon/spd5118.c +++ b/drivers/hwmon/spd5118.c @@ -382,45 +382,19 @@ static const struct hwmon_chip_info spd5118_chip_info = { /* nvmem */ -static int spd5118_nvmem_set_page(struct regmap *regmap, int page) -{ - unsigned int old_page; - int err; - - err = regmap_read(regmap, SPD5118_REG_I2C_LEGACY_MODE, &old_page); - if (err) - return err; - - if (page != (old_page & SPD5118_LEGACY_MODE_MASK)) { - /* Update page and explicitly select 1-byte addressing */ - err = regmap_update_bits(regmap, SPD5118_REG_I2C_LEGACY_MODE, - SPD5118_LEGACY_MODE_MASK, page); - if (err) - return err; - - /* Selected new NVMEM page, drop cached data */ - regcache_drop_region(regmap, SPD5118_EEPROM_BASE, 0xff); - } - - return 0; -} - static ssize_t spd5118_nvmem_read_page(struct regmap *regmap, char *buf, unsigned int offset, size_t count) { + int addr = (offset >> SPD5118_PAGE_SHIFT) * 0x100 + SPD5118_EEPROM_BASE; int err; - err = spd5118_nvmem_set_page(regmap, offset >> SPD5118_PAGE_SHIFT); - if (err) - return err; - offset &= SPD5118_PAGE_MASK; /* Can't cross page boundaries */ if (offset + count > SPD5118_PAGE_SIZE) count = SPD5118_PAGE_SIZE - offset; - err = regmap_bulk_read(regmap, SPD5118_EEPROM_BASE + offset, buf, count); + err = regmap_bulk_read(regmap, addr + offset, buf, count); if (err) return err; @@ -515,13 +489,28 @@ static bool spd5118_volatile_reg(struct device *dev, unsigned int reg) } } +static const struct regmap_range_cfg spd5118_regmap_range_cfg[] = { + { + .selector_reg = SPD5118_REG_I2C_LEGACY_MODE, + .selector_mask = SPD5118_LEGACY_PAGE_MASK, + .selector_shift = 0, + .window_start = 0, + .window_len = 0x100, + .range_min = 0, + .range_max = 0x7ff, + }, +}; + static const struct regmap_config spd5118_regmap_config = { .reg_bits = 8, .val_bits = 8, - .max_register = 0xff, + .max_register = 0x7ff, .writeable_reg = spd5118_writeable_reg, .volatile_reg = spd5118_volatile_reg, .cache_type = REGCACHE_MAPLE, + + .ranges = spd5118_regmap_range_cfg, + .num_ranges = ARRAY_SIZE(spd5118_regmap_range_cfg), }; static int spd5118_probe(struct i2c_client *client) -- cgit From 1226a1b2e5611d17aae55a01ad9d6883879feab4 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 14 Jun 2024 08:27:21 -0700 Subject: hwmon: (spd5118) Add support for Renesas/ITD SPD5118 hub controllers The SPD5118 specification says, in its documentation of the page bits in the MR11 register: " This register only applies to non-volatile memory (1024) Bytes) access of SPD5 Hub device. For volatile memory access, this register must be programmed to '000'. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ " Renesas/ITD SPD5118 hub controllers take this literally and disable access to volatile memory if the page selected in MR11 is != 0. Since the BIOS or ROMMON will access the non-volatile memory and likely select a page != 0, this means that the driver will not instantiate since it can not identify the chip. Even if the driver instantiates, access to volatile registers is blocked after a nvram read operation which selects a page other than 0. To solve the problem, add initialization code to select page 0 during probe. Before doing that, use basic validation to ensure that this is really a SPD5118 device and not some random EEPROM. Cc: Sasha Kozachuk Cc: John Hamrick Cc: Chris Sarra Tested-by: Armin Wolf Signed-off-by: Guenter Roeck --- drivers/hwmon/spd5118.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/spd5118.c b/drivers/hwmon/spd5118.c index d405c5ef755d..fcbce5a01e55 100644 --- a/drivers/hwmon/spd5118.c +++ b/drivers/hwmon/spd5118.c @@ -513,6 +513,58 @@ static const struct regmap_config spd5118_regmap_config = { .num_ranges = ARRAY_SIZE(spd5118_regmap_range_cfg), }; +static int spd5118_init(struct i2c_client *client) +{ + struct i2c_adapter *adapter = client->adapter; + int err, regval, mode; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA)) + return -ENODEV; + + regval = i2c_smbus_read_word_swapped(client, SPD5118_REG_TYPE); + if (regval < 0 || (regval && regval != 0x5118)) + return -ENODEV; + + /* + * If the device type registers return 0, it is possible that the chip + * has a non-zero page selected and takes the specification literally, + * i.e. disables access to volatile registers besides the page register + * if the page is not 0. Try to identify such chips. + */ + if (!regval) { + /* Vendor ID registers must also be 0 */ + regval = i2c_smbus_read_word_data(client, SPD5118_REG_VENDOR); + if (regval) + return -ENODEV; + + /* The selected page in MR11 must not be 0 */ + mode = i2c_smbus_read_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE); + if (mode < 0 || (mode & ~SPD5118_LEGACY_MODE_MASK) || + !(mode & SPD5118_LEGACY_PAGE_MASK)) + return -ENODEV; + + err = i2c_smbus_write_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE, + mode & SPD5118_LEGACY_MODE_ADDR); + if (err) + return -ENODEV; + + /* + * If the device type registers are still bad after selecting + * page 0, this is not a SPD5118 device. Restore original + * legacy mode register value and abort. + */ + regval = i2c_smbus_read_word_swapped(client, SPD5118_REG_TYPE); + if (regval != 0x5118) { + i2c_smbus_write_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE, mode); + return -ENODEV; + } + } + + /* We are reasonably sure that this is really a SPD5118 hub controller */ + return 0; +} + static int spd5118_probe(struct i2c_client *client) { struct device *dev = &client->dev; @@ -522,6 +574,10 @@ static int spd5118_probe(struct i2c_client *client) struct regmap *regmap; int err; + err = spd5118_init(client); + if (err) + return err; + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; -- cgit From 52c1e818d66bfed276bd371f9e7947be4055af87 Mon Sep 17 00:00:00 2001 From: Sean Anderson Date: Thu, 20 Jun 2024 17:20:05 -0400 Subject: hwmon: iio: Use iio_read_channel_processed_scale for IIO_POWER Instead of rescaling power channels after the fact, use the dedicated scaling API. This should reduce any inaccuracies resulting from the scaling. Signed-off-by: Sean Anderson Link: https://lore.kernel.org/r/20240620212005.821805-1-sean.anderson@linux.dev Signed-off-by: Guenter Roeck --- drivers/hwmon/iio_hwmon.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/iio_hwmon.c b/drivers/hwmon/iio_hwmon.c index 4c8a80847891..fab32e1e15f2 100644 --- a/drivers/hwmon/iio_hwmon.c +++ b/drivers/hwmon/iio_hwmon.c @@ -49,16 +49,17 @@ static ssize_t iio_hwmon_read_val(struct device *dev, struct iio_channel *chan = &state->channels[sattr->index]; enum iio_chan_type type; - ret = iio_read_channel_processed(chan, &result); - if (ret < 0) - return ret; - ret = iio_get_channel_type(chan, &type); if (ret < 0) return ret; if (type == IIO_POWER) - result *= 1000; /* mili-Watts to micro-Watts conversion */ + /* mili-Watts to micro-Watts conversion */ + ret = iio_read_channel_processed_scale(chan, &result, 1000); + else + ret = iio_read_channel_processed(chan, &result); + if (ret < 0) + return ret; return sprintf(buf, "%d\n", result); } -- cgit From 38b2b022363d8c911dba40cf30af46febf4974fe Mon Sep 17 00:00:00 2001 From: Noah Wang Date: Wed, 26 Jun 2024 17:46:01 +0800 Subject: hwmon: add MP2891 driver Add support for MPS VR controller mp2891. This driver exposes telemetry and limit value readings and writtings. Signed-off-by: Noah Wang Link: https://lore.kernel.org/r/SEYPR04MB64828A352836982C0184AA10FAD62@SEYPR04MB6482.apcprd04.prod.outlook.com Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/Kconfig | 9 + drivers/hwmon/pmbus/Makefile | 1 + drivers/hwmon/pmbus/mp2891.c | 600 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 610 insertions(+) create mode 100644 drivers/hwmon/pmbus/mp2891.c (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 7d32cfc19820..338b2e98dc22 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -337,6 +337,15 @@ config SENSORS_MP2888 This driver can also be built as a module. If so, the module will be called mp2888. +config SENSORS_MP2891 + tristate "MPS MP2891" + help + If you say yes here you get hardware monitoring support for MPS + MP2891 Dual Loop Digital Multi-Phase Controller. + + This driver can also be built as a module. If so, the module will + be called mp2891. + config SENSORS_MP2975 tristate "MPS MP2975" help diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile index 6c7177fde355..8afc5ba748f6 100644 --- a/drivers/hwmon/pmbus/Makefile +++ b/drivers/hwmon/pmbus/Makefile @@ -36,6 +36,7 @@ obj-$(CONFIG_SENSORS_MAX34440) += max34440.o obj-$(CONFIG_SENSORS_MAX8688) += max8688.o obj-$(CONFIG_SENSORS_MP2856) += mp2856.o obj-$(CONFIG_SENSORS_MP2888) += mp2888.o +obj-$(CONFIG_SENSORS_MP2891) += mp2891.o obj-$(CONFIG_SENSORS_MP2975) += mp2975.o obj-$(CONFIG_SENSORS_MP2993) += mp2993.o obj-$(CONFIG_SENSORS_MP5023) += mp5023.o diff --git a/drivers/hwmon/pmbus/mp2891.c b/drivers/hwmon/pmbus/mp2891.c new file mode 100644 index 000000000000..bb28b15a9103 --- /dev/null +++ b/drivers/hwmon/pmbus/mp2891.c @@ -0,0 +1,600 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for MPS Multi-phase Digital VR Controllers(MP2891) + */ + +#include +#include +#include +#include +#include "pmbus.h" + +/* + * Vender specific registers, the register MFR_SVI3_IOUT_PRT(0x65), + * MFR_VOUT_LOOP_CTRL(0xBD), READ_PIN_EST(0x94)and READ_IIN_EST(0x95) + * redefine the standard PMBUS register. The MFR_SVI3_IOUT_PRT(0x65) + * is used to identify the iout scale and the MFR_VOUT_LOOP_CTRL(0xBD) + * is used to identify the vout scale. The READ_PIN_EST(0x94) is used + * to read input power per rail. The MP2891 does not have standard + * READ_IIN register(0x89), the iin telemetry can be obtained through + * the vendor redefined register READ_IIN_EST(0x95). + */ +#define MFR_VOUT_LOOP_CTRL 0xBD +#define READ_PIN_EST 0x94 +#define READ_IIN_EST 0x95 +#define MFR_SVI3_IOUT_PRT 0x65 + +#define MP2891_TEMP_LIMIT_OFFSET 40 +#define MP2891_PIN_LIMIT_UINT 2 +#define MP2891_IOUT_LIMIT_UINT 8 +#define MP2891_IOUT_SCALE_DIV 32 +#define MP2891_VOUT_SCALE_DIV 100 +#define MP2891_OVUV_DELTA_SCALE 50 +#define MP2891_OV_LIMIT_SCALE 20 +#define MP2891_UV_LIMIT_SCALE 5 + +#define MP2891_PAGE_NUM 2 + +#define MP2891_RAIL1_FUNC (PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | \ + PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP | \ + PMBUS_HAVE_POUT | PMBUS_HAVE_PIN | \ + PMBUS_HAVE_IIN | PMBUS_HAVE_STATUS_VOUT | \ + PMBUS_HAVE_STATUS_IOUT | \ + PMBUS_HAVE_STATUS_INPUT | \ + PMBUS_HAVE_STATUS_TEMP) + +#define MP2891_RAIL2_FUNC (PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | \ + PMBUS_HAVE_TEMP | PMBUS_HAVE_POUT | \ + PMBUS_HAVE_PIN | PMBUS_HAVE_IIN | \ + PMBUS_HAVE_STATUS_VOUT | \ + PMBUS_HAVE_STATUS_IOUT | \ + PMBUS_HAVE_STATUS_INPUT | \ + PMBUS_HAVE_STATUS_TEMP) + +struct mp2891_data { + struct pmbus_driver_info info; + int vout_scale[MP2891_PAGE_NUM]; + int iout_scale[MP2891_PAGE_NUM]; +}; + +#define to_mp2891_data(x) container_of(x, struct mp2891_data, info) + +/* Converts a LINEAR11 value to DIRECT format */ +static u16 mp2891_reg2data_linear11(u16 word) +{ + s16 exponent; + s32 mantissa; + s64 val; + + exponent = ((s16)word) >> 11; + mantissa = ((s16)((word & 0x7ff) << 5)) >> 5; + val = mantissa; + + if (exponent >= 0) + val <<= exponent; + else + val >>= -exponent; + + return val; +} + +static int +mp2891_identify_vout_scale(struct i2c_client *client, struct pmbus_driver_info *info, + int page) +{ + struct mp2891_data *data = to_mp2891_data(info); + int ret; + + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page); + if (ret < 0) + return ret; + + ret = i2c_smbus_read_word_data(client, MFR_VOUT_LOOP_CTRL); + if (ret < 0) + return ret; + + /* + * The output voltage is equal to the READ_VOUT(0x8B) register value multiplied + * by vout_scale. + * Obtain vout scale from the register MFR_VOUT_LOOP_CTRL, bits 15-14,bit 13. + * If MFR_VOUT_LOOP_CTRL[13] = 1, the vout scale is below: + * 2.5mV/LSB + * If MFR_VOUT_LOOP_CTRL[13] = 0, the vout scale is decided by + * MFR_VOUT_LOOP_CTRL[15:14]: + * 00b - 6.25mV/LSB, 01b - 5mV/LSB, 10b - 2mV/LSB, 11b - 1mV + */ + if (ret & GENMASK(13, 13)) { + data->vout_scale[page] = 250; + } else { + ret = FIELD_GET(GENMASK(15, 14), ret); + if (ret == 0) + data->vout_scale[page] = 625; + else if (ret == 1) + data->vout_scale[page] = 500; + else if (ret == 2) + data->vout_scale[page] = 200; + else + data->vout_scale[page] = 100; + } + + return 0; +} + +static int +mp2891_identify_iout_scale(struct i2c_client *client, struct pmbus_driver_info *info, + int page) +{ + struct mp2891_data *data = to_mp2891_data(info); + int ret; + + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page); + if (ret < 0) + return ret; + + ret = i2c_smbus_read_word_data(client, MFR_SVI3_IOUT_PRT); + if (ret < 0) + return ret; + + /* + * The output current is equal to the READ_IOUT(0x8C) register value + * multiplied by iout_scale. + * Obtain iout_scale from the register MFR_SVI3_IOUT_PRT[2:0]. + * The value is selected as below: + * 000b - 1A/LSB, 001b - (1/32)A/LSB, 010b - (1/16)A/LSB, + * 011b - (1/8)A/LSB, 100b - (1/4)A/LSB, 101b - (1/2)A/LSB + * 110b - 1A/LSB, 111b - 2A/LSB + */ + switch (ret & GENMASK(2, 0)) { + case 0: + case 6: + data->iout_scale[page] = 32; + break; + case 1: + data->iout_scale[page] = 1; + break; + case 2: + data->iout_scale[page] = 2; + break; + case 3: + data->iout_scale[page] = 4; + break; + case 4: + data->iout_scale[page] = 8; + break; + case 5: + data->iout_scale[page] = 16; + break; + default: + data->iout_scale[page] = 64; + break; + } + + return 0; +} + +static int mp2891_identify(struct i2c_client *client, struct pmbus_driver_info *info) +{ + int ret; + + /* Identify vout scale for rail 1. */ + ret = mp2891_identify_vout_scale(client, info, 0); + if (ret < 0) + return ret; + + /* Identify vout scale for rail 2. */ + ret = mp2891_identify_vout_scale(client, info, 1); + if (ret < 0) + return ret; + + /* Identify iout scale for rail 1. */ + ret = mp2891_identify_iout_scale(client, info, 0); + if (ret < 0) + return ret; + + /* Identify iout scale for rail 2. */ + return mp2891_identify_iout_scale(client, info, 1); +} + +static int mp2891_read_byte_data(struct i2c_client *client, int page, int reg) +{ + int ret; + + switch (reg) { + case PMBUS_VOUT_MODE: + /* + * The MP2891 does not follow standard PMBus protocol completely, the + * PMBUS_VOUT_MODE(0x20) in MP2891 is reserved and 0x00 is always + * returned when the register is read. But the calculation of vout in + * this driver is based on direct format. As a result, the format of + * vout is enforced to direct. + */ + ret = PB_VOUT_MODE_DIRECT; + break; + default: + ret = -ENODATA; + break; + } + + return ret; +} + +static int mp2891_read_word_data(struct i2c_client *client, int page, + int phase, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct mp2891_data *data = to_mp2891_data(info); + int ret; + + switch (reg) { + case PMBUS_READ_VIN: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + ret = ret & GENMASK(9, 0); + break; + case PMBUS_READ_IIN: + /* + * The MP2891 does not have standard PMBUS_READ_IIN register(0x89), + * the iin telemetry can be obtained through the vender redefined + * register READ_IIN_EST(0x95). The MP2891 PMBUS_READ_IIN register + * is linear11 format, But the pout scale is set to 1A/Lsb(using + * r/m/b scale). As a result, the iin read from MP2891 should be + * calculated to A, then return the result to pmbus core. + */ + ret = pmbus_read_word_data(client, page, phase, READ_IIN_EST); + if (ret < 0) + return ret; + + ret = mp2891_reg2data_linear11(ret); + break; + case PMBUS_READ_PIN: + /* + * The MP2891 has standard PMBUS_READ_PIN register(0x97), but this + * is not used to read the input power per rail. The input power + * per rail is read through the vender redefined register + * READ_PIN_EST(0x94). The MP2891 PMBUS_READ_PIN register is linear11 + * format, But the pout scale is set to 1W/Lsb(using r/m/b scale). + * As a result, the pin read from MP2891 should be calculated to W, + * then return the result to pmbus core. + */ + ret = pmbus_read_word_data(client, page, phase, READ_PIN_EST); + if (ret < 0) + return ret; + + ret = mp2891_reg2data_linear11(ret); + break; + case PMBUS_READ_POUT: + /* + * The MP2891 PMBUS_READ_POUT register is linear11 format, and the + * exponent is not a constant value. But the pout scale is set to + * 1W/Lsb(using r/m/b scale). As a result, the pout read from MP2891 + * should be calculated to W, then return the result to pmbus core. + */ + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + ret = mp2891_reg2data_linear11(ret); + break; + case PMBUS_READ_VOUT: + case PMBUS_VOUT_UV_WARN_LIMIT: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + ret = DIV_ROUND_CLOSEST(ret * data->vout_scale[page], MP2891_VOUT_SCALE_DIV); + break; + case PMBUS_READ_IOUT: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + ret = DIV_ROUND_CLOSEST((ret & GENMASK(10, 0)) * data->iout_scale[page], + MP2891_IOUT_SCALE_DIV); + break; + case PMBUS_OT_FAULT_LIMIT: + case PMBUS_OT_WARN_LIMIT: + /* + * The scale of MP2891 PMBUS_OT_FAULT_LIMIT and PMBUS_OT_WARN_LIMIT + * is 1°C/LSB and they have 40°C offset. + */ + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + ret = (ret & GENMASK(7, 0)) - MP2891_TEMP_LIMIT_OFFSET; + break; + case PMBUS_VIN_OV_FAULT_LIMIT: + /* + * The MP2891 PMBUS_VIN_OV_FAULT_LIMIT scale is 125mV/Lsb. + * but the vin scale is set to 31.25mV/Lsb(using r/m/b scale). + * As a result, the limit value should be multiplied by 4. + */ + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + ret = (ret & GENMASK(7, 0)) * 4; + break; + case PMBUS_VOUT_UV_FAULT_LIMIT: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + if (FIELD_GET(GENMASK(11, 8), ret)) + ret = FIELD_GET(GENMASK(7, 0), ret) * MP2891_UV_LIMIT_SCALE - + (FIELD_GET(GENMASK(11, 8), ret) + 1) * MP2891_OVUV_DELTA_SCALE; + else + ret = FIELD_GET(GENMASK(7, 0), ret) * MP2891_UV_LIMIT_SCALE; + + ret = ret < 0 ? 0 : ret; + break; + case PMBUS_VOUT_OV_FAULT_LIMIT: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + if (FIELD_GET(GENMASK(11, 8), ret)) + ret = FIELD_GET(GENMASK(7, 0), ret) * MP2891_OV_LIMIT_SCALE + + (FIELD_GET(GENMASK(11, 8), ret) + 1) * MP2891_OVUV_DELTA_SCALE; + else + ret = FIELD_GET(GENMASK(7, 0), ret) * MP2891_OV_LIMIT_SCALE; + break; + case PMBUS_IOUT_OC_WARN_LIMIT: + case PMBUS_IOUT_OC_FAULT_LIMIT: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + ret = DIV_ROUND_CLOSEST((ret & GENMASK(7, 0)) * data->iout_scale[page] * + MP2891_IOUT_LIMIT_UINT, MP2891_IOUT_SCALE_DIV); + break; + case PMBUS_IIN_OC_WARN_LIMIT: + /* + * The scale of PMBUS_IIN_OC_WARN_LIMIT is 0.5A/Lsb, but the iin scale + * is set to 1A/Lsb(using r/m/b scale), so the word data should be + * divided by 2. + */ + ret = pmbus_read_word_data(client, 0, phase, reg); + if (ret < 0) + return ret; + + ret = DIV_ROUND_CLOSEST((ret & GENMASK(9, 0)), 2); + break; + case PMBUS_PIN_OP_WARN_LIMIT: + /* + * The scale of PMBUS_PIN_OP_WARN_LIMIT is 2W/Lsb, but the pin scale + * is set to 1W/Lsb(using r/m/b scale), so the word data should be + * multiplied by 2. + */ + ret = pmbus_read_word_data(client, 0, phase, reg); + if (ret < 0) + return ret; + + ret = (ret & GENMASK(9, 0)) * MP2891_PIN_LIMIT_UINT; + break; + case PMBUS_READ_TEMPERATURE_1: + case PMBUS_VIN_UV_FAULT_LIMIT: + case PMBUS_VIN_UV_WARN_LIMIT: + ret = -ENODATA; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int mp2891_write_word_data(struct i2c_client *client, int page, int reg, + u16 word) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct mp2891_data *data = to_mp2891_data(info); + int ret; + + switch (reg) { + case PMBUS_VOUT_UV_WARN_LIMIT: + ret = pmbus_write_word_data(client, page, reg, + DIV_ROUND_CLOSEST(word * MP2891_VOUT_SCALE_DIV, + data->vout_scale[page])); + break; + case PMBUS_VOUT_UV_FAULT_LIMIT: + /* + * The PMBUS_VOUT_UV_FAULT_LIMIT[7:0] is the limit value, and bit8-bit15 + * should not be changed. + */ + ret = pmbus_read_word_data(client, page, 0xff, reg); + if (ret < 0) + return ret; + + if (FIELD_GET(GENMASK(11, 8), ret)) + ret = pmbus_write_word_data(client, page, reg, + (ret & ~GENMASK(7, 0)) | + FIELD_PREP(GENMASK(7, 0), + DIV_ROUND_CLOSEST(word + + (FIELD_GET(GENMASK(11, 8), ret) + 1) * + MP2891_OVUV_DELTA_SCALE, + MP2891_UV_LIMIT_SCALE))); + else + ret = pmbus_write_word_data(client, page, reg, + (ret & ~GENMASK(7, 0)) | + FIELD_PREP(GENMASK(7, 0), + DIV_ROUND_CLOSEST(word, + MP2891_UV_LIMIT_SCALE))); + break; + case PMBUS_VOUT_OV_FAULT_LIMIT: + /* + * The PMBUS_VOUT_OV_FAULT_LIMIT[7:0] is the limit value, and bit8-bit15 + * should not be changed. + */ + ret = pmbus_read_word_data(client, page, 0xff, reg); + if (ret < 0) + return ret; + + if (FIELD_GET(GENMASK(11, 8), ret)) + ret = pmbus_write_word_data(client, page, reg, + (ret & ~GENMASK(7, 0)) | + FIELD_PREP(GENMASK(7, 0), + DIV_ROUND_CLOSEST(word - + (FIELD_GET(GENMASK(11, 8), ret) + 1) * + MP2891_OVUV_DELTA_SCALE, + MP2891_OV_LIMIT_SCALE))); + else + ret = pmbus_write_word_data(client, page, reg, + (ret & ~GENMASK(7, 0)) | + FIELD_PREP(GENMASK(7, 0), + DIV_ROUND_CLOSEST(word, + MP2891_OV_LIMIT_SCALE))); + break; + case PMBUS_VIN_OV_FAULT_LIMIT: + /* + * The PMBUS_VIN_OV_FAULT_LIMIT[7:0] is the limit value, and bit8-bit15 + * should not be changed. The scale of PMBUS_VIN_OV_FAULT_LIMIT is 125mV/Lsb, + * but the vin scale is set to 31.25mV/Lsb(using r/m/b scale), so the word data + * should be divided by 4. + */ + ret = pmbus_read_word_data(client, page, 0xff, reg); + if (ret < 0) + return ret; + + ret = pmbus_write_word_data(client, page, reg, + (ret & ~GENMASK(7, 0)) | + FIELD_PREP(GENMASK(7, 0), + DIV_ROUND_CLOSEST(word, 4))); + break; + case PMBUS_OT_FAULT_LIMIT: + case PMBUS_OT_WARN_LIMIT: + /* + * The scale of MP2891 PMBUS_OT_FAULT_LIMIT and PMBUS_OT_WARN_LIMIT + * have 40°C offset. The bit0-bit7 is the limit value, and bit8-bit15 + * should not be changed. + */ + ret = pmbus_read_word_data(client, page, 0xff, reg); + if (ret < 0) + return ret; + + ret = pmbus_write_word_data(client, page, reg, + (ret & ~GENMASK(7, 0)) | + FIELD_PREP(GENMASK(7, 0), word + MP2891_TEMP_LIMIT_OFFSET)); + break; + case PMBUS_IOUT_OC_WARN_LIMIT: + case PMBUS_IOUT_OC_FAULT_LIMIT: + ret = pmbus_write_word_data(client, page, reg, + DIV_ROUND_CLOSEST(word * MP2891_IOUT_SCALE_DIV, + MP2891_IOUT_LIMIT_UINT * + data->iout_scale[page])); + break; + case PMBUS_IIN_OC_WARN_LIMIT: + /* + * The scale of PMBUS_IIN_OC_WARN_LIMIT is 0.5A/Lsb, but the iin scale + * is set to 1A/Lsb(using r/m/b scale), so the word data should be + * multiplied by 2. + */ + ret = pmbus_write_word_data(client, page, reg, word * 2); + break; + case PMBUS_PIN_OP_WARN_LIMIT: + /* + * The scale of PMBUS_PIN_OP_WARN_LIMIT is 2W/Lsb, but the pin scale + * is set to 1W/Lsb(using r/m/b scale), so the word data should be + * divided by 2. + */ + ret = pmbus_write_word_data(client, page, reg, + DIV_ROUND_CLOSEST(word, MP2891_PIN_LIMIT_UINT)); + break; + case PMBUS_VIN_UV_FAULT_LIMIT: + case PMBUS_VIN_UV_WARN_LIMIT: + ret = -ENODATA; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static const struct pmbus_driver_info mp2891_info = { + .pages = MP2891_PAGE_NUM, + .format[PSC_VOLTAGE_IN] = direct, + .format[PSC_CURRENT_IN] = direct, + .format[PSC_CURRENT_OUT] = direct, + .format[PSC_TEMPERATURE] = direct, + .format[PSC_POWER] = direct, + .format[PSC_VOLTAGE_OUT] = direct, + + /* set vin scale 31.25mV/Lsb */ + .m[PSC_VOLTAGE_IN] = 32, + .R[PSC_VOLTAGE_IN] = 0, + .b[PSC_VOLTAGE_IN] = 0, + + /* set temp scale 1000m°C/Lsb */ + .m[PSC_TEMPERATURE] = 1, + .R[PSC_TEMPERATURE] = 0, + .b[PSC_TEMPERATURE] = 0, + + .m[PSC_CURRENT_IN] = 1, + .R[PSC_CURRENT_IN] = 0, + .b[PSC_CURRENT_IN] = 0, + + .m[PSC_CURRENT_OUT] = 1, + .R[PSC_CURRENT_OUT] = 0, + .b[PSC_CURRENT_OUT] = 0, + + .m[PSC_POWER] = 1, + .R[PSC_POWER] = 0, + .b[PSC_POWER] = 0, + + .m[PSC_VOLTAGE_OUT] = 1, + .R[PSC_VOLTAGE_OUT] = 3, + .b[PSC_VOLTAGE_OUT] = 0, + + .func[0] = MP2891_RAIL1_FUNC, + .func[1] = MP2891_RAIL2_FUNC, + .read_word_data = mp2891_read_word_data, + .write_word_data = mp2891_write_word_data, + .read_byte_data = mp2891_read_byte_data, + .identify = mp2891_identify, +}; + +static int mp2891_probe(struct i2c_client *client) +{ + struct mp2891_data *data; + + data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + memcpy(&data->info, &mp2891_info, sizeof(mp2891_info)); + + return pmbus_do_probe(client, &data->info); +} + +static const struct i2c_device_id mp2891_id[] = { + {"mp2891", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, mp2891_id); + +static const struct of_device_id __maybe_unused mp2891_of_match[] = { + {.compatible = "mps,mp2891"}, + {} +}; +MODULE_DEVICE_TABLE(of, mp2891_of_match); + +static struct i2c_driver mp2891_driver = { + .driver = { + .name = "mp2891", + .of_match_table = mp2891_of_match, + }, + .probe = mp2891_probe, + .id_table = mp2891_id, +}; + +module_i2c_driver(mp2891_driver); + +MODULE_AUTHOR("Noah Wang "); +MODULE_DESCRIPTION("PMBus driver for MPS MP2891"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); -- cgit From ef8df816eea8b985bec1b8fc838c737fbdb4f8b0 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Fri, 28 Jun 2024 23:47:23 +0200 Subject: hwmon: (dell-smm) Add Dell OptiPlex 7060 to DMI table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The BIOS on this machine is buggy and will in some cases return an error when trying to get the fan state, but reading of the RPM values and the temperature sensors still works. Closes: https://github.com/vitorafsr/i8kutils/issues/38 Signed-off-by: Armin Wolf Acked-by: Pali Rohár Link: https://lore.kernel.org/r/20240628214723.19665-1-W_Armin@gmx.de Signed-off-by: Guenter Roeck --- drivers/hwmon/dell-smm-hwmon.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/dell-smm-hwmon.c b/drivers/hwmon/dell-smm-hwmon.c index 48a81c64f00d..c75bfe93f2f6 100644 --- a/drivers/hwmon/dell-smm-hwmon.c +++ b/drivers/hwmon/dell-smm-hwmon.c @@ -1263,6 +1263,13 @@ static const struct dmi_system_id i8k_dmi_table[] __initconst = { DMI_MATCH(DMI_PRODUCT_NAME, "MP061"), }, }, + { + .ident = "Dell OptiPlex 7060", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "OptiPlex 7060"), + }, + }, { .ident = "Dell Precision", .matches = { -- cgit From c98cf41dfbab71e3c6d71061c182fafde0bf0f99 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 30 Jun 2024 09:32:10 -0700 Subject: hwmon: (jc42) Use common device ID for TSE2004av compliant sensors TSE2004av standardizes the device ID of compliant temperature sensors to be 0x22xx, where xx is the device revision. Use a single define for all TSE2004av compliant temperature sensors, and relax the device id mask to match the upper 8 bit of the device id register. Signed-off-by: Guenter Roeck --- drivers/hwmon/jc42.c | 43 ++++++++++--------------------------------- 1 file changed, 10 insertions(+), 33 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/jc42.c b/drivers/hwmon/jc42.c index 7092f8f025b8..1180af1b1638 100644 --- a/drivers/hwmon/jc42.c +++ b/drivers/hwmon/jc42.c @@ -79,20 +79,9 @@ static const unsigned short normal_i2c[] = { #define AT30TS00_DEVID 0x8201 #define AT30TS00_DEVID_MASK 0xffff -#define AT30TSE004_DEVID 0x2200 -#define AT30TSE004_DEVID_MASK 0xffff - -/* Giantec */ -#define GT30TS00_DEVID 0x2200 -#define GT30TS00_DEVID_MASK 0xff00 - #define GT34TS02_DEVID 0x3300 #define GT34TS02_DEVID_MASK 0xff00 -/* IDT */ -#define TSE2004_DEVID 0x2200 -#define TSE2004_DEVID_MASK 0xff00 - #define TS3000_DEVID 0x2900 /* Also matches TSE2002 */ #define TS3000_DEVID_MASK 0xff00 @@ -116,9 +105,6 @@ static const unsigned short normal_i2c[] = { #define MCP98243_DEVID 0x2100 #define MCP98243_DEVID_MASK 0xfffc -#define MCP98244_DEVID 0x2200 -#define MCP98244_DEVID_MASK 0xfffc - #define MCP9843_DEVID 0x0000 /* Also matches mcp9805 */ #define MCP9843_DEVID_MASK 0xfffe @@ -136,12 +122,6 @@ static const unsigned short normal_i2c[] = { #define CAT34TS02C_DEVID 0x0a00 #define CAT34TS02C_DEVID_MASK 0xfff0 -#define CAT34TS04_DEVID 0x2200 -#define CAT34TS04_DEVID_MASK 0xfff0 - -#define N34TS04_DEVID 0x2230 -#define N34TS04_DEVID_MASK 0xfff0 - /* ST Microelectronics */ #define STTS424_DEVID 0x0101 #define STTS424_DEVID_MASK 0xffff @@ -152,15 +132,12 @@ static const unsigned short normal_i2c[] = { #define STTS2002_DEVID 0x0300 #define STTS2002_DEVID_MASK 0xffff -#define STTS2004_DEVID 0x2201 -#define STTS2004_DEVID_MASK 0xffff - #define STTS3000_DEVID 0x0200 #define STTS3000_DEVID_MASK 0xffff -/* Seiko Instruments */ -#define S34TS04A_DEVID 0x2221 -#define S34TS04A_DEVID_MASK 0xffff +/* TSE2004 compliant sensors */ +#define TSE2004_DEVID 0x2200 +#define TSE2004_DEVID_MASK 0xff00 static u16 jc42_hysteresis[] = { 0, 1500, 3000, 6000 }; @@ -173,8 +150,8 @@ struct jc42_chips { static struct jc42_chips jc42_chips[] = { { ADT_MANID, ADT7408_DEVID, ADT7408_DEVID_MASK }, { ATMEL_MANID, AT30TS00_DEVID, AT30TS00_DEVID_MASK }, - { ATMEL_MANID2, AT30TSE004_DEVID, AT30TSE004_DEVID_MASK }, - { GT_MANID, GT30TS00_DEVID, GT30TS00_DEVID_MASK }, + { ATMEL_MANID2, TSE2004_DEVID, TSE2004_DEVID_MASK }, + { GT_MANID, TSE2004_DEVID, TSE2004_DEVID_MASK }, { GT_MANID2, GT34TS02_DEVID, GT34TS02_DEVID_MASK }, { IDT_MANID, TSE2004_DEVID, TSE2004_DEVID_MASK }, { IDT_MANID, TS3000_DEVID, TS3000_DEVID_MASK }, @@ -184,19 +161,19 @@ static struct jc42_chips jc42_chips[] = { { MCP_MANID, MCP9808_DEVID, MCP9808_DEVID_MASK }, { MCP_MANID, MCP98242_DEVID, MCP98242_DEVID_MASK }, { MCP_MANID, MCP98243_DEVID, MCP98243_DEVID_MASK }, - { MCP_MANID, MCP98244_DEVID, MCP98244_DEVID_MASK }, + { MCP_MANID, TSE2004_DEVID, TSE2004_DEVID_MASK }, { MCP_MANID, MCP9843_DEVID, MCP9843_DEVID_MASK }, { NXP_MANID, SE97_DEVID, SE97_DEVID_MASK }, { ONS_MANID, CAT6095_DEVID, CAT6095_DEVID_MASK }, { ONS_MANID, CAT34TS02C_DEVID, CAT34TS02C_DEVID_MASK }, - { ONS_MANID, CAT34TS04_DEVID, CAT34TS04_DEVID_MASK }, - { ONS_MANID, N34TS04_DEVID, N34TS04_DEVID_MASK }, + { ONS_MANID, TSE2004_DEVID, TSE2004_DEVID_MASK }, + { ONS_MANID, TSE2004_DEVID, TSE2004_DEVID_MASK }, { NXP_MANID, SE98_DEVID, SE98_DEVID_MASK }, - { SI_MANID, S34TS04A_DEVID, S34TS04A_DEVID_MASK }, + { SI_MANID, TSE2004_DEVID, TSE2004_DEVID_MASK }, { STM_MANID, STTS424_DEVID, STTS424_DEVID_MASK }, { STM_MANID, STTS424E_DEVID, STTS424E_DEVID_MASK }, { STM_MANID, STTS2002_DEVID, STTS2002_DEVID_MASK }, - { STM_MANID, STTS2004_DEVID, STTS2004_DEVID_MASK }, + { STM_MANID, TSE2004_DEVID, TSE2004_DEVID_MASK }, { STM_MANID, STTS3000_DEVID, STTS3000_DEVID_MASK }, }; -- cgit From b3e992f69c239b0eb99c408c1ca9cd4253d2e7ad Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 30 Jun 2024 13:08:40 -0700 Subject: hwmon: (jc42) Strengthen detect function Configuration register bit 5 must read 0 for all JC42.4 compliant chips. Several capability register bits must be set for all TSE2004 compliant chips. Use that information to strengthen the detect function. Signed-off-by: Guenter Roeck --- drivers/hwmon/jc42.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/jc42.c b/drivers/hwmon/jc42.c index 1180af1b1638..a260cff750a5 100644 --- a/drivers/hwmon/jc42.c +++ b/drivers/hwmon/jc42.c @@ -413,7 +413,11 @@ static int jc42_detect(struct i2c_client *client, struct i2c_board_info *info) if (cap < 0 || config < 0 || manid < 0 || devid < 0) return -ENODEV; - if ((cap & 0xff00) || (config & 0xf800)) + if ((cap & 0xff00) || (config & 0xf820)) + return -ENODEV; + + if ((devid & TSE2004_DEVID_MASK) == TSE2004_DEVID && + (cap & 0x00e7) != 0x00e7) return -ENODEV; for (i = 0; i < ARRAY_SIZE(jc42_chips); i++) { -- cgit From 39b24cced70fdc336dbc0070f8b3bde61d8513a8 Mon Sep 17 00:00:00 2001 From: Wayne Tung Date: Mon, 1 Jul 2024 15:32:52 +0800 Subject: hwmon: (adt7475) Fix default duty on fan is disabled According to the comments on fan is disabled, we change to manual mode and set the duty cycle to 0. For setting the duty cycle part, the register is wrong. Fix it. Fixes: 1c301fc5394f ("hwmon: Add a driver for the ADT7475 hardware monitoring chip") Signed-off-by: Wayne Tung Link: https://lore.kernel.org/r/20240701073252.317397-1-chineweff@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/adt7475.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c index 5f78e6633018..bc186c61a2c0 100644 --- a/drivers/hwmon/adt7475.c +++ b/drivers/hwmon/adt7475.c @@ -1896,7 +1896,7 @@ static void adt7475_read_pwm(struct i2c_client *client, int index) data->pwm[CONTROL][index] &= ~0xE0; data->pwm[CONTROL][index] |= (7 << 5); - i2c_smbus_write_byte_data(client, PWM_CONFIG_REG(index), + i2c_smbus_write_byte_data(client, PWM_REG(index), data->pwm[INPUT][index]); i2c_smbus_write_byte_data(client, PWM_CONFIG_REG(index), -- cgit From 9d047eb05c079f35584d047cfbc8c9434d128d00 Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Tue, 2 Jul 2024 12:09:30 +0200 Subject: hwmon: (ina238) Constify struct regmap_config `ina238_regmap_config` is not modified and can be declared as const to move its data to a read-only section. Signed-off-by: Javier Carrasco Link: https://lore.kernel.org/r/20240702-hwmon-const-regmap-v1-1-63f6d4765fe0@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/ina238.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/ina238.c b/drivers/hwmon/ina238.c index 855626f1bc01..2d9f12f68d50 100644 --- a/drivers/hwmon/ina238.c +++ b/drivers/hwmon/ina238.c @@ -96,7 +96,7 @@ #define INA238_BUS_VOLTAGE_LSB 3125 /* 3.125 mV/lsb */ #define INA238_DIE_TEMP_LSB 125 /* 125 mC/lsb */ -static struct regmap_config ina238_regmap_config = { +static const struct regmap_config ina238_regmap_config = { .max_register = INA238_REGISTERS, .reg_bits = 8, .val_bits = 16, -- cgit From 4bc82dd20be09fa133bf3116be6afc933497f860 Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Tue, 2 Jul 2024 12:09:31 +0200 Subject: hwmon: (tmp513) Constify struct regmap_config `tmp51x_regmap_config` is not modified and can be declared as const to move its data to a read-only section. Signed-off-by: Javier Carrasco Link: https://lore.kernel.org/r/20240702-hwmon-const-regmap-v1-2-63f6d4765fe0@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/tmp513.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/tmp513.c b/drivers/hwmon/tmp513.c index ea6f4416c124..926d28cd3fab 100644 --- a/drivers/hwmon/tmp513.c +++ b/drivers/hwmon/tmp513.c @@ -159,7 +159,7 @@ static const u8 TMP51X_CURR_INPUT[2] = { TMP51X_BUS_CURRENT_RESULT }; -static struct regmap_config tmp51x_regmap_config = { +static const struct regmap_config tmp51x_regmap_config = { .reg_bits = 8, .val_bits = 16, .max_register = TMP51X_MAX_REGISTER_ADDR, -- cgit From f642714b788b993c229fc9839cda15d9c5d965fc Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Tue, 2 Jul 2024 12:09:32 +0200 Subject: hwmon: (tps23861) Constify struct regmap_config `tps23861_regmap_config` is not modified and can be declared as const to move its data to a read-only section. Signed-off-by: Javier Carrasco Link: https://lore.kernel.org/r/20240702-hwmon-const-regmap-v1-3-63f6d4765fe0@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/tps23861.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/tps23861.c b/drivers/hwmon/tps23861.c index d33ecbac00d6..dfcfb09d9f3c 100644 --- a/drivers/hwmon/tps23861.c +++ b/drivers/hwmon/tps23861.c @@ -117,7 +117,7 @@ struct tps23861_data { struct dentry *debugfs_dir; }; -static struct regmap_config tps23861_regmap_config = { +static const struct regmap_config tps23861_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = 0x6f, -- cgit From cd228e7b65d43c441964a17f02c92f33d2c4af7b Mon Sep 17 00:00:00 2001 From: Alex Vdovydchenko Date: Tue, 2 Jul 2024 14:52:51 +0300 Subject: hwmon: add MP5920 driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for MPS Hot-Swap controller mp5920. This driver exposes telemetry and limit value readings and writings. Signed-off-by: Alex Vdovydchenko Reviewed-by: Thomas Weißschuh Link: https://lore.kernel.org/r/20240702115252.981416-3-xzeol@yahoo.com [groeck: Use min_t() to limit length of displayed model string] Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/Kconfig | 9 +++++ drivers/hwmon/pmbus/Makefile | 1 + drivers/hwmon/pmbus/mp5920.c | 90 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+) create mode 100644 drivers/hwmon/pmbus/mp5920.c (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 338b2e98dc22..a4f02cad92fd 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -380,6 +380,15 @@ config SENSORS_MP5023 This driver can also be built as a module. If so, the module will be called mp5023. +config SENSORS_MP5920 + tristate "MPS MP5920" + help + If you say yes here you get hardware monitoring support for Monolithic + MP5920. + + This driver can also be built as a module. If so, the module will + be called mp5920. + config SENSORS_MP5990 tristate "MPS MP5990" help diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile index 8afc5ba748f6..d00bcc758b97 100644 --- a/drivers/hwmon/pmbus/Makefile +++ b/drivers/hwmon/pmbus/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_SENSORS_MP2891) += mp2891.o obj-$(CONFIG_SENSORS_MP2975) += mp2975.o obj-$(CONFIG_SENSORS_MP2993) += mp2993.o obj-$(CONFIG_SENSORS_MP5023) += mp5023.o +obj-$(CONFIG_SENSORS_MP5920) += mp5920.o obj-$(CONFIG_SENSORS_MP5990) += mp5990.o obj-$(CONFIG_SENSORS_MP9941) += mp9941.o obj-$(CONFIG_SENSORS_MPQ7932) += mpq7932.o diff --git a/drivers/hwmon/pmbus/mp5920.c b/drivers/hwmon/pmbus/mp5920.c new file mode 100644 index 000000000000..f6d7527ade7d --- /dev/null +++ b/drivers/hwmon/pmbus/mp5920.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for MP5920 and compatible chips. + */ + +#include +#include +#include +#include +#include "pmbus.h" + +static struct pmbus_driver_info mp5920_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = direct, + .format[PSC_VOLTAGE_OUT] = direct, + .format[PSC_CURRENT_OUT] = direct, + .format[PSC_POWER] = direct, + .format[PSC_TEMPERATURE] = direct, + .m[PSC_VOLTAGE_IN] = 2266, + .b[PSC_VOLTAGE_IN] = 0, + .R[PSC_VOLTAGE_IN] = -1, + .m[PSC_VOLTAGE_OUT] = 2266, + .b[PSC_VOLTAGE_OUT] = 0, + .R[PSC_VOLTAGE_OUT] = -1, + .m[PSC_CURRENT_OUT] = 546, + .b[PSC_CURRENT_OUT] = 0, + .R[PSC_CURRENT_OUT] = -2, + .m[PSC_POWER] = 5840, + .b[PSC_POWER] = 0, + .R[PSC_POWER] = -3, + .m[PSC_TEMPERATURE] = 1067, + .b[PSC_TEMPERATURE] = 20500, + .R[PSC_TEMPERATURE] = -2, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | + PMBUS_HAVE_IOUT | PMBUS_HAVE_POUT | + PMBUS_HAVE_TEMP, +}; + +static int mp5920_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + int ret; + u8 buf[I2C_SMBUS_BLOCK_MAX]; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA)) + return -ENODEV; + + ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to read PMBUS_MFR_MODEL\n"); + + if (ret != 6 || strncmp(buf, "MP5920", 6)) { + return dev_err_probe(dev, -ENODEV, "Model '%.*s' not supported\n", + min_t(int, ret, sizeof(buf)), buf); + } + + return pmbus_do_probe(client, &mp5920_info); +} + +static const struct of_device_id mp5920_of_match[] = { + { .compatible = "mps,mp5920" }, + { } +}; + +MODULE_DEVICE_TABLE(of, mp5920_of_match); + +static const struct i2c_device_id mp5920_id[] = { + { "mp5920" }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, mp5920_id); + +static struct i2c_driver mp5920_driver = { + .driver = { + .name = "mp5920", + .of_match_table = mp5920_of_match, + }, + .probe = mp5920_probe, + .id_table = mp5920_id, +}; + +module_i2c_driver(mp5920_driver); + +MODULE_AUTHOR("Tony Ao "); +MODULE_AUTHOR("Alex Vdovydchenko "); +MODULE_DESCRIPTION("PMBus driver for MP5920 HSC"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); -- cgit From 99bf7c2eccff82760fa23ce967cc67c8c219c6a6 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 4 Jul 2024 10:18:59 -0500 Subject: hwmon: (ltc2991) re-order conditions to fix off by one bug LTC2991_T_INT_CH_NR is 4. The st->temp_en[] array has LTC2991_MAX_CHANNEL (4) elements. Thus if "channel" is equal to LTC2991_T_INT_CH_NR then we have read one element beyond the end of the array. Flip the conditions around so that we check if "channel" is valid before using it as an array index. Fixes: 2b9ea4262ae9 ("hwmon: Add driver for ltc2991") Signed-off-by: Dan Carpenter Link: https://lore.kernel.org/r/Zoa9Y_UMY4_ROfhF@stanley.mountain Signed-off-by: Guenter Roeck --- drivers/hwmon/ltc2991.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/ltc2991.c b/drivers/hwmon/ltc2991.c index a01f887392f9..573cd8f5721b 100644 --- a/drivers/hwmon/ltc2991.c +++ b/drivers/hwmon/ltc2991.c @@ -225,8 +225,8 @@ static umode_t ltc2991_is_visible(const void *data, case hwmon_temp: switch (attr) { case hwmon_temp_input: - if (st->temp_en[channel] || - channel == LTC2991_T_INT_CH_NR) + if (channel == LTC2991_T_INT_CH_NR || + st->temp_en[channel]) return 0444; break; } -- cgit From 0811b8b03bb30b82c84283dde1ff697bc192abc0 Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Fri, 5 Jul 2024 20:27:28 +0200 Subject: hwmon: (gsc-hwmon) constify read-only struct regmap_bus `gsc_hwmon_regmap_bus` is not modified and can be declared as const to move its data to a read-only section. Signed-off-by: Javier Carrasco Link: https://lore.kernel.org/r/20240705-hwmon-const-regmap-v1-1-7cde543ba818@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/gsc-hwmon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/gsc-hwmon.c b/drivers/hwmon/gsc-hwmon.c index 0f2147699c91..cb2f01dc4326 100644 --- a/drivers/hwmon/gsc-hwmon.c +++ b/drivers/hwmon/gsc-hwmon.c @@ -39,7 +39,7 @@ struct gsc_hwmon_data { struct hwmon_chip_info chip; }; -static struct regmap_bus gsc_hwmon_regmap_bus = { +static const struct regmap_bus gsc_hwmon_regmap_bus = { .reg_read = gsc_read, .reg_write = gsc_write, }; -- cgit From 8cad724c8537fe3e0da8004646abc00290adae40 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 6 Jul 2024 23:43:04 -0700 Subject: hwmon: (adc128d818) Fix underflows seen when writing limit attributes DIV_ROUND_CLOSEST() after kstrtol() results in an underflow if a large negative number such as -9223372036854775808 is provided by the user. Fix it by reordering clamp_val() and DIV_ROUND_CLOSEST() operations. Signed-off-by: Guenter Roeck --- drivers/hwmon/adc128d818.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/adc128d818.c b/drivers/hwmon/adc128d818.c index 8ac6e735ec5c..5e805d4ee76a 100644 --- a/drivers/hwmon/adc128d818.c +++ b/drivers/hwmon/adc128d818.c @@ -175,7 +175,7 @@ static ssize_t adc128_in_store(struct device *dev, mutex_lock(&data->update_lock); /* 10 mV LSB on limit registers */ - regval = clamp_val(DIV_ROUND_CLOSEST(val, 10), 0, 255); + regval = DIV_ROUND_CLOSEST(clamp_val(val, 0, 2550), 10); data->in[index][nr] = regval << 4; reg = index == 1 ? ADC128_REG_IN_MIN(nr) : ADC128_REG_IN_MAX(nr); i2c_smbus_write_byte_data(data->client, reg, regval); @@ -213,7 +213,7 @@ static ssize_t adc128_temp_store(struct device *dev, return err; mutex_lock(&data->update_lock); - regval = clamp_val(DIV_ROUND_CLOSEST(val, 1000), -128, 127); + regval = DIV_ROUND_CLOSEST(clamp_val(val, -128000, 127000), 1000); data->temp[index] = regval << 1; i2c_smbus_write_byte_data(data->client, index == 1 ? ADC128_REG_TEMP_MAX -- cgit From af64e3e1537896337405f880c1e9ac1f8c0c6198 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 6 Jul 2024 23:48:42 -0700 Subject: hwmon: (lm95234) Fix underflows seen when writing limit attributes DIV_ROUND_CLOSEST() after kstrtol() results in an underflow if a large negative number such as -9223372036854775808 is provided by the user. Fix it by reordering clamp_val() and DIV_ROUND_CLOSEST() operations. Signed-off-by: Guenter Roeck --- drivers/hwmon/lm95234.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/lm95234.c b/drivers/hwmon/lm95234.c index be00a374632d..9a7afdb49895 100644 --- a/drivers/hwmon/lm95234.c +++ b/drivers/hwmon/lm95234.c @@ -301,7 +301,8 @@ static ssize_t tcrit2_store(struct device *dev, struct device_attribute *attr, if (ret < 0) return ret; - val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0, index ? 255 : 127); + val = DIV_ROUND_CLOSEST(clamp_val(val, 0, (index ? 255 : 127) * 1000), + 1000); mutex_lock(&data->update_lock); data->tcrit2[index] = val; @@ -350,7 +351,7 @@ static ssize_t tcrit1_store(struct device *dev, struct device_attribute *attr, if (ret < 0) return ret; - val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0, 255); + val = DIV_ROUND_CLOSEST(clamp_val(val, 0, 255000), 1000); mutex_lock(&data->update_lock); data->tcrit1[index] = val; @@ -391,7 +392,7 @@ static ssize_t tcrit1_hyst_store(struct device *dev, if (ret < 0) return ret; - val = DIV_ROUND_CLOSEST(val, 1000); + val = DIV_ROUND_CLOSEST(clamp_val(val, -255000, 255000), 1000); val = clamp_val((int)data->tcrit1[index] - val, 0, 31); mutex_lock(&data->update_lock); @@ -431,7 +432,7 @@ static ssize_t offset_store(struct device *dev, struct device_attribute *attr, return ret; /* Accuracy is 1/2 degrees C */ - val = clamp_val(DIV_ROUND_CLOSEST(val, 500), -128, 127); + val = DIV_ROUND_CLOSEST(clamp_val(val, -64000, 63500), 500); mutex_lock(&data->update_lock); data->toffset[index] = val; -- cgit From 0403e10bf0824bf0ec2bb135d4cf1c0cc3bf4bf0 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 6 Jul 2024 23:50:08 -0700 Subject: hwmon: (nct6775-core) Fix underflows seen when writing limit attributes DIV_ROUND_CLOSEST() after kstrtol() results in an underflow if a large negative number such as -9223372036854775808 is provided by the user. Fix it by reordering clamp_val() and DIV_ROUND_CLOSEST() operations. Signed-off-by: Guenter Roeck --- drivers/hwmon/nct6775-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c index 9fbab8f02334..934fed3dd586 100644 --- a/drivers/hwmon/nct6775-core.c +++ b/drivers/hwmon/nct6775-core.c @@ -2262,7 +2262,7 @@ store_temp_offset(struct device *dev, struct device_attribute *attr, if (err < 0) return err; - val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), -128, 127); + val = DIV_ROUND_CLOSEST(clamp_val(val, -128000, 127000), 1000); mutex_lock(&data->update_lock); data->temp_offset[nr] = val; -- cgit From 5c1de37969b7bc0abcb20b86e91e70caebbd4f89 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 6 Jul 2024 23:51:34 -0700 Subject: hwmon: (w83627ehf) Fix underflows seen when writing limit attributes DIV_ROUND_CLOSEST() after kstrtol() results in an underflow if a large negative number such as -9223372036854775808 is provided by the user. Fix it by reordering clamp_val() and DIV_ROUND_CLOSEST() operations. Signed-off-by: Guenter Roeck --- drivers/hwmon/w83627ehf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c index fe960c0a624f..7d7d70afde65 100644 --- a/drivers/hwmon/w83627ehf.c +++ b/drivers/hwmon/w83627ehf.c @@ -895,7 +895,7 @@ store_target_temp(struct device *dev, struct device_attribute *attr, if (err < 0) return err; - val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0, 127); + val = DIV_ROUND_CLOSEST(clamp_val(val, 0, 127000), 1000); mutex_lock(&data->update_lock); data->target_temp[nr] = val; @@ -920,7 +920,7 @@ store_tolerance(struct device *dev, struct device_attribute *attr, return err; /* Limit the temp to 0C - 15C */ - val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0, 15); + val = DIV_ROUND_CLOSEST(clamp_val(val, 0, 15000), 1000); mutex_lock(&data->update_lock); reg = w83627ehf_read_value(data, W83627EHF_REG_TOLERANCE[nr]); -- cgit From af4d04b8ce08a734889fb0777059063635242614 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 26 Jun 2024 17:21:54 -0700 Subject: hwmon: (amc6821) Stop accepting invalid pwm values The pwm value range is well defined from 0..255. Don't accept any values outside this range. This changes the valid range of pwm1_auto_point2_pwm from 0..254 to 0..255, meaning it can now be equivalent to not only pwm1_auto_point1_pwm (which is always 0) but also to pwm1_auto_point3_pwm (which is always 255). While that may not be practical, there seems to be no technical reason for preventing a user from doing it. Reviewed-by: Quentin Schulz Signed-off-by: Guenter Roeck --- drivers/hwmon/amc6821.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/amc6821.c b/drivers/hwmon/amc6821.c index 9b02b304c2f5..dc35e9b21f91 100644 --- a/drivers/hwmon/amc6821.c +++ b/drivers/hwmon/amc6821.c @@ -355,13 +355,13 @@ static ssize_t pwm1_store(struct device *dev, { struct amc6821_data *data = dev_get_drvdata(dev); struct i2c_client *client = data->client; - long val; - int ret = kstrtol(buf, 10, &val); + u8 val; + int ret = kstrtou8(buf, 10, &val); if (ret) return ret; mutex_lock(&data->update_lock); - data->pwm1 = clamp_val(val , 0, 255); + data->pwm1 = val; i2c_smbus_write_byte_data(client, AMC6821_REG_DCY, data->pwm1); mutex_unlock(&data->update_lock); return count; @@ -558,13 +558,15 @@ static ssize_t pwm1_auto_point_pwm_store(struct device *dev, struct amc6821_data *data = dev_get_drvdata(dev); struct i2c_client *client = data->client; int dpwm; - long val; - int ret = kstrtol(buf, 10, &val); + u8 val; + int ret; + + ret = kstrtou8(buf, 10, &val); if (ret) return ret; mutex_lock(&data->update_lock); - data->pwm1_auto_point_pwm[1] = clamp_val(val, 0, 254); + data->pwm1_auto_point_pwm[1] = val; if (i2c_smbus_write_byte_data(client, AMC6821_REG_DCY_LOW_TEMP, data->pwm1_auto_point_pwm[1])) { dev_err(&client->dev, "Register write error, aborting.\n"); -- cgit From 8f4fd97df27ea4bc8b569c647fb2189a57047cac Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 26 Jun 2024 17:46:04 -0700 Subject: hwmon: (amc6821) Make reading and writing fan speed limits consistent The default value of the maximum fan speed limit register is 0, essentially translating to an unlimited fan speed. When reading the limit, a value of 0 is reported in this case. However, writing a value of 0 results in writing a value of 0xffff into the register, which is inconsistent. To solve the problem, permit writing a limit of 0 for the maximim fan speed, effectively translating to "no limit". Write 0 into the register if a limit value of 0 is written. Otherwise limit the range to <1..6000000> and write 1..0xffff into the register. This ensures that reading and writing from and to a limit register return the same value while at the same time not changing reported values when reading the speed or limits. While at it, restrict fan limit writes to non-negative numbers; writing a negative limit does not make sense and should be reported instead of being corrected. Reviewed-by: Quentin Schulz Signed-off-by: Guenter Roeck --- drivers/hwmon/amc6821.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/amc6821.c b/drivers/hwmon/amc6821.c index dc35e9b21f91..8e3cc33d8073 100644 --- a/drivers/hwmon/amc6821.c +++ b/drivers/hwmon/amc6821.c @@ -616,15 +616,20 @@ static ssize_t fan_store(struct device *dev, struct device_attribute *attr, { struct amc6821_data *data = dev_get_drvdata(dev); struct i2c_client *client = data->client; - long val; + unsigned long val; int ix = to_sensor_dev_attr(attr)->index; - int ret = kstrtol(buf, 10, &val); + int ret = kstrtoul(buf, 10, &val); if (ret) return ret; - val = 1 > val ? 0xFFFF : 6000000/val; + + /* The minimum fan speed must not be unlimited (0) */ + if (ix == IDX_FAN1_MIN && !val) + return -EINVAL; + + val = val > 0 ? 6000000 / clamp_val(val, 1, 6000000) : 0; mutex_lock(&data->update_lock); - data->fan[ix] = (u16) clamp_val(val, 1, 0xFFFF); + data->fan[ix] = clamp_val(val, 0, 0xFFFF); if (i2c_smbus_write_byte_data(client, fan_reg_low[ix], data->fan[ix] & 0xFF)) { dev_err(&client->dev, "Register write error, aborting.\n"); -- cgit From 154a0c2bd6694c45d34a1688e1d9a41f5a854b3f Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 27 Jun 2024 07:05:35 -0700 Subject: hwmon: (amc6821) Rename fan1_div to fan1_pulses The chip does not have a fan divisor. What it does have is a configuration to set either 2 or 4 pulses per fan rotation. Rename the attribute to reflect its use. Update documentation accordingly. Reviewed-by: Quentin Schulz Signed-off-by: Guenter Roeck --- drivers/hwmon/amc6821.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/amc6821.c b/drivers/hwmon/amc6821.c index 8e3cc33d8073..39bf52a5c432 100644 --- a/drivers/hwmon/amc6821.c +++ b/drivers/hwmon/amc6821.c @@ -148,7 +148,7 @@ struct amc6821_data { int temp[TEMP_IDX_LEN]; u16 fan[FAN1_IDX_LEN]; - u8 fan1_div; + u8 fan1_pulses; u8 pwm1; u8 temp1_auto_point_temp[3]; @@ -193,9 +193,9 @@ static struct amc6821_data *amc6821_update_device(struct device *dev) client, fan_reg_hi[i]) << 8; } - data->fan1_div = i2c_smbus_read_byte_data(client, + data->fan1_pulses = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF4); - data->fan1_div = data->fan1_div & AMC6821_CONF4_PSPR ? 4 : 2; + data->fan1_pulses = data->fan1_pulses & AMC6821_CONF4_PSPR ? 4 : 2; data->pwm1_auto_point_pwm[0] = 0; data->pwm1_auto_point_pwm[2] = 255; @@ -646,16 +646,16 @@ EXIT: return count; } -static ssize_t fan1_div_show(struct device *dev, - struct device_attribute *devattr, char *buf) +static ssize_t fan1_pulses_show(struct device *dev, + struct device_attribute *devattr, char *buf) { struct amc6821_data *data = amc6821_update_device(dev); - return sprintf(buf, "%d\n", data->fan1_div); + return sprintf(buf, "%d\n", data->fan1_pulses); } -static ssize_t fan1_div_store(struct device *dev, - struct device_attribute *attr, const char *buf, - size_t count) +static ssize_t fan1_pulses_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) { struct amc6821_data *data = dev_get_drvdata(dev); struct i2c_client *client = data->client; @@ -675,11 +675,11 @@ static ssize_t fan1_div_store(struct device *dev, switch (val) { case 2: config &= ~AMC6821_CONF4_PSPR; - data->fan1_div = 2; + data->fan1_pulses = 2; break; case 4: config |= AMC6821_CONF4_PSPR; - data->fan1_div = 4; + data->fan1_pulses = 4; break; default: count = -EINVAL; @@ -714,7 +714,7 @@ static SENSOR_DEVICE_ATTR_RO(fan1_input, fan, IDX_FAN1_INPUT); static SENSOR_DEVICE_ATTR_RW(fan1_min, fan, IDX_FAN1_MIN); static SENSOR_DEVICE_ATTR_RW(fan1_max, fan, IDX_FAN1_MAX); static SENSOR_DEVICE_ATTR_RO(fan1_fault, fan1_fault, 0); -static SENSOR_DEVICE_ATTR_RW(fan1_div, fan1_div, 0); +static SENSOR_DEVICE_ATTR_RW(fan1_pulses, fan1_pulses, 0); static SENSOR_DEVICE_ATTR_RW(pwm1, pwm1, 0); static SENSOR_DEVICE_ATTR_RW(pwm1_enable, pwm1_enable, 0); @@ -757,7 +757,7 @@ static struct attribute *amc6821_attrs[] = { &sensor_dev_attr_fan1_min.dev_attr.attr, &sensor_dev_attr_fan1_max.dev_attr.attr, &sensor_dev_attr_fan1_fault.dev_attr.attr, - &sensor_dev_attr_fan1_div.dev_attr.attr, + &sensor_dev_attr_fan1_pulses.dev_attr.attr, &sensor_dev_attr_pwm1.dev_attr.attr, &sensor_dev_attr_pwm1_enable.dev_attr.attr, &sensor_dev_attr_pwm1_auto_channels_temp.dev_attr.attr, -- cgit From becbd16ed2f8f427239ffda66b5d894008bc56af Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 27 Jun 2024 07:50:48 -0700 Subject: hwmon: (amc6821) Add support for fan1_target and pwm1_enable mode 4 After setting fan1_target and setting pwm1_enable to 4, the fan controller tries to achieve the requested fan speed. Reviewed-by: Quentin Schulz Signed-off-by: Guenter Roeck --- drivers/hwmon/amc6821.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/amc6821.c b/drivers/hwmon/amc6821.c index 39bf52a5c432..9e9a70afbfd4 100644 --- a/drivers/hwmon/amc6821.c +++ b/drivers/hwmon/amc6821.c @@ -66,6 +66,8 @@ enum chips { amc6821 }; #define AMC6821_REG_TACH_LLIMITH 0x11 #define AMC6821_REG_TACH_HLIMITL 0x12 #define AMC6821_REG_TACH_HLIMITH 0x13 +#define AMC6821_REG_TACH_SETTINGL 0x1e +#define AMC6821_REG_TACH_SETTINGH 0x1f #define AMC6821_CONF1_START 0x01 #define AMC6821_CONF1_FAN_INT_EN 0x02 @@ -122,17 +124,18 @@ static const u8 temp_reg[] = {AMC6821_REG_LTEMP_HI, AMC6821_REG_RTEMP_LIMIT_MAX, AMC6821_REG_RTEMP_CRIT, }; -enum {IDX_FAN1_INPUT = 0, IDX_FAN1_MIN, IDX_FAN1_MAX, +enum {IDX_FAN1_INPUT = 0, IDX_FAN1_MIN, IDX_FAN1_MAX, IDX_FAN1_TARGET, FAN1_IDX_LEN, }; static const u8 fan_reg_low[] = {AMC6821_REG_TDATA_LOW, AMC6821_REG_TACH_LLIMITL, - AMC6821_REG_TACH_HLIMITL, }; - + AMC6821_REG_TACH_HLIMITL, + AMC6821_REG_TACH_SETTINGL, }; static const u8 fan_reg_hi[] = {AMC6821_REG_TDATA_HI, AMC6821_REG_TACH_LLIMITH, - AMC6821_REG_TACH_HLIMITH, }; + AMC6821_REG_TACH_HLIMITH, + AMC6821_REG_TACH_SETTINGH, }; /* * Client data (each client gets its own) @@ -250,10 +253,10 @@ static struct amc6821_data *amc6821_update_device(struct device *dev) break; case 1: /* * semi-open loop: software sets rpm, chip controls - * pwm1, currently not implemented + * pwm1 */ data->pwm1_auto_channels_temp = 0; - data->pwm1_enable = 0; + data->pwm1_enable = 4; break; } @@ -407,6 +410,10 @@ static ssize_t pwm1_enable_store(struct device *dev, config |= AMC6821_CONF1_FDRC0; config |= AMC6821_CONF1_FDRC1; break; + case 4: + config |= AMC6821_CONF1_FDRC0; + config &= ~AMC6821_CONF1_FDRC1; + break; default: count = -EINVAL; goto unlock; @@ -622,8 +629,8 @@ static ssize_t fan_store(struct device *dev, struct device_attribute *attr, if (ret) return ret; - /* The minimum fan speed must not be unlimited (0) */ - if (ix == IDX_FAN1_MIN && !val) + /* Minimum and target fan speed must not be unlimited (0) */ + if ((ix == IDX_FAN1_MIN || ix == IDX_FAN1_TARGET) && !val) return -EINVAL; val = val > 0 ? 6000000 / clamp_val(val, 1, 6000000) : 0; @@ -713,6 +720,7 @@ static SENSOR_DEVICE_ATTR_RO(temp2_crit_alarm, temp_alarm, IDX_TEMP2_CRIT); static SENSOR_DEVICE_ATTR_RO(fan1_input, fan, IDX_FAN1_INPUT); static SENSOR_DEVICE_ATTR_RW(fan1_min, fan, IDX_FAN1_MIN); static SENSOR_DEVICE_ATTR_RW(fan1_max, fan, IDX_FAN1_MAX); +static SENSOR_DEVICE_ATTR_RW(fan1_target, fan, IDX_FAN1_TARGET); static SENSOR_DEVICE_ATTR_RO(fan1_fault, fan1_fault, 0); static SENSOR_DEVICE_ATTR_RW(fan1_pulses, fan1_pulses, 0); @@ -756,6 +764,7 @@ static struct attribute *amc6821_attrs[] = { &sensor_dev_attr_fan1_input.dev_attr.attr, &sensor_dev_attr_fan1_min.dev_attr.attr, &sensor_dev_attr_fan1_max.dev_attr.attr, + &sensor_dev_attr_fan1_target.dev_attr.attr, &sensor_dev_attr_fan1_fault.dev_attr.attr, &sensor_dev_attr_fan1_pulses.dev_attr.attr, &sensor_dev_attr_pwm1.dev_attr.attr, -- cgit From e5cd7dd9a4e6e0692938e00fb43f14fb4711c17b Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 27 Jun 2024 08:27:53 -0700 Subject: hwmon: (amc6821) Reorder include files, drop unnecessary ones Reorder include files to alphabetic order to simplify maintenance, and drop the unnecessary kernel.h include. No functional change intended. Reviewed-by: Quentin Schulz Signed-off-by: Guenter Roeck --- drivers/hwmon/amc6821.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/amc6821.c b/drivers/hwmon/amc6821.c index 9e9a70afbfd4..8869dbe5a733 100644 --- a/drivers/hwmon/amc6821.c +++ b/drivers/hwmon/amc6821.c @@ -8,16 +8,15 @@ * Copyright (C) 2007 Hans J. Koch */ -#include /* Needed for KERN_INFO */ -#include -#include -#include -#include -#include +#include #include #include -#include +#include +#include +#include +#include #include +#include /* * Addresses to scan. -- cgit From d632e82993899a837ba0efb6679584aa3efc16b6 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 27 Jun 2024 09:41:41 -0700 Subject: hwmon: (amc6821) Use tabs for column alignment in defines Using tabs for column alignment makes the code easier to read. No functional change intended. Reviewed-by: Quentin Schulz Signed-off-by: Guenter Roeck --- drivers/hwmon/amc6821.c | 142 ++++++++++++++++++++++++------------------------ 1 file changed, 71 insertions(+), 71 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/amc6821.c b/drivers/hwmon/amc6821.c index 8869dbe5a733..bb20ccde5fea 100644 --- a/drivers/hwmon/amc6821.c +++ b/drivers/hwmon/amc6821.c @@ -37,77 +37,77 @@ module_param(init, int, 0444); enum chips { amc6821 }; -#define AMC6821_REG_DEV_ID 0x3D -#define AMC6821_REG_COMP_ID 0x3E -#define AMC6821_REG_CONF1 0x00 -#define AMC6821_REG_CONF2 0x01 -#define AMC6821_REG_CONF3 0x3F -#define AMC6821_REG_CONF4 0x04 -#define AMC6821_REG_STAT1 0x02 -#define AMC6821_REG_STAT2 0x03 -#define AMC6821_REG_TDATA_LOW 0x08 -#define AMC6821_REG_TDATA_HI 0x09 -#define AMC6821_REG_LTEMP_HI 0x0A -#define AMC6821_REG_RTEMP_HI 0x0B -#define AMC6821_REG_LTEMP_LIMIT_MIN 0x15 -#define AMC6821_REG_LTEMP_LIMIT_MAX 0x14 -#define AMC6821_REG_RTEMP_LIMIT_MIN 0x19 -#define AMC6821_REG_RTEMP_LIMIT_MAX 0x18 -#define AMC6821_REG_LTEMP_CRIT 0x1B -#define AMC6821_REG_RTEMP_CRIT 0x1D -#define AMC6821_REG_PSV_TEMP 0x1C -#define AMC6821_REG_DCY 0x22 -#define AMC6821_REG_LTEMP_FAN_CTRL 0x24 -#define AMC6821_REG_RTEMP_FAN_CTRL 0x25 -#define AMC6821_REG_DCY_LOW_TEMP 0x21 - -#define AMC6821_REG_TACH_LLIMITL 0x10 -#define AMC6821_REG_TACH_LLIMITH 0x11 -#define AMC6821_REG_TACH_HLIMITL 0x12 -#define AMC6821_REG_TACH_HLIMITH 0x13 -#define AMC6821_REG_TACH_SETTINGL 0x1e -#define AMC6821_REG_TACH_SETTINGH 0x1f - -#define AMC6821_CONF1_START 0x01 -#define AMC6821_CONF1_FAN_INT_EN 0x02 -#define AMC6821_CONF1_FANIE 0x04 -#define AMC6821_CONF1_PWMINV 0x08 -#define AMC6821_CONF1_FAN_FAULT_EN 0x10 -#define AMC6821_CONF1_FDRC0 0x20 -#define AMC6821_CONF1_FDRC1 0x40 -#define AMC6821_CONF1_THERMOVIE 0x80 - -#define AMC6821_CONF2_PWM_EN 0x01 -#define AMC6821_CONF2_TACH_MODE 0x02 -#define AMC6821_CONF2_TACH_EN 0x04 -#define AMC6821_CONF2_RTFIE 0x08 -#define AMC6821_CONF2_LTOIE 0x10 -#define AMC6821_CONF2_RTOIE 0x20 -#define AMC6821_CONF2_PSVIE 0x40 -#define AMC6821_CONF2_RST 0x80 - -#define AMC6821_CONF3_THERM_FAN_EN 0x80 -#define AMC6821_CONF3_REV_MASK 0x0F - -#define AMC6821_CONF4_OVREN 0x10 -#define AMC6821_CONF4_TACH_FAST 0x20 -#define AMC6821_CONF4_PSPR 0x40 -#define AMC6821_CONF4_MODE 0x80 - -#define AMC6821_STAT1_RPM_ALARM 0x01 -#define AMC6821_STAT1_FANS 0x02 -#define AMC6821_STAT1_RTH 0x04 -#define AMC6821_STAT1_RTL 0x08 -#define AMC6821_STAT1_R_THERM 0x10 -#define AMC6821_STAT1_RTF 0x20 -#define AMC6821_STAT1_LTH 0x40 -#define AMC6821_STAT1_LTL 0x80 - -#define AMC6821_STAT2_RTC 0x08 -#define AMC6821_STAT2_LTC 0x10 -#define AMC6821_STAT2_LPSV 0x20 -#define AMC6821_STAT2_L_THERM 0x40 -#define AMC6821_STAT2_THERM_IN 0x80 +#define AMC6821_REG_DEV_ID 0x3D +#define AMC6821_REG_COMP_ID 0x3E +#define AMC6821_REG_CONF1 0x00 +#define AMC6821_REG_CONF2 0x01 +#define AMC6821_REG_CONF3 0x3F +#define AMC6821_REG_CONF4 0x04 +#define AMC6821_REG_STAT1 0x02 +#define AMC6821_REG_STAT2 0x03 +#define AMC6821_REG_TDATA_LOW 0x08 +#define AMC6821_REG_TDATA_HI 0x09 +#define AMC6821_REG_LTEMP_HI 0x0A +#define AMC6821_REG_RTEMP_HI 0x0B +#define AMC6821_REG_LTEMP_LIMIT_MIN 0x15 +#define AMC6821_REG_LTEMP_LIMIT_MAX 0x14 +#define AMC6821_REG_RTEMP_LIMIT_MIN 0x19 +#define AMC6821_REG_RTEMP_LIMIT_MAX 0x18 +#define AMC6821_REG_LTEMP_CRIT 0x1B +#define AMC6821_REG_RTEMP_CRIT 0x1D +#define AMC6821_REG_PSV_TEMP 0x1C +#define AMC6821_REG_DCY 0x22 +#define AMC6821_REG_LTEMP_FAN_CTRL 0x24 +#define AMC6821_REG_RTEMP_FAN_CTRL 0x25 +#define AMC6821_REG_DCY_LOW_TEMP 0x21 + +#define AMC6821_REG_TACH_LLIMITL 0x10 +#define AMC6821_REG_TACH_LLIMITH 0x11 +#define AMC6821_REG_TACH_HLIMITL 0x12 +#define AMC6821_REG_TACH_HLIMITH 0x13 +#define AMC6821_REG_TACH_SETTINGL 0x1e +#define AMC6821_REG_TACH_SETTINGH 0x1f + +#define AMC6821_CONF1_START 0x01 +#define AMC6821_CONF1_FAN_INT_EN 0x02 +#define AMC6821_CONF1_FANIE 0x04 +#define AMC6821_CONF1_PWMINV 0x08 +#define AMC6821_CONF1_FAN_FAULT_EN 0x10 +#define AMC6821_CONF1_FDRC0 0x20 +#define AMC6821_CONF1_FDRC1 0x40 +#define AMC6821_CONF1_THERMOVIE 0x80 + +#define AMC6821_CONF2_PWM_EN 0x01 +#define AMC6821_CONF2_TACH_MODE 0x02 +#define AMC6821_CONF2_TACH_EN 0x04 +#define AMC6821_CONF2_RTFIE 0x08 +#define AMC6821_CONF2_LTOIE 0x10 +#define AMC6821_CONF2_RTOIE 0x20 +#define AMC6821_CONF2_PSVIE 0x40 +#define AMC6821_CONF2_RST 0x80 + +#define AMC6821_CONF3_THERM_FAN_EN 0x80 +#define AMC6821_CONF3_REV_MASK 0x0F + +#define AMC6821_CONF4_OVREN 0x10 +#define AMC6821_CONF4_TACH_FAST 0x20 +#define AMC6821_CONF4_PSPR 0x40 +#define AMC6821_CONF4_MODE 0x80 + +#define AMC6821_STAT1_RPM_ALARM 0x01 +#define AMC6821_STAT1_FANS 0x02 +#define AMC6821_STAT1_RTH 0x04 +#define AMC6821_STAT1_RTL 0x08 +#define AMC6821_STAT1_R_THERM 0x10 +#define AMC6821_STAT1_RTF 0x20 +#define AMC6821_STAT1_LTH 0x40 +#define AMC6821_STAT1_LTL 0x80 + +#define AMC6821_STAT2_RTC 0x08 +#define AMC6821_STAT2_LTC 0x10 +#define AMC6821_STAT2_LPSV 0x20 +#define AMC6821_STAT2_L_THERM 0x40 +#define AMC6821_STAT2_THERM_IN 0x80 enum {IDX_TEMP1_INPUT = 0, IDX_TEMP1_MIN, IDX_TEMP1_MAX, IDX_TEMP1_CRIT, IDX_TEMP2_INPUT, IDX_TEMP2_MIN, -- cgit From a9c2f41f02aba3ceb0f9f795dc00753a52b284cd Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 27 Jun 2024 09:46:12 -0700 Subject: hwmon: (amc6821) Use BIT() and GENMASK() Use BIT() and GENMASK() for bit and mask definitions to help distinguish bit and mask definitions from other defines and to make the code easier to read. No functional change intended. Reviewed-by: Quentin Schulz Signed-off-by: Guenter Roeck --- drivers/hwmon/amc6821.c | 81 +++++++++++++++++++++++++------------------------ 1 file changed, 41 insertions(+), 40 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/amc6821.c b/drivers/hwmon/amc6821.c index bb20ccde5fea..546e79ce93b9 100644 --- a/drivers/hwmon/amc6821.c +++ b/drivers/hwmon/amc6821.c @@ -8,6 +8,7 @@ * Copyright (C) 2007 Hans J. Koch */ +#include #include #include #include @@ -68,46 +69,46 @@ enum chips { amc6821 }; #define AMC6821_REG_TACH_SETTINGL 0x1e #define AMC6821_REG_TACH_SETTINGH 0x1f -#define AMC6821_CONF1_START 0x01 -#define AMC6821_CONF1_FAN_INT_EN 0x02 -#define AMC6821_CONF1_FANIE 0x04 -#define AMC6821_CONF1_PWMINV 0x08 -#define AMC6821_CONF1_FAN_FAULT_EN 0x10 -#define AMC6821_CONF1_FDRC0 0x20 -#define AMC6821_CONF1_FDRC1 0x40 -#define AMC6821_CONF1_THERMOVIE 0x80 - -#define AMC6821_CONF2_PWM_EN 0x01 -#define AMC6821_CONF2_TACH_MODE 0x02 -#define AMC6821_CONF2_TACH_EN 0x04 -#define AMC6821_CONF2_RTFIE 0x08 -#define AMC6821_CONF2_LTOIE 0x10 -#define AMC6821_CONF2_RTOIE 0x20 -#define AMC6821_CONF2_PSVIE 0x40 -#define AMC6821_CONF2_RST 0x80 - -#define AMC6821_CONF3_THERM_FAN_EN 0x80 -#define AMC6821_CONF3_REV_MASK 0x0F - -#define AMC6821_CONF4_OVREN 0x10 -#define AMC6821_CONF4_TACH_FAST 0x20 -#define AMC6821_CONF4_PSPR 0x40 -#define AMC6821_CONF4_MODE 0x80 - -#define AMC6821_STAT1_RPM_ALARM 0x01 -#define AMC6821_STAT1_FANS 0x02 -#define AMC6821_STAT1_RTH 0x04 -#define AMC6821_STAT1_RTL 0x08 -#define AMC6821_STAT1_R_THERM 0x10 -#define AMC6821_STAT1_RTF 0x20 -#define AMC6821_STAT1_LTH 0x40 -#define AMC6821_STAT1_LTL 0x80 - -#define AMC6821_STAT2_RTC 0x08 -#define AMC6821_STAT2_LTC 0x10 -#define AMC6821_STAT2_LPSV 0x20 -#define AMC6821_STAT2_L_THERM 0x40 -#define AMC6821_STAT2_THERM_IN 0x80 +#define AMC6821_CONF1_START BIT(0) +#define AMC6821_CONF1_FAN_INT_EN BIT(1) +#define AMC6821_CONF1_FANIE BIT(2) +#define AMC6821_CONF1_PWMINV BIT(3) +#define AMC6821_CONF1_FAN_FAULT_EN BIT(4) +#define AMC6821_CONF1_FDRC0 BIT(5) +#define AMC6821_CONF1_FDRC1 BIT(6) +#define AMC6821_CONF1_THERMOVIE BIT(7) + +#define AMC6821_CONF2_PWM_EN BIT(0) +#define AMC6821_CONF2_TACH_MODE BIT(1) +#define AMC6821_CONF2_TACH_EN BIT(2) +#define AMC6821_CONF2_RTFIE BIT(3) +#define AMC6821_CONF2_LTOIE BIT(4) +#define AMC6821_CONF2_RTOIE BIT(5) +#define AMC6821_CONF2_PSVIE BIT(6) +#define AMC6821_CONF2_RST BIT(7) + +#define AMC6821_CONF3_THERM_FAN_EN BIT(7) +#define AMC6821_CONF3_REV_MASK GENMASK(3, 0) + +#define AMC6821_CONF4_OVREN BIT(4) +#define AMC6821_CONF4_TACH_FAST BIT(5) +#define AMC6821_CONF4_PSPR BIT(6) +#define AMC6821_CONF4_MODE BIT(7) + +#define AMC6821_STAT1_RPM_ALARM BIT(0) +#define AMC6821_STAT1_FANS BIT(1) +#define AMC6821_STAT1_RTH BIT(2) +#define AMC6821_STAT1_RTL BIT(3) +#define AMC6821_STAT1_R_THERM BIT(4) +#define AMC6821_STAT1_RTF BIT(5) +#define AMC6821_STAT1_LTH BIT(6) +#define AMC6821_STAT1_LTL BIT(7) + +#define AMC6821_STAT2_RTC BIT(3) +#define AMC6821_STAT2_LTC BIT(4) +#define AMC6821_STAT2_LPSV BIT(5) +#define AMC6821_STAT2_L_THERM BIT(6) +#define AMC6821_STAT2_THERM_IN BIT(7) enum {IDX_TEMP1_INPUT = 0, IDX_TEMP1_MIN, IDX_TEMP1_MAX, IDX_TEMP1_CRIT, IDX_TEMP2_INPUT, IDX_TEMP2_MIN, -- cgit From 8d061050fd826b0cc425765b1ac22b4c088dedbb Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 27 Jun 2024 11:08:14 -0700 Subject: hwmon: (amc6821) Drop unnecessary enum chips The driver only supports a single chip, so an enum to determine the chip type is unnecessary. Drop it. No functional change intended. Reviewed-by: Quentin Schulz Signed-off-by: Guenter Roeck --- drivers/hwmon/amc6821.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/amc6821.c b/drivers/hwmon/amc6821.c index 546e79ce93b9..295a9148779d 100644 --- a/drivers/hwmon/amc6821.c +++ b/drivers/hwmon/amc6821.c @@ -36,8 +36,6 @@ module_param(pwminv, int, 0444); static int init = 1; /*Power-on initialization.*/ module_param(init, int, 0444); -enum chips { amc6821 }; - #define AMC6821_REG_DEV_ID 0x3D #define AMC6821_REG_COMP_ID 0x3E #define AMC6821_REG_CONF1 0x00 @@ -944,7 +942,7 @@ static int amc6821_probe(struct i2c_client *client) } static const struct i2c_device_id amc6821_id[] = { - { "amc6821", amc6821 }, + { "amc6821", 0 }, { } }; @@ -953,7 +951,6 @@ MODULE_DEVICE_TABLE(i2c, amc6821_id); static const struct of_device_id __maybe_unused amc6821_of_match[] = { { .compatible = "ti,amc6821", - .data = (void *)amc6821, }, { } }; -- cgit From a051d507ba1718e0e833f3d8d80defc85ca20c2d Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 27 Jun 2024 10:48:27 -0700 Subject: hwmon: (amc6821) Convert to use regmap Use regmap for register accesses and caching. While at it, use sysfs_emit() instead of sprintf() to write sysfs attribute data, and remove spurious debug messages which would only be seen as result of a bug in the code. Also make sure that error codes are propagated and not replaced with -EIO. While at it, introduce rounding of written temperature values and for internal calculations to reduce deviation from written values and as much as possible. No functional change intended except for differences introduced by rounding. Reviewed-by: Quentin Schulz Signed-off-by: Guenter Roeck --- drivers/hwmon/Kconfig | 1 + drivers/hwmon/amc6821.c | 827 ++++++++++++++++++++++-------------------------- 2 files changed, 384 insertions(+), 444 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index d5eced417fc3..3d6f49526716 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -2127,6 +2127,7 @@ config SENSORS_ADS7871 config SENSORS_AMC6821 tristate "Texas Instruments AMC6821" depends on I2C + select REGMAP_I2C help If you say yes here you get support for the Texas Instruments AMC6821 hardware monitoring chips. diff --git a/drivers/hwmon/amc6821.c b/drivers/hwmon/amc6821.c index 295a9148779d..0b9e83fc8188 100644 --- a/drivers/hwmon/amc6821.c +++ b/drivers/hwmon/amc6821.c @@ -8,15 +8,18 @@ * Copyright (C) 2007 Hans J. Koch */ +#include +#include #include #include #include #include #include #include -#include +#include #include #include +#include #include /* @@ -44,6 +47,7 @@ module_param(init, int, 0444); #define AMC6821_REG_CONF4 0x04 #define AMC6821_REG_STAT1 0x02 #define AMC6821_REG_STAT2 0x03 +#define AMC6821_REG_TEMP_LO 0x06 #define AMC6821_REG_TDATA_LOW 0x08 #define AMC6821_REG_TDATA_HI 0x09 #define AMC6821_REG_LTEMP_HI 0x0A @@ -61,11 +65,8 @@ module_param(init, int, 0444); #define AMC6821_REG_DCY_LOW_TEMP 0x21 #define AMC6821_REG_TACH_LLIMITL 0x10 -#define AMC6821_REG_TACH_LLIMITH 0x11 #define AMC6821_REG_TACH_HLIMITL 0x12 -#define AMC6821_REG_TACH_HLIMITH 0x13 #define AMC6821_REG_TACH_SETTINGL 0x1e -#define AMC6821_REG_TACH_SETTINGH 0x1f #define AMC6821_CONF1_START BIT(0) #define AMC6821_CONF1_FAN_INT_EN BIT(1) @@ -108,6 +109,9 @@ module_param(init, int, 0444); #define AMC6821_STAT2_L_THERM BIT(6) #define AMC6821_STAT2_THERM_IN BIT(7) +#define AMC6821_TEMP_SLOPE_MASK GENMASK(2, 0) +#define AMC6821_TEMP_LIMIT_MASK GENMASK(7, 3) + enum {IDX_TEMP1_INPUT = 0, IDX_TEMP1_MIN, IDX_TEMP1_MAX, IDX_TEMP1_CRIT, IDX_TEMP2_INPUT, IDX_TEMP2_MIN, IDX_TEMP2_MAX, IDX_TEMP2_CRIT, @@ -130,224 +134,159 @@ static const u8 fan_reg_low[] = {AMC6821_REG_TDATA_LOW, AMC6821_REG_TACH_HLIMITL, AMC6821_REG_TACH_SETTINGL, }; -static const u8 fan_reg_hi[] = {AMC6821_REG_TDATA_HI, - AMC6821_REG_TACH_LLIMITH, - AMC6821_REG_TACH_HLIMITH, - AMC6821_REG_TACH_SETTINGH, }; - /* * Client data (each client gets its own) */ struct amc6821_data { - struct i2c_client *client; + struct regmap *regmap; struct mutex update_lock; - bool valid; /* false until following fields are valid */ - unsigned long last_updated; /* in jiffies */ - - /* register values */ - int temp[TEMP_IDX_LEN]; - - u16 fan[FAN1_IDX_LEN]; - u8 fan1_pulses; - - u8 pwm1; - u8 temp1_auto_point_temp[3]; - u8 temp2_auto_point_temp[3]; - u8 pwm1_auto_point_pwm[3]; - u8 pwm1_enable; - u8 pwm1_auto_channels_temp; - - u8 stat1; - u8 stat2; }; -static struct amc6821_data *amc6821_update_device(struct device *dev) +/* + * Return 0 on success or negative error code. + * + * temps returns set of three temperatures, in °C: + * temps[0]: Passive cooling temperature, applies to both channels + * temps[1]: Low temperature, start slope calculations + * temps[2]: High temperature + * + * Channel 0: local, channel 1: remote. + */ +static int amc6821_get_auto_point_temps(struct regmap *regmap, int channel, u8 *temps) { - struct amc6821_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - int timeout = HZ; - u8 reg; - int i; + u32 pwm, regval; + int err; - mutex_lock(&data->update_lock); + err = regmap_read(regmap, AMC6821_REG_DCY_LOW_TEMP, &pwm); + if (err) + return err; - if (time_after(jiffies, data->last_updated + timeout) || - !data->valid) { - - for (i = 0; i < TEMP_IDX_LEN; i++) - data->temp[i] = (int8_t)i2c_smbus_read_byte_data( - client, temp_reg[i]); - - data->stat1 = i2c_smbus_read_byte_data(client, - AMC6821_REG_STAT1); - data->stat2 = i2c_smbus_read_byte_data(client, - AMC6821_REG_STAT2); - - data->pwm1 = i2c_smbus_read_byte_data(client, - AMC6821_REG_DCY); - for (i = 0; i < FAN1_IDX_LEN; i++) { - data->fan[i] = i2c_smbus_read_byte_data( - client, - fan_reg_low[i]); - data->fan[i] += i2c_smbus_read_byte_data( - client, - fan_reg_hi[i]) << 8; - } - data->fan1_pulses = i2c_smbus_read_byte_data(client, - AMC6821_REG_CONF4); - data->fan1_pulses = data->fan1_pulses & AMC6821_CONF4_PSPR ? 4 : 2; - - data->pwm1_auto_point_pwm[0] = 0; - data->pwm1_auto_point_pwm[2] = 255; - data->pwm1_auto_point_pwm[1] = i2c_smbus_read_byte_data(client, - AMC6821_REG_DCY_LOW_TEMP); - - data->temp1_auto_point_temp[0] = - i2c_smbus_read_byte_data(client, - AMC6821_REG_PSV_TEMP); - data->temp2_auto_point_temp[0] = - data->temp1_auto_point_temp[0]; - reg = i2c_smbus_read_byte_data(client, - AMC6821_REG_LTEMP_FAN_CTRL); - data->temp1_auto_point_temp[1] = (reg & 0xF8) >> 1; - reg &= 0x07; - reg = 0x20 >> reg; - if (reg > 0) - data->temp1_auto_point_temp[2] = - data->temp1_auto_point_temp[1] + - (data->pwm1_auto_point_pwm[2] - - data->pwm1_auto_point_pwm[1]) / reg; - else - data->temp1_auto_point_temp[2] = 255; - - reg = i2c_smbus_read_byte_data(client, - AMC6821_REG_RTEMP_FAN_CTRL); - data->temp2_auto_point_temp[1] = (reg & 0xF8) >> 1; - reg &= 0x07; - reg = 0x20 >> reg; - if (reg > 0) - data->temp2_auto_point_temp[2] = - data->temp2_auto_point_temp[1] + - (data->pwm1_auto_point_pwm[2] - - data->pwm1_auto_point_pwm[1]) / reg; - else - data->temp2_auto_point_temp[2] = 255; - - reg = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF1); - reg = (reg >> 5) & 0x3; - switch (reg) { - case 0: /*open loop: software sets pwm1*/ - data->pwm1_auto_channels_temp = 0; - data->pwm1_enable = 1; - break; - case 2: /*closed loop: remote T (temp2)*/ - data->pwm1_auto_channels_temp = 2; - data->pwm1_enable = 2; - break; - case 3: /*closed loop: local and remote T (temp2)*/ - data->pwm1_auto_channels_temp = 3; - data->pwm1_enable = 3; - break; - case 1: /* - * semi-open loop: software sets rpm, chip controls - * pwm1 - */ - data->pwm1_auto_channels_temp = 0; - data->pwm1_enable = 4; - break; - } + err = regmap_read(regmap, AMC6821_REG_PSV_TEMP, ®val); + if (err) + return err; + temps[0] = regval; - data->last_updated = jiffies; - data->valid = true; - } - mutex_unlock(&data->update_lock); - return data; + err = regmap_read(regmap, + channel ? AMC6821_REG_RTEMP_FAN_CTRL : AMC6821_REG_LTEMP_FAN_CTRL, + ®val); + if (err) + return err; + temps[1] = FIELD_GET(AMC6821_TEMP_LIMIT_MASK, regval) * 4; + + /* slope is 32 >> in °C */ + regval = 32 >> FIELD_GET(AMC6821_TEMP_SLOPE_MASK, regval); + if (regval) + temps[2] = temps[1] + DIV_ROUND_CLOSEST(255 - pwm, regval); + else + temps[2] = 255; + + return 0; } static ssize_t temp_show(struct device *dev, struct device_attribute *devattr, char *buf) { - struct amc6821_data *data = amc6821_update_device(dev); + struct amc6821_data *data = dev_get_drvdata(dev); int ix = to_sensor_dev_attr(devattr)->index; + u32 regval; + int err; - return sprintf(buf, "%d\n", data->temp[ix] * 1000); + err = regmap_read(data->regmap, temp_reg[ix], ®val); + if (err) + return err; + + return sysfs_emit(buf, "%d\n", sign_extend32(regval, 7) * 1000); } static ssize_t temp_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct amc6821_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; int ix = to_sensor_dev_attr(attr)->index; long val; + int err; int ret = kstrtol(buf, 10, &val); if (ret) return ret; val = clamp_val(val / 1000, -128, 127); - mutex_lock(&data->update_lock); - data->temp[ix] = val; - if (i2c_smbus_write_byte_data(client, temp_reg[ix], data->temp[ix])) { - dev_err(&client->dev, "Register write error, aborting.\n"); - count = -EIO; - } - mutex_unlock(&data->update_lock); + err = regmap_write(data->regmap, temp_reg[ix], val); + if (err) + return err; + return count; } static ssize_t temp_alarm_show(struct device *dev, struct device_attribute *devattr, char *buf) { - struct amc6821_data *data = amc6821_update_device(dev); + struct amc6821_data *data = dev_get_drvdata(dev); int ix = to_sensor_dev_attr(devattr)->index; - u8 flag; + u32 regval, mask, reg; + int err; switch (ix) { case IDX_TEMP1_MIN: - flag = data->stat1 & AMC6821_STAT1_LTL; + reg = AMC6821_REG_STAT1; + mask = AMC6821_STAT1_LTL; break; case IDX_TEMP1_MAX: - flag = data->stat1 & AMC6821_STAT1_LTH; + reg = AMC6821_REG_STAT1; + mask = AMC6821_STAT1_LTH; break; case IDX_TEMP1_CRIT: - flag = data->stat2 & AMC6821_STAT2_LTC; + reg = AMC6821_REG_STAT2; + mask = AMC6821_STAT2_LTC; break; case IDX_TEMP2_MIN: - flag = data->stat1 & AMC6821_STAT1_RTL; + reg = AMC6821_REG_STAT1; + mask = AMC6821_STAT1_RTL; break; case IDX_TEMP2_MAX: - flag = data->stat1 & AMC6821_STAT1_RTH; + reg = AMC6821_REG_STAT1; + mask = AMC6821_STAT1_RTH; break; case IDX_TEMP2_CRIT: - flag = data->stat2 & AMC6821_STAT2_RTC; + reg = AMC6821_REG_STAT2; + mask = AMC6821_STAT2_RTC; break; default: - dev_dbg(dev, "Unknown attr->index (%d).\n", ix); return -EINVAL; } - if (flag) - return sprintf(buf, "1"); - else - return sprintf(buf, "0"); + err = regmap_read(data->regmap, reg, ®val); + if (err) + return err; + return sysfs_emit(buf, "%d\n", !!(regval & mask)); } static ssize_t temp2_fault_show(struct device *dev, struct device_attribute *devattr, char *buf) { - struct amc6821_data *data = amc6821_update_device(dev); - if (data->stat1 & AMC6821_STAT1_RTF) - return sprintf(buf, "1"); - else - return sprintf(buf, "0"); + struct amc6821_data *data = dev_get_drvdata(dev); + u32 regval; + int err; + + err = regmap_read(data->regmap, AMC6821_REG_STAT1, ®val); + if (err) + return err; + + return sysfs_emit(buf, "%d\n", !!(regval & AMC6821_STAT1_RTF)); } static ssize_t pwm1_show(struct device *dev, struct device_attribute *devattr, char *buf) { - struct amc6821_data *data = amc6821_update_device(dev); - return sprintf(buf, "%d\n", data->pwm1); + struct amc6821_data *data = dev_get_drvdata(dev); + u32 regval; + int err; + + err = regmap_read(data->regmap, AMC6821_REG_DCY, ®val); + if (err) + return err; + + return sysfs_emit(buf, "%d\n", regval); } static ssize_t pwm1_store(struct device *dev, @@ -355,24 +294,43 @@ static ssize_t pwm1_store(struct device *dev, size_t count) { struct amc6821_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; u8 val; int ret = kstrtou8(buf, 10, &val); if (ret) return ret; - mutex_lock(&data->update_lock); - data->pwm1 = val; - i2c_smbus_write_byte_data(client, AMC6821_REG_DCY, data->pwm1); - mutex_unlock(&data->update_lock); + ret = regmap_write(data->regmap, AMC6821_REG_DCY, val); + if (ret) + return ret; + return count; } static ssize_t pwm1_enable_show(struct device *dev, struct device_attribute *devattr, char *buf) { - struct amc6821_data *data = amc6821_update_device(dev); - return sprintf(buf, "%d\n", data->pwm1_enable); + struct amc6821_data *data = dev_get_drvdata(dev); + int err; + u32 val; + + err = regmap_read(data->regmap, AMC6821_REG_CONF1, &val); + if (err) + return err; + switch (val & (AMC6821_CONF1_FDRC0 | AMC6821_CONF1_FDRC1)) { + case 0: + val = 1; /* manual */ + break; + case AMC6821_CONF1_FDRC0: + val = 4; /* target rpm (fan1_target) controlled */ + break; + case AMC6821_CONF1_FDRC1: + val = 2; /* remote temp controlled */ + break; + default: + val = 3; /* max(local, remote) temp controlled */ + break; + } + return sysfs_emit(buf, "%d\n", val); } static ssize_t pwm1_enable_store(struct device *dev, @@ -380,49 +338,37 @@ static ssize_t pwm1_enable_store(struct device *dev, const char *buf, size_t count) { struct amc6821_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; long val; - int config = kstrtol(buf, 10, &val); - if (config) - return config; + u32 mode; + int err; - mutex_lock(&data->update_lock); - config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF1); - if (config < 0) { - dev_err(&client->dev, - "Error reading configuration register, aborting.\n"); - count = config; - goto unlock; - } + err = kstrtol(buf, 10, &val); + if (err) + return err; switch (val) { case 1: - config &= ~AMC6821_CONF1_FDRC0; - config &= ~AMC6821_CONF1_FDRC1; + mode = 0; break; case 2: - config &= ~AMC6821_CONF1_FDRC0; - config |= AMC6821_CONF1_FDRC1; + mode = AMC6821_CONF1_FDRC1; break; case 3: - config |= AMC6821_CONF1_FDRC0; - config |= AMC6821_CONF1_FDRC1; + mode = AMC6821_CONF1_FDRC0 | AMC6821_CONF1_FDRC1; break; case 4: - config |= AMC6821_CONF1_FDRC0; - config &= ~AMC6821_CONF1_FDRC1; + mode = AMC6821_CONF1_FDRC0; break; default: - count = -EINVAL; - goto unlock; - } - if (i2c_smbus_write_byte_data(client, AMC6821_REG_CONF1, config)) { - dev_err(&client->dev, - "Configuration register write error, aborting.\n"); - count = -EIO; + return -EINVAL; } -unlock: - mutex_unlock(&data->update_lock); + + err = regmap_update_bits(data->regmap, AMC6821_REG_CONF1, + AMC6821_CONF1_FDRC0 | AMC6821_CONF1_FDRC1, + mode); + if (err) + return err; + return count; } @@ -430,130 +376,166 @@ static ssize_t pwm1_auto_channels_temp_show(struct device *dev, struct device_attribute *devattr, char *buf) { - struct amc6821_data *data = amc6821_update_device(dev); - return sprintf(buf, "%d\n", data->pwm1_auto_channels_temp); + struct amc6821_data *data = dev_get_drvdata(dev); + u32 val; + int err; + + err = regmap_read(data->regmap, AMC6821_REG_CONF1, &val); + if (err) + return err; + switch (val & (AMC6821_CONF1_FDRC0 | AMC6821_CONF1_FDRC1)) { + case 0: + case AMC6821_CONF1_FDRC0: + val = 0; /* manual or target rpm controlled */ + break; + case AMC6821_CONF1_FDRC1: + val = 2; /* remote temp controlled */ + break; + default: + val = 3; /* max(local, remote) temp controlled */ + break; + } + + return sysfs_emit(buf, "%d\n", val); } static ssize_t temp_auto_point_temp_show(struct device *dev, struct device_attribute *devattr, char *buf) { + struct amc6821_data *data = dev_get_drvdata(dev); int ix = to_sensor_dev_attr_2(devattr)->index; int nr = to_sensor_dev_attr_2(devattr)->nr; - struct amc6821_data *data = amc6821_update_device(dev); - switch (nr) { - case 1: - return sprintf(buf, "%d\n", - data->temp1_auto_point_temp[ix] * 1000); - case 2: - return sprintf(buf, "%d\n", - data->temp2_auto_point_temp[ix] * 1000); - default: - dev_dbg(dev, "Unknown attr->nr (%d).\n", nr); - return -EINVAL; - } + u8 temps[3]; + int err; + + mutex_lock(&data->update_lock); + err = amc6821_get_auto_point_temps(data->regmap, nr, temps); + mutex_unlock(&data->update_lock); + if (err) + return err; + + return sysfs_emit(buf, "%d\n", temps[ix] * 1000); } static ssize_t pwm1_auto_point_pwm_show(struct device *dev, struct device_attribute *devattr, char *buf) { + struct amc6821_data *data = dev_get_drvdata(dev); int ix = to_sensor_dev_attr(devattr)->index; - struct amc6821_data *data = amc6821_update_device(dev); - return sprintf(buf, "%d\n", data->pwm1_auto_point_pwm[ix]); + u32 val; + int err; + + switch (ix) { + case 0: + val = 0; + break; + case 1: + err = regmap_read(data->regmap, AMC6821_REG_DCY_LOW_TEMP, &val); + if (err) + return err; + break; + default: + val = 255; + break; + } + return sysfs_emit(buf, "%d\n", val); } -static inline ssize_t set_slope_register(struct i2c_client *client, - u8 reg, - u8 dpwm, - u8 *ptemp) +/* + * Set TEMP[0-4] (low temperature) and SLP[0-2] (slope) of local or remote + * TEMP-FAN control register. + * + * Return 0 on success or negative error code. + * + * Channel 0: local, channel 1: remote + */ +static inline int set_slope_register(struct regmap *regmap, int channel, u8 *temps) { - int dt; - u8 tmp; + u8 regval = FIELD_PREP(AMC6821_TEMP_LIMIT_MASK, temps[1] / 4); + u8 tmp, dpwm; + int err, dt; + u32 pwm; + + err = regmap_read(regmap, AMC6821_REG_DCY_LOW_TEMP, &pwm); + if (err) + return err; - dt = ptemp[2]-ptemp[1]; + dpwm = 255 - pwm; + + dt = temps[2] - temps[1]; for (tmp = 4; tmp > 0; tmp--) { - if (dt * (0x20 >> tmp) >= dpwm) + if (dt * (32 >> tmp) >= dpwm) break; } - tmp |= (ptemp[1] & 0x7C) << 1; - if (i2c_smbus_write_byte_data(client, - reg, tmp)) { - dev_err(&client->dev, "Register write error, aborting.\n"); - return -EIO; - } - return 0; + regval |= FIELD_PREP(AMC6821_TEMP_SLOPE_MASK, tmp); + + return regmap_write(regmap, + channel ? AMC6821_REG_RTEMP_FAN_CTRL : AMC6821_REG_LTEMP_FAN_CTRL, + regval); } static ssize_t temp_auto_point_temp_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct amc6821_data *data = amc6821_update_device(dev); - struct i2c_client *client = data->client; + struct amc6821_data *data = dev_get_drvdata(dev); int ix = to_sensor_dev_attr_2(attr)->index; int nr = to_sensor_dev_attr_2(attr)->nr; - u8 *ptemp; - u8 reg; - int dpwm; + struct regmap *regmap = data->regmap; + u8 temps[3], otemps[3]; long val; - int ret = kstrtol(buf, 10, &val); + int ret; + + ret = kstrtol(buf, 10, &val); if (ret) return ret; - switch (nr) { - case 1: - ptemp = data->temp1_auto_point_temp; - reg = AMC6821_REG_LTEMP_FAN_CTRL; - break; - case 2: - ptemp = data->temp2_auto_point_temp; - reg = AMC6821_REG_RTEMP_FAN_CTRL; - break; - default: - dev_dbg(dev, "Unknown attr->nr (%d).\n", nr); - return -EINVAL; - } - mutex_lock(&data->update_lock); - data->valid = false; + + ret = amc6821_get_auto_point_temps(data->regmap, nr, temps); + if (ret) + goto unlock; switch (ix) { case 0: - ptemp[0] = clamp_val(val / 1000, 0, - data->temp1_auto_point_temp[1]); - ptemp[0] = clamp_val(ptemp[0], 0, - data->temp2_auto_point_temp[1]); - ptemp[0] = clamp_val(ptemp[0], 0, 63); - if (i2c_smbus_write_byte_data( - client, - AMC6821_REG_PSV_TEMP, - ptemp[0])) { - dev_err(&client->dev, - "Register write error, aborting.\n"); - count = -EIO; - } - goto EXIT; + /* + * Passive cooling temperature. Range limit against low limit + * of both channels. + */ + ret = amc6821_get_auto_point_temps(data->regmap, 1 - nr, otemps); + if (ret) + goto unlock; + val = DIV_ROUND_CLOSEST(clamp_val(val, 0, 63000), 1000); + val = clamp_val(val, 0, min(temps[1], otemps[1])); + ret = regmap_write(regmap, AMC6821_REG_PSV_TEMP, val); + break; case 1: - ptemp[1] = clamp_val(val / 1000, (ptemp[0] & 0x7C) + 4, 124); - ptemp[1] &= 0x7C; - ptemp[2] = clamp_val(ptemp[2], ptemp[1] + 1, 255); + /* + * Low limit; must be between passive and high limit, + * and not exceed 124. Step size is 4 degrees C. + */ + val = clamp_val(val, DIV_ROUND_UP(temps[0], 4) * 4000, 124000); + temps[1] = DIV_ROUND_CLOSEST(val, 4000) * 4; + val = temps[1] / 4; + /* Auto-adjust high limit if necessary */ + temps[2] = clamp_val(temps[2], temps[1] + 1, 255); + ret = set_slope_register(regmap, nr, temps); break; case 2: - ptemp[2] = clamp_val(val / 1000, ptemp[1]+1, 255); + /* high limit, must be higher than low limit */ + val = clamp_val(val, (temps[1] + 1) * 1000, 255000); + temps[2] = DIV_ROUND_CLOSEST(val, 1000); + ret = set_slope_register(regmap, nr, temps); break; default: - dev_dbg(dev, "Unknown attr->index (%d).\n", ix); - count = -EINVAL; - goto EXIT; + ret = -EINVAL; + break; } - dpwm = data->pwm1_auto_point_pwm[2] - data->pwm1_auto_point_pwm[1]; - if (set_slope_register(client, reg, dpwm, ptemp)) - count = -EIO; - -EXIT: +unlock: mutex_unlock(&data->update_lock); - return count; + return ret ? : count; } static ssize_t pwm1_auto_point_pwm_store(struct device *dev, @@ -561,101 +543,107 @@ static ssize_t pwm1_auto_point_pwm_store(struct device *dev, const char *buf, size_t count) { struct amc6821_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - int dpwm; + struct regmap *regmap = data->regmap; + int i, ret; u8 val; - int ret; ret = kstrtou8(buf, 10, &val); if (ret) return ret; mutex_lock(&data->update_lock); - data->pwm1_auto_point_pwm[1] = val; - if (i2c_smbus_write_byte_data(client, AMC6821_REG_DCY_LOW_TEMP, - data->pwm1_auto_point_pwm[1])) { - dev_err(&client->dev, "Register write error, aborting.\n"); - count = -EIO; - goto EXIT; - } - dpwm = data->pwm1_auto_point_pwm[2] - data->pwm1_auto_point_pwm[1]; - if (set_slope_register(client, AMC6821_REG_LTEMP_FAN_CTRL, dpwm, - data->temp1_auto_point_temp)) { - count = -EIO; - goto EXIT; - } - if (set_slope_register(client, AMC6821_REG_RTEMP_FAN_CTRL, dpwm, - data->temp2_auto_point_temp)) { - count = -EIO; - goto EXIT; - } + ret = regmap_write(regmap, AMC6821_REG_DCY_LOW_TEMP, val); + if (ret) + goto unlock; + + for (i = 0; i < 2; i++) { + u8 temps[3]; -EXIT: - data->valid = false; + ret = amc6821_get_auto_point_temps(regmap, i, temps); + if (ret) + break; + ret = set_slope_register(regmap, i, temps); + if (ret) + break; + } +unlock: mutex_unlock(&data->update_lock); - return count; + return ret ? : count; } static ssize_t fan_show(struct device *dev, struct device_attribute *devattr, char *buf) { - struct amc6821_data *data = amc6821_update_device(dev); + struct amc6821_data *data = dev_get_drvdata(dev); int ix = to_sensor_dev_attr(devattr)->index; - if (0 == data->fan[ix]) - return sprintf(buf, "0"); - return sprintf(buf, "%d\n", (int)(6000000 / data->fan[ix])); + u32 regval; + u8 regs[2]; + int err; + + err = regmap_bulk_read(data->regmap, fan_reg_low[ix], regs, 2); + if (err) + return err; + regval = (regs[1] << 8) | regs[0]; + + return sysfs_emit(buf, "%d\n", regval ? 6000000 / regval : 0); } static ssize_t fan1_fault_show(struct device *dev, struct device_attribute *devattr, char *buf) { - struct amc6821_data *data = amc6821_update_device(dev); - if (data->stat1 & AMC6821_STAT1_FANS) - return sprintf(buf, "1"); - else - return sprintf(buf, "0"); + struct amc6821_data *data = dev_get_drvdata(dev); + u32 regval; + int err; + + err = regmap_read(data->regmap, AMC6821_REG_STAT1, ®val); + if (err) + return err; + + return sysfs_emit(buf, "%d\n", !!(regval & AMC6821_STAT1_FANS)); } static ssize_t fan_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct amc6821_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - unsigned long val; int ix = to_sensor_dev_attr(attr)->index; - int ret = kstrtoul(buf, 10, &val); - if (ret) - return ret; + unsigned long val; + u8 regs[2]; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; /* Minimum and target fan speed must not be unlimited (0) */ if ((ix == IDX_FAN1_MIN || ix == IDX_FAN1_TARGET) && !val) return -EINVAL; - val = val > 0 ? 6000000 / clamp_val(val, 1, 6000000) : 0; + val = val > 0 ? 6000000 / min(val, 6000000) : 0; + val = clamp_val(val, 0, 0xFFFF); + + regs[0] = val & 0xff; + regs[1] = val >> 8; + + err = regmap_bulk_write(data->regmap, fan_reg_low[ix], regs, 2); + if (err) + return err; - mutex_lock(&data->update_lock); - data->fan[ix] = clamp_val(val, 0, 0xFFFF); - if (i2c_smbus_write_byte_data(client, fan_reg_low[ix], - data->fan[ix] & 0xFF)) { - dev_err(&client->dev, "Register write error, aborting.\n"); - count = -EIO; - goto EXIT; - } - if (i2c_smbus_write_byte_data(client, - fan_reg_hi[ix], data->fan[ix] >> 8)) { - dev_err(&client->dev, "Register write error, aborting.\n"); - count = -EIO; - } -EXIT: - mutex_unlock(&data->update_lock); return count; } static ssize_t fan1_pulses_show(struct device *dev, struct device_attribute *devattr, char *buf) { - struct amc6821_data *data = amc6821_update_device(dev); - return sprintf(buf, "%d\n", data->fan1_pulses); + struct amc6821_data *data = dev_get_drvdata(dev); + u32 regval; + int err; + + err = regmap_read(data->regmap, AMC6821_REG_CONF4, ®val); + if (err) + return err; + + return sysfs_emit(buf, "%d\n", (regval & AMC6821_CONF4_PSPR) ? 4 : 2); } static ssize_t fan1_pulses_store(struct device *dev, @@ -663,40 +651,22 @@ static ssize_t fan1_pulses_store(struct device *dev, size_t count) { struct amc6821_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; long val; - int config = kstrtol(buf, 10, &val); - if (config) - return config; + int err; + + err = kstrtol(buf, 10, &val); + if (err) + return err; + + if (val != 2 && val != 4) + return -EINVAL; + + err = regmap_update_bits(data->regmap, AMC6821_REG_CONF4, + AMC6821_CONF4_PSPR, + val == 4 ? AMC6821_CONF4_PSPR : 0); + if (err) + return err; - mutex_lock(&data->update_lock); - config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF4); - if (config < 0) { - dev_err(&client->dev, - "Error reading configuration register, aborting.\n"); - count = config; - goto EXIT; - } - switch (val) { - case 2: - config &= ~AMC6821_CONF4_PSPR; - data->fan1_pulses = 2; - break; - case 4: - config |= AMC6821_CONF4_PSPR; - data->fan1_pulses = 4; - break; - default: - count = -EINVAL; - goto EXIT; - } - if (i2c_smbus_write_byte_data(client, AMC6821_REG_CONF4, config)) { - dev_err(&client->dev, - "Configuration register write error, aborting.\n"); - count = -EIO; - } -EXIT: - mutex_unlock(&data->update_lock); return count; } @@ -730,18 +700,18 @@ static SENSOR_DEVICE_ATTR_RO(pwm1_auto_point3_pwm, pwm1_auto_point_pwm, 2); static SENSOR_DEVICE_ATTR_RO(pwm1_auto_channels_temp, pwm1_auto_channels_temp, 0); static SENSOR_DEVICE_ATTR_2_RO(temp1_auto_point1_temp, temp_auto_point_temp, - 1, 0); + 0, 0); static SENSOR_DEVICE_ATTR_2_RW(temp1_auto_point2_temp, temp_auto_point_temp, - 1, 1); + 0, 1); static SENSOR_DEVICE_ATTR_2_RW(temp1_auto_point3_temp, temp_auto_point_temp, - 1, 2); + 0, 2); static SENSOR_DEVICE_ATTR_2_RW(temp2_auto_point1_temp, temp_auto_point_temp, - 2, 0); + 1, 0); static SENSOR_DEVICE_ATTR_2_RW(temp2_auto_point2_temp, temp_auto_point_temp, - 2, 1); + 1, 1); static SENSOR_DEVICE_ATTR_2_RW(temp2_auto_point3_temp, temp_auto_point_temp, - 2, 2); + 1, 2); static struct attribute *amc6821_attrs[] = { &sensor_dev_attr_temp1_input.dev_attr.attr, @@ -828,110 +798,79 @@ static int amc6821_detect( return 0; } -static int amc6821_init_client(struct i2c_client *client) +static int amc6821_init_client(struct amc6821_data *data) { - int config; - int err = -EIO; + struct regmap *regmap = data->regmap; + int err; if (init) { - config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF4); - - if (config < 0) { - dev_err(&client->dev, - "Error reading configuration register, aborting.\n"); - return err; - } - - config |= AMC6821_CONF4_MODE; - - if (i2c_smbus_write_byte_data(client, AMC6821_REG_CONF4, - config)) { - dev_err(&client->dev, - "Configuration register write error, aborting.\n"); - return err; - } - - config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF3); - - if (config < 0) { - dev_err(&client->dev, - "Error reading configuration register, aborting.\n"); - return err; - } - - dev_info(&client->dev, "Revision %d\n", config & 0x0f); - - config &= ~AMC6821_CONF3_THERM_FAN_EN; - - if (i2c_smbus_write_byte_data(client, AMC6821_REG_CONF3, - config)) { - dev_err(&client->dev, - "Configuration register write error, aborting.\n"); + err = regmap_set_bits(regmap, AMC6821_REG_CONF4, AMC6821_CONF4_MODE); + if (err) return err; - } - - config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF2); - - if (config < 0) { - dev_err(&client->dev, - "Error reading configuration register, aborting.\n"); + err = regmap_clear_bits(regmap, AMC6821_REG_CONF3, AMC6821_CONF3_THERM_FAN_EN); + if (err) return err; - } - - config &= ~AMC6821_CONF2_RTFIE; - config &= ~AMC6821_CONF2_LTOIE; - config &= ~AMC6821_CONF2_RTOIE; - if (i2c_smbus_write_byte_data(client, - AMC6821_REG_CONF2, config)) { - dev_err(&client->dev, - "Configuration register write error, aborting.\n"); + err = regmap_clear_bits(regmap, AMC6821_REG_CONF2, + AMC6821_CONF2_RTFIE | + AMC6821_CONF2_LTOIE | + AMC6821_CONF2_RTOIE); + if (err) return err; - } - - config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF1); - if (config < 0) { - dev_err(&client->dev, - "Error reading configuration register, aborting.\n"); + err = regmap_update_bits(regmap, AMC6821_REG_CONF1, + AMC6821_CONF1_THERMOVIE | AMC6821_CONF1_FANIE | + AMC6821_CONF1_START | AMC6821_CONF1_PWMINV, + AMC6821_CONF1_START | + (pwminv ? AMC6821_CONF1_PWMINV : 0)); + if (err) return err; - } - - config &= ~AMC6821_CONF1_THERMOVIE; - config &= ~AMC6821_CONF1_FANIE; - config |= AMC6821_CONF1_START; - if (pwminv) - config |= AMC6821_CONF1_PWMINV; - else - config &= ~AMC6821_CONF1_PWMINV; - - if (i2c_smbus_write_byte_data( - client, AMC6821_REG_CONF1, config)) { - dev_err(&client->dev, - "Configuration register write error, aborting.\n"); - return err; - } } return 0; } +static bool amc6821_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case AMC6821_REG_STAT1: + case AMC6821_REG_STAT2: + case AMC6821_REG_TEMP_LO: + case AMC6821_REG_TDATA_LOW: + case AMC6821_REG_LTEMP_HI: + case AMC6821_REG_RTEMP_HI: + case AMC6821_REG_TDATA_HI: + return true; + default: + return false; + } +} + +static const struct regmap_config amc6821_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = AMC6821_REG_CONF3, + .volatile_reg = amc6821_volatile_reg, + .cache_type = REGCACHE_MAPLE, +}; + static int amc6821_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct amc6821_data *data; struct device *hwmon_dev; + struct regmap *regmap; int err; data = devm_kzalloc(dev, sizeof(struct amc6821_data), GFP_KERNEL); if (!data) return -ENOMEM; - data->client = client; - mutex_init(&data->update_lock); + regmap = devm_regmap_init_i2c(client, &amc6821_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "Failed to initialize regmap\n"); + data->regmap = regmap; - /* - * Initialize the amc6821 chip - */ - err = amc6821_init_client(client); + err = amc6821_init_client(data); if (err) return err; -- cgit From e98ab50e1f2dd6ab8d1bddae4f9857725defafbc Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 26 Jun 2024 17:20:40 -0700 Subject: hwmon: (amc6821) Convert to with_info API Convert to use with_info API to simplify the code and make it easier to maintain. This also reduces code size by approximately 20%. No functional change intended. Reviewed-by: Quentin Schulz Signed-off-by: Guenter Roeck --- drivers/hwmon/amc6821.c | 688 +++++++++++++++++++++++++----------------------- 1 file changed, 362 insertions(+), 326 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/amc6821.c b/drivers/hwmon/amc6821.c index 0b9e83fc8188..8beaea9ecbab 100644 --- a/drivers/hwmon/amc6821.c +++ b/drivers/hwmon/amc6821.c @@ -6,6 +6,9 @@ * * Based on max6650.c: * Copyright (C) 2007 Hans J. Koch + * + * Conversion to regmap and with_info API: + * Copyright (C) 2024 Guenter Roeck */ #include @@ -112,28 +115,6 @@ module_param(init, int, 0444); #define AMC6821_TEMP_SLOPE_MASK GENMASK(2, 0) #define AMC6821_TEMP_LIMIT_MASK GENMASK(7, 3) -enum {IDX_TEMP1_INPUT = 0, IDX_TEMP1_MIN, IDX_TEMP1_MAX, - IDX_TEMP1_CRIT, IDX_TEMP2_INPUT, IDX_TEMP2_MIN, - IDX_TEMP2_MAX, IDX_TEMP2_CRIT, - TEMP_IDX_LEN, }; - -static const u8 temp_reg[] = {AMC6821_REG_LTEMP_HI, - AMC6821_REG_LTEMP_LIMIT_MIN, - AMC6821_REG_LTEMP_LIMIT_MAX, - AMC6821_REG_LTEMP_CRIT, - AMC6821_REG_RTEMP_HI, - AMC6821_REG_RTEMP_LIMIT_MIN, - AMC6821_REG_RTEMP_LIMIT_MAX, - AMC6821_REG_RTEMP_CRIT, }; - -enum {IDX_FAN1_INPUT = 0, IDX_FAN1_MIN, IDX_FAN1_MAX, IDX_FAN1_TARGET, - FAN1_IDX_LEN, }; - -static const u8 fan_reg_low[] = {AMC6821_REG_TDATA_LOW, - AMC6821_REG_TACH_LLIMITL, - AMC6821_REG_TACH_HLIMITL, - AMC6821_REG_TACH_SETTINGL, }; - /* * Client data (each client gets its own) */ @@ -184,219 +165,319 @@ static int amc6821_get_auto_point_temps(struct regmap *regmap, int channel, u8 * return 0; } -static ssize_t temp_show(struct device *dev, struct device_attribute *devattr, - char *buf) +static int amc6821_temp_read_values(struct regmap *regmap, u32 attr, int channel, long *val) { - struct amc6821_data *data = dev_get_drvdata(dev); - int ix = to_sensor_dev_attr(devattr)->index; + int reg, err; u32 regval; - int err; - - err = regmap_read(data->regmap, temp_reg[ix], ®val); - if (err) - return err; - - return sysfs_emit(buf, "%d\n", sign_extend32(regval, 7) * 1000); -} - -static ssize_t temp_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct amc6821_data *data = dev_get_drvdata(dev); - int ix = to_sensor_dev_attr(attr)->index; - long val; - int err; - - int ret = kstrtol(buf, 10, &val); - if (ret) - return ret; - val = clamp_val(val / 1000, -128, 127); - - err = regmap_write(data->regmap, temp_reg[ix], val); - if (err) - return err; - - return count; -} - -static ssize_t temp_alarm_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct amc6821_data *data = dev_get_drvdata(dev); - int ix = to_sensor_dev_attr(devattr)->index; - u32 regval, mask, reg; - int err; - switch (ix) { - case IDX_TEMP1_MIN: - reg = AMC6821_REG_STAT1; - mask = AMC6821_STAT1_LTL; - break; - case IDX_TEMP1_MAX: - reg = AMC6821_REG_STAT1; - mask = AMC6821_STAT1_LTH; + switch (attr) { + case hwmon_temp_input: + reg = channel ? AMC6821_REG_RTEMP_HI : AMC6821_REG_LTEMP_HI; break; - case IDX_TEMP1_CRIT: - reg = AMC6821_REG_STAT2; - mask = AMC6821_STAT2_LTC; + case hwmon_temp_min: + reg = channel ? AMC6821_REG_RTEMP_LIMIT_MIN : AMC6821_REG_LTEMP_LIMIT_MIN; break; - case IDX_TEMP2_MIN: - reg = AMC6821_REG_STAT1; - mask = AMC6821_STAT1_RTL; + case hwmon_temp_max: + reg = channel ? AMC6821_REG_RTEMP_LIMIT_MAX : AMC6821_REG_LTEMP_LIMIT_MAX; break; - case IDX_TEMP2_MAX: - reg = AMC6821_REG_STAT1; - mask = AMC6821_STAT1_RTH; - break; - case IDX_TEMP2_CRIT: - reg = AMC6821_REG_STAT2; - mask = AMC6821_STAT2_RTC; + case hwmon_temp_crit: + reg = channel ? AMC6821_REG_RTEMP_CRIT : AMC6821_REG_LTEMP_CRIT; break; default: - return -EINVAL; + return -EOPNOTSUPP; } - err = regmap_read(data->regmap, reg, ®val); + err = regmap_read(regmap, reg, ®val); if (err) return err; - return sysfs_emit(buf, "%d\n", !!(regval & mask)); + *val = sign_extend32(regval, 7) * 1000; + return 0; } -static ssize_t temp2_fault_show(struct device *dev, - struct device_attribute *devattr, char *buf) +static int amc6821_read_alarms(struct regmap *regmap, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) { - struct amc6821_data *data = dev_get_drvdata(dev); + int reg, mask, err; u32 regval; - int err; - err = regmap_read(data->regmap, AMC6821_REG_STAT1, ®val); + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_min_alarm: + reg = AMC6821_REG_STAT1; + mask = channel ? AMC6821_STAT1_RTL : AMC6821_STAT1_LTL; + break; + case hwmon_temp_max_alarm: + reg = AMC6821_REG_STAT1; + mask = channel ? AMC6821_STAT1_RTH : AMC6821_STAT1_LTH; + break; + case hwmon_temp_crit_alarm: + reg = AMC6821_REG_STAT2; + mask = channel ? AMC6821_STAT2_RTC : AMC6821_STAT2_LTC; + break; + case hwmon_temp_fault: + reg = AMC6821_REG_STAT1; + mask = AMC6821_STAT1_RTF; + break; + default: + return -EOPNOTSUPP; + } + break; + case hwmon_fan: + switch (attr) { + case hwmon_fan_fault: + reg = AMC6821_REG_STAT1; + mask = AMC6821_STAT1_FANS; + break; + default: + return -EOPNOTSUPP; + } + break; + default: + return -EOPNOTSUPP; + } + err = regmap_read(regmap, reg, ®val); if (err) return err; - - return sysfs_emit(buf, "%d\n", !!(regval & AMC6821_STAT1_RTF)); + *val = !!(regval & mask); + return 0; } -static ssize_t pwm1_show(struct device *dev, struct device_attribute *devattr, - char *buf) +static int amc6821_temp_read(struct device *dev, u32 attr, int channel, long *val) { struct amc6821_data *data = dev_get_drvdata(dev); - u32 regval; - int err; - err = regmap_read(data->regmap, AMC6821_REG_DCY, ®val); - if (err) - return err; - - return sysfs_emit(buf, "%d\n", regval); + switch (attr) { + case hwmon_temp_input: + case hwmon_temp_min: + case hwmon_temp_max: + case hwmon_temp_crit: + return amc6821_temp_read_values(data->regmap, attr, channel, val); + case hwmon_temp_min_alarm: + case hwmon_temp_max_alarm: + case hwmon_temp_crit_alarm: + case hwmon_temp_fault: + return amc6821_read_alarms(data->regmap, hwmon_temp, attr, channel, val); + default: + return -EOPNOTSUPP; + } } -static ssize_t pwm1_store(struct device *dev, - struct device_attribute *devattr, const char *buf, - size_t count) +static int amc6821_temp_write(struct device *dev, u32 attr, int channel, long val) { struct amc6821_data *data = dev_get_drvdata(dev); - u8 val; - int ret = kstrtou8(buf, 10, &val); - if (ret) - return ret; + int reg; - ret = regmap_write(data->regmap, AMC6821_REG_DCY, val); - if (ret) - return ret; + val = DIV_ROUND_CLOSEST(clamp_val(val, -128000, 127000), 1000); - return count; + switch (attr) { + case hwmon_temp_min: + reg = channel ? AMC6821_REG_RTEMP_LIMIT_MIN : AMC6821_REG_LTEMP_LIMIT_MIN; + break; + case hwmon_temp_max: + reg = channel ? AMC6821_REG_RTEMP_LIMIT_MAX : AMC6821_REG_LTEMP_LIMIT_MAX; + break; + case hwmon_temp_crit: + reg = channel ? AMC6821_REG_RTEMP_CRIT : AMC6821_REG_LTEMP_CRIT; + break; + default: + return -EOPNOTSUPP; + } + return regmap_write(data->regmap, reg, val); } -static ssize_t pwm1_enable_show(struct device *dev, - struct device_attribute *devattr, char *buf) +static int amc6821_pwm_read(struct device *dev, u32 attr, long *val) { struct amc6821_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; + u32 regval; int err; - u32 val; - err = regmap_read(data->regmap, AMC6821_REG_CONF1, &val); - if (err) - return err; - switch (val & (AMC6821_CONF1_FDRC0 | AMC6821_CONF1_FDRC1)) { - case 0: - val = 1; /* manual */ - break; - case AMC6821_CONF1_FDRC0: - val = 4; /* target rpm (fan1_target) controlled */ - break; - case AMC6821_CONF1_FDRC1: - val = 2; /* remote temp controlled */ - break; + switch (attr) { + case hwmon_pwm_enable: + err = regmap_read(regmap, AMC6821_REG_CONF1, ®val); + if (err) + return err; + switch (regval & (AMC6821_CONF1_FDRC0 | AMC6821_CONF1_FDRC1)) { + case 0: + *val = 1; /* manual */ + break; + case AMC6821_CONF1_FDRC0: + *val = 4; /* target rpm (fan1_target) controlled */ + break; + case AMC6821_CONF1_FDRC1: + *val = 2; /* remote temp controlled */ + break; + default: + *val = 3; /* max(local, remote) temp controlled */ + break; + } + return 0; + case hwmon_pwm_auto_channels_temp: + err = regmap_read(regmap, AMC6821_REG_CONF1, ®val); + if (err) + return err; + switch (regval & (AMC6821_CONF1_FDRC0 | AMC6821_CONF1_FDRC1)) { + case 0: + case AMC6821_CONF1_FDRC0: + *val = 0; /* manual or target rpm controlled */ + break; + case AMC6821_CONF1_FDRC1: + *val = 2; /* remote temp controlled */ + break; + default: + *val = 3; /* max(local, remote) temp controlled */ + break; + } + return 0; + case hwmon_pwm_input: + err = regmap_read(regmap, AMC6821_REG_DCY, ®val); + if (err) + return err; + *val = regval; + return 0; default: - val = 3; /* max(local, remote) temp controlled */ - break; + return -EOPNOTSUPP; } - return sysfs_emit(buf, "%d\n", val); } -static ssize_t pwm1_enable_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static int amc6821_pwm_write(struct device *dev, u32 attr, long val) { struct amc6821_data *data = dev_get_drvdata(dev); - long val; + struct regmap *regmap = data->regmap; u32 mode; - int err; - err = kstrtol(buf, 10, &val); - if (err) - return err; + switch (attr) { + case hwmon_pwm_enable: + switch (val) { + case 1: + mode = 0; + break; + case 2: + mode = AMC6821_CONF1_FDRC1; + break; + case 3: + mode = AMC6821_CONF1_FDRC0 | AMC6821_CONF1_FDRC1; + break; + case 4: + mode = AMC6821_CONF1_FDRC0; + break; + default: + return -EINVAL; + } + return regmap_update_bits(regmap, AMC6821_REG_CONF1, + AMC6821_CONF1_FDRC0 | AMC6821_CONF1_FDRC1, + mode); + case hwmon_pwm_input: + if (val < 0 || val > 255) + return -EINVAL; + return regmap_write(regmap, AMC6821_REG_DCY, val); + default: + return -EOPNOTSUPP; + } +} - switch (val) { - case 1: - mode = 0; +static int amc6821_fan_read_rpm(struct regmap *regmap, u32 attr, long *val) +{ + int reg, err; + u8 regs[2]; + u32 regval; + + switch (attr) { + case hwmon_fan_input: + reg = AMC6821_REG_TDATA_LOW; break; - case 2: - mode = AMC6821_CONF1_FDRC1; + case hwmon_fan_min: + reg = AMC6821_REG_TACH_LLIMITL; break; - case 3: - mode = AMC6821_CONF1_FDRC0 | AMC6821_CONF1_FDRC1; + case hwmon_fan_max: + reg = AMC6821_REG_TACH_HLIMITL; break; - case 4: - mode = AMC6821_CONF1_FDRC0; + case hwmon_fan_target: + reg = AMC6821_REG_TACH_SETTINGL; break; default: - return -EINVAL; + return -EOPNOTSUPP; } - err = regmap_update_bits(data->regmap, AMC6821_REG_CONF1, - AMC6821_CONF1_FDRC0 | AMC6821_CONF1_FDRC1, - mode); + err = regmap_bulk_read(regmap, reg, regs, 2); if (err) return err; - return count; + regval = (regs[1] << 8) | regs[0]; + *val = regval ? 6000000 / regval : 0; + + return 0; } -static ssize_t pwm1_auto_channels_temp_show(struct device *dev, - struct device_attribute *devattr, - char *buf) +static int amc6821_fan_read(struct device *dev, u32 attr, long *val) { struct amc6821_data *data = dev_get_drvdata(dev); - u32 val; + struct regmap *regmap = data->regmap; + u32 regval; int err; - err = regmap_read(data->regmap, AMC6821_REG_CONF1, &val); - if (err) - return err; - switch (val & (AMC6821_CONF1_FDRC0 | AMC6821_CONF1_FDRC1)) { - case 0: - case AMC6821_CONF1_FDRC0: - val = 0; /* manual or target rpm controlled */ + switch (attr) { + case hwmon_fan_input: + case hwmon_fan_min: + case hwmon_fan_max: + case hwmon_fan_target: + return amc6821_fan_read_rpm(regmap, attr, val); + case hwmon_fan_fault: + return amc6821_read_alarms(regmap, hwmon_fan, attr, 0, val); + case hwmon_fan_pulses: + err = regmap_read(regmap, AMC6821_REG_CONF4, ®val); + if (err) + return err; + *val = (regval & AMC6821_CONF4_PSPR) ? 4 : 2; + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int amc6821_fan_write(struct device *dev, u32 attr, long val) +{ + struct amc6821_data *data = dev_get_drvdata(dev); + struct regmap *regmap = data->regmap; + u8 regs[2]; + int reg; + + if (attr == hwmon_fan_pulses) { + if (val != 2 && val != 4) + return -EINVAL; + return regmap_update_bits(regmap, AMC6821_REG_CONF4, + AMC6821_CONF4_PSPR, + val == 4 ? AMC6821_CONF4_PSPR : 0); + } + + if (val < 0) + return -EINVAL; + + switch (attr) { + case hwmon_fan_min: + if (!val) /* no unlimited minimum speed */ + return -EINVAL; + reg = AMC6821_REG_TACH_LLIMITL; break; - case AMC6821_CONF1_FDRC1: - val = 2; /* remote temp controlled */ + case hwmon_fan_max: + reg = AMC6821_REG_TACH_HLIMITL; break; - default: - val = 3; /* max(local, remote) temp controlled */ + case hwmon_fan_target: + if (!val) /* no unlimited target speed */ + return -EINVAL; + reg = AMC6821_REG_TACH_SETTINGL; break; + default: + return -EOPNOTSUPP; } - return sysfs_emit(buf, "%d\n", val); + val = val ? 6000000 / clamp_val(val, 1, 6000000) : 0; + val = clamp_val(val, 0, 0xffff); + + regs[0] = val & 0xff; + regs[1] = val >> 8; + + return regmap_bulk_write(data->regmap, reg, regs, 2); } static ssize_t temp_auto_point_temp_show(struct device *dev, @@ -571,134 +652,9 @@ unlock: return ret ? : count; } -static ssize_t fan_show(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct amc6821_data *data = dev_get_drvdata(dev); - int ix = to_sensor_dev_attr(devattr)->index; - u32 regval; - u8 regs[2]; - int err; - - err = regmap_bulk_read(data->regmap, fan_reg_low[ix], regs, 2); - if (err) - return err; - regval = (regs[1] << 8) | regs[0]; - - return sysfs_emit(buf, "%d\n", regval ? 6000000 / regval : 0); -} - -static ssize_t fan1_fault_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct amc6821_data *data = dev_get_drvdata(dev); - u32 regval; - int err; - - err = regmap_read(data->regmap, AMC6821_REG_STAT1, ®val); - if (err) - return err; - - return sysfs_emit(buf, "%d\n", !!(regval & AMC6821_STAT1_FANS)); -} - -static ssize_t fan_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct amc6821_data *data = dev_get_drvdata(dev); - int ix = to_sensor_dev_attr(attr)->index; - unsigned long val; - u8 regs[2]; - int err; - - err = kstrtoul(buf, 10, &val); - if (err) - return err; - - /* Minimum and target fan speed must not be unlimited (0) */ - if ((ix == IDX_FAN1_MIN || ix == IDX_FAN1_TARGET) && !val) - return -EINVAL; - - val = val > 0 ? 6000000 / min(val, 6000000) : 0; - val = clamp_val(val, 0, 0xFFFF); - - regs[0] = val & 0xff; - regs[1] = val >> 8; - - err = regmap_bulk_write(data->regmap, fan_reg_low[ix], regs, 2); - if (err) - return err; - - return count; -} - -static ssize_t fan1_pulses_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct amc6821_data *data = dev_get_drvdata(dev); - u32 regval; - int err; - - err = regmap_read(data->regmap, AMC6821_REG_CONF4, ®val); - if (err) - return err; - - return sysfs_emit(buf, "%d\n", (regval & AMC6821_CONF4_PSPR) ? 4 : 2); -} - -static ssize_t fan1_pulses_store(struct device *dev, - struct device_attribute *attr, const char *buf, - size_t count) -{ - struct amc6821_data *data = dev_get_drvdata(dev); - long val; - int err; - - err = kstrtol(buf, 10, &val); - if (err) - return err; - - if (val != 2 && val != 4) - return -EINVAL; - - err = regmap_update_bits(data->regmap, AMC6821_REG_CONF4, - AMC6821_CONF4_PSPR, - val == 4 ? AMC6821_CONF4_PSPR : 0); - if (err) - return err; - - return count; -} - -static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, IDX_TEMP1_INPUT); -static SENSOR_DEVICE_ATTR_RW(temp1_min, temp, IDX_TEMP1_MIN); -static SENSOR_DEVICE_ATTR_RW(temp1_max, temp, IDX_TEMP1_MAX); -static SENSOR_DEVICE_ATTR_RW(temp1_crit, temp, IDX_TEMP1_CRIT); -static SENSOR_DEVICE_ATTR_RO(temp1_min_alarm, temp_alarm, IDX_TEMP1_MIN); -static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, temp_alarm, IDX_TEMP1_MAX); -static SENSOR_DEVICE_ATTR_RO(temp1_crit_alarm, temp_alarm, IDX_TEMP1_CRIT); -static SENSOR_DEVICE_ATTR_RO(temp2_input, temp, IDX_TEMP2_INPUT); -static SENSOR_DEVICE_ATTR_RW(temp2_min, temp, IDX_TEMP2_MIN); -static SENSOR_DEVICE_ATTR_RW(temp2_max, temp, IDX_TEMP2_MAX); -static SENSOR_DEVICE_ATTR_RW(temp2_crit, temp, IDX_TEMP2_CRIT); -static SENSOR_DEVICE_ATTR_RO(temp2_fault, temp2_fault, 0); -static SENSOR_DEVICE_ATTR_RO(temp2_min_alarm, temp_alarm, IDX_TEMP2_MIN); -static SENSOR_DEVICE_ATTR_RO(temp2_max_alarm, temp_alarm, IDX_TEMP2_MAX); -static SENSOR_DEVICE_ATTR_RO(temp2_crit_alarm, temp_alarm, IDX_TEMP2_CRIT); -static SENSOR_DEVICE_ATTR_RO(fan1_input, fan, IDX_FAN1_INPUT); -static SENSOR_DEVICE_ATTR_RW(fan1_min, fan, IDX_FAN1_MIN); -static SENSOR_DEVICE_ATTR_RW(fan1_max, fan, IDX_FAN1_MAX); -static SENSOR_DEVICE_ATTR_RW(fan1_target, fan, IDX_FAN1_TARGET); -static SENSOR_DEVICE_ATTR_RO(fan1_fault, fan1_fault, 0); -static SENSOR_DEVICE_ATTR_RW(fan1_pulses, fan1_pulses, 0); - -static SENSOR_DEVICE_ATTR_RW(pwm1, pwm1, 0); -static SENSOR_DEVICE_ATTR_RW(pwm1_enable, pwm1_enable, 0); static SENSOR_DEVICE_ATTR_RO(pwm1_auto_point1_pwm, pwm1_auto_point_pwm, 0); static SENSOR_DEVICE_ATTR_RW(pwm1_auto_point2_pwm, pwm1_auto_point_pwm, 1); static SENSOR_DEVICE_ATTR_RO(pwm1_auto_point3_pwm, pwm1_auto_point_pwm, 2); -static SENSOR_DEVICE_ATTR_RO(pwm1_auto_channels_temp, pwm1_auto_channels_temp, - 0); static SENSOR_DEVICE_ATTR_2_RO(temp1_auto_point1_temp, temp_auto_point_temp, 0, 0); static SENSOR_DEVICE_ATTR_2_RW(temp1_auto_point2_temp, temp_auto_point_temp, @@ -714,30 +670,6 @@ static SENSOR_DEVICE_ATTR_2_RW(temp2_auto_point3_temp, temp_auto_point_temp, 1, 2); static struct attribute *amc6821_attrs[] = { - &sensor_dev_attr_temp1_input.dev_attr.attr, - &sensor_dev_attr_temp1_min.dev_attr.attr, - &sensor_dev_attr_temp1_max.dev_attr.attr, - &sensor_dev_attr_temp1_crit.dev_attr.attr, - &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, - &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, - &sensor_dev_attr_temp2_input.dev_attr.attr, - &sensor_dev_attr_temp2_min.dev_attr.attr, - &sensor_dev_attr_temp2_max.dev_attr.attr, - &sensor_dev_attr_temp2_crit.dev_attr.attr, - &sensor_dev_attr_temp2_min_alarm.dev_attr.attr, - &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, - &sensor_dev_attr_temp2_fault.dev_attr.attr, - &sensor_dev_attr_fan1_input.dev_attr.attr, - &sensor_dev_attr_fan1_min.dev_attr.attr, - &sensor_dev_attr_fan1_max.dev_attr.attr, - &sensor_dev_attr_fan1_target.dev_attr.attr, - &sensor_dev_attr_fan1_fault.dev_attr.attr, - &sensor_dev_attr_fan1_pulses.dev_attr.attr, - &sensor_dev_attr_pwm1.dev_attr.attr, - &sensor_dev_attr_pwm1_enable.dev_attr.attr, - &sensor_dev_attr_pwm1_auto_channels_temp.dev_attr.attr, &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr, &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr, &sensor_dev_attr_pwm1_auto_point3_pwm.dev_attr.attr, @@ -749,13 +681,117 @@ static struct attribute *amc6821_attrs[] = { &sensor_dev_attr_temp2_auto_point3_temp.dev_attr.attr, NULL }; - ATTRIBUTE_GROUPS(amc6821); +static int amc6821_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + switch (type) { + case hwmon_temp: + return amc6821_temp_read(dev, attr, channel, val); + case hwmon_fan: + return amc6821_fan_read(dev, attr, val); + case hwmon_pwm: + return amc6821_pwm_read(dev, attr, val); + default: + return -EOPNOTSUPP; + } +} + +static int amc6821_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + switch (type) { + case hwmon_temp: + return amc6821_temp_write(dev, attr, channel, val); + case hwmon_fan: + return amc6821_fan_write(dev, attr, val); + case hwmon_pwm: + return amc6821_pwm_write(dev, attr, val); + default: + return -EOPNOTSUPP; + } +} + +static umode_t amc6821_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + case hwmon_temp_min_alarm: + case hwmon_temp_max_alarm: + case hwmon_temp_crit_alarm: + case hwmon_temp_fault: + return 0444; + case hwmon_temp_min: + case hwmon_temp_max: + case hwmon_temp_crit: + return 0644; + default: + return 0; + } + case hwmon_fan: + switch (attr) { + case hwmon_fan_input: + case hwmon_fan_fault: + return 0444; + case hwmon_fan_pulses: + case hwmon_fan_min: + case hwmon_fan_max: + case hwmon_fan_target: + return 0644; + default: + return 0; + } + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_enable: + case hwmon_pwm_input: + return 0644; + case hwmon_pwm_auto_channels_temp: + return 0444; + default: + return 0; + } + default: + return 0; + } +} + +static const struct hwmon_channel_info * const amc6821_info[] = { + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | + HWMON_T_CRIT | HWMON_T_MIN_ALARM | + HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM, + HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | + HWMON_T_CRIT | HWMON_T_MIN_ALARM | + HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | + HWMON_T_FAULT), + HWMON_CHANNEL_INFO(fan, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX | + HWMON_F_TARGET | HWMON_F_PULSES | HWMON_F_FAULT), + HWMON_CHANNEL_INFO(pwm, + HWMON_PWM_INPUT | HWMON_PWM_ENABLE | + HWMON_PWM_AUTO_CHANNELS_TEMP), + NULL +}; + +static const struct hwmon_ops amc6821_hwmon_ops = { + .is_visible = amc6821_is_visible, + .read = amc6821_read, + .write = amc6821_write, +}; + +static const struct hwmon_chip_info amc6821_chip_info = { + .ops = &amc6821_hwmon_ops, + .info = amc6821_info, +}; + /* Return 0 if detection is successful, -ENODEV otherwise */ -static int amc6821_detect( - struct i2c_client *client, - struct i2c_board_info *info) +static int amc6821_detect(struct i2c_client *client, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; int address = client->addr; @@ -874,9 +910,9 @@ static int amc6821_probe(struct i2c_client *client) if (err) return err; - hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, - data, - amc6821_groups); + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, + data, &amc6821_chip_info, + amc6821_groups); return PTR_ERR_OR_ZERO(hwmon_dev); } -- cgit From 4814241a5817ce8140dda8f1375bf76f99e68697 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 1 Jul 2024 08:57:58 -0700 Subject: hwmon: (amc6821) Add support for pwm1_mode attribute AMC6821 supports configuring if a fan is DC or PWM controlled. Add support for the pwm1_mode attribute to make it runtime configurable. Reviewed-by: Quentin Schulz Signed-off-by: Guenter Roeck --- drivers/hwmon/amc6821.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/amc6821.c b/drivers/hwmon/amc6821.c index 8beaea9ecbab..ec94392fcb65 100644 --- a/drivers/hwmon/amc6821.c +++ b/drivers/hwmon/amc6821.c @@ -312,6 +312,12 @@ static int amc6821_pwm_read(struct device *dev, u32 attr, long *val) break; } return 0; + case hwmon_pwm_mode: + err = regmap_read(regmap, AMC6821_REG_CONF2, ®val); + if (err) + return err; + *val = !!(regval & AMC6821_CONF2_TACH_MODE); + return 0; case hwmon_pwm_auto_channels_temp: err = regmap_read(regmap, AMC6821_REG_CONF1, ®val); if (err) @@ -367,6 +373,13 @@ static int amc6821_pwm_write(struct device *dev, u32 attr, long val) return regmap_update_bits(regmap, AMC6821_REG_CONF1, AMC6821_CONF1_FDRC0 | AMC6821_CONF1_FDRC1, mode); + case hwmon_pwm_mode: + if (val < 0 || val > 1) + return -EINVAL; + return regmap_update_bits(regmap, AMC6821_REG_CONF2, + AMC6821_CONF2_TACH_MODE, + val ? AMC6821_CONF2_TACH_MODE : 0); + break; case hwmon_pwm_input: if (val < 0 || val > 255) return -EINVAL; @@ -748,6 +761,7 @@ static umode_t amc6821_is_visible(const void *data, } case hwmon_pwm: switch (attr) { + case hwmon_pwm_mode: case hwmon_pwm_enable: case hwmon_pwm_input: return 0644; @@ -774,7 +788,7 @@ static const struct hwmon_channel_info * const amc6821_info[] = { HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_MAX | HWMON_F_TARGET | HWMON_F_PULSES | HWMON_F_FAULT), HWMON_CHANNEL_INFO(pwm, - HWMON_PWM_INPUT | HWMON_PWM_ENABLE | + HWMON_PWM_INPUT | HWMON_PWM_ENABLE | HWMON_PWM_MODE | HWMON_PWM_AUTO_CHANNELS_TEMP), NULL }; -- cgit From f4a180247e06c69e7d8fac4eb14d172c3a5d43a3 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 9 Jul 2024 15:41:07 -0700 Subject: hwmon: (g762) Initialize fans after configuring clock Adding support for G761 included adding support for an internal clock. Enabling the internal clock requires setting a bit in the FAN_CMD2 register. This is implemented in g762_fan_init(). However, g762_fan_init() is called before clock support is selected, and the flag indicating that the internal clock should be used is not yet set. Initialize the clock before initializing the fan to solve the problem. While at it, also add "g7621" to the i2c_device_id array. Cc: Christian Marangi Fixes: 6ce402327a6f ("hwmon: g672: add support for g761") Signed-off-by: Guenter Roeck --- drivers/hwmon/g762.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/g762.c b/drivers/hwmon/g762.c index a00cf3245eec..4fa3aa1271da 100644 --- a/drivers/hwmon/g762.c +++ b/drivers/hwmon/g762.c @@ -44,6 +44,7 @@ #define DRVNAME "g762" static const struct i2c_device_id g762_id[] = { + { "g761" }, { "g762" }, { "g763" }, { } @@ -1083,15 +1084,16 @@ static int g762_probe(struct i2c_client *client) data->client = client; mutex_init(&data->update_lock); - /* Enable fan failure detection and fan out of control protection */ - ret = g762_fan_init(dev); + /* Get configuration via DT ... */ + ret = g762_of_clock_enable(client); if (ret) return ret; - /* Get configuration via DT ... */ - ret = g762_of_clock_enable(client); + /* Enable fan failure detection and fan out of control protection */ + ret = g762_fan_init(dev); if (ret) return ret; + ret = g762_of_prop_import(client); if (ret) return ret; -- cgit From 04e3bb0b00a872e5bed0457c3e2c48a7828eaf28 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 10 Jul 2024 10:35:45 +0200 Subject: hwmon: (pmbus/ltc4286) Drop unused i2c device ids MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver doesn't make use of the different numbers assigned to the different devices. So drop them. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/9a48ba0368e0c8cddc94c5e4cf3edd7eadc03a2d.1720600141.git.u.kleine-koenig@baylibre.com Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/ltc4286.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/pmbus/ltc4286.c b/drivers/hwmon/pmbus/ltc4286.c index 9e7ceeb7e789..aabd0bcdfeee 100644 --- a/drivers/hwmon/pmbus/ltc4286.c +++ b/drivers/hwmon/pmbus/ltc4286.c @@ -58,8 +58,8 @@ static struct pmbus_driver_info ltc4286_info = { }; static const struct i2c_device_id ltc4286_id[] = { - { "ltc4286", 0 }, - { "ltc4287", 1 }, + { "ltc4286", }, + { "ltc4287", }, {} }; MODULE_DEVICE_TABLE(i2c, ltc4286_id); -- cgit From ed99ae74f2328e42286880c946953a8f9dd95a7e Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 12 Jul 2024 10:27:26 -0700 Subject: hwmon: Remove obsolete adm1021 and max6642 drivers ADM1021, MAX6642, and compatible chips are supported by the lm90 driver. Remove the obsolete stand-alone drivers to reduce maintenance overhead. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/Kconfig | 24 --- drivers/hwmon/Makefile | 2 - drivers/hwmon/adm1021.c | 503 ------------------------------------------------ drivers/hwmon/max6642.c | 314 ------------------------------ 4 files changed, 843 deletions(-) delete mode 100644 drivers/hwmon/adm1021.c delete mode 100644 drivers/hwmon/max6642.c (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 3d6f49526716..656439afcdc0 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -105,18 +105,6 @@ config SENSORS_AD7418 This driver can also be built as a module. If so, the module will be called ad7418. -config SENSORS_ADM1021 - tristate "Analog Devices ADM1021 and compatibles" - depends on I2C - depends on SENSORS_LM90=n - help - If you say yes here you get support for Analog Devices ADM1021 - and ADM1023 sensor chips and clones: Maxim MAX1617 and MAX1617A, - Genesys Logic GL523SM, National Semiconductor LM84 and TI THMC10. - - This driver can also be built as a module. If so, the module - will be called adm1021. - config SENSORS_ADM1025 tristate "Analog Devices ADM1025 and compatibles" depends on I2C @@ -1241,18 +1229,6 @@ config SENSORS_MAX6639 This driver can also be built as a module. If so, the module will be called max6639. -config SENSORS_MAX6642 - tristate "Maxim MAX6642 sensor chip" - depends on I2C - depends on SENSORS_LM90=n - help - If you say yes here you get support for MAX6642 sensor chip. - MAX6642 is a SMBus-Compatible Remote/Local Temperature Sensor - with Overtemperature Alarm from Maxim. - - This driver can also be built as a module. If so, the module - will be called max6642. - config SENSORS_MAX6650 tristate "Maxim MAX6650 sensor chip" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 6574ca67d761..4ab5ad06adde 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -31,7 +31,6 @@ obj-$(CONFIG_SENSORS_AD7414) += ad7414.o obj-$(CONFIG_SENSORS_AD7418) += ad7418.o obj-$(CONFIG_SENSORS_ADC128D818) += adc128d818.o obj-$(CONFIG_SENSORS_ADCXX) += adcxx.o -obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o obj-$(CONFIG_SENSORS_ADM1025) += adm1025.o obj-$(CONFIG_SENSORS_ADM1026) += adm1026.o obj-$(CONFIG_SENSORS_ADM1029) += adm1029.o @@ -154,7 +153,6 @@ obj-$(CONFIG_SENSORS_MAX31760) += max31760.o obj-$(CONFIG_SENSORS_MAX6620) += max6620.o obj-$(CONFIG_SENSORS_MAX6621) += max6621.o obj-$(CONFIG_SENSORS_MAX6639) += max6639.o -obj-$(CONFIG_SENSORS_MAX6642) += max6642.o obj-$(CONFIG_SENSORS_MAX6650) += max6650.o obj-$(CONFIG_SENSORS_MAX6697) += max6697.o obj-$(CONFIG_SENSORS_MAX31790) += max31790.o diff --git a/drivers/hwmon/adm1021.c b/drivers/hwmon/adm1021.c deleted file mode 100644 index 87b42e0cfd2e..000000000000 --- a/drivers/hwmon/adm1021.c +++ /dev/null @@ -1,503 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * adm1021.c - Part of lm_sensors, Linux kernel modules for hardware - * monitoring - * Copyright (c) 1998, 1999 Frodo Looijaard and - * Philip Edelbrock - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -/* Addresses to scan */ -static const unsigned short normal_i2c[] = { - 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END }; - -enum chips { - adm1021, adm1023, max1617, max1617a, thmc10, lm84, gl523sm, mc1066 }; - -/* adm1021 constants specified below */ - -/* The adm1021 registers */ -/* Read-only */ -/* For nr in 0-1 */ -#define ADM1021_REG_TEMP(nr) (nr) -#define ADM1021_REG_STATUS 0x02 -/* 0x41 = AD, 0x49 = TI, 0x4D = Maxim, 0x23 = Genesys , 0x54 = Onsemi */ -#define ADM1021_REG_MAN_ID 0xFE -/* ADM1021 = 0x0X, ADM1023 = 0x3X */ -#define ADM1021_REG_DEV_ID 0xFF -/* These use different addresses for reading/writing */ -#define ADM1021_REG_CONFIG_R 0x03 -#define ADM1021_REG_CONFIG_W 0x09 -#define ADM1021_REG_CONV_RATE_R 0x04 -#define ADM1021_REG_CONV_RATE_W 0x0A -/* These are for the ADM1023's additional precision on the remote temp sensor */ -#define ADM1023_REG_REM_TEMP_PREC 0x10 -#define ADM1023_REG_REM_OFFSET 0x11 -#define ADM1023_REG_REM_OFFSET_PREC 0x12 -#define ADM1023_REG_REM_TOS_PREC 0x13 -#define ADM1023_REG_REM_THYST_PREC 0x14 -/* limits */ -/* For nr in 0-1 */ -#define ADM1021_REG_TOS_R(nr) (0x05 + 2 * (nr)) -#define ADM1021_REG_TOS_W(nr) (0x0B + 2 * (nr)) -#define ADM1021_REG_THYST_R(nr) (0x06 + 2 * (nr)) -#define ADM1021_REG_THYST_W(nr) (0x0C + 2 * (nr)) -/* write-only */ -#define ADM1021_REG_ONESHOT 0x0F - -/* Initial values */ - -/* - * Note: Even though I left the low and high limits named os and hyst, - * they don't quite work like a thermostat the way the LM75 does. I.e., - * a lower temp than THYST actually triggers an alarm instead of - * clearing it. Weird, ey? --Phil - */ - -/* Each client has this additional data */ -struct adm1021_data { - struct i2c_client *client; - enum chips type; - - const struct attribute_group *groups[3]; - - struct mutex update_lock; - bool valid; /* true if following fields are valid */ - char low_power; /* !=0 if device in low power mode */ - unsigned long last_updated; /* In jiffies */ - - int temp_max[2]; /* Register values */ - int temp_min[2]; - int temp[2]; - u8 alarms; - /* Special values for ADM1023 only */ - u8 remote_temp_offset; - u8 remote_temp_offset_prec; -}; - -/* (amalysh) read only mode, otherwise any limit's writing confuse BIOS */ -static bool read_only; - -static struct adm1021_data *adm1021_update_device(struct device *dev) -{ - struct adm1021_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - - mutex_lock(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ + HZ / 2) - || !data->valid) { - int i; - - dev_dbg(dev, "Starting adm1021 update\n"); - - for (i = 0; i < 2; i++) { - data->temp[i] = 1000 * - (s8) i2c_smbus_read_byte_data( - client, ADM1021_REG_TEMP(i)); - data->temp_max[i] = 1000 * - (s8) i2c_smbus_read_byte_data( - client, ADM1021_REG_TOS_R(i)); - if (data->type != lm84) { - data->temp_min[i] = 1000 * - (s8) i2c_smbus_read_byte_data(client, - ADM1021_REG_THYST_R(i)); - } - } - data->alarms = i2c_smbus_read_byte_data(client, - ADM1021_REG_STATUS) & 0x7c; - if (data->type == adm1023) { - /* - * The ADM1023 provides 3 extra bits of precision for - * the remote sensor in extra registers. - */ - data->temp[1] += 125 * (i2c_smbus_read_byte_data( - client, ADM1023_REG_REM_TEMP_PREC) >> 5); - data->temp_max[1] += 125 * (i2c_smbus_read_byte_data( - client, ADM1023_REG_REM_TOS_PREC) >> 5); - data->temp_min[1] += 125 * (i2c_smbus_read_byte_data( - client, ADM1023_REG_REM_THYST_PREC) >> 5); - data->remote_temp_offset = - i2c_smbus_read_byte_data(client, - ADM1023_REG_REM_OFFSET); - data->remote_temp_offset_prec = - i2c_smbus_read_byte_data(client, - ADM1023_REG_REM_OFFSET_PREC); - } - data->last_updated = jiffies; - data->valid = true; - } - - mutex_unlock(&data->update_lock); - - return data; -} - -static ssize_t temp_show(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - int index = to_sensor_dev_attr(devattr)->index; - struct adm1021_data *data = adm1021_update_device(dev); - - return sprintf(buf, "%d\n", data->temp[index]); -} - -static ssize_t temp_max_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - int index = to_sensor_dev_attr(devattr)->index; - struct adm1021_data *data = adm1021_update_device(dev); - - return sprintf(buf, "%d\n", data->temp_max[index]); -} - -static ssize_t temp_min_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - int index = to_sensor_dev_attr(devattr)->index; - struct adm1021_data *data = adm1021_update_device(dev); - - return sprintf(buf, "%d\n", data->temp_min[index]); -} - -static ssize_t alarm_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - int index = to_sensor_dev_attr(attr)->index; - struct adm1021_data *data = adm1021_update_device(dev); - return sprintf(buf, "%u\n", (data->alarms >> index) & 1); -} - -static ssize_t alarms_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct adm1021_data *data = adm1021_update_device(dev); - return sprintf(buf, "%u\n", data->alarms); -} - -static ssize_t temp_max_store(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) -{ - int index = to_sensor_dev_attr(devattr)->index; - struct adm1021_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - long temp; - int reg_val, err; - - err = kstrtol(buf, 10, &temp); - if (err) - return err; - temp /= 1000; - - mutex_lock(&data->update_lock); - reg_val = clamp_val(temp, -128, 127); - data->temp_max[index] = reg_val * 1000; - if (!read_only) - i2c_smbus_write_byte_data(client, ADM1021_REG_TOS_W(index), - reg_val); - mutex_unlock(&data->update_lock); - - return count; -} - -static ssize_t temp_min_store(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) -{ - int index = to_sensor_dev_attr(devattr)->index; - struct adm1021_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - long temp; - int reg_val, err; - - err = kstrtol(buf, 10, &temp); - if (err) - return err; - temp /= 1000; - - mutex_lock(&data->update_lock); - reg_val = clamp_val(temp, -128, 127); - data->temp_min[index] = reg_val * 1000; - if (!read_only) - i2c_smbus_write_byte_data(client, ADM1021_REG_THYST_W(index), - reg_val); - mutex_unlock(&data->update_lock); - - return count; -} - -static ssize_t low_power_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct adm1021_data *data = adm1021_update_device(dev); - return sprintf(buf, "%d\n", data->low_power); -} - -static ssize_t low_power_store(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) -{ - struct adm1021_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - char low_power; - unsigned long val; - int err; - - err = kstrtoul(buf, 10, &val); - if (err) - return err; - low_power = val != 0; - - mutex_lock(&data->update_lock); - if (low_power != data->low_power) { - int config = i2c_smbus_read_byte_data( - client, ADM1021_REG_CONFIG_R); - data->low_power = low_power; - i2c_smbus_write_byte_data(client, ADM1021_REG_CONFIG_W, - (config & 0xBF) | (low_power << 6)); - } - mutex_unlock(&data->update_lock); - - return count; -} - - -static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, 0); -static SENSOR_DEVICE_ATTR_RW(temp1_max, temp_max, 0); -static SENSOR_DEVICE_ATTR_RW(temp1_min, temp_min, 0); -static SENSOR_DEVICE_ATTR_RO(temp2_input, temp, 1); -static SENSOR_DEVICE_ATTR_RW(temp2_max, temp_max, 1); -static SENSOR_DEVICE_ATTR_RW(temp2_min, temp_min, 1); -static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, alarm, 6); -static SENSOR_DEVICE_ATTR_RO(temp1_min_alarm, alarm, 5); -static SENSOR_DEVICE_ATTR_RO(temp2_max_alarm, alarm, 4); -static SENSOR_DEVICE_ATTR_RO(temp2_min_alarm, alarm, 3); -static SENSOR_DEVICE_ATTR_RO(temp2_fault, alarm, 2); - -static DEVICE_ATTR_RO(alarms); -static DEVICE_ATTR_RW(low_power); - -static struct attribute *adm1021_attributes[] = { - &sensor_dev_attr_temp1_max.dev_attr.attr, - &sensor_dev_attr_temp1_input.dev_attr.attr, - &sensor_dev_attr_temp2_max.dev_attr.attr, - &sensor_dev_attr_temp2_input.dev_attr.attr, - &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp2_fault.dev_attr.attr, - &dev_attr_alarms.attr, - &dev_attr_low_power.attr, - NULL -}; - -static const struct attribute_group adm1021_group = { - .attrs = adm1021_attributes, -}; - -static struct attribute *adm1021_min_attributes[] = { - &sensor_dev_attr_temp1_min.dev_attr.attr, - &sensor_dev_attr_temp2_min.dev_attr.attr, - &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, - &sensor_dev_attr_temp2_min_alarm.dev_attr.attr, - NULL -}; - -static const struct attribute_group adm1021_min_group = { - .attrs = adm1021_min_attributes, -}; - -/* Return 0 if detection is successful, -ENODEV otherwise */ -static int adm1021_detect(struct i2c_client *client, - struct i2c_board_info *info) -{ - struct i2c_adapter *adapter = client->adapter; - const char *type_name; - int reg, conv_rate, status, config, man_id, dev_id; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { - pr_debug("detect failed, smbus byte data not supported!\n"); - return -ENODEV; - } - - status = i2c_smbus_read_byte_data(client, ADM1021_REG_STATUS); - conv_rate = i2c_smbus_read_byte_data(client, - ADM1021_REG_CONV_RATE_R); - config = i2c_smbus_read_byte_data(client, ADM1021_REG_CONFIG_R); - - /* Check unused bits */ - if ((status & 0x03) || (config & 0x3F) || (conv_rate & 0xF8)) { - pr_debug("detect failed, chip not detected!\n"); - return -ENODEV; - } - - /* Determine the chip type. */ - man_id = i2c_smbus_read_byte_data(client, ADM1021_REG_MAN_ID); - dev_id = i2c_smbus_read_byte_data(client, ADM1021_REG_DEV_ID); - - if (man_id < 0 || dev_id < 0) - return -ENODEV; - - if (man_id == 0x4d && dev_id == 0x01) { - /* - * dev_id 0x01 matches MAX6680, MAX6695, MAX6696, and possibly - * others. Read register which is unsupported on MAX1617 but - * exists on all those chips and compare with the dev_id - * register. If it matches, it may be a MAX1617A. - */ - reg = i2c_smbus_read_byte_data(client, - ADM1023_REG_REM_TEMP_PREC); - if (reg != dev_id) - return -ENODEV; - type_name = "max1617a"; - } else if (man_id == 0x41) { - if ((dev_id & 0xF0) == 0x30) - type_name = "adm1023"; - else if ((dev_id & 0xF0) == 0x00) - type_name = "adm1021"; - else - return -ENODEV; - } else if (man_id == 0x49) - type_name = "thmc10"; - else if (man_id == 0x23) - type_name = "gl523sm"; - else if (man_id == 0x54) - type_name = "mc1066"; - else { - int lte, rte, lhi, rhi, llo, rlo; - - /* extra checks for LM84 and MAX1617 to avoid misdetections */ - - llo = i2c_smbus_read_byte_data(client, ADM1021_REG_THYST_R(0)); - rlo = i2c_smbus_read_byte_data(client, ADM1021_REG_THYST_R(1)); - - /* fail if any of the additional register reads failed */ - if (llo < 0 || rlo < 0) - return -ENODEV; - - lte = i2c_smbus_read_byte_data(client, ADM1021_REG_TEMP(0)); - rte = i2c_smbus_read_byte_data(client, ADM1021_REG_TEMP(1)); - lhi = i2c_smbus_read_byte_data(client, ADM1021_REG_TOS_R(0)); - rhi = i2c_smbus_read_byte_data(client, ADM1021_REG_TOS_R(1)); - - /* - * Fail for negative temperatures and negative high limits. - * This check also catches read errors on the tested registers. - */ - if ((s8)lte < 0 || (s8)rte < 0 || (s8)lhi < 0 || (s8)rhi < 0) - return -ENODEV; - - /* fail if all registers hold the same value */ - if (lte == rte && lte == lhi && lte == rhi && lte == llo - && lte == rlo) - return -ENODEV; - - /* - * LM84 Mfr ID is in a different place, - * and it has more unused bits. Registers at 0xfe and 0xff - * are undefined and return the most recently read value, - * here the value of the configuration register. - */ - if (conv_rate == 0x00 - && man_id == config && dev_id == config - && (config & 0x7F) == 0x00 - && (status & 0xAB) == 0x00) { - type_name = "lm84"; - } else { - if ((config & 0x3f) || (status & 0x03)) - return -ENODEV; - /* fail if low limits are larger than high limits */ - if ((s8)llo > lhi || (s8)rlo > rhi) - return -ENODEV; - type_name = "max1617"; - } - } - - pr_debug("Detected chip %s at adapter %d, address 0x%02x.\n", - type_name, i2c_adapter_id(adapter), client->addr); - strscpy(info->type, type_name, I2C_NAME_SIZE); - - return 0; -} - -static void adm1021_init_client(struct i2c_client *client) -{ - /* Enable ADC and disable suspend mode */ - i2c_smbus_write_byte_data(client, ADM1021_REG_CONFIG_W, - i2c_smbus_read_byte_data(client, ADM1021_REG_CONFIG_R) & 0xBF); - /* Set Conversion rate to 1/sec (this can be tinkered with) */ - i2c_smbus_write_byte_data(client, ADM1021_REG_CONV_RATE_W, 0x04); -} - -static int adm1021_probe(struct i2c_client *client) -{ - struct device *dev = &client->dev; - struct adm1021_data *data; - struct device *hwmon_dev; - - data = devm_kzalloc(dev, sizeof(struct adm1021_data), GFP_KERNEL); - if (!data) - return -ENOMEM; - - data->client = client; - data->type = (uintptr_t)i2c_get_match_data(client); - mutex_init(&data->update_lock); - - /* Initialize the ADM1021 chip */ - if (data->type != lm84 && !read_only) - adm1021_init_client(client); - - data->groups[0] = &adm1021_group; - if (data->type != lm84) - data->groups[1] = &adm1021_min_group; - - hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, - data, data->groups); - - return PTR_ERR_OR_ZERO(hwmon_dev); -} - -static const struct i2c_device_id adm1021_id[] = { - { "adm1021", adm1021 }, - { "adm1023", adm1023 }, - { "max1617", max1617 }, - { "max1617a", max1617a }, - { "thmc10", thmc10 }, - { "lm84", lm84 }, - { "gl523sm", gl523sm }, - { "mc1066", mc1066 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, adm1021_id); - -static struct i2c_driver adm1021_driver = { - .class = I2C_CLASS_HWMON, - .driver = { - .name = "adm1021", - }, - .probe = adm1021_probe, - .id_table = adm1021_id, - .detect = adm1021_detect, - .address_list = normal_i2c, -}; - -module_i2c_driver(adm1021_driver); - -MODULE_AUTHOR("Frodo Looijaard and " - "Philip Edelbrock "); -MODULE_DESCRIPTION("adm1021 driver"); -MODULE_LICENSE("GPL"); - -module_param(read_only, bool, 0); -MODULE_PARM_DESC(read_only, "Don't set any values, read only mode"); diff --git a/drivers/hwmon/max6642.c b/drivers/hwmon/max6642.c deleted file mode 100644 index 9302ab233910..000000000000 --- a/drivers/hwmon/max6642.c +++ /dev/null @@ -1,314 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Driver for +/-1 degree C, SMBus-Compatible Remote/Local Temperature Sensor - * with Overtemperature Alarm - * - * Copyright (C) 2011 AppearTV AS - * - * Derived from: - * - * Based on the max1619 driver. - * Copyright (C) 2003-2004 Oleksij Rempel - * Jean Delvare - * - * The MAX6642 is a sensor chip made by Maxim. - * It reports up to two temperatures (its own plus up to - * one external one). Complete datasheet can be - * obtained from Maxim's website at: - * http://datasheets.maxim-ic.com/en/ds/MAX6642.pdf - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static const unsigned short normal_i2c[] = { - 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; - -/* - * The MAX6642 registers - */ - -#define MAX6642_REG_R_MAN_ID 0xFE -#define MAX6642_REG_R_CONFIG 0x03 -#define MAX6642_REG_W_CONFIG 0x09 -#define MAX6642_REG_R_STATUS 0x02 -#define MAX6642_REG_R_LOCAL_TEMP 0x00 -#define MAX6642_REG_R_LOCAL_TEMPL 0x11 -#define MAX6642_REG_R_LOCAL_HIGH 0x05 -#define MAX6642_REG_W_LOCAL_HIGH 0x0B -#define MAX6642_REG_R_REMOTE_TEMP 0x01 -#define MAX6642_REG_R_REMOTE_TEMPL 0x10 -#define MAX6642_REG_R_REMOTE_HIGH 0x07 -#define MAX6642_REG_W_REMOTE_HIGH 0x0D - -/* - * Conversions - */ - -static int temp_from_reg10(int val) -{ - return val * 250; -} - -static int temp_from_reg(int val) -{ - return val * 1000; -} - -static int temp_to_reg(int val) -{ - return val / 1000; -} - -/* - * Client data (each client gets its own) - */ - -struct max6642_data { - struct i2c_client *client; - struct mutex update_lock; - bool valid; /* zero until following fields are valid */ - unsigned long last_updated; /* in jiffies */ - - /* registers values */ - u16 temp_input[2]; /* local/remote */ - u16 temp_high[2]; /* local/remote */ - u8 alarms; -}; - -/* - * Real code - */ - -static void max6642_init_client(struct max6642_data *data, - struct i2c_client *client) -{ - u8 config; - - /* - * Start the conversions. - */ - config = i2c_smbus_read_byte_data(client, MAX6642_REG_R_CONFIG); - if (config & 0x40) - i2c_smbus_write_byte_data(client, MAX6642_REG_W_CONFIG, - config & 0xBF); /* run */ - - data->temp_high[0] = i2c_smbus_read_byte_data(client, - MAX6642_REG_R_LOCAL_HIGH); - data->temp_high[1] = i2c_smbus_read_byte_data(client, - MAX6642_REG_R_REMOTE_HIGH); -} - -/* Return 0 if detection is successful, -ENODEV otherwise */ -static int max6642_detect(struct i2c_client *client, - struct i2c_board_info *info) -{ - struct i2c_adapter *adapter = client->adapter; - u8 reg_config, reg_status, man_id; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; - - /* identification */ - man_id = i2c_smbus_read_byte_data(client, MAX6642_REG_R_MAN_ID); - if (man_id != 0x4D) - return -ENODEV; - - /* sanity check */ - if (i2c_smbus_read_byte_data(client, 0x04) != 0x4D - || i2c_smbus_read_byte_data(client, 0x06) != 0x4D - || i2c_smbus_read_byte_data(client, 0xff) != 0x4D) - return -ENODEV; - - /* - * We read the config and status register, the 4 lower bits in the - * config register should be zero and bit 5, 3, 1 and 0 should be - * zero in the status register. - */ - reg_config = i2c_smbus_read_byte_data(client, MAX6642_REG_R_CONFIG); - if ((reg_config & 0x0f) != 0x00) - return -ENODEV; - - /* in between, another round of sanity checks */ - if (i2c_smbus_read_byte_data(client, 0x04) != reg_config - || i2c_smbus_read_byte_data(client, 0x06) != reg_config - || i2c_smbus_read_byte_data(client, 0xff) != reg_config) - return -ENODEV; - - reg_status = i2c_smbus_read_byte_data(client, MAX6642_REG_R_STATUS); - if ((reg_status & 0x2b) != 0x00) - return -ENODEV; - - strscpy(info->type, "max6642", I2C_NAME_SIZE); - - return 0; -} - -static struct max6642_data *max6642_update_device(struct device *dev) -{ - struct max6642_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - u16 val, tmp; - - mutex_lock(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { - dev_dbg(dev, "Updating max6642 data.\n"); - val = i2c_smbus_read_byte_data(client, - MAX6642_REG_R_LOCAL_TEMPL); - tmp = (val >> 6) & 3; - val = i2c_smbus_read_byte_data(client, - MAX6642_REG_R_LOCAL_TEMP); - val = (val << 2) | tmp; - data->temp_input[0] = val; - val = i2c_smbus_read_byte_data(client, - MAX6642_REG_R_REMOTE_TEMPL); - tmp = (val >> 6) & 3; - val = i2c_smbus_read_byte_data(client, - MAX6642_REG_R_REMOTE_TEMP); - val = (val << 2) | tmp; - data->temp_input[1] = val; - data->alarms = i2c_smbus_read_byte_data(client, - MAX6642_REG_R_STATUS); - - data->last_updated = jiffies; - data->valid = true; - } - - mutex_unlock(&data->update_lock); - - return data; -} - -/* - * Sysfs stuff - */ - -static ssize_t temp_max10_show(struct device *dev, - struct device_attribute *dev_attr, char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr); - struct max6642_data *data = max6642_update_device(dev); - - return sprintf(buf, "%d\n", - temp_from_reg10(data->temp_input[attr->index])); -} - -static ssize_t temp_max_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(attr); - struct max6642_data *data = max6642_update_device(dev); - - return sprintf(buf, "%d\n", temp_from_reg(data->temp_high[attr2->nr])); -} - -static ssize_t temp_max_store(struct device *dev, - struct device_attribute *attr, const char *buf, - size_t count) -{ - struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(attr); - struct max6642_data *data = dev_get_drvdata(dev); - unsigned long val; - int err; - - err = kstrtoul(buf, 10, &val); - if (err < 0) - return err; - - mutex_lock(&data->update_lock); - data->temp_high[attr2->nr] = clamp_val(temp_to_reg(val), 0, 255); - i2c_smbus_write_byte_data(data->client, attr2->index, - data->temp_high[attr2->nr]); - mutex_unlock(&data->update_lock); - return count; -} - -static ssize_t alarm_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - int bitnr = to_sensor_dev_attr(attr)->index; - struct max6642_data *data = max6642_update_device(dev); - return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1); -} - -static SENSOR_DEVICE_ATTR_RO(temp1_input, temp_max10, 0); -static SENSOR_DEVICE_ATTR_RO(temp2_input, temp_max10, 1); -static SENSOR_DEVICE_ATTR_2_RW(temp1_max, temp_max, 0, - MAX6642_REG_W_LOCAL_HIGH); -static SENSOR_DEVICE_ATTR_2_RW(temp2_max, temp_max, 1, - MAX6642_REG_W_REMOTE_HIGH); -static SENSOR_DEVICE_ATTR_RO(temp2_fault, alarm, 2); -static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, alarm, 6); -static SENSOR_DEVICE_ATTR_RO(temp2_max_alarm, alarm, 4); - -static struct attribute *max6642_attrs[] = { - &sensor_dev_attr_temp1_input.dev_attr.attr, - &sensor_dev_attr_temp2_input.dev_attr.attr, - &sensor_dev_attr_temp1_max.dev_attr.attr, - &sensor_dev_attr_temp2_max.dev_attr.attr, - - &sensor_dev_attr_temp2_fault.dev_attr.attr, - &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, - NULL -}; -ATTRIBUTE_GROUPS(max6642); - -static int max6642_probe(struct i2c_client *client) -{ - struct device *dev = &client->dev; - struct max6642_data *data; - struct device *hwmon_dev; - - data = devm_kzalloc(dev, sizeof(struct max6642_data), GFP_KERNEL); - if (!data) - return -ENOMEM; - - data->client = client; - mutex_init(&data->update_lock); - - /* Initialize the MAX6642 chip */ - max6642_init_client(data, client); - - hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev, - client->name, data, - max6642_groups); - return PTR_ERR_OR_ZERO(hwmon_dev); -} - -/* - * Driver data (common to all clients) - */ - -static const struct i2c_device_id max6642_id[] = { - { "max6642" }, - { } -}; -MODULE_DEVICE_TABLE(i2c, max6642_id); - -static struct i2c_driver max6642_driver = { - .class = I2C_CLASS_HWMON, - .driver = { - .name = "max6642", - }, - .probe = max6642_probe, - .id_table = max6642_id, - .detect = max6642_detect, - .address_list = normal_i2c, -}; - -module_i2c_driver(max6642_driver); - -MODULE_AUTHOR("Per Dalen "); -MODULE_DESCRIPTION("MAX6642 sensor driver"); -MODULE_LICENSE("GPL"); -- cgit From cbf7467828cd4ec7ceac7a8b5b5ddb2f69f07b0e Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 13 Jul 2024 14:26:19 -0700 Subject: hwmon: (max6697) Fix underflow when writing limit attributes Using DIV_ROUND_CLOSEST() on an unbound value can result in underflows. Indeed, module test scripts report: temp1_max: Suspected underflow: [min=0, read 255000, written -9223372036854775808] temp1_crit: Suspected underflow: [min=0, read 255000, written -9223372036854775808] Fix by introducing an extra set of clamping. Fixes: 5372d2d71c46 ("hwmon: Driver for Maxim MAX6697 and compatibles") Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/max6697.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/max6697.c b/drivers/hwmon/max6697.c index b28b7b9448aa..05085646ba99 100644 --- a/drivers/hwmon/max6697.c +++ b/drivers/hwmon/max6697.c @@ -311,6 +311,7 @@ static ssize_t temp_store(struct device *dev, return ret; mutex_lock(&data->update_lock); + temp = clamp_val(temp, -1000000, 1000000); /* prevent underflow */ temp = DIV_ROUND_CLOSEST(temp, 1000) + data->temp_offset; temp = clamp_val(temp, 0, data->type == max6581 ? 255 : 127); data->temp[nr][index] = temp; -- cgit From 1ea3fd1eb9869fcdcbc9c68f9728bfc47b9503f1 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 13 Jul 2024 12:03:53 -0700 Subject: hwmon: (max6697) Fix swapped temp{1,8} critical alarms The critical alarm bit for the local temperature sensor (temp1) is in bit 7 of register 0x45 (not bit 6), and the critical alarm bit for remote temperature sensor 7 (temp8) is in bit 6 (not bit 7). This only affects MAX6581 since all other chips supported by this driver do not support those critical alarms. Fixes: 5372d2d71c46 ("hwmon: Driver for Maxim MAX6697 and compatibles") Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/max6697.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/max6697.c b/drivers/hwmon/max6697.c index 05085646ba99..20981f9443dd 100644 --- a/drivers/hwmon/max6697.c +++ b/drivers/hwmon/max6697.c @@ -429,14 +429,14 @@ static SENSOR_DEVICE_ATTR_RO(temp6_max_alarm, alarm, 20); static SENSOR_DEVICE_ATTR_RO(temp7_max_alarm, alarm, 21); static SENSOR_DEVICE_ATTR_RO(temp8_max_alarm, alarm, 23); -static SENSOR_DEVICE_ATTR_RO(temp1_crit_alarm, alarm, 14); +static SENSOR_DEVICE_ATTR_RO(temp1_crit_alarm, alarm, 15); static SENSOR_DEVICE_ATTR_RO(temp2_crit_alarm, alarm, 8); static SENSOR_DEVICE_ATTR_RO(temp3_crit_alarm, alarm, 9); static SENSOR_DEVICE_ATTR_RO(temp4_crit_alarm, alarm, 10); static SENSOR_DEVICE_ATTR_RO(temp5_crit_alarm, alarm, 11); static SENSOR_DEVICE_ATTR_RO(temp6_crit_alarm, alarm, 12); static SENSOR_DEVICE_ATTR_RO(temp7_crit_alarm, alarm, 13); -static SENSOR_DEVICE_ATTR_RO(temp8_crit_alarm, alarm, 15); +static SENSOR_DEVICE_ATTR_RO(temp8_crit_alarm, alarm, 14); static SENSOR_DEVICE_ATTR_RO(temp2_fault, alarm, 1); static SENSOR_DEVICE_ATTR_RO(temp3_fault, alarm, 2); -- cgit