From 64fbb4c42ab910af6c06c37106f9e06ccde91e34 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 26 Jan 2025 13:46:19 -0800 Subject: [PATCH 01/16] hwmon: (pmbus/core) Optimize debugfs status attribute initialization Define debugfs attributes used to access status registers in a data structure and loop through it instead of creating debugfs files one by one. This reduces code size and simplifies adding additional attributes if needed. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus_core.c | 132 +++++++++---------------------- 1 file changed, 36 insertions(+), 96 deletions(-) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index a0a9da6e9453..f8965829fe17 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -3500,6 +3500,7 @@ static void pmbus_remove_symlink(void *symlink) struct pmbus_debugfs_data { u8 reg; + u32 flag; const char *name; }; @@ -3512,6 +3513,19 @@ static const struct pmbus_debugfs_data pmbus_debugfs_block_data[] = { { .reg = PMBUS_MFR_SERIAL, .name = "mfr_serial" }, }; +static const struct pmbus_debugfs_data pmbus_debugfs_status_data[] = { + { .reg = PMBUS_STATUS_VOUT, .flag = PMBUS_HAVE_STATUS_VOUT, .name = "status%d_vout" }, + { .reg = PMBUS_STATUS_IOUT, .flag = PMBUS_HAVE_STATUS_IOUT, .name = "status%d_iout" }, + { .reg = PMBUS_STATUS_INPUT, .flag = PMBUS_HAVE_STATUS_INPUT, .name = "status%d_input" }, + { .reg = PMBUS_STATUS_TEMPERATURE, .flag = PMBUS_HAVE_STATUS_TEMP, + .name = "status%d_temp" }, + { .reg = PMBUS_STATUS_FAN_12, .flag = PMBUS_HAVE_STATUS_FAN12, .name = "status%d_fan12" }, + { .reg = PMBUS_STATUS_FAN_34, .flag = PMBUS_HAVE_STATUS_FAN34, .name = "status%d_fan34" }, + { .reg = PMBUS_STATUS_CML, .name = "status%d_cml" }, + { .reg = PMBUS_STATUS_OTHER, .name = "status%d_other" }, + { .reg = PMBUS_STATUS_MFR_SPECIFIC, .name = "status%d_mfr" }, +}; + static void pmbus_init_debugfs(struct i2c_client *client, struct pmbus_data *data) { @@ -3519,7 +3533,7 @@ static void pmbus_init_debugfs(struct i2c_client *client, struct pmbus_debugfs_entry *entries; const char *pathname, *symlink; char name[PMBUS_NAME_SIZE]; - int i, idx = 0; + int page, i, idx = 0; /* * client->debugfs may be NULL or an ERR_PTR(). dentry_path_raw() @@ -3555,11 +3569,12 @@ static void pmbus_init_debugfs(struct i2c_client *client, * Allocate the max possible entries we need. * device specific: * ARRAY_SIZE(pmbus_debugfs_block_data) + 1 - * 10 entries page-specific + * page specific: + * ARRAY_SIZE(pmbus_debugfs_status_data) + 1 */ entries = devm_kcalloc(data->dev, ARRAY_SIZE(pmbus_debugfs_block_data) + 1 + - data->info->pages * 10, + data->info->pages * (ARRAY_SIZE(pmbus_debugfs_status_data) + 1), sizeof(*entries), GFP_KERNEL); if (!entries) return; @@ -3595,107 +3610,32 @@ static void pmbus_init_debugfs(struct i2c_client *client, } /* Add page specific entries */ - for (i = 0; i < data->info->pages; ++i) { + for (page = 0; page < data->info->pages; ++page) { /* Check accessibility of status register if it's not page 0 */ - if (!i || pmbus_check_status_register(client, i)) { + if (!page || pmbus_check_status_register(client, page)) { /* No need to set reg as we have special read op. */ entries[idx].client = client; - entries[idx].page = i; - scnprintf(name, PMBUS_NAME_SIZE, "status%d", i); + entries[idx].page = page; + scnprintf(name, PMBUS_NAME_SIZE, "status%d", page); debugfs_create_file(name, 0444, debugfs, &entries[idx++], &pmbus_debugfs_ops_status); } - if (data->info->func[i] & PMBUS_HAVE_STATUS_VOUT) { - entries[idx].client = client; - entries[idx].page = i; - entries[idx].reg = PMBUS_STATUS_VOUT; - scnprintf(name, PMBUS_NAME_SIZE, "status%d_vout", i); - debugfs_create_file(name, 0444, debugfs, - &entries[idx++], - &pmbus_debugfs_ops); - } - - if (data->info->func[i] & PMBUS_HAVE_STATUS_IOUT) { - entries[idx].client = client; - entries[idx].page = i; - entries[idx].reg = PMBUS_STATUS_IOUT; - scnprintf(name, PMBUS_NAME_SIZE, "status%d_iout", i); - debugfs_create_file(name, 0444, debugfs, - &entries[idx++], - &pmbus_debugfs_ops); - } - - if (data->info->func[i] & PMBUS_HAVE_STATUS_INPUT) { - entries[idx].client = client; - entries[idx].page = i; - entries[idx].reg = PMBUS_STATUS_INPUT; - scnprintf(name, PMBUS_NAME_SIZE, "status%d_input", i); - debugfs_create_file(name, 0444, debugfs, - &entries[idx++], - &pmbus_debugfs_ops); - } - - if (data->info->func[i] & PMBUS_HAVE_STATUS_TEMP) { - entries[idx].client = client; - entries[idx].page = i; - entries[idx].reg = PMBUS_STATUS_TEMPERATURE; - scnprintf(name, PMBUS_NAME_SIZE, "status%d_temp", i); - debugfs_create_file(name, 0444, debugfs, - &entries[idx++], - &pmbus_debugfs_ops); - } - - if (pmbus_check_byte_register(client, i, PMBUS_STATUS_CML)) { - entries[idx].client = client; - entries[idx].page = i; - entries[idx].reg = PMBUS_STATUS_CML; - scnprintf(name, PMBUS_NAME_SIZE, "status%d_cml", i); - debugfs_create_file(name, 0444, debugfs, - &entries[idx++], - &pmbus_debugfs_ops); - } - - if (pmbus_check_byte_register(client, i, PMBUS_STATUS_OTHER)) { - entries[idx].client = client; - entries[idx].page = i; - entries[idx].reg = PMBUS_STATUS_OTHER; - scnprintf(name, PMBUS_NAME_SIZE, "status%d_other", i); - debugfs_create_file(name, 0444, debugfs, - &entries[idx++], - &pmbus_debugfs_ops); - } - - if (pmbus_check_byte_register(client, i, - PMBUS_STATUS_MFR_SPECIFIC)) { - entries[idx].client = client; - entries[idx].page = i; - entries[idx].reg = PMBUS_STATUS_MFR_SPECIFIC; - scnprintf(name, PMBUS_NAME_SIZE, "status%d_mfr", i); - debugfs_create_file(name, 0444, debugfs, - &entries[idx++], - &pmbus_debugfs_ops); - } - - if (data->info->func[i] & PMBUS_HAVE_STATUS_FAN12) { - entries[idx].client = client; - entries[idx].page = i; - entries[idx].reg = PMBUS_STATUS_FAN_12; - scnprintf(name, PMBUS_NAME_SIZE, "status%d_fan12", i); - debugfs_create_file(name, 0444, debugfs, - &entries[idx++], - &pmbus_debugfs_ops); - } - - if (data->info->func[i] & PMBUS_HAVE_STATUS_FAN34) { - entries[idx].client = client; - entries[idx].page = i; - entries[idx].reg = PMBUS_STATUS_FAN_34; - scnprintf(name, PMBUS_NAME_SIZE, "status%d_fan34", i); - debugfs_create_file(name, 0444, debugfs, - &entries[idx++], - &pmbus_debugfs_ops); + for (i = 0; i < ARRAY_SIZE(pmbus_debugfs_status_data); i++) { + const struct pmbus_debugfs_data *d = + &pmbus_debugfs_status_data[i]; + + if ((data->info->func[page] & d->flag) || + (!d->flag && pmbus_check_byte_register(client, page, d->reg))) { + entries[idx].client = client; + entries[idx].page = page; + entries[idx].reg = d->reg; + scnprintf(name, PMBUS_NAME_SIZE, d->name, page); + debugfs_create_file(name, 0444, debugfs, + &entries[idx++], + &pmbus_debugfs_ops); + } } } } -- 2.51.0 From c90a00368d90b4ac46712b870debfff6ae50bb87 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 1 Feb 2025 06:42:29 -0800 Subject: [PATCH 02/16] hwmon: (pmbus/core) Report content of CAPABILITY register in debugfs Report the value of the CAPABILITY register in debugfs if supported. Only check if the register is supported if PMBUS_NO_CAPABILITY is not set. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus_core.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index f8965829fe17..cfeba2e4c5c3 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -3568,12 +3568,12 @@ static void pmbus_init_debugfs(struct i2c_client *client, /* * Allocate the max possible entries we need. * device specific: - * ARRAY_SIZE(pmbus_debugfs_block_data) + 1 + * ARRAY_SIZE(pmbus_debugfs_block_data) + 2 * page specific: * ARRAY_SIZE(pmbus_debugfs_status_data) + 1 */ entries = devm_kcalloc(data->dev, - ARRAY_SIZE(pmbus_debugfs_block_data) + 1 + + ARRAY_SIZE(pmbus_debugfs_block_data) + 2 + data->info->pages * (ARRAY_SIZE(pmbus_debugfs_status_data) + 1), sizeof(*entries), GFP_KERNEL); if (!entries) @@ -3587,6 +3587,15 @@ static void pmbus_init_debugfs(struct i2c_client *client, * assume that values of the following registers are the same for all * pages and report values only for page 0. */ + if (!(data->flags & PMBUS_NO_CAPABILITY) && + pmbus_check_byte_register(client, 0, PMBUS_CAPABILITY)) { + entries[idx].client = client; + entries[idx].page = 0; + entries[idx].reg = PMBUS_CAPABILITY; + debugfs_create_file("capability", 0444, debugfs, + &entries[idx++], + &pmbus_debugfs_ops); + } if (pmbus_check_byte_register(client, 0, PMBUS_REVISION)) { entries[idx].client = client; entries[idx].page = 0; -- 2.51.0 From 9f8e2e49c55fd4df0f9da2ddfa313cc6b868565d Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Mon, 10 Feb 2025 15:59:29 +0100 Subject: [PATCH 03/16] dt-bindings: hwmon: gpio-fan: Add optional regulator support This adds an optional regulator support (e.g. switchable supply) to the GPIO fan binding. Signed-off-by: Alexander Stein Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20250210145934.761280-2-alexander.stein@ew.tq-group.com [groeck: Changed power supply description as suggested by Krzysztof] Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/hwmon/gpio-fan.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/hwmon/gpio-fan.yaml b/Documentation/devicetree/bindings/hwmon/gpio-fan.yaml index 7f30cfc87350..4faebbb4c7ab 100644 --- a/Documentation/devicetree/bindings/hwmon/gpio-fan.yaml +++ b/Documentation/devicetree/bindings/hwmon/gpio-fan.yaml @@ -23,6 +23,9 @@ properties: alarm-gpios: maxItems: 1 + fan-supply: + description: Power supply for fan + gpio-fan,speed-map: $ref: /schemas/types.yaml#/definitions/uint32-matrix minItems: 2 -- 2.51.0 From 9fee7d19bab635f89223cc40dfd2c8797fdc4988 Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Mon, 10 Feb 2025 15:59:30 +0100 Subject: [PATCH 04/16] hwmon: (gpio-fan) Add missing mutex locks set_fan_speed() is expected to be called with fan_data->lock being locked. Add locking for proper synchronization. Signed-off-by: Alexander Stein Link: https://lore.kernel.org/r/20250210145934.761280-3-alexander.stein@ew.tq-group.com Signed-off-by: Guenter Roeck --- drivers/hwmon/gpio-fan.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c index d92c536be9af..b779240328d5 100644 --- a/drivers/hwmon/gpio-fan.c +++ b/drivers/hwmon/gpio-fan.c @@ -393,7 +393,12 @@ static int gpio_fan_set_cur_state(struct thermal_cooling_device *cdev, if (state >= fan_data->num_speed) return -EINVAL; + mutex_lock(&fan_data->lock); + set_fan_speed(fan_data, state); + + mutex_unlock(&fan_data->lock); + return 0; } @@ -489,7 +494,11 @@ MODULE_DEVICE_TABLE(of, of_gpio_fan_match); static void gpio_fan_stop(void *data) { + struct gpio_fan_data *fan_data = data; + + mutex_lock(&fan_data->lock); set_fan_speed(data, 0); + mutex_unlock(&fan_data->lock); } static int gpio_fan_probe(struct platform_device *pdev) @@ -562,7 +571,9 @@ static int gpio_fan_suspend(struct device *dev) if (fan_data->gpios) { fan_data->resume_speed = fan_data->speed_index; + mutex_lock(&fan_data->lock); set_fan_speed(fan_data, 0); + mutex_unlock(&fan_data->lock); } return 0; @@ -572,8 +583,11 @@ static int gpio_fan_resume(struct device *dev) { struct gpio_fan_data *fan_data = dev_get_drvdata(dev); - if (fan_data->gpios) + if (fan_data->gpios) { + mutex_lock(&fan_data->lock); set_fan_speed(fan_data, fan_data->resume_speed); + mutex_unlock(&fan_data->lock); + } return 0; } -- 2.51.0 From 0ea627381eb527a0ebd262c690c3992085b87ff4 Mon Sep 17 00:00:00 2001 From: Huisong Li Date: Thu, 20 Feb 2025 11:08:32 +0800 Subject: [PATCH 05/16] hwmon: (acpi_power_meter) Fix the fake power alarm reporting We encountered a problem that a fake power alarm is reported to user on the platform unsupported notifications at the second step below: 1> Query 'power1_alarm' attribute when the power capping occurs. 2> Query 'power1_alarm' attribute when the power capping is over and the current average power is less then power cap value. The root cause is that the resource->power_alarm is set to true at the first step. And power meter use this old value to show the power alarm state instead of the current the comparison value. Signed-off-by: Huisong Li Link: https://lore.kernel.org/r/20250220030832.2976-1-lihuisong@huawei.com Signed-off-by: Guenter Roeck --- drivers/hwmon/acpi_power_meter.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c index 44afb07409a4..f05986e4f379 100644 --- a/drivers/hwmon/acpi_power_meter.c +++ b/drivers/hwmon/acpi_power_meter.c @@ -437,9 +437,13 @@ static ssize_t show_val(struct device *dev, ret = update_cap(resource); if (ret) return ret; + resource->power_alarm = resource->power > resource->cap; + val = resource->power_alarm; + } else { + val = resource->power_alarm || + resource->power > resource->cap; + resource->power_alarm = resource->power > resource->cap; } - val = resource->power_alarm || resource->power > resource->cap; - resource->power_alarm = resource->power > resource->cap; break; case 7: case 8: -- 2.51.0 From 232427772fc123942d110c18269cfbdf40edae18 Mon Sep 17 00:00:00 2001 From: Huisong Li Date: Tue, 4 Mar 2025 15:46:40 +0800 Subject: [PATCH 06/16] hwmon: Fix the missing of 'average' word in hwmon_power_attr_templates The string "power%d_interval_max" and "power%d_interval_min" in the hwmon_power_attr_templates[] are corresponding to the sysfs interface name of hwmon_power_average_interval_max and hwmon_power_average_interval_min. But the 'average' word is missing in two strings. Fortunately, there is no driver to use it yet. Signed-off-by: Huisong Li Link: https://lore.kernel.org/r/20250304074640.2770353-1-lihuisong@huawei.com Signed-off-by: Guenter Roeck --- drivers/hwmon/hwmon.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index 9703d60e9bbf..1688c210888a 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -646,8 +646,8 @@ static const char * const hwmon_power_attr_templates[] = { [hwmon_power_enable] = "power%d_enable", [hwmon_power_average] = "power%d_average", [hwmon_power_average_interval] = "power%d_average_interval", - [hwmon_power_average_interval_max] = "power%d_interval_max", - [hwmon_power_average_interval_min] = "power%d_interval_min", + [hwmon_power_average_interval_max] = "power%d_average_interval_max", + [hwmon_power_average_interval_min] = "power%d_average_interval_min", [hwmon_power_average_highest] = "power%d_average_highest", [hwmon_power_average_lowest] = "power%d_average_lowest", [hwmon_power_average_max] = "power%d_average_max", -- 2.51.0 From ee65d9e63046de1de2a5383ba6f998634c86c42c Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 5 Mar 2025 13:30:12 +0100 Subject: [PATCH 07/16] hwmon: (pt5161l) Use per-client debugfs entry The I2C core now offers a debugfs-directory per client. Use it and remove the custom handling. Signed-off-by: Wolfram Sang Link: https://lore.kernel.org/r/20250305123149.16990-2-wsa+renesas@sang-engineering.com Signed-off-by: Guenter Roeck --- drivers/hwmon/pt5161l.c | 46 ++++++++--------------------------------- 1 file changed, 9 insertions(+), 37 deletions(-) diff --git a/drivers/hwmon/pt5161l.c b/drivers/hwmon/pt5161l.c index a9f0b23f9e76..20e3cfa625f1 100644 --- a/drivers/hwmon/pt5161l.c +++ b/drivers/hwmon/pt5161l.c @@ -63,7 +63,6 @@ struct pt5161l_fw_ver { /* Each client has this additional data */ struct pt5161l_data { struct i2c_client *client; - struct dentry *debugfs; struct pt5161l_fw_ver fw_ver; struct mutex lock; /* for atomic I2C transactions */ bool init_done; @@ -72,8 +71,6 @@ struct pt5161l_data { bool mm_wide_reg_access; /* MM assisted wide register access */ }; -static struct dentry *pt5161l_debugfs_dir; - /* * Write multiple data bytes to Aries over I2C */ @@ -568,21 +565,16 @@ static const struct file_operations pt5161l_debugfs_ops_hb_sts = { .open = simple_open, }; -static int pt5161l_init_debugfs(struct pt5161l_data *data) +static void pt5161l_init_debugfs(struct i2c_client *client, struct pt5161l_data *data) { - data->debugfs = debugfs_create_dir(dev_name(&data->client->dev), - pt5161l_debugfs_dir); - - debugfs_create_file("fw_ver", 0444, data->debugfs, data, + debugfs_create_file("fw_ver", 0444, client->debugfs, data, &pt5161l_debugfs_ops_fw_ver); - debugfs_create_file("fw_load_status", 0444, data->debugfs, data, + debugfs_create_file("fw_load_status", 0444, client->debugfs, data, &pt5161l_debugfs_ops_fw_load_sts); - debugfs_create_file("heartbeat_status", 0444, data->debugfs, data, + debugfs_create_file("heartbeat_status", 0444, client->debugfs, data, &pt5161l_debugfs_ops_hb_sts); - - return 0; } static int pt5161l_probe(struct i2c_client *client) @@ -604,17 +596,12 @@ static int pt5161l_probe(struct i2c_client *client) data, &pt5161l_chip_info, NULL); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); - pt5161l_init_debugfs(data); - - return PTR_ERR_OR_ZERO(hwmon_dev); -} - -static void pt5161l_remove(struct i2c_client *client) -{ - struct pt5161l_data *data = i2c_get_clientdata(client); + pt5161l_init_debugfs(client, data); - debugfs_remove_recursive(data->debugfs); + return 0; } static const struct of_device_id __maybe_unused pt5161l_of_match[] = { @@ -643,24 +630,9 @@ static struct i2c_driver pt5161l_driver = { .acpi_match_table = ACPI_PTR(pt5161l_acpi_match), }, .probe = pt5161l_probe, - .remove = pt5161l_remove, .id_table = pt5161l_id, }; - -static int __init pt5161l_init(void) -{ - pt5161l_debugfs_dir = debugfs_create_dir("pt5161l", NULL); - return i2c_add_driver(&pt5161l_driver); -} - -static void __exit pt5161l_exit(void) -{ - i2c_del_driver(&pt5161l_driver); - debugfs_remove_recursive(pt5161l_debugfs_dir); -} - -module_init(pt5161l_init); -module_exit(pt5161l_exit); +module_i2c_driver(pt5161l_driver); MODULE_AUTHOR("Cosmo Chou "); MODULE_DESCRIPTION("Hwmon driver for Astera Labs Aries PCIe retimer"); -- 2.51.0 From 7953605976f55acd4f297d310e6b0b2944f4bdc8 Mon Sep 17 00:00:00 2001 From: Maud Spierings Date: Fri, 7 Mar 2025 09:10:43 +0100 Subject: [PATCH 08/16] hwmon: (ntc_thermistor) return error instead of clipping on OOB When the ntc is reading Out Of Bounds instead of clipping to the nearest limit (min/max) return -ENODATA. This prevents malfunctioning sensors from sending a device into a shutdown loop due to a critical trip. This implementation will only work for ntc type thermistors if a ptc type is to be implemented the min/max ohm calculation must be adjusted to take that into account. Signed-off-by: Maud Spierings Link: https://lore.kernel.org/r/20250307-ntc_oob-v2-1-bba2d32b1a8e@gocontroll.com Signed-off-by: Guenter Roeck --- drivers/hwmon/ntc_thermistor.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c index b5352900463f..7c759669b26f 100644 --- a/drivers/hwmon/ntc_thermistor.c +++ b/drivers/hwmon/ntc_thermistor.c @@ -387,12 +387,9 @@ static int get_ohm_of_thermistor(struct ntc_data *data, unsigned int uv) puo = data->pullup_ohm; pdo = data->pulldown_ohm; - if (uv == 0) - return (data->connect == NTC_CONNECTED_POSITIVE) ? - INT_MAX : 0; - if (uv >= puv) - return (data->connect == NTC_CONNECTED_POSITIVE) ? - 0 : INT_MAX; + /* faulty adc value */ + if (uv == 0 || uv >= puv) + return -ENODATA; if (data->connect == NTC_CONNECTED_POSITIVE && puo == 0) n = div_u64(pdo * (puv - uv), uv); @@ -404,8 +401,10 @@ static int get_ohm_of_thermistor(struct ntc_data *data, unsigned int uv) else n = div64_u64_safe(pdo * puo * uv, pdo * (puv - uv) - puo * uv); - if (n > INT_MAX) - n = INT_MAX; + /* sensor out of bounds */ + if (n > data->comp[0].ohm || n < data->comp[data->n_comp - 1].ohm) + return -ENODATA; + return n; } -- 2.51.0 From dbcfcb239b3b452ef8782842c36fb17dd1b9092f Mon Sep 17 00:00:00 2001 From: Kurt Borja Date: Tue, 4 Mar 2025 00:52:50 -0500 Subject: [PATCH 09/16] hwmon: (dell-smm) Increment the number of fans Some Alienware laptops that support the SMM interface, may have up to 4 fans. Tested on an Alienware x15 r1. Signed-off-by: Kurt Borja Link: https://lore.kernel.org/r/20250304055249.51940-2-kuurtb@gmail.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/dell-smm-hwmon.rst | 14 +++++++------- drivers/hwmon/dell-smm-hwmon.c | 5 ++++- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/Documentation/hwmon/dell-smm-hwmon.rst b/Documentation/hwmon/dell-smm-hwmon.rst index 74905675d71f..5a4edb6565cf 100644 --- a/Documentation/hwmon/dell-smm-hwmon.rst +++ b/Documentation/hwmon/dell-smm-hwmon.rst @@ -32,12 +32,12 @@ Temperature sensors and fans can be queried and set via the standard =============================== ======= ======================================= Name Perm Description =============================== ======= ======================================= -fan[1-3]_input RO Fan speed in RPM. -fan[1-3]_label RO Fan label. -fan[1-3]_min RO Minimal Fan speed in RPM -fan[1-3]_max RO Maximal Fan speed in RPM -fan[1-3]_target RO Expected Fan speed in RPM -pwm[1-3] RW Control the fan PWM duty-cycle. +fan[1-4]_input RO Fan speed in RPM. +fan[1-4]_label RO Fan label. +fan[1-4]_min RO Minimal Fan speed in RPM +fan[1-4]_max RO Maximal Fan speed in RPM +fan[1-4]_target RO Expected Fan speed in RPM +pwm[1-4] RW Control the fan PWM duty-cycle. pwm1_enable WO Enable or disable automatic BIOS fan control (not supported on all laptops, see below for details). @@ -93,7 +93,7 @@ Again, when you find new codes, we'd be happy to have your patches! --------------------------- The driver also exports the fans as thermal cooling devices with -``type`` set to ``dell-smm-fan[1-3]``. This allows for easy fan control +``type`` set to ``dell-smm-fan[1-4]``. This allows for easy fan control using one of the thermal governors. Module parameters diff --git a/drivers/hwmon/dell-smm-hwmon.c b/drivers/hwmon/dell-smm-hwmon.c index cd00adaad1b4..79e5606e6d2f 100644 --- a/drivers/hwmon/dell-smm-hwmon.c +++ b/drivers/hwmon/dell-smm-hwmon.c @@ -73,7 +73,7 @@ #define DELL_SMM_LEGACY_EXECUTE 0x1 #define DELL_SMM_NO_TEMP 10 -#define DELL_SMM_NO_FANS 3 +#define DELL_SMM_NO_FANS 4 struct smm_regs { unsigned int eax; @@ -1074,11 +1074,14 @@ static const struct hwmon_channel_info * const dell_smm_info[] = { HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MIN | HWMON_F_MAX | HWMON_F_TARGET, HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MIN | HWMON_F_MAX | + HWMON_F_TARGET, + HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MIN | HWMON_F_MAX | HWMON_F_TARGET ), HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT | HWMON_PWM_ENABLE, HWMON_PWM_INPUT, + HWMON_PWM_INPUT, HWMON_PWM_INPUT ), NULL -- 2.51.0 From 80d363e1b3b6665dc80ca45e72b97d4dd0dcae50 Mon Sep 17 00:00:00 2001 From: Kim Seer Paller Date: Mon, 17 Mar 2025 13:02:25 +0800 Subject: [PATCH 10/16] hwmon: (pmbus/ltc2978) Add support for LT717x - docs Add LT7170 and LT7171 to compatible devices of LTC2978. Co-developed-by: Cherrence Sarip Signed-off-by: Cherrence Sarip Signed-off-by: Kim Seer Paller Link: https://lore.kernel.org/r/20250317-hwmon-next-v1-1-da0218c38197@analog.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/ltc2978.rst | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/Documentation/hwmon/ltc2978.rst b/Documentation/hwmon/ltc2978.rst index feae53eb9fbf..0b72d566cac7 100644 --- a/Documentation/hwmon/ltc2978.rst +++ b/Documentation/hwmon/ltc2978.rst @@ -5,6 +5,22 @@ Kernel driver ltc2978 Supported chips: + * Analog Devices LT7170 + + Prefix: 'lt7170' + + Addresses scanned: - + + Datasheet: https://www.analog.com/en/products/lt7170.html + + * Analog Devices LT7171 + + Prefix: 'lt7171' + + Addresses scanned: - + + Datasheet: https://www.analog.com/en/products/lt7171.html + * Linear Technology LTC2972 Prefix: 'ltc2972' @@ -223,6 +239,8 @@ Author: Guenter Roeck Description ----------- +- LT7170 and LT7171 are 20 A, 16 V, single- or dual-phase Silent Switcher +- step-down regulators with Digital Power System Management. - LTC2974 and LTC2975 are quad digital power supply managers. - LTC2978 is an octal power supply monitor. - LTC2977 is a pin compatible replacement for LTC2978. @@ -300,6 +318,7 @@ in1_reset_history Reset input voltage history. in[N]_label "vout[1-8]". + - LT7170, LT7171: N=2 - LTC2972: N=2-3 - LTC2974, LTC2975: N=2-5 - LTC2977, LTC2979, LTC2980, LTM2987: N=2-9 @@ -338,6 +357,8 @@ in[N]_reset_history Reset output voltage history. temp[N]_input Measured temperature. + - On LT7170 and LT7171, temp1 reports the chip + temperature. - On LTC2972, temp[1-2] report external temperatures, and temp 3 reports the chip temperature. - On LTC2974 and LTC2975, temp[1-4] report external @@ -411,9 +432,9 @@ power[N]_input Measured output power. curr1_label "iin". - LTC3880, LTC3883, LTC3884, LTC3886, LTC3887, LTC3889, - LTM4644, LTM4675, LTM4676, LTM4677, LTM4678, LTM4680, - and LTM4700 only. + LT7170, LT7171, LTC3880, LTC3883, LTC3884, LTC3886, + LTC3887, LTC3889, LTM4644, LTM4675, LTM4676, LTM4677, + LTM4678, LTM4680, and LTM4700 only. curr1_input Measured input current. @@ -431,6 +452,7 @@ curr1_reset_history Reset input current history. curr[N]_label "iout[1-4]". + - LT7170, LT7171: N=1 - LTC2972: N-1-2 - LTC2974, LTC2975: N=1-4 - LTC2977, LTC2979, LTC2980, LTM2987: not supported -- 2.51.0 From 156c6ebbab10e1eecc78403029a69d60dd8073bf Mon Sep 17 00:00:00 2001 From: Kim Seer Paller Date: Mon, 17 Mar 2025 13:02:26 +0800 Subject: [PATCH 11/16] dt-bindings: hwmon: ltc2978: add support for LT717x Add LTC7170 and LT7171 to supported devices of LTC2978. It has similar set of registers to LTC3887, differing only in number of channels and some PMBUS status and functionalities. Co-developed-by: Cherrence Sarip Signed-off-by: Cherrence Sarip Signed-off-by: Kim Seer Paller Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20250317-hwmon-next-v1-2-da0218c38197@analog.com Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/hwmon/lltc,ltc2978.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/hwmon/lltc,ltc2978.yaml b/Documentation/devicetree/bindings/hwmon/lltc,ltc2978.yaml index eeb6a4fe80b2..aa801ef1640b 100644 --- a/Documentation/devicetree/bindings/hwmon/lltc,ltc2978.yaml +++ b/Documentation/devicetree/bindings/hwmon/lltc,ltc2978.yaml @@ -12,6 +12,8 @@ maintainers: properties: compatible: enum: + - lltc,lt7170 + - lltc,lt7171 - lltc,ltc2972 - lltc,ltc2974 - lltc,ltc2975 @@ -47,6 +49,7 @@ properties: description: | list of regulators provided by this controller. Valid names of regulators depend on number of supplies supported per device: + * lt7170, lt7171 : vout0 * ltc2972 vout0 - vout1 * ltc2974, ltc2975 : vout0 - vout3 * ltc2977, ltc2979, ltc2980, ltm2987 : vout0 - vout7 -- 2.51.0 From c1d6afdbb8ed0ef9291c21214e0a11cc91f65c7f Mon Sep 17 00:00:00 2001 From: Kim Seer Paller Date: Mon, 17 Mar 2025 13:02:27 +0800 Subject: [PATCH 12/16] hwmon: (pmbus/ltc2978) add support for lt717x Add support for LT7170 and LT7171. The LT7170 and LT7171 are 20 A, 16 V, Single- or Dual-Phase, Silent Switcher Step-Down Regulators with Digital Power System Management. The relevant registers in the LT7170 and LT7171 are similar to those in the LTC3887, but with fewer channels. This adds the chip ID and identification of ASCII to differentiate between the LT7170 and LT7171. These devices support polling for status updates and clearing peak values. The data format for voltage, current, and temperature is set to IEEE754 for precision and compatibility. Co-developed-by: Cherrence Sarip Signed-off-by: Cherrence Sarip Signed-off-by: Kim Seer Paller Link: https://lore.kernel.org/r/20250317-hwmon-next-v1-3-da0218c38197@analog.com Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/Kconfig | 6 ++--- drivers/hwmon/pmbus/ltc2978.c | 44 ++++++++++++++++++++++++++++++++--- 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 675b0d4703d8..6ce68dd33369 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -233,9 +233,9 @@ config SENSORS_LTC2978_REGULATOR depends on SENSORS_LTC2978 && REGULATOR help If you say yes here you get regulator support for Linear Technology - LTC3880, LTC3883, LTC3884, LTC3886, LTC3887, LTC3889, LTC7841, - LTC7880, LTM4644, LTM4673, LTM4675, LTM4676, LTM4677, LTM4678, - LTM4680, LTM4686, and LTM4700. + LT7170, LT7171, LTC3880, LTC3883, LTC3884, LTC3886, LTC3887, LTC3889, + LTC7841, LTC7880, LTM4644, LTM4673, LTM4675, LTM4676, LTM4677, + LTM4678, LTM4680, LTM4686, and LTM4700. config SENSORS_LTC3815 tristate "Linear Technologies LTC3815" diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c index 658cb1173291..8f5be520a15d 100644 --- a/drivers/hwmon/pmbus/ltc2978.c +++ b/drivers/hwmon/pmbus/ltc2978.c @@ -23,8 +23,8 @@ enum chips { /* Managers */ ltc2972, ltc2974, ltc2975, ltc2977, ltc2978, ltc2979, ltc2980, /* Controllers */ - ltc3880, ltc3882, ltc3883, ltc3884, ltc3886, ltc3887, ltc3889, ltc7132, - ltc7841, ltc7880, + lt7170, lt7171, ltc3880, ltc3882, ltc3883, ltc3884, ltc3886, ltc3887, + ltc3889, ltc7132, ltc7841, ltc7880, /* Modules */ ltm2987, ltm4664, ltm4673, ltm4675, ltm4676, ltm4677, ltm4678, ltm4680, ltm4686, ltm4700, @@ -62,6 +62,7 @@ enum chips { #define LTC2978_ID_MASK 0xfff0 +#define LT7170_ID 0x1C10 #define LTC2972_ID 0x0310 #define LTC2974_ID 0x0210 #define LTC2975_ID 0x0220 @@ -537,6 +538,8 @@ static int ltc2978_write_word_data(struct i2c_client *client, int page, } static const struct i2c_device_id ltc2978_id[] = { + {"lt7170", lt7170}, + {"lt7171", lt7171}, {"ltc2972", ltc2972}, {"ltc2974", ltc2974}, {"ltc2975", ltc2975}, @@ -615,7 +618,7 @@ static int ltc2978_get_id(struct i2c_client *client) ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf); if (ret < 0) return ret; - if (ret < 3 || strncmp(buf, "LTC", 3)) + if (ret < 3 || (strncmp(buf, "LTC", 3) && strncmp(buf, "ADI", 3))) return -ENODEV; ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf); @@ -630,6 +633,25 @@ static int ltc2978_get_id(struct i2c_client *client) chip_id &= LTC2978_ID_MASK; + if (chip_id == LT7170_ID) { + u8 buf[I2C_SMBUS_BLOCK_MAX]; + int ret; + + ret = i2c_smbus_read_i2c_block_data(client, PMBUS_IC_DEVICE_ID, + sizeof(buf), buf); + if (ret < 0) + return ret; + + if (!strncmp(buf + 1, "LT7170", 6) || + !strncmp(buf + 1, "LT7170-1", 8)) + return lt7170; + if (!strncmp(buf + 1, "LT7171", 6) || + !strncmp(buf + 1, "LT7171-1", 8)) + return lt7171; + + return -ENODEV; + } + if (chip_id == LTC2972_ID) return ltc2972; else if (chip_id == LTC2974_ID) @@ -741,6 +763,20 @@ static int ltc2978_probe(struct i2c_client *client) data->temp2_max = 0x7c00; switch (data->id) { + case lt7170: + case lt7171: + data->features |= FEAT_CLEAR_PEAKS | FEAT_NEEDS_POLLING; + info->read_word_data = ltc3883_read_word_data; + info->pages = LTC3883_NUM_PAGES; + info->format[PSC_VOLTAGE_IN] = ieee754; + info->format[PSC_VOLTAGE_OUT] = ieee754; + info->format[PSC_CURRENT_OUT] = ieee754; + info->format[PSC_TEMPERATURE] = ieee754; + info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; + break; case ltc2972: info->read_word_data = ltc2975_read_word_data; info->pages = LTC2972_NUM_PAGES; @@ -927,6 +963,8 @@ static int ltc2978_probe(struct i2c_client *client) #ifdef CONFIG_OF static const struct of_device_id ltc2978_of_match[] = { + { .compatible = "lltc,lt7170" }, + { .compatible = "lltc,lt7171" }, { .compatible = "lltc,ltc2972" }, { .compatible = "lltc,ltc2974" }, { .compatible = "lltc,ltc2975" }, -- 2.51.0 From 08ebc9def79fc0c4dbb6ecc39263006e3f98b750 Mon Sep 17 00:00:00 2001 From: Thomas Richard Date: Mon, 3 Feb 2025 12:01:05 +0100 Subject: [PATCH 13/16] hwmon: Add Congatec Board Controller monitoring driver Add support for the Congatec Board Controller. This controller exposes temperature, voltage, current and fan sensors. The available sensors list cannot be predicted. Some sensors can be present or not, depending the system. The driver has an internal list of all possible sensors, for all Congatec boards. The Board Controller gives to the driver its sensors list, and their status (active or not). Signed-off-by: Thomas Richard Link: https://lore.kernel.org/r/20250203-congatec-board-controller-hwmon-v4-1-ff6c76a4662c@bootlin.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/cgbc-hwmon.rst | 63 ++++++ Documentation/hwmon/index.rst | 1 + MAINTAINERS | 1 + drivers/hwmon/Kconfig | 10 + drivers/hwmon/Makefile | 1 + drivers/hwmon/cgbc-hwmon.c | 304 +++++++++++++++++++++++++++++ 6 files changed, 380 insertions(+) create mode 100644 Documentation/hwmon/cgbc-hwmon.rst create mode 100644 drivers/hwmon/cgbc-hwmon.c diff --git a/Documentation/hwmon/cgbc-hwmon.rst b/Documentation/hwmon/cgbc-hwmon.rst new file mode 100644 index 000000000000..3a5e6e6e8639 --- /dev/null +++ b/Documentation/hwmon/cgbc-hwmon.rst @@ -0,0 +1,63 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Kernel driver cgbc-hwmon +======================== + +Supported chips: + + * Congatec Board Controller. + + Prefix: 'cgbc-hwmon' + +Author: Thomas Richard + +Description +----------- + +This driver enables monitoring support for the Congatec Board Controller. +This controller is embedded on the x86 SoMs of Congatec. + +Sysfs entries +------------- + +The following sysfs entries list contains all sensors defined in the Board +Controller. The available sensors in sysfs depend on the SoM and the +system. + +============= ====================== +Name Description +============= ====================== +temp1_input CPU temperature +temp2_input Box temperature +temp3_input Ambient temperature +temp4_input Board temperature +temp5_input Carrier temperature +temp6_input Chipset temperature +temp7_input Video temperature +temp8_input Other temperature +temp9_input TOPDIM temperature +temp10_input BOTTOMDIM temperature +in0_input CPU voltage +in1_input DC Runtime voltage +in2_input DC Standby voltage +in3_input CMOS Battery voltage +in4_input Battery voltage +in5_input AC voltage +in6_input Other voltage +in7_input 5V voltage +in8_input 5V Standby voltage +in9_input 3V3 voltage +in10_input 3V3 Standby voltage +in11_input VCore A voltage +in12_input VCore B voltage +in13_input 12V voltage +curr1_input DC current +curr2_input 5V current +curr3_input 12V current +fan1_input CPU fan +fan2_input Box fan +fan3_input Ambient fan +fan4_input Chiptset fan +fan5_input Video fan +fan6_input Other fan +============= ====================== diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index 874f8fd26325..ef86489bb47c 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -53,6 +53,7 @@ Hardware Monitoring Kernel Drivers bel-pfe bpa-rs600 bt1-pvt + cgbc-hwmon chipcap2 coretemp corsair-cpro diff --git a/MAINTAINERS b/MAINTAINERS index 896a307fa065..c85ce9deb397 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5874,6 +5874,7 @@ CONGATEC BOARD CONTROLLER MFD DRIVER M: Thomas Richard S: Maintained F: drivers/gpio/gpio-cgbc.c +F: drivers/hwmon/cgbc-hwmon.c F: drivers/i2c/busses/i2c-cgbc.c F: drivers/mfd/cgbc-core.c F: drivers/watchdog/cgbc_wdt.c diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 56494ab85b83..76a55ad70bd6 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -463,6 +463,16 @@ config SENSORS_BT1_PVT_ALARMS the data conversion will be periodically performed and the data will be saved in the internal driver cache. +config SENSORS_CGBC + tristate "Congatec Board Controller Sensors" + depends on MFD_CGBC + help + Enable sensors support for the Congatec Board Controller. It has + temperature, voltage, current and fan sensors. + + This driver can also be built as a module. If so, the module will be + called cgbc-hwmon. + config SENSORS_CHIPCAP2 tristate "Amphenol ChipCap 2 relative humidity and temperature sensor" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index b7ef0f0562d3..8af5fdbd5dff 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -59,6 +59,7 @@ obj-$(CONFIG_SENSORS_ASUS_ROG_RYUJIN) += asus_rog_ryujin.o obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o obj-$(CONFIG_SENSORS_AXI_FAN_CONTROL) += axi-fan-control.o obj-$(CONFIG_SENSORS_BT1_PVT) += bt1-pvt.o +obj-$(CONFIG_SENSORS_CGBC) += cgbc-hwmon.o obj-$(CONFIG_SENSORS_CHIPCAP2) += chipcap2.o obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o obj-$(CONFIG_SENSORS_CORSAIR_CPRO) += corsair-cpro.o diff --git a/drivers/hwmon/cgbc-hwmon.c b/drivers/hwmon/cgbc-hwmon.c new file mode 100644 index 000000000000..772f44d56ccf --- /dev/null +++ b/drivers/hwmon/cgbc-hwmon.c @@ -0,0 +1,304 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * cgbc-hwmon - Congatec Board Controller hardware monitoring driver + * + * Copyright (C) 2024 Thomas Richard + */ + +#include +#include +#include +#include +#include +#include + +#define CGBC_HWMON_CMD_SENSOR 0x77 +#define CGBC_HWMON_CMD_SENSOR_DATA_SIZE 0x05 + +#define CGBC_HWMON_TYPE_MASK GENMASK(6, 5) +#define CGBC_HWMON_ID_MASK GENMASK(4, 0) +#define CGBC_HWMON_ACTIVE_BIT BIT(7) + +struct cgbc_hwmon_sensor { + enum hwmon_sensor_types type; + bool active; + unsigned int index; + unsigned int channel; + const char *label; +}; + +struct cgbc_hwmon_data { + struct cgbc_device_data *cgbc; + unsigned int nb_sensors; + struct cgbc_hwmon_sensor *sensors; +}; + +enum cgbc_sensor_types { + CGBC_HWMON_TYPE_TEMP = 1, + CGBC_HWMON_TYPE_IN, + CGBC_HWMON_TYPE_FAN +}; + +static const char * const cgbc_hwmon_labels_temp[] = { + "CPU Temperature", + "Box Temperature", + "Ambient Temperature", + "Board Temperature", + "Carrier Temperature", + "Chipset Temperature", + "Video Temperature", + "Other Temperature", + "TOPDIM Temperature", + "BOTTOMDIM Temperature", +}; + +static const struct { + enum hwmon_sensor_types type; + const char *label; +} cgbc_hwmon_labels_in[] = { + { hwmon_in, "CPU Voltage" }, + { hwmon_in, "DC Runtime Voltage" }, + { hwmon_in, "DC Standby Voltage" }, + { hwmon_in, "CMOS Battery Voltage" }, + { hwmon_in, "Battery Voltage" }, + { hwmon_in, "AC Voltage" }, + { hwmon_in, "Other Voltage" }, + { hwmon_in, "5V Voltage" }, + { hwmon_in, "5V Standby Voltage" }, + { hwmon_in, "3V3 Voltage" }, + { hwmon_in, "3V3 Standby Voltage" }, + { hwmon_in, "VCore A Voltage" }, + { hwmon_in, "VCore B Voltage" }, + { hwmon_in, "12V Voltage" }, + { hwmon_curr, "DC Current" }, + { hwmon_curr, "5V Current" }, + { hwmon_curr, "12V Current" }, +}; + +#define CGBC_HWMON_NB_IN_SENSORS 14 + +static const char * const cgbc_hwmon_labels_fan[] = { + "CPU Fan", + "Box Fan", + "Ambient Fan", + "Chipset Fan", + "Video Fan", + "Other Fan", +}; + +static int cgbc_hwmon_cmd(struct cgbc_device_data *cgbc, u8 index, u8 *data) +{ + u8 cmd[2] = {CGBC_HWMON_CMD_SENSOR, index}; + + return cgbc_command(cgbc, cmd, sizeof(cmd), data, CGBC_HWMON_CMD_SENSOR_DATA_SIZE, NULL); +} + +static int cgbc_hwmon_probe_sensors(struct device *dev, struct cgbc_hwmon_data *hwmon) +{ + struct cgbc_device_data *cgbc = hwmon->cgbc; + struct cgbc_hwmon_sensor *sensor = hwmon->sensors; + u8 data[CGBC_HWMON_CMD_SENSOR_DATA_SIZE], nb_sensors, i; + int ret; + + ret = cgbc_hwmon_cmd(cgbc, 0, &data[0]); + if (ret) + return ret; + + nb_sensors = data[0]; + + hwmon->sensors = devm_kzalloc(dev, sizeof(*hwmon->sensors) * nb_sensors, GFP_KERNEL); + sensor = hwmon->sensors; + + for (i = 0; i < nb_sensors; i++) { + enum cgbc_sensor_types type; + unsigned int channel; + + /* + * No need to request data for the first sensor. + * We got data for the first sensor when we ask the number of sensors to the Board + * Controller. + */ + if (i) { + ret = cgbc_hwmon_cmd(cgbc, i, &data[0]); + if (ret) + return ret; + } + + type = FIELD_GET(CGBC_HWMON_TYPE_MASK, data[1]); + channel = FIELD_GET(CGBC_HWMON_ID_MASK, data[1]) - 1; + + if (type == CGBC_HWMON_TYPE_TEMP && channel < ARRAY_SIZE(cgbc_hwmon_labels_temp)) { + sensor->type = hwmon_temp; + sensor->label = cgbc_hwmon_labels_temp[channel]; + } else if (type == CGBC_HWMON_TYPE_IN && + channel < ARRAY_SIZE(cgbc_hwmon_labels_in)) { + /* + * The Board Controller doesn't differentiate current and voltage sensors. + * Get the sensor type from cgbc_hwmon_labels_in[channel].type instead. + */ + sensor->type = cgbc_hwmon_labels_in[channel].type; + sensor->label = cgbc_hwmon_labels_in[channel].label; + } else if (type == CGBC_HWMON_TYPE_FAN && + channel < ARRAY_SIZE(cgbc_hwmon_labels_fan)) { + sensor->type = hwmon_fan; + sensor->label = cgbc_hwmon_labels_fan[channel]; + } else { + dev_warn(dev, "Board Controller returned an unknown sensor (type=%d, channel=%d), ignore it", + type, channel); + continue; + } + + sensor->active = FIELD_GET(CGBC_HWMON_ACTIVE_BIT, data[1]); + sensor->channel = channel; + sensor->index = i; + sensor++; + hwmon->nb_sensors++; + } + + return 0; +} + +static struct cgbc_hwmon_sensor *cgbc_hwmon_find_sensor(struct cgbc_hwmon_data *hwmon, + enum hwmon_sensor_types type, int channel) +{ + struct cgbc_hwmon_sensor *sensor = NULL; + int i; + + /* + * The Board Controller doesn't differentiate current and voltage sensors. + * The channel value (from the Board Controller point of view) shall be computed for current + * sensors. + */ + if (type == hwmon_curr) + channel += CGBC_HWMON_NB_IN_SENSORS; + + for (i = 0; i < hwmon->nb_sensors; i++) { + if (hwmon->sensors[i].type == type && hwmon->sensors[i].channel == channel) { + sensor = &hwmon->sensors[i]; + break; + } + } + + return sensor; +} + +static int cgbc_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, + long *val) +{ + struct cgbc_hwmon_data *hwmon = dev_get_drvdata(dev); + struct cgbc_hwmon_sensor *sensor = cgbc_hwmon_find_sensor(hwmon, type, channel); + struct cgbc_device_data *cgbc = hwmon->cgbc; + u8 data[CGBC_HWMON_CMD_SENSOR_DATA_SIZE]; + int ret; + + ret = cgbc_hwmon_cmd(cgbc, sensor->index, &data[0]); + if (ret) + return ret; + + *val = (data[3] << 8) | data[2]; + + /* + * For the Board Controller 1lsb = 0.1 degree centigrade. + * Other units are as expected. + */ + if (sensor->type == hwmon_temp) + *val *= 100; + + return 0; +} + +static umode_t cgbc_hwmon_is_visible(const void *_data, enum hwmon_sensor_types type, u32 attr, + int channel) +{ + struct cgbc_hwmon_data *data = (struct cgbc_hwmon_data *)_data; + struct cgbc_hwmon_sensor *sensor; + + sensor = cgbc_hwmon_find_sensor(data, type, channel); + if (!sensor) + return 0; + + return sensor->active ? 0444 : 0; +} + +static int cgbc_hwmon_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr, + int channel, const char **str) +{ + struct cgbc_hwmon_data *hwmon = dev_get_drvdata(dev); + struct cgbc_hwmon_sensor *sensor = cgbc_hwmon_find_sensor(hwmon, type, channel); + + *str = sensor->label; + + return 0; +} + +static const struct hwmon_channel_info * const cgbc_hwmon_info[] = { + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL, + HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL), + HWMON_CHANNEL_INFO(in, + HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL), + HWMON_CHANNEL_INFO(curr, + HWMON_C_INPUT | HWMON_C_LABEL, HWMON_C_INPUT | HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_LABEL), + HWMON_CHANNEL_INFO(fan, + HWMON_F_INPUT | HWMON_F_LABEL, HWMON_F_INPUT | HWMON_F_LABEL, + HWMON_F_INPUT | HWMON_F_LABEL, HWMON_F_INPUT | HWMON_F_LABEL, + HWMON_F_INPUT | HWMON_F_LABEL, HWMON_F_INPUT | HWMON_F_LABEL), + NULL +}; + +static const struct hwmon_ops cgbc_hwmon_ops = { + .is_visible = cgbc_hwmon_is_visible, + .read = cgbc_hwmon_read, + .read_string = cgbc_hwmon_read_string, +}; + +static const struct hwmon_chip_info cgbc_chip_info = { + .ops = &cgbc_hwmon_ops, + .info = cgbc_hwmon_info, +}; + +static int cgbc_hwmon_probe(struct platform_device *pdev) +{ + struct cgbc_device_data *cgbc = dev_get_drvdata(pdev->dev.parent); + struct device *dev = &pdev->dev; + struct cgbc_hwmon_data *data; + struct device *hwmon_dev; + int ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->cgbc = cgbc; + + ret = cgbc_hwmon_probe_sensors(dev, data); + if (ret) + return dev_err_probe(dev, ret, "Failed to probe sensors"); + + hwmon_dev = devm_hwmon_device_register_with_info(dev, "cgbc_hwmon", data, &cgbc_chip_info, + NULL); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static struct platform_driver cgbc_hwmon_driver = { + .driver = { + .name = "cgbc-hwmon", + }, + .probe = cgbc_hwmon_probe, +}; + +module_platform_driver(cgbc_hwmon_driver); + +MODULE_AUTHOR("Thomas Richard "); +MODULE_DESCRIPTION("Congatec Board Controller Hardware Monitoring Driver"); +MODULE_LICENSE("GPL"); -- 2.51.0 From 52ffdbbd49b8ea759b66fd4d3f66853ed2b1613d Mon Sep 17 00:00:00 2001 From: Leo Yang Date: Thu, 16 Jan 2025 16:59:40 +0800 Subject: [PATCH 14/16] dt-bindings: hwmon: ti,ina2xx: Add INA233 device Add TI INA233 Current and Power Monitor bindings. Signed-off-by: Leo Yang Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20250116085939.1235598-2-leo.yang.sy0@gmail.com Signed-off-by: Guenter Roeck --- .../devicetree/bindings/hwmon/ti,ina2xx.yaml | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml b/Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml index 05a9cb36cd82..7372e282765b 100644 --- a/Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml +++ b/Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml @@ -27,6 +27,7 @@ properties: - ti,ina226 - ti,ina230 - ti,ina231 + - ti,ina233 - ti,ina237 - ti,ina238 - ti,ina260 @@ -75,12 +76,41 @@ properties: the alert polarity to active-high. $ref: /schemas/types.yaml#/definitions/flag + ti,maximum-expected-current-microamp: + description: | + This value indicates the maximum current in microamps that you can + expect to measure with ina233 in your circuit. + + This value will be used to calculate the Current_LSB and current/power + coefficient for the pmbus and to calibrate the IC. + minimum: 32768 + maximum: 4294967295 + default: 32768000 + required: - compatible - reg allOf: - $ref: hwmon-common.yaml# + - if: + properties: + compatible: + contains: + enum: + - silergy,sy24655 + - ti,ina209 + - ti,ina219 + - ti,ina220 + - ti,ina226 + - ti,ina230 + - ti,ina231 + - ti,ina237 + - ti,ina238 + - ti,ina260 + then: + properties: + ti,maximum-expected-current-microamp: false unevaluatedProperties: false -- 2.51.0 From b64b6cb163f16425c3c4fab077963bf6a67f45c7 Mon Sep 17 00:00:00 2001 From: Leo Yang Date: Thu, 16 Jan 2025 16:59:42 +0800 Subject: [PATCH 15/16] hwmon: Add driver for TI INA233 Current and Power Monitor Driver for Texas Instruments INA233 Current and Power Monitor With I2C-, SMBus-, and PMBus-Compatible Interface Signed-off-by: Leo Yang Link: https://lore.kernel.org/r/20250116085939.1235598-3-leo.yang.sy0@gmail.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/ina233.rst | 75 +++++++++++++ Documentation/hwmon/index.rst | 1 + MAINTAINERS | 7 ++ drivers/hwmon/pmbus/Kconfig | 9 ++ drivers/hwmon/pmbus/Makefile | 1 + drivers/hwmon/pmbus/ina233.c | 191 +++++++++++++++++++++++++++++++++ 6 files changed, 284 insertions(+) create mode 100644 Documentation/hwmon/ina233.rst create mode 100644 drivers/hwmon/pmbus/ina233.c diff --git a/Documentation/hwmon/ina233.rst b/Documentation/hwmon/ina233.rst new file mode 100644 index 000000000000..42323162e6db --- /dev/null +++ b/Documentation/hwmon/ina233.rst @@ -0,0 +1,75 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Kernel driver ina233 +==================== + +Supported chips: + + * TI INA233 + + Prefix: 'ina233' + + * Datasheet + + Publicly available at the TI website : https://www.ti.com/lit/ds/symlink/ina233.pdf + +Author: Leo Yang + +Usage Notes +----------- + +The shunt resistor value can be configured by a device tree property; +see Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml for details. + + +Description +----------- + +This driver supports hardware monitoring for TI INA233. + +The driver is a client driver to the core PMBus driver. Please see +Documentation/hwmon/pmbus.rst for details on PMBus client drivers. + +The driver provides the following attributes for input voltage: + +**in1_input** + +**in1_label** + +**in1_max** + +**in1_max_alarm** + +**in1_min** + +**in1_min_alarm** + +The driver provides the following attributes for shunt voltage: + +**in2_input** + +**in2_label** + +The driver provides the following attributes for output voltage: + +**in3_input** + +**in3_label** + +**in3_alarm** + +The driver provides the following attributes for output current: + +**curr1_input** + +**curr1_label** + +**curr1_max** + +**curr1_max_alarm** + +The driver provides the following attributes for input power: + +**power1_input** + +**power1_label** diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index ef86489bb47c..bd491bc37ba4 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -91,6 +91,7 @@ Hardware Monitoring Kernel Drivers ibmpowernv ina209 ina2xx + ina233 ina238 ina3221 inspur-ipsps1 diff --git a/MAINTAINERS b/MAINTAINERS index c85ce9deb397..af4e39a803f6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11331,6 +11331,13 @@ L: linux-fbdev@vger.kernel.org S: Orphan F: drivers/video/fbdev/imsttfb.c +INA233 HARDWARE MONITOR DRIVERS +M: Leo Yang +L: linux-hwmon@vger.kernel.org +S: Maintained +F: Documentation/hwmon/ina233.rst +F: drivers/hwmon/pmbus/ina233.c + INDEX OF FURTHER KERNEL DOCUMENTATION M: Carlos Bilbao S: Maintained diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 6ce68dd33369..c9b3c3149982 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -133,6 +133,15 @@ config SENSORS_DPS920AB This driver can also be built as a module. If so, the module will be called dps920ab. +config SENSORS_INA233 + tristate "Texas Instruments INA233 and compatibles" + help + If you say yes here you get hardware monitoring support for Texas + Instruments INA233. + + This driver can also be built as a module. If so, the module will + be called ina233. + config SENSORS_INSPUR_IPSPS tristate "INSPUR Power System Power Supply" help diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile index c7eb7739b7f8..56f128c4653e 100644 --- a/drivers/hwmon/pmbus/Makefile +++ b/drivers/hwmon/pmbus/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_SENSORS_DELTA_AHE50DC_FAN) += delta-ahe50dc-fan.o obj-$(CONFIG_SENSORS_FSP_3Y) += fsp-3y.o obj-$(CONFIG_SENSORS_IBM_CFFPS) += ibm-cffps.o obj-$(CONFIG_SENSORS_DPS920AB) += dps920ab.o +obj-$(CONFIG_SENSORS_INA233) += ina233.o obj-$(CONFIG_SENSORS_INSPUR_IPSPS) += inspur-ipsps.o obj-$(CONFIG_SENSORS_IR35221) += ir35221.o obj-$(CONFIG_SENSORS_IR36021) += ir36021.o diff --git a/drivers/hwmon/pmbus/ina233.c b/drivers/hwmon/pmbus/ina233.c new file mode 100644 index 000000000000..dde1e1678394 --- /dev/null +++ b/drivers/hwmon/pmbus/ina233.c @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for ina233 + * + * Copyright (c) 2025 Leo Yang + */ + +#include +#include +#include +#include +#include +#include "pmbus.h" + +#define MFR_READ_VSHUNT 0xd1 +#define MFR_CALIBRATION 0xd4 + +#define INA233_MAX_CURRENT_DEFAULT 32768000 /* uA */ +#define INA233_RSHUNT_DEFAULT 2000 /* uOhm */ + +#define MAX_M_VAL 32767 + +static void calculate_coef(int *m, int *R, u32 current_lsb, int power_coef) +{ + u64 scaled_m; + int scale_factor = 0; + int scale_coef = 1; + + /* + * 1000000 from Current_LSB A->uA . + * scale_coef is for scaling up to minimize rounding errors, + * If there is no decimal information, no need to scale. + */ + if (1000000 % current_lsb) { + /* Scaling to keep integer precision */ + scale_factor = -3; + scale_coef = 1000; + } + + /* + * Unit Conversion (Current_LSB A->uA) and use scaling(scale_factor) + * to keep integer precision. + * Formulae referenced from spec. + */ + scaled_m = div64_u64(1000000 * scale_coef, (u64)current_lsb * power_coef); + + /* Maximize while keeping it bounded.*/ + while (scaled_m > MAX_M_VAL) { + scaled_m = div_u64(scaled_m, 10); + scale_factor++; + } + /* Scale up only if fractional part exists. */ + while (scaled_m * 10 < MAX_M_VAL && scale_coef != 1) { + scaled_m *= 10; + scale_factor--; + } + + *m = scaled_m; + *R = scale_factor; +} + +static int ina233_read_word_data(struct i2c_client *client, int page, + int phase, int reg) +{ + int ret; + + switch (reg) { + case PMBUS_VIRT_READ_VMON: + ret = pmbus_read_word_data(client, 0, 0xff, MFR_READ_VSHUNT); + + /* Adjust returned value to match VIN coefficients */ + /* VIN: 1.25 mV VSHUNT: 2.5 uV LSB */ + ret = DIV_ROUND_CLOSEST(ret * 25, 12500); + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static int ina233_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + int ret, m, R; + u32 rshunt; + u32 max_current; + u32 current_lsb; + u16 calibration; + struct pmbus_driver_info *info; + + info = devm_kzalloc(dev, sizeof(struct pmbus_driver_info), + GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->pages = 1; + info->format[PSC_VOLTAGE_IN] = direct; + info->format[PSC_VOLTAGE_OUT] = direct; + info->format[PSC_CURRENT_OUT] = direct; + info->format[PSC_POWER] = direct; + info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT + | PMBUS_HAVE_POUT + | PMBUS_HAVE_VMON | PMBUS_HAVE_STATUS_VMON; + info->m[PSC_VOLTAGE_IN] = 8; + info->R[PSC_VOLTAGE_IN] = 2; + info->m[PSC_VOLTAGE_OUT] = 8; + info->R[PSC_VOLTAGE_OUT] = 2; + info->read_word_data = ina233_read_word_data; + + /* If INA233 skips current/power, shunt-resistor and current-lsb aren't needed. */ + /* read rshunt value (uOhm) */ + ret = device_property_read_u32(dev, "shunt-resistor", &rshunt); + if (ret) { + if (ret != -EINVAL) + return dev_err_probe(dev, ret, "Shunt resistor property read fail.\n"); + rshunt = INA233_RSHUNT_DEFAULT; + } + if (!rshunt) + return dev_err_probe(dev, -EINVAL, + "Shunt resistor cannot be zero.\n"); + + /* read Maximum expected current value (uA) */ + ret = device_property_read_u32(dev, "ti,maximum-expected-current-microamp", &max_current); + if (ret) { + if (ret != -EINVAL) + return dev_err_probe(dev, ret, + "Maximum expected current property read fail.\n"); + max_current = INA233_MAX_CURRENT_DEFAULT; + } + if (max_current < 32768) + return dev_err_probe(dev, -EINVAL, + "Maximum expected current cannot less then 32768.\n"); + + /* Calculate Current_LSB according to the spec formula */ + current_lsb = max_current / 32768; + + /* calculate current coefficient */ + calculate_coef(&m, &R, current_lsb, 1); + info->m[PSC_CURRENT_OUT] = m; + info->R[PSC_CURRENT_OUT] = R; + + /* calculate power coefficient */ + calculate_coef(&m, &R, current_lsb, 25); + info->m[PSC_POWER] = m; + info->R[PSC_POWER] = R; + + /* write MFR_CALIBRATION register, Apply formula from spec with unit scaling. */ + calibration = div64_u64(5120000000ULL, (u64)rshunt * current_lsb); + if (calibration > 0x7FFF) + return dev_err_probe(dev, -EINVAL, + "Product of Current_LSB %u and shunt resistor %u too small, MFR_CALIBRATION reg exceeds 0x7FFF.\n", + current_lsb, rshunt); + ret = i2c_smbus_write_word_data(client, MFR_CALIBRATION, calibration); + if (ret < 0) + return dev_err_probe(dev, ret, "Unable to write calibration.\n"); + + dev_dbg(dev, "power monitor %s (Rshunt = %u uOhm, Current_LSB = %u uA/bit)\n", + client->name, rshunt, current_lsb); + + return pmbus_do_probe(client, info); +} + +static const struct i2c_device_id ina233_id[] = { + {"ina233", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, ina233_id); + +static const struct of_device_id __maybe_unused ina233_of_match[] = { + { .compatible = "ti,ina233" }, + {} +}; +MODULE_DEVICE_TABLE(of, ina233_of_match); + +static struct i2c_driver ina233_driver = { + .driver = { + .name = "ina233", + .of_match_table = of_match_ptr(ina233_of_match), + }, + .probe = ina233_probe, + .id_table = ina233_id, +}; + +module_i2c_driver(ina233_driver); + +MODULE_AUTHOR("Leo Yang "); +MODULE_DESCRIPTION("PMBus driver for INA233 and compatible chips"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("PMBUS"); -- 2.51.0 From fb36a0b3398a791d7266150f63b60e4cf1c98f34 Mon Sep 17 00:00:00 2001 From: Andrei Lalaev Date: Mon, 17 Feb 2025 06:10:56 +0100 Subject: [PATCH 16/16] dt-bindings: hwmon: Add description for sensor HTU31 Add trivial binding for HTU31 Temperature and Humidity sensor. Signed-off-by: Andrei Lalaev Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20250217051110.46827-3-andrey.lalaev@gmail.com Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/trivial-devices.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/trivial-devices.yaml b/Documentation/devicetree/bindings/trivial-devices.yaml index fadbd3c041c8..30e8f89fa032 100644 --- a/Documentation/devicetree/bindings/trivial-devices.yaml +++ b/Documentation/devicetree/bindings/trivial-devices.yaml @@ -189,6 +189,8 @@ properties: - mcube,mc3230 # Measurement Specialities I2C temperature and humidity sensor - meas,htu21 + # Measurement Specialities I2C temperature and humidity sensor + - meas,htu31 # Measurement Specialities I2C pressure and temperature sensor - meas,ms5637 # Measurement Specialities I2C pressure and temperature sensor -- 2.51.0