From 352a21d29092d16aef6905ecb8857c3876038b15 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 29 Jan 2025 17:20:36 +0200 Subject: [PATCH 01/49] hwmon: (gsc) drop unneeded assignment for cache_type REGCACHE_NONE is the default type of the cache when not provided. Drop unneeded explicit assignment to it. Note, it's defined to 0, and if ever be redefined, it will break literally a lot of the drivers, so it very unlikely to happen. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20250129152036.1797725-1-andriy.shevchenko@linux.intel.com Signed-off-by: Guenter Roeck --- drivers/hwmon/gsc-hwmon.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/hwmon/gsc-hwmon.c b/drivers/hwmon/gsc-hwmon.c index 14a6385cd7cc..0f9af82cebec 100644 --- a/drivers/hwmon/gsc-hwmon.c +++ b/drivers/hwmon/gsc-hwmon.c @@ -47,7 +47,6 @@ static const struct regmap_bus gsc_hwmon_regmap_bus = { static const struct regmap_config gsc_hwmon_regmap_config = { .reg_bits = 8, .val_bits = 8, - .cache_type = REGCACHE_NONE, }; static ssize_t pwm_auto_point_temp_show(struct device *dev, From 8dfd7e08269ad48a8d64d647ced6878f3b2da84a Mon Sep 17 00:00:00 2001 From: Cedric Encarnacion Date: Fri, 24 Jan 2025 23:23:05 +0800 Subject: [PATCH 02/49] dt-bindings: hwmon: ltc2978: add support for ltm4673 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add LTM4673 to supported devices of LTC2978. Unlike other LTM46xx devices, LTM4673 is a μModule regulator that outputs four channels. Signed-off-by: Cedric Encarnacion Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20250124-ltm4673-v1-1-a2c6aa37c903@analog.com Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/hwmon/lltc,ltc2978.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/hwmon/lltc,ltc2978.yaml b/Documentation/devicetree/bindings/hwmon/lltc,ltc2978.yaml index 37e1dc9c7dd3..eeb6a4fe80b2 100644 --- a/Documentation/devicetree/bindings/hwmon/lltc,ltc2978.yaml +++ b/Documentation/devicetree/bindings/hwmon/lltc,ltc2978.yaml @@ -30,6 +30,7 @@ properties: - lltc,ltc7880 - lltc,ltm2987 - lltc,ltm4664 + - lltc,ltm4673 - lltc,ltm4675 - lltc,ltm4676 - lltc,ltm4677 @@ -55,6 +56,7 @@ properties: * ltc7880 : vout0 - vout1 * ltc3883 : vout0 * ltm4664 : vout0 - vout1 + * ltm4673 : vout0 - vout3 * ltm4675, ltm4676, ltm4677, ltm4678 : vout0 - vout1 * ltm4680, ltm4686 : vout0 - vout1 * ltm4700 : vout0 - vout1 From 68b6f9586199fdc7f123bd91ea4a74bf9416eae8 Mon Sep 17 00:00:00 2001 From: Cedric Encarnacion Date: Fri, 24 Jan 2025 23:23:06 +0800 Subject: [PATCH 03/49] hwmon: (pmbus/ltc2978) add support for ltm4673 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for LTM4673. The LTM4673 is a quad output, dual 12A and dual 5A, switching mode DC/DC step-down μModule regulator integrated with 4-channel power system manager. This adds only the chip id, the checks for the manufacturer special id, and the relevant attributes for the device's pmbus_driver_info. The device does not support clear peaks. Signed-off-by: Cedric Encarnacion Link: https://lore.kernel.org/r/20250124-ltm4673-v1-2-a2c6aa37c903@analog.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/ltc2978.rst | 8 ++++++++ drivers/hwmon/pmbus/Kconfig | 4 ++-- drivers/hwmon/pmbus/ltc2978.c | 25 +++++++++++++++++++++++-- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/Documentation/hwmon/ltc2978.rst b/Documentation/hwmon/ltc2978.rst index 651ca4904c66..feae53eb9fbf 100644 --- a/Documentation/hwmon/ltc2978.rst +++ b/Documentation/hwmon/ltc2978.rst @@ -151,6 +151,14 @@ Supported chips: Datasheet: https://www.analog.com/en/products/ltm4644 + * Linear Technology LTM4673 + + Prefix: 'ltm4673' + + Addresses scanned: - + + Datasheet: https://www.analog.com/en/products/ltm4673 + * Linear Technology LTM4675 Prefix: 'ltm4675' diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 419469f40ba0..675b0d4703d8 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -234,8 +234,8 @@ config 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, LTM4675, LTM4676, LTM4677, LTM4678, LTM4680, - LTM4686, and LTM4700. + 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 4c306943383a..658cb1173291 100644 --- a/drivers/hwmon/pmbus/ltc2978.c +++ b/drivers/hwmon/pmbus/ltc2978.c @@ -26,8 +26,8 @@ enum chips { ltc3880, ltc3882, ltc3883, ltc3884, ltc3886, ltc3887, ltc3889, ltc7132, ltc7841, ltc7880, /* Modules */ - ltm2987, ltm4664, ltm4675, ltm4676, ltm4677, ltm4678, ltm4680, ltm4686, - ltm4700, + ltm2987, ltm4664, ltm4673, ltm4675, ltm4676, ltm4677, ltm4678, ltm4680, + ltm4686, ltm4700, }; /* Common for all chips */ @@ -86,6 +86,8 @@ enum chips { #define LTM2987_ID_A 0x8010 /* A/B for two die IDs */ #define LTM2987_ID_B 0x8020 #define LTM4664_ID 0x4120 +#define LTM4673_ID_REV1 0x0230 +#define LTM4673_ID 0x4480 #define LTM4675_ID 0x47a0 #define LTM4676_ID_REV1 0x4400 #define LTM4676_ID_REV2 0x4480 @@ -554,6 +556,7 @@ static const struct i2c_device_id ltc2978_id[] = { {"ltc7880", ltc7880}, {"ltm2987", ltm2987}, {"ltm4664", ltm4664}, + {"ltm4673", ltm4673}, {"ltm4675", ltm4675}, {"ltm4676", ltm4676}, {"ltm4677", ltm4677}, @@ -665,6 +668,8 @@ static int ltc2978_get_id(struct i2c_client *client) return ltm2987; else if (chip_id == LTM4664_ID) return ltm4664; + else if (chip_id == LTM4673_ID || chip_id == LTM4673_ID_REV1) + return ltm4673; else if (chip_id == LTM4675_ID) return ltm4675; else if (chip_id == LTM4676_ID_REV1 || chip_id == LTM4676_ID_REV2 || @@ -869,6 +874,21 @@ static int ltc2978_probe(struct i2c_client *client) | PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; break; + case ltm4673: + data->features |= FEAT_NEEDS_POLLING; + info->read_word_data = ltc2975_read_word_data; + info->pages = LTC2974_NUM_PAGES; + info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_TEMP2; + for (i = 0; i < info->pages; i++) { + info->func[i] |= PMBUS_HAVE_IIN + | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT + | PMBUS_HAVE_PIN + | PMBUS_HAVE_POUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; + } + break; default: return -ENODEV; } @@ -926,6 +946,7 @@ static const struct of_device_id ltc2978_of_match[] = { { .compatible = "lltc,ltc7880" }, { .compatible = "lltc,ltm2987" }, { .compatible = "lltc,ltm4664" }, + { .compatible = "lltc,ltm4673" }, { .compatible = "lltc,ltm4675" }, { .compatible = "lltc,ltm4676" }, { .compatible = "lltc,ltm4677" }, From d9371812f181c8d162d89f23804112888a045d08 Mon Sep 17 00:00:00 2001 From: Ming Yu Date: Fri, 17 Jan 2025 18:07:44 +0800 Subject: [PATCH 04/49] dt-bindings: hwmon: lm90: Add support for NCT7716, NCT7717 and NCT7718 Add support for the Nuvoton NCT7716/7717/7718 thermal sensors. NCT7716 and NCT7717 do not support to add temperature offset. The maximum offset supported by NCT7718 is 127875 millicelsius Reviewed-by: Krzysztof Kozlowski Signed-off-by: Ming Yu Link: https://lore.kernel.org/r/20250117100744.1571385-3-a0282524688@gmail.com Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/hwmon/national,lm90.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/hwmon/national,lm90.yaml b/Documentation/devicetree/bindings/hwmon/national,lm90.yaml index 6e59c8fdef30..4feb76919404 100644 --- a/Documentation/devicetree/bindings/hwmon/national,lm90.yaml +++ b/Documentation/devicetree/bindings/hwmon/national,lm90.yaml @@ -32,6 +32,9 @@ properties: - national,lm89 - national,lm90 - national,lm99 + - nuvoton,nct7716 + - nuvoton,nct7717 + - nuvoton,nct7718 - nxp,sa56004 - onnn,nct1008 - ti,tmp451 @@ -120,6 +123,8 @@ allOf: - dallas,max6659 - dallas,max6695 - dallas,max6696 + - nuvoton,nct7716 + - nuvoton,nct7717 then: patternProperties: "^channel@([0-2])$": @@ -155,6 +160,7 @@ allOf: - national,lm89 - national,lm90 - national,lm99 + - nuvoton,nct7718 - nxp,sa56004 - winbond,w83l771 then: From 02778f69fd34a368dbc8ae37a36cf1b25beb70d1 Mon Sep 17 00:00:00 2001 From: Ming Yu Date: Fri, 17 Jan 2025 18:07:43 +0800 Subject: [PATCH 05/49] hwmon: (lm90): Add support for NCT7716, NCT7717 and NCT7718 NCT7716 is similar to NCT7717 but has one more address support, both of them only have a 8 bit resolution local thermal sensor. NCT7718 has 11 bit resoulution remote thermal sensor. Signed-off-by: Ming Yu Link: https://lore.kernel.org/r/20250117100744.1571385-2-a0282524688@gmail.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/lm90.rst | 43 +++++++++++++++++++ drivers/hwmon/Kconfig | 2 +- drivers/hwmon/lm90.c | 82 +++++++++++++++++++++++++++++++++++- 3 files changed, 124 insertions(+), 3 deletions(-) diff --git a/Documentation/hwmon/lm90.rst b/Documentation/hwmon/lm90.rst index 23af17a0ab44..98452eed16d5 100644 --- a/Documentation/hwmon/lm90.rst +++ b/Documentation/hwmon/lm90.rst @@ -365,6 +365,34 @@ Supported chips: Datasheet: Not publicly available, can be requested from Nuvoton + * Nuvoton NCT7716 + + Prefix: 'nct7716' + + Addresses scanned: I2C 0x48, 0x49 + + Datasheet: Not publicly available, can be requested from Nuvoton + + * Nuvoton NCT7717 + + Prefix: 'nct7717' + + Addresses scanned: I2C 0x48 + + Datasheet: Publicly available at Nuvoton website + + https://www.nuvoton.com/resource-files/Nuvoton_NCT7717U_Datasheet_V111.pdf + + * Nuvoton NCT7718 + + Prefix: 'nct7718' + + Addresses scanned: I2C 0x4c + + Datasheet: Publicly available at Nuvoton website + + https://www.nuvoton.com/resource-files/Nuvoton_NCT7718W_Datasheet_V11.pdf + * Philips/NXP SA56004X Prefix: 'sa56004' @@ -573,6 +601,21 @@ W83L771AWG/ASG * The AWG and ASG variants only differ in package format. * Diode ideality factor configuration (remote sensor) at 0xE3 +NCT7716: + * 8 bit sensor resolution + * Selectable address + * Configurable conversion rate + +NCT7717: + * 8 bit sensor resolution + * Configurable conversion rate + +NCT7718: + * Temperature offset register for remote temperature sensor + * 11 bit resolution for remote temperature sensor + * Low temperature limits + * Configurable conversion rate + SA56004X: * Better local resolution diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 4cbaba15d86e..f114d0c55d78 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1519,7 +1519,7 @@ config SENSORS_LM90 MAX6657, MAX6658, MAX6659, MAX6680, MAX6681, MAX6692, MAX6695, MAX6696, ON Semiconductor NCT1008, NCT210, NCT72, NCT214, NCT218, - Winbond/Nuvoton W83L771W/G/AWG/ASG, + Winbond/Nuvoton W83L771W/G/AWG/ASG, NCT7716, NCT7717 and NCT7718, Philips NE1618, SA56004, GMT G781, Texas Instruments TMP451 and TMP461 sensor chips. diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 511d95a0efb3..75f09553fd67 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -90,6 +90,9 @@ * This driver also supports NE1618 from Philips. It is similar to NE1617 * but supports 11 bit external temperature values. * + * This driver also supports NCT7716, NCT7717 and NCT7718 from Nuvoton. + * The NCT7716 is similar to NCT7717 but has one more address support. + * * Since the LM90 was the first chipset supported by this driver, most * comments will refer to this chipset, but are actually general and * concern all supported chipsets, unless mentioned otherwise. @@ -119,13 +122,15 @@ * Address is fully defined internally and cannot be changed except for * MAX6659, MAX6680 and MAX6681. * LM86, LM89, LM90, LM99, ADM1032, ADM1032-1, ADT7461, ADT7461A, MAX6649, - * MAX6657, MAX6658, NCT1008 and W83L771 have address 0x4c. + * MAX6657, MAX6658, NCT1008, NCT7718 and W83L771 have address 0x4c. * ADM1032-2, ADT7461-2, ADT7461A-2, LM89-1, LM99-1, MAX6646, and NCT1008D * have address 0x4d. * MAX6647 has address 0x4e. * MAX6659 can have address 0x4c, 0x4d or 0x4e. * MAX6654, MAX6680, and MAX6681 can have address 0x18, 0x19, 0x1a, 0x29, * 0x2a, 0x2b, 0x4c, 0x4d or 0x4e. + * NCT7716 can have address 0x48 or 0x49. + * NCT7717 has address 0x48. * SA56004 can have address 0x48 through 0x4F. */ @@ -136,7 +141,7 @@ static const unsigned short normal_i2c[] = { enum chips { adm1023, adm1032, adt7461, adt7461a, adt7481, g781, lm84, lm90, lm99, max1617, max6642, max6646, max6648, max6654, max6657, max6659, max6680, max6696, - nct210, nct72, ne1618, sa56004, tmp451, tmp461, w83l771, + nct210, nct72, nct7716, nct7717, nct7718, ne1618, sa56004, tmp451, tmp461, w83l771, }; /* @@ -191,6 +196,9 @@ enum chips { adm1023, adm1032, adt7461, adt7461a, adt7481, #define ADT7481_REG_MAN_ID 0x3e #define ADT7481_REG_CHIP_ID 0x3d +/* NCT7716/7717/7718 registers */ +#define NCT7716_REG_CHIP_ID 0xFD + /* Device features */ #define LM90_HAVE_EXTENDED_TEMP BIT(0) /* extended temperature support */ #define LM90_HAVE_OFFSET BIT(1) /* temperature offset register */ @@ -275,6 +283,9 @@ static const struct i2c_device_id lm90_id[] = { { "nct214", nct72 }, { "nct218", nct72 }, { "nct72", nct72 }, + { "nct7716", nct7716 }, + { "nct7717", nct7717 }, + { "nct7718", nct7718 }, { "ne1618", ne1618 }, { "w83l771", w83l771 }, { "sa56004", sa56004 }, @@ -382,6 +393,18 @@ static const struct of_device_id __maybe_unused lm90_of_match[] = { .compatible = "onnn,nct72", .data = (void *)nct72 }, + { + .compatible = "nuvoton,nct7716", + .data = (void *)nct7716 + }, + { + .compatible = "nuvoton,nct7717", + .data = (void *)nct7717 + }, + { + .compatible = "nuvoton,nct7718", + .data = (void *)nct7718 + }, { .compatible = "winbond,w83l771", .data = (void *)w83l771 @@ -601,6 +624,26 @@ static const struct lm90_params lm90_params[] = { .resolution = 11, .max_convrate = 7, }, + [nct7716] = { + .flags = LM90_HAVE_ALARMS | LM90_HAVE_CONVRATE, + .alert_alarms = 0x40, + .resolution = 8, + .max_convrate = 8, + }, + [nct7717] = { + .flags = LM90_HAVE_ALARMS | LM90_HAVE_CONVRATE, + .alert_alarms = 0x40, + .resolution = 8, + .max_convrate = 8, + }, + [nct7718] = { + .flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_CRIT + | LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE + | LM90_HAVE_REMOTE_EXT, + .alert_alarms = 0x7c, + .resolution = 11, + .max_convrate = 8, + }, [ne1618] = { .flags = LM90_PAUSE_FOR_CONFIG | LM90_HAVE_BROKEN_ALERT | LM90_HAVE_LOW | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT, @@ -2300,6 +2343,38 @@ static const char *lm90_detect_nuvoton(struct i2c_client *client, int chip_id, return name; } +static const char *lm90_detect_nuvoton_50(struct i2c_client *client, int chip_id, + int config1, int convrate) +{ + int chip_id2 = i2c_smbus_read_byte_data(client, NCT7716_REG_CHIP_ID); + int config2 = i2c_smbus_read_byte_data(client, LM90_REG_CONFIG2); + int address = client->addr; + const char *name = NULL; + + if (chip_id2 < 0 || config2 < 0) + return NULL; + + if (chip_id2 != 0x50 || convrate > 0x08) + return NULL; + + switch (chip_id) { + case 0x90: + if (address == 0x48 && !(config1 & 0x3e) && !(config2 & 0xfe)) + name = "nct7717"; + break; + case 0x91: + if ((address == 0x48 || address == 0x49) && !(config1 & 0x3e) && + !(config2 & 0xfe)) + name = "nct7716"; + else if (address == 0x4c && !(config1 & 0x38) && !(config2 & 0xf8)) + name = "nct7718"; + break; + default: + break; + } + return name; +} + static const char *lm90_detect_nxp(struct i2c_client *client, bool common_address, int chip_id, int config1, int convrate) { @@ -2484,6 +2559,9 @@ static int lm90_detect(struct i2c_client *client, struct i2c_board_info *info) name = lm90_detect_maxim(client, common_address, chip_id, config1, convrate); break; + case 0x50: + name = lm90_detect_nuvoton_50(client, chip_id, config1, convrate); + break; case 0x54: /* ON MC1066, Microchip TC1068, TCM1617 (originally TelCom) */ if (common_address && !(config1 & 0x3f) && !(convrate & 0xf8)) name = "mc1066"; From c36e0a1b26cb914fd4156c68de0687d8df02bf01 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Thu, 23 Jan 2025 17:03:47 +0100 Subject: [PATCH 06/49] hwmon: (isl28022) 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 Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Link: https://lore.kernel.org/r/20250123160347.44635-1-wsa+renesas@sang-engineering.com Signed-off-by: Guenter Roeck --- drivers/hwmon/isl28022.c | 44 ++-------------------------------------- 1 file changed, 2 insertions(+), 42 deletions(-) diff --git a/drivers/hwmon/isl28022.c b/drivers/hwmon/isl28022.c index 3f9b4520b53e..1fb9864635db 100644 --- a/drivers/hwmon/isl28022.c +++ b/drivers/hwmon/isl28022.c @@ -324,26 +324,6 @@ static int shunt_voltage_show(struct seq_file *seqf, void *unused) } DEFINE_SHOW_ATTRIBUTE(shunt_voltage); -static struct dentry *isl28022_debugfs_root; - -static void isl28022_debugfs_remove(void *res) -{ - debugfs_remove_recursive(res); -} - -static void isl28022_debugfs_init(struct i2c_client *client, struct isl28022_data *data) -{ - char name[16]; - struct dentry *debugfs; - - scnprintf(name, sizeof(name), "%d-%04hx", client->adapter->nr, client->addr); - - debugfs = debugfs_create_dir(name, isl28022_debugfs_root); - debugfs_create_file("shunt_voltage", 0444, debugfs, data, &shunt_voltage_fops); - - devm_add_action_or_reset(&client->dev, isl28022_debugfs_remove, debugfs); -} - /* * read property values and make consistency checks. * @@ -475,7 +455,7 @@ static int isl28022_probe(struct i2c_client *client) if (err) return err; - isl28022_debugfs_init(client, data); + debugfs_create_file("shunt_voltage", 0444, client->debugfs, data, &shunt_voltage_fops); hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, &isl28022_chip_info, NULL); @@ -505,27 +485,7 @@ static struct i2c_driver isl28022_driver = { .probe = isl28022_probe, .id_table = isl28022_ids, }; - -static int __init isl28022_init(void) -{ - int err; - - isl28022_debugfs_root = debugfs_create_dir("isl28022", NULL); - err = i2c_add_driver(&isl28022_driver); - if (!err) - return 0; - - debugfs_remove_recursive(isl28022_debugfs_root); - return err; -} -module_init(isl28022_init); - -static void __exit isl28022_exit(void) -{ - i2c_del_driver(&isl28022_driver); - debugfs_remove_recursive(isl28022_debugfs_root); -} -module_exit(isl28022_exit); +module_i2c_driver(isl28022_driver); MODULE_AUTHOR("Carsten Spieß "); MODULE_DESCRIPTION("ISL28022 driver"); From 82dda5cb88f8e8604fc7ddf3ea614579696c5f10 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sat, 25 Jan 2025 13:39:41 +0100 Subject: [PATCH 07/49] hwmon: (ina3221) 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/20250125123941.36729-9-wsa+renesas@sang-engineering.com Signed-off-by: Guenter Roeck --- drivers/hwmon/ina3221.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/hwmon/ina3221.c b/drivers/hwmon/ina3221.c index 1bf479a0f793..ce0e3f214f5b 100644 --- a/drivers/hwmon/ina3221.c +++ b/drivers/hwmon/ina3221.c @@ -116,7 +116,6 @@ struct ina3221_input { * @fields: Register fields of the device * @inputs: Array of channel input source specific structures * @lock: mutex lock to serialize sysfs attribute accesses - * @debugfs: Pointer to debugfs entry for device * @reg_config: Register value of INA3221_CONFIG * @summation_shunt_resistor: equivalent shunt resistor value for summation * @summation_channel_control: Value written to SCC field in INA3221_MASK_ENABLE @@ -128,7 +127,6 @@ struct ina3221_data { struct regmap_field *fields[F_MAX_FIELDS]; struct ina3221_input inputs[INA3221_NUM_CHANNELS]; struct mutex lock; - struct dentry *debugfs; u32 reg_config; int summation_shunt_resistor; u32 summation_channel_control; @@ -913,12 +911,9 @@ static int ina3221_probe(struct i2c_client *client) goto fail; } - scnprintf(name, sizeof(name), "%s-%s", INA3221_DRIVER_NAME, dev_name(dev)); - ina->debugfs = debugfs_create_dir(name, NULL); - for (i = 0; i < INA3221_NUM_CHANNELS; i++) { scnprintf(name, sizeof(name), "in%d_summation_disable", i); - debugfs_create_bool(name, 0400, ina->debugfs, + debugfs_create_bool(name, 0400, client->debugfs, &ina->inputs[i].summation_disable); } @@ -940,8 +935,6 @@ static void ina3221_remove(struct i2c_client *client) struct ina3221_data *ina = dev_get_drvdata(&client->dev); int i; - debugfs_remove_recursive(ina->debugfs); - pm_runtime_disable(ina->pm_dev); pm_runtime_set_suspended(ina->pm_dev); From 2e522c2933d0291d470ea8633bb36097304b9361 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sat, 25 Jan 2025 13:39:42 +0100 Subject: [PATCH 08/49] hwmon: (ltc4282) 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/20250125123941.36729-10-wsa+renesas@sang-engineering.com Signed-off-by: Guenter Roeck --- drivers/hwmon/ltc4282.c | 44 ++++++++--------------------------------- 1 file changed, 8 insertions(+), 36 deletions(-) diff --git a/drivers/hwmon/ltc4282.c b/drivers/hwmon/ltc4282.c index 4f608a3790fb..7f38d2696239 100644 --- a/drivers/hwmon/ltc4282.c +++ b/drivers/hwmon/ltc4282.c @@ -1674,47 +1674,19 @@ static int ltc4282_show_power1_bad_fault_log(void *arg, u64 *val) DEFINE_DEBUGFS_ATTRIBUTE(ltc4282_power1_bad_fault_log, ltc4282_show_power1_bad_fault_log, NULL, "%llu\n"); -static void ltc4282_debugfs_remove(void *dir) +static void ltc4282_debugfs_init(struct ltc4282_state *st, struct i2c_client *i2c) { - debugfs_remove_recursive(dir); -} - -static void ltc4282_debugfs_init(struct ltc4282_state *st, - struct i2c_client *i2c, - const struct device *hwmon) -{ - const char *debugfs_name; - struct dentry *dentry; - int ret; - - if (!IS_ENABLED(CONFIG_DEBUG_FS)) - return; - - debugfs_name = devm_kasprintf(&i2c->dev, GFP_KERNEL, "ltc4282-%s", - dev_name(hwmon)); - if (!debugfs_name) - return; - - dentry = debugfs_create_dir(debugfs_name, NULL); - if (IS_ERR(dentry)) - return; - - ret = devm_add_action_or_reset(&i2c->dev, ltc4282_debugfs_remove, - dentry); - if (ret) - return; - - debugfs_create_file_unsafe("power1_bad_fault_log", 0400, dentry, st, + debugfs_create_file_unsafe("power1_bad_fault_log", 0400, i2c->debugfs, st, <c4282_power1_bad_fault_log); - debugfs_create_file_unsafe("in0_fet_short_fault_log", 0400, dentry, st, + debugfs_create_file_unsafe("in0_fet_short_fault_log", 0400, i2c->debugfs, st, <c4282_fet_short_fault_log); - debugfs_create_file_unsafe("in0_fet_bad_fault_log", 0400, dentry, st, + debugfs_create_file_unsafe("in0_fet_bad_fault_log", 0400, i2c->debugfs, st, <c4282_fet_bad_fault_log); - debugfs_create_file_unsafe("in1_crit_fault_log", 0400, dentry, st, + debugfs_create_file_unsafe("in1_crit_fault_log", 0400, i2c->debugfs, st, <c4282_in1_crit_fault_log); - debugfs_create_file_unsafe("in1_lcrit_fault_log", 0400, dentry, st, + debugfs_create_file_unsafe("in1_lcrit_fault_log", 0400, i2c->debugfs, st, <c4282_in1_lcrit_fault_log); - debugfs_create_file_unsafe("curr1_crit_fault_log", 0400, dentry, st, + debugfs_create_file_unsafe("curr1_crit_fault_log", 0400, i2c->debugfs, st, <c4282_curr1_crit_fault_log); } @@ -1757,7 +1729,7 @@ static int ltc4282_probe(struct i2c_client *i2c) if (IS_ERR(hwmon)) return PTR_ERR(hwmon); - ltc4282_debugfs_init(st, i2c, hwmon); + ltc4282_debugfs_init(st, i2c); return 0; } From cd4db38c436809bc9ea0ee2988f3f2562570ac93 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sat, 25 Jan 2025 13:39:44 +0100 Subject: [PATCH 09/49] hwmon: (sg2042) 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/20250125123941.36729-12-wsa+renesas@sang-engineering.com Signed-off-by: Guenter Roeck --- drivers/hwmon/sg2042-mcu.c | 44 ++++++-------------------------------- 1 file changed, 7 insertions(+), 37 deletions(-) diff --git a/drivers/hwmon/sg2042-mcu.c b/drivers/hwmon/sg2042-mcu.c index aa3fb773602c..74c35945d169 100644 --- a/drivers/hwmon/sg2042-mcu.c +++ b/drivers/hwmon/sg2042-mcu.c @@ -50,12 +50,9 @@ struct sg2042_mcu_data { struct i2c_client *client; - struct dentry *debugfs; struct mutex mutex; }; -static struct dentry *sgmcu_debugfs; - static ssize_t reset_count_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -292,18 +289,15 @@ static const struct hwmon_chip_info sg2042_mcu_chip_info = { .info = sg2042_mcu_info, }; -static void sg2042_mcu_debugfs_init(struct sg2042_mcu_data *mcu, - struct device *dev) +static void sg2042_mcu_debugfs_init(struct sg2042_mcu_data *mcu) { - mcu->debugfs = debugfs_create_dir(dev_name(dev), sgmcu_debugfs); - - debugfs_create_file("firmware_version", 0444, mcu->debugfs, + debugfs_create_file("firmware_version", 0444, mcu->client->debugfs, mcu, &firmware_version_fops); - debugfs_create_file("pcb_version", 0444, mcu->debugfs, mcu, + debugfs_create_file("pcb_version", 0444, mcu->client->debugfs, mcu, &pcb_version_fops); - debugfs_create_file("mcu_type", 0444, mcu->debugfs, mcu, + debugfs_create_file("mcu_type", 0444, mcu->client->debugfs, mcu, &mcu_type_fops); - debugfs_create_file("board_type", 0444, mcu->debugfs, mcu, + debugfs_create_file("board_type", 0444, mcu->client->debugfs, mcu, &board_type_fops); } @@ -333,18 +327,11 @@ static int sg2042_mcu_i2c_probe(struct i2c_client *client) if (IS_ERR(hwmon_dev)) return PTR_ERR(hwmon_dev); - sg2042_mcu_debugfs_init(mcu, dev); + sg2042_mcu_debugfs_init(mcu); return 0; } -static void sg2042_mcu_i2c_remove(struct i2c_client *client) -{ - struct sg2042_mcu_data *mcu = i2c_get_clientdata(client); - - debugfs_remove_recursive(mcu->debugfs); -} - static const struct i2c_device_id sg2042_mcu_id[] = { { "sg2042-hwmon-mcu" }, { } @@ -364,25 +351,8 @@ static struct i2c_driver sg2042_mcu_driver = { .dev_groups = sg2042_mcu_groups, }, .probe = sg2042_mcu_i2c_probe, - .remove = sg2042_mcu_i2c_remove, .id_table = sg2042_mcu_id, }; +module_i2c_driver(sg2042_mcu_driver); -static int __init sg2042_mcu_init(void) -{ - sgmcu_debugfs = debugfs_create_dir("sg2042-mcu", NULL); - return i2c_add_driver(&sg2042_mcu_driver); -} - -static void __exit sg2042_mcu_exit(void) -{ - debugfs_remove_recursive(sgmcu_debugfs); - i2c_del_driver(&sg2042_mcu_driver); -} - -module_init(sg2042_mcu_init); -module_exit(sg2042_mcu_exit); - -MODULE_AUTHOR("Inochi Amaoto "); -MODULE_DESCRIPTION("MCU I2C driver for SG2042 soc platform"); MODULE_LICENSE("GPL"); From 84d867067b2b18f2dd59a12b85c70c3600464874 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sat, 25 Jan 2025 13:39:46 +0100 Subject: [PATCH 10/49] hwmon: (tps23861) 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/20250125123941.36729-14-wsa+renesas@sang-engineering.com Signed-off-by: Guenter Roeck --- drivers/hwmon/tps23861.c | 31 ++----------------------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/drivers/hwmon/tps23861.c b/drivers/hwmon/tps23861.c index 80fb03f30c30..4cb3960d5170 100644 --- a/drivers/hwmon/tps23861.c +++ b/drivers/hwmon/tps23861.c @@ -114,7 +114,6 @@ struct tps23861_data { struct regmap *regmap; u32 shunt_resistor; struct i2c_client *client; - struct dentry *debugfs_dir; }; static const struct regmap_config tps23861_regmap_config = { @@ -503,25 +502,6 @@ static int tps23861_port_status_show(struct seq_file *s, void *data) DEFINE_SHOW_ATTRIBUTE(tps23861_port_status); -static void tps23861_init_debugfs(struct tps23861_data *data, - struct device *hwmon_dev) -{ - const char *debugfs_name; - - debugfs_name = devm_kasprintf(&data->client->dev, GFP_KERNEL, "%s-%s", - data->client->name, dev_name(hwmon_dev)); - if (!debugfs_name) - return; - - data->debugfs_dir = debugfs_create_dir(debugfs_name, NULL); - - debugfs_create_file("port_status", - 0400, - data->debugfs_dir, - data, - &tps23861_port_status_fops); -} - static int tps23861_probe(struct i2c_client *client) { struct device *dev = &client->dev; @@ -562,18 +542,12 @@ static int tps23861_probe(struct i2c_client *client) if (IS_ERR(hwmon_dev)) return PTR_ERR(hwmon_dev); - tps23861_init_debugfs(data, hwmon_dev); + debugfs_create_file("port_status", 0400, client->debugfs, data, + &tps23861_port_status_fops); return 0; } -static void tps23861_remove(struct i2c_client *client) -{ - struct tps23861_data *data = i2c_get_clientdata(client); - - debugfs_remove_recursive(data->debugfs_dir); -} - static const struct of_device_id __maybe_unused tps23861_of_match[] = { { .compatible = "ti,tps23861", }, { }, @@ -582,7 +556,6 @@ MODULE_DEVICE_TABLE(of, tps23861_of_match); static struct i2c_driver tps23861_driver = { .probe = tps23861_probe, - .remove = tps23861_remove, .driver = { .name = "tps23861", .of_match_table = of_match_ptr(tps23861_of_match), From bfbb730c4255e1965d202f48e7aa71baa9a7c65b Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Fri, 31 Jan 2025 10:38:33 +0100 Subject: [PATCH 11/49] hwmon: (sht3x) 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/20250131095148.11973-2-wsa+renesas@sang-engineering.com Signed-off-by: Guenter Roeck --- drivers/hwmon/sht3x.c | 67 +++++++------------------------------------ 1 file changed, 11 insertions(+), 56 deletions(-) diff --git a/drivers/hwmon/sht3x.c b/drivers/hwmon/sht3x.c index 650b0bcc2359..557ad3e7752a 100644 --- a/drivers/hwmon/sht3x.c +++ b/drivers/hwmon/sht3x.c @@ -44,8 +44,6 @@ static const unsigned char sht3x_cmd_read_status_reg[] = { 0xf3, 0x2d }; static const unsigned char sht3x_cmd_clear_status_reg[] = { 0x30, 0x41 }; static const unsigned char sht3x_cmd_read_serial_number[] = { 0x37, 0x80 }; -static struct dentry *debugfs; - /* delays for single-shot mode i2c commands, both in us */ #define SHT3X_SINGLE_WAIT_TIME_HPM 15000 #define SHT3X_SINGLE_WAIT_TIME_MPM 6000 @@ -167,7 +165,6 @@ struct sht3x_data { enum sht3x_chips chip_id; struct mutex i2c_lock; /* lock for sending i2c commands */ struct mutex data_lock; /* lock for updating driver data */ - struct dentry *sensor_dir; u8 mode; const unsigned char *command; @@ -837,23 +834,7 @@ static int sht3x_write(struct device *dev, enum hwmon_sensor_types type, } } -static void sht3x_debugfs_init(struct sht3x_data *data) -{ - char name[32]; - - snprintf(name, sizeof(name), "i2c%u-%02x", - data->client->adapter->nr, data->client->addr); - data->sensor_dir = debugfs_create_dir(name, debugfs); - debugfs_create_u32("serial_number", 0444, - data->sensor_dir, &data->serial_number); -} - -static void sht3x_debugfs_remove(void *sensor_dir) -{ - debugfs_remove_recursive(sensor_dir); -} - -static int sht3x_serial_number_read(struct sht3x_data *data) +static void sht3x_serial_number_read(struct sht3x_data *data) { int ret; char buffer[SHT3X_RESPONSE_LENGTH]; @@ -864,11 +845,12 @@ static int sht3x_serial_number_read(struct sht3x_data *data) buffer, SHT3X_RESPONSE_LENGTH, 0); if (ret) - return ret; + return; data->serial_number = (buffer[0] << 24) | (buffer[1] << 16) | (buffer[3] << 8) | buffer[4]; - return ret; + + debugfs_create_u32("serial_number", 0444, client->debugfs, &data->serial_number); } static const struct hwmon_ops sht3x_ops = { @@ -930,28 +912,14 @@ static int sht3x_probe(struct i2c_client *client) if (ret) return ret; - ret = sht3x_serial_number_read(data); - if (ret) { - dev_dbg(dev, "unable to read serial number\n"); - } else { - sht3x_debugfs_init(data); - ret = devm_add_action_or_reset(dev, - sht3x_debugfs_remove, - data->sensor_dir); - if (ret) - return ret; - } - - hwmon_dev = devm_hwmon_device_register_with_info(dev, - client->name, - data, - &sht3x_chip_info, - sht3x_groups); - + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, + &sht3x_chip_info, sht3x_groups); if (IS_ERR(hwmon_dev)) - dev_dbg(dev, "unable to register hwmon device\n"); + return PTR_ERR(hwmon_dev); - return PTR_ERR_OR_ZERO(hwmon_dev); + sht3x_serial_number_read(data); + + return 0; } /* device ID table */ @@ -968,20 +936,7 @@ static struct i2c_driver sht3x_i2c_driver = { .probe = sht3x_probe, .id_table = sht3x_ids, }; - -static int __init sht3x_init(void) -{ - debugfs = debugfs_create_dir("sht3x", NULL); - return i2c_add_driver(&sht3x_i2c_driver); -} -module_init(sht3x_init); - -static void __exit sht3x_cleanup(void) -{ - debugfs_remove_recursive(debugfs); - i2c_del_driver(&sht3x_i2c_driver); -} -module_exit(sht3x_cleanup); +module_i2c_driver(sht3x_i2c_driver); MODULE_AUTHOR("David Frey "); MODULE_AUTHOR("Pascal Sachs "); From 89cb3ca56cb3192ebd07a2d8eb62e857a42cf83f Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 5 Feb 2025 13:14:08 +0100 Subject: [PATCH 12/49] hwmon: (sg2042) Add back module description/author tags A previous code reorganization inadvertently dropped the two tags, which leads to a "make W=1" warning: WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/hwmon/sg2042-mcu.o Add these back. Fixes: cd4db38c4368 ("hwmon: (sg2042) Use per-client debugfs entry") Signed-off-by: Arnd Bergmann Reviewed-by: Inochi Amaoto Reviewed-by: Wolfram Sang Link: https://lore.kernel.org/r/20250205121419.373464-1-arnd@kernel.org Signed-off-by: Guenter Roeck --- drivers/hwmon/sg2042-mcu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/hwmon/sg2042-mcu.c b/drivers/hwmon/sg2042-mcu.c index 74c35945d169..105131c4acf7 100644 --- a/drivers/hwmon/sg2042-mcu.c +++ b/drivers/hwmon/sg2042-mcu.c @@ -355,4 +355,6 @@ static struct i2c_driver sg2042_mcu_driver = { }; module_i2c_driver(sg2042_mcu_driver); +MODULE_AUTHOR("Inochi Amaoto "); +MODULE_DESCRIPTION("MCU I2C driver for SG2042 soc platform"); MODULE_LICENSE("GPL"); From 4f3cef561f6551a4fcf0ef100530fd9e4c255f43 Mon Sep 17 00:00:00 2001 From: Purva Yeshi Date: Thu, 6 Feb 2025 01:31:34 +0530 Subject: [PATCH 13/49] docs: hwmon: Fix spelling and grammatical issues Fix spelling and grammatical errors across hwmon driver documentation files. Signed-off-by: Purva Yeshi Reviewed-by: Randy Dunlap Link: https://lore.kernel.org/r/20250205200134.12006-1-purvayeshi550@gmail.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/abituguru-datasheet.rst | 8 ++++---- Documentation/hwmon/abituguru.rst | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Documentation/hwmon/abituguru-datasheet.rst b/Documentation/hwmon/abituguru-datasheet.rst index 0cd61471d2a2..19ba4b4cd034 100644 --- a/Documentation/hwmon/abituguru-datasheet.rst +++ b/Documentation/hwmon/abituguru-datasheet.rst @@ -6,9 +6,9 @@ First of all, what I know about uGuru is no fact based on any help, hints or datasheet from Abit. The data I have got on uGuru have I assembled through my weak knowledge in "backwards engineering". And just for the record, you may have noticed uGuru isn't a chip developed by -Abit, as they claim it to be. It's really just an microprocessor (uC) created by +Abit, as they claim it to be. It's really just a microprocessor (uC) created by Winbond (W83L950D). And no, reading the manual for this specific uC or -mailing Windbond for help won't give any useful data about uGuru, as it is +mailing Winbond for help won't give any useful data about uGuru, as it is the program inside the uC that is responding to calls. Olle Sandberg , 2005-05-25 @@ -35,7 +35,7 @@ As far as known the uGuru is always placed at and using the (ISA) I/O-ports ports are holding for detection. We will refer to 0xE0 as CMD (command-port) and 0xE4 as DATA because Abit refers to them with these names. -If DATA holds 0x00 or 0x08 and CMD holds 0x00 or 0xAC an uGuru could be +If DATA holds 0x00 or 0x08 and CMD holds 0x00 or 0xAC a uGuru could be present. We have to check for two different values at data-port, because after a reboot uGuru will hold 0x00 here, but if the driver is removed and later on attached again data-port will hold 0x08, more about this later. @@ -46,7 +46,7 @@ have to test CMD for two different values. On these uGuru's DATA will initially hold 0x09 and will only hold 0x08 after reading CMD first, so CMD must be read first! -To be really sure an uGuru is present a test read of one or more register +To be really sure a uGuru is present a test read of one or more register sets should be done. diff --git a/Documentation/hwmon/abituguru.rst b/Documentation/hwmon/abituguru.rst index cfda60b757ce..4a5ee16b1048 100644 --- a/Documentation/hwmon/abituguru.rst +++ b/Documentation/hwmon/abituguru.rst @@ -40,7 +40,7 @@ Supported chips: .. [2] There is a separate abituguru3 driver for these motherboards, the abituguru (without the 3 !) driver will not work on these - motherboards (and visa versa)! + motherboards (and vice versa)! Authors: - Hans de Goede , From 8df0f002827e18632dcd986f7546c1abf1953a6f Mon Sep 17 00:00:00 2001 From: Andrey Vatoropin Date: Tue, 4 Feb 2025 09:54:08 +0000 Subject: [PATCH 14/49] hwmon: (xgene-hwmon) use appropriate type for the latency value The expression PCC_NUM_RETRIES * pcc_chan->latency is currently being evaluated using 32-bit arithmetic. Since a value of type 'u64' is used to store the eventual result, and this result is later sent to the function usecs_to_jiffies with input parameter unsigned int, the current data type is too wide to store the value of ctx->usecs_lat. Change the data type of "usecs_lat" to a more suitable (narrower) type. Found by Linux Verification Center (linuxtesting.org) with SVACE. Signed-off-by: Andrey Vatoropin Link: https://lore.kernel.org/r/20250204095400.95013-1-a.vatoropin@crpt.ru Signed-off-by: Guenter Roeck --- drivers/hwmon/xgene-hwmon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/xgene-hwmon.c b/drivers/hwmon/xgene-hwmon.c index 1e3bd129a922..eb67190d9a86 100644 --- a/drivers/hwmon/xgene-hwmon.c +++ b/drivers/hwmon/xgene-hwmon.c @@ -105,7 +105,7 @@ struct xgene_hwmon_dev { phys_addr_t comm_base_addr; void *pcc_comm_addr; - u64 usecs_lat; + unsigned int usecs_lat; }; /* From f392611e268f5462b51d90857c72058c31282706 Mon Sep 17 00:00:00 2001 From: Mikhail Paulyshka Date: Sun, 2 Mar 2025 18:50:53 +0300 Subject: [PATCH 15/49] hwmon: (nct6683) Add customer ID for AMD BC-250 This value was found on an AMD BC-250 board with an NCT6686D chip. Signed-off-by: Mikhail Paulyshka Link: https://lore.kernel.org/r/20250302155053.50096-1-me@mixaill.net Signed-off-by: Guenter Roeck --- Documentation/hwmon/nct6683.rst | 3 ++- drivers/hwmon/nct6683.c | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Documentation/hwmon/nct6683.rst b/Documentation/hwmon/nct6683.rst index 8d4a20d99e59..3e549ba95a15 100644 --- a/Documentation/hwmon/nct6683.rst +++ b/Documentation/hwmon/nct6683.rst @@ -3,7 +3,7 @@ Kernel driver nct6683 Supported chips: - * Nuvoton NCT6683D/NCT6687D + * Nuvoton NCT6683D/NCT6686D/NCT6687D Prefix: 'nct6683' @@ -61,6 +61,7 @@ Board Firmware version Intel DH87RL NCT6683D EC firmware version 1.0 build 04/03/13 Intel DH87MC NCT6683D EC firmware version 1.0 build 04/03/13 Intel DB85FL NCT6683D EC firmware version 1.0 build 04/03/13 +AMD BC-250 NCT6686D EC firmware version 1.0 build 07/28/21 ASRock X570 NCT6683D EC firmware version 1.0 build 06/28/19 ASRock X670E NCT6686D EC firmware version 1.0 build 05/19/22 ASRock B650 Steel Legend WiFi NCT6686D EC firmware version 1.0 build 11/09/23 diff --git a/drivers/hwmon/nct6683.c b/drivers/hwmon/nct6683.c index 416ac02e9f74..6cda35388b24 100644 --- a/drivers/hwmon/nct6683.c +++ b/drivers/hwmon/nct6683.c @@ -176,6 +176,7 @@ superio_exit(int ioreg) #define NCT6683_CUSTOMER_ID_MSI2 0x200 #define NCT6683_CUSTOMER_ID_MSI3 0x207 #define NCT6683_CUSTOMER_ID_MSI4 0x20d +#define NCT6683_CUSTOMER_ID_AMD 0x162b #define NCT6683_CUSTOMER_ID_ASROCK 0xe2c #define NCT6683_CUSTOMER_ID_ASROCK2 0xe1b #define NCT6683_CUSTOMER_ID_ASROCK3 0x1631 @@ -1231,6 +1232,8 @@ static int nct6683_probe(struct platform_device *pdev) break; case NCT6683_CUSTOMER_ID_MSI4: break; + case NCT6683_CUSTOMER_ID_AMD: + break; case NCT6683_CUSTOMER_ID_ASROCK: break; case NCT6683_CUSTOMER_ID_ASROCK2: From fafac0ebb289288c767272d600644701e0df8a76 Mon Sep 17 00:00:00 2001 From: Mikhail Paulyshka Date: Sun, 2 Mar 2025 18:50:09 +0300 Subject: [PATCH 16/49] hwmon: (k10temp) add support for cyan skillfish Add support for Cyan Skillfish (AMD Family 17h Model 47h), which appear to be Zen 2 based APU. The patch was tested with an AMD BC-250 board. Signed-off-by: Mikhail Paulyshka Link: https://lore.kernel.org/r/20250302155009.49951-1-me@mixaill.net Signed-off-by: Guenter Roeck --- drivers/hwmon/k10temp.c | 2 ++ include/linux/pci_ids.h | 1 + 2 files changed, 3 insertions(+) diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c index d0b4cc9a5011..3685906cc57c 100644 --- a/drivers/hwmon/k10temp.c +++ b/drivers/hwmon/k10temp.c @@ -467,6 +467,7 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id) k10temp_get_ccd_support(data, 4); break; case 0x31: /* Zen2 Threadripper */ + case 0x47: /* Cyan Skillfish */ case 0x60: /* Renoir */ case 0x68: /* Lucienne */ case 0x71: /* Zen2 */ @@ -535,6 +536,7 @@ static const struct pci_device_id k10temp_id_table[] = { { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_DF_F3) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M10H_DF_F3) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M30H_DF_F3) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M40H_DF_F3) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M60H_DF_F3) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M70H_DF_F3) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_MA0H_DF_F3) }, diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index de5deb1a0118..7a4ad478487d 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -569,6 +569,7 @@ #define PCI_DEVICE_ID_AMD_17H_DF_F3 0x1463 #define PCI_DEVICE_ID_AMD_17H_M10H_DF_F3 0x15eb #define PCI_DEVICE_ID_AMD_17H_M30H_DF_F3 0x1493 +#define PCI_DEVICE_ID_AMD_17H_M40H_DF_F3 0x13f3 #define PCI_DEVICE_ID_AMD_17H_M60H_DF_F3 0x144b #define PCI_DEVICE_ID_AMD_17H_M70H_DF_F3 0x1443 #define PCI_DEVICE_ID_AMD_17H_MA0H_DF_F3 0x1727 From 69af654bf97232cf13a3924c27eac559b6fac951 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Thu, 27 Feb 2025 18:39:33 +0100 Subject: [PATCH 17/49] hwmon: (pmbus/core) Replace deprecated strncpy() with strscpy() strncpy() is deprecated for NUL-terminated destination buffers; use strscpy() instead. Compile-tested only. Note(groeck): strscpy() uses sizeof() to determine the length of the destination buffer if it is not provided as argument. Link: https://github.com/KSPP/linux/issues/90 Cc: linux-hardening@vger.kernel.org Signed-off-by: Thorsten Blum Link: https://lore.kernel.org/r/20250227173936.7746-2-thorsten.blum@linux.dev Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus_core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 787683e83db6..cdde8b03a6e9 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -1470,8 +1470,7 @@ static int pmbus_add_label(struct pmbus_data *data, snprintf(label->name, sizeof(label->name), "%s%d_label", name, seq); if (!index) { if (phase == 0xff) - strncpy(label->label, lstring, - sizeof(label->label) - 1); + strscpy(label->label, lstring); else snprintf(label->label, sizeof(label->label), "%s.%d", lstring, phase); From e3ff0c72cbb7ff6e9c293c3ec4bd612253a68b1e Mon Sep 17 00:00:00 2001 From: Maud Spierings Date: Thu, 27 Feb 2025 13:57:51 +0100 Subject: [PATCH 18/49] hwmon: (ntc_thermistor) Fix module name in the Kconfig The module name is incorrectly stated with a hyphen while it is an underscore. Reviewed-by: Linus Walleij Signed-off-by: Maud Spierings Link: https://lore.kernel.org/r/20250227-ntc_thermistor_fixes-v1-1-70fa73200b52@gocontroll.com Signed-off-by: Guenter Roeck --- drivers/hwmon/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index f114d0c55d78..56494ab85b83 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1625,7 +1625,7 @@ config SENSORS_NTC_THERMISTOR B57891S0103 from EPCOS. This driver can also be built as a module. If so, the module - will be called ntc-thermistor. + will be called ntc_thermistor. config SENSORS_NCT6683 tristate "Nuvoton NCT6683D" From 9fd33bb86747bc9d76f86e3a7ef7da5c613b95a9 Mon Sep 17 00:00:00 2001 From: Maud Spierings Date: Thu, 27 Feb 2025 13:57:52 +0100 Subject: [PATCH 19/49] dt-bindings: hwmon: ntc-thermistor: fix typo regarding the deprecation of the ntc, compatibles Fix the comment stating that the "ntp," compatible strings are deprecated which should be "ntc," Signed-off-by: Maud Spierings Acked-by: Conor Dooley Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250227-ntc_thermistor_fixes-v1-2-70fa73200b52@gocontroll.com Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/hwmon/ntc-thermistor.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/hwmon/ntc-thermistor.yaml b/Documentation/devicetree/bindings/hwmon/ntc-thermistor.yaml index 3d0146e20d3e..b8e500e6cd9f 100644 --- a/Documentation/devicetree/bindings/hwmon/ntc-thermistor.yaml +++ b/Documentation/devicetree/bindings/hwmon/ntc-thermistor.yaml @@ -76,7 +76,7 @@ properties: - const: murata,ncp03wf104 - const: murata,ncp15xh103 - const: samsung,1404-001221 - # Deprecated "ntp," compatible strings + # Deprecated "ntc," compatible strings - const: ntc,ncp15wb473 deprecated: true - const: ntc,ncp18wb473 From e0444758dd1ba299e85a4afea758d7ba61388da8 Mon Sep 17 00:00:00 2001 From: Shengyu Qu Date: Tue, 25 Feb 2025 12:25:06 +0800 Subject: [PATCH 20/49] hwmon: (asus-ec-sensors) add PRIME X670E-PRO WIFI Add support for PRIME X670E-PRO WIFI. Signed-off-by: Shengyu Qu Link: https://lore.kernel.org/r/TYCPR01MB84377BEADF97E8E7554EF0CF98C32@TYCPR01MB8437.jpnprd01.prod.outlook.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/asus_ec_sensors.rst | 1 + drivers/hwmon/asus-ec-sensors.c | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/Documentation/hwmon/asus_ec_sensors.rst b/Documentation/hwmon/asus_ec_sensors.rst index 739636cf7994..d2be9db29614 100644 --- a/Documentation/hwmon/asus_ec_sensors.rst +++ b/Documentation/hwmon/asus_ec_sensors.rst @@ -6,6 +6,7 @@ Kernel driver asus_ec_sensors Supported boards: * PRIME X470-PRO * PRIME X570-PRO + * PRIME X670E-PRO WIFI * Pro WS X570-ACE * ProArt X570-CREATOR WIFI * ProArt X670E-CREATOR WIFI diff --git a/drivers/hwmon/asus-ec-sensors.c b/drivers/hwmon/asus-ec-sensors.c index 43e54dc513da..006ced5ab6e6 100644 --- a/drivers/hwmon/asus-ec-sensors.c +++ b/drivers/hwmon/asus-ec-sensors.c @@ -316,6 +316,14 @@ static const struct ec_board_info board_info_prime_x570_pro = { .family = family_amd_500_series, }; +static const struct ec_board_info board_info_prime_x670e_pro_wifi = { + .sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE | + SENSOR_TEMP_MB | SENSOR_TEMP_VRM | + SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CPU_OPT, + .mutex_path = ACPI_GLOBAL_LOCK_PSEUDO_PATH, + .family = family_amd_600_series, +}; + static const struct ec_board_info board_info_pro_art_x570_creator_wifi = { .sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_VRM | SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CPU_OPT | @@ -503,6 +511,8 @@ static const struct dmi_system_id dmi_table[] = { &board_info_prime_x470_pro), DMI_EXACT_MATCH_ASUS_BOARD_NAME("PRIME X570-PRO", &board_info_prime_x570_pro), + DMI_EXACT_MATCH_ASUS_BOARD_NAME("PRIME X670E-PRO WIFI", + &board_info_prime_x670e_pro_wifi), 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", From e6b082d7a136b8c48847d365e299f247015e9508 Mon Sep 17 00:00:00 2001 From: Jonathan Stroud Date: Mon, 24 Feb 2025 09:06:26 +0100 Subject: [PATCH 21/49] dt-bindings: hwmon: Add UCD90320 gpio description Add optional gpio device tree bindings to the UCD90320. The binding's description is already mentioning the number of GPIOs but without actual gpio controller description. Signed-off-by: Jonathan Stroud Signed-off-by: Michal Simek Reviewed-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/662a050f3f8160fe7c80d4f19e45eb4fac0f2f0a.1740384385.git.michal.simek@amd.com Signed-off-by: Guenter Roeck --- .../devicetree/bindings/hwmon/pmbus/ti,ucd90320.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Documentation/devicetree/bindings/hwmon/pmbus/ti,ucd90320.yaml b/Documentation/devicetree/bindings/hwmon/pmbus/ti,ucd90320.yaml index e8feee38c76c..f8bea1c0e94a 100644 --- a/Documentation/devicetree/bindings/hwmon/pmbus/ti,ucd90320.yaml +++ b/Documentation/devicetree/bindings/hwmon/pmbus/ti,ucd90320.yaml @@ -28,6 +28,15 @@ properties: reg: maxItems: 1 + gpio-controller: true + + gpio-line-names: + minItems: 84 + maxItems: 84 + + '#gpio-cells': + const: 1 + required: - compatible - reg From 649b419f918fedf7213e590dba16e2fab5ea7259 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 2 Jul 2023 13:58:19 -0700 Subject: [PATCH 22/49] hwmon: (pmbus/core) Fix various coding style issues Checkpatch reports bad multi-line comments, bad multi-line alignments, missing blank lines after variable declarations, unnecessary empty lines, unnecessary spaces, and unnecessary braces. Fix most of the reported problems except for some multi-line alignment problems. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus_core.c | 41 +++++++++++++------------------- 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index cdde8b03a6e9..c4a88233e454 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -44,8 +44,7 @@ struct pmbus_sensor { enum pmbus_sensor_classes class; /* sensor class */ bool update; /* runtime sensor update needed */ bool convert; /* Whether or not to apply linear/vid/direct */ - int data; /* Sensor data. - Negative if there was a read error */ + int data; /* Sensor data; negative if there was a read error */ }; #define to_pmbus_sensor(_attr) \ container_of(_attr, struct pmbus_sensor, attribute) @@ -192,11 +191,10 @@ static void pmbus_update_ts(struct i2c_client *client, bool write_op) struct pmbus_data *data = i2c_get_clientdata(client); const struct pmbus_driver_info *info = data->info; - if (info->access_delay) { + if (info->access_delay) data->access_time = ktime_get(); - } else if (info->write_delay && write_op) { + else if (info->write_delay && write_op) data->write_time = ktime_get(); - } } int pmbus_set_page(struct i2c_client *client, int page, int phase) @@ -292,7 +290,6 @@ int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg, } EXPORT_SYMBOL_NS_GPL(pmbus_write_word_data, "PMBUS"); - static int pmbus_write_virt_reg(struct i2c_client *client, int page, int reg, u16 word) { @@ -381,14 +378,14 @@ int pmbus_update_fan(struct i2c_client *client, int page, int id, u8 to; from = _pmbus_read_byte_data(client, page, - pmbus_fan_config_registers[id]); + pmbus_fan_config_registers[id]); if (from < 0) return from; to = (from & ~mask) | (config & mask); if (to != from) { rv = _pmbus_write_byte_data(client, page, - pmbus_fan_config_registers[id], to); + pmbus_fan_config_registers[id], to); if (rv < 0) return rv; } @@ -563,7 +560,7 @@ static int pmbus_get_fan_rate(struct i2c_client *client, int page, int id, } config = _pmbus_read_byte_data(client, page, - pmbus_fan_config_registers[id]); + pmbus_fan_config_registers[id]); if (config < 0) return config; @@ -788,7 +785,7 @@ static s64 pmbus_reg2data_linear(struct pmbus_data *data, if (sensor->class == PSC_VOLTAGE_OUT) { /* LINEAR16 */ exponent = data->exponent[sensor->page]; - mantissa = (u16) sensor->data; + mantissa = (u16)sensor->data; } else { /* LINEAR11 */ exponent = ((s16)sensor->data) >> 11; mantissa = ((s16)((sensor->data & 0x7ff) << 5)) >> 5; @@ -1173,7 +1170,6 @@ static int pmbus_get_boolean(struct i2c_client *client, struct pmbus_boolean *b, } else { pmbus_clear_fault_page(client, page); } - } if (s1 && s2) { s64 v1, v2; @@ -1499,8 +1495,7 @@ struct pmbus_limit_attr { u16 reg; /* Limit register */ u16 sbit; /* Alarm attribute status bit */ bool update; /* True if register needs updates */ - bool low; /* True if low limit; for limits with compare - functions only */ + bool low; /* True if low limit; for limits with compare functions only */ const char *attr; /* Attribute name */ const char *alarm; /* Alarm attribute name */ }; @@ -2211,8 +2206,8 @@ static const u32 pmbus_fan_status_flags[] = { /* Precondition: FAN_CONFIG_x_y and FAN_COMMAND_x must exist for the fan ID */ static int pmbus_add_fan_ctrl(struct i2c_client *client, - struct pmbus_data *data, int index, int page, int id, - u8 config) + struct pmbus_data *data, int index, int page, + int id, u8 config) { struct pmbus_sensor *sensor; @@ -2224,7 +2219,7 @@ static int pmbus_add_fan_ctrl(struct i2c_client *client, return -ENOMEM; if (!((data->info->func[page] & PMBUS_HAVE_PWM12) || - (data->info->func[page] & PMBUS_HAVE_PWM34))) + (data->info->func[page] & PMBUS_HAVE_PWM34))) return 0; sensor = pmbus_add_sensor(data, "pwm", NULL, index, page, @@ -2934,7 +2929,7 @@ static void pmbus_notify(struct pmbus_data *data, int page, int reg, int flags) } static int _pmbus_get_flags(struct pmbus_data *data, u8 page, unsigned int *flags, - unsigned int *event, bool notify) + unsigned int *event, bool notify) { int i, status; const struct pmbus_status_category *cat; @@ -2963,7 +2958,6 @@ static int _pmbus_get_flags(struct pmbus_data *data, u8 page, unsigned int *flag if (notify && status) pmbus_notify(data, page, cat->reg, status); - } /* @@ -3014,7 +3008,6 @@ static int _pmbus_get_flags(struct pmbus_data *data, u8 page, unsigned int *flag *event |= REGULATOR_EVENT_OVER_TEMP_WARN; } - return 0; } @@ -3227,7 +3220,7 @@ static int pmbus_regulator_set_voltage(struct regulator_dev *rdev, int min_uv, } static int pmbus_regulator_list_voltage(struct regulator_dev *rdev, - unsigned int selector) + unsigned int selector) { struct device *dev = rdev_get_dev(rdev); struct i2c_client *client = to_i2c_client(dev->parent); @@ -3362,8 +3355,8 @@ static irqreturn_t pmbus_fault_handler(int irq, void *pdata) { struct pmbus_data *data = pdata; struct i2c_client *client = to_i2c_client(data->dev); - int i, status, event; + mutex_lock(&data->update_lock); for (i = 0; i < data->info->pages; i++) { _pmbus_get_flags(data, i, &status, &event, true); @@ -3471,7 +3464,7 @@ DEFINE_DEBUGFS_ATTRIBUTE(pmbus_debugfs_ops_status, pmbus_debugfs_get_status, NULL, "0x%04llx\n"); static ssize_t pmbus_debugfs_mfr_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) + size_t count, loff_t *ppos) { int rc; struct pmbus_debugfs_entry *entry = file->private_data; @@ -3799,8 +3792,8 @@ int pmbus_do_probe(struct i2c_client *client, struct pmbus_driver_info *info) data->groups[0] = &data->group; memcpy(data->groups + 1, info->groups, sizeof(void *) * groups_num); - data->hwmon_dev = devm_hwmon_device_register_with_groups(dev, - name, data, data->groups); + data->hwmon_dev = devm_hwmon_device_register_with_groups(dev, name, + data, data->groups); if (IS_ERR(data->hwmon_dev)) { dev_err(dev, "Failed to register hwmon device\n"); return PTR_ERR(data->hwmon_dev); From 6625a059e60c4ca8908219b309d26e7635706845 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 25 Jan 2025 16:18:45 -0800 Subject: [PATCH 23/49] hwmon: (pmbus/core) Use local debugfs variable in debugfs initialization In preparation for the next patch in the series, use a local debugfs variable during debugfs initialization. No functional change intended. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus_core.c | 49 ++++++++++++++++---------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index c4a88233e454..c0e657c36f24 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -3506,6 +3506,7 @@ static void pmbus_remove_debugfs(void *data) static int pmbus_init_debugfs(struct i2c_client *client, struct pmbus_data *data) { + struct dentry *debugfs; int i, idx = 0; char name[PMBUS_NAME_SIZE]; struct pmbus_debugfs_entry *entries; @@ -3517,12 +3518,12 @@ static int pmbus_init_debugfs(struct i2c_client *client, * Create the debugfs directory for this device. Use the hwmon device * name to avoid conflicts (hwmon numbers are globally unique). */ - data->debugfs = debugfs_create_dir(dev_name(data->hwmon_dev), - pmbus_debugfs_dir); - if (IS_ERR_OR_NULL(data->debugfs)) { - data->debugfs = NULL; + debugfs = debugfs_create_dir(dev_name(data->hwmon_dev), + pmbus_debugfs_dir); + if (IS_ERR_OR_NULL(debugfs)) return -ENODEV; - } + + data->debugfs = debugfs; /* * Allocate the max possible entries we need. @@ -3547,7 +3548,7 @@ static int pmbus_init_debugfs(struct i2c_client *client, entries[idx].client = client; entries[idx].page = 0; entries[idx].reg = PMBUS_REVISION; - debugfs_create_file("revision", 0444, data->debugfs, + debugfs_create_file("revision", 0444, debugfs, &entries[idx++], &pmbus_debugfs_ops); } @@ -3556,7 +3557,7 @@ static int pmbus_init_debugfs(struct i2c_client *client, entries[idx].client = client; entries[idx].page = 0; entries[idx].reg = PMBUS_MFR_ID; - debugfs_create_file("mfr_id", 0444, data->debugfs, + debugfs_create_file("mfr_id", 0444, debugfs, &entries[idx++], &pmbus_debugfs_ops_mfr); } @@ -3565,7 +3566,7 @@ static int pmbus_init_debugfs(struct i2c_client *client, entries[idx].client = client; entries[idx].page = 0; entries[idx].reg = PMBUS_MFR_MODEL; - debugfs_create_file("mfr_model", 0444, data->debugfs, + debugfs_create_file("mfr_model", 0444, debugfs, &entries[idx++], &pmbus_debugfs_ops_mfr); } @@ -3574,7 +3575,7 @@ static int pmbus_init_debugfs(struct i2c_client *client, entries[idx].client = client; entries[idx].page = 0; entries[idx].reg = PMBUS_MFR_REVISION; - debugfs_create_file("mfr_revision", 0444, data->debugfs, + debugfs_create_file("mfr_revision", 0444, debugfs, &entries[idx++], &pmbus_debugfs_ops_mfr); } @@ -3583,7 +3584,7 @@ static int pmbus_init_debugfs(struct i2c_client *client, entries[idx].client = client; entries[idx].page = 0; entries[idx].reg = PMBUS_MFR_LOCATION; - debugfs_create_file("mfr_location", 0444, data->debugfs, + debugfs_create_file("mfr_location", 0444, debugfs, &entries[idx++], &pmbus_debugfs_ops_mfr); } @@ -3592,7 +3593,7 @@ static int pmbus_init_debugfs(struct i2c_client *client, entries[idx].client = client; entries[idx].page = 0; entries[idx].reg = PMBUS_MFR_DATE; - debugfs_create_file("mfr_date", 0444, data->debugfs, + debugfs_create_file("mfr_date", 0444, debugfs, &entries[idx++], &pmbus_debugfs_ops_mfr); } @@ -3601,7 +3602,7 @@ static int pmbus_init_debugfs(struct i2c_client *client, entries[idx].client = client; entries[idx].page = 0; entries[idx].reg = PMBUS_MFR_SERIAL; - debugfs_create_file("mfr_serial", 0444, data->debugfs, + debugfs_create_file("mfr_serial", 0444, debugfs, &entries[idx++], &pmbus_debugfs_ops_mfr); } @@ -3614,7 +3615,7 @@ static int pmbus_init_debugfs(struct i2c_client *client, entries[idx].client = client; entries[idx].page = i; scnprintf(name, PMBUS_NAME_SIZE, "status%d", i); - debugfs_create_file(name, 0444, data->debugfs, + debugfs_create_file(name, 0444, debugfs, &entries[idx++], &pmbus_debugfs_ops_status); } @@ -3624,7 +3625,7 @@ static int pmbus_init_debugfs(struct i2c_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, data->debugfs, + debugfs_create_file(name, 0444, debugfs, &entries[idx++], &pmbus_debugfs_ops); } @@ -3634,7 +3635,7 @@ static int pmbus_init_debugfs(struct i2c_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, data->debugfs, + debugfs_create_file(name, 0444, debugfs, &entries[idx++], &pmbus_debugfs_ops); } @@ -3644,7 +3645,7 @@ static int pmbus_init_debugfs(struct i2c_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, data->debugfs, + debugfs_create_file(name, 0444, debugfs, &entries[idx++], &pmbus_debugfs_ops); } @@ -3654,7 +3655,7 @@ static int pmbus_init_debugfs(struct i2c_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, data->debugfs, + debugfs_create_file(name, 0444, debugfs, &entries[idx++], &pmbus_debugfs_ops); } @@ -3664,7 +3665,7 @@ static int pmbus_init_debugfs(struct i2c_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, data->debugfs, + debugfs_create_file(name, 0444, debugfs, &entries[idx++], &pmbus_debugfs_ops); } @@ -3674,7 +3675,7 @@ static int pmbus_init_debugfs(struct i2c_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, data->debugfs, + debugfs_create_file(name, 0444, debugfs, &entries[idx++], &pmbus_debugfs_ops); } @@ -3685,7 +3686,7 @@ static int pmbus_init_debugfs(struct i2c_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, data->debugfs, + debugfs_create_file(name, 0444, debugfs, &entries[idx++], &pmbus_debugfs_ops); } @@ -3695,7 +3696,7 @@ static int pmbus_init_debugfs(struct i2c_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, data->debugfs, + debugfs_create_file(name, 0444, debugfs, &entries[idx++], &pmbus_debugfs_ops); } @@ -3705,14 +3706,14 @@ static int pmbus_init_debugfs(struct i2c_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, data->debugfs, + debugfs_create_file(name, 0444, debugfs, &entries[idx++], &pmbus_debugfs_ops); } } - return devm_add_action_or_reset(data->dev, - pmbus_remove_debugfs, data->debugfs); + return devm_add_action_or_reset(data->dev, pmbus_remove_debugfs, + debugfs); } #else static int pmbus_init_debugfs(struct i2c_client *client, From de438ec92f83a2c3107514dd676fbd7db5eebbba Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 25 Jan 2025 14:35:47 -0800 Subject: [PATCH 24/49] hwmon: (pmbus/core) Use the new i2c_client debugfs directory The I2C core now manages a debugfs directory per I2C client. PMBus has its own debugfs hierarchy. Link the two, so a user will be pointed to the I2C domain from the PMBus domain. Suggested-by: Wolfram Sang Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus_core.c | 63 +++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 22 deletions(-) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index c0e657c36f24..c38072b6ee98 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -99,7 +100,6 @@ struct pmbus_data { int num_attributes; struct attribute_group group; const struct attribute_group **groups; - struct dentry *debugfs; /* debugfs device directory */ struct pmbus_sensor *sensors; @@ -3496,34 +3496,49 @@ static const struct file_operations pmbus_debugfs_ops_mfr = { .open = simple_open, }; -static void pmbus_remove_debugfs(void *data) +static void pmbus_remove_symlink(void *symlink) { - struct dentry *entry = data; - - debugfs_remove_recursive(entry); + debugfs_remove(symlink); } static int pmbus_init_debugfs(struct i2c_client *client, struct pmbus_data *data) { - struct dentry *debugfs; - int i, idx = 0; - char name[PMBUS_NAME_SIZE]; + struct dentry *symlink_d, *debugfs = client->debugfs; struct pmbus_debugfs_entry *entries; + const char *pathname, *symlink; + char name[PMBUS_NAME_SIZE]; + int i, idx = 0; - if (!pmbus_debugfs_dir) + /* + * client->debugfs may be NULL or an ERR_PTR(). dentry_path_raw() + * does not check if its parameters are valid, so validate + * client->debugfs before using it. + */ + if (!pmbus_debugfs_dir || IS_ERR_OR_NULL(debugfs)) return -ENODEV; /* - * Create the debugfs directory for this device. Use the hwmon device - * name to avoid conflicts (hwmon numbers are globally unique). + * Backwards compatibility: Create symlink from /pmbus/ + * to i2c debugfs directory. */ - debugfs = debugfs_create_dir(dev_name(data->hwmon_dev), - pmbus_debugfs_dir); - if (IS_ERR_OR_NULL(debugfs)) - return -ENODEV; + pathname = dentry_path_raw(debugfs, name, sizeof(name)); + if (IS_ERR(pathname)) + return PTR_ERR(pathname); - data->debugfs = debugfs; + /* + * The path returned by dentry_path_raw() starts with '/'. Prepend it + * with ".." to get the symlink relative to the pmbus root directory. + */ + symlink = kasprintf(GFP_KERNEL, "..%s", pathname); + if (!symlink) + return -ENOMEM; + + symlink_d = debugfs_create_symlink(dev_name(data->hwmon_dev), + pmbus_debugfs_dir, symlink); + kfree(symlink); + + devm_add_action_or_reset(data->dev, pmbus_remove_symlink, symlink_d); /* * Allocate the max possible entries we need. @@ -3711,9 +3726,7 @@ static int pmbus_init_debugfs(struct i2c_client *client, &pmbus_debugfs_ops); } } - - return devm_add_action_or_reset(data->dev, pmbus_remove_debugfs, - debugfs); + return 0; } #else static int pmbus_init_debugfs(struct i2c_client *client, @@ -3818,9 +3831,15 @@ EXPORT_SYMBOL_NS_GPL(pmbus_do_probe, "PMBUS"); struct dentry *pmbus_get_debugfs_dir(struct i2c_client *client) { - struct pmbus_data *data = i2c_get_clientdata(client); - - return data->debugfs; + /* + * client->debugfs may be an ERR_PTR(). Returning that to + * the calling code would potentially require additional + * complexity in the calling code and otherwise add no + * value. Return NULL in that case. + */ + if (IS_ERR_OR_NULL(client->debugfs)) + return NULL; + return client->debugfs; } EXPORT_SYMBOL_NS_GPL(pmbus_get_debugfs_dir, "PMBUS"); From 05452a3328a57237878f2f8759cec91d7583915c Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 25 Jan 2025 19:19:37 -0800 Subject: [PATCH 25/49] hwmon: (pmbus/core) Make debugfs code unconditional Drop contitionals around debugfs code to compile it unconditionally. In practice it will be optimized away by the compiler if CONFIG_DEBUG_FS is not enabled, so the code size is not affected by this change. Also silently ignore errors if debugfs initialization fails. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus_core.c | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index c38072b6ee98..a19b32eb9a04 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -3420,7 +3420,6 @@ static int pmbus_irq_setup(struct i2c_client *client, struct pmbus_data *data) static struct dentry *pmbus_debugfs_dir; /* pmbus debugfs directory */ -#if IS_ENABLED(CONFIG_DEBUG_FS) static int pmbus_debugfs_get(void *data, u64 *val) { int rc; @@ -3501,8 +3500,8 @@ static void pmbus_remove_symlink(void *symlink) debugfs_remove(symlink); } -static int pmbus_init_debugfs(struct i2c_client *client, - struct pmbus_data *data) +static void pmbus_init_debugfs(struct i2c_client *client, + struct pmbus_data *data) { struct dentry *symlink_d, *debugfs = client->debugfs; struct pmbus_debugfs_entry *entries; @@ -3516,7 +3515,7 @@ static int pmbus_init_debugfs(struct i2c_client *client, * client->debugfs before using it. */ if (!pmbus_debugfs_dir || IS_ERR_OR_NULL(debugfs)) - return -ENODEV; + return; /* * Backwards compatibility: Create symlink from /pmbus/ @@ -3524,7 +3523,7 @@ static int pmbus_init_debugfs(struct i2c_client *client, */ pathname = dentry_path_raw(debugfs, name, sizeof(name)); if (IS_ERR(pathname)) - return PTR_ERR(pathname); + return; /* * The path returned by dentry_path_raw() starts with '/'. Prepend it @@ -3532,7 +3531,7 @@ static int pmbus_init_debugfs(struct i2c_client *client, */ symlink = kasprintf(GFP_KERNEL, "..%s", pathname); if (!symlink) - return -ENOMEM; + return; symlink_d = debugfs_create_symlink(dev_name(data->hwmon_dev), pmbus_debugfs_dir, symlink); @@ -3549,7 +3548,7 @@ static int pmbus_init_debugfs(struct i2c_client *client, 7 + data->info->pages * 10, sizeof(*entries), GFP_KERNEL); if (!entries) - return -ENOMEM; + return; /* * Add device-specific entries. @@ -3726,15 +3725,7 @@ static int pmbus_init_debugfs(struct i2c_client *client, &pmbus_debugfs_ops); } } - return 0; } -#else -static int pmbus_init_debugfs(struct i2c_client *client, - struct pmbus_data *data) -{ - return 0; -} -#endif /* IS_ENABLED(CONFIG_DEBUG_FS) */ int pmbus_do_probe(struct i2c_client *client, struct pmbus_driver_info *info) { @@ -3821,9 +3812,7 @@ int pmbus_do_probe(struct i2c_client *client, struct pmbus_driver_info *info) if (ret) return ret; - ret = pmbus_init_debugfs(client, data); - if (ret) - dev_warn(dev, "Failed to register debugfs\n"); + pmbus_init_debugfs(client, data); return 0; } From 510db88a1c56abfbdb82c976f29bb3dc85a71c13 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 25 Jan 2025 20:40:46 -0800 Subject: [PATCH 26/49] hwmon: (pmbus/core) Declare regulator notification function as void The regulator notification function never returns an error. Declare it as void. While at it, fix its indentation. No functional change. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus_core.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index a19b32eb9a04..d9d5efc44715 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -3312,17 +3312,16 @@ static int pmbus_regulator_register(struct pmbus_data *data) return 0; } -static int pmbus_regulator_notify(struct pmbus_data *data, int page, int event) +static void pmbus_regulator_notify(struct pmbus_data *data, int page, int event) { - int j; + int j; - for (j = 0; j < data->info->num_regulators; j++) { - if (page == rdev_get_id(data->rdevs[j])) { - regulator_notifier_call_chain(data->rdevs[j], event, NULL); - break; - } + for (j = 0; j < data->info->num_regulators; j++) { + if (page == rdev_get_id(data->rdevs[j])) { + regulator_notifier_call_chain(data->rdevs[j], event, NULL); + break; } - return 0; + } } #else static int pmbus_regulator_register(struct pmbus_data *data) @@ -3330,9 +3329,8 @@ static int pmbus_regulator_register(struct pmbus_data *data) return 0; } -static int pmbus_regulator_notify(struct pmbus_data *data, int page, int event) +static void pmbus_regulator_notify(struct pmbus_data *data, int page, int event) { - return 0; } #endif From 2a431adc96395f3e1c971d10fb75c17275b60d1b Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 26 Jan 2025 12:42:18 -0800 Subject: [PATCH 27/49] hwmon: (pmbus/core) Optimize debugfs block data attribute initialization Define debugfs attributes which need block data access 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. While this is currently only used for manufacturer specific attributes, the access code is generic and also works for other block attributes, so rename operation functions from _mfg to _block. While at it, rename the "revison" file to "pmbus_revision" to make its meaning more obvious and to create a clear distinction against the "mfg_revision" file. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus_core.c | 93 ++++++++++++-------------------- 1 file changed, 34 insertions(+), 59 deletions(-) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index d9d5efc44715..a0a9da6e9453 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -3460,8 +3460,8 @@ static int pmbus_debugfs_get_status(void *data, u64 *val) DEFINE_DEBUGFS_ATTRIBUTE(pmbus_debugfs_ops_status, pmbus_debugfs_get_status, NULL, "0x%04llx\n"); -static ssize_t pmbus_debugfs_mfr_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) +static ssize_t pmbus_debugfs_block_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) { int rc; struct pmbus_debugfs_entry *entry = file->private_data; @@ -3486,9 +3486,9 @@ static ssize_t pmbus_debugfs_mfr_read(struct file *file, char __user *buf, return simple_read_from_buffer(buf, count, ppos, data, rc); } -static const struct file_operations pmbus_debugfs_ops_mfr = { +static const struct file_operations pmbus_debugfs_block_ops = { .llseek = noop_llseek, - .read = pmbus_debugfs_mfr_read, + .read = pmbus_debugfs_block_read, .write = NULL, .open = simple_open, }; @@ -3498,6 +3498,20 @@ static void pmbus_remove_symlink(void *symlink) debugfs_remove(symlink); } +struct pmbus_debugfs_data { + u8 reg; + const char *name; +}; + +static const struct pmbus_debugfs_data pmbus_debugfs_block_data[] = { + { .reg = PMBUS_MFR_ID, .name = "mfr_id" }, + { .reg = PMBUS_MFR_MODEL, .name = "mfr_model" }, + { .reg = PMBUS_MFR_REVISION, .name = "mfr_revision" }, + { .reg = PMBUS_MFR_LOCATION, .name = "mfr_location" }, + { .reg = PMBUS_MFR_DATE, .name = "mfr_date" }, + { .reg = PMBUS_MFR_SERIAL, .name = "mfr_serial" }, +}; + static void pmbus_init_debugfs(struct i2c_client *client, struct pmbus_data *data) { @@ -3539,12 +3553,14 @@ static void pmbus_init_debugfs(struct i2c_client *client, /* * Allocate the max possible entries we need. - * 7 entries device-specific + * device specific: + * ARRAY_SIZE(pmbus_debugfs_block_data) + 1 * 10 entries page-specific */ entries = devm_kcalloc(data->dev, - 7 + data->info->pages * 10, sizeof(*entries), - GFP_KERNEL); + ARRAY_SIZE(pmbus_debugfs_block_data) + 1 + + data->info->pages * 10, + sizeof(*entries), GFP_KERNEL); if (!entries) return; @@ -3560,63 +3576,22 @@ static void pmbus_init_debugfs(struct i2c_client *client, entries[idx].client = client; entries[idx].page = 0; entries[idx].reg = PMBUS_REVISION; - debugfs_create_file("revision", 0444, debugfs, + debugfs_create_file("pmbus_revision", 0444, debugfs, &entries[idx++], &pmbus_debugfs_ops); } - if (pmbus_check_block_register(client, 0, PMBUS_MFR_ID)) { - entries[idx].client = client; - entries[idx].page = 0; - entries[idx].reg = PMBUS_MFR_ID; - debugfs_create_file("mfr_id", 0444, debugfs, - &entries[idx++], - &pmbus_debugfs_ops_mfr); - } + for (i = 0; i < ARRAY_SIZE(pmbus_debugfs_block_data); i++) { + const struct pmbus_debugfs_data *d = &pmbus_debugfs_block_data[i]; - if (pmbus_check_block_register(client, 0, PMBUS_MFR_MODEL)) { - entries[idx].client = client; - entries[idx].page = 0; - entries[idx].reg = PMBUS_MFR_MODEL; - debugfs_create_file("mfr_model", 0444, debugfs, - &entries[idx++], - &pmbus_debugfs_ops_mfr); - } - - if (pmbus_check_block_register(client, 0, PMBUS_MFR_REVISION)) { - entries[idx].client = client; - entries[idx].page = 0; - entries[idx].reg = PMBUS_MFR_REVISION; - debugfs_create_file("mfr_revision", 0444, debugfs, - &entries[idx++], - &pmbus_debugfs_ops_mfr); - } - - if (pmbus_check_block_register(client, 0, PMBUS_MFR_LOCATION)) { - entries[idx].client = client; - entries[idx].page = 0; - entries[idx].reg = PMBUS_MFR_LOCATION; - debugfs_create_file("mfr_location", 0444, debugfs, - &entries[idx++], - &pmbus_debugfs_ops_mfr); - } - - if (pmbus_check_block_register(client, 0, PMBUS_MFR_DATE)) { - entries[idx].client = client; - entries[idx].page = 0; - entries[idx].reg = PMBUS_MFR_DATE; - debugfs_create_file("mfr_date", 0444, debugfs, - &entries[idx++], - &pmbus_debugfs_ops_mfr); - } - - if (pmbus_check_block_register(client, 0, PMBUS_MFR_SERIAL)) { - entries[idx].client = client; - entries[idx].page = 0; - entries[idx].reg = PMBUS_MFR_SERIAL; - debugfs_create_file("mfr_serial", 0444, debugfs, - &entries[idx++], - &pmbus_debugfs_ops_mfr); + if (pmbus_check_block_register(client, 0, d->reg)) { + entries[idx].client = client; + entries[idx].page = 0; + entries[idx].reg = d->reg; + debugfs_create_file(d->name, 0444, debugfs, + &entries[idx++], + &pmbus_debugfs_block_ops); + } } /* Add page specific entries */ From 64fbb4c42ab910af6c06c37106f9e06ccde91e34 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 26 Jan 2025 13:46:19 -0800 Subject: [PATCH 28/49] 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 | 130 +++++++++---------------------- 1 file changed, 35 insertions(+), 95 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); - } + 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[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); + 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); + } } } } From c90a00368d90b4ac46712b870debfff6ae50bb87 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 1 Feb 2025 06:42:29 -0800 Subject: [PATCH 29/49] 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; From 9f8e2e49c55fd4df0f9da2ddfa313cc6b868565d Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Mon, 10 Feb 2025 15:59:29 +0100 Subject: [PATCH 30/49] 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 From 9fee7d19bab635f89223cc40dfd2c8797fdc4988 Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Mon, 10 Feb 2025 15:59:30 +0100 Subject: [PATCH 31/49] 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; } From 0ea627381eb527a0ebd262c690c3992085b87ff4 Mon Sep 17 00:00:00 2001 From: Huisong Li Date: Thu, 20 Feb 2025 11:08:32 +0800 Subject: [PATCH 32/49] 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: From 232427772fc123942d110c18269cfbdf40edae18 Mon Sep 17 00:00:00 2001 From: Huisong Li Date: Tue, 4 Mar 2025 15:46:40 +0800 Subject: [PATCH 33/49] 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", From ee65d9e63046de1de2a5383ba6f998634c86c42c Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 5 Mar 2025 13:30:12 +0100 Subject: [PATCH 34/49] 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); + pt5161l_init_debugfs(client, data); - return PTR_ERR_OR_ZERO(hwmon_dev); -} - -static void pt5161l_remove(struct i2c_client *client) -{ - struct pt5161l_data *data = i2c_get_clientdata(client); - - 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"); From 7953605976f55acd4f297d310e6b0b2944f4bdc8 Mon Sep 17 00:00:00 2001 From: Maud Spierings Date: Fri, 7 Mar 2025 09:10:43 +0100 Subject: [PATCH 35/49] 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; } From dbcfcb239b3b452ef8782842c36fb17dd1b9092f Mon Sep 17 00:00:00 2001 From: Kurt Borja Date: Tue, 4 Mar 2025 00:52:50 -0500 Subject: [PATCH 36/49] 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 From 80d363e1b3b6665dc80ca45e72b97d4dd0dcae50 Mon Sep 17 00:00:00 2001 From: Kim Seer Paller Date: Mon, 17 Mar 2025 13:02:25 +0800 Subject: [PATCH 37/49] 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 From 156c6ebbab10e1eecc78403029a69d60dd8073bf Mon Sep 17 00:00:00 2001 From: Kim Seer Paller Date: Mon, 17 Mar 2025 13:02:26 +0800 Subject: [PATCH 38/49] 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 From c1d6afdbb8ed0ef9291c21214e0a11cc91f65c7f Mon Sep 17 00:00:00 2001 From: Kim Seer Paller Date: Mon, 17 Mar 2025 13:02:27 +0800 Subject: [PATCH 39/49] 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" }, From 08ebc9def79fc0c4dbb6ecc39263006e3f98b750 Mon Sep 17 00:00:00 2001 From: Thomas Richard Date: Mon, 3 Feb 2025 12:01:05 +0100 Subject: [PATCH 40/49] 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"); From 52ffdbbd49b8ea759b66fd4d3f66853ed2b1613d Mon Sep 17 00:00:00 2001 From: Leo Yang Date: Thu, 16 Jan 2025 16:59:40 +0800 Subject: [PATCH 41/49] 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 From b64b6cb163f16425c3c4fab077963bf6a67f45c7 Mon Sep 17 00:00:00 2001 From: Leo Yang Date: Thu, 16 Jan 2025 16:59:42 +0800 Subject: [PATCH 42/49] 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"); From fb36a0b3398a791d7266150f63b60e4cf1c98f34 Mon Sep 17 00:00:00 2001 From: Andrei Lalaev Date: Mon, 17 Feb 2025 06:10:56 +0100 Subject: [PATCH 43/49] 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 From bf1bb26f23f10f835e596337604d32d34ab798a6 Mon Sep 17 00:00:00 2001 From: Andrei Lalaev Date: Mon, 17 Feb 2025 06:10:55 +0100 Subject: [PATCH 44/49] hwmon: add driver for HTU31 Add base support for HTU31 temperature and humidity sensor. Besides temperature and humidity values, the driver also exports a 24-bit heater control to sysfs and serial number to debugfs. Signed-off-by: Andrei Lalaev Link: https://lore.kernel.org/r/20250217051110.46827-2-andrey.lalaev@gmail.com [groeck: Fixed continuation line alignment] Signed-off-by: Guenter Roeck --- Documentation/hwmon/htu31.rst | 37 ++++ Documentation/hwmon/index.rst | 1 + MAINTAINERS | 6 + drivers/hwmon/Kconfig | 11 ++ drivers/hwmon/Makefile | 1 + drivers/hwmon/htu31.c | 350 ++++++++++++++++++++++++++++++++++ 6 files changed, 406 insertions(+) create mode 100644 Documentation/hwmon/htu31.rst create mode 100644 drivers/hwmon/htu31.c diff --git a/Documentation/hwmon/htu31.rst b/Documentation/hwmon/htu31.rst new file mode 100644 index 000000000000..ccde84264643 --- /dev/null +++ b/Documentation/hwmon/htu31.rst @@ -0,0 +1,37 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Kernel driver HTU31 +==================== + +Supported chips: + + * Measurement Specialties HTU31 + + Prefix: 'htu31' + + Addresses scanned: - + + Datasheet: Publicly available from https://www.te.com/en/product-CAT-HSC0007.html + +Author: + + - Andrei Lalaev + +Description +----------- + +HTU31 is a humidity and temperature sensor. + +Supported temperature range is from -40 to 125 degrees Celsius. + +Communication with the device is performed via I2C protocol. Sensor's default address +is 0x40. + +sysfs-Interface +--------------- + +=================== ================= +temp1_input: temperature input +humidity1_input: humidity input +heater_enable: heater control +=================== ================= diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index bd491bc37ba4..f0ddf6222c44 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -86,6 +86,7 @@ Hardware Monitoring Kernel Drivers hih6130 hp-wmi-sensors hs3001 + htu31 ibmaem ibm-cffps ibmpowernv diff --git a/MAINTAINERS b/MAINTAINERS index af4e39a803f6..3800bd89e82d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10690,6 +10690,12 @@ W: http://www.st.com/ F: Documentation/devicetree/bindings/iio/humidity/st,hts221.yaml F: drivers/iio/humidity/hts221* +HTU31 Hardware Temperature and Humidity Sensor +M: Andrei Lalaev +L: linux-hwmon@vger.kernel.org +S: Maintained +F: drivers/hwmon/htu31.c + HUAWEI ETHERNET DRIVER M: Cai Huoqing L: netdev@vger.kernel.org diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 76a55ad70bd6..f91f713b0105 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -799,6 +799,17 @@ config SENSORS_HS3001 This driver can also be built as a module. If so, the module will be called hs3001. +config SENSORS_HTU31 + tristate "Measurement Specialties HTU31 humidity and temperature sensor" + depends on I2C + select CRC8 + help + If you say yes here you get support for the HTU31 humidity + and temperature sensors. + + This driver can also be built as a module. If so, the module + will be called htu31. + config SENSORS_IBMAEM tristate "IBM Active Energy Manager temperature/power sensors and control" select IPMI_SI diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 8af5fdbd5dff..766c652ef22b 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -92,6 +92,7 @@ obj-$(CONFIG_SENSORS_GPIO_FAN) += gpio-fan.o obj-$(CONFIG_SENSORS_GXP_FAN_CTRL) += gxp-fan-ctrl.o obj-$(CONFIG_SENSORS_HIH6130) += hih6130.o obj-$(CONFIG_SENSORS_HS3001) += hs3001.o +obj-$(CONFIG_SENSORS_HTU31) += htu31.o obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o obj-$(CONFIG_SENSORS_I5500) += i5500_temp.o obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o diff --git a/drivers/hwmon/htu31.c b/drivers/hwmon/htu31.c new file mode 100644 index 000000000000..7521a371aa6c --- /dev/null +++ b/drivers/hwmon/htu31.c @@ -0,0 +1,350 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * The driver for Measurement Specialties HTU31 Temperature and Humidity sensor. + * + * Copyright (C) 2025 + * Author: Andrei Lalaev + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HTU31_READ_TEMP_HUM_CMD 0x00 +#define HTU31_READ_SERIAL_CMD 0x0a +#define HTU31_CONVERSION_CMD 0x5e +#define HTU31_HEATER_OFF_CMD 0x02 +#define HTU31_HEATER_ON_CMD 0x04 + +#define HTU31_TEMP_HUM_LEN 6 + +/* Conversion time for the highest resolution */ +#define HTU31_HUMIDITY_CONV_TIME 10000 /* us */ +#define HTU31_TEMPERATURE_CONV_TIME 15000 /* us */ + +#define HTU31_SERIAL_NUMBER_LEN 3 +#define HTU31_SERIAL_NUMBER_CRC_LEN 1 +#define HTU31_SERIAL_NUMBER_CRC_OFFSET 3 + +#define HTU31_CRC8_INIT_VAL 0 +#define HTU31_CRC8_POLYNOMIAL 0x31 +DECLARE_CRC8_TABLE(htu31_crc8_table); + +/** + * struct htu31_data - all the data required to operate a HTU31 chip + * @client: the i2c client associated with the HTU31 + * @lock: a mutex to prevent parallel access to the data + * @wait_time: the time needed by sensor to convert values + * @temperature: the latest temperature value in millidegrees + * @humidity: the latest relative humidity value in millipercent + * @serial_number: the serial number of the sensor + * @heater_enable: the internal state of the heater + */ +struct htu31_data { + struct i2c_client *client; + struct mutex lock; /* Used to protect against parallel data updates */ + long wait_time; + long temperature; + long humidity; + u8 serial_number[HTU31_SERIAL_NUMBER_LEN]; + bool heater_enable; +}; + +static long htu31_temp_to_millicelsius(u16 val) +{ + return -40000 + DIV_ROUND_CLOSEST_ULL(165000ULL * val, 65535); +} + +static long htu31_relative_humidity(u16 val) +{ + return DIV_ROUND_CLOSEST_ULL(100000ULL * val, 65535); +} + +static int htu31_data_fetch_command(struct htu31_data *data) +{ + struct i2c_client *client = data->client; + u8 conversion_on = HTU31_CONVERSION_CMD; + u8 read_data_cmd = HTU31_READ_TEMP_HUM_CMD; + u8 t_h_buf[HTU31_TEMP_HUM_LEN] = {}; + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = &read_data_cmd, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = sizeof(t_h_buf), + .buf = t_h_buf, + }, + }; + int ret; + u8 crc; + + guard(mutex)(&data->lock); + + ret = i2c_master_send(client, &conversion_on, 1); + if (ret != 1) { + ret = ret < 0 ? ret : -EIO; + dev_err(&client->dev, + "Conversion command is failed. Error code: %d\n", ret); + return ret; + } + + fsleep(data->wait_time); + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) { + ret = ret < 0 ? ret : -EIO; + dev_err(&client->dev, + "T&H command is failed. Error code: %d\n", ret); + return ret; + } + + crc = crc8(htu31_crc8_table, &t_h_buf[0], 2, HTU31_CRC8_INIT_VAL); + if (crc != t_h_buf[2]) { + dev_err(&client->dev, "Temperature CRC mismatch\n"); + return -EIO; + } + + crc = crc8(htu31_crc8_table, &t_h_buf[3], 2, HTU31_CRC8_INIT_VAL); + if (crc != t_h_buf[5]) { + dev_err(&client->dev, "Humidity CRC mismatch\n"); + return -EIO; + } + + data->temperature = htu31_temp_to_millicelsius(be16_to_cpup((__be16 *)&t_h_buf[0])); + data->humidity = htu31_relative_humidity(be16_to_cpup((__be16 *)&t_h_buf[3])); + + return 0; +} + +static umode_t htu31_is_visible(const void *data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_temp: + case hwmon_humidity: + return 0444; + default: + return 0; + } +} + +static int htu31_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct htu31_data *data = dev_get_drvdata(dev); + int ret; + + ret = htu31_data_fetch_command(data); + if (ret < 0) + return ret; + + switch (type) { + case hwmon_temp: + if (attr != hwmon_temp_input) + return -EINVAL; + + *val = data->temperature; + break; + case hwmon_humidity: + if (attr != hwmon_humidity_input) + return -EINVAL; + + *val = data->humidity; + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int htu31_read_serial_number(struct htu31_data *data) +{ + struct i2c_client *client = data->client; + u8 read_sn_cmd = HTU31_READ_SERIAL_CMD; + u8 sn_buf[HTU31_SERIAL_NUMBER_LEN + HTU31_SERIAL_NUMBER_CRC_LEN]; + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = &read_sn_cmd, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = sizeof(sn_buf), + .buf = sn_buf, + }, + }; + int ret; + u8 crc; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) + return ret; + + crc = crc8(htu31_crc8_table, sn_buf, HTU31_SERIAL_NUMBER_LEN, HTU31_CRC8_INIT_VAL); + if (crc != sn_buf[HTU31_SERIAL_NUMBER_CRC_OFFSET]) { + dev_err(&client->dev, "Serial number CRC mismatch\n"); + return -EIO; + } + + memcpy(data->serial_number, sn_buf, HTU31_SERIAL_NUMBER_LEN); + + return 0; +} + +static ssize_t heater_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct htu31_data *data = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%d\n", data->heater_enable); +} + +static ssize_t heater_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct htu31_data *data = dev_get_drvdata(dev); + u8 heater_cmd; + bool status; + int ret; + + ret = kstrtobool(buf, &status); + if (ret) + return ret; + + heater_cmd = status ? HTU31_HEATER_ON_CMD : HTU31_HEATER_OFF_CMD; + + guard(mutex)(&data->lock); + + ret = i2c_master_send(data->client, &heater_cmd, 1); + if (ret < 0) + return ret; + + data->heater_enable = status; + + return count; +} + +static DEVICE_ATTR_RW(heater_enable); + +static int serial_number_show(struct seq_file *seq_file, + void *unused) +{ + struct htu31_data *data = seq_file->private; + + seq_printf(seq_file, "%X%X%X\n", data->serial_number[0], + data->serial_number[1], data->serial_number[2]); + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(serial_number); + +static struct attribute *htu31_attrs[] = { + &dev_attr_heater_enable.attr, + NULL +}; + +ATTRIBUTE_GROUPS(htu31); + +static const struct hwmon_channel_info * const htu31_info[] = { + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), + HWMON_CHANNEL_INFO(humidity, HWMON_H_INPUT), + NULL +}; + +static const struct hwmon_ops htu31_hwmon_ops = { + .is_visible = htu31_is_visible, + .read = htu31_read, +}; + +static const struct hwmon_chip_info htu31_chip_info = { + .info = htu31_info, + .ops = &htu31_hwmon_ops, +}; + +static int htu31_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct device *hwmon_dev; + struct htu31_data *data; + int ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->client = client; + data->wait_time = HTU31_TEMPERATURE_CONV_TIME + HTU31_HUMIDITY_CONV_TIME; + + ret = devm_mutex_init(dev, &data->lock); + if (ret) + return ret; + + crc8_populate_msb(htu31_crc8_table, HTU31_CRC8_POLYNOMIAL); + + ret = htu31_read_serial_number(data); + if (ret) { + dev_err(dev, "Failed to read serial number\n"); + return ret; + } + + debugfs_create_file("serial_number", + 0444, + client->debugfs, + data, + &serial_number_fops); + + hwmon_dev = devm_hwmon_device_register_with_info(dev, + client->name, + data, + &htu31_chip_info, + htu31_groups); + + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct i2c_device_id htu31_id[] = { + { "htu31" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, htu31_id); + +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id htu31_of_match[] = { + { .compatible = "meas,htu31" }, + { } +}; +MODULE_DEVICE_TABLE(of, htu31_of_match); +#endif + +static struct i2c_driver htu31_driver = { + .driver = { + .name = "htu31", + .of_match_table = of_match_ptr(htu31_of_match), + }, + .probe = htu31_probe, + .id_table = htu31_id, +}; +module_i2c_driver(htu31_driver); + +MODULE_AUTHOR("Andrei Lalaev "); +MODULE_DESCRIPTION("HTU31 Temperature and Humidity sensor driver"); +MODULE_LICENSE("GPL"); From 16746ce8adfe04f9ff8df75c1133286ba93c0e17 Mon Sep 17 00:00:00 2001 From: Huisong Li Date: Wed, 19 Mar 2025 10:06:38 +0800 Subject: [PATCH 45/49] hwmon: (acpi_power_meter) Replace the deprecated hwmon_device_register When load this mode, we can see the following log: "power_meter ACPI000D:00: hwmon_device_register() is deprecated. Please convert the driver to use hwmon_device_register_with_info()." So replace hwmon_device_register with hwmon_device_register_with_info. These attributes, 'power_accuracy', 'power_cap_hyst', 'power_average_min' and 'power_average_max', should have been placed in hwmon_chip_info as power data type. But these attributes are displayed as string format on the following case: a) power1_accuracy --> display like '90.0%' b) power1_cap_hyst --> display 'unknown' when its value is 0xFFFFFFFF c) power1_average_min/max --> display 'unknown' when its value is negative. To avoid any changes in the display of these sysfs interfaces, we can't modifiy the type of these attributes in hwmon core and have to put them to extra_groups. Please note that the path of these sysfs interfaces are modified accordingly if use hwmon_device_register_with_info(): old: all sysfs interfaces are under acpi device, namely, /sys/class/hwmon/hwmonX/device/ now: all sysfs interfaces are under hwmon device, namely, /sys/class/hwmon/hwmonX/ The new ABI does not guarantee that the underlying path remains the same. But we have to accept this change so as to replace the deprecated API. Fortunately, some userspace application, like libsensors, would scan the two path and handles this automatically. So we can accept this change so as to drop the deprecated message. Signed-off-by: Huisong Li Link: https://lore.kernel.org/r/20250319020638.59925-1-lihuisong@huawei.com [groeck: Fixed some multi-line alignment issues; reverted to 32-bit arithmetic in power1_accuracy_show() fixed bad return code from power_meter_is_visible()] Signed-off-by: Guenter Roeck --- drivers/hwmon/acpi_power_meter.c | 868 +++++++++++++++---------------- 1 file changed, 429 insertions(+), 439 deletions(-) diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c index f05986e4f379..29ccdc2fb7ff 100644 --- a/drivers/hwmon/acpi_power_meter.c +++ b/drivers/hwmon/acpi_power_meter.c @@ -87,25 +87,14 @@ struct acpi_power_meter_resource { bool power_alarm; int sensors_valid; unsigned long sensors_last_updated; - struct sensor_device_attribute sensors[NUM_SENSORS]; - int num_sensors; +#define POWER_METER_TRIP_AVERAGE_MIN_IDX 0 +#define POWER_METER_TRIP_AVERAGE_MAX_IDX 1 s64 trip[2]; int num_domain_devices; struct acpi_device **domain_devices; struct kobject *holders_dir; }; -struct sensor_template { - char *label; - ssize_t (*show)(struct device *dev, - struct device_attribute *devattr, - char *buf); - ssize_t (*set)(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count); - int index; -}; - /* Averaging interval */ static int update_avg_interval(struct acpi_power_meter_resource *resource) { @@ -124,62 +113,6 @@ static int update_avg_interval(struct acpi_power_meter_resource *resource) return 0; } -static ssize_t show_avg_interval(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - struct acpi_device *acpi_dev = to_acpi_device(dev); - struct acpi_power_meter_resource *resource = acpi_dev->driver_data; - - mutex_lock(&resource->lock); - update_avg_interval(resource); - mutex_unlock(&resource->lock); - - return sprintf(buf, "%llu\n", resource->avg_interval); -} - -static ssize_t set_avg_interval(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) -{ - struct acpi_device *acpi_dev = to_acpi_device(dev); - struct acpi_power_meter_resource *resource = acpi_dev->driver_data; - union acpi_object arg0 = { ACPI_TYPE_INTEGER }; - struct acpi_object_list args = { 1, &arg0 }; - int res; - unsigned long temp; - unsigned long long data; - acpi_status status; - - res = kstrtoul(buf, 10, &temp); - if (res) - return res; - - if (temp > resource->caps.max_avg_interval || - temp < resource->caps.min_avg_interval) - return -EINVAL; - arg0.integer.value = temp; - - mutex_lock(&resource->lock); - status = acpi_evaluate_integer(resource->acpi_dev->handle, "_PAI", - &args, &data); - if (ACPI_SUCCESS(status)) - resource->avg_interval = temp; - mutex_unlock(&resource->lock); - - if (ACPI_FAILURE(status)) { - acpi_evaluation_failure_warn(resource->acpi_dev->handle, "_PAI", - status); - return -EINVAL; - } - - /* _PAI returns 0 on success, nonzero otherwise */ - if (data) - return -EINVAL; - - return count; -} - /* Cap functions */ static int update_cap(struct acpi_power_meter_resource *resource) { @@ -198,61 +131,6 @@ static int update_cap(struct acpi_power_meter_resource *resource) return 0; } -static ssize_t show_cap(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - struct acpi_device *acpi_dev = to_acpi_device(dev); - struct acpi_power_meter_resource *resource = acpi_dev->driver_data; - - mutex_lock(&resource->lock); - update_cap(resource); - mutex_unlock(&resource->lock); - - return sprintf(buf, "%llu\n", resource->cap * 1000); -} - -static ssize_t set_cap(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) -{ - struct acpi_device *acpi_dev = to_acpi_device(dev); - struct acpi_power_meter_resource *resource = acpi_dev->driver_data; - union acpi_object arg0 = { ACPI_TYPE_INTEGER }; - struct acpi_object_list args = { 1, &arg0 }; - int res; - unsigned long temp; - unsigned long long data; - acpi_status status; - - res = kstrtoul(buf, 10, &temp); - if (res) - return res; - - temp = DIV_ROUND_CLOSEST(temp, 1000); - if (temp > resource->caps.max_cap || temp < resource->caps.min_cap) - return -EINVAL; - arg0.integer.value = temp; - - mutex_lock(&resource->lock); - status = acpi_evaluate_integer(resource->acpi_dev->handle, "_SHL", - &args, &data); - if (ACPI_SUCCESS(status)) - resource->cap = temp; - mutex_unlock(&resource->lock); - - if (ACPI_FAILURE(status)) { - acpi_evaluation_failure_warn(resource->acpi_dev->handle, "_SHL", - status); - return -EINVAL; - } - - /* _SHL returns 0 on success, nonzero otherwise */ - if (data) - return -EINVAL; - - return count; -} - /* Power meter trip points */ static int set_acpi_trip(struct acpi_power_meter_resource *resource) { @@ -287,34 +165,6 @@ static int set_acpi_trip(struct acpi_power_meter_resource *resource) return 0; } -static ssize_t set_trip(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct acpi_device *acpi_dev = to_acpi_device(dev); - struct acpi_power_meter_resource *resource = acpi_dev->driver_data; - unsigned long temp, trip_bk; - int res; - - res = kstrtoul(buf, 10, &temp); - if (res) - return res; - - temp = DIV_ROUND_CLOSEST(temp, 1000); - - guard(mutex)(&resource->lock); - - trip_bk = resource->trip[attr->index - 7]; - resource->trip[attr->index - 7] = temp; - res = set_acpi_trip(resource); - if (res) { - resource->trip[attr->index - 7] = trip_bk; - return res; - } - - return count; -} - /* Power meter */ static int update_meter(struct acpi_power_meter_resource *resource) { @@ -341,206 +191,6 @@ static int update_meter(struct acpi_power_meter_resource *resource) return 0; } -static ssize_t show_power(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - struct acpi_device *acpi_dev = to_acpi_device(dev); - struct acpi_power_meter_resource *resource = acpi_dev->driver_data; - - mutex_lock(&resource->lock); - update_meter(resource); - mutex_unlock(&resource->lock); - - if (resource->power == UNKNOWN_POWER) - return -ENODATA; - - return sprintf(buf, "%llu\n", resource->power * 1000); -} - -/* Miscellaneous */ -static ssize_t show_str(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct acpi_device *acpi_dev = to_acpi_device(dev); - struct acpi_power_meter_resource *resource = acpi_dev->driver_data; - acpi_string val; - int ret; - - mutex_lock(&resource->lock); - switch (attr->index) { - case 0: - val = resource->model_number; - break; - case 1: - val = resource->serial_number; - break; - case 2: - val = resource->oem_info; - break; - default: - WARN(1, "Implementation error: unexpected attribute index %d\n", - attr->index); - val = ""; - break; - } - ret = sprintf(buf, "%s\n", val); - mutex_unlock(&resource->lock); - return ret; -} - -static ssize_t show_val(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct acpi_device *acpi_dev = to_acpi_device(dev); - struct acpi_power_meter_resource *resource = acpi_dev->driver_data; - u64 val = 0; - int ret; - - guard(mutex)(&resource->lock); - - switch (attr->index) { - case 0: - val = resource->caps.min_avg_interval; - break; - case 1: - val = resource->caps.max_avg_interval; - break; - case 2: - val = resource->caps.min_cap * 1000; - break; - case 3: - val = resource->caps.max_cap * 1000; - break; - case 4: - if (resource->caps.hysteresis == UNKNOWN_HYSTERESIS) - return sprintf(buf, "unknown\n"); - - val = resource->caps.hysteresis * 1000; - break; - case 5: - if (resource->caps.flags & POWER_METER_IS_BATTERY) - val = 1; - else - val = 0; - break; - case 6: - ret = update_meter(resource); - if (ret) - return ret; - /* need to update cap if not to support the notification. */ - if (!(resource->caps.flags & POWER_METER_CAN_NOTIFY)) { - ret = update_cap(resource); - if (ret) - return ret; - 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; - } - break; - case 7: - case 8: - if (resource->trip[attr->index - 7] < 0) - return sprintf(buf, "unknown\n"); - - val = resource->trip[attr->index - 7] * 1000; - break; - default: - WARN(1, "Implementation error: unexpected attribute index %d\n", - attr->index); - break; - } - - return sprintf(buf, "%llu\n", val); -} - -static ssize_t show_accuracy(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - struct acpi_device *acpi_dev = to_acpi_device(dev); - struct acpi_power_meter_resource *resource = acpi_dev->driver_data; - unsigned int acc = resource->caps.accuracy; - - return sprintf(buf, "%u.%u%%\n", acc / 1000, acc % 1000); -} - -static ssize_t show_name(struct device *dev, - struct device_attribute *devattr, - char *buf) -{ - return sprintf(buf, "%s\n", ACPI_POWER_METER_NAME); -} - -#define RO_SENSOR_TEMPLATE(_label, _show, _index) \ - { \ - .label = _label, \ - .show = _show, \ - .index = _index, \ - } - -#define RW_SENSOR_TEMPLATE(_label, _show, _set, _index) \ - { \ - .label = _label, \ - .show = _show, \ - .set = _set, \ - .index = _index, \ - } - -/* Sensor descriptions. If you add a sensor, update NUM_SENSORS above! */ -static struct sensor_template meter_attrs[] = { - RO_SENSOR_TEMPLATE(POWER_AVERAGE_NAME, show_power, 0), - RO_SENSOR_TEMPLATE("power1_accuracy", show_accuracy, 0), - RO_SENSOR_TEMPLATE("power1_average_interval_min", show_val, 0), - RO_SENSOR_TEMPLATE("power1_average_interval_max", show_val, 1), - RO_SENSOR_TEMPLATE("power1_is_battery", show_val, 5), - RW_SENSOR_TEMPLATE(POWER_AVG_INTERVAL_NAME, show_avg_interval, - set_avg_interval, 0), - {}, -}; - -static struct sensor_template misc_cap_attrs[] = { - RO_SENSOR_TEMPLATE("power1_cap_min", show_val, 2), - RO_SENSOR_TEMPLATE("power1_cap_max", show_val, 3), - RO_SENSOR_TEMPLATE("power1_cap_hyst", show_val, 4), - RO_SENSOR_TEMPLATE(POWER_ALARM_NAME, show_val, 6), - {}, -}; - -static struct sensor_template ro_cap_attrs[] = { - RO_SENSOR_TEMPLATE(POWER_CAP_NAME, show_cap, 0), - {}, -}; - -static struct sensor_template rw_cap_attrs[] = { - RW_SENSOR_TEMPLATE(POWER_CAP_NAME, show_cap, set_cap, 0), - {}, -}; - -static struct sensor_template trip_attrs[] = { - RW_SENSOR_TEMPLATE("power1_average_min", show_val, set_trip, 7), - RW_SENSOR_TEMPLATE("power1_average_max", show_val, set_trip, 8), - {}, -}; - -static struct sensor_template misc_attrs[] = { - RO_SENSOR_TEMPLATE("name", show_name, 0), - RO_SENSOR_TEMPLATE("power1_model_number", show_str, 0), - RO_SENSOR_TEMPLATE("power1_oem_info", show_str, 2), - RO_SENSOR_TEMPLATE("power1_serial_number", show_str, 1), - {}, -}; - -#undef RO_SENSOR_TEMPLATE -#undef RW_SENSOR_TEMPLATE - /* Read power domain data */ static void remove_domain_devices(struct acpi_power_meter_resource *resource) { @@ -642,109 +292,434 @@ static int read_domain_devices(struct acpi_power_meter_resource *resource) return res; } -/* Registration and deregistration */ -static int register_attrs(struct acpi_power_meter_resource *resource, - struct sensor_template *attrs) +static int set_trip(struct acpi_power_meter_resource *resource, u16 trip_idx, + unsigned long trip) { - struct device *dev = &resource->acpi_dev->dev; - struct sensor_device_attribute *sensors = - &resource->sensors[resource->num_sensors]; - int res = 0; + unsigned long trip_bk; + int ret; - while (attrs->label) { - sensors->dev_attr.attr.name = attrs->label; - sensors->dev_attr.attr.mode = 0444; - sensors->dev_attr.show = attrs->show; - sensors->index = attrs->index; + trip = DIV_ROUND_CLOSEST(trip, 1000); + trip_bk = resource->trip[trip_idx]; - if (attrs->set) { - sensors->dev_attr.attr.mode |= 0200; - sensors->dev_attr.store = attrs->set; - } - - sysfs_attr_init(&sensors->dev_attr.attr); - res = device_create_file(dev, &sensors->dev_attr); - if (res) { - sensors->dev_attr.attr.name = NULL; - goto error; - } - sensors++; - resource->num_sensors++; - attrs++; + resource->trip[trip_idx] = trip; + ret = set_acpi_trip(resource); + if (ret) { + dev_err(&resource->acpi_dev->dev, "set %s failed.\n", + (trip_idx == POWER_METER_TRIP_AVERAGE_MIN_IDX) ? + "power1_average_min" : "power1_average_max"); + resource->trip[trip_idx] = trip_bk; } -error: - return res; + return ret; } -static void remove_attrs(struct acpi_power_meter_resource *resource) +static int set_cap(struct acpi_power_meter_resource *resource, + unsigned long cap) { - int i; + union acpi_object arg0 = { ACPI_TYPE_INTEGER }; + struct acpi_object_list args = { 1, &arg0 }; + unsigned long long data; + acpi_status status; - for (i = 0; i < resource->num_sensors; i++) { - if (!resource->sensors[i].dev_attr.attr.name) - continue; - device_remove_file(&resource->acpi_dev->dev, - &resource->sensors[i].dev_attr); + cap = DIV_ROUND_CLOSEST(cap, 1000); + if (cap > resource->caps.max_cap || cap < resource->caps.min_cap) + return -EINVAL; + + arg0.integer.value = cap; + status = acpi_evaluate_integer(resource->acpi_dev->handle, "_SHL", + &args, &data); + if (ACPI_FAILURE(status)) { + acpi_evaluation_failure_warn(resource->acpi_dev->handle, "_SHL", + status); + return -EINVAL; } + resource->cap = cap; - remove_domain_devices(resource); + /* _SHL returns 0 on success, nonzero otherwise */ + if (data) + return -EINVAL; - resource->num_sensors = 0; + return 0; } -static int setup_attrs(struct acpi_power_meter_resource *resource) +static int set_avg_interval(struct acpi_power_meter_resource *resource, + unsigned long val) { - int res = 0; + union acpi_object arg0 = { ACPI_TYPE_INTEGER }; + struct acpi_object_list args = { 1, &arg0 }; + unsigned long long data; + acpi_status status; - /* _PMD method is optional. */ - res = read_domain_devices(resource); - if (res && res != -ENODEV) - return res; + if (val > resource->caps.max_avg_interval || + val < resource->caps.min_avg_interval) + return -EINVAL; - if (resource->caps.flags & POWER_METER_CAN_MEASURE) { - res = register_attrs(resource, meter_attrs); - if (res) - goto error; + arg0.integer.value = val; + status = acpi_evaluate_integer(resource->acpi_dev->handle, "_PAI", + &args, &data); + if (ACPI_FAILURE(status)) { + acpi_evaluation_failure_warn(resource->acpi_dev->handle, "_PAI", + status); + return -EINVAL; + } + resource->avg_interval = val; + + /* _PAI returns 0 on success, nonzero otherwise */ + if (data) + return -EINVAL; + + return 0; +} + +static int get_power_alarm_state(struct acpi_power_meter_resource *resource, + long *val) +{ + int ret; + + ret = update_meter(resource); + if (ret) + return ret; + + /* need to update cap if not to support the notification. */ + if (!(resource->caps.flags & POWER_METER_CAN_NOTIFY)) { + ret = update_cap(resource); + if (ret) + return ret; + 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; } - if (resource->caps.flags & POWER_METER_CAN_CAP) { - if (!can_cap_in_hardware()) { - dev_warn(&resource->acpi_dev->dev, + return 0; +} + +static umode_t power_meter_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + const struct acpi_power_meter_resource *res = data; + + if (type != hwmon_power) + return 0; + + switch (attr) { + case hwmon_power_average: + case hwmon_power_average_interval_min: + case hwmon_power_average_interval_max: + if (res->caps.flags & POWER_METER_CAN_MEASURE) + return 0444; + break; + case hwmon_power_average_interval: + if (res->caps.flags & POWER_METER_CAN_MEASURE) + return 0644; + break; + case hwmon_power_cap_min: + case hwmon_power_cap_max: + case hwmon_power_alarm: + if (res->caps.flags & POWER_METER_CAN_CAP && can_cap_in_hardware()) + return 0444; + break; + case hwmon_power_cap: + if (res->caps.flags & POWER_METER_CAN_CAP && can_cap_in_hardware()) { + if (res->caps.configurable_cap) + return 0644; + else + return 0444; + } + break; + default: + break; + } + + return 0; +} + +static int power_meter_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct acpi_power_meter_resource *res = dev_get_drvdata(dev); + int ret = 0; + + if (type != hwmon_power) + return -EINVAL; + + guard(mutex)(&res->lock); + + switch (attr) { + case hwmon_power_average: + ret = update_meter(res); + if (ret) + return ret; + if (res->power == UNKNOWN_POWER) + return -ENODATA; + *val = res->power * 1000; + break; + case hwmon_power_average_interval_min: + *val = res->caps.min_avg_interval; + break; + case hwmon_power_average_interval_max: + *val = res->caps.max_avg_interval; + break; + case hwmon_power_average_interval: + ret = update_avg_interval(res); + if (ret) + return ret; + *val = (res)->avg_interval; + break; + case hwmon_power_cap_min: + *val = res->caps.min_cap * 1000; + break; + case hwmon_power_cap_max: + *val = res->caps.max_cap * 1000; + break; + case hwmon_power_alarm: + ret = get_power_alarm_state(res, val); + if (ret) + return ret; + break; + case hwmon_power_cap: + ret = update_cap(res); + if (ret) + return ret; + *val = res->cap * 1000; + break; + default: + break; + } + + return 0; +} + +static int power_meter_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct acpi_power_meter_resource *res = dev_get_drvdata(dev); + int ret; + + if (type != hwmon_power) + return -EINVAL; + + guard(mutex)(&res->lock); + switch (attr) { + case hwmon_power_cap: + ret = set_cap(res, val); + break; + case hwmon_power_average_interval: + ret = set_avg_interval(res, val); + break; + default: + ret = -EOPNOTSUPP; + } + + return ret; +} + +static const struct hwmon_channel_info * const power_meter_info[] = { + HWMON_CHANNEL_INFO(power, HWMON_P_AVERAGE | + HWMON_P_AVERAGE_INTERVAL | HWMON_P_AVERAGE_INTERVAL_MIN | + HWMON_P_AVERAGE_INTERVAL_MAX | HWMON_P_CAP | HWMON_P_CAP_MIN | + HWMON_P_CAP_MAX | HWMON_P_ALARM), + NULL +}; + +static const struct hwmon_ops power_meter_ops = { + .is_visible = power_meter_is_visible, + .read = power_meter_read, + .write = power_meter_write, +}; + +static const struct hwmon_chip_info power_meter_chip_info = { + .ops = &power_meter_ops, + .info = power_meter_info, +}; + +static ssize_t power1_average_max_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct acpi_power_meter_resource *res = dev_get_drvdata(dev); + unsigned long trip; + int ret; + + ret = kstrtoul(buf, 10, &trip); + if (ret) + return ret; + + mutex_lock(&res->lock); + ret = set_trip(res, POWER_METER_TRIP_AVERAGE_MAX_IDX, trip); + mutex_unlock(&res->lock); + + return ret == 0 ? count : ret; +} + +static ssize_t power1_average_min_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct acpi_power_meter_resource *res = dev_get_drvdata(dev); + unsigned long trip; + int ret; + + ret = kstrtoul(buf, 10, &trip); + if (ret) + return ret; + + mutex_lock(&res->lock); + ret = set_trip(res, POWER_METER_TRIP_AVERAGE_MIN_IDX, trip); + mutex_unlock(&res->lock); + + return ret == 0 ? count : ret; +} + +static ssize_t power1_average_min_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct acpi_power_meter_resource *res = dev_get_drvdata(dev); + + if (res->trip[POWER_METER_TRIP_AVERAGE_MIN_IDX] < 0) + return sysfs_emit(buf, "unknown\n"); + + return sysfs_emit(buf, "%lld\n", + res->trip[POWER_METER_TRIP_AVERAGE_MIN_IDX] * 1000); +} + +static ssize_t power1_average_max_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct acpi_power_meter_resource *res = dev_get_drvdata(dev); + + if (res->trip[POWER_METER_TRIP_AVERAGE_MAX_IDX] < 0) + return sysfs_emit(buf, "unknown\n"); + + return sysfs_emit(buf, "%lld\n", + res->trip[POWER_METER_TRIP_AVERAGE_MAX_IDX] * 1000); +} + +static ssize_t power1_cap_hyst_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct acpi_power_meter_resource *res = dev_get_drvdata(dev); + + if (res->caps.hysteresis == UNKNOWN_HYSTERESIS) + return sysfs_emit(buf, "unknown\n"); + + return sysfs_emit(buf, "%llu\n", res->caps.hysteresis * 1000); +} + +static ssize_t power1_accuracy_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct acpi_power_meter_resource *res = dev_get_drvdata(dev); + unsigned int acc = res->caps.accuracy; + + return sysfs_emit(buf, "%u.%u%%\n", acc / 1000, acc % 1000); +} + +static ssize_t power1_is_battery_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct acpi_power_meter_resource *res = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%u\n", + res->caps.flags & POWER_METER_IS_BATTERY ? 1 : 0); +} + +static ssize_t power1_model_number_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct acpi_power_meter_resource *res = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%s\n", res->model_number); +} + +static ssize_t power1_oem_info_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct acpi_power_meter_resource *res = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%s\n", res->oem_info); +} + +static ssize_t power1_serial_number_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct acpi_power_meter_resource *res = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%s\n", res->serial_number); +} + +/* depend on POWER_METER_CAN_TRIP */ +static DEVICE_ATTR_RW(power1_average_max); +static DEVICE_ATTR_RW(power1_average_min); + +/* depend on POWER_METER_CAN_CAP */ +static DEVICE_ATTR_RO(power1_cap_hyst); + +/* depend on POWER_METER_CAN_MEASURE */ +static DEVICE_ATTR_RO(power1_accuracy); +static DEVICE_ATTR_RO(power1_is_battery); + +static DEVICE_ATTR_RO(power1_model_number); +static DEVICE_ATTR_RO(power1_oem_info); +static DEVICE_ATTR_RO(power1_serial_number); + +static umode_t power_extra_is_visible(struct kobject *kobj, + struct attribute *attr, int idx) +{ + struct device *dev = kobj_to_dev(kobj); + struct acpi_power_meter_resource *res = dev_get_drvdata(dev); + + if (attr == &dev_attr_power1_is_battery.attr || + attr == &dev_attr_power1_accuracy.attr) { + if ((res->caps.flags & POWER_METER_CAN_MEASURE) == 0) + return 0; + } + + if (attr == &dev_attr_power1_cap_hyst.attr) { + if ((res->caps.flags & POWER_METER_CAN_CAP) == 0) { + return 0; + } else if (!can_cap_in_hardware()) { + dev_warn(&res->acpi_dev->dev, "Ignoring unsafe software power cap!\n"); - goto skip_unsafe_cap; + return 0; } - - if (resource->caps.configurable_cap) - res = register_attrs(resource, rw_cap_attrs); - else - res = register_attrs(resource, ro_cap_attrs); - - if (res) - goto error; - - res = register_attrs(resource, misc_cap_attrs); - if (res) - goto error; } -skip_unsafe_cap: - if (resource->caps.flags & POWER_METER_CAN_TRIP) { - res = register_attrs(resource, trip_attrs); - if (res) - goto error; + if (attr == &dev_attr_power1_average_max.attr || + attr == &dev_attr_power1_average_min.attr) { + if ((res->caps.flags & POWER_METER_CAN_TRIP) == 0) + return 0; } - res = register_attrs(resource, misc_attrs); - if (res) - goto error; - - return res; -error: - remove_attrs(resource); - return res; + return attr->mode; } +static struct attribute *power_extra_attrs[] = { + &dev_attr_power1_average_max.attr, + &dev_attr_power1_average_min.attr, + &dev_attr_power1_cap_hyst.attr, + &dev_attr_power1_accuracy.attr, + &dev_attr_power1_is_battery.attr, + &dev_attr_power1_model_number.attr, + &dev_attr_power1_oem_info.attr, + &dev_attr_power1_serial_number.attr, + NULL +}; + +static const struct attribute_group power_extra_group = { + .attrs = power_extra_attrs, + .is_visible = power_extra_is_visible, +}; + +__ATTRIBUTE_GROUPS(power_extra); + static void free_capabilities(struct acpi_power_meter_resource *resource) { acpi_string *str; @@ -852,13 +827,23 @@ static void acpi_power_meter_notify(struct acpi_device *device, u32 event) case METER_NOTIFY_CONFIG: mutex_lock(&resource->lock); free_capabilities(resource); + remove_domain_devices(resource); + hwmon_device_unregister(resource->hwmon_dev); res = read_capabilities(resource); - mutex_unlock(&resource->lock); if (res) - break; - - remove_attrs(resource); - setup_attrs(resource); + dev_err_once(&device->dev, "read capabilities failed.\n"); + res = read_domain_devices(resource); + if (res && res != -ENODEV) + dev_err_once(&device->dev, "read domain devices failed.\n"); + resource->hwmon_dev = + hwmon_device_register_with_info(&device->dev, + ACPI_POWER_METER_NAME, + resource, + &power_meter_chip_info, + power_extra_groups); + if (IS_ERR(resource->hwmon_dev)) + dev_err_once(&device->dev, "register hwmon device failed.\n"); + mutex_unlock(&resource->lock); break; case METER_NOTIFY_TRIP: sysfs_notify(&device->dev.kobj, NULL, POWER_AVERAGE_NAME); @@ -920,7 +905,7 @@ static int acpi_power_meter_add(struct acpi_device *device) struct acpi_device *ipi_device = acpi_dev_get_first_match_dev("IPI0001", NULL, -1); if (ipi_device && acpi_wait_for_acpi_ipmi()) - dev_warn(&device->dev, "Waiting for ACPI IPMI timeout"); + dev_warn(&device->dev, "Waiting for ACPI IPMI timeout"); acpi_dev_put(ipi_device); } #endif @@ -932,11 +917,16 @@ static int acpi_power_meter_add(struct acpi_device *device) resource->trip[0] = -1; resource->trip[1] = -1; - res = setup_attrs(resource); - if (res) + /* _PMD method is optional. */ + res = read_domain_devices(resource); + if (res && res != -ENODEV) goto exit_free_capability; - resource->hwmon_dev = hwmon_device_register(&device->dev); + resource->hwmon_dev = + hwmon_device_register_with_info(&device->dev, + ACPI_POWER_METER_NAME, resource, + &power_meter_chip_info, + power_extra_groups); if (IS_ERR(resource->hwmon_dev)) { res = PTR_ERR(resource->hwmon_dev); goto exit_remove; @@ -946,7 +936,7 @@ static int acpi_power_meter_add(struct acpi_device *device) goto exit; exit_remove: - remove_attrs(resource); + remove_domain_devices(resource); exit_free_capability: free_capabilities(resource); exit_free: @@ -965,7 +955,7 @@ static void acpi_power_meter_remove(struct acpi_device *device) resource = acpi_driver_data(device); hwmon_device_unregister(resource->hwmon_dev); - remove_attrs(resource); + remove_domain_devices(resource); free_capabilities(resource); kfree(resource); From 73d51cb9396b10f98851c64adb625d0074e24120 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 21 Mar 2025 09:02:12 +0100 Subject: [PATCH 46/49] dt-bindings: hwmon: Drop stray blank line in the header There should be no blank line between the YAML opening header and the schema '$id'. Reported-by: Florin Leotescu (OSS) Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20250321080212.18013-1-krzysztof.kozlowski@linaro.org Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/hwmon/adi,ad741x.yaml | 1 - Documentation/devicetree/bindings/hwmon/adi,adm1275.yaml | 1 - Documentation/devicetree/bindings/hwmon/adi,ltc2991.yaml | 1 - Documentation/devicetree/bindings/hwmon/maxim,max20730.yaml | 1 - Documentation/devicetree/bindings/hwmon/maxim,max6639.yaml | 1 - Documentation/devicetree/bindings/hwmon/maxim,max6650.yaml | 1 - Documentation/devicetree/bindings/hwmon/nuvoton,nct6775.yaml | 1 - Documentation/devicetree/bindings/hwmon/nuvoton,nct7363.yaml | 1 - Documentation/devicetree/bindings/hwmon/nuvoton,nct7802.yaml | 1 - Documentation/devicetree/bindings/hwmon/ti,adc128d818.yaml | 1 - Documentation/devicetree/bindings/hwmon/ti,ads7828.yaml | 1 - Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml | 1 - Documentation/devicetree/bindings/hwmon/ti,lm87.yaml | 1 - Documentation/devicetree/bindings/hwmon/ti,tmp513.yaml | 1 - Documentation/devicetree/bindings/hwmon/ti,tps23861.yaml | 1 - Documentation/devicetree/bindings/hwmon/winbond,w83781d.yaml | 1 - 16 files changed, 16 deletions(-) diff --git a/Documentation/devicetree/bindings/hwmon/adi,ad741x.yaml b/Documentation/devicetree/bindings/hwmon/adi,ad741x.yaml index ce7f8ce9da0a..236d8b52ef85 100644 --- a/Documentation/devicetree/bindings/hwmon/adi,ad741x.yaml +++ b/Documentation/devicetree/bindings/hwmon/adi,ad741x.yaml @@ -1,7 +1,6 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) %YAML 1.2 --- - $id: http://devicetree.org/schemas/hwmon/adi,ad741x.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# diff --git a/Documentation/devicetree/bindings/hwmon/adi,adm1275.yaml b/Documentation/devicetree/bindings/hwmon/adi,adm1275.yaml index fd79bf2e0d16..ddb72857c846 100644 --- a/Documentation/devicetree/bindings/hwmon/adi,adm1275.yaml +++ b/Documentation/devicetree/bindings/hwmon/adi,adm1275.yaml @@ -1,7 +1,6 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) %YAML 1.2 --- - $id: http://devicetree.org/schemas/hwmon/adi,adm1275.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# diff --git a/Documentation/devicetree/bindings/hwmon/adi,ltc2991.yaml b/Documentation/devicetree/bindings/hwmon/adi,ltc2991.yaml index 011e5b65c79c..1ff44cb22ef4 100644 --- a/Documentation/devicetree/bindings/hwmon/adi,ltc2991.yaml +++ b/Documentation/devicetree/bindings/hwmon/adi,ltc2991.yaml @@ -1,7 +1,6 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) %YAML 1.2 --- - $id: http://devicetree.org/schemas/hwmon/adi,ltc2991.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# diff --git a/Documentation/devicetree/bindings/hwmon/maxim,max20730.yaml b/Documentation/devicetree/bindings/hwmon/maxim,max20730.yaml index 93e86e3b4602..8af0d7458e62 100644 --- a/Documentation/devicetree/bindings/hwmon/maxim,max20730.yaml +++ b/Documentation/devicetree/bindings/hwmon/maxim,max20730.yaml @@ -1,7 +1,6 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) %YAML 1.2 --- - $id: http://devicetree.org/schemas/hwmon/maxim,max20730.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# diff --git a/Documentation/devicetree/bindings/hwmon/maxim,max6639.yaml b/Documentation/devicetree/bindings/hwmon/maxim,max6639.yaml index 4f5837a30773..139a95e00fe5 100644 --- a/Documentation/devicetree/bindings/hwmon/maxim,max6639.yaml +++ b/Documentation/devicetree/bindings/hwmon/maxim,max6639.yaml @@ -1,7 +1,6 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) %YAML 1.2 --- - $id: http://devicetree.org/schemas/hwmon/maxim,max6639.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# diff --git a/Documentation/devicetree/bindings/hwmon/maxim,max6650.yaml b/Documentation/devicetree/bindings/hwmon/maxim,max6650.yaml index 2c26104a5e16..24c7697fdc1a 100644 --- a/Documentation/devicetree/bindings/hwmon/maxim,max6650.yaml +++ b/Documentation/devicetree/bindings/hwmon/maxim,max6650.yaml @@ -1,7 +1,6 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) %YAML 1.2 --- - $id: http://devicetree.org/schemas/hwmon/maxim,max6650.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# diff --git a/Documentation/devicetree/bindings/hwmon/nuvoton,nct6775.yaml b/Documentation/devicetree/bindings/hwmon/nuvoton,nct6775.yaml index e3db642878d4..244470282890 100644 --- a/Documentation/devicetree/bindings/hwmon/nuvoton,nct6775.yaml +++ b/Documentation/devicetree/bindings/hwmon/nuvoton,nct6775.yaml @@ -1,7 +1,6 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) %YAML 1.2 --- - $id: http://devicetree.org/schemas/hwmon/nuvoton,nct6775.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# diff --git a/Documentation/devicetree/bindings/hwmon/nuvoton,nct7363.yaml b/Documentation/devicetree/bindings/hwmon/nuvoton,nct7363.yaml index c1e5dedc2f6a..625fcf5d3b54 100644 --- a/Documentation/devicetree/bindings/hwmon/nuvoton,nct7363.yaml +++ b/Documentation/devicetree/bindings/hwmon/nuvoton,nct7363.yaml @@ -1,7 +1,6 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) %YAML 1.2 --- - $id: http://devicetree.org/schemas/hwmon/nuvoton,nct7363.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# diff --git a/Documentation/devicetree/bindings/hwmon/nuvoton,nct7802.yaml b/Documentation/devicetree/bindings/hwmon/nuvoton,nct7802.yaml index cd8dcd797031..c16a33227e94 100644 --- a/Documentation/devicetree/bindings/hwmon/nuvoton,nct7802.yaml +++ b/Documentation/devicetree/bindings/hwmon/nuvoton,nct7802.yaml @@ -1,7 +1,6 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) %YAML 1.2 --- - $id: http://devicetree.org/schemas/hwmon/nuvoton,nct7802.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# diff --git a/Documentation/devicetree/bindings/hwmon/ti,adc128d818.yaml b/Documentation/devicetree/bindings/hwmon/ti,adc128d818.yaml index a32035409cee..78e3d97e2ae5 100644 --- a/Documentation/devicetree/bindings/hwmon/ti,adc128d818.yaml +++ b/Documentation/devicetree/bindings/hwmon/ti,adc128d818.yaml @@ -1,7 +1,6 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) %YAML 1.2 --- - $id: http://devicetree.org/schemas/hwmon/ti,adc128d818.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# diff --git a/Documentation/devicetree/bindings/hwmon/ti,ads7828.yaml b/Documentation/devicetree/bindings/hwmon/ti,ads7828.yaml index 926be9a29044..fb80456120e1 100644 --- a/Documentation/devicetree/bindings/hwmon/ti,ads7828.yaml +++ b/Documentation/devicetree/bindings/hwmon/ti,ads7828.yaml @@ -1,7 +1,6 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) %YAML 1.2 --- - $id: http://devicetree.org/schemas/hwmon/ti,ads7828.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# diff --git a/Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml b/Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml index 7372e282765b..bc03781342c0 100644 --- a/Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml +++ b/Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml @@ -1,7 +1,6 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) %YAML 1.2 --- - $id: http://devicetree.org/schemas/hwmon/ti,ina2xx.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# diff --git a/Documentation/devicetree/bindings/hwmon/ti,lm87.yaml b/Documentation/devicetree/bindings/hwmon/ti,lm87.yaml index f553235a7321..63d8cf467806 100644 --- a/Documentation/devicetree/bindings/hwmon/ti,lm87.yaml +++ b/Documentation/devicetree/bindings/hwmon/ti,lm87.yaml @@ -1,7 +1,6 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) %YAML 1.2 --- - $id: http://devicetree.org/schemas/hwmon/ti,lm87.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# diff --git a/Documentation/devicetree/bindings/hwmon/ti,tmp513.yaml b/Documentation/devicetree/bindings/hwmon/ti,tmp513.yaml index 227858e76058..cba5b4a1b81f 100644 --- a/Documentation/devicetree/bindings/hwmon/ti,tmp513.yaml +++ b/Documentation/devicetree/bindings/hwmon/ti,tmp513.yaml @@ -1,7 +1,6 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) %YAML 1.2 --- - $id: http://devicetree.org/schemas/hwmon/ti,tmp513.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# diff --git a/Documentation/devicetree/bindings/hwmon/ti,tps23861.yaml b/Documentation/devicetree/bindings/hwmon/ti,tps23861.yaml index f58248c29e22..ee7de53e1918 100644 --- a/Documentation/devicetree/bindings/hwmon/ti,tps23861.yaml +++ b/Documentation/devicetree/bindings/hwmon/ti,tps23861.yaml @@ -1,7 +1,6 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) %YAML 1.2 --- - $id: http://devicetree.org/schemas/hwmon/ti,tps23861.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# diff --git a/Documentation/devicetree/bindings/hwmon/winbond,w83781d.yaml b/Documentation/devicetree/bindings/hwmon/winbond,w83781d.yaml index 31ce77a4b087..6971ecb314eb 100644 --- a/Documentation/devicetree/bindings/hwmon/winbond,w83781d.yaml +++ b/Documentation/devicetree/bindings/hwmon/winbond,w83781d.yaml @@ -1,7 +1,6 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) %YAML 1.2 --- - $id: http://devicetree.org/schemas/hwmon/winbond,w83781d.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# From a25633a00906a59f6e4323f1b5668771c520c7db Mon Sep 17 00:00:00 2001 From: Florin Leotescu Date: Fri, 21 Mar 2025 16:33:06 +0200 Subject: [PATCH 47/49] dt-bindings: hwmon: Add Microchip emc2305 support Introduce yaml schema for Microchip emc2305 pwm fan controller. Signed-off-by: Florin Leotescu Reviewed-by: Frank Li Reviewed-by: Krzysztof Kozlowski Reviewed-by: Rob Herring (Arm) Link: https://lore.kernel.org/r/20250321143308.4008623-2-florin.leotescu@oss.nxp.com [groeck: Fixed comment line length, added 'maxItems: 1' Signed-off-by: Guenter Roeck --- .../bindings/hwmon/microchip,emc2305.yaml | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 Documentation/devicetree/bindings/hwmon/microchip,emc2305.yaml diff --git a/Documentation/devicetree/bindings/hwmon/microchip,emc2305.yaml b/Documentation/devicetree/bindings/hwmon/microchip,emc2305.yaml new file mode 100644 index 000000000000..d3f06ebc19fa --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/microchip,emc2305.yaml @@ -0,0 +1,111 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/hwmon/microchip,emc2305.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip EMC2305 SMBus compliant PWM fan controller + +maintainers: + - Michael Shych + +description: + Microchip EMC2301/2/3/5 pwm controller which supports up to five programmable + fan control circuits. + +properties: + compatible: + oneOf: + - enum: + - microchip,emc2305 + - items: + - enum: + - microchip,emc2303 + - microchip,emc2302 + - microchip,emc2301 + - const: microchip,emc2305 + + reg: + maxItems: 1 + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + '#pwm-cells': + const: 3 + description: | + Number of cells in a PWM specifier. + - cell 0: The PWM frequency + - cell 1: The PWM polarity: 0 or PWM_POLARITY_INVERTED + - cell 2: The PWM output config: + - 0 (Open-Drain) + - 1 (Push-Pull) + +patternProperties: + '^fan@[0-4]$': + $ref: fan-common.yaml# + unevaluatedProperties: false + properties: + reg: + description: + The fan number used to determine the associated PWM channel. + maxItems: 1 + + required: + - reg + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + + fan_controller: fan-controller@2f { + compatible = "microchip,emc2305"; + reg = <0x2f>; + #address-cells = <1>; + #size-cells = <0>; + #pwm-cells = <3>; + + fan@0 { + reg = <0x0>; + pwms = <&fan_controller 26000 PWM_POLARITY_INVERTED 1>; + #cooling-cells = <2>; + }; + + fan@1 { + reg = <0x1>; + pwms = <&fan_controller 26000 0 1>; + #cooling-cells = <2>; + }; + + fan@2 { + reg = <0x2>; + pwms = <&fan_controller 26000 0 1>; + #cooling-cells = <2>; + }; + + fan@3 { + reg = <0x3>; + pwms = <&fan_controller 26000 0 1>; + #cooling-cells = <2>; + }; + + fan@4 { + reg = <0x4>; + pwms = <&fan_controller 26000 0 1>; + #cooling-cells = <2>; + }; + }; + }; +... From 882bd6de1a5b4488dc0747d74420af34d419fd99 Mon Sep 17 00:00:00 2001 From: Florin Leotescu Date: Fri, 21 Mar 2025 16:33:07 +0200 Subject: [PATCH 48/49] hwmon: emc2305: Add OF support Introduce OF support for Microchip emc2305 pwm fan controller. Signed-off-by: Florin Leotescu Reviewed-by: Frank Li Link: https://lore.kernel.org/r/20250321143308.4008623-3-florin.leotescu@oss.nxp.com Signed-off-by: Guenter Roeck --- drivers/hwmon/emc2305.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/hwmon/emc2305.c b/drivers/hwmon/emc2305.c index 4d39fbd83769..f8a4c76fcadd 100644 --- a/drivers/hwmon/emc2305.c +++ b/drivers/hwmon/emc2305.c @@ -607,9 +607,16 @@ static void emc2305_remove(struct i2c_client *client) emc2305_unset_tz(dev); } +static const struct of_device_id of_emc2305_match_table[] = { + { .compatible = "microchip,emc2305", }, + {}, +}; +MODULE_DEVICE_TABLE(of, of_emc2305_match_table); + static struct i2c_driver emc2305_driver = { .driver = { .name = "emc2305", + .of_match_table = of_emc2305_match_table, }, .probe = emc2305_probe, .remove = emc2305_remove, From 2115cbeec8a3ccc69e3b7ecdf97b4472b0829cfc Mon Sep 17 00:00:00 2001 From: Florin Leotescu Date: Fri, 21 Mar 2025 16:33:08 +0200 Subject: [PATCH 49/49] hwmon: emc2305: Use devm_thermal_of_cooling_device_register Prepare the emc2305 driver to use configuration from Device Tree nodes. Switch to devm_thermal_of_cooling_device_register to simplify the cleanup procedure, allowing the removal of emc2305_unset_tz and emc2305_remove, which are no longer needed. Signed-off-by: Florin Leotescu Reviewed-by: Frank Li Link: https://lore.kernel.org/r/20250321143308.4008623-4-florin.leotescu@oss.nxp.com Signed-off-by: Guenter Roeck --- drivers/hwmon/emc2305.c | 33 ++++----------------------------- 1 file changed, 4 insertions(+), 29 deletions(-) diff --git a/drivers/hwmon/emc2305.c b/drivers/hwmon/emc2305.c index f8a4c76fcadd..234c54956a4b 100644 --- a/drivers/hwmon/emc2305.c +++ b/drivers/hwmon/emc2305.c @@ -112,8 +112,6 @@ static char *emc2305_fan_name[] = { "emc2305_fan5", }; -static void emc2305_unset_tz(struct device *dev); - static int emc2305_get_max_channel(const struct emc2305_data *data) { return data->pwm_num; @@ -293,8 +291,9 @@ static int emc2305_set_single_tz(struct device *dev, int idx) pwm = data->pwm_min[cdev_idx]; data->cdev_data[cdev_idx].cdev = - thermal_cooling_device_register(emc2305_fan_name[idx], data, - &emc2305_cooling_ops); + devm_thermal_of_cooling_device_register(dev, dev->of_node, + emc2305_fan_name[idx], data, + &emc2305_cooling_ops); if (IS_ERR(data->cdev_data[cdev_idx].cdev)) { dev_err(dev, "Failed to register cooling device %s\n", emc2305_fan_name[idx]); @@ -332,24 +331,9 @@ static int emc2305_set_tz(struct device *dev) for (i = 0; i < data->pwm_num; i++) { ret = emc2305_set_single_tz(dev, i + 1); if (ret) - goto thermal_cooling_device_register_fail; + return ret; } return 0; - -thermal_cooling_device_register_fail: - emc2305_unset_tz(dev); - return ret; -} - -static void emc2305_unset_tz(struct device *dev) -{ - struct emc2305_data *data = dev_get_drvdata(dev); - int i; - - /* Unregister cooling device. */ - for (i = 0; i < EMC2305_PWM_MAX; i++) - if (data->cdev_data[i].cdev) - thermal_cooling_device_unregister(data->cdev_data[i].cdev); } static umode_t @@ -599,14 +583,6 @@ static int emc2305_probe(struct i2c_client *client) return 0; } -static void emc2305_remove(struct i2c_client *client) -{ - struct device *dev = &client->dev; - - if (IS_REACHABLE(CONFIG_THERMAL)) - emc2305_unset_tz(dev); -} - static const struct of_device_id of_emc2305_match_table[] = { { .compatible = "microchip,emc2305", }, {}, @@ -619,7 +595,6 @@ static struct i2c_driver emc2305_driver = { .of_match_table = of_emc2305_match_table, }, .probe = emc2305_probe, - .remove = emc2305_remove, .id_table = emc2305_ids, };