From a7e03f96791e098ebde43acce6b04283f10815db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Wed, 18 Sep 2024 14:31:49 +0200 Subject: [PATCH 01/51] hwmon: Drop explicit initialization of struct i2c_device_id::driver_data to 0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These drivers don't use the driver_data member of struct i2c_device_id, so don't explicitly initialize this member. This prepares putting driver_data in an anonymous union which requires either no initialization or named designators. But it's also a nice cleanup on its own. While touching these structs, also remove commas after the sentinel entries and use a consistent indention style. Signed-off-by: Uwe Kleine-König Message-ID: <20240918123150.1540161-7-u.kleine-koenig@baylibre.com> Signed-off-by: Guenter Roeck --- drivers/hwmon/amc6821.c | 2 +- drivers/hwmon/pmbus/mp2891.c | 4 ++-- drivers/hwmon/pmbus/mp2993.c | 4 ++-- drivers/hwmon/pmbus/mp9941.c | 4 ++-- drivers/hwmon/sg2042-mcu.c | 4 ++-- drivers/hwmon/spd5118.c | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/hwmon/amc6821.c b/drivers/hwmon/amc6821.c index ac64b407ed0e..e86027f850c9 100644 --- a/drivers/hwmon/amc6821.c +++ b/drivers/hwmon/amc6821.c @@ -927,7 +927,7 @@ static int amc6821_probe(struct i2c_client *client) } static const struct i2c_device_id amc6821_id[] = { - { "amc6821", 0 }, + { "amc6821" }, { } }; diff --git a/drivers/hwmon/pmbus/mp2891.c b/drivers/hwmon/pmbus/mp2891.c index bb28b15a9103..94ab4ae5fba0 100644 --- a/drivers/hwmon/pmbus/mp2891.c +++ b/drivers/hwmon/pmbus/mp2891.c @@ -572,8 +572,8 @@ static int mp2891_probe(struct i2c_client *client) } static const struct i2c_device_id mp2891_id[] = { - {"mp2891", 0}, - {} + { "mp2891" }, + { } }; MODULE_DEVICE_TABLE(i2c, mp2891_id); diff --git a/drivers/hwmon/pmbus/mp2993.c b/drivers/hwmon/pmbus/mp2993.c index 944593e13231..63691dac2281 100644 --- a/drivers/hwmon/pmbus/mp2993.c +++ b/drivers/hwmon/pmbus/mp2993.c @@ -233,8 +233,8 @@ static int mp2993_probe(struct i2c_client *client) } static const struct i2c_device_id mp2993_id[] = { - {"mp2993", 0}, - {} + { "mp2993" }, + { } }; MODULE_DEVICE_TABLE(i2c, mp2993_id); diff --git a/drivers/hwmon/pmbus/mp9941.c b/drivers/hwmon/pmbus/mp9941.c index 543955cfce67..8ab5fc4d4092 100644 --- a/drivers/hwmon/pmbus/mp9941.c +++ b/drivers/hwmon/pmbus/mp9941.c @@ -291,8 +291,8 @@ static int mp9941_probe(struct i2c_client *client) } static const struct i2c_device_id mp9941_id[] = { - {"mp9941", 0}, - {} + { "mp9941" }, + { } }; MODULE_DEVICE_TABLE(i2c, mp9941_id); diff --git a/drivers/hwmon/sg2042-mcu.c b/drivers/hwmon/sg2042-mcu.c index 141045769354..aa3fb773602c 100644 --- a/drivers/hwmon/sg2042-mcu.c +++ b/drivers/hwmon/sg2042-mcu.c @@ -346,8 +346,8 @@ static void sg2042_mcu_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id sg2042_mcu_id[] = { - { "sg2042-hwmon-mcu", 0 }, - {}, + { "sg2042-hwmon-mcu" }, + { } }; MODULE_DEVICE_TABLE(i2c, sg2042_mcu_id); diff --git a/drivers/hwmon/spd5118.c b/drivers/hwmon/spd5118.c index fcbce5a01e55..6cee48a3e5c3 100644 --- a/drivers/hwmon/spd5118.c +++ b/drivers/hwmon/spd5118.c @@ -671,7 +671,7 @@ static int spd5118_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(spd5118_pm_ops, spd5118_suspend, spd5118_resume); static const struct i2c_device_id spd5118_id[] = { - { "spd5118", 0 }, + { "spd5118" }, { } }; MODULE_DEVICE_TABLE(i2c, spd5118_id); From de0da6ae1908b43b23782d64b4564b5ca3119f7f Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 27 Aug 2024 12:57:10 -0700 Subject: [PATCH 02/51] hwmon: (ina2xx) Add support for has_alerts configuration flag Add configuration flag indicating if the chip supports alerts and limits to prepare for adding INA260 support. Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- drivers/hwmon/ina2xx.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index f0fa6d073627..03a011c9b73d 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -129,6 +129,7 @@ enum ina2xx_ids { ina219, ina226 }; struct ina2xx_config { u16 config_default; + bool has_alerts; /* chip supports alerts and limits */ int calibration_value; int shunt_div; int bus_voltage_shift; @@ -155,6 +156,7 @@ static const struct ina2xx_config ina2xx_config[] = { .bus_voltage_shift = 3, .bus_voltage_lsb = 4000, .power_lsb_factor = 20, + .has_alerts = false, }, [ina226] = { .config_default = INA226_CONFIG_DEFAULT, @@ -163,6 +165,7 @@ static const struct ina2xx_config ina2xx_config[] = { .bus_voltage_shift = 0, .bus_voltage_lsb = 1250, .power_lsb_factor = 25, + .has_alerts = true, }, }; @@ -624,6 +627,7 @@ static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type u32 attr, int channel) { const struct ina2xx_data *data = _data; + bool has_alerts = data->config->has_alerts; enum ina2xx_ids chip = data->chip; switch (type) { @@ -633,12 +637,12 @@ static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type return 0444; case hwmon_in_lcrit: case hwmon_in_crit: - if (chip == ina226) + if (has_alerts) return 0644; break; case hwmon_in_lcrit_alarm: case hwmon_in_crit_alarm: - if (chip == ina226) + if (has_alerts) return 0444; break; default: @@ -651,12 +655,12 @@ static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type return 0444; case hwmon_curr_lcrit: case hwmon_curr_crit: - if (chip == ina226) + if (has_alerts) return 0644; break; case hwmon_curr_lcrit_alarm: case hwmon_curr_crit_alarm: - if (chip == ina226) + if (has_alerts) return 0444; break; default: @@ -668,11 +672,11 @@ static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type case hwmon_power_input: return 0444; case hwmon_power_crit: - if (chip == ina226) + if (has_alerts) return 0644; break; case hwmon_power_crit_alarm: - if (chip == ina226) + if (has_alerts) return 0444; break; default: @@ -802,7 +806,7 @@ static int ina2xx_init(struct device *dev, struct ina2xx_data *data) if (ret < 0) return ret; - if (data->chip == ina226) { + if (data->config->has_alerts) { bool active_high = device_property_read_bool(dev, "ti,alert-polarity-active-high"); regmap_update_bits(regmap, INA226_MASK_ENABLE, From 70fb84a109c639637f0636281dbdb21ed8ffb000 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 27 Aug 2024 10:23:10 -0700 Subject: [PATCH 03/51] hwmon: (ina2xx) Add support for INA260 INA260 is similar to other chips of the series, except it has an internal shunt resistor. The calibration register is therefore not present. Also, the current register address was changed, though that does not matter for the driver since the shunt voltage register (which is now the current register) value is already used to read the current. Cc: Loic Guegan Reviewed-by: Tzung-Bi Shih Signed-off-by: Guenter Roeck --- Documentation/hwmon/ina2xx.rst | 23 +++++++++++++++---- drivers/hwmon/Kconfig | 5 ++-- drivers/hwmon/ina2xx.c | 42 ++++++++++++++++++++++++++++++---- 3 files changed, 59 insertions(+), 11 deletions(-) diff --git a/Documentation/hwmon/ina2xx.rst b/Documentation/hwmon/ina2xx.rst index 7f1939b40f74..1ce161e6c0bf 100644 --- a/Documentation/hwmon/ina2xx.rst +++ b/Documentation/hwmon/ina2xx.rst @@ -53,6 +53,16 @@ Supported chips: https://www.ti.com/ + * Texas Instruments INA260 + + Prefix: 'ina260' + + Addresses: I2C 0x40 - 0x4f + + Datasheet: Publicly available at the Texas Instruments website + + https://www.ti.com/ + Author: Lothar Felten Description @@ -72,6 +82,9 @@ INA230 and INA231 are high or low side current shunt and power monitors with an I2C interface. The chips monitor both a shunt voltage drop and bus supply voltage. +INA260 is a high or low side current and power monitor with integrated shunt +resistor. + The shunt value in micro-ohms can be set via platform data or device tree at compile-time or via the shunt_resistor attribute in sysfs at run-time. Please refer to the Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml for bindings @@ -87,16 +100,16 @@ The actual programmed interval may vary from the desired value. General sysfs entries --------------------- -======================= =============================== +======================= =============================================== in0_input Shunt voltage(mV) channel in1_input Bus voltage(mV) channel curr1_input Current(mA) measurement channel power1_input Power(uW) measurement channel -shunt_resistor Shunt resistance(uOhm) channel -======================= =============================== +shunt_resistor Shunt resistance(uOhm) channel (not for ina260) +======================= =============================================== -Sysfs entries for ina226, ina230 and ina231 only ------------------------------------------------- +Additional sysfs entries for ina226, ina230, ina231, and ina260 +--------------------------------------------------------------- ======================= ==================================================== curr1_lcrit Critical low current diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 08a3c863f80a..6a3385598cc6 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -2167,11 +2167,12 @@ config SENSORS_INA2XX select REGMAP_I2C help If you say yes here you get support for INA219, INA220, INA226, - INA230, and INA231 power monitor chips. + INA230, INA231, and INA260 power monitor chips. The INA2xx driver is configured for the default configuration of the part as described in the datasheet. - Default value for Rshunt is 10 mOhms. + Default value for Rshunt is 10 mOhms except for INA260 which has an + internal 2 mOhm shunt resistor. This driver can also be built as a module. If so, the module will be called ina2xx. diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index 03a011c9b73d..cecc80a41a97 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -56,12 +56,14 @@ /* settings - depend on use case */ #define INA219_CONFIG_DEFAULT 0x399F /* PGA=8 */ #define INA226_CONFIG_DEFAULT 0x4527 /* averages=16 */ +#define INA260_CONFIG_DEFAULT 0x6527 /* averages=16 */ /* worst case is 68.10 ms (~14.6Hz, ina219) */ #define INA2XX_CONVERSION_RATE 15 #define INA2XX_MAX_DELAY 69 /* worst case delay in ms */ #define INA2XX_RSHUNT_DEFAULT 10000 +#define INA260_RSHUNT 2000 /* bit mask for reading the averaging setting in the configuration register */ #define INA226_AVG_RD_MASK GENMASK(11, 9) @@ -125,11 +127,12 @@ static const struct regmap_config ina2xx_regmap_config = { .writeable_reg = ina2xx_writeable_reg, }; -enum ina2xx_ids { ina219, ina226 }; +enum ina2xx_ids { ina219, ina226, ina260 }; struct ina2xx_config { u16 config_default; bool has_alerts; /* chip supports alerts and limits */ + bool has_ishunt; /* chip has internal shunt resistor */ int calibration_value; int shunt_div; int bus_voltage_shift; @@ -157,6 +160,7 @@ static const struct ina2xx_config ina2xx_config[] = { .bus_voltage_lsb = 4000, .power_lsb_factor = 20, .has_alerts = false, + .has_ishunt = false, }, [ina226] = { .config_default = INA226_CONFIG_DEFAULT, @@ -166,6 +170,16 @@ static const struct ina2xx_config ina2xx_config[] = { .bus_voltage_lsb = 1250, .power_lsb_factor = 25, .has_alerts = true, + .has_ishunt = false, + }, + [ina260] = { + .config_default = INA260_CONFIG_DEFAULT, + .shunt_div = 400, + .bus_voltage_shift = 0, + .bus_voltage_lsb = 1250, + .power_lsb_factor = 8, + .has_alerts = true, + .has_ishunt = true, }, }; @@ -257,6 +271,15 @@ static int ina2xx_read_init(struct device *dev, int reg, long *val) unsigned int regval; int ret, retry; + if (data->config->has_ishunt) { + /* No calibration needed */ + ret = regmap_read(regmap, reg, ®val); + if (ret < 0) + return ret; + *val = ina2xx_get_value(data, reg, regval); + return 0; + } + for (retry = 5; retry; retry--) { ret = regmap_read(regmap, reg, ®val); if (ret < 0) @@ -686,7 +709,7 @@ static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type case hwmon_chip: switch (attr) { case hwmon_chip_update_interval: - if (chip == ina226) + if (chip == ina226 || chip == ina260) return 0644; break; default: @@ -795,7 +818,9 @@ static int ina2xx_init(struct device *dev, struct ina2xx_data *data) u32 shunt; int ret; - if (device_property_read_u32(dev, "shunt-resistor", &shunt) < 0) + if (data->config->has_ishunt) + shunt = INA260_RSHUNT; + else if (device_property_read_u32(dev, "shunt-resistor", &shunt) < 0) shunt = INA2XX_RSHUNT_DEFAULT; ret = ina2xx_set_shunt(data, shunt); @@ -815,6 +840,9 @@ static int ina2xx_init(struct device *dev, struct ina2xx_data *data) FIELD_PREP(INA226_ALERT_POLARITY, active_high)); } + if (data->config->has_ishunt) + return 0; + /* * Calibration register is set to the best value, which eliminates * truncation errors on calculating current register in hardware. @@ -860,7 +888,8 @@ static int ina2xx_probe(struct i2c_client *client) hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, &ina2xx_chip_info, - ina2xx_groups); + data->config->has_ishunt ? + NULL : ina2xx_groups); if (IS_ERR(hwmon_dev)) return PTR_ERR(hwmon_dev); @@ -876,6 +905,7 @@ static const struct i2c_device_id ina2xx_id[] = { { "ina226", ina226 }, { "ina230", ina226 }, { "ina231", ina226 }, + { "ina260", ina260 }, { } }; MODULE_DEVICE_TABLE(i2c, ina2xx_id); @@ -901,6 +931,10 @@ static const struct of_device_id __maybe_unused ina2xx_of_match[] = { .compatible = "ti,ina231", .data = (void *)ina226 }, + { + .compatible = "ti,ina260", + .data = (void *)ina260 + }, { }, }; MODULE_DEVICE_TABLE(of, ina2xx_of_match); From ff140456b5fbf03cbd04277c21d4fcdc5974e97b Mon Sep 17 00:00:00 2001 From: Shen Lichuan Date: Thu, 19 Sep 2024 14:49:39 +0800 Subject: [PATCH 04/51] hwmon: (pmbus/mpq8785) Convert comma to semicolon To ensure code clarity and prevent potential errors, use ';' instead of ',' as a statement separator in the function mpq8785_identify. Signed-off-by: Shen Lichuan Message-ID: <20240919064939.3282-1-shenlichuan@vivo.com> Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/mpq8785.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/pmbus/mpq8785.c b/drivers/hwmon/pmbus/mpq8785.c index 7f87e117b49d..0d16491cd770 100644 --- a/drivers/hwmon/pmbus/mpq8785.c +++ b/drivers/hwmon/pmbus/mpq8785.c @@ -22,7 +22,7 @@ static int mpq8785_identify(struct i2c_client *client, break; case 1: case 2: - info->format[PSC_VOLTAGE_OUT] = direct, + info->format[PSC_VOLTAGE_OUT] = direct; info->m[PSC_VOLTAGE_OUT] = 64; info->b[PSC_VOLTAGE_OUT] = 0; info->R[PSC_VOLTAGE_OUT] = 1; From 247a80fde14edc84feb8bae707e60a55993ee636 Mon Sep 17 00:00:00 2001 From: Farouk Bouabid Date: Fri, 6 Sep 2024 17:54:14 +0200 Subject: [PATCH 05/51] dt-bindings: hwmon: add support for ti,amc6821 Add dt-bindings for amc6821 intelligent temperature monitor and pulse-width modulation (PWM) fan controller. Reviewed-by: Rob Herring (Arm) Signed-off-by: Farouk Bouabid Message-ID: <20240906-dev-mule-i2c-mux-v8-3-dbd28a150e41@cherry.de> Signed-off-by: Guenter Roeck --- .../devicetree/bindings/hwmon/ti,amc6821.yaml | 86 +++++++++++++++++++ .../devicetree/bindings/trivial-devices.yaml | 2 - 2 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 Documentation/devicetree/bindings/hwmon/ti,amc6821.yaml diff --git a/Documentation/devicetree/bindings/hwmon/ti,amc6821.yaml b/Documentation/devicetree/bindings/hwmon/ti,amc6821.yaml new file mode 100644 index 000000000000..5d33f1a23d03 --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/ti,amc6821.yaml @@ -0,0 +1,86 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/hwmon/ti,amc6821.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: AMC6821 Intelligent Temperature Monitor and PWM Fan Controller + +maintainers: + - Farouk Bouabid + - Quentin Schulz + +description: + Intelligent temperature monitor and pulse-width modulation (PWM) fan + controller. + +properties: + compatible: + oneOf: + - items: + - const: tsd,mule + - const: ti,amc6821 + - const: ti,amc6821 + + reg: + maxItems: 1 + + i2c-mux: + type: object + +required: + - compatible + - reg + +if: + properties: + compatible: + contains: + const: tsd,mule + +then: + required: + - i2c-mux + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + fan@18 { + compatible = "ti,amc6821"; + reg = <0x18>; + }; + }; + + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + fan@18 { + compatible = "tsd,mule", "ti,amc6821"; + reg = <0x18>; + + i2c-mux { + compatible = "tsd,mule-i2c-mux"; + #address-cells = <1>; + #size-cells = <0>; + + i2c@0 { + reg = <0x0>; + #address-cells = <1>; + #size-cells = <0>; + + rtc@6f { + compatible = "isil,isl1208"; + reg = <0x6f>; + }; + }; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/trivial-devices.yaml b/Documentation/devicetree/bindings/trivial-devices.yaml index 9bf0fb17a05e..b803581ba927 100644 --- a/Documentation/devicetree/bindings/trivial-devices.yaml +++ b/Documentation/devicetree/bindings/trivial-devices.yaml @@ -357,8 +357,6 @@ properties: - swir,mangoh-iotport-spi # Ambient Light Sensor with SMBUS/Two Wire Serial Interface - taos,tsl2550 - # Temperature Monitoring and Fan Control - - ti,amc6821 # Temperature and humidity sensor with i2c interface - ti,hdc1000 # Temperature and humidity sensor with i2c interface From 8f38236de689afe1ebba6ff25676f0631818ef37 Mon Sep 17 00:00:00 2001 From: Farouk Bouabid Date: Fri, 6 Sep 2024 17:54:15 +0200 Subject: [PATCH 06/51] hwmon: (amc6821) add support for tsd,mule Theobroma Systems Mule is an MCU that emulates a set of I2C devices, among which is an amc6821 and other devices that are reachable through an I2C-mux. The devices on the mux can be selected by writing the appropriate device number to an I2C config register (amc6821: reg 0xff) Implement "tsd,mule" compatible to instantiate the I2C-mux platform device when probing the amc6821. Reviewed-by: Guenter Roeck Signed-off-by: Farouk Bouabid Message-ID: <20240906-dev-mule-i2c-mux-v8-4-dbd28a150e41@cherry.de> Signed-off-by: Guenter Roeck --- drivers/hwmon/amc6821.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/amc6821.c b/drivers/hwmon/amc6821.c index e86027f850c9..1e3c6acd8974 100644 --- a/drivers/hwmon/amc6821.c +++ b/drivers/hwmon/amc6821.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -893,7 +894,6 @@ static bool amc6821_volatile_reg(struct device *dev, unsigned int reg) static const struct regmap_config amc6821_regmap_config = { .reg_bits = 8, .val_bits = 8, - .max_register = AMC6821_REG_CONF3, .volatile_reg = amc6821_volatile_reg, .cache_type = REGCACHE_MAPLE, }; @@ -920,6 +920,13 @@ static int amc6821_probe(struct i2c_client *client) if (err) return err; + if (of_device_is_compatible(dev->of_node, "tsd,mule")) { + err = devm_of_platform_populate(dev); + if (err) + return dev_err_probe(dev, err, + "Failed to create sub-devices\n"); + } + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, &amc6821_chip_info, amc6821_groups); @@ -937,6 +944,9 @@ static const struct of_device_id __maybe_unused amc6821_of_match[] = { { .compatible = "ti,amc6821", }, + { + .compatible = "tsd,mule", + }, { } }; From edeed7b6666cd02bd7e370b47573d95bc9b0f51c Mon Sep 17 00:00:00 2001 From: "Everest K.C." Date: Mon, 30 Sep 2024 19:15:17 -0600 Subject: [PATCH 07/51] hwmon: (max31827) Fix spelling errors reported by codespell Below mentioned spelling errors reported by codesepll were fixed: respresents ==> represents signifcant ==> significant bandwitdh ==> bandwidth Signed-off-by: Everest K.C. Message-ID: <20241001011521.80982-1-everestkc@everestkc.com.np> Signed-off-by: Guenter Roeck --- Documentation/hwmon/max31827.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/hwmon/max31827.rst b/Documentation/hwmon/max31827.rst index 9c11a9518c67..6cc5088b26b7 100644 --- a/Documentation/hwmon/max31827.rst +++ b/Documentation/hwmon/max31827.rst @@ -136,7 +136,7 @@ PEC Support When reading a register value, the PEC byte is computed and sent by the chip. -PEC on word data transaction respresents a signifcant increase in bandwitdh +PEC on word data transaction represents a significant increase in bandwidth usage (+33% for both write and reads) in normal conditions. Since this operation implies there will be an extra delay to each From 79bc0af904db647979c735563299c9b0d820e432 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Thu, 10 Oct 2024 21:35:42 +0200 Subject: [PATCH 08/51] hwmon: Add static visibility member to struct hwmon_ops Several drivers return the same static value in their is_visible callback, what results in code duplication. Therefore add an option for drivers to specify a static visibility directly. Signed-off-by: Heiner Kallweit Message-ID: <89690b81-2c73-47ae-9ae9-45c77b45ca0c@gmail.com> groeck: Renamed hwmon_ops_is_visible -> hwmon_is_visible Signed-off-by: Guenter Roeck --- drivers/hwmon/hwmon.c | 19 +++++++++++++++---- include/linux/hwmon.h | 5 ++++- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index 9c35c4d0369d..49b366254529 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -145,6 +145,17 @@ static const struct class hwmon_class = { static DEFINE_IDA(hwmon_ida); +static umode_t hwmon_is_visible(const struct hwmon_ops *ops, + const void *drvdata, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + if (ops->visible) + return ops->visible; + + return ops->is_visible(drvdata, type, attr, channel); +} + /* Thermal zone handling */ /* @@ -267,8 +278,8 @@ static int hwmon_thermal_register_sensors(struct device *dev) int err; if (!(info[i]->config[j] & HWMON_T_INPUT) || - !chip->ops->is_visible(drvdata, hwmon_temp, - hwmon_temp_input, j)) + !hwmon_is_visible(chip->ops, drvdata, hwmon_temp, + hwmon_temp_input, j)) continue; err = hwmon_thermal_add_sensor(dev, j); @@ -506,7 +517,7 @@ static struct attribute *hwmon_genattr(const void *drvdata, const char *name; bool is_string = is_string_attr(type, attr); - mode = ops->is_visible(drvdata, type, attr, index); + mode = hwmon_is_visible(ops, drvdata, type, attr, index); if (!mode) return ERR_PTR(-ENOENT); @@ -1033,7 +1044,7 @@ hwmon_device_register_with_info(struct device *dev, const char *name, if (!dev || !name || !chip) return ERR_PTR(-EINVAL); - if (!chip->ops || !chip->ops->is_visible || !chip->info) + if (!chip->ops || !(chip->ops->visible || chip->ops->is_visible) || !chip->info) return ERR_PTR(-EINVAL); return __hwmon_device_register(dev, name, drvdata, chip, extra_groups); diff --git a/include/linux/hwmon.h b/include/linux/hwmon.h index 5c6a421ad580..3a63dff62d03 100644 --- a/include/linux/hwmon.h +++ b/include/linux/hwmon.h @@ -368,7 +368,9 @@ enum hwmon_intrusion_attributes { /** * struct hwmon_ops - hwmon device operations - * @is_visible: Callback to return attribute visibility. Mandatory. + * @visible: Static visibility. If non-zero, 'is_visible' is ignored. + * @is_visible: Callback to return attribute visibility. Mandatory unless + * 'visible' is non-zero. * Parameters are: * @const void *drvdata: * Pointer to driver-private data structure passed @@ -412,6 +414,7 @@ enum hwmon_intrusion_attributes { * The function returns 0 on success or a negative error number. */ struct hwmon_ops { + umode_t visible; umode_t (*is_visible)(const void *drvdata, enum hwmon_sensor_types type, u32 attr, int channel); int (*read)(struct device *dev, enum hwmon_sensor_types type, From 7506ebcd662b868780774d191a7c024c18c557a8 Mon Sep 17 00:00:00 2001 From: Naresh Solanki Date: Mon, 7 Oct 2024 14:34:24 +0530 Subject: [PATCH 09/51] hwmon: (max6639) : Configure based on DT property Remove platform data & initialize with defaults configuration & overwrite based on DT properties. Signed-off-by: Naresh Solanki Message-ID: <20241007090426.811736-1-naresh.solanki@9elements.com> [groeck: Dropped some unnecessary empty lines] Signed-off-by: Guenter Roeck --- drivers/hwmon/max6639.c | 83 +++++++++++++++++++-------- include/linux/platform_data/max6639.h | 15 ----- 2 files changed, 60 insertions(+), 38 deletions(-) delete mode 100644 include/linux/platform_data/max6639.h diff --git a/drivers/hwmon/max6639.c b/drivers/hwmon/max6639.c index c955b0f3a8d3..32b4d54b2076 100644 --- a/drivers/hwmon/max6639.c +++ b/drivers/hwmon/max6639.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include @@ -531,14 +530,49 @@ static int rpm_range_to_reg(int range) return 1; /* default: 4000 RPM */ } +static int max6639_probe_child_from_dt(struct i2c_client *client, + struct device_node *child, + struct max6639_data *data) + +{ + struct device *dev = &client->dev; + u32 i; + int err, val; + + err = of_property_read_u32(child, "reg", &i); + if (err) { + dev_err(dev, "missing reg property of %pOFn\n", child); + return err; + } + + if (i > 1) { + dev_err(dev, "Invalid fan index reg %d\n", i); + return -EINVAL; + } + + err = of_property_read_u32(child, "pulses-per-revolution", &val); + if (!err) { + if (val < 1 || val > 5) { + dev_err(dev, "invalid pulses-per-revolution %d of %pOFn\n", val, child); + return -EINVAL; + } + data->ppr[i] = val; + } + + err = of_property_read_u32(child, "max-rpm", &val); + if (!err) + data->rpm_range[i] = rpm_range_to_reg(val); + + return 0; +} + static int max6639_init_client(struct i2c_client *client, struct max6639_data *data) { - struct max6639_platform_data *max6639_info = - dev_get_platdata(&client->dev); - int i; - int rpm_range = 1; /* default: 4000 RPM */ - int err, ppr; + struct device *dev = &client->dev; + const struct device_node *np = dev->of_node; + struct device_node *child; + int i, err; /* Reset chip to default values, see below for GCONFIG setup */ err = regmap_write(data->regmap, MAX6639_REG_GCONFIG, MAX6639_GCONFIG_POR); @@ -546,21 +580,29 @@ static int max6639_init_client(struct i2c_client *client, return err; /* Fans pulse per revolution is 2 by default */ - if (max6639_info && max6639_info->ppr > 0 && - max6639_info->ppr < 5) - ppr = max6639_info->ppr; - else - ppr = 2; + data->ppr[0] = 2; + data->ppr[1] = 2; - data->ppr[0] = ppr; - data->ppr[1] = ppr; + /* default: 4000 RPM */ + data->rpm_range[0] = 1; + data->rpm_range[1] = 1; - if (max6639_info) - rpm_range = rpm_range_to_reg(max6639_info->rpm_range); - data->rpm_range[0] = rpm_range; - data->rpm_range[1] = rpm_range; + for_each_child_of_node(np, child) { + if (strcmp(child->name, "fan")) + continue; + + err = max6639_probe_child_from_dt(client, child, data); + if (err) { + of_node_put(child); + return err; + } + } for (i = 0; i < MAX6639_NUM_CHANNELS; i++) { + err = regmap_set_bits(data->regmap, MAX6639_REG_OUTPUT_MASK, BIT(1 - i)); + if (err) + return err; + /* Set Fan pulse per revolution */ err = max6639_set_ppr(data, i, data->ppr[i]); if (err) @@ -573,12 +615,7 @@ static int max6639_init_client(struct i2c_client *client, return err; /* Fans PWM polarity high by default */ - if (max6639_info) { - if (max6639_info->pwm_polarity == 0) - err = regmap_write(data->regmap, MAX6639_REG_FAN_CONFIG2a(i), 0x00); - else - err = regmap_write(data->regmap, MAX6639_REG_FAN_CONFIG2a(i), 0x02); - } + err = regmap_write(data->regmap, MAX6639_REG_FAN_CONFIG2a(i), 0x00); if (err) return err; diff --git a/include/linux/platform_data/max6639.h b/include/linux/platform_data/max6639.h deleted file mode 100644 index 65bfdb4fdc15..000000000000 --- a/include/linux/platform_data/max6639.h +++ /dev/null @@ -1,15 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _LINUX_MAX6639_H -#define _LINUX_MAX6639_H - -#include - -/* platform data for the MAX6639 temperature sensor and fan control */ - -struct max6639_platform_data { - bool pwm_polarity; /* Polarity low (0) or high (1, default) */ - int ppr; /* Pulses per rotation 1..4 (default == 2) */ - int rpm_range; /* 2000, 4000 (default), 8000 or 16000 */ -}; - -#endif /* _LINUX_MAX6639_H */ From 5deb42295db7296e9c47f0647cc1eadcd2b4fb49 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Fri, 11 Oct 2024 21:55:28 +0200 Subject: [PATCH 10/51] hwmon: (i5500_temp) Simplify specifying static visibility attribute Use new member visible of struct hwmon_ops to simplify specifying the static attribute visibility. Signed-off-by: Heiner Kallweit Message-ID: <2b1f2778-1127-4979-b02d-f75e16497ad7@gmail.com> Signed-off-by: Guenter Roeck --- drivers/hwmon/i5500_temp.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/hwmon/i5500_temp.c b/drivers/hwmon/i5500_temp.c index 7b00b38c7f7b..2a530da21949 100644 --- a/drivers/hwmon/i5500_temp.c +++ b/drivers/hwmon/i5500_temp.c @@ -29,12 +29,6 @@ #define REG_CTCTRL 0xF7 #define REG_TSTIMER 0xF8 -static umode_t i5500_is_visible(const void *drvdata, enum hwmon_sensor_types type, u32 attr, - int channel) -{ - return 0444; -} - static int i5500_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val) { @@ -84,7 +78,7 @@ static int i5500_read(struct device *dev, enum hwmon_sensor_types type, u32 attr } static const struct hwmon_ops i5500_ops = { - .is_visible = i5500_is_visible, + .visible = 0444, .read = i5500_read, }; From 041e20ee60409de444e71981ad6941393fb5d7a3 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Fri, 11 Oct 2024 21:56:52 +0200 Subject: [PATCH 11/51] hwmon: (surface_fan) Simplify specifying static visibility attribute Use new member visible of struct hwmon_ops to simplify specifying the static attribute visibility. Signed-off-by: Heiner Kallweit Message-ID: Signed-off-by: Guenter Roeck --- drivers/hwmon/surface_fan.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/hwmon/surface_fan.c b/drivers/hwmon/surface_fan.c index de3c5a2409c6..aafb4ac92e6c 100644 --- a/drivers/hwmon/surface_fan.c +++ b/drivers/hwmon/surface_fan.c @@ -18,14 +18,6 @@ SSAM_DEFINE_SYNC_REQUEST_CL_R(__ssam_fan_rpm_get, __le16, { .command_id = 0x01, }); -// hwmon -static umode_t surface_fan_hwmon_is_visible(const void *drvdata, - enum hwmon_sensor_types type, u32 attr, - int channel) -{ - return 0444; -} - static int surface_fan_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val) @@ -49,7 +41,7 @@ static const struct hwmon_channel_info *const surface_fan_info[] = { }; static const struct hwmon_ops surface_fan_hwmon_ops = { - .is_visible = surface_fan_hwmon_is_visible, + .visible = 0444, .read = surface_fan_hwmon_read, }; From 10963cef5f510a847939d50c455263618cfe8a70 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Fri, 11 Oct 2024 21:58:09 +0200 Subject: [PATCH 12/51] hwmon: (sl28cpld) Simplify specifying static visibility attribute Use new member visible of struct hwmon_ops to simplify specifying the static attribute visibility. Signed-off-by: Heiner Kallweit Message-ID: <5c26d8cf-d6dc-46c5-be7c-fd8207b3f177@gmail.com> Signed-off-by: Guenter Roeck --- drivers/hwmon/sl28cpld-hwmon.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/hwmon/sl28cpld-hwmon.c b/drivers/hwmon/sl28cpld-hwmon.c index e020f25c9300..454cc844fb9d 100644 --- a/drivers/hwmon/sl28cpld-hwmon.c +++ b/drivers/hwmon/sl28cpld-hwmon.c @@ -23,13 +23,6 @@ struct sl28cpld_hwmon { u32 offset; }; -static umode_t sl28cpld_hwmon_is_visible(const void *data, - enum hwmon_sensor_types type, - u32 attr, int channel) -{ - return 0444; -} - static int sl28cpld_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *input) @@ -73,7 +66,7 @@ static const struct hwmon_channel_info * const sl28cpld_hwmon_info[] = { }; static const struct hwmon_ops sl28cpld_hwmon_ops = { - .is_visible = sl28cpld_hwmon_is_visible, + .visible = 0444, .read = sl28cpld_hwmon_read, }; From 9df39924c47a95a688762616cb3183610985ab9c Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Fri, 11 Oct 2024 21:59:27 +0200 Subject: [PATCH 13/51] hwmon: (gsc) Simplify specifying static visibility attribute Use new member visible of struct hwmon_ops to simplify specifying the static attribute visibility. Signed-off-by: Heiner Kallweit Message-ID: Signed-off-by: Guenter Roeck --- drivers/hwmon/gsc-hwmon.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/hwmon/gsc-hwmon.c b/drivers/hwmon/gsc-hwmon.c index 4514f3ed90cc..14a6385cd7cc 100644 --- a/drivers/hwmon/gsc-hwmon.c +++ b/drivers/hwmon/gsc-hwmon.c @@ -231,15 +231,8 @@ gsc_hwmon_read_string(struct device *dev, enum hwmon_sensor_types type, return 0; } -static umode_t -gsc_hwmon_is_visible(const void *_data, enum hwmon_sensor_types type, u32 attr, - int ch) -{ - return 0444; -} - static const struct hwmon_ops gsc_hwmon_ops = { - .is_visible = gsc_hwmon_is_visible, + .visible = 0444, .read = gsc_hwmon_read, .read_string = gsc_hwmon_read_string, }; From 1d92c936ca18d60638e1a8807d3e3d492c35d65a Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Fri, 11 Oct 2024 22:00:49 +0200 Subject: [PATCH 14/51] hwmon: (powerz) Simplify specifying static visibility attribute MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use new member visible of struct hwmon_ops to simplify specifying the static attribute visibility. Signed-off-by: Heiner Kallweit Acked-by: Thomas Weißschuh Message-ID: Signed-off-by: Guenter Roeck --- drivers/hwmon/powerz.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/hwmon/powerz.c b/drivers/hwmon/powerz.c index cfb635f94d66..4e663d5b4e33 100644 --- a/drivers/hwmon/powerz.c +++ b/drivers/hwmon/powerz.c @@ -54,12 +54,6 @@ static const struct hwmon_channel_info *const powerz_info[] = { NULL }; -static umode_t powerz_is_visible(const void *data, enum hwmon_sensor_types type, - u32 attr, int channel) -{ - return 0444; -} - static int powerz_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, const char **str) { @@ -201,7 +195,7 @@ out: } static const struct hwmon_ops powerz_hwmon_ops = { - .is_visible = powerz_is_visible, + .visible = 0444, .read = powerz_read, .read_string = powerz_read_string, }; From 39a8cf342bc2ad656b4bad536f930e2aa8562151 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Fri, 11 Oct 2024 22:01:57 +0200 Subject: [PATCH 15/51] hwmon: (raspberrypi) Simplify specifying static visibility attribute Use new member visible of struct hwmon_ops to simplify specifying the static attribute visibility. Signed-off-by: Heiner Kallweit Message-ID: <4e8893a1-b080-4676-97b9-a48ac9ead28a@gmail.com> Signed-off-by: Guenter Roeck --- drivers/hwmon/raspberrypi-hwmon.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/hwmon/raspberrypi-hwmon.c b/drivers/hwmon/raspberrypi-hwmon.c index 65cc52e47db0..10ef1e1f9458 100644 --- a/drivers/hwmon/raspberrypi-hwmon.c +++ b/drivers/hwmon/raspberrypi-hwmon.c @@ -81,12 +81,6 @@ static int rpi_read(struct device *dev, enum hwmon_sensor_types type, return 0; } -static umode_t rpi_is_visible(const void *_data, enum hwmon_sensor_types type, - u32 attr, int channel) -{ - return 0444; -} - static const struct hwmon_channel_info * const rpi_info[] = { HWMON_CHANNEL_INFO(in, HWMON_I_LCRIT_ALARM), @@ -94,7 +88,7 @@ static const struct hwmon_channel_info * const rpi_info[] = { }; static const struct hwmon_ops rpi_hwmon_ops = { - .is_visible = rpi_is_visible, + .visible = 0444, .read = rpi_read, }; From 182137ecfb37509127fc94b1edb2e2b5dc6fccf1 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Fri, 11 Oct 2024 22:03:13 +0200 Subject: [PATCH 16/51] hwmon: (intel-m10-bmc) Simplify specifying static visibility attribute Use new member visible of struct hwmon_ops to simplify specifying the static attribute visibility. Signed-off-by: Heiner Kallweit Message-ID: <8ef99170-b37d-4c9a-b3bf-59f4ea76cf29@gmail.com> Signed-off-by: Guenter Roeck --- drivers/hwmon/intel-m10-bmc-hwmon.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/hwmon/intel-m10-bmc-hwmon.c b/drivers/hwmon/intel-m10-bmc-hwmon.c index 96397ae6ff18..e221f2c1f332 100644 --- a/drivers/hwmon/intel-m10-bmc-hwmon.c +++ b/drivers/hwmon/intel-m10-bmc-hwmon.c @@ -565,13 +565,6 @@ static const struct m10bmc_hwmon_board_data n6000bmc_hwmon_bdata = { .hinfo = n6000bmc_hinfo, }; -static umode_t -m10bmc_hwmon_is_visible(const void *data, enum hwmon_sensor_types type, - u32 attr, int channel) -{ - return 0444; -} - static const struct m10bmc_sdata * find_sensor_data(struct m10bmc_hwmon *hw, enum hwmon_sensor_types type, int channel) @@ -729,7 +722,7 @@ static int m10bmc_hwmon_read_string(struct device *dev, } static const struct hwmon_ops m10bmc_hwmon_ops = { - .is_visible = m10bmc_hwmon_is_visible, + .visible = 0444, .read = m10bmc_hwmon_read, .read_string = m10bmc_hwmon_read_string, }; From 83cca55a63344f0d43102badc0e77e715180153f Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Fri, 11 Oct 2024 22:04:31 +0200 Subject: [PATCH 17/51] hwmon: (nzxt-kraken2) Simplify specifying static visibility attribute Use new member visible of struct hwmon_ops to simplify specifying the static attribute visibility. Signed-off-by: Heiner Kallweit Message-ID: <1ac2be2d-df4f-455a-900d-821fc7bd12c4@gmail.com> Acked-by: Jonas Malaco Signed-off-by: Guenter Roeck --- drivers/hwmon/nzxt-kraken2.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/hwmon/nzxt-kraken2.c b/drivers/hwmon/nzxt-kraken2.c index ed38645a1dc2..034698232758 100644 --- a/drivers/hwmon/nzxt-kraken2.c +++ b/drivers/hwmon/nzxt-kraken2.c @@ -35,13 +35,6 @@ struct kraken2_priv_data { unsigned long updated; /* jiffies */ }; -static umode_t kraken2_is_visible(const void *data, - enum hwmon_sensor_types type, - u32 attr, int channel) -{ - return 0444; -} - static int kraken2_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val) { @@ -81,7 +74,7 @@ static int kraken2_read_string(struct device *dev, enum hwmon_sensor_types type, } static const struct hwmon_ops kraken2_hwmon_ops = { - .is_visible = kraken2_is_visible, + .visible = 0444, .read = kraken2_read, .read_string = kraken2_read_string, }; From 0eed6fc3d2b9eef8074dc799176e0c1e78a3cc3f Mon Sep 17 00:00:00 2001 From: Antoni Pokusinski Date: Mon, 30 Sep 2024 22:53:47 +0200 Subject: [PATCH 18/51] hwmon: (sht4x): add heater support Add support for manipulating the internal heater of sht4x devices. Enabling the heater removes condensed water from the sensor surface which disturbs the relative humidity measurements. The heater can operate at three heating levels (20, 110 or 200 milliwatts). Also, two heating durations may be selected (0.1 or 1s). Once the heating time elapses the heater is automatically switched off. Changes since v3: * struct sht4x_data: add heating_complete timestamp * struct sht4x_data: add data_pending flag * heater_enable_store: return -EINVAL if input != 1 * heater_enable_store: check for data->heating_complete and update it * heater_enable_store: set data_pending flag after heating request * sht4x_read_values: msleep if heating in progress * sht4x_read_values: dont send measurement request if data_pending * heater_enable attr: make it RW * Documentation: update info about heater_enable attr Changes since v2: * heater_enable_store: remove unnecessary if * Documentation: remove incorrect info about turning off the heater * be more specific in the patch description Changes since v1: * explain the use case of the new attributes set * heater_enable attr: make it write-only * heater_enable_store: define cmd as u8 instead of u8* * heater_enable_store: remove unreachable data path * heater_enable_store: remove unnecessary lock * heater_enable_store: call i2c_master_send only if status==true * define attributes as DEVICE_ATTR_* instead of SENSOR_DEVICE_ATTR_* Signed-off-by: Antoni Pokusinski Message-ID: <20240930205346.2147-1-apokusinski01@gmail.com> Signed-off-by: Guenter Roeck --- Documentation/hwmon/sht4x.rst | 14 +++ drivers/hwmon/sht4x.c | 184 ++++++++++++++++++++++++++++++++-- 2 files changed, 189 insertions(+), 9 deletions(-) diff --git a/Documentation/hwmon/sht4x.rst b/Documentation/hwmon/sht4x.rst index daf21e763425..ba094ad0e281 100644 --- a/Documentation/hwmon/sht4x.rst +++ b/Documentation/hwmon/sht4x.rst @@ -42,4 +42,18 @@ humidity1_input Measured humidity in %H update_interval The minimum interval for polling the sensor, in milliseconds. Writable. Must be at least 2000. +heater_power The requested heater power, in milliwatts. + Available values: 20, 110, 200 (default: 200). +heater_time The requested operating time of the heater, + in milliseconds. + Available values: 100, 1000 (default 1000). +heater_enable Enable the heater with the selected power + and for the selected time in order to remove + condensed water from the sensor surface. The + heater cannot be manually turned off once + enabled (it will automatically turn off + after completing its operation). + + - 0: turned off (read-only value) + - 1: turn on =============== ============================================ diff --git a/drivers/hwmon/sht4x.c b/drivers/hwmon/sht4x.c index b8916d2735b5..6c9b776237c2 100644 --- a/drivers/hwmon/sht4x.c +++ b/drivers/hwmon/sht4x.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -31,6 +32,12 @@ */ #define SHT4X_CMD_MEASURE_HPM 0b11111101 #define SHT4X_CMD_RESET 0b10010100 +#define SHT4X_CMD_HEATER_20_1 0b00011110 +#define SHT4X_CMD_HEATER_20_01 0b00010101 +#define SHT4X_CMD_HEATER_110_1 0b00101111 +#define SHT4X_CMD_HEATER_110_01 0b00100100 +#define SHT4X_CMD_HEATER_200_1 0b00111001 +#define SHT4X_CMD_HEATER_200_01 0b00110010 #define SHT4X_CMD_LEN 1 #define SHT4X_CRC8_LEN 1 @@ -49,6 +56,10 @@ DECLARE_CRC8_TABLE(sht4x_crc8_table); * struct sht4x_data - All the data required to operate an SHT4X chip * @client: the i2c client associated with the SHT4X * @lock: a mutex that is used to prevent parallel access to the i2c client + * @heating_complete: the time that the last heating finished + * @data_pending: true if and only if there are measurements to retrieve after heating + * @heater_power: the power at which the heater will be started + * @heater_time: the time for which the heater will remain turned on * @valid: validity of fields below * @update_interval: the minimum poll interval * @last_updated: the previous time that the SHT4X was polled @@ -58,6 +69,10 @@ DECLARE_CRC8_TABLE(sht4x_crc8_table); struct sht4x_data { struct i2c_client *client; struct mutex lock; /* atomic read data updates */ + unsigned long heating_complete; /* in jiffies */ + bool data_pending; + u32 heater_power; /* in milli-watts */ + u32 heater_time; /* in milli-seconds */ bool valid; /* validity of fields below */ long update_interval; /* in milli-seconds */ long last_updated; /* in jiffies */ @@ -79,19 +94,30 @@ static int sht4x_read_values(struct sht4x_data *data) u8 crc; u8 cmd[SHT4X_CMD_LEN] = {SHT4X_CMD_MEASURE_HPM}; u8 raw_data[SHT4X_RESPONSE_LENGTH]; + unsigned long curr_jiffies; mutex_lock(&data->lock); - next_update = data->last_updated + - msecs_to_jiffies(data->update_interval); - if (data->valid && time_before_eq(jiffies, next_update)) - goto unlock; + curr_jiffies = jiffies; + if (time_before(curr_jiffies, data->heating_complete)) + msleep(jiffies_to_msecs(data->heating_complete - curr_jiffies)); - ret = i2c_master_send(client, cmd, SHT4X_CMD_LEN); - if (ret < 0) - goto unlock; + if (data->data_pending && + time_before(jiffies, data->heating_complete + data->update_interval)) { + data->data_pending = false; + } else { + next_update = data->last_updated + + msecs_to_jiffies(data->update_interval); - usleep_range(SHT4X_MEAS_DELAY_HPM, SHT4X_MEAS_DELAY_HPM + SHT4X_DELAY_EXTRA); + if (data->valid && time_before_eq(jiffies, next_update)) + goto unlock; + + ret = i2c_master_send(client, cmd, SHT4X_CMD_LEN); + if (ret < 0) + goto unlock; + + usleep_range(SHT4X_MEAS_DELAY_HPM, SHT4X_MEAS_DELAY_HPM + SHT4X_DELAY_EXTRA); + } ret = i2c_master_recv(client, raw_data, SHT4X_RESPONSE_LENGTH); if (ret != SHT4X_RESPONSE_LENGTH) { @@ -215,6 +241,143 @@ static int sht4x_hwmon_write(struct device *dev, enum hwmon_sensor_types type, } } +static ssize_t heater_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct sht4x_data *data = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%u\n", time_before(jiffies, data->heating_complete)); +} + +static ssize_t heater_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct sht4x_data *data = dev_get_drvdata(dev); + bool status; + ssize_t ret; + u8 cmd; + u32 heating_time_bound; + + ret = kstrtobool(buf, &status); + if (ret) + return ret; + if (!status) + return -EINVAL; + + if (data->heater_time == 100) { + if (data->heater_power == 20) + cmd = SHT4X_CMD_HEATER_20_01; + else if (data->heater_power == 110) + cmd = SHT4X_CMD_HEATER_110_01; + else /* data->heater_power == 200 */ + cmd = SHT4X_CMD_HEATER_200_01; + + heating_time_bound = 110; + } else { /* data->heater_time == 1000 */ + if (data->heater_power == 20) + cmd = SHT4X_CMD_HEATER_20_1; + else if (data->heater_power == 110) + cmd = SHT4X_CMD_HEATER_110_1; + else /* data->heater_power == 200 */ + cmd = SHT4X_CMD_HEATER_200_1; + + heating_time_bound = 1100; + } + + mutex_lock(&data->lock); + + if (time_before(jiffies, data->heating_complete)) { + ret = -EBUSY; + goto unlock; + } + + ret = i2c_master_send(data->client, &cmd, SHT4X_CMD_LEN); + if (ret < 0) + goto unlock; + + data->heating_complete = jiffies + msecs_to_jiffies(heating_time_bound); + data->data_pending = true; +unlock: + mutex_unlock(&data->lock); + return ret; +} + +static ssize_t heater_power_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct sht4x_data *data = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%u\n", data->heater_power); +} + +static ssize_t heater_power_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct sht4x_data *data = dev_get_drvdata(dev); + u32 power; + ssize_t ret; + + ret = kstrtou32(buf, 10, &power); + if (ret) + return ret; + + if (power != 20 && power != 110 && power != 200) + return -EINVAL; + + data->heater_power = power; + + return count; +} + +static ssize_t heater_time_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct sht4x_data *data = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%u\n", data->heater_time); +} + +static ssize_t heater_time_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct sht4x_data *data = dev_get_drvdata(dev); + u32 time; + ssize_t ret; + + ret = kstrtou32(buf, 10, &time); + if (ret) + return ret; + + if (time != 100 && time != 1000) + return -EINVAL; + + data->heater_time = time; + + return count; +} + +static DEVICE_ATTR_RW(heater_enable); +static DEVICE_ATTR_RW(heater_power); +static DEVICE_ATTR_RW(heater_time); + +static struct attribute *sht4x_attrs[] = { + &dev_attr_heater_enable.attr, + &dev_attr_heater_power.attr, + &dev_attr_heater_time.attr, + NULL +}; + +ATTRIBUTE_GROUPS(sht4x); + static const struct hwmon_channel_info * const sht4x_info[] = { HWMON_CHANNEL_INFO(chip, HWMON_C_UPDATE_INTERVAL), HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), @@ -255,6 +418,9 @@ static int sht4x_probe(struct i2c_client *client) data->update_interval = SHT4X_MIN_POLL_INTERVAL; data->client = client; + data->heater_power = 200; + data->heater_time = 1000; + data->heating_complete = jiffies; mutex_init(&data->lock); @@ -270,7 +436,7 @@ static int sht4x_probe(struct i2c_client *client) client->name, data, &sht4x_chip_info, - NULL); + sht4x_groups); return PTR_ERR_OR_ZERO(hwmon_dev); } From 6126f7bb6075d0af577e55bf7e2cbbcc272f520b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 17 Oct 2024 17:59:01 +0200 Subject: [PATCH 19/51] hwmon: Switch back to struct platform_driver::remove() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After commit 0edb555a65d1 ("platform: Make platform_driver::remove() return void") .remove() is (again) the right callback to implement for platform drivers. Convert all platform drivers below drivers/hwmonto use .remove(), with the eventual goal to drop struct platform_driver::remove_new(). As .remove() and .remove_new() have the same prototypes, conversion is done by just changing the structure member name in the driver initializer. While touching these files, make indention of the struct initializer consistent in several files. Signed-off-by: Uwe Kleine-König Message-ID: <20241017155900.137357-2-u.kleine-koenig@baylibre.com> Signed-off-by: Guenter Roeck --- drivers/hwmon/abituguru.c | 2 +- drivers/hwmon/abituguru3.c | 4 ++-- drivers/hwmon/aspeed-g6-pwm-tach.c | 2 +- drivers/hwmon/da9052-hwmon.c | 2 +- drivers/hwmon/dme1737.c | 2 +- drivers/hwmon/f71805f.c | 2 +- drivers/hwmon/f71882fg.c | 2 +- drivers/hwmon/i5k_amb.c | 2 +- drivers/hwmon/max197.c | 2 +- drivers/hwmon/mc13783-adc.c | 2 +- drivers/hwmon/occ/p9_sbe.c | 4 ++-- drivers/hwmon/pc87360.c | 2 +- drivers/hwmon/pc87427.c | 2 +- drivers/hwmon/sch5636.c | 2 +- drivers/hwmon/sht15.c | 2 +- drivers/hwmon/sis5595.c | 2 +- drivers/hwmon/smsc47m1.c | 2 +- drivers/hwmon/ultra45_env.c | 2 +- drivers/hwmon/via-cputemp.c | 2 +- drivers/hwmon/via686a.c | 2 +- drivers/hwmon/vt1211.c | 2 +- drivers/hwmon/vt8231.c | 4 ++-- drivers/hwmon/w83627hf.c | 2 +- drivers/hwmon/w83781d.c | 2 +- drivers/hwmon/xgene-hwmon.c | 2 +- 25 files changed, 28 insertions(+), 28 deletions(-) diff --git a/drivers/hwmon/abituguru.c b/drivers/hwmon/abituguru.c index 93653ea05430..ba8c68ae4595 100644 --- a/drivers/hwmon/abituguru.c +++ b/drivers/hwmon/abituguru.c @@ -1531,7 +1531,7 @@ static struct platform_driver abituguru_driver = { .pm = pm_sleep_ptr(&abituguru_pm), }, .probe = abituguru_probe, - .remove_new = abituguru_remove, + .remove = abituguru_remove, }; static int __init abituguru_detect(void) diff --git a/drivers/hwmon/abituguru3.c b/drivers/hwmon/abituguru3.c index 4501f0e49efb..b70330dc2198 100644 --- a/drivers/hwmon/abituguru3.c +++ b/drivers/hwmon/abituguru3.c @@ -1147,12 +1147,12 @@ static int abituguru3_resume(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(abituguru3_pm, abituguru3_suspend, abituguru3_resume); static struct platform_driver abituguru3_driver = { - .driver = { + .driver = { .name = ABIT_UGURU3_NAME, .pm = pm_sleep_ptr(&abituguru3_pm), }, .probe = abituguru3_probe, - .remove_new = abituguru3_remove, + .remove = abituguru3_remove, }; static int __init abituguru3_dmi_detect(void) diff --git a/drivers/hwmon/aspeed-g6-pwm-tach.c b/drivers/hwmon/aspeed-g6-pwm-tach.c index 75eadda738ab..4174b129d1fc 100644 --- a/drivers/hwmon/aspeed-g6-pwm-tach.c +++ b/drivers/hwmon/aspeed-g6-pwm-tach.c @@ -534,7 +534,7 @@ MODULE_DEVICE_TABLE(of, aspeed_pwm_tach_match); static struct platform_driver aspeed_pwm_tach_driver = { .probe = aspeed_pwm_tach_probe, - .remove_new = aspeed_pwm_tach_remove, + .remove = aspeed_pwm_tach_remove, .driver = { .name = "aspeed-g6-pwm-tach", .of_match_table = aspeed_pwm_tach_match, diff --git a/drivers/hwmon/da9052-hwmon.c b/drivers/hwmon/da9052-hwmon.c index 7fb0c57dfef5..588e96790850 100644 --- a/drivers/hwmon/da9052-hwmon.c +++ b/drivers/hwmon/da9052-hwmon.c @@ -473,7 +473,7 @@ static void da9052_hwmon_remove(struct platform_device *pdev) static struct platform_driver da9052_hwmon_driver = { .probe = da9052_hwmon_probe, - .remove_new = da9052_hwmon_remove, + .remove = da9052_hwmon_remove, .driver = { .name = "da9052-hwmon", }, diff --git a/drivers/hwmon/dme1737.c b/drivers/hwmon/dme1737.c index 1a9b28dc91e6..3d4057309950 100644 --- a/drivers/hwmon/dme1737.c +++ b/drivers/hwmon/dme1737.c @@ -2721,7 +2721,7 @@ static struct platform_driver dme1737_isa_driver = { .name = "dme1737", }, .probe = dme1737_isa_probe, - .remove_new = dme1737_isa_remove, + .remove = dme1737_isa_remove, }; /* --------------------------------------------------------------------- diff --git a/drivers/hwmon/f71805f.c b/drivers/hwmon/f71805f.c index 243c570dee4c..820f894d9ffd 100644 --- a/drivers/hwmon/f71805f.c +++ b/drivers/hwmon/f71805f.c @@ -1497,7 +1497,7 @@ static struct platform_driver f71805f_driver = { .name = DRVNAME, }, .probe = f71805f_probe, - .remove_new = f71805f_remove, + .remove = f71805f_remove, }; static int __init f71805f_device_add(unsigned short address, diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c index 7c941d320a18..df83f9866fbc 100644 --- a/drivers/hwmon/f71882fg.c +++ b/drivers/hwmon/f71882fg.c @@ -2658,7 +2658,7 @@ static struct platform_driver f71882fg_driver = { .name = DRVNAME, }, .probe = f71882fg_probe, - .remove_new = f71882fg_remove, + .remove = f71882fg_remove, }; static int __init f71882fg_init(void) diff --git a/drivers/hwmon/i5k_amb.c b/drivers/hwmon/i5k_amb.c index 02f5d35dd319..b22e0423e324 100644 --- a/drivers/hwmon/i5k_amb.c +++ b/drivers/hwmon/i5k_amb.c @@ -568,7 +568,7 @@ static struct platform_driver i5k_amb_driver = { .name = DRVNAME, }, .probe = i5k_amb_probe, - .remove_new = i5k_amb_remove, + .remove = i5k_amb_remove, }; static int __init i5k_amb_init(void) diff --git a/drivers/hwmon/max197.c b/drivers/hwmon/max197.c index bb30403f81ca..f0048ff37607 100644 --- a/drivers/hwmon/max197.c +++ b/drivers/hwmon/max197.c @@ -332,7 +332,7 @@ static struct platform_driver max197_driver = { .name = "max197", }, .probe = max197_probe, - .remove_new = max197_remove, + .remove = max197_remove, .id_table = max197_device_ids, }; module_platform_driver(max197_driver); diff --git a/drivers/hwmon/mc13783-adc.c b/drivers/hwmon/mc13783-adc.c index 67471c9cd4d4..66304d48d33a 100644 --- a/drivers/hwmon/mc13783-adc.c +++ b/drivers/hwmon/mc13783-adc.c @@ -315,7 +315,7 @@ static const struct platform_device_id mc13783_adc_idtable[] = { MODULE_DEVICE_TABLE(platform, mc13783_adc_idtable); static struct platform_driver mc13783_adc_driver = { - .remove_new = mc13783_adc_remove, + .remove = mc13783_adc_remove, .driver = { .name = DRIVER_NAME, }, diff --git a/drivers/hwmon/occ/p9_sbe.c b/drivers/hwmon/occ/p9_sbe.c index b5993c79c09e..89761a9c8892 100644 --- a/drivers/hwmon/occ/p9_sbe.c +++ b/drivers/hwmon/occ/p9_sbe.c @@ -192,8 +192,8 @@ static struct platform_driver p9_sbe_occ_driver = { .name = "occ-hwmon", .of_match_table = p9_sbe_occ_of_match, }, - .probe = p9_sbe_occ_probe, - .remove_new = p9_sbe_occ_remove, + .probe = p9_sbe_occ_probe, + .remove = p9_sbe_occ_remove, }; module_platform_driver(p9_sbe_occ_driver); diff --git a/drivers/hwmon/pc87360.c b/drivers/hwmon/pc87360.c index 788b5d58f77e..0f8aa6b42164 100644 --- a/drivers/hwmon/pc87360.c +++ b/drivers/hwmon/pc87360.c @@ -1606,7 +1606,7 @@ static struct platform_driver pc87360_driver = { .name = DRIVER_NAME, }, .probe = pc87360_probe, - .remove_new = pc87360_remove, + .remove = pc87360_remove, }; /* diff --git a/drivers/hwmon/pc87427.c b/drivers/hwmon/pc87427.c index 7bca04eb4ee4..571402a89368 100644 --- a/drivers/hwmon/pc87427.c +++ b/drivers/hwmon/pc87427.c @@ -1129,7 +1129,7 @@ static struct platform_driver pc87427_driver = { .name = DRVNAME, }, .probe = pc87427_probe, - .remove_new = pc87427_remove, + .remove = pc87427_remove, }; static int __init pc87427_device_add(const struct pc87427_sio_data *sio_data) diff --git a/drivers/hwmon/sch5636.c b/drivers/hwmon/sch5636.c index a4b05ebb0546..d00bd5cc6b15 100644 --- a/drivers/hwmon/sch5636.c +++ b/drivers/hwmon/sch5636.c @@ -512,7 +512,7 @@ static struct platform_driver sch5636_driver = { .name = DRVNAME, }, .probe = sch5636_probe, - .remove_new = sch5636_remove, + .remove = sch5636_remove, .id_table = sch5636_device_id, }; diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c index 494f9655f44f..3d55047e9baf 100644 --- a/drivers/hwmon/sht15.c +++ b/drivers/hwmon/sht15.c @@ -1051,7 +1051,7 @@ static struct platform_driver sht15_driver = { .of_match_table = of_match_ptr(sht15_dt_match), }, .probe = sht15_probe, - .remove_new = sht15_remove, + .remove = sht15_remove, .id_table = sht15_device_ids, }; module_platform_driver(sht15_driver); diff --git a/drivers/hwmon/sis5595.c b/drivers/hwmon/sis5595.c index e73b1522f3ce..b7a7bcd6d3af 100644 --- a/drivers/hwmon/sis5595.c +++ b/drivers/hwmon/sis5595.c @@ -784,7 +784,7 @@ static struct platform_driver sis5595_driver = { .name = DRIVER_NAME, }, .probe = sis5595_probe, - .remove_new = sis5595_remove, + .remove = sis5595_remove, }; static int sis5595_pci_probe(struct pci_dev *dev, diff --git a/drivers/hwmon/smsc47m1.c b/drivers/hwmon/smsc47m1.c index 0d46edbcb144..595bceb78d76 100644 --- a/drivers/hwmon/smsc47m1.c +++ b/drivers/hwmon/smsc47m1.c @@ -858,7 +858,7 @@ static struct platform_driver smsc47m1_driver __refdata = { .driver = { .name = DRVNAME, }, - .remove_new = __exit_p(smsc47m1_remove), + .remove = __exit_p(smsc47m1_remove), }; static int __init smsc47m1_device_add(unsigned short address, diff --git a/drivers/hwmon/ultra45_env.c b/drivers/hwmon/ultra45_env.c index 2765d5f1b7f0..e4f1bb538628 100644 --- a/drivers/hwmon/ultra45_env.c +++ b/drivers/hwmon/ultra45_env.c @@ -317,7 +317,7 @@ static struct platform_driver env_driver = { .of_match_table = env_match, }, .probe = env_probe, - .remove_new = env_remove, + .remove = env_remove, }; module_platform_driver(env_driver); diff --git a/drivers/hwmon/via-cputemp.c b/drivers/hwmon/via-cputemp.c index 5abe95b683c0..823bff2871e1 100644 --- a/drivers/hwmon/via-cputemp.c +++ b/drivers/hwmon/via-cputemp.c @@ -197,7 +197,7 @@ static struct platform_driver via_cputemp_driver = { .name = DRVNAME, }, .probe = via_cputemp_probe, - .remove_new = via_cputemp_remove, + .remove = via_cputemp_remove, }; struct pdev_entry { diff --git a/drivers/hwmon/via686a.c b/drivers/hwmon/via686a.c index 3a002ad3c005..bbaeb808cc15 100644 --- a/drivers/hwmon/via686a.c +++ b/drivers/hwmon/via686a.c @@ -799,7 +799,7 @@ static struct platform_driver via686a_driver = { .name = DRIVER_NAME, }, .probe = via686a_probe, - .remove_new = via686a_remove, + .remove = via686a_remove, }; static const struct pci_device_id via686a_pci_ids[] = { diff --git a/drivers/hwmon/vt1211.c b/drivers/hwmon/vt1211.c index 2f3890463e18..386edea6b69e 100644 --- a/drivers/hwmon/vt1211.c +++ b/drivers/hwmon/vt1211.c @@ -1221,7 +1221,7 @@ static struct platform_driver vt1211_driver = { .name = DRVNAME, }, .probe = vt1211_probe, - .remove_new = vt1211_remove, + .remove = vt1211_remove, }; static int __init vt1211_device_add(unsigned short address) diff --git a/drivers/hwmon/vt8231.c b/drivers/hwmon/vt8231.c index dcdd14ccd115..3bf27c21845b 100644 --- a/drivers/hwmon/vt8231.c +++ b/drivers/hwmon/vt8231.c @@ -910,11 +910,11 @@ static void vt8231_remove(struct platform_device *pdev) static struct platform_driver vt8231_driver = { - .driver = { + .driver = { .name = DRIVER_NAME, }, .probe = vt8231_probe, - .remove_new = vt8231_remove, + .remove = vt8231_remove, }; static const struct pci_device_id vt8231_pci_ids[] = { diff --git a/drivers/hwmon/w83627hf.c b/drivers/hwmon/w83627hf.c index 2fc9b718e2ab..95115d7b863e 100644 --- a/drivers/hwmon/w83627hf.c +++ b/drivers/hwmon/w83627hf.c @@ -1844,7 +1844,7 @@ static struct platform_driver w83627hf_driver = { .pm = W83627HF_DEV_PM_OPS, }, .probe = w83627hf_probe, - .remove_new = w83627hf_remove, + .remove = w83627hf_remove, }; static int __init w83627hf_find(int sioaddr, unsigned short *addr, diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c index b7957c84d235..076200ed2ec9 100644 --- a/drivers/hwmon/w83781d.c +++ b/drivers/hwmon/w83781d.c @@ -1828,7 +1828,7 @@ static struct platform_driver w83781d_isa_driver = { .name = "w83781d", }, .probe = w83781d_isa_probe, - .remove_new = w83781d_isa_remove, + .remove = w83781d_isa_remove, }; /* return 1 if a supported chip is found, 0 otherwise */ diff --git a/drivers/hwmon/xgene-hwmon.c b/drivers/hwmon/xgene-hwmon.c index 5e0759a70f6d..1e3bd129a922 100644 --- a/drivers/hwmon/xgene-hwmon.c +++ b/drivers/hwmon/xgene-hwmon.c @@ -772,7 +772,7 @@ MODULE_DEVICE_TABLE(of, xgene_hwmon_of_match); static struct platform_driver xgene_hwmon_driver = { .probe = xgene_hwmon_probe, - .remove_new = xgene_hwmon_remove, + .remove = xgene_hwmon_remove, .driver = { .name = "xgene-slimpro-hwmon", .of_match_table = xgene_hwmon_of_match, From f3bfd13c4a45ab8874d4cf855abf57d0b9f29279 Mon Sep 17 00:00:00 2001 From: Yikai Tsai Date: Wed, 2 Oct 2024 16:11:29 +0800 Subject: [PATCH 20/51] dt-bindings: hwmon: add renesas,isl28022 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add dt-bindings for Renesas ISL28022 power monitor. Signed-off-by: Carsten Spieß Reviewed-by: Krzysztof Kozlowski Signed-off-by: Yikai Tsai Message-ID: <20241002081133.13123-2-yikai.tsai.wiwynn@gmail.com> Signed-off-by: Guenter Roeck --- .../bindings/hwmon/renesas,isl28022.yaml | 64 +++++++++++++++++++ MAINTAINERS | 6 ++ 2 files changed, 70 insertions(+) create mode 100644 Documentation/devicetree/bindings/hwmon/renesas,isl28022.yaml diff --git a/Documentation/devicetree/bindings/hwmon/renesas,isl28022.yaml b/Documentation/devicetree/bindings/hwmon/renesas,isl28022.yaml new file mode 100644 index 000000000000..dd82a80e4115 --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/renesas,isl28022.yaml @@ -0,0 +1,64 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/hwmon/renesas,isl28022.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Renesas ISL28022 power monitor + +maintainers: + - Carsten Spieß + +description: | + The ISL28022 is a power monitor with I2C interface. The device monitors + voltage, current via shunt resistor and calculated power. + + Datasheets: + https://www.renesas.com/us/en/www/doc/datasheet/isl28022.pdf + +properties: + compatible: + const: renesas,isl28022 + + reg: + maxItems: 1 + + shunt-resistor-micro-ohms: + description: + Shunt resistor value in micro-Ohm + minimum: 800 + default: 10000 + + renesas,shunt-range-microvolt: + description: + Maximal shunt voltage range of +/- 40 mV, 80 mV, 160 mV or 320 mV + default: 320000 + enum: [40000, 80000, 160000, 320000] + + renesas,average-samples: + description: + Number of samples to be used to report voltage, current and power values. + default: 1 + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [1, 2, 4, 8, 16, 32, 64, 128] + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + power-monitor@40 { + compatible = "renesas,isl28022"; + reg = <0x40>; + shunt-resistor-micro-ohms = <8000>; + renesas,shunt-range-microvolt = <40000>; + renesas,average-samples = <128>; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 21fdaa19229a..8fcda1d6a4c1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12104,6 +12104,12 @@ F: drivers/isdn/Makefile F: drivers/isdn/hardware/ F: drivers/isdn/mISDN/ +ISL28022 HARDWARE MONITORING DRIVER +M: Carsten Spieß +L: linux-hwmon@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/hwmon/renesas,isl28022.yaml + ISOFS FILESYSTEM M: Jan Kara L: linux-fsdevel@vger.kernel.org From 39671a14df4f26e091730d3b8fd3bb900005b83c Mon Sep 17 00:00:00 2001 From: Yikai Tsai Date: Wed, 2 Oct 2024 16:11:30 +0800 Subject: [PATCH 21/51] hwmon: (isl28022) new driver for ISL28022 power monitor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Driver for Renesas ISL28022 power monitor with I2C interface. The device monitors voltage, current via shunt resistor and calculated power. Signed-off-by: Carsten Spieß Signed-off-by: Yikai Tsai Message-ID: <20241002081133.13123-3-yikai.tsai.wiwynn@gmail.com> [groeck: Fixed alignment issues, dropped noise at end of probe] Signed-off-by: Guenter Roeck --- Documentation/hwmon/index.rst | 1 + Documentation/hwmon/isl28022.rst | 63 ++++ MAINTAINERS | 2 + drivers/hwmon/Kconfig | 11 + drivers/hwmon/Makefile | 1 + drivers/hwmon/isl28022.c | 535 +++++++++++++++++++++++++++++++ 6 files changed, 613 insertions(+) create mode 100644 Documentation/hwmon/isl28022.rst create mode 100644 drivers/hwmon/isl28022.c diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index ea3b5be8fe4f..4d15664bc41e 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -96,6 +96,7 @@ Hardware Monitoring Kernel Drivers ir35221 ir38064 ir36021 + isl28022 isl68137 it87 jc42 diff --git a/Documentation/hwmon/isl28022.rst b/Documentation/hwmon/isl28022.rst new file mode 100644 index 000000000000..8d4422a2dacd --- /dev/null +++ b/Documentation/hwmon/isl28022.rst @@ -0,0 +1,63 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Kernel driver isl28022 +====================== + +Supported chips: + + * Renesas ISL28022 + + Prefix: 'isl28022' + + Addresses scanned: none + + Datasheet: Publicly available at the Renesas website + + https://www.renesas.com/us/en/www/doc/datasheet/isl28022.pdf + +Author: + Carsten Spieß + +Description +----------- + +The ISL28022 is a power monitor with I2C interface. The device monitors +voltage, current via shunt resistor and calculated power. + +Usage Notes +----------- + +This driver does not auto-detect devices. You will have to instantiate the +device explicitly. Please see Documentation/i2c/instantiating-devices.rst for +details. + +The shunt value in micro-ohms, shunt voltage range and averaging can be set +with device properties. +Please refer to the Documentation/devicetree/bindings/hwmon/isl,isl28022.yaml +for bindings if the device tree is used. + +The driver supports only shunt and bus continuous ADC mode at 15bit resolution. +Averaging can be set from 1 to 128 samples (power of 2) on both channels. +Shunt voltage range of 40, 80, 160 or 320mV is allowed +The bus voltage range is 60V fixed. + +Sysfs entries +------------- + +The following attributes are supported. All attributes are read-only. + +======================= ======================================================= +in0_input bus voltage (milli Volt) + +curr1_input current (milli Ampere) +power1_input power (micro Watt) +======================= ======================================================= + +Debugfs entries +--------------- + +The following attributes are supported. All attributes are read-only. + +======================= ======================================================= +shunt_voltage shunt voltage (micro Volt) +======================= ======================================================= diff --git a/MAINTAINERS b/MAINTAINERS index 8fcda1d6a4c1..b1cc6030b455 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12109,6 +12109,8 @@ M: Carsten Spieß L: linux-hwmon@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/hwmon/renesas,isl28022.yaml +F: Documentation/hwmon/isl28022.rst +F: drivers/hwmon/isl28022.c ISOFS FILESYSTEM M: Jan Kara diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 6a3385598cc6..64fefb22ecee 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -855,6 +855,17 @@ config SENSORS_CORETEMP sensor inside your CPU. Most of the family 6 CPUs are supported. Check Documentation/hwmon/coretemp.rst for details. +config SENSORS_ISL28022 + tristate "Renesas ISL28022" + depends on I2C + select REGMAP_I2C + help + If you say yes here you get support for ISL28022 power monitor. + Check Documentation/hwmon/isl28022.rst for details. + + This driver can also be built as a module. If so, the module + will be called isl28022. + config SENSORS_IT87 tristate "ITE IT87xx and compatibles" depends on HAS_IOPORT diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 9554d2fdcf7b..4ea6f7c95315 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -103,6 +103,7 @@ obj-$(CONFIG_SENSORS_INA2XX) += ina2xx.o obj-$(CONFIG_SENSORS_INA238) += ina238.o obj-$(CONFIG_SENSORS_INA3221) += ina3221.o obj-$(CONFIG_SENSORS_INTEL_M10_BMC_HWMON) += intel-m10-bmc-hwmon.o +obj-$(CONFIG_SENSORS_ISL28022) += isl28022.o obj-$(CONFIG_SENSORS_IT87) += it87.o obj-$(CONFIG_SENSORS_JC42) += jc42.o obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o diff --git a/drivers/hwmon/isl28022.c b/drivers/hwmon/isl28022.c new file mode 100644 index 000000000000..f9edcfd164c2 --- /dev/null +++ b/drivers/hwmon/isl28022.c @@ -0,0 +1,535 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * isl28022.c - driver for Renesas ISL28022 power monitor chip monitoring + * + * Copyright (c) 2023 Carsten Spieß + */ + +#include +#include +#include +#include +#include +#include + +/* ISL28022 registers */ +#define ISL28022_REG_CONFIG 0x00 +#define ISL28022_REG_SHUNT 0x01 +#define ISL28022_REG_BUS 0x02 +#define ISL28022_REG_POWER 0x03 +#define ISL28022_REG_CURRENT 0x04 +#define ISL28022_REG_CALIB 0x05 +#define ISL28022_REG_SHUNT_THR 0x06 +#define ISL28022_REG_BUS_THR 0x07 +#define ISL28022_REG_INT 0x08 +#define ISL28022_REG_AUX 0x09 +#define ISL28022_REG_MAX ISL28022_REG_AUX + +/* ISL28022 config flags */ +/* mode flags */ +#define ISL28022_MODE_SHIFT 0 +#define ISL28022_MODE_MASK 0x0007 + +#define ISL28022_MODE_PWR_DOWN 0x0 +#define ISL28022_MODE_TRG_S 0x1 +#define ISL28022_MODE_TRG_B 0x2 +#define ISL28022_MODE_TRG_SB 0x3 +#define ISL28022_MODE_ADC_OFF 0x4 +#define ISL28022_MODE_CONT_S 0x5 +#define ISL28022_MODE_CONT_B 0x6 +#define ISL28022_MODE_CONT_SB 0x7 + +/* shunt ADC settings */ +#define ISL28022_SADC_SHIFT 3 +#define ISL28022_SADC_MASK 0x0078 + +#define ISL28022_BADC_SHIFT 7 +#define ISL28022_BADC_MASK 0x0780 + +#define ISL28022_ADC_12 0x0 /* 12 bit ADC */ +#define ISL28022_ADC_13 0x1 /* 13 bit ADC */ +#define ISL28022_ADC_14 0x2 /* 14 bit ADC */ +#define ISL28022_ADC_15 0x3 /* 15 bit ADC */ +#define ISL28022_ADC_15_1 0x8 /* 15 bit ADC, 1 sample */ +#define ISL28022_ADC_15_2 0x9 /* 15 bit ADC, 2 samples */ +#define ISL28022_ADC_15_4 0xA /* 15 bit ADC, 4 samples */ +#define ISL28022_ADC_15_8 0xB /* 15 bit ADC, 8 samples */ +#define ISL28022_ADC_15_16 0xC /* 15 bit ADC, 16 samples */ +#define ISL28022_ADC_15_32 0xD /* 15 bit ADC, 32 samples */ +#define ISL28022_ADC_15_64 0xE /* 15 bit ADC, 64 samples */ +#define ISL28022_ADC_15_128 0xF /* 15 bit ADC, 128 samples */ + +/* shunt voltage range */ +#define ISL28022_PG_SHIFT 11 +#define ISL28022_PG_MASK 0x1800 + +#define ISL28022_PG_40 0x0 /* +/-40 mV */ +#define ISL28022_PG_80 0x1 /* +/-80 mV */ +#define ISL28022_PG_160 0x2 /* +/-160 mV */ +#define ISL28022_PG_320 0x3 /* +/-3200 mV */ + +/* bus voltage range */ +#define ISL28022_BRNG_SHIFT 13 +#define ISL28022_BRNG_MASK 0x6000 + +#define ISL28022_BRNG_16 0x0 /* 16 V */ +#define ISL28022_BRNG_32 0x1 /* 32 V */ +#define ISL28022_BRNG_60 0x3 /* 60 V */ + +/* reset */ +#define ISL28022_RESET 0x8000 + +struct isl28022_data { + struct regmap *regmap; + u32 shunt; + u32 gain; + u32 average; +}; + +static int isl28022_read_in(struct device *dev, u32 attr, int channel, long *val) +{ + struct isl28022_data *data = dev_get_drvdata(dev); + unsigned int regval; + int err; + u16 sign_bit; + + switch (channel) { + case 0: + switch (attr) { + case hwmon_in_input: + err = regmap_read(data->regmap, + ISL28022_REG_BUS, ®val); + if (err < 0) + return err; + /* driver supports only 60V mode (BRNG 11) */ + *val = (long)(((u16)regval) & 0xFFFC); + break; + default: + return -EOPNOTSUPP; + } + break; + case 1: + switch (attr) { + case hwmon_in_input: + err = regmap_read(data->regmap, + ISL28022_REG_SHUNT, ®val); + if (err < 0) + return err; + switch (data->gain) { + case 8: + sign_bit = (regval >> 15) & 0x01; + *val = (long)((((u16)regval) & 0x7FFF) - + (sign_bit * 32768)) / 100; + break; + case 4: + sign_bit = (regval >> 14) & 0x01; + *val = (long)((((u16)regval) & 0x3FFF) - + (sign_bit * 16384)) / 100; + break; + case 2: + sign_bit = (regval >> 13) & 0x01; + *val = (long)((((u16)regval) & 0x1FFF) - + (sign_bit * 8192)) / 100; + break; + case 1: + sign_bit = (regval >> 12) & 0x01; + *val = (long)((((u16)regval) & 0x0FFF) - + (sign_bit * 4096)) / 100; + break; + } + break; + default: + return -EOPNOTSUPP; + } + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int isl28022_read_current(struct device *dev, u32 attr, long *val) +{ + struct isl28022_data *data = dev_get_drvdata(dev); + unsigned int regval; + int err; + + switch (attr) { + case hwmon_curr_input: + err = regmap_read(data->regmap, + ISL28022_REG_CURRENT, ®val); + if (err < 0) + return err; + *val = ((long)regval * 1250L * (long)data->gain) / + (long)data->shunt; + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int isl28022_read_power(struct device *dev, u32 attr, long *val) +{ + struct isl28022_data *data = dev_get_drvdata(dev); + unsigned int regval; + int err; + + switch (attr) { + case hwmon_power_input: + err = regmap_read(data->regmap, + ISL28022_REG_POWER, ®val); + if (err < 0) + return err; + *val = ((51200000L * ((long)data->gain)) / + (long)data->shunt) * (long)regval; + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int isl28022_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + switch (type) { + case hwmon_in: + return isl28022_read_in(dev, attr, channel, val); + case hwmon_curr: + return isl28022_read_current(dev, attr, val); + case hwmon_power: + return isl28022_read_power(dev, attr, val); + default: + return -EOPNOTSUPP; + } + return 0; +} + +static umode_t isl28022_is_visible(const void *data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_in: + switch (attr) { + case hwmon_in_input: + return 0444; + default: + break; + } + break; + case hwmon_curr: + switch (attr) { + case hwmon_curr_input: + return 0444; + default: + break; + } + break; + case hwmon_power: + switch (attr) { + case hwmon_power_input: + return 0444; + default: + break; + } + break; + default: + break; + } + return 0; +} + +static const struct hwmon_channel_info *isl28022_info[] = { + HWMON_CHANNEL_INFO(in, + HWMON_I_INPUT, /* channel 0: bus voltage (mV) */ + HWMON_I_INPUT), /* channel 1: shunt voltage (mV) */ + HWMON_CHANNEL_INFO(curr, + HWMON_C_INPUT), /* channel 1: current (mA) */ + HWMON_CHANNEL_INFO(power, + HWMON_P_INPUT), /* channel 1: power (µW) */ + NULL +}; + +static const struct hwmon_ops isl28022_hwmon_ops = { + .is_visible = isl28022_is_visible, + .read = isl28022_read, +}; + +static const struct hwmon_chip_info isl28022_chip_info = { + .ops = &isl28022_hwmon_ops, + .info = isl28022_info, +}; + +static bool isl28022_is_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ISL28022_REG_CONFIG: + case ISL28022_REG_CALIB: + case ISL28022_REG_SHUNT_THR: + case ISL28022_REG_BUS_THR: + case ISL28022_REG_INT: + case ISL28022_REG_AUX: + return true; + } + + return false; +} + +static bool isl28022_is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ISL28022_REG_CONFIG: + case ISL28022_REG_SHUNT: + case ISL28022_REG_BUS: + case ISL28022_REG_POWER: + case ISL28022_REG_CURRENT: + case ISL28022_REG_INT: + case ISL28022_REG_AUX: + return true; + } + return true; +} + +static const struct regmap_config isl28022_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .max_register = ISL28022_REG_MAX, + .writeable_reg = isl28022_is_writeable_reg, + .volatile_reg = isl28022_is_volatile_reg, + .val_format_endian = REGMAP_ENDIAN_BIG, + .cache_type = REGCACHE_RBTREE, + .use_single_read = true, + .use_single_write = true, +}; + +static int shunt_voltage_show(struct seq_file *seqf, void *unused) +{ + struct isl28022_data *data = seqf->private; + unsigned int regval; + int err; + + err = regmap_read(data->regmap, + ISL28022_REG_SHUNT, ®val); + if (err) + return err; + + /* print shunt voltage in micro volt */ + seq_printf(seqf, "%d\n", regval * 10); + + return 0; +} +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. + * + * following values for shunt range and resistor are allowed: + * 40 mV -> gain 1, shunt min. 800 micro ohms + * 80 mV -> gain 2, shunt min. 1600 micro ohms + * 160 mV -> gain 4, shunt min. 3200 micro ohms + * 320 mV -> gain 8, shunt min. 6400 micro ohms + */ +static int isl28022_read_properties(struct device *dev, struct isl28022_data *data) +{ + u32 val; + int err; + + err = device_property_read_u32(dev, "shunt-resistor-micro-ohms", &val); + if (err == -EINVAL) + val = 10000; + else if (err < 0) + return err; + data->shunt = val; + + err = device_property_read_u32(dev, "renesas,shunt-range-microvolt", &val); + if (err == -EINVAL) + val = 320000; + else if (err < 0) + return err; + + switch (val) { + case 40000: + data->gain = 1; + if (data->shunt < 800) + goto shunt_invalid; + break; + case 80000: + data->gain = 2; + if (data->shunt < 1600) + goto shunt_invalid; + break; + case 160000: + data->gain = 4; + if (data->shunt < 3200) + goto shunt_invalid; + break; + case 320000: + data->gain = 8; + if (data->shunt < 6400) + goto shunt_invalid; + break; + default: + return dev_err_probe(dev, -EINVAL, + "renesas,shunt-range-microvolt invalid value %d\n", + val); + } + + err = device_property_read_u32(dev, "renesas,average-samples", &val); + if (err == -EINVAL) + val = 1; + else if (err < 0) + return err; + if (val > 128 || hweight32(val) != 1) + return dev_err_probe(dev, -EINVAL, + "renesas,average-samples invalid value %d\n", + val); + + data->average = val; + + return 0; + +shunt_invalid: + return dev_err_probe(dev, -EINVAL, + "renesas,shunt-resistor-microvolt invalid value %d\n", + data->shunt); +} + +/* + * write configuration and calibration registers + * + * The driver supports only shunt and bus continuous ADC mode at 15bit resolution + * with averaging from 1 to 128 samples (pow of 2) on both channels. + * Shunt voltage gain 1,2,4 or 8 is allowed. + * The bus voltage range is 60V fixed. + */ +static int isl28022_config(struct isl28022_data *data) +{ + int err; + u16 config; + u16 calib; + + config = (ISL28022_MODE_CONT_SB << ISL28022_MODE_SHIFT) | + (ISL28022_BRNG_60 << ISL28022_BRNG_SHIFT) | + (__ffs(data->gain) << ISL28022_PG_SHIFT) | + ((ISL28022_ADC_15_1 + __ffs(data->average)) << ISL28022_SADC_SHIFT) | + ((ISL28022_ADC_15_1 + __ffs(data->average)) << ISL28022_BADC_SHIFT); + + calib = data->shunt ? 0x8000 / data->gain : 0; + + err = regmap_write(data->regmap, ISL28022_REG_CONFIG, config); + if (err < 0) + return err; + + return regmap_write(data->regmap, ISL28022_REG_CALIB, calib); +} + +static int isl28022_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct device *hwmon_dev; + struct isl28022_data *data; + int err; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA)) + return -ENODEV; + + data = devm_kzalloc(dev, sizeof(struct isl28022_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + err = isl28022_read_properties(dev, data); + if (err) + return err; + + data->regmap = devm_regmap_init_i2c(client, &isl28022_regmap_config); + if (IS_ERR(data->regmap)) + return PTR_ERR(data->regmap); + + err = isl28022_config(data); + if (err) + return err; + + isl28022_debugfs_init(client, data); + + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, + data, &isl28022_chip_info, NULL); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); + + return 0; +} + +static const struct i2c_device_id isl28022_ids[] = { + { "isl28022", 0}, + { /* LIST END */ } +}; +MODULE_DEVICE_TABLE(i2c, isl28022_ids); + +static const struct of_device_id __maybe_unused isl28022_of_match[] = { + { .compatible = "renesas,isl28022"}, + { /* LIST END */ } +}; +MODULE_DEVICE_TABLE(of, isl28022_of_match); + +static struct i2c_driver isl28022_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "isl28022", + }, + .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; +} + +static void __exit +isl28022_exit(void) +{ + i2c_del_driver(&isl28022_driver); + debugfs_remove_recursive(isl28022_debugfs_root); +} + +module_init(isl28022_init); +module_exit(isl28022_exit); + +MODULE_AUTHOR("Carsten Spieß "); +MODULE_DESCRIPTION("ISL28022 driver"); +MODULE_LICENSE("GPL"); From ccae49e5cf6ebda1a7fa5d2ca99500987c7420c4 Mon Sep 17 00:00:00 2001 From: Sarah Maedel Date: Fri, 18 Oct 2024 09:46:10 +0200 Subject: [PATCH 22/51] hwmon: (nct6775) Add 665-ACE/600M-CL to ASUS WMI monitoring list Boards such as * Pro WS 665-ACE * Pro WS 600M-CL have got a nct6775 chip, but by default there's no use of it because of resource conflict with WMI method. Add affected boards to the WMI monitoring list. Link: https://bugzilla.kernel.org/show_bug.cgi?id=204807 Co-developed-by: Tommy Giesler Signed-off-by: Tommy Giesler Signed-off-by: Sarah Maedel Message-ID: <20241018074611.358619-1-sarah.maedel@hetzner-cloud.de> [groeck: Change commit message to imperative mood] Signed-off-by: Guenter Roeck --- drivers/hwmon/nct6775-platform.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/hwmon/nct6775-platform.c b/drivers/hwmon/nct6775-platform.c index 096f1daa8f2b..1218a3b449a8 100644 --- a/drivers/hwmon/nct6775-platform.c +++ b/drivers/hwmon/nct6775-platform.c @@ -1350,6 +1350,8 @@ static const char * const asus_msi_boards[] = { "Pro H610M-CT D4", "Pro H610T D4", "Pro Q670M-C", + "Pro WS 600M-CL", + "Pro WS 665-ACE", "Pro WS W680-ACE", "Pro WS W680-ACE IPMI", "Pro WS W790-ACE", From 547a5c453cd386f39d5e8cf4d299defafe8bc8f0 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 23 Oct 2024 20:34:49 -0700 Subject: [PATCH 23/51] MAINTAINERS: Remove Aleksandr Mezin as NZXT-SMART2 driver maintainer Per his request, remove Aleksandr Mezin as maintainer of the NZXT-SMART2 hardware monitoring driver. Cc: Aleksandr Mezin Signed-off-by: Guenter Roeck --- MAINTAINERS | 7 ------- 1 file changed, 7 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index b1cc6030b455..3342120e7a59 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16747,13 +16747,6 @@ S: Maintained F: Documentation/hwmon/nzxt-kraken3.rst F: drivers/hwmon/nzxt-kraken3.c -NZXT-SMART2 HARDWARE MONITORING DRIVER -M: Aleksandr Mezin -L: linux-hwmon@vger.kernel.org -S: Maintained -F: Documentation/hwmon/nzxt-smart2.rst -F: drivers/hwmon/nzxt-smart2.c - OBJAGG M: Jiri Pirko L: netdev@vger.kernel.org From c9fb2552fd23ad748743ba5209b0cd4ca583b62d Mon Sep 17 00:00:00 2001 From: Naresh Solanki Date: Tue, 22 Oct 2024 16:07:49 +0530 Subject: [PATCH 24/51] dt-bindings: hwmon: pmbus: Add bindings for MPS MP297x Remove mps297x from trivial-devices as it requires additional properties and does not fit into the trivial devices category. Add new bindings for MPS mp2971, mp2973 & mp2975. It is Dual-Loop, Digital Multi-Phase Controller with PMBUS interface Signed-off-by: Naresh Solanki Reviewed-by: Conor Dooley Message-ID: <20241022103750.572677-1-naresh.solanki@9elements.com> Signed-off-by: Guenter Roeck --- .../bindings/hwmon/pmbus/mps,mp2975.yaml | 75 +++++++++++++++++++ .../devicetree/bindings/trivial-devices.yaml | 6 -- 2 files changed, 75 insertions(+), 6 deletions(-) create mode 100644 Documentation/devicetree/bindings/hwmon/pmbus/mps,mp2975.yaml diff --git a/Documentation/devicetree/bindings/hwmon/pmbus/mps,mp2975.yaml b/Documentation/devicetree/bindings/hwmon/pmbus/mps,mp2975.yaml new file mode 100644 index 000000000000..f7bc4f077929 --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/pmbus/mps,mp2975.yaml @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/hwmon/pmbus/mps,mp2975.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MPS MP2975 Synchronous Buck Regulator + +maintainers: + - Naresh Solanki + +description: + The MPS MP2971, MP2973 & MP2975 is a multi-phase voltage regulator + designed for use in high-performance computing and server + applications. It supports I2C/PMBus for control and monitoring. + +properties: + compatible: + enum: + - mps,mp2971 + - mps,mp2973 + - mps,mp2975 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + regulators: + type: object + description: + List of regulators provided by this controller. + + patternProperties: + "^vout[0-1]$": + $ref: /schemas/regulator/regulator.yaml# + type: object + unevaluatedProperties: false + + additionalProperties: false + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + + regulator@58 { + compatible = "mps,mp2973"; + reg = <0x58>; + + interrupt-parent = <&smb_pex_cpu1_event>; + interrupts = <12 IRQ_TYPE_LEVEL_LOW>; + + regulators { + vout0 { + regulator-name = "pvccin_cpu1"; + regulator-enable-ramp-delay = <200>; + }; + vout1 { + regulator-name = "pvccfa_ehv_fivra_cpu1"; + regulator-enable-ramp-delay = <200>; + }; + }; + }; + }; + diff --git a/Documentation/devicetree/bindings/trivial-devices.yaml b/Documentation/devicetree/bindings/trivial-devices.yaml index b803581ba927..2ae1f7f8d9b1 100644 --- a/Documentation/devicetree/bindings/trivial-devices.yaml +++ b/Documentation/devicetree/bindings/trivial-devices.yaml @@ -279,12 +279,6 @@ properties: - mps,mp2888 # Monolithic Power Systems Inc. multi-phase controller mp2891 - mps,mp2891 - # Monolithic Power Systems Inc. multi-phase controller mp2971 - - mps,mp2971 - # Monolithic Power Systems Inc. multi-phase controller mp2973 - - mps,mp2973 - # Monolithic Power Systems Inc. multi-phase controller mp2975 - - mps,mp2975 # Monolithic Power Systems Inc. multi-phase controller mp2993 - mps,mp2993 # Monolithic Power Systems Inc. multi-phase hot-swap controller mp5920 From c20a783b67e75e549e6d45d8791f7bb8b8d97e32 Mon Sep 17 00:00:00 2001 From: Naresh Solanki Date: Mon, 21 Oct 2024 18:00:43 +0530 Subject: [PATCH 25/51] dt-bindings: hwmon: pmbus: Add bindings for Vicor pli1209bc Remove vicor,pli1209bc from trivial-devices as it requires additional properties and does not fit into the trivial devices category. Add new bindings for Vicor pli1209bc, a Digital Supervisor with Isolation for use with BCM Bus Converter Modules. VR rails are defined under regulator node as expected by pmbus driver. Signed-off-by: Naresh Solanki Reviewed-by: Krzysztof Kozlowski Message-ID: <20241021123044.3648960-1-naresh.solanki@9elements.com> Signed-off-by: Guenter Roeck --- .../bindings/hwmon/pmbus/vicor,pli1209bc.yaml | 62 +++++++++++++++++++ .../devicetree/bindings/trivial-devices.yaml | 2 - 2 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 Documentation/devicetree/bindings/hwmon/pmbus/vicor,pli1209bc.yaml diff --git a/Documentation/devicetree/bindings/hwmon/pmbus/vicor,pli1209bc.yaml b/Documentation/devicetree/bindings/hwmon/pmbus/vicor,pli1209bc.yaml new file mode 100644 index 000000000000..4aa62d67e1a9 --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/pmbus/vicor,pli1209bc.yaml @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/hwmon/pmbus/vicor,pli1209bc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Vicor PLI1209BC Power Regulator + +maintainers: + - Marcello Sylvester Bauer + - Naresh Solanki + +description: + The Vicor PLI1209BC is a Digital Supervisor with Isolation for use + with BCM Bus Converter Modules. + +properties: + compatible: + enum: + - vicor,pli1209bc + + reg: + maxItems: 1 + + regulators: + type: object + description: + List of regulators provided by this controller. + + properties: + vout2: + $ref: /schemas/regulator/regulator.yaml# + type: object + unevaluatedProperties: false + + additionalProperties: false + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + regulator@5f { + compatible = "vicor,pli1209bc"; + reg = <0x5f>; + + regulators { + p12v_d: vout2 { + regulator-name = "bcm3"; + regulator-boot-on; + }; + }; + }; + }; + diff --git a/Documentation/devicetree/bindings/trivial-devices.yaml b/Documentation/devicetree/bindings/trivial-devices.yaml index 2ae1f7f8d9b1..8850b11052b1 100644 --- a/Documentation/devicetree/bindings/trivial-devices.yaml +++ b/Documentation/devicetree/bindings/trivial-devices.yaml @@ -392,8 +392,6 @@ properties: - ti,tps546d24 # I2C Touch-Screen Controller - ti,tsc2003 - # Vicor Corporation Digital Supervisor - - vicor,pli1209bc # Winbond/Nuvoton H/W Monitor - winbond,w83793 From 15cae9c0b9c0e78b78da7a08ff8ab0ea15294f57 Mon Sep 17 00:00:00 2001 From: Ban Feng Date: Tue, 22 Oct 2024 13:29:04 +0800 Subject: [PATCH 26/51] dt-bindings: hwmon: Add NCT7363Y documentation Add bindings for the Nuvoton NCT7363Y Fan Controller Reviewed-by: Rob Herring Reviewed-by: Paul Menzel Acked-by: Krzysztof Kozlowski Signed-off-by: Ban Feng Message-ID: <20241022052905.4062682-2-kcfeng0@nuvoton.com> Signed-off-by: Guenter Roeck --- .../bindings/hwmon/nuvoton,nct7363.yaml | 66 +++++++++++++++++++ MAINTAINERS | 6 ++ 2 files changed, 72 insertions(+) create mode 100644 Documentation/devicetree/bindings/hwmon/nuvoton,nct7363.yaml diff --git a/Documentation/devicetree/bindings/hwmon/nuvoton,nct7363.yaml b/Documentation/devicetree/bindings/hwmon/nuvoton,nct7363.yaml new file mode 100644 index 000000000000..c1e5dedc2f6a --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/nuvoton,nct7363.yaml @@ -0,0 +1,66 @@ +# 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# + +title: Nuvoton NCT7363Y Hardware Monitoring IC + +maintainers: + - Ban Feng + +description: | + The NCT7363Y is a fan controller which provides up to 16 independent + FAN input monitors, and up to 16 independent PWM outputs with SMBus interface. + + Datasheets: Available from Nuvoton upon request + +properties: + compatible: + enum: + - nuvoton,nct7363 + - nuvoton,nct7362 + + reg: + maxItems: 1 + + "#pwm-cells": + const: 2 + +patternProperties: + "^fan-[0-9]+$": + $ref: fan-common.yaml# + unevaluatedProperties: false + required: + - pwms + - tach-ch + +required: + - compatible + - reg + - "#pwm-cells" + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + hwmon: hwmon@22 { + compatible = "nuvoton,nct7363"; + reg = <0x22>; + #pwm-cells = <2>; + + fan-0 { + pwms = <&hwmon 0 50000>; + tach-ch = /bits/ 8 <0x00>; + }; + fan-1 { + pwms = <&hwmon 1 50000>; + tach-ch = /bits/ 8 <0x01>; + }; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 3342120e7a59..4f489551a193 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15950,6 +15950,12 @@ S: Maintained F: Documentation/devicetree/bindings/hwmon/nuvoton,nct6775.yaml F: drivers/hwmon/nct6775-i2c.c +NCT7363 HARDWARE MONITOR DRIVER +M: Ban Feng +L: linux-hwmon@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/hwmon/nuvoton,nct7363.yaml + NETCONSOLE M: Breno Leitao S: Maintained From 46b94c485ed197bc681da242440c6e2315697c57 Mon Sep 17 00:00:00 2001 From: Ban Feng Date: Tue, 22 Oct 2024 13:29:05 +0800 Subject: [PATCH 27/51] hwmon: Add driver for I2C chip Nuvoton NCT7363Y The NCT7363Y is a fan controller which provides up to 16 independent FAN input monitors. It can report each FAN input count values. The NCT7363Y also provides up to 16 independent PWM outputs. Each PWM can output specific PWM signal by manual mode to control the FAN duty outside. Signed-off-by: Ban Feng Message-ID: <20241022052905.4062682-3-kcfeng0@nuvoton.com> [groeck: Dropped unnecessary variable initialization, and , after { }] Signed-off-by: Guenter Roeck --- Documentation/hwmon/index.rst | 1 + Documentation/hwmon/nct7363.rst | 35 +++ MAINTAINERS | 2 + drivers/hwmon/Kconfig | 11 + drivers/hwmon/Makefile | 1 + drivers/hwmon/nct7363.c | 447 ++++++++++++++++++++++++++++++++ 6 files changed, 497 insertions(+) create mode 100644 Documentation/hwmon/nct7363.rst create mode 100644 drivers/hwmon/nct7363.c diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index 4d15664bc41e..55f1111594b2 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -175,6 +175,7 @@ Hardware Monitoring Kernel Drivers mpq8785 nct6683 nct6775 + nct7363 nct7802 nct7904 npcm750-pwm-fan diff --git a/Documentation/hwmon/nct7363.rst b/Documentation/hwmon/nct7363.rst new file mode 100644 index 000000000000..623cb4f0c8ce --- /dev/null +++ b/Documentation/hwmon/nct7363.rst @@ -0,0 +1,35 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Kernel driver nct7363 +===================== + +Supported chip: + + * Nuvoton NCT7363Y + + Prefix: nct7363 + + Addresses: I2C 0x20, 0x21, 0x22, 0x23 + +Author: Ban Feng + + +Description +----------- + +The NCT7363Y is a fan controller which provides up to 16 independent +FAN input monitors, and up to 16 independent PWM outputs with SMBus interface. + + +Sysfs entries +------------- + +Currently, the driver supports the following features: + +========== ========================================== +fanX_input provide current fan rotation value in RPM +fanX_alarm report fan low speed real status +fanX_min get or set fan count threshold + +pwmX get or set PWM fan control value. +========== ========================================== diff --git a/MAINTAINERS b/MAINTAINERS index 4f489551a193..91f7552b3609 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15955,6 +15955,8 @@ M: Ban Feng L: linux-hwmon@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/hwmon/nuvoton,nct7363.yaml +F: Documentation/hwmon/nct7363.rst +F: drivers/hwmon/nct7363.c NETCONSOLE M: Breno Leitao diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 64fefb22ecee..3ab2d02e7db3 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1681,6 +1681,17 @@ config SENSORS_NCT6775_I2C This driver can also be built as a module. If so, the module will be called nct6775-i2c. +config SENSORS_NCT7363 + tristate "Nuvoton NCT7363Y" + depends on I2C + select REGMAP_I2C + help + If you say yes here you get support for the Nuvoton NCT7363Y + hardware monitoring chip. + + This driver can also be built as a module. If so, the module + will be called nct7363. + config SENSORS_NCT7802 tristate "Nuvoton NCT7802Y" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 4ea6f7c95315..b827b92f2a78 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -172,6 +172,7 @@ obj-$(CONFIG_SENSORS_NCT6775_CORE) += nct6775-core.o nct6775-objs := nct6775-platform.o obj-$(CONFIG_SENSORS_NCT6775) += nct6775.o obj-$(CONFIG_SENSORS_NCT6775_I2C) += nct6775-i2c.o +obj-$(CONFIG_SENSORS_NCT7363) += nct7363.o obj-$(CONFIG_SENSORS_NCT7802) += nct7802.o obj-$(CONFIG_SENSORS_NCT7904) += nct7904.o obj-$(CONFIG_SENSORS_NPCM7XX) += npcm750-pwm-fan.o diff --git a/drivers/hwmon/nct7363.c b/drivers/hwmon/nct7363.c new file mode 100644 index 000000000000..be7bf32f6e68 --- /dev/null +++ b/drivers/hwmon/nct7363.c @@ -0,0 +1,447 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2023 Nuvoton Technology corporation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NCT7363_REG_FUNC_CFG_BASE(x) (0x20 + (x)) +#define NCT7363_REG_LSRS(x) (0x34 + ((x) / 8)) +#define NCT7363_REG_PWMEN_BASE(x) (0x38 + (x)) +#define NCT7363_REG_FANINEN_BASE(x) (0x41 + (x)) +#define NCT7363_REG_FANINX_HVAL(x) (0x48 + ((x) * 2)) +#define NCT7363_REG_FANINX_LVAL(x) (0x49 + ((x) * 2)) +#define NCT7363_REG_FANINX_HL(x) (0x6C + ((x) * 2)) +#define NCT7363_REG_FANINX_LL(x) (0x6D + ((x) * 2)) +#define NCT7363_REG_FSCPXDUTY(x) (0x90 + ((x) * 2)) +#define NCT7363_REG_FSCPXDIV(x) (0x91 + ((x) * 2)) + +#define PWM_SEL(x) (BIT(0) << ((x) * 2)) +#define FANIN_SEL(_x) ({typeof(_x) (x) = (_x); \ + BIT(1) << (((x) < 8) ? \ + (((x) + 8) * 2) : \ + (((x) % 8) * 2)); }) +#define ALARM_SEL(x, y) ((x) & (BIT((y) % 8))) +#define VALUE_TO_REG(x, y) (((x) >> ((y) * 8)) & 0xFF) + +#define NCT7363_FANINX_LVAL_MASK GENMASK(4, 0) +#define NCT7363_FANIN_MASK GENMASK(12, 0) + +#define NCT7363_PWM_COUNT 16 + +static inline unsigned int fan_from_reg(u16 val) +{ + if (val == NCT7363_FANIN_MASK || val == 0) + return 0; + + return (1350000UL / val); +} + +static const struct of_device_id nct7363_of_match[] = { + { .compatible = "nuvoton,nct7363", }, + { .compatible = "nuvoton,nct7362", }, + { } +}; +MODULE_DEVICE_TABLE(of, nct7363_of_match); + +struct nct7363_data { + struct regmap *regmap; + + u16 fanin_mask; + u16 pwm_mask; +}; + +static int nct7363_read_fan(struct device *dev, u32 attr, int channel, + long *val) +{ + struct nct7363_data *data = dev_get_drvdata(dev); + unsigned int reg; + u8 regval[2]; + int ret; + u16 cnt; + + switch (attr) { + case hwmon_fan_input: + /* + * High-byte register should be read first to latch + * synchronous low-byte value + */ + ret = regmap_bulk_read(data->regmap, + NCT7363_REG_FANINX_HVAL(channel), + ®val, 2); + if (ret) + return ret; + + cnt = (regval[0] << 5) | (regval[1] & NCT7363_FANINX_LVAL_MASK); + *val = fan_from_reg(cnt); + return 0; + case hwmon_fan_min: + ret = regmap_bulk_read(data->regmap, + NCT7363_REG_FANINX_HL(channel), + ®val, 2); + if (ret) + return ret; + + cnt = (regval[0] << 5) | (regval[1] & NCT7363_FANINX_LVAL_MASK); + *val = fan_from_reg(cnt); + return 0; + case hwmon_fan_alarm: + ret = regmap_read(data->regmap, + NCT7363_REG_LSRS(channel), ®); + if (ret) + return ret; + + *val = (long)ALARM_SEL(reg, channel) > 0 ? 1 : 0; + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int nct7363_write_fan(struct device *dev, u32 attr, int channel, + long val) +{ + struct nct7363_data *data = dev_get_drvdata(dev); + u8 regval[2]; + int ret; + + if (val <= 0) + return -EINVAL; + + switch (attr) { + case hwmon_fan_min: + val = clamp_val(DIV_ROUND_CLOSEST(1350000, val), + 1, NCT7363_FANIN_MASK); + regval[0] = val >> 5; + regval[1] = val & NCT7363_FANINX_LVAL_MASK; + + ret = regmap_bulk_write(data->regmap, + NCT7363_REG_FANINX_HL(channel), + regval, 2); + return ret; + default: + return -EOPNOTSUPP; + } +} + +static umode_t nct7363_fan_is_visible(const void *_data, u32 attr, int channel) +{ + const struct nct7363_data *data = _data; + + switch (attr) { + case hwmon_fan_input: + case hwmon_fan_alarm: + if (data->fanin_mask & BIT(channel)) + return 0444; + break; + case hwmon_fan_min: + if (data->fanin_mask & BIT(channel)) + return 0644; + break; + default: + break; + } + + return 0; +} + +static int nct7363_read_pwm(struct device *dev, u32 attr, int channel, + long *val) +{ + struct nct7363_data *data = dev_get_drvdata(dev); + unsigned int regval; + int ret; + + switch (attr) { + case hwmon_pwm_input: + ret = regmap_read(data->regmap, + NCT7363_REG_FSCPXDUTY(channel), ®val); + if (ret) + return ret; + + *val = (long)regval; + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int nct7363_write_pwm(struct device *dev, u32 attr, int channel, + long val) +{ + struct nct7363_data *data = dev_get_drvdata(dev); + int ret; + + switch (attr) { + case hwmon_pwm_input: + if (val < 0 || val > 255) + return -EINVAL; + + ret = regmap_write(data->regmap, + NCT7363_REG_FSCPXDUTY(channel), val); + + return ret; + + default: + return -EOPNOTSUPP; + } +} + +static umode_t nct7363_pwm_is_visible(const void *_data, u32 attr, int channel) +{ + const struct nct7363_data *data = _data; + + switch (attr) { + case hwmon_pwm_input: + if (data->pwm_mask & BIT(channel)) + return 0644; + break; + default: + break; + } + + return 0; +} + +static int nct7363_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + switch (type) { + case hwmon_fan: + return nct7363_read_fan(dev, attr, channel, val); + case hwmon_pwm: + return nct7363_read_pwm(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} + +static int nct7363_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + switch (type) { + case hwmon_fan: + return nct7363_write_fan(dev, attr, channel, val); + case hwmon_pwm: + return nct7363_write_pwm(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} + +static umode_t nct7363_is_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_fan: + return nct7363_fan_is_visible(data, attr, channel); + case hwmon_pwm: + return nct7363_pwm_is_visible(data, attr, channel); + default: + return 0; + } +} + +static const struct hwmon_channel_info *nct7363_info[] = { + HWMON_CHANNEL_INFO(fan, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM), + HWMON_CHANNEL_INFO(pwm, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT, + HWMON_PWM_INPUT), + NULL +}; + +static const struct hwmon_ops nct7363_hwmon_ops = { + .is_visible = nct7363_is_visible, + .read = nct7363_read, + .write = nct7363_write, +}; + +static const struct hwmon_chip_info nct7363_chip_info = { + .ops = &nct7363_hwmon_ops, + .info = nct7363_info, +}; + +static int nct7363_init_chip(struct nct7363_data *data) +{ + u32 func_config = 0; + int i, ret; + + /* Pin Function Configuration */ + for (i = 0; i < NCT7363_PWM_COUNT; i++) { + if (data->pwm_mask & BIT(i)) + func_config |= PWM_SEL(i); + if (data->fanin_mask & BIT(i)) + func_config |= FANIN_SEL(i); + } + + for (i = 0; i < 4; i++) { + ret = regmap_write(data->regmap, NCT7363_REG_FUNC_CFG_BASE(i), + VALUE_TO_REG(func_config, i)); + if (ret < 0) + return ret; + } + + /* PWM and FANIN Monitoring Enable */ + for (i = 0; i < 2; i++) { + ret = regmap_write(data->regmap, NCT7363_REG_PWMEN_BASE(i), + VALUE_TO_REG(data->pwm_mask, i)); + if (ret < 0) + return ret; + + ret = regmap_write(data->regmap, NCT7363_REG_FANINEN_BASE(i), + VALUE_TO_REG(data->fanin_mask, i)); + if (ret < 0) + return ret; + } + + return 0; +} + +static int nct7363_present_pwm_fanin(struct device *dev, + struct device_node *child, + struct nct7363_data *data) +{ + u8 fanin_ch[NCT7363_PWM_COUNT]; + struct of_phandle_args args; + int ret, fanin_cnt; + u8 ch, index; + + ret = of_parse_phandle_with_args(child, "pwms", "#pwm-cells", + 0, &args); + if (ret) + return ret; + + if (args.args[0] >= NCT7363_PWM_COUNT) + return -EINVAL; + data->pwm_mask |= BIT(args.args[0]); + + fanin_cnt = of_property_count_u8_elems(child, "tach-ch"); + if (fanin_cnt < 1 || fanin_cnt > NCT7363_PWM_COUNT) + return -EINVAL; + + ret = of_property_read_u8_array(child, "tach-ch", fanin_ch, fanin_cnt); + if (ret) + return ret; + + for (ch = 0; ch < fanin_cnt; ch++) { + index = fanin_ch[ch]; + if (index >= NCT7363_PWM_COUNT) + return -EINVAL; + data->fanin_mask |= BIT(index); + } + + return 0; +} + +static bool nct7363_regmap_is_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NCT7363_REG_LSRS(0) ... NCT7363_REG_LSRS(15): + case NCT7363_REG_FANINX_HVAL(0) ... NCT7363_REG_FANINX_LVAL(15): + case NCT7363_REG_FANINX_HL(0) ... NCT7363_REG_FANINX_LL(15): + case NCT7363_REG_FSCPXDUTY(0) ... NCT7363_REG_FSCPXDIV(15): + return true; + default: + return false; + } +} + +static const struct regmap_config nct7363_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .use_single_read = true, + .use_single_write = true, + .cache_type = REGCACHE_RBTREE, + .volatile_reg = nct7363_regmap_is_volatile, +}; + +static int nct7363_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct device_node *child; + struct nct7363_data *data; + struct device *hwmon_dev; + int ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->regmap = devm_regmap_init_i2c(client, &nct7363_regmap_config); + if (IS_ERR(data->regmap)) + return PTR_ERR(data->regmap); + + for_each_child_of_node(dev->of_node, child) { + ret = nct7363_present_pwm_fanin(dev, child, data); + if (ret) { + of_node_put(child); + return ret; + } + } + + /* Initialize the chip */ + ret = nct7363_init_chip(data); + if (ret) + return ret; + + hwmon_dev = + devm_hwmon_device_register_with_info(dev, client->name, data, + &nct7363_chip_info, NULL); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static struct i2c_driver nct7363_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "nct7363", + .of_match_table = nct7363_of_match, + }, + .probe = nct7363_probe, +}; + +module_i2c_driver(nct7363_driver); + +MODULE_AUTHOR("CW Ho "); +MODULE_AUTHOR("Ban Feng "); +MODULE_DESCRIPTION("NCT7363 Hardware Monitoring Driver"); +MODULE_LICENSE("GPL"); From 8b882f6ff00536a7324fc89dafdaa3c9573967ca Mon Sep 17 00:00:00 2001 From: Mariel Tinaco Date: Tue, 29 Oct 2024 09:37:32 +0800 Subject: [PATCH 28/51] dt-bindings: hwmon: ltc2978: add support for ltc7841 Add LTC7841 to supported devices of LTC2978. It has similar set of registers to LTC7880, differing only in number of output channels and some unimplemented PMBUS status and functionalities. Signed-off-by: Mariel Tinaco Acked-by: Rob Herring (Arm) Message-ID: <20241029013734.293024-2-Mariel.Tinaco@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 1f98da32f3fe..37e1dc9c7dd3 100644 --- a/Documentation/devicetree/bindings/hwmon/lltc,ltc2978.yaml +++ b/Documentation/devicetree/bindings/hwmon/lltc,ltc2978.yaml @@ -26,6 +26,7 @@ properties: - lltc,ltc3886 - lltc,ltc3887 - lltc,ltc3889 + - lltc,ltc7841 - lltc,ltc7880 - lltc,ltm2987 - lltc,ltm4664 @@ -50,6 +51,7 @@ properties: * ltc2977, ltc2979, ltc2980, ltm2987 : vout0 - vout7 * ltc2978 : vout0 - vout7 * ltc3880, ltc3882, ltc3884, ltc3886, ltc3887, ltc3889 : vout0 - vout1 + * ltc7841 : vout0 * ltc7880 : vout0 - vout1 * ltc3883 : vout0 * ltm4664 : vout0 - vout1 From baf88c49a2b1cb0ca0d060486441b8ce876ec079 Mon Sep 17 00:00:00 2001 From: Mariel Tinaco Date: Tue, 29 Oct 2024 09:37:33 +0800 Subject: [PATCH 29/51] hwmon: (pmbus/ltc7841) add support for LTC7841 - docs Add LTC7841 to compatible devices of LTC2978 Signed-off-by: Mariel Tinaco Message-ID: <20241029013734.293024-3-Mariel.Tinaco@analog.com> Signed-off-by: Guenter Roeck --- Documentation/hwmon/ltc2978.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Documentation/hwmon/ltc2978.rst b/Documentation/hwmon/ltc2978.rst index edf24e5e1e11..651ca4904c66 100644 --- a/Documentation/hwmon/ltc2978.rst +++ b/Documentation/hwmon/ltc2978.rst @@ -1,3 +1,5 @@ +.. SPDX-License-Identifier: GPL-2.0 + Kernel driver ltc2978 ===================== @@ -117,6 +119,14 @@ Supported chips: Datasheet: https://www.analog.com/en/products/ltc3889 + * Linear Technology LTC7841 + + Prefix: 'ltc7841' + + Addresses scanned: - + + Datasheet: https://www.analog.com/en/products/ltc7841 + * Linear Technology LTC7880 Prefix: 'ltc7880' @@ -290,6 +300,7 @@ in[N]_label "vout[1-8]". LTC7880, LTM4644, LTM4675, LTM4676, LTM4677, LTM4678, LTM4680, LTM4700: N=2-3 - LTC3883: N=2 + - LTC7841: N=2 in[N]_input Measured output voltage. @@ -420,6 +431,7 @@ curr[N]_label "iout[1-4]". LTM4664, LTM4675, LTM4676, LTM4677, LTM4678, LTM4680, LTM4700: N=2-3 - LTC3883: N=2 + - LTC7841: N=2 curr[N]_input Measured output current. From 9c1716bc519c11615898c587b2e30398d083ddee Mon Sep 17 00:00:00 2001 From: Mariel Tinaco Date: Tue, 29 Oct 2024 09:37:34 +0800 Subject: [PATCH 30/51] hwmon: (pmbus/ltc2978) add support for ltc7841 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for LTC7841. The LTC7841 is a high performance PolyPhase® single output synchronous boost converter controller. Multiphase operation reduces input and output capacitor requirements and allows the use of smaller inductors than the single-phase equivalent. The relevant registers in the LTC7841 are similar to the LTC7880, only reduced by some amount. So it's just a matter of adding the chip id. The device also doesn't support polling, on top of the reduced register set, so a separate case for setting the chip info is added. Signed-off-by: Mariel Tinaco Message-ID: <20241029013734.293024-4-Mariel.Tinaco@analog.com> Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/Kconfig | 6 +++--- drivers/hwmon/pmbus/ltc2978.c | 20 ++++++++++++++++++-- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index a4f02cad92fd..f6d352841953 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -224,9 +224,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, LTC7880, - LTM4644, LTM4675, LTM4676, LTM4677, LTM4678, LTM4680, LTM4686, - and LTM4700. + LTC3880, LTC3883, LTC3884, LTC3886, LTC3887, LTC3889, LTC7841, + LTC7880, LTM4644, 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 73a86f4d6472..a6eb4d4b5487 100644 --- a/drivers/hwmon/pmbus/ltc2978.c +++ b/drivers/hwmon/pmbus/ltc2978.c @@ -23,7 +23,8 @@ enum chips { /* Managers */ ltc2972, ltc2974, ltc2975, ltc2977, ltc2978, ltc2979, ltc2980, /* Controllers */ - ltc3880, ltc3882, ltc3883, ltc3884, ltc3886, ltc3887, ltc3889, ltc7132, ltc7880, + ltc3880, ltc3882, ltc3883, ltc3884, ltc3886, ltc3887, ltc3889, ltc7132, + ltc7841, ltc7880, /* Modules */ ltm2987, ltm4664, ltm4675, ltm4676, ltm4677, ltm4678, ltm4680, ltm4686, ltm4700, @@ -50,7 +51,7 @@ enum chips { #define LTC3880_MFR_CLEAR_PEAKS 0xe3 #define LTC3880_MFR_TEMPERATURE2_PEAK 0xf4 -/* LTC3883, LTC3884, LTC3886, LTC3889, LTC7132, LTC7880 */ +/* LTC3883, LTC3884, LTC3886, LTC3889, LTC7132, LTC7841 and LTC7880 only */ #define LTC3883_MFR_IIN_PEAK 0xe1 /* LTC2975 only */ @@ -80,6 +81,7 @@ enum chips { #define LTC3887_ID 0x4700 #define LTC3889_ID 0x4900 #define LTC7132_ID 0x4CE0 +#define LTC7841_ID 0x40D0 #define LTC7880_ID 0x49E0 #define LTM2987_ID_A 0x8010 /* A/B for two die IDs */ #define LTM2987_ID_B 0x8020 @@ -548,6 +550,7 @@ static const struct i2c_device_id ltc2978_id[] = { {"ltc3887", ltc3887}, {"ltc3889", ltc3889}, {"ltc7132", ltc7132}, + {"ltc7841", ltc7841}, {"ltc7880", ltc7880}, {"ltm2987", ltm2987}, {"ltm4664", ltm4664}, @@ -654,6 +657,8 @@ static int ltc2978_get_id(struct i2c_client *client) return ltc3889; else if (chip_id == LTC7132_ID) return ltc7132; + else if (chip_id == LTC7841_ID) + return ltc7841; else if (chip_id == LTC7880_ID) return ltc7880; else if (chip_id == LTM2987_ID_A || chip_id == LTM2987_ID_B) @@ -854,6 +859,16 @@ static int ltc2978_probe(struct i2c_client *client) | PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; break; + case ltc7841: + data->features |= FEAT_CLEAR_PEAKS; + info->read_word_data = ltc3883_read_word_data; + info->pages = LTC3883_NUM_PAGES; + info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN + | PMBUS_HAVE_STATUS_INPUT + | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT + | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; + break; default: return -ENODEV; } @@ -907,6 +922,7 @@ static const struct of_device_id ltc2978_of_match[] = { { .compatible = "lltc,ltc3887" }, { .compatible = "lltc,ltc3889" }, { .compatible = "lltc,ltc7132" }, + { .compatible = "lltc,ltc7841" }, { .compatible = "lltc,ltc7880" }, { .compatible = "lltc,ltm2987" }, { .compatible = "lltc,ltm4664" }, From 0196d07f0e3ba3f5639ea5a74f59af8c990b95f0 Mon Sep 17 00:00:00 2001 From: Wenliang Yan Date: Wed, 6 Nov 2024 10:05:47 -0500 Subject: [PATCH 31/51] dt-bindings: Add SY24655 to ina2xx devicetree bindings SY24655 is similar to INA226. Its supply voltage and pin definitions are therefore the same. Compared to INA226, SY24655 has two additional registers for configuring and calculating average power. Signed-off-by: Wenliang Yan Acked-by: Conor Dooley Message-ID: <20241106150547.2538-2-wenliang202407@163.com> Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml b/Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml index 6ae961732e6b..05a9cb36cd82 100644 --- a/Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml +++ b/Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml @@ -20,6 +20,7 @@ description: | properties: compatible: enum: + - silergy,sy24655 - ti,ina209 - ti,ina219 - ti,ina220 From 52172ad87a22ed6e687ca678da21d3c949bc89a1 Mon Sep 17 00:00:00 2001 From: Wenliang Yan Date: Wed, 6 Nov 2024 10:05:46 -0500 Subject: [PATCH 32/51] hwmon: (ina226) Add support for SY24655 SY24655: Support for current and voltage detection as well as power calculation. Signed-off-by: Wenliang Yan Message-ID: <20241106150547.2538-1-wenliang202407@163.com> [groeck: Changed order of compatible entries; dropped spurious extra return statement in is_visible(); fixed code problems] Signed-off-by: Guenter Roeck --- Documentation/hwmon/ina2xx.rst | 27 +++++++++- drivers/hwmon/Kconfig | 2 +- drivers/hwmon/ina2xx.c | 94 ++++++++++++++++++++++++++++++++-- 3 files changed, 116 insertions(+), 7 deletions(-) diff --git a/Documentation/hwmon/ina2xx.rst b/Documentation/hwmon/ina2xx.rst index 1ce161e6c0bf..a3860aae444c 100644 --- a/Documentation/hwmon/ina2xx.rst +++ b/Documentation/hwmon/ina2xx.rst @@ -63,6 +63,17 @@ Supported chips: https://www.ti.com/ + * Silergy SY24655 + + Prefix: 'sy24655' + + Addresses: I2C 0x40 - 0x4f + + Datasheet: Publicly available at the Silergy website + + https://us1.silergy.com/ + + Author: Lothar Felten Description @@ -85,6 +96,11 @@ bus supply voltage. INA260 is a high or low side current and power monitor with integrated shunt resistor. +The SY24655 is a high- and low-side current shunt and power monitor with an I2C +interface. The SY24655 supports both shunt drop and supply voltage, with +programmable calibration value and conversion times. The SY24655 can also +calculate average power for use in energy conversion. + The shunt value in micro-ohms can be set via platform data or device tree at compile-time or via the shunt_resistor attribute in sysfs at run-time. Please refer to the Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml for bindings @@ -108,8 +124,8 @@ power1_input Power(uW) measurement channel shunt_resistor Shunt resistance(uOhm) channel (not for ina260) ======================= =============================================== -Additional sysfs entries for ina226, ina230, ina231, and ina260 ---------------------------------------------------------------- +Additional sysfs entries for ina226, ina230, ina231, ina260, and sy24655 +------------------------------------------------------------------------ ======================= ==================================================== curr1_lcrit Critical low current @@ -130,6 +146,13 @@ update_interval data conversion time; affects number of samples used to average results for shunt and bus voltages. ======================= ==================================================== +Sysfs entries for sy24655 only +------------------------------ + +======================= ==================================================== +power1_average average power from last reading to the present. +======================= ==================================================== + .. note:: - Configure `shunt_resistor` before configure `power1_crit`, because power diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 3ab2d02e7db3..863b61330c0a 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -2189,7 +2189,7 @@ config SENSORS_INA2XX select REGMAP_I2C help If you say yes here you get support for INA219, INA220, INA226, - INA230, INA231, and INA260 power monitor chips. + INA230, INA231, INA260, and SY24655 power monitor chips. The INA2xx driver is configured for the default configuration of the part as described in the datasheet. diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index cecc80a41a97..345fe7db9de9 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -51,12 +51,19 @@ #define INA226_ALERT_LIMIT 0x07 #define INA226_DIE_ID 0xFF -#define INA2XX_MAX_REGISTERS 8 +/* SY24655 register definitions */ +#define SY24655_EIN 0x0A +#define SY24655_ACCUM_CONFIG 0x0D +#define INA2XX_MAX_REGISTERS 0x0D /* settings - depend on use case */ #define INA219_CONFIG_DEFAULT 0x399F /* PGA=8 */ #define INA226_CONFIG_DEFAULT 0x4527 /* averages=16 */ #define INA260_CONFIG_DEFAULT 0x6527 /* averages=16 */ +#define SY24655_CONFIG_DEFAULT 0x4527 /* averages=16 */ + +/* (only for sy24655) */ +#define SY24655_ACCUM_CONFIG_DEFAULT 0x044C /* continuous mode, clear after read*/ /* worst case is 68.10 ms (~14.6Hz, ina219) */ #define INA2XX_CONVERSION_RATE 15 @@ -97,6 +104,7 @@ static bool ina2xx_writeable_reg(struct device *dev, unsigned int reg) case INA2XX_CALIBRATION: case INA226_MASK_ENABLE: case INA226_ALERT_LIMIT: + case SY24655_ACCUM_CONFIG: return true; default: return false; @@ -127,12 +135,13 @@ static const struct regmap_config ina2xx_regmap_config = { .writeable_reg = ina2xx_writeable_reg, }; -enum ina2xx_ids { ina219, ina226, ina260 }; +enum ina2xx_ids { ina219, ina226, ina260, sy24655 }; struct ina2xx_config { u16 config_default; bool has_alerts; /* chip supports alerts and limits */ bool has_ishunt; /* chip has internal shunt resistor */ + bool has_power_average; /* chip has internal shunt resistor */ int calibration_value; int shunt_div; int bus_voltage_shift; @@ -149,6 +158,7 @@ struct ina2xx_data { long power_lsb_uW; struct mutex config_lock; struct regmap *regmap; + struct i2c_client *client; }; static const struct ina2xx_config ina2xx_config[] = { @@ -161,6 +171,7 @@ static const struct ina2xx_config ina2xx_config[] = { .power_lsb_factor = 20, .has_alerts = false, .has_ishunt = false, + .has_power_average = false, }, [ina226] = { .config_default = INA226_CONFIG_DEFAULT, @@ -171,6 +182,7 @@ static const struct ina2xx_config ina2xx_config[] = { .power_lsb_factor = 25, .has_alerts = true, .has_ishunt = false, + .has_power_average = false, }, [ina260] = { .config_default = INA260_CONFIG_DEFAULT, @@ -180,6 +192,18 @@ static const struct ina2xx_config ina2xx_config[] = { .power_lsb_factor = 8, .has_alerts = true, .has_ishunt = true, + .has_power_average = false, + }, + [sy24655] = { + .config_default = SY24655_CONFIG_DEFAULT, + .calibration_value = 4096, + .shunt_div = 400, + .bus_voltage_shift = 0, + .bus_voltage_lsb = 1250, + .power_lsb_factor = 25, + .has_alerts = true, + .has_ishunt = false, + .has_power_average = true, }, }; @@ -485,6 +509,41 @@ static int ina2xx_in_read(struct device *dev, u32 attr, int channel, long *val) return 0; } +/* + * Configuring the READ_EIN (bit 10) of the ACCUM_CONFIG register to 1 + * can clear accumulator and sample_count after reading the EIN register. + * This way, the average power between the last read and the current + * read can be obtained. By combining with accurate time data from + * outside, the energy consumption during that period can be calculated. + */ +static int sy24655_average_power_read(struct ina2xx_data *data, u8 reg, long *val) +{ + u8 template[6]; + int ret; + long accumulator_24, sample_count; + + /* 48-bit register read */ + ret = i2c_smbus_read_i2c_block_data(data->client, reg, 6, template); + if (ret < 0) + return ret; + if (ret != 6) + return -EIO; + accumulator_24 = ((template[3] << 16) | + (template[4] << 8) | + template[5]); + sample_count = ((template[0] << 16) | + (template[1] << 8) | + template[2]); + if (sample_count <= 0) { + *val = 0; + return 0; + } + + *val = DIV_ROUND_CLOSEST(accumulator_24, sample_count) * data->power_lsb_uW; + + return 0; +} + static int ina2xx_power_read(struct device *dev, u32 attr, long *val) { struct ina2xx_data *data = dev_get_drvdata(dev); @@ -492,6 +551,8 @@ static int ina2xx_power_read(struct device *dev, u32 attr, long *val) switch (attr) { case hwmon_power_input: return ina2xx_read_init(dev, INA2XX_POWER, val); + case hwmon_power_average: + return sy24655_average_power_read(data, SY24655_EIN, val); case hwmon_power_crit: return ina226_alert_limit_read(data, INA226_POWER_OVER_LIMIT_MASK, INA2XX_POWER, val); @@ -651,6 +712,7 @@ static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type { const struct ina2xx_data *data = _data; bool has_alerts = data->config->has_alerts; + bool has_power_average = data->config->has_power_average; enum ina2xx_ids chip = data->chip; switch (type) { @@ -702,6 +764,10 @@ static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type if (has_alerts) return 0444; break; + case hwmon_power_average: + if (has_power_average) + return 0444; + break; default: break; } @@ -734,7 +800,8 @@ static const struct hwmon_channel_info * const ina2xx_info[] = { HWMON_CHANNEL_INFO(curr, HWMON_C_INPUT | HWMON_C_CRIT | HWMON_C_CRIT_ALARM | HWMON_C_LCRIT | HWMON_C_LCRIT_ALARM), HWMON_CHANNEL_INFO(power, - HWMON_P_INPUT | HWMON_P_CRIT | HWMON_P_CRIT_ALARM), + HWMON_P_INPUT | HWMON_P_CRIT | HWMON_P_CRIT_ALARM | + HWMON_P_AVERAGE), NULL }; @@ -839,6 +906,19 @@ static int ina2xx_init(struct device *dev, struct ina2xx_data *data) INA226_ALERT_LATCH_ENABLE | FIELD_PREP(INA226_ALERT_POLARITY, active_high)); } + if (data->config->has_power_average) { + if (data->chip == sy24655) { + /* + * Initialize the power accumulation method to continuous + * mode and clear the EIN register after each read of the + * EIN register + */ + ret = regmap_write(regmap, SY24655_ACCUM_CONFIG, + SY24655_ACCUM_CONFIG_DEFAULT); + if (ret < 0) + return ret; + } + } if (data->config->has_ishunt) return 0; @@ -868,6 +948,7 @@ static int ina2xx_probe(struct i2c_client *client) return -ENOMEM; /* set the device type */ + data->client = client; data->config = &ina2xx_config[chip]; data->chip = chip; mutex_init(&data->config_lock); @@ -906,11 +987,16 @@ static const struct i2c_device_id ina2xx_id[] = { { "ina230", ina226 }, { "ina231", ina226 }, { "ina260", ina260 }, + { "sy24655", sy24655 }, { } }; MODULE_DEVICE_TABLE(i2c, ina2xx_id); static const struct of_device_id __maybe_unused ina2xx_of_match[] = { + { + .compatible = "silergy,sy24655", + .data = (void *)sy24655 + }, { .compatible = "ti,ina219", .data = (void *)ina219 @@ -935,7 +1021,7 @@ static const struct of_device_id __maybe_unused ina2xx_of_match[] = { .compatible = "ti,ina260", .data = (void *)ina260 }, - { }, + { } }; MODULE_DEVICE_TABLE(of, ina2xx_of_match); From 544177f20be2eebc7dd2a55feaf186ac68acc22d Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Tue, 5 Nov 2024 18:58:38 +0100 Subject: [PATCH 33/51] hwmon: (pmbus) add documentation for existing flags PMBUS_NO_WRITE_PROTECT and PMBUS_USE_COEFFICIENTS_CMD flags have been added to pmbus, but the corresponding documentation was not updated. Update the documentation before adding new flags Signed-off-by: Jerome Brunet Message-ID: <20241105-tps25990-v4-1-0e312ac70b62@baylibre.com> Signed-off-by: Guenter Roeck --- Documentation/hwmon/pmbus-core.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Documentation/hwmon/pmbus-core.rst b/Documentation/hwmon/pmbus-core.rst index 1eaf2b015837..686a00265bf7 100644 --- a/Documentation/hwmon/pmbus-core.rst +++ b/Documentation/hwmon/pmbus-core.rst @@ -308,6 +308,10 @@ currently provides a flags field with four bits used:: #define PMBUS_READ_STATUS_AFTER_FAILED_CHECK BIT(3) + #define PMBUS_NO_WRITE_PROTECT BIT(4) + + #define PMBUS_USE_COEFFICIENTS_CMD BIT(5) + struct pmbus_platform_data { u32 flags; /* Device specific flags */ @@ -358,3 +362,14 @@ This can be done by reading a known register. By setting this flag the driver will try to read the STATUS register after each failed register check. This read may fail, but it will put the chip into a known state. + +PMBUS_NO_WRITE_PROTECT + +Some PMBus chips respond with invalid data when reading the WRITE_PROTECT +register. For such chips, this flag should be set so that the PMBus core +driver doesn't use the WRITE_PROTECT command to determine its behavior. + +PMBUS_USE_COEFFICIENTS_CMD + +When this flag is set the PMBus core driver will use the COEFFICIENTS +register to initialize the coefficients for the direct mode format. From 7a750b3986b7910711a5525dde0c84b541e72746 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Tue, 5 Nov 2024 18:58:39 +0100 Subject: [PATCH 34/51] hwmon: (pmbus/core) allow drivers to override WRITE_PROTECT Use _pmbus_read_byte_data() rather than calling smbus directly to check the write protection status. This give a chance to device implementing write protection differently to report back on the actual write protection status. Signed-off-by: Jerome Brunet Message-ID: <20241105-tps25990-v4-2-0e312ac70b62@baylibre.com> [groeck: Fix page parameter of _pmbus_read_byte_data()] Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus_core.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index ce7fd4ca9d89..7f88b5d08046 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -2719,9 +2719,7 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, * limit registers need to be disabled. */ if (!(data->flags & PMBUS_NO_WRITE_PROTECT)) { - pmbus_wait(client); - ret = i2c_smbus_read_byte_data(client, PMBUS_WRITE_PROTECT); - pmbus_update_ts(client, false); + ret = _pmbus_read_byte_data(client, -1, PMBUS_WRITE_PROTECT); if (ret > 0 && (ret & PB_WP_ANY)) data->flags |= PMBUS_WRITE_PROTECTED | PMBUS_SKIP_STATUS_CHECK; From 509c3a362675bc995771df74d545548f98e37621 Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Tue, 5 Nov 2024 18:58:42 +0100 Subject: [PATCH 35/51] hwmon: (pmbus/core) clear faults after setting smbalert mask pmbus_write_smbalert_mask() ignores the errors if the chip can't set smbalert mask the standard way. It is not necessarily a problem for the irq support if the chip is otherwise properly setup but it may leave an uncleared fault behind. pmbus_core will pick the fault on the next register_check(). The register check will fails regardless of the actual register support by the chip. This leads to missing attributes or debugfs entries for chips that should provide them. We cannot rely on register_check() as PMBUS_SMBALERT_MASK may be read-only. Unconditionally clear the page fault after setting PMBUS_SMBALERT_MASK to avoid the problem. Suggested-by: Guenter Roeck Fixes: 221819ca4c36 ("hwmon: (pmbus/core) Add interrupt support") Signed-off-by: Jerome Brunet Message-ID: <20241105-tps25990-v4-5-0e312ac70b62@baylibre.com> Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus_core.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 7f88b5d08046..a0109296a994 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -3277,7 +3277,17 @@ static int pmbus_regulator_notify(struct pmbus_data *data, int page, int event) static int pmbus_write_smbalert_mask(struct i2c_client *client, u8 page, u8 reg, u8 val) { - return _pmbus_write_word_data(client, page, PMBUS_SMBALERT_MASK, reg | (val << 8)); + int ret; + + ret = _pmbus_write_word_data(client, page, PMBUS_SMBALERT_MASK, reg | (val << 8)); + + /* + * Clear fault systematically in case writing PMBUS_SMBALERT_MASK + * is not supported by the chip. + */ + pmbus_clear_fault_page(client, page); + + return ret; } static irqreturn_t pmbus_fault_handler(int irq, void *pdata) From 2fc032ec1de485c64d8561dc7fa0de3f7acf147d Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Tue, 5 Nov 2024 18:58:43 +0100 Subject: [PATCH 36/51] dt-bindings: hwmon: pmbus: add ti tps25990 support Add DT binding for the Texas Instruments TPS25990 eFuse Reviewed-by: Conor Dooley Signed-off-by: Jerome Brunet Message-ID: <20241105-tps25990-v4-6-0e312ac70b62@baylibre.com> Signed-off-by: Guenter Roeck --- .../bindings/hwmon/pmbus/ti,tps25990.yaml | 83 +++++++++++++++++++ MAINTAINERS | 6 ++ 2 files changed, 89 insertions(+) create mode 100644 Documentation/devicetree/bindings/hwmon/pmbus/ti,tps25990.yaml diff --git a/Documentation/devicetree/bindings/hwmon/pmbus/ti,tps25990.yaml b/Documentation/devicetree/bindings/hwmon/pmbus/ti,tps25990.yaml new file mode 100644 index 000000000000..f4115870e450 --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/pmbus/ti,tps25990.yaml @@ -0,0 +1,83 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- + +$id: http://devicetree.org/schemas/hwmon/pmbus/ti,tps25990.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Texas Instruments TPS25990 Stackable eFuse + +maintainers: + - Jerome Brunet + +description: + The TI TPS25990 is an integrated, high-current circuit + protection and power management device with PMBUS interface + +properties: + compatible: + const: ti,tps25990 + + reg: + maxItems: 1 + + ti,rimon-micro-ohms: + description: + micro Ohms value of the resistance installed between the Imon pin + and the ground reference. + + interrupts: + description: PMBUS SMB Alert Interrupt. + maxItems: 1 + + regulators: + type: object + description: + list of regulators provided by this controller. + + properties: + vout: + $ref: /schemas/regulator/regulator.yaml# + type: object + unevaluatedProperties: false + + gpdac1: + $ref: /schemas/regulator/regulator.yaml# + type: object + unevaluatedProperties: false + + gpdac2: + $ref: /schemas/regulator/regulator.yaml# + type: object + unevaluatedProperties: false + additionalProperties: false + +required: + - compatible + - reg + - ti,rimon-micro-ohms + +additionalProperties: false + +examples: + - | + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + + hw-monitor@46 { + compatible = "ti,tps25990"; + reg = <0x46>; + + interrupt-parent = <&gpio>; + interrupts = <42 IRQ_TYPE_LEVEL_LOW>; + ti,rimon-micro-ohms = <1370000000>; + + regulators { + cpu0_vout: vout { + regulator-name = "main_cpu0"; + }; + }; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 91f7552b3609..03ed02bd7e4f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -22879,6 +22879,12 @@ F: include/linux/dma/k3-udma-glue.h F: include/linux/dma/ti-cppi5.h X: drivers/dma/ti/cppi41.c +TEXAS INSTRUMENTS TPS25990 HARDWARE MONITOR DRIVER +M: Jerome Brunet +L: linux-hwmon@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/hwmon/pmbus/ti,tps25990.yaml + TEXAS INSTRUMENTS TPS23861 PoE PSE DRIVER M: Robert Marko M: Luka Perkov From 2e5bdf8aa2f49d06200fb6e645673c819135b614 Mon Sep 17 00:00:00 2001 From: Abhinav Saxena Date: Wed, 6 Nov 2024 18:38:49 -0700 Subject: [PATCH 37/51] hwmon: (f71882fg) Fix grammar in fan speed trip points explanation Fix several grammatical issues in the fan speed trip points documentation: - Replace awkward "which % the fan should run at at" construction with clearer "that specify the percentage at which" - Fix incorrect "is chip depended" to "are chip dependent" for correct verb agreement and adjective form - Improve readability by reorganizing first sentence and separating the complex explanation into simpler parts - Add hyphen before "see" to improve readability - Remove redundant "at" in temperature description No functional changes, documentation only. Signed-off-by: Abhinav Saxena Message-ID: <20241107013849.47833-1-xandfury@gmail.com> Signed-off-by: Guenter Roeck --- Documentation/hwmon/f71882fg.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Documentation/hwmon/f71882fg.rst b/Documentation/hwmon/f71882fg.rst index 38e30fbd4806..53d32bf06b70 100644 --- a/Documentation/hwmon/f71882fg.rst +++ b/Documentation/hwmon/f71882fg.rst @@ -178,10 +178,11 @@ Writing an unsupported mode will result in an invalid parameter error. available on the F71858FG / F8000 if the fan channel is in RPM mode. * 2: Normal auto mode - You can define a number of temperature/fan speed trip points, which % the - fan should run at at this temp and which temp a fan should follow using the - standard sysfs interface. The number and type of trip points is chip - depended, see which files are available in sysfs. + You can define a number of temperature/fan speed trip points that specify + the percentage at which the fan should run at each temperature, and which + temperature sensor a fan should follow, using the standard sysfs interface. + The number and type of trip points are chip dependent - see the available + files in sysfs. Fan/PWM channel 3 of the F8000 is always in this mode! * 3: Thermostat mode (Only available on the F8000 when in duty cycle mode) From c412d7af010154ce9273dbc0296ae9327a605256 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 8 Nov 2024 14:43:48 +0200 Subject: [PATCH 38/51] hwmon: (jc42) Drop of_match_ptr() protection This prevents use of this driver with ACPI via PRP0001 and is an example of an anti pattern I'm trying to remove from the kernel. Hence drop from this driver. Also switch of.h for mod_devicetable.h include given use of struct of_device_id which is defined in that header. Reported-by: Konstantin Aladyshev Closes: https://lore.kernel.org/r/CACSj6VW7WKv5tiAkLCvSujENJvXq1Mc7_7vtkQsRSz3JGY0i3Q@mail.gmail.com Signed-off-by: Andy Shevchenko Message-ID: <20241108124348.1392473-1-andriy.shevchenko@linux.intel.com> Signed-off-by: Guenter Roeck --- drivers/hwmon/jc42.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/hwmon/jc42.c b/drivers/hwmon/jc42.c index c459dce496a6..06f0ab2f52fa 100644 --- a/drivers/hwmon/jc42.c +++ b/drivers/hwmon/jc42.c @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -19,7 +20,6 @@ #include #include #include -#include #include /* Addresses to scan */ @@ -595,20 +595,18 @@ static const struct i2c_device_id jc42_id[] = { }; MODULE_DEVICE_TABLE(i2c, jc42_id); -#ifdef CONFIG_OF static const struct of_device_id jc42_of_ids[] = { { .compatible = "jedec,jc-42.4-temp", }, { } }; MODULE_DEVICE_TABLE(of, jc42_of_ids); -#endif static struct i2c_driver jc42_driver = { .class = I2C_CLASS_HWMON, .driver = { .name = "jc42", .pm = JC42_DEV_PM_OPS, - .of_match_table = of_match_ptr(jc42_of_ids), + .of_match_table = jc42_of_ids, }, .probe = jc42_probe, .remove = jc42_remove, From 1f22c0725705d2100b0a63dd77130c30d90fd6ad Mon Sep 17 00:00:00 2001 From: Abhinav Saxena Date: Fri, 8 Nov 2024 14:22:01 -0700 Subject: [PATCH 39/51] hwmon: (sch5627, max31827) Fix typos in driver documentation Fix some typos in hwmon/sch5627 and hwmon/max31827 reported by checkpatch.pl. These changes are purely documentation cleanup with no functional modifications. Signed-off-by: Abhinav Saxena Acked-by: Randy Dunlap Reviewed-by: Bagas Sanjaya Message-ID: <20241108212201.144482-1-xandfury@gmail.com> [groeck: Updated subject] Signed-off-by: Guenter Roeck --- Documentation/hwmon/sch5627.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/hwmon/sch5627.rst b/Documentation/hwmon/sch5627.rst index 8639dff234fc..5f521c6e90ab 100644 --- a/Documentation/hwmon/sch5627.rst +++ b/Documentation/hwmon/sch5627.rst @@ -39,7 +39,7 @@ Controlling fan speed --------------------- The SCH5627 allows for partially controlling the fan speed. If a temperature -channel excedes tempX_max, all fans are forced to maximum speed. The same is not +channel exceeds tempX_max, all fans are forced to maximum speed. The same is not true for tempX_crit, presumably some other measures to cool down the system are take in this case. In which way the value of fanX_min affects the fan speed is currently unknown. From 4585580781d69d8627e15f027873e46282ea3787 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Mon, 11 Nov 2024 12:31:58 -0500 Subject: [PATCH 40/51] dt-bindings: hwmon: ti,tmp108: Add nxp,p3t1085 compatible string The register layout of P3T1085 is the same as ti,tmp108. Add compatible string nxp,p3t1085 for it. The difference of P3T1085 is support I3C. Acked-by: Krzysztof Kozlowski Signed-off-by: Frank Li Message-ID: <20241111-p3t1085-v3-1-bff511550aad@nxp.com> Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/hwmon/ti,tmp108.yaml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/hwmon/ti,tmp108.yaml b/Documentation/devicetree/bindings/hwmon/ti,tmp108.yaml index 0ad10d43fac0..a6f9319e068d 100644 --- a/Documentation/devicetree/bindings/hwmon/ti,tmp108.yaml +++ b/Documentation/devicetree/bindings/hwmon/ti,tmp108.yaml @@ -4,22 +4,26 @@ $id: http://devicetree.org/schemas/hwmon/ti,tmp108.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: TMP108 temperature sensor +title: TMP108/P3T1085(NXP) temperature sensor maintainers: - Krzysztof Kozlowski description: | - The TMP108 is a digital-output temperature sensor with a + The TMP108/P3T1085(NXP) is a digital-output temperature sensor with a dynamically-programmable limit window, and under- and overtemperature alert functions. + P3T1085(NXP) support I3C. + Datasheets: https://www.ti.com/product/TMP108 + https://www.nxp.com/docs/en/data-sheet/P3T1085UK.pdf properties: compatible: enum: + - nxp,p3t1085 - ti,tmp108 interrupts: From 11d35624d7696a64724160199fc94587010d7cb2 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Mon, 11 Nov 2024 12:31:59 -0500 Subject: [PATCH 41/51] hwmon: (tmp108) Add NXP p3t1085 support Add compatible string 'nxp,p3t1085' since p3t1085's register layout is the same as tmp108. The p3t1085 supports I3C interface. Update document tmp108.rst and Kconfig's help context. Signed-off-by: Frank Li Message-ID: <20241111-p3t1085-v3-2-bff511550aad@nxp.com> Signed-off-by: Guenter Roeck --- Documentation/hwmon/tmp108.rst | 8 ++++++++ drivers/hwmon/Kconfig | 2 +- drivers/hwmon/tmp108.c | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Documentation/hwmon/tmp108.rst b/Documentation/hwmon/tmp108.rst index 6df7cf1b42f4..bc4941d98268 100644 --- a/Documentation/hwmon/tmp108.rst +++ b/Documentation/hwmon/tmp108.rst @@ -3,6 +3,14 @@ Kernel driver tmp108 Supported chips: + * NXP P3T1085 + + Prefix: 'p3t1085' + + Addresses scanned: none + + Datasheet: https://www.nxp.com/docs/en/data-sheet/P3T1085UK.pdf + * Texas Instruments TMP108 Prefix: 'tmp108' diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 863b61330c0a..7b9b52fa731c 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -2300,7 +2300,7 @@ config SENSORS_TMP108 select REGMAP_I2C help If you say yes here you get support for Texas Instruments TMP108 - sensor chips. + sensor chips and NXP P3T1085. This driver can also be built as a module. If so, the module will be called tmp108. diff --git a/drivers/hwmon/tmp108.c b/drivers/hwmon/tmp108.c index a82bbc959eb1..b561b452d8d3 100644 --- a/drivers/hwmon/tmp108.c +++ b/drivers/hwmon/tmp108.c @@ -420,6 +420,7 @@ MODULE_DEVICE_TABLE(i2c, tmp108_i2c_ids); #ifdef CONFIG_OF static const struct of_device_id tmp108_of_ids[] = { + { .compatible = "nxp,p3t1085", }, { .compatible = "ti,tmp108", }, {} }; From 80bc64201e78f18248343c8986a55fe342a19e17 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 6 Nov 2024 19:59:04 +0100 Subject: [PATCH 42/51] dt-bindings: hwmon: pwm-fan: Document start from stopped state properties Delta AFC0612DB-F00 fan has to be set to at least 30% PWM duty cycle to spin up from a stopped state, and can be afterward throttled down to lower PWM duty cycle. Introduce support for operating such fans which need to start at higher PWM duty cycle first and can slow down next. Document two new DT properties, "fan-stop-to-start-percent" and "fan-stop-to-start-usec". The former describes the minimum percent of fan RPM at which it will surely spin up from stopped state. This value can be found in the fan datasheet and can be converted to PWM duty cycle easily. The "fan-stop-to-start-usec" describes the minimum time in microseconds for which the fan has to be set to stopped state start RPM for the fan to surely spin up. Signed-off-by: Marek Vasut Acked-by: Conor Dooley Message-ID: <20241106185925.223736-1-marex@denx.de> Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/hwmon/pwm-fan.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Documentation/devicetree/bindings/hwmon/pwm-fan.yaml b/Documentation/devicetree/bindings/hwmon/pwm-fan.yaml index 4e5abf7580cc..8b4ed5ee962f 100644 --- a/Documentation/devicetree/bindings/hwmon/pwm-fan.yaml +++ b/Documentation/devicetree/bindings/hwmon/pwm-fan.yaml @@ -31,6 +31,16 @@ properties: it must be self resetting edge interrupts. maxItems: 1 + fan-stop-to-start-percent: + description: + Minimum fan RPM in percent to start when stopped. + minimum: 0 + maximum: 100 + + fan-stop-to-start-us: + description: + Time to wait in microseconds after start when stopped. + pulses-per-revolution: description: Define the number of pulses per fan revolution for each tachometer From 255ab27a0743865e625d7a6c385d6d10d38eafd5 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 6 Nov 2024 19:59:05 +0100 Subject: [PATCH 43/51] hwmon: (pwm-fan) Introduce start from stopped state handling Delta AFC0612DB-F00 fan has to be set to at least 30% PWM duty cycle to spin up from a stopped state, and can be afterward throttled down to lower PWM duty cycle. Introduce support for operating such fans which need to start at higher PWM duty cycle first and can slow down next. Introduce two new DT properties, "fan-stop-to-start-percent" and "fan-stop-to-start-us". The former describes the minimum percent of fan RPM at which it will surely spin up from stopped state. This value can be found in the fan datasheet and can be converted to PWM duty cycle easily. The "fan-stop-to-start-us" describes the minimum time in microseconds for which the fan has to be set to stopped state start RPM for the fan to surely spin up. Adjust the PWM setting code such that if the PWM duty cycle is below the minimum duty cycle needed by the fan to spin up from stopped state, then first set the PWM duty cycle to the minimum duty cycle needed by the fan to spin up from stopped state, then wait the time necessary for the fan to spin up from stopped state, and finally set the PWM duty cycle to the one desired by user. Signed-off-by: Marek Vasut Message-ID: <20241106185925.223736-2-marex@denx.de> Signed-off-by: Guenter Roeck --- drivers/hwmon/pwm-fan.c | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c index c434db4656e7..53a1a968d00d 100644 --- a/drivers/hwmon/pwm-fan.c +++ b/drivers/hwmon/pwm-fan.c @@ -7,6 +7,7 @@ * Author: Kamil Debski */ +#include #include #include #include @@ -60,6 +61,9 @@ struct pwm_fan_ctx { struct hwmon_chip_info info; struct hwmon_channel_info fan_channel; + + u64 pwm_duty_cycle_from_stopped; + u32 pwm_usec_from_stopped; }; /* This handler assumes self resetting edge triggered interrupt. */ @@ -199,7 +203,9 @@ static int pwm_fan_power_off(struct pwm_fan_ctx *ctx, bool force_disable) static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm) { struct pwm_state *state = &ctx->pwm_state; + unsigned long final_pwm = pwm; unsigned long period; + bool update = false; int ret = 0; if (pwm > 0) { @@ -208,11 +214,22 @@ static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm) return 0; period = state->period; - state->duty_cycle = DIV_ROUND_UP(pwm * (period - 1), MAX_PWM); + update = state->duty_cycle < ctx->pwm_duty_cycle_from_stopped; + if (update) + state->duty_cycle = ctx->pwm_duty_cycle_from_stopped; + else + state->duty_cycle = DIV_ROUND_UP(pwm * (period - 1), MAX_PWM); ret = pwm_apply_might_sleep(ctx->pwm, state); if (ret) return ret; ret = pwm_fan_power_on(ctx); + if (!ret && update) { + pwm = final_pwm; + state->duty_cycle = DIV_ROUND_UP(pwm * (period - 1), MAX_PWM); + usleep_range(ctx->pwm_usec_from_stopped, + ctx->pwm_usec_from_stopped * 2); + ret = pwm_apply_might_sleep(ctx->pwm, state); + } } else { ret = pwm_fan_power_off(ctx, false); } @@ -480,6 +497,7 @@ static int pwm_fan_probe(struct platform_device *pdev) struct device *hwmon; int ret; const struct hwmon_channel_info **channels; + u32 pwm_min_from_stopped = 0; u32 *fan_channel_config; int channel_count = 1; /* We always have a PWM channel. */ int i; @@ -620,6 +638,19 @@ static int pwm_fan_probe(struct platform_device *pdev) channels[1] = &ctx->fan_channel; } + ret = of_property_read_u32(dev->of_node, "fan-stop-to-start-percent", + &pwm_min_from_stopped); + if (!ret && pwm_min_from_stopped) { + ctx->pwm_duty_cycle_from_stopped = + DIV_ROUND_UP_ULL(pwm_min_from_stopped * + (ctx->pwm_state.period - 1), + 100); + } + ret = of_property_read_u32(dev->of_node, "fan-stop-to-start-us", + &ctx->pwm_usec_from_stopped); + if (ret) + ctx->pwm_usec_from_stopped = 250000; + ctx->info.ops = &pwm_fan_hwmon_ops; ctx->info.info = channels; From 57ee12b6c514146c19b6a159013b48727a012960 Mon Sep 17 00:00:00 2001 From: Pei Xiao Date: Tue, 12 Nov 2024 09:39:51 +0800 Subject: [PATCH 44/51] hwmon: (nct6775-core) Fix overflows seen when writing limit attributes DIV_ROUND_CLOSEST() after kstrtoul() results in an overflow if a large number such as 18446744073709551615 is provided by the user. Fix it by reordering clamp_val() and DIV_ROUND_CLOSEST() operations. Signed-off-by: Pei Xiao Fixes: c3963bc0a0cf ("hwmon: (nct6775) Split core and platform driver") Message-ID: <7d5084cea33f7c0fd0578c59adfff71f93de94d9.1731375425.git.xiaopei01@kylinos.cn> Signed-off-by: Guenter Roeck --- drivers/hwmon/nct6775-core.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c index 934fed3dd586..ee04795b98aa 100644 --- a/drivers/hwmon/nct6775-core.c +++ b/drivers/hwmon/nct6775-core.c @@ -2878,8 +2878,7 @@ store_target_temp(struct device *dev, struct device_attribute *attr, if (err < 0) return err; - val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0, - data->target_temp_mask); + val = DIV_ROUND_CLOSEST(clamp_val(val, 0, data->target_temp_mask * 1000), 1000); mutex_lock(&data->update_lock); data->target_temp[nr] = val; @@ -2959,7 +2958,7 @@ store_temp_tolerance(struct device *dev, struct device_attribute *attr, return err; /* Limit tolerance as needed */ - val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0, data->tolerance_mask); + val = DIV_ROUND_CLOSEST(clamp_val(val, 0, data->tolerance_mask * 1000), 1000); mutex_lock(&data->update_lock); data->temp_tolerance[index][nr] = val; @@ -3085,7 +3084,7 @@ store_weight_temp(struct device *dev, struct device_attribute *attr, if (err < 0) return err; - val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0, 255); + val = DIV_ROUND_CLOSEST(clamp_val(val, 0, 255000), 1000); mutex_lock(&data->update_lock); data->weight_temp[index][nr] = val; From fabb1f813ec05975fd3428e72a62ef9f855fd3b4 Mon Sep 17 00:00:00 2001 From: Huisong Li Date: Tue, 12 Nov 2024 10:12:28 +0800 Subject: [PATCH 45/51] hwmon: (acpi_power_meter) Fix fail to load module on platform without _PMD method According to the ACPI specification, the _PMD method is optional. The acpi_power_meter driver shouldn't fail to load if the platform has no _PMD method. Signed-off-by: Huisong Li Message-ID: <20241112021228.22914-1-lihuisong@huawei.com> [groeck: Reworded commit description] Signed-off-by: Guenter Roeck --- drivers/hwmon/acpi_power_meter.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c index 6c8a9c863528..2f1c9d97ad21 100644 --- a/drivers/hwmon/acpi_power_meter.c +++ b/drivers/hwmon/acpi_power_meter.c @@ -680,8 +680,9 @@ static int setup_attrs(struct acpi_power_meter_resource *resource) { int res = 0; + /* _PMD method is optional. */ res = read_domain_devices(resource); - if (res) + if (res != -ENODEV) return res; if (resource->caps.flags & POWER_METER_CAN_MEASURE) { From 700f3250198d51093884254220a96777a922dd47 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Tue, 12 Nov 2024 11:51:59 -0500 Subject: [PATCH 46/51] hwmon: (tmp108) Add helper function tmp108_common_probe() to prepare I3C support Add help function tmp108_common_probe() to pave road to support i3c for P3T1085(NXP) chip. Use dev_err_probe() to simplify the code. Signed-off-by: Frank Li Message-ID: <20241112-p3t1085-v4-1-a1334314b1e6@nxp.com> Signed-off-by: Guenter Roeck --- drivers/hwmon/tmp108.c | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/drivers/hwmon/tmp108.c b/drivers/hwmon/tmp108.c index b561b452d8d3..bfbea6349a95 100644 --- a/drivers/hwmon/tmp108.c +++ b/drivers/hwmon/tmp108.c @@ -323,33 +323,19 @@ static const struct regmap_config tmp108_regmap_config = { .use_single_write = true, }; -static int tmp108_probe(struct i2c_client *client) +static int tmp108_common_probe(struct device *dev, struct regmap *regmap, char *name) { - struct device *dev = &client->dev; struct device *hwmon_dev; struct tmp108 *tmp108; - int err; u32 config; - - if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_WORD_DATA)) { - dev_err(dev, - "adapter doesn't support SMBus word transactions\n"); - return -ENODEV; - } + int err; tmp108 = devm_kzalloc(dev, sizeof(*tmp108), GFP_KERNEL); if (!tmp108) return -ENOMEM; dev_set_drvdata(dev, tmp108); - - tmp108->regmap = devm_regmap_init_i2c(client, &tmp108_regmap_config); - if (IS_ERR(tmp108->regmap)) { - err = PTR_ERR(tmp108->regmap); - dev_err(dev, "regmap init failed: %d", err); - return err; - } + tmp108->regmap = regmap; err = regmap_read(tmp108->regmap, TMP108_REG_CONF, &config); if (err < 0) { @@ -383,13 +369,30 @@ static int tmp108_probe(struct i2c_client *client) return err; } - hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, + hwmon_dev = devm_hwmon_device_register_with_info(dev, name, tmp108, &tmp108_chip_info, NULL); return PTR_ERR_OR_ZERO(hwmon_dev); } +static int tmp108_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct regmap *regmap; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WORD_DATA)) + return dev_err_probe(dev, -ENODEV, + "adapter doesn't support SMBus word transactions\n"); + + regmap = devm_regmap_init_i2c(client, &tmp108_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), "regmap init failed"); + + return tmp108_common_probe(dev, regmap, client->name); +} + static int tmp108_suspend(struct device *dev) { struct tmp108 *tmp108 = dev_get_drvdata(dev); From c40655e3310649866c4ebf7a10f0d53802ebdfa9 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Tue, 12 Nov 2024 11:52:00 -0500 Subject: [PATCH 47/51] hwmon: (tmp108) Add support for I3C device Add support for I3C device in the tmp108 driver to handle the P3T1085 sensor. Register the I3C device driver to enable I3C functionality for the sensor. Signed-off-by: Frank Li Message-ID: <20241112-p3t1085-v4-2-a1334314b1e6@nxp.com> Signed-off-by: Guenter Roeck --- drivers/hwmon/Kconfig | 1 + drivers/hwmon/tmp108.c | 35 ++++++++++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 7b9b52fa731c..ad20dc8506f9 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -2298,6 +2298,7 @@ config SENSORS_TMP108 tristate "Texas Instruments TMP108" depends on I2C select REGMAP_I2C + select REGMAP_I3C if I3C help If you say yes here you get support for Texas Instruments TMP108 sensor chips and NXP P3T1085. diff --git a/drivers/hwmon/tmp108.c b/drivers/hwmon/tmp108.c index bfbea6349a95..1f36af2cd2d9 100644 --- a/drivers/hwmon/tmp108.c +++ b/drivers/hwmon/tmp108.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -440,7 +441,39 @@ static struct i2c_driver tmp108_driver = { .id_table = tmp108_i2c_ids, }; -module_i2c_driver(tmp108_driver); +static const struct i3c_device_id p3t1085_i3c_ids[] = { + I3C_DEVICE(0x011b, 0x1529, NULL), + {} +}; +MODULE_DEVICE_TABLE(i3c, p3t1085_i3c_ids); + +static int p3t1085_i3c_probe(struct i3c_device *i3cdev) +{ + struct device *dev = i3cdev_to_dev(i3cdev); + struct regmap *regmap; + +#ifdef CONFIG_REGMAP_I3C + regmap = devm_regmap_init_i3c(i3cdev, &tmp108_regmap_config); +#else + regmap = ERR_PTR(-ENODEV); +#endif + + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "Failed to register i3c regmap\n"); + + return tmp108_common_probe(dev, regmap, "p3t1085_i3c"); +} + +static struct i3c_driver p3t1085_driver = { + .driver = { + .name = "p3t1085_i3c", + }, + .probe = p3t1085_i3c_probe, + .id_table = p3t1085_i3c_ids, +}; + +module_i3c_i2c_driver(p3t1085_driver, &tmp108_driver) MODULE_AUTHOR("John Muir "); MODULE_DESCRIPTION("Texas Instruments TMP108 temperature sensor driver"); From c1fff92d808bb41064b783a072dee834bcc29f33 Mon Sep 17 00:00:00 2001 From: "Sung-Chi, Li" Date: Wed, 13 Nov 2024 10:39:51 +0800 Subject: [PATCH 48/51] hwmon: (cros_ec) register thermal sensors to thermal framework cros_ec hwmon driver probes available thermal sensors when probing the driver. Register these thermal sensors to the thermal framework as well via setting HWMON_C_REGISTER_TZ as a chip info, such that thermal framework can adopt these sensors as well. To make cros_ec registrable to thermal framework, the cros_ec dts need the corresponding changes: &cros_ec { #thermal-sensor-cells = <1>; }; Signed-off-by: Sung-Chi, Li Message-ID: <20241113024000.3327161-1-lschyi@chromium.org> Signed-off-by: Guenter Roeck --- drivers/hwmon/cros_ec_hwmon.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwmon/cros_ec_hwmon.c b/drivers/hwmon/cros_ec_hwmon.c index 5514cf780b8b..9991c3fa020a 100644 --- a/drivers/hwmon/cros_ec_hwmon.c +++ b/drivers/hwmon/cros_ec_hwmon.c @@ -141,6 +141,7 @@ static umode_t cros_ec_hwmon_is_visible(const void *data, enum hwmon_sensor_type } static const struct hwmon_channel_info * const cros_ec_hwmon_info[] = { + HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ), HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_FAULT, HWMON_F_INPUT | HWMON_F_FAULT, From 83b645ee43f796dc765cc541d5cafa6fe2b1f3fa Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 13 Nov 2024 18:55:33 +0100 Subject: [PATCH 49/51] hwmon: tmp108: fix I3C dependency It's possible to build a kernel with tmp108 built-in but i3c support in a loadable module, but that results in a link failure: x86_64-linux-ld: drivers/hwmon/tmp108.o: in function `p3t1085_i3c_probe': tmp108.c:(.text+0x5f9): undefined reference to `i3cdev_to_dev' Add a Kconfig dependency to ensure only the working configurations are allowed. Fixes: c40655e33106 ("hwmon: (tmp108) Add support for I3C device") Signed-off-by: Arnd Bergmann Message-ID: <20241113175615.2442851-1-arnd@kernel.org> Signed-off-by: Guenter Roeck --- drivers/hwmon/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index ad20dc8506f9..6b497dd87562 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -2297,6 +2297,7 @@ config SENSORS_TMP103 config SENSORS_TMP108 tristate "Texas Instruments TMP108" depends on I2C + depends on I3C || !I3C select REGMAP_I2C select REGMAP_I3C if I3C help From 71d689f60b51113a9a6de343bde35d9375c22c57 Mon Sep 17 00:00:00 2001 From: Grant Peltier Date: Tue, 12 Nov 2024 14:10:06 -0600 Subject: [PATCH 50/51] dt-bindings: hwmon: isl68137: add bindings to support voltage dividers Add devicetree bindings to support declaring optional voltage dividers to the rail outputs of supported digital multiphase regulators. Some applications require Vout to exceed the voltage range that the Vsense pin can detect. This binding definition allows users to define the characteristics of a voltage divider placed between Vout and the Vsense pin for any rail powered by the device. These bindings copy the vout-voltage-divider property defined in the maxim,max20730 bindings schema since it is the best fit for the use case of scaling hwmon PMBus telemetry. The generic voltage-divider property used by many iio drivers was determined to be a poor fit because that schema is tied directly to iio for the purpose of scaling io-channel voltages and the isl68137 driver is not an iio driver. New schema file named isil,isl68137.yaml to align with the corresponding driver name and pre-existing bindings ported from trivial bindings. However, all new device bindings use renesas as the vendor prefix since Renesas acquired Intersil and now maintains all documentation for the devices. Signed-off-by: Grant Peltier Reviewed-by: Rob Herring (Arm) Message-ID: Signed-off-by: Guenter Roeck --- .../bindings/hwmon/pmbus/isil,isl68137.yaml | 148 ++++++++++++++++++ .../devicetree/bindings/trivial-devices.yaml | 6 - 2 files changed, 148 insertions(+), 6 deletions(-) create mode 100644 Documentation/devicetree/bindings/hwmon/pmbus/isil,isl68137.yaml diff --git a/Documentation/devicetree/bindings/hwmon/pmbus/isil,isl68137.yaml b/Documentation/devicetree/bindings/hwmon/pmbus/isil,isl68137.yaml new file mode 100644 index 000000000000..bac5f8e352aa --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/pmbus/isil,isl68137.yaml @@ -0,0 +1,148 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- + +$id: http://devicetree.org/schemas/hwmon/pmbus/isil,isl68137.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Renesas Digital Multiphase Voltage Regulators with PMBus + +maintainers: + - Grant Peltier + +description: | + Renesas digital multiphase voltage regulators with PMBus. + https://www.renesas.com/en/products/power-management/multiphase-power/multiphase-dcdc-switching-controllers + +properties: + compatible: + enum: + - isil,isl68137 + - renesas,isl68220 + - renesas,isl68221 + - renesas,isl68222 + - renesas,isl68223 + - renesas,isl68224 + - renesas,isl68225 + - renesas,isl68226 + - renesas,isl68227 + - renesas,isl68229 + - renesas,isl68233 + - renesas,isl68239 + - renesas,isl69222 + - renesas,isl69223 + - renesas,isl69224 + - renesas,isl69225 + - renesas,isl69227 + - renesas,isl69228 + - renesas,isl69234 + - renesas,isl69236 + - renesas,isl69239 + - renesas,isl69242 + - renesas,isl69243 + - renesas,isl69247 + - renesas,isl69248 + - renesas,isl69254 + - renesas,isl69255 + - renesas,isl69256 + - renesas,isl69259 + - isil,isl69260 + - renesas,isl69268 + - isil,isl69269 + - renesas,isl69298 + - renesas,raa228000 + - renesas,raa228004 + - renesas,raa228006 + - renesas,raa228228 + - renesas,raa229001 + - renesas,raa229004 + + reg: + maxItems: 1 + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + +patternProperties: + "^channel@([0-3])$": + type: object + description: + Container for properties specific to a particular channel (rail). + + properties: + reg: + description: The channel (rail) index. + items: + minimum: 0 + maximum: 3 + + vout-voltage-divider: + description: | + Resistances of a voltage divider placed between Vout and the voltage + sense (Vsense) pin for the given channel (rail). It has two numbers + representing the resistances of the voltage divider provided as + which yields an adjusted Vout as + Vout_adj = Vout * Rtotal / Rout given the original Vout as reported + by the Vsense pin. Given a circuit configuration similar to the one + below, Rtotal = R1 + Rout. + + Vout ----. + | + .-----. + | R1 | + '-----' + | + +---- Vsense + | + .-----. + | Rout| + '-----' + | + GND + + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 2 + maxItems: 2 + + required: + - reg + + additionalProperties: false + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + isl68239@60 { + compatible = "isil,isl68137"; + reg = <0x60>; + }; + }; + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + isl68239@60 { + compatible = "renesas,isl68239"; + reg = <0x60>; + #address-cells = <1>; + #size-cells = <0>; + + channel@0 { + reg = <0>; + vout-voltage-divider = <1000 2000>; // Reported Vout/Pout would be scaled by 2 + }; + }; + }; diff --git a/Documentation/devicetree/bindings/trivial-devices.yaml b/Documentation/devicetree/bindings/trivial-devices.yaml index 8850b11052b1..bf7dded9451d 100644 --- a/Documentation/devicetree/bindings/trivial-devices.yaml +++ b/Documentation/devicetree/bindings/trivial-devices.yaml @@ -153,12 +153,6 @@ properties: - isil,isl29028 # Intersil ISL29030 Ambient Light and Proximity Sensor - isil,isl29030 - # Intersil ISL68137 Digital Output Configurable PWM Controller - - isil,isl68137 - # Intersil ISL69260 PMBus Voltage Regulator - - isil,isl69260 - # Intersil ISL69269 PMBus Voltage Regulator - - isil,isl69269 # Intersil ISL76682 Ambient Light Sensor - isil,isl76682 # JEDEC JESD300 (SPD5118) Hub and Serial Presence Detect From 3996187f80a0e24bff1959609065d49041cf648d Mon Sep 17 00:00:00 2001 From: Grant Peltier Date: Tue, 12 Nov 2024 14:09:28 -0600 Subject: [PATCH 51/51] hwmon: (pmbus/isl68137) add support for voltage divider on Vout Some applications require Vout to be higher than the detectable voltage range of the Vsense pin for a given rail. In such applications, a voltage divider may be placed between Vout and the Vsense pin, but this results in erroneous telemetry being read back from the part. This change adds support for a voltage divider to be defined in the devicetree for a (or multiple) specific rail(s) for a supported digital multiphase device and for the applicable Vout telemetry to be scaled based on the voltage divider configuration. This change copies the implementation of the vout-voltage-divider devicetree property defined in the maxim,max20730 bindings schema since it is the best fit for the use case of scaling hwmon PMBus telemetry. The generic voltage-divider property used by many iio drivers was determined to be a poor fit because that schema is tied directly to iio and the isl68137 driver is not an iio driver. Signed-off-by: Grant Peltier Message-ID: <8c2d048f87282bcf66313afbf5e923d8fc17b4d7.1731439797.git.grantpeltier93@gmail.com> Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/isl68137.c | 210 ++++++++++++++++++++++++++++++++- 1 file changed, 205 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/pmbus/isl68137.c b/drivers/hwmon/pmbus/isl68137.c index 7e53fb1d5ea3..97cc951a13a4 100644 --- a/drivers/hwmon/pmbus/isl68137.c +++ b/drivers/hwmon/pmbus/isl68137.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -20,6 +21,7 @@ #define ISL68137_VOUT_AVS 0x30 #define RAA_DMPVR2_READ_VMON 0xc8 +#define MAX_CHANNELS 4 enum chips { isl68137, @@ -72,6 +74,17 @@ enum variants { raa_dmpvr2_hv, }; +struct isl68137_channel { + u32 vout_voltage_divider[2]; +}; + +struct isl68137_data { + struct pmbus_driver_info info; + struct isl68137_channel channel[MAX_CHANNELS]; +}; + +#define to_isl68137_data(x) container_of(x, struct isl68137_data, info) + static const struct i2c_device_id raa_dmpvr_id[]; static ssize_t isl68137_avs_enable_show_page(struct i2c_client *client, @@ -163,13 +176,32 @@ static const struct attribute_group *isl68137_attribute_groups[] = { static int raa_dmpvr2_read_word_data(struct i2c_client *client, int page, int phase, int reg) { + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + const struct isl68137_data *data = to_isl68137_data(info); int ret; + u64 temp; switch (reg) { case PMBUS_VIRT_READ_VMON: ret = pmbus_read_word_data(client, page, phase, RAA_DMPVR2_READ_VMON); break; + case PMBUS_READ_POUT: + case PMBUS_READ_VOUT: + /* + * In cases where a voltage divider is attached to the target + * rail between Vout and the Vsense pin, both Vout and Pout + * should be scaled by the voltage divider scaling factor. + * I.e. Vout = Vsense * Rtotal / Rout + */ + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret > 0) { + temp = DIV_U64_ROUND_CLOSEST((u64)ret * + data->channel[page].vout_voltage_divider[1], + data->channel[page].vout_voltage_divider[0]); + ret = clamp_val(temp, 0, 0xffff); + } + break; default: ret = -ENODATA; break; @@ -178,6 +210,40 @@ static int raa_dmpvr2_read_word_data(struct i2c_client *client, int page, return ret; } +static int raa_dmpvr2_write_word_data(struct i2c_client *client, int page, + int reg, u16 word) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + const struct isl68137_data *data = to_isl68137_data(info); + int ret; + u64 temp; + + switch (reg) { + case PMBUS_VOUT_MAX: + case PMBUS_VOUT_MARGIN_HIGH: + case PMBUS_VOUT_MARGIN_LOW: + case PMBUS_VOUT_OV_FAULT_LIMIT: + case PMBUS_VOUT_UV_FAULT_LIMIT: + case PMBUS_VOUT_COMMAND: + /* + * In cases where a voltage divider is attached to the target + * rail between Vout and the Vsense pin, Vout related PMBus + * commands should be scaled based on the expected voltage + * at the Vsense pin. + * I.e. Vsense = Vout * Rout / Rtotal + */ + temp = DIV_U64_ROUND_CLOSEST((u64)word * + data->channel[page].vout_voltage_divider[0], + data->channel[page].vout_voltage_divider[1]); + ret = clamp_val(temp, 0, 0xffff); + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + static struct pmbus_driver_info raa_dmpvr_info = { .pages = 3, .format[PSC_VOLTAGE_IN] = direct, @@ -220,14 +286,90 @@ static struct pmbus_driver_info raa_dmpvr_info = { | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT, }; +static int isl68137_probe_child_from_dt(struct device *dev, + struct device_node *child, + struct isl68137_data *data) +{ + u32 channel, rout, rtotal; + int err; + + err = of_property_read_u32(child, "reg", &channel); + if (err) { + dev_err(dev, "missing reg property of %pOFn\n", child); + return err; + } + if (channel >= data->info.pages) { + dev_err(dev, "invalid reg %d of %pOFn\n", channel, child); + return -EINVAL; + } + + err = of_property_read_u32_array(child, "vout-voltage-divider", + data->channel[channel].vout_voltage_divider, + ARRAY_SIZE(data->channel[channel].vout_voltage_divider)); + if (err && err != -EINVAL) { + dev_err(dev, + "malformed vout-voltage-divider value for channel %d\n", + channel); + return err; + } + + rout = data->channel[channel].vout_voltage_divider[0]; + rtotal = data->channel[channel].vout_voltage_divider[1]; + if (rout == 0) { + dev_err(dev, + "Voltage divider output resistance must be greater than 0\n"); + return -EINVAL; + } + if (rtotal < rout) { + dev_err(dev, + "Voltage divider total resistance is less than output resistance\n"); + return -EINVAL; + } + + return 0; +} + +static int isl68137_probe_from_dt(struct device *dev, + struct isl68137_data *data) +{ + const struct device_node *np = dev->of_node; + struct device_node *child; + int err; + + for_each_child_of_node(np, child) { + if (strcmp(child->name, "channel")) + continue; + + err = isl68137_probe_child_from_dt(dev, child, data); + if (err) + return err; + } + + return 0; +} + static int isl68137_probe(struct i2c_client *client) { + struct device *dev = &client->dev; struct pmbus_driver_info *info; + struct isl68137_data *data; + int i, err; - info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL); - if (!info) + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) return -ENOMEM; - memcpy(info, &raa_dmpvr_info, sizeof(*info)); + + /* + * Initialize all voltage dividers to Rout=1 and Rtotal=1 to simplify + * logic in PMBus word read/write functions + */ + for (i = 0; i < MAX_CHANNELS; i++) + memset(data->channel[i].vout_voltage_divider, + 1, + sizeof(data->channel[i].vout_voltage_divider)); + + memcpy(&data->info, &raa_dmpvr_info, sizeof(data->info)); + info = &data->info; switch (i2c_match_id(raa_dmpvr_id, client)->driver_data) { case raa_dmpvr1_2rail: @@ -237,11 +379,14 @@ static int isl68137_probe(struct i2c_client *client) info->func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT; + info->read_word_data = raa_dmpvr2_read_word_data; + info->write_word_data = raa_dmpvr2_write_word_data; info->groups = isl68137_attribute_groups; break; case raa_dmpvr2_1rail: info->pages = 1; info->read_word_data = raa_dmpvr2_read_word_data; + info->write_word_data = raa_dmpvr2_write_word_data; break; case raa_dmpvr2_2rail_nontc: info->func[0] &= ~PMBUS_HAVE_TEMP3; @@ -250,9 +395,11 @@ static int isl68137_probe(struct i2c_client *client) case raa_dmpvr2_2rail: info->pages = 2; info->read_word_data = raa_dmpvr2_read_word_data; + info->write_word_data = raa_dmpvr2_write_word_data; break; case raa_dmpvr2_3rail: info->read_word_data = raa_dmpvr2_read_word_data; + info->write_word_data = raa_dmpvr2_write_word_data; break; case raa_dmpvr2_hv: info->pages = 1; @@ -263,11 +410,16 @@ static int isl68137_probe(struct i2c_client *client) info->m[PSC_POWER] = 2; info->R[PSC_POWER] = -1; info->read_word_data = raa_dmpvr2_read_word_data; + info->write_word_data = raa_dmpvr2_write_word_data; break; default: return -ENODEV; } + err = isl68137_probe_from_dt(dev, data); + if (err) + return err; + return pmbus_do_probe(client, info); } @@ -318,11 +470,59 @@ static const struct i2c_device_id raa_dmpvr_id[] = { MODULE_DEVICE_TABLE(i2c, raa_dmpvr_id); +static const struct of_device_id isl68137_of_match[] = { + { .compatible = "isil,isl68137", .data = (void *)raa_dmpvr1_2rail }, + { .compatible = "renesas,isl68220", .data = (void *)raa_dmpvr2_2rail }, + { .compatible = "renesas,isl68221", .data = (void *)raa_dmpvr2_3rail }, + { .compatible = "renesas,isl68222", .data = (void *)raa_dmpvr2_2rail }, + { .compatible = "renesas,isl68223", .data = (void *)raa_dmpvr2_2rail }, + { .compatible = "renesas,isl68224", .data = (void *)raa_dmpvr2_3rail }, + { .compatible = "renesas,isl68225", .data = (void *)raa_dmpvr2_2rail }, + { .compatible = "renesas,isl68226", .data = (void *)raa_dmpvr2_3rail }, + { .compatible = "renesas,isl68227", .data = (void *)raa_dmpvr2_1rail }, + { .compatible = "renesas,isl68229", .data = (void *)raa_dmpvr2_3rail }, + { .compatible = "renesas,isl68233", .data = (void *)raa_dmpvr2_2rail }, + { .compatible = "renesas,isl68239", .data = (void *)raa_dmpvr2_3rail }, + + { .compatible = "renesas,isl69222", .data = (void *)raa_dmpvr2_2rail }, + { .compatible = "renesas,isl69223", .data = (void *)raa_dmpvr2_3rail }, + { .compatible = "renesas,isl69224", .data = (void *)raa_dmpvr2_2rail }, + { .compatible = "renesas,isl69225", .data = (void *)raa_dmpvr2_2rail }, + { .compatible = "renesas,isl69227", .data = (void *)raa_dmpvr2_3rail }, + { .compatible = "renesas,isl69228", .data = (void *)raa_dmpvr2_3rail }, + { .compatible = "renesas,isl69234", .data = (void *)raa_dmpvr2_2rail }, + { .compatible = "renesas,isl69236", .data = (void *)raa_dmpvr2_2rail }, + { .compatible = "renesas,isl69239", .data = (void *)raa_dmpvr2_3rail }, + { .compatible = "renesas,isl69242", .data = (void *)raa_dmpvr2_2rail }, + { .compatible = "renesas,isl69243", .data = (void *)raa_dmpvr2_1rail }, + { .compatible = "renesas,isl69247", .data = (void *)raa_dmpvr2_2rail }, + { .compatible = "renesas,isl69248", .data = (void *)raa_dmpvr2_2rail }, + { .compatible = "renesas,isl69254", .data = (void *)raa_dmpvr2_2rail }, + { .compatible = "renesas,isl69255", .data = (void *)raa_dmpvr2_2rail }, + { .compatible = "renesas,isl69256", .data = (void *)raa_dmpvr2_2rail }, + { .compatible = "renesas,isl69259", .data = (void *)raa_dmpvr2_2rail }, + { .compatible = "isil,isl69260", .data = (void *)raa_dmpvr2_2rail }, + { .compatible = "renesas,isl69268", .data = (void *)raa_dmpvr2_2rail }, + { .compatible = "isil,isl69269", .data = (void *)raa_dmpvr2_3rail }, + { .compatible = "renesas,isl69298", .data = (void *)raa_dmpvr2_2rail }, + + { .compatible = "renesas,raa228000", .data = (void *)raa_dmpvr2_hv }, + { .compatible = "renesas,raa228004", .data = (void *)raa_dmpvr2_hv }, + { .compatible = "renesas,raa228006", .data = (void *)raa_dmpvr2_hv }, + { .compatible = "renesas,raa228228", .data = (void *)raa_dmpvr2_2rail_nontc }, + { .compatible = "renesas,raa229001", .data = (void *)raa_dmpvr2_2rail }, + { .compatible = "renesas,raa229004", .data = (void *)raa_dmpvr2_2rail }, + { }, +}; + +MODULE_DEVICE_TABLE(of, isl68137_of_match); + /* This is the driver that will be inserted */ static struct i2c_driver isl68137_driver = { .driver = { - .name = "isl68137", - }, + .name = "isl68137", + .of_match_table = isl68137_of_match, + }, .probe = isl68137_probe, .id_table = raa_dmpvr_id, };