From 6e9ef8ca687e69e9d4cc89033d98e06350b0f3e0 Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Wed, 16 Jun 2021 15:42:18 +1200 Subject: hwmon: (pmbus/bpa-rs600) Handle Vin readings >= 256V The BPA-RS600 doesn't follow the PMBus spec for linear data. Specifically it treats the mantissa as an unsigned 11-bit value instead of a two's complement 11-bit value. At this point it's unclear whether this only affects Vin or if Pin/Pout1 are affected as well. Erring on the side of caution only Vin is dealt with here. Fixes: 15b2703e5e02 ("hwmon: (pmbus) Add driver for BluTek BPA-RS600") Signed-off-by: Chris Packham Link: https://lore.kernel.org/r/20210616034218.25821-1-chris.packham@alliedtelesis.co.nz Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/bpa-rs600.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/drivers/hwmon/pmbus/bpa-rs600.c b/drivers/hwmon/pmbus/bpa-rs600.c index f6558ee9dec3..2be69fedfa36 100644 --- a/drivers/hwmon/pmbus/bpa-rs600.c +++ b/drivers/hwmon/pmbus/bpa-rs600.c @@ -46,6 +46,32 @@ static int bpa_rs600_read_byte_data(struct i2c_client *client, int page, int reg return ret; } +/* + * The BPA-RS600 violates the PMBus spec. Specifically it treats the + * mantissa as unsigned. Deal with this here to allow the PMBus core + * to work with correctly encoded data. + */ +static int bpa_rs600_read_vin(struct i2c_client *client) +{ + int ret, exponent, mantissa; + + ret = pmbus_read_word_data(client, 0, 0xff, PMBUS_READ_VIN); + if (ret < 0) + return ret; + + if (ret & BIT(10)) { + exponent = ret >> 11; + mantissa = ret & 0x7ff; + + exponent++; + mantissa >>= 1; + + ret = (exponent << 11) | mantissa; + } + + return ret; +} + static int bpa_rs600_read_word_data(struct i2c_client *client, int page, int phase, int reg) { int ret; @@ -85,6 +111,9 @@ static int bpa_rs600_read_word_data(struct i2c_client *client, int page, int pha /* These commands return data but it is invalid/un-documented */ ret = -ENXIO; break; + case PMBUS_READ_VIN: + ret = bpa_rs600_read_vin(client); + break; default: if (reg >= PMBUS_VIRT_BASE) ret = -ENXIO; -- cgit v1.2.3 From ab9d85e9d5555c75992dc42bf3b9eebe0955ceb9 Mon Sep 17 00:00:00 2001 From: Erik Rosen Date: Fri, 23 Apr 2021 17:33:28 +0200 Subject: hwmon: (pmbus/zl6100) Add support for ZLS1003, ZLS4009 and ZL8802 Add support for Renesas ZL8802 Dual Channel/Dual Phase PMBus DC/DC Digital Controller as well as ZLS1003 and ZLS4009 custom DC/DC controller chips. Signed-off-by: Erik Rosen Link: https://lore.kernel.org/r/20210423153329.33457-2-erik.rosen@metormote.com Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/zl6100.c | 94 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 80 insertions(+), 14 deletions(-) diff --git a/drivers/hwmon/pmbus/zl6100.c b/drivers/hwmon/pmbus/zl6100.c index b7d4eacdc3ef..e9df0c56d91e 100644 --- a/drivers/hwmon/pmbus/zl6100.c +++ b/drivers/hwmon/pmbus/zl6100.c @@ -18,7 +18,7 @@ #include "pmbus.h" enum chips { zl2004, zl2005, zl2006, zl2008, zl2105, zl2106, zl6100, zl6105, - zl9101, zl9117 }; + zl8802, zl9101, zl9117, zls1003, zls4009 }; struct zl6100_data { int id; @@ -34,6 +34,13 @@ struct zl6100_data { #define ZL6100_MFR_XTEMP_ENABLE BIT(7) +#define ZL8802_MFR_USER_GLOBAL_CONFIG 0xe9 +#define ZL8802_MFR_TMON_ENABLE BIT(12) +#define ZL8802_MFR_USER_CONFIG 0xd1 +#define ZL8802_MFR_XTEMP_ENABLE_2 BIT(1) +#define ZL8802_MFR_DDC_CONFIG 0xd3 +#define ZL8802_MFR_PHASES_MASK 0x0007 + #define MFR_VMON_OV_FAULT_LIMIT 0xf5 #define MFR_VMON_UV_FAULT_LIMIT 0xf6 #define MFR_READ_VMON 0xf7 @@ -132,7 +139,7 @@ static int zl6100_read_word_data(struct i2c_client *client, int page, struct zl6100_data *data = to_zl6100_data(info); int ret, vreg; - if (page > 0) + if (page >= info->pages) return -ENXIO; if (data->id == zl2005) { @@ -191,7 +198,7 @@ static int zl6100_read_byte_data(struct i2c_client *client, int page, int reg) struct zl6100_data *data = to_zl6100_data(info); int ret, status; - if (page > 0) + if (page >= info->pages) return -ENXIO; zl6100_wait(data); @@ -230,7 +237,7 @@ static int zl6100_write_word_data(struct i2c_client *client, int page, int reg, struct zl6100_data *data = to_zl6100_data(info); int ret, vreg; - if (page > 0) + if (page >= info->pages) return -ENXIO; switch (reg) { @@ -271,7 +278,7 @@ static int zl6100_write_byte(struct i2c_client *client, int page, u8 value) struct zl6100_data *data = to_zl6100_data(info); int ret; - if (page > 0) + if (page >= info->pages) return -ENXIO; zl6100_wait(data); @@ -287,6 +294,10 @@ static const struct i2c_device_id zl6100_id[] = { {"bmr462", zl2008}, {"bmr463", zl2008}, {"bmr464", zl2008}, + {"bmr465", zls4009}, + {"bmr466", zls1003}, + {"bmr467", zls4009}, + {"bmr469", zl8802}, {"zl2004", zl2004}, {"zl2005", zl2005}, {"zl2006", zl2006}, @@ -295,15 +306,18 @@ static const struct i2c_device_id zl6100_id[] = { {"zl2106", zl2106}, {"zl6100", zl6100}, {"zl6105", zl6105}, + {"zl8802", zl8802}, {"zl9101", zl9101}, {"zl9117", zl9117}, + {"zls1003", zls1003}, + {"zls4009", zls4009}, { } }; MODULE_DEVICE_TABLE(i2c, zl6100_id); static int zl6100_probe(struct i2c_client *client) { - int ret; + int ret, i; struct zl6100_data *data; struct pmbus_driver_info *info; u8 device_id[I2C_SMBUS_BLOCK_MAX + 1]; @@ -367,18 +381,70 @@ static int zl6100_probe(struct i2c_client *client) | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; /* - * ZL2004, ZL9101M, and ZL9117M support monitoring an extra voltage - * (VMON for ZL2004, VDRV for ZL9101M and ZL9117M). Report it as vmon. + * ZL2004, ZL8802, ZL9101M, ZL9117M and ZLS4009 support monitoring + * an extra voltage (VMON for ZL2004, ZL8802 and ZLS4009, + * VDRV for ZL9101M and ZL9117M). Report it as vmon. */ - if (data->id == zl2004 || data->id == zl9101 || data->id == zl9117) + if (data->id == zl2004 || data->id == zl8802 || data->id == zl9101 || + data->id == zl9117 || data->id == zls4009) info->func[0] |= PMBUS_HAVE_VMON | PMBUS_HAVE_STATUS_VMON; - ret = i2c_smbus_read_word_data(client, ZL6100_MFR_CONFIG); - if (ret < 0) - return ret; + /* + * ZL8802 has two outputs that can be used either independently or in + * a current sharing configuration. The driver uses the DDC_CONFIG + * register to check if the module is running with independent or + * shared outputs. If the module is in shared output mode, only one + * output voltage will be reported. + */ + if (data->id == zl8802) { + info->pages = 2; + info->func[0] |= PMBUS_HAVE_IIN; + + ret = i2c_smbus_read_word_data(client, ZL8802_MFR_DDC_CONFIG); + if (ret < 0) + return ret; + + data->access = ktime_get(); + zl6100_wait(data); + + if (ret & ZL8802_MFR_PHASES_MASK) + info->func[1] |= PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT; + else + info->func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT + | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT; - if (ret & ZL6100_MFR_XTEMP_ENABLE) - info->func[0] |= PMBUS_HAVE_TEMP2; + for (i = 0; i < 2; i++) { + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, i); + if (ret < 0) + return ret; + + data->access = ktime_get(); + zl6100_wait(data); + + ret = i2c_smbus_read_word_data(client, ZL8802_MFR_USER_CONFIG); + if (ret < 0) + return ret; + + if (ret & ZL8802_MFR_XTEMP_ENABLE_2) + info->func[i] |= PMBUS_HAVE_TEMP2; + + data->access = ktime_get(); + zl6100_wait(data); + } + ret = i2c_smbus_read_word_data(client, ZL8802_MFR_USER_GLOBAL_CONFIG); + if (ret < 0) + return ret; + + if (ret & ZL8802_MFR_TMON_ENABLE) + info->func[0] |= PMBUS_HAVE_TEMP3; + } else { + ret = i2c_smbus_read_word_data(client, ZL6100_MFR_CONFIG); + if (ret < 0) + return ret; + + if (ret & ZL6100_MFR_XTEMP_ENABLE) + info->func[0] |= PMBUS_HAVE_TEMP2; + } data->access = ktime_get(); zl6100_wait(data); -- cgit v1.2.3 From 6e954d2e649a373cdebb4d2b0de5197ca3f6b87e Mon Sep 17 00:00:00 2001 From: Erik Rosen Date: Fri, 23 Apr 2021 17:33:29 +0200 Subject: hwmon: (pmbus/zl6100) Update documentation for zl6100 driver Update documentation for zl6100 driver and fix dead links to technical specifications Signed-off-by: Erik Rosen Link: https://lore.kernel.org/r/20210423153329.33457-3-erik.rosen@metormote.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/zl6100.rst | 132 +++++++++++++++++++++++++++-------------- 1 file changed, 89 insertions(+), 43 deletions(-) diff --git a/Documentation/hwmon/zl6100.rst b/Documentation/hwmon/zl6100.rst index 968aff10ce0a..d42ed9d3ac69 100644 --- a/Documentation/hwmon/zl6100.rst +++ b/Documentation/hwmon/zl6100.rst @@ -3,87 +3,103 @@ Kernel driver zl6100 Supported chips: - * Intersil / Zilker Labs ZL2004 + * Renesas / Intersil / Zilker Labs ZL2004 Prefix: 'zl2004' Addresses scanned: - - Datasheet: http://www.intersil.com/data/fn/fn6847.pdf + Datasheet: https://www.renesas.com/us/en/document/dst/zl2004-datasheet.pdf - * Intersil / Zilker Labs ZL2005 + * Renesas / Intersil / Zilker Labs ZL2005 Prefix: 'zl2005' Addresses scanned: - - Datasheet: http://www.intersil.com/data/fn/fn6848.pdf + Datasheet: https://www.renesas.com/us/en/document/dst/zl2005-datasheet.pdf - * Intersil / Zilker Labs ZL2006 + * Renesas / Intersil / Zilker Labs ZL2006 Prefix: 'zl2006' Addresses scanned: - - Datasheet: http://www.intersil.com/data/fn/fn6850.pdf + Datasheet: https://www.renesas.com/us/en/document/dst/zl2006-datasheet.pdf - * Intersil / Zilker Labs ZL2008 + * Renesas / Intersil / Zilker Labs ZL2008 Prefix: 'zl2008' Addresses scanned: - - Datasheet: http://www.intersil.com/data/fn/fn6859.pdf + Datasheet: https://www.renesas.com/us/en/document/dst/zl2008-datasheet.pdf - * Intersil / Zilker Labs ZL2105 + * Renesas / Intersil / Zilker Labs ZL2105 Prefix: 'zl2105' Addresses scanned: - - Datasheet: http://www.intersil.com/data/fn/fn6851.pdf + Datasheet: https://www.renesas.com/us/en/document/dst/zl2105-datasheet.pdf - * Intersil / Zilker Labs ZL2106 + * Renesas / Intersil / Zilker Labs ZL2106 Prefix: 'zl2106' Addresses scanned: - - Datasheet: http://www.intersil.com/data/fn/fn6852.pdf + Datasheet: https://www.renesas.com/us/en/document/dst/zl2106-datasheet.pdf - * Intersil / Zilker Labs ZL6100 + * Renesas / Intersil / Zilker Labs ZL6100 Prefix: 'zl6100' Addresses scanned: - - Datasheet: http://www.intersil.com/data/fn/fn6876.pdf + Datasheet: https://www.renesas.com/us/en/document/dst/zl6100-datasheet.pdf - * Intersil / Zilker Labs ZL6105 + * Renesas / Intersil / Zilker Labs ZL6105 Prefix: 'zl6105' Addresses scanned: - - Datasheet: http://www.intersil.com/data/fn/fn6906.pdf + Datasheet: https://www.renesas.com/us/en/document/dst/zl6105-datasheet.pdf - * Intersil / Zilker Labs ZL9101M + * Renesas / Intersil / Zilker Labs ZL8802 + + Prefix: 'zl8802' + + Addresses scanned: - + + Datasheet: https://www.renesas.com/us/en/document/dst/zl8802-datasheet + + * Renesas / Intersil / Zilker Labs ZL9101M Prefix: 'zl9101' Addresses scanned: - - Datasheet: http://www.intersil.com/data/fn/fn7669.pdf + Datasheet: https://www.renesas.com/us/en/document/dst/zl9101m-datasheet - * Intersil / Zilker Labs ZL9117M + * Renesas / Intersil / Zilker Labs ZL9117M Prefix: 'zl9117' Addresses scanned: - - Datasheet: http://www.intersil.com/data/fn/fn7914.pdf + Datasheet: https://www.renesas.com/us/en/document/dst/zl9117m-datasheet + + * Renesas / Intersil / Zilker Labs ZLS1003, ZLS4009 + + Prefix: 'zls1003', zls4009 + + Addresses scanned: - + + Datasheet: Not published - * Ericsson BMR450, BMR451 + * Flex BMR450, BMR451 Prefix: 'bmr450', 'bmr451' @@ -91,17 +107,39 @@ Supported chips: Datasheet: -http://archive.ericsson.net/service/internet/picov/get?DocNo=28701-EN/LZT146401 +https://flexpowermodules.com/resources/fpm-techspec-bmr450-digital-pol-regulators-20a - * Ericsson BMR462, BMR463, BMR464 + * Flex BMR462, BMR463, BMR464 Prefixes: 'bmr462', 'bmr463', 'bmr464' Addresses scanned: - - Datasheet: + Datasheet: https://flexpowermodules.com/resources/fpm-techspec-bmr462 + + * Flex BMR465, BMR467 + + Prefixes: 'bmr465', 'bmr467' + + Addresses scanned: - + + Datasheet: https://flexpowermodules.com/resources/fpm-techspec-bmr465-digital-pol + + * Flex BMR466 + + Prefixes: 'bmr466' + + Addresses scanned: - + + Datasheet: https://flexpowermodules.com/resources/fpm-techspec-bmr466-8x12 - http://archive.ericsson.net/service/internet/picov/get?DocNo=28701-EN/LZT146256 + * Flex BMR469 + + Prefixes: 'bmr469' + + Addresses scanned: - + + Datasheet: https://flexpowermodules.com/resources/fpm-techspec-bmr4696001 Author: Guenter Roeck @@ -109,8 +147,8 @@ Author: Guenter Roeck Description ----------- -This driver supports hardware monitoring for Intersil / Zilker Labs ZL6100 and -compatible digital DC-DC controllers. +This driver supports hardware monitoring for Renesas / Intersil / Zilker Labs +ZL6100 and compatible digital DC-DC controllers. The driver is a client driver to the core PMBus driver. Please see Documentation/hwmon/pmbus.rst and Documentation.hwmon/pmbus-core for details @@ -147,12 +185,12 @@ Module parameters delay ----- -Intersil/Zilker Labs DC-DC controllers require a minimum interval between I2C -bus accesses. According to Intersil, the minimum interval is 2 ms, though 1 ms -appears to be sufficient and has not caused any problems in testing. The problem -is known to affect all currently supported chips. For manual override, the -driver provides a writeable module parameter, 'delay', which can be used to set -the interval to a value between 0 and 65,535 microseconds. +Renesas/Intersil/Zilker Labs DC-DC controllers require a minimum interval +between I2C bus accesses. According to Intersil, the minimum interval is 2 ms, +though 1 ms appears to be sufficient and has not caused any problems in testing. +The problem is known to affect all currently supported chips. For manual override, +the driver provides a writeable module parameter, 'delay', which can be used +to set the interval to a value between 0 and 65,535 microseconds. Sysfs entries @@ -182,24 +220,32 @@ in2_crit Critical maximum VMON/VDRV voltage. in2_lcrit_alarm VMON/VDRV voltage critical low alarm. in2_crit_alarm VMON/VDRV voltage critical high alarm. - vmon attributes are supported on ZL2004, ZL9101M, - and ZL9117M only. + vmon attributes are supported on ZL2004, ZL8802, + ZL9101M, ZL9117M and ZLS4009 only. -inX_label "vout1" +inX_label "vout[12]" inX_input Measured output voltage. inX_lcrit Critical minimum output Voltage. inX_crit Critical maximum output voltage. inX_lcrit_alarm Critical output voltage critical low alarm. inX_crit_alarm Critical output voltage critical high alarm. - X is 3 for ZL2004, ZL9101M, and ZL9117M, 2 otherwise. + X is 3 for ZL2004, ZL9101M, and ZL9117M, + 3, 4 for ZL8802 and 2 otherwise. + +curr1_label "iin" +curr1_input Measured input current. + + iin attributes are supported on ZL8802 only + +currY_label "iout[12]" +currY_input Measured output current. +currY_lcrit Critical minimum output current. +currY_crit Critical maximum output current. +currY_lcrit_alarm Output current critical low alarm. +currY_crit_alarm Output current critical high alarm. -curr1_label "iout1" -curr1_input Measured output current. -curr1_lcrit Critical minimum output current. -curr1_crit Critical maximum output current. -curr1_lcrit_alarm Output current critical low alarm. -curr1_crit_alarm Output current critical high alarm. + Y is 2, 3 for ZL8802, 1 otherwise temp[12]_input Measured temperature. temp[12]_min Minimum temperature. -- cgit v1.2.3 From ec081f9154766be98b7be6e4c4483b580c5b12e7 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Thu, 29 Apr 2021 14:11:49 +0200 Subject: hwmon: (lm75) Add TI TMP1075 support TI TMP1075 is a LM75 compatible sensor, so lets add support for it. Signed-off-by: Robert Marko Link: https://lore.kernel.org/r/20210429121150.106804-1-robert.marko@sartura.hr Signed-off-by: Guenter Roeck --- Documentation/hwmon/lm75.rst | 6 ++++-- drivers/hwmon/lm75.c | 13 +++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/Documentation/hwmon/lm75.rst b/Documentation/hwmon/lm75.rst index 81257d5fc48f..8d0ab4ad5fb5 100644 --- a/Documentation/hwmon/lm75.rst +++ b/Documentation/hwmon/lm75.rst @@ -93,9 +93,9 @@ Supported chips: https://www.st.com/resource/en/datasheet/stlm75.pdf - * Texas Instruments TMP100, TMP101, TMP105, TMP112, TMP75, TMP75B, TMP75C, TMP175, TMP275 + * Texas Instruments TMP100, TMP101, TMP105, TMP112, TMP75, TMP75B, TMP75C, TMP175, TMP275, TMP1075 - Prefixes: 'tmp100', 'tmp101', 'tmp105', 'tmp112', 'tmp175', 'tmp75', 'tmp75b', 'tmp75c', 'tmp275' + Prefixes: 'tmp100', 'tmp101', 'tmp105', 'tmp112', 'tmp175', 'tmp75', 'tmp75b', 'tmp75c', 'tmp275', 'tmp1075' Addresses scanned: none @@ -119,6 +119,8 @@ Supported chips: https://www.ti.com/product/tmp275 + https://www.ti.com/product/TMP1075 + * NXP LM75B, PCT2075 Prefix: 'lm75b', 'pct2075' diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c index e447febd121a..afdbb63237b9 100644 --- a/drivers/hwmon/lm75.c +++ b/drivers/hwmon/lm75.c @@ -50,6 +50,7 @@ enum lm75_type { /* keep sorted in alphabetical order */ tmp75, tmp75b, tmp75c, + tmp1075, }; /** @@ -293,6 +294,13 @@ static const struct lm75_params device_params[] = { .clr_mask = 1 << 5, /*not one-shot mode*/ .default_resolution = 12, .default_sample_time = MSEC_PER_SEC / 12, + }, + [tmp1075] = { /* not one-shot mode, 27.5 ms sample rate */ + .clr_mask = 1 << 5 | 1 << 6 | 1 << 7, + .default_resolution = 12, + .default_sample_time = 28, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 28, 55, 110, 220 }, } }; @@ -662,6 +670,7 @@ static const struct i2c_device_id lm75_ids[] = { { "tmp75", tmp75, }, { "tmp75b", tmp75b, }, { "tmp75c", tmp75c, }, + { "tmp1075", tmp1075, }, { /* LIST END */ } }; MODULE_DEVICE_TABLE(i2c, lm75_ids); @@ -771,6 +780,10 @@ static const struct of_device_id __maybe_unused lm75_of_match[] = { .compatible = "ti,tmp75c", .data = (void *)tmp75c }, + { + .compatible = "ti,tmp1075", + .data = (void *)tmp1075 + }, { }, }; MODULE_DEVICE_TABLE(of, lm75_of_match); -- cgit v1.2.3 From 42c7fd53aeff8241d64cdcfaffe06bb955852112 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Thu, 29 Apr 2021 14:11:50 +0200 Subject: dt-bindings: hwmon: Add Texas Instruments TMP1075 Document the DT compatible for TI TMP1075 which is a LM75 compatible sensor. Signed-off-by: Robert Marko Link: https://lore.kernel.org/r/20210429121150.106804-2-robert.marko@sartura.hr Acked-by: Rob Herring Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/hwmon/lm75.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/hwmon/lm75.yaml b/Documentation/devicetree/bindings/hwmon/lm75.yaml index 96eed5cc7841..72980d083c21 100644 --- a/Documentation/devicetree/bindings/hwmon/lm75.yaml +++ b/Documentation/devicetree/bindings/hwmon/lm75.yaml @@ -30,6 +30,7 @@ properties: - st,stds75 - st,stlm75 - microchip,tcn75 + - ti,tmp1075 - ti,tmp100 - ti,tmp101 - ti,tmp105 -- cgit v1.2.3 From f0635523c8b57aea6b1b75e99ea9c86ccc2a8b45 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 16 May 2021 12:18:18 +0200 Subject: docs: hwmon: ir36021.rst: replace some characters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The conversion tools used during DocBook/LaTeX/html/Markdown->ReST conversion and some cut-and-pasted text contain some characters that aren't easily reachable on standard keyboards and/or could cause troubles when parsed by the documentation build system. Replace the occurences of the following characters: - U+2010 ('‐'): HYPHEN as ASCII HYPHEN is preferred over U+2010 Signed-off-by: Mauro Carvalho Chehab Link: https://lore.kernel.org/r/ba8b5122ac9d4918fd966d0eb0a5ca9d89044b04.1621159997.git.mchehab+huawei@kernel.org Signed-off-by: Guenter Roeck --- Documentation/hwmon/ir36021.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/hwmon/ir36021.rst b/Documentation/hwmon/ir36021.rst index ca3436b04e20..1faa85c39f1b 100644 --- a/Documentation/hwmon/ir36021.rst +++ b/Documentation/hwmon/ir36021.rst @@ -19,7 +19,7 @@ Authors: Description ----------- -The IR36021 is a dual‐loop digital multi‐phase buck controller designed for +The IR36021 is a dual-loop digital multi-phase buck controller designed for point of load applications. Usage Notes -- cgit v1.2.3 From b3ea2fe7e2814d17426674eff3d440c4e9c3a107 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 10 May 2021 12:26:17 +0200 Subject: docs: hwmon: avoid using UTF-8 chars MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While UTF-8 characters can be used at the Linux documentation, the best is to use them only when ASCII doesn't offer a good replacement. So, replace the occurences of the following UTF-8 characters: - U+2010 ('‐'): HYPHEN - U+2013 ('–'): EN DASH - U+2019 ('’'): RIGHT SINGLE QUOTATION MARK Signed-off-by: Mauro Carvalho Chehab Link: https://lore.kernel.org/r/ccdd1bf45963a7748188a97c75f667b37bd43d2f.1620641727.git.mchehab+huawei@kernel.org Signed-off-by: Guenter Roeck --- Documentation/hwmon/ltc2992.rst | 2 +- Documentation/hwmon/pm6764tr.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/hwmon/ltc2992.rst b/Documentation/hwmon/ltc2992.rst index 46aa1aa84a1a..a0bcd867a0f5 100644 --- a/Documentation/hwmon/ltc2992.rst +++ b/Documentation/hwmon/ltc2992.rst @@ -19,7 +19,7 @@ This driver supports hardware monitoring for Linear Technology LTC2992 power mon LTC2992 is a rail-to-rail system monitor that measures current, voltage, and power of two supplies. -Two ADCs simultaneously measure each supply’s current. A third ADC monitors +Two ADCs simultaneously measure each supply's current. A third ADC monitors the input voltages and four auxiliary external voltages. diff --git a/Documentation/hwmon/pm6764tr.rst b/Documentation/hwmon/pm6764tr.rst index a1fb8fea2326..294a8ffc8bd8 100644 --- a/Documentation/hwmon/pm6764tr.rst +++ b/Documentation/hwmon/pm6764tr.rst @@ -20,7 +20,7 @@ Description: ------------ This driver supports the STMicroelectronics PM6764TR chip. The PM6764TR is a high -performance digital controller designed to power Intel’s VR12.5 processors and memories. +performance digital controller designed to power Intel's VR12.5 processors and memories. The device utilizes digital technology to implement all control and power management functions to provide maximum flexibility and performance. The NVM is embedded to store -- cgit v1.2.3 From ac61c8aae446b9c0fe18981fe721d4a43e283ad6 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 8 May 2021 09:44:50 -0700 Subject: hwmon: (lm70) Revert "hwmon: (lm70) Add support for ACPI" This reverts commit b58bd4c6dfe709646ed9efcbba2a70643f9bc873. None of the ACPI IDs introduced with the reverted patch is a valid ACPI device ID. Any ACPI users of this driver are advised to use PRP0001 and a devicetree-compatible device identification. Fixes: b58bd4c6dfe7 ("hwmon: (lm70) Add support for ACPI") Cc: Andrej Picej Signed-off-by: Guenter Roeck --- drivers/hwmon/lm70.c | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/drivers/hwmon/lm70.c b/drivers/hwmon/lm70.c index 40eab3349904..6b884ea00987 100644 --- a/drivers/hwmon/lm70.c +++ b/drivers/hwmon/lm70.c @@ -22,10 +22,10 @@ #include #include #include +#include #include #include #include -#include #define DRVNAME "lm70" @@ -148,29 +148,6 @@ static const struct of_device_id lm70_of_ids[] = { MODULE_DEVICE_TABLE(of, lm70_of_ids); #endif -#ifdef CONFIG_ACPI -static const struct acpi_device_id lm70_acpi_ids[] = { - { - .id = "LM000070", - .driver_data = LM70_CHIP_LM70, - }, - { - .id = "TMP00121", - .driver_data = LM70_CHIP_TMP121, - }, - { - .id = "LM000071", - .driver_data = LM70_CHIP_LM71, - }, - { - .id = "LM000074", - .driver_data = LM70_CHIP_LM74, - }, - {}, -}; -MODULE_DEVICE_TABLE(acpi, lm70_acpi_ids); -#endif - static int lm70_probe(struct spi_device *spi) { struct device *hwmon_dev; @@ -217,7 +194,6 @@ static struct spi_driver lm70_driver = { .driver = { .name = "lm70", .of_match_table = of_match_ptr(lm70_of_ids), - .acpi_match_table = ACPI_PTR(lm70_acpi_ids), }, .id_table = lm70_ids, .probe = lm70_probe, -- cgit v1.2.3 From 97387c2f06bcfd79d04a848d35517b32ee6dca7c Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 8 May 2021 09:50:25 -0700 Subject: hwmon: (max31722) Remove non-standard ACPI device IDs Valid Maxim Integrated ACPI device IDs would start with MXIM, not with MAX1. On top of that, ACPI device IDs reflecting chip names are almost always invalid. Remove the invalid ACPI IDs. Fixes: 04e1e70afec6 ("hwmon: (max31722) Add support for MAX31722/MAX31723 temperature sensors") Signed-off-by: Guenter Roeck --- drivers/hwmon/max31722.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/hwmon/max31722.c b/drivers/hwmon/max31722.c index 062eceb7be0d..613338cbcb17 100644 --- a/drivers/hwmon/max31722.c +++ b/drivers/hwmon/max31722.c @@ -6,7 +6,6 @@ * Copyright (c) 2016, Intel Corporation. */ -#include #include #include #include @@ -133,20 +132,12 @@ static const struct spi_device_id max31722_spi_id[] = { {"max31723", 0}, {} }; - -static const struct acpi_device_id __maybe_unused max31722_acpi_id[] = { - {"MAX31722", 0}, - {"MAX31723", 0}, - {} -}; - MODULE_DEVICE_TABLE(spi, max31722_spi_id); static struct spi_driver max31722_driver = { .driver = { .name = "max31722", .pm = &max31722_pm_ops, - .acpi_match_table = ACPI_PTR(max31722_acpi_id), }, .probe = max31722_probe, .remove = max31722_remove, -- cgit v1.2.3 From ba9c5fc395de5bb642ed973dbf34c1d0c82d185d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 10 May 2021 17:13:31 +0300 Subject: hwmon: (lm70) Use SPI_MODE_X_MASK Use SPI_MODE_X_MASK instead of open coded variant. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20210510141331.56736-1-andriy.shevchenko@linux.intel.com Signed-off-by: Guenter Roeck --- drivers/hwmon/lm70.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/lm70.c b/drivers/hwmon/lm70.c index 6b884ea00987..d2a60de5b8de 100644 --- a/drivers/hwmon/lm70.c +++ b/drivers/hwmon/lm70.c @@ -161,7 +161,7 @@ static int lm70_probe(struct spi_device *spi) /* signaling is SPI_MODE_0 */ - if (spi->mode & (SPI_CPOL | SPI_CPHA)) + if ((spi->mode & SPI_MODE_X_MASK) != SPI_MODE_0) return -EINVAL; /* NOTE: we assume 8-bit words, and convert to 16 bits manually */ -- cgit v1.2.3 From 2be5f0d7532566d41194fe99d35d022ad399460d Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Sat, 8 May 2021 15:14:54 +0200 Subject: hwmon: (sch56xx) Use devres functions for watchdog Use devm_kzalloc()/devm_watchdog_register() for watchdog registration since it allows us to remove the sch56xx_watchdog_data struct from the drivers own data structs. Remove sch56xx_watchdog_unregister since devres takes care of that now. Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20210508131457.12780-2-W_Armin@gmx.de Reviewed-by: Hans de Goede [groeck: Dropped unnecessary return; at end of void function] Signed-off-by: Guenter Roeck --- drivers/hwmon/sch5627.c | 18 +++--------------- drivers/hwmon/sch5636.c | 9 ++------- drivers/hwmon/sch56xx-common.c | 28 +++++++++------------------- drivers/hwmon/sch56xx-common.h | 4 ++-- 4 files changed, 16 insertions(+), 43 deletions(-) diff --git a/drivers/hwmon/sch5627.c b/drivers/hwmon/sch5627.c index 4324a5dbc968..8f1b569c69e7 100644 --- a/drivers/hwmon/sch5627.c +++ b/drivers/hwmon/sch5627.c @@ -64,7 +64,6 @@ static const char * const SCH5627_IN_LABELS[SCH5627_NO_IN] = { struct sch5627_data { unsigned short addr; - struct sch56xx_watchdog_data *watchdog; u8 control; u8 temp_max[SCH5627_NO_TEMPS]; u8 temp_crit[SCH5627_NO_TEMPS]; @@ -357,16 +356,6 @@ static const struct hwmon_chip_info sch5627_chip_info = { .info = sch5627_info, }; -static int sch5627_remove(struct platform_device *pdev) -{ - struct sch5627_data *data = platform_get_drvdata(pdev); - - if (data->watchdog) - sch56xx_watchdog_unregister(data->watchdog); - - return 0; -} - static int sch5627_probe(struct platform_device *pdev) { struct sch5627_data *data; @@ -460,9 +449,9 @@ static int sch5627_probe(struct platform_device *pdev) return PTR_ERR(hwmon_dev); /* Note failing to register the watchdog is not a fatal error */ - data->watchdog = sch56xx_watchdog_register(&pdev->dev, data->addr, - (build_code << 24) | (build_id << 8) | hwmon_rev, - &data->update_lock, 1); + sch56xx_watchdog_register(&pdev->dev, data->addr, + (build_code << 24) | (build_id << 8) | hwmon_rev, + &data->update_lock, 1); return 0; } @@ -472,7 +461,6 @@ static struct platform_driver sch5627_driver = { .name = DRVNAME, }, .probe = sch5627_probe, - .remove = sch5627_remove, }; module_platform_driver(sch5627_driver); diff --git a/drivers/hwmon/sch5636.c b/drivers/hwmon/sch5636.c index 5683a38740f6..a5cd4de36575 100644 --- a/drivers/hwmon/sch5636.c +++ b/drivers/hwmon/sch5636.c @@ -54,7 +54,6 @@ static const u16 SCH5636_REG_FAN_VAL[SCH5636_NO_FANS] = { struct sch5636_data { unsigned short addr; struct device *hwmon_dev; - struct sch56xx_watchdog_data *watchdog; struct mutex update_lock; char valid; /* !=0 if following fields are valid */ @@ -372,9 +371,6 @@ static int sch5636_remove(struct platform_device *pdev) struct sch5636_data *data = platform_get_drvdata(pdev); int i; - if (data->watchdog) - sch56xx_watchdog_unregister(data->watchdog); - if (data->hwmon_dev) hwmon_device_unregister(data->hwmon_dev); @@ -495,9 +491,8 @@ static int sch5636_probe(struct platform_device *pdev) } /* Note failing to register the watchdog is not a fatal error */ - data->watchdog = sch56xx_watchdog_register(&pdev->dev, data->addr, - (revision[0] << 8) | revision[1], - &data->update_lock, 0); + sch56xx_watchdog_register(&pdev->dev, data->addr, (revision[0] << 8) | revision[1], + &data->update_lock, 0); return 0; diff --git a/drivers/hwmon/sch56xx-common.c b/drivers/hwmon/sch56xx-common.c index 6c84780e358e..b469d16ec175 100644 --- a/drivers/hwmon/sch56xx-common.c +++ b/drivers/hwmon/sch56xx-common.c @@ -378,8 +378,8 @@ static const struct watchdog_ops watchdog_ops = { .set_timeout = watchdog_set_timeout, }; -struct sch56xx_watchdog_data *sch56xx_watchdog_register(struct device *parent, - u16 addr, u32 revision, struct mutex *io_lock, int check_enabled) +void sch56xx_watchdog_register(struct device *parent, u16 addr, u32 revision, + struct mutex *io_lock, int check_enabled) { struct sch56xx_watchdog_data *data; int err, control, output_enable; @@ -393,17 +393,17 @@ struct sch56xx_watchdog_data *sch56xx_watchdog_register(struct device *parent, mutex_unlock(io_lock); if (control < 0) - return NULL; + return; if (output_enable < 0) - return NULL; + return; if (check_enabled && !(output_enable & SCH56XX_WDOG_OUTPUT_ENABLE)) { pr_warn("Watchdog not enabled by BIOS, not registering\n"); - return NULL; + return; } - data = kzalloc(sizeof(struct sch56xx_watchdog_data), GFP_KERNEL); + data = devm_kzalloc(parent, sizeof(struct sch56xx_watchdog_data), GFP_KERNEL); if (!data) - return NULL; + return; data->addr = addr; data->io_lock = io_lock; @@ -438,24 +438,14 @@ struct sch56xx_watchdog_data *sch56xx_watchdog_register(struct device *parent, data->watchdog_output_enable = output_enable; watchdog_set_drvdata(&data->wddev, data); - err = watchdog_register_device(&data->wddev); + err = devm_watchdog_register_device(parent, &data->wddev); if (err) { pr_err("Registering watchdog chardev: %d\n", err); - kfree(data); - return NULL; + devm_kfree(parent, data); } - - return data; } EXPORT_SYMBOL(sch56xx_watchdog_register); -void sch56xx_watchdog_unregister(struct sch56xx_watchdog_data *data) -{ - watchdog_unregister_device(&data->wddev); - kfree(data); -} -EXPORT_SYMBOL(sch56xx_watchdog_unregister); - /* * platform dev find, add and remove functions */ diff --git a/drivers/hwmon/sch56xx-common.h b/drivers/hwmon/sch56xx-common.h index 75eb73617cf2..e907d9da0dd5 100644 --- a/drivers/hwmon/sch56xx-common.h +++ b/drivers/hwmon/sch56xx-common.h @@ -14,6 +14,6 @@ int sch56xx_read_virtual_reg16(u16 addr, u16 reg); int sch56xx_read_virtual_reg12(u16 addr, u16 msb_reg, u16 lsn_reg, int high_nibble); -struct sch56xx_watchdog_data *sch56xx_watchdog_register(struct device *parent, - u16 addr, u32 revision, struct mutex *io_lock, int check_enabled); +void sch56xx_watchdog_register(struct device *parent, u16 addr, u32 revision, + struct mutex *io_lock, int check_enabled); void sch56xx_watchdog_unregister(struct sch56xx_watchdog_data *data); -- cgit v1.2.3 From 6df5cba5c9e7bf98c114f15835d20dfd6c7898cf Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Sat, 8 May 2021 15:14:55 +0200 Subject: hwmon: (sch56xx-common) Use strscpy strlcpy is considered deprecated. Replace it with strscpy. Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20210508131457.12780-3-W_Armin@gmx.de Reviewed-by: Hans de Goede Signed-off-by: Guenter Roeck --- drivers/hwmon/sch56xx-common.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/hwmon/sch56xx-common.c b/drivers/hwmon/sch56xx-common.c index b469d16ec175..da915ca2b3b5 100644 --- a/drivers/hwmon/sch56xx-common.c +++ b/drivers/hwmon/sch56xx-common.c @@ -408,8 +408,7 @@ void sch56xx_watchdog_register(struct device *parent, u16 addr, u32 revision, data->addr = addr; data->io_lock = io_lock; - strlcpy(data->wdinfo.identity, "sch56xx watchdog", - sizeof(data->wdinfo.identity)); + strscpy(data->wdinfo.identity, "sch56xx watchdog", sizeof(data->wdinfo.identity)); data->wdinfo.firmware_version = revision; data->wdinfo.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT; if (!nowayout) -- cgit v1.2.3 From 989c9c675bbbf3264b42b05e8924a9930b500e6c Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Sat, 8 May 2021 15:14:56 +0200 Subject: hwmon: (sch56xx-common) Use helper function Use watchdog_set_nowayout() to process param setting and change param type to bool. Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20210508131457.12780-4-W_Armin@gmx.de Reviewed-by: Hans de Goede Signed-off-by: Guenter Roeck --- drivers/hwmon/sch56xx-common.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/hwmon/sch56xx-common.c b/drivers/hwmon/sch56xx-common.c index da915ca2b3b5..04739bbff8f9 100644 --- a/drivers/hwmon/sch56xx-common.c +++ b/drivers/hwmon/sch56xx-common.c @@ -20,8 +20,8 @@ #include "sch56xx-common.h" /* Insmod parameters */ -static int nowayout = WATCHDOG_NOWAYOUT; -module_param(nowayout, int, 0); +static bool nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); @@ -420,8 +420,7 @@ void sch56xx_watchdog_register(struct device *parent, u16 addr, u32 revision, data->wddev.timeout = 60; data->wddev.min_timeout = 1; data->wddev.max_timeout = 255 * 60; - if (nowayout) - set_bit(WDOG_NO_WAY_OUT, &data->wddev.status); + watchdog_set_nowayout(&data->wddev, nowayout); if (output_enable & SCH56XX_WDOG_OUTPUT_ENABLE) set_bit(WDOG_ACTIVE, &data->wddev.status); -- cgit v1.2.3 From 5c1c78e0a0a2f37de0b05851878af8e02eeae02f Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Sat, 8 May 2021 15:14:57 +0200 Subject: hwmon: (sch56xx-common) Simplify sch56xx_device_add Use platform_device_register_simple() instead of manually calling platform_device_alloc()/platform_device_add(). Signed-off-by: Armin Wolf Link: https://lore.kernel.org/r/20210508131457.12780-5-W_Armin@gmx.de Reviewed-by: Hans de Goede Signed-off-by: Guenter Roeck --- drivers/hwmon/sch56xx-common.c | 27 ++++----------------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/drivers/hwmon/sch56xx-common.c b/drivers/hwmon/sch56xx-common.c index 04739bbff8f9..40cdadad35e5 100644 --- a/drivers/hwmon/sch56xx-common.c +++ b/drivers/hwmon/sch56xx-common.c @@ -504,37 +504,18 @@ static int __init sch56xx_device_add(int address, const char *name) struct resource res = { .start = address, .end = address + REGION_LENGTH - 1, + .name = name, .flags = IORESOURCE_IO, }; int err; - sch56xx_pdev = platform_device_alloc(name, address); - if (!sch56xx_pdev) - return -ENOMEM; - - res.name = sch56xx_pdev->name; err = acpi_check_resource_conflict(&res); if (err) - goto exit_device_put; - - err = platform_device_add_resources(sch56xx_pdev, &res, 1); - if (err) { - pr_err("Device resource addition failed\n"); - goto exit_device_put; - } - - err = platform_device_add(sch56xx_pdev); - if (err) { - pr_err("Device addition failed\n"); - goto exit_device_put; - } - - return 0; + return err; -exit_device_put: - platform_device_put(sch56xx_pdev); + sch56xx_pdev = platform_device_register_simple(name, -1, &res, 1); - return err; + return PTR_ERR_OR_ZERO(sch56xx_pdev); } static int __init sch56xx_init(void) -- cgit v1.2.3 From 86c908d90fb17273f5f6d15539ad3d7bf134d892 Mon Sep 17 00:00:00 2001 From: Erik Rosen Date: Fri, 7 May 2021 21:40:21 +0200 Subject: hwmon: (pmbus) Add new flag PMBUS_READ_STATUS_AFTER_FAILED_CHECK Some PMBus chips end up in an undefined state when trying to read an unsupported register. For such chips, it is necessary to reset the chip pmbus controller to a known state after a failed register check. 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. Signed-off-by: Erik Rosen Link: https://lore.kernel.org/r/20210507194023.61138-2-erik.rosen@metormote.com Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus_core.c | 2 ++ include/linux/pmbus.h | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index bbd745178147..1f7fa5337974 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -523,6 +523,8 @@ static bool pmbus_check_register(struct i2c_client *client, rv = func(client, page, reg); if (rv >= 0 && !(data->flags & PMBUS_SKIP_STATUS_CHECK)) rv = pmbus_check_status_cml(client); + if (rv < 0 && (data->flags & PMBUS_READ_STATUS_AFTER_FAILED_CHECK)) + data->read_status(client, -1); pmbus_clear_fault_page(client, -1); return rv >= 0; } diff --git a/include/linux/pmbus.h b/include/linux/pmbus.h index 12cbbf305969..edd7c84fef65 100644 --- a/include/linux/pmbus.h +++ b/include/linux/pmbus.h @@ -43,6 +43,19 @@ */ #define PMBUS_NO_CAPABILITY BIT(2) +/* + * PMBUS_READ_STATUS_AFTER_FAILED_CHECK + * + * Some PMBus chips end up in an undefined state when trying to read an + * unsupported register. For such chips, it is necessary to reset the + * chip pmbus controller to a known state after a failed register check. + * 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 in a + * known state. + */ +#define PMBUS_READ_STATUS_AFTER_FAILED_CHECK BIT(3) + struct pmbus_platform_data { u32 flags; /* Device specific flags */ -- cgit v1.2.3 From b976760dc4efd1de7965bf020195a22fce4f456c Mon Sep 17 00:00:00 2001 From: Erik Rosen Date: Fri, 7 May 2021 21:40:22 +0200 Subject: hwmon: (pmbus) Add documentation for new flags Add documentation for the new pmbus flags PMBUS_WRITE_PROTECTED, PMBUS_NO_CAPABILITY and PMBUS_READ_STATUS_AFTER_FAILED_CHECK Signed-off-by: Erik Rosen Link: https://lore.kernel.org/r/20210507194023.61138-3-erik.rosen@metormote.com [groeck: Added newline at end of file] Signed-off-by: Guenter Roeck --- Documentation/hwmon/pmbus-core.rst | 42 ++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/Documentation/hwmon/pmbus-core.rst b/Documentation/hwmon/pmbus-core.rst index 73e23ab42cc3..e7e0c9ef10be 100644 --- a/Documentation/hwmon/pmbus-core.rst +++ b/Documentation/hwmon/pmbus-core.rst @@ -289,12 +289,22 @@ PMBus driver platform data ========================== PMBus platform data is defined in include/linux/pmbus.h. Platform data -currently only provides a flag field with a single bit used:: +currently provides a flags field with four bits used:: - #define PMBUS_SKIP_STATUS_CHECK (1 << 0) + #define PMBUS_SKIP_STATUS_CHECK BIT(0) + + #define PMBUS_WRITE_PROTECTED BIT(1) + + #define PMBUS_NO_CAPABILITY BIT(2) + + #define PMBUS_READ_STATUS_AFTER_FAILED_CHECK BIT(3) struct pmbus_platform_data { u32 flags; /* Device specific flags */ + + /* regulator support */ + int num_regulators; + struct regulator_init_data *reg_init_data; }; @@ -302,8 +312,9 @@ Flags ----- PMBUS_SKIP_STATUS_CHECK - During register detection, skip checking the status register for - communication or command errors. + +During register detection, skip checking the status register for +communication or command errors. Some PMBus chips respond with valid data when trying to read an unsupported register. For such chips, checking the status register is mandatory when @@ -315,3 +326,26 @@ status register must be disabled. Some i2c controllers do not support single-byte commands (write commands with no data, i2c_smbus_write_byte()). With such controllers, clearing the status register is impossible, and the PMBUS_SKIP_STATUS_CHECK flag must be set. + +PMBUS_WRITE_PROTECTED + +Set if the chip is write protected and write protection is not determined +by the standard WRITE_PROTECT command. + +PMBUS_NO_CAPABILITY + +Some PMBus chips don't respond with valid data when reading the CAPABILITY +register. For such chips, this flag should be set so that the PMBus core +driver doesn't use CAPABILITY to determine it's behavior. + +PMBUS_READ_STATUS_AFTER_FAILED_CHECK + +Read the STATUS register after each failed register check. + +Some PMBus chips end up in an undefined state when trying to read an +unsupported register. For such chips, it is necessary to reset the +chip pmbus controller to a known state after a failed register check. +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. -- cgit v1.2.3 From ea541c185c358f870ccb0d5fce6f726c5146daae Mon Sep 17 00:00:00 2001 From: Erik Rosen Date: Fri, 7 May 2021 21:40:23 +0200 Subject: hwmon: (pmbus) Add support for additional Flex BMR converters to pmbus Add support for Flex BMR310, BMR456, BMR457, BMR458, BMR480, BMR490, BMR491 and BMR492 to the pmbus driver Signed-off-by: Erik Rosen Link: https://lore.kernel.org/r/20210507194023.61138-4-erik.rosen@metormote.com [groeck: Fixed minor whitespace error] Signed-off-by: Guenter Roeck --- Documentation/hwmon/pmbus.rst | 11 +++++++---- drivers/hwmon/pmbus/Kconfig | 7 ++++--- drivers/hwmon/pmbus/pmbus.c | 19 +++++++++++++++++-- 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/Documentation/hwmon/pmbus.rst b/Documentation/hwmon/pmbus.rst index c44f14115413..7ecfec6ca2db 100644 --- a/Documentation/hwmon/pmbus.rst +++ b/Documentation/hwmon/pmbus.rst @@ -3,15 +3,18 @@ Kernel driver pmbus Supported chips: - * Ericsson BMR453, BMR454 + * Flex BMR310, BMR453, BMR454, BMR456, BMR457, BMR458, BMR480, + BMR490, BMR491, BMR492 - Prefixes: 'bmr453', 'bmr454' + Prefixes: 'bmr310', 'bmr453', 'bmr454', 'bmr456', 'bmr457', 'bmr458', 'bmr480', + 'bmr490', 'bmr491', 'bmr492' Addresses scanned: - - Datasheet: + Datasheets: + + https://flexpowermodules.com/products - http://archive.ericsson.net/service/internet/picov/get?DocNo=28701-EN/LZT146395 * ON Semiconductor ADP4000, NCP4200, NCP4208 diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 37a5c39784fa..6275dcf78675 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -19,9 +19,10 @@ config SENSORS_PMBUS default y help If you say yes here you get hardware monitoring support for generic - PMBus devices, including but not limited to ADP4000, BMR453, BMR454, - MAX20796, MDT040, NCP4200, NCP4208, PDT003, PDT006, PDT012, TPS40400, - TPS544B20, TPS544B25, TPS544C20, TPS544C25, and UDT020. + PMBus devices, including but not limited to ADP4000, BMR310, BMR453, + BMR454, BMR456, BMR457, BMR458, BMR480, BMR490, BMR491, BMR492, + MAX20796, MDT040, NCP4200, NCP4208, PDT003, PDT006, PDT012, + TPS40400, TPS544B20, TPS544B25, TPS544C20, TPS544C25, and UDT020. This driver can also be built as a module. If so, the module will be called pmbus. diff --git a/drivers/hwmon/pmbus/pmbus.c b/drivers/hwmon/pmbus/pmbus.c index 618c377664c4..d0d386990af5 100644 --- a/drivers/hwmon/pmbus/pmbus.c +++ b/drivers/hwmon/pmbus/pmbus.c @@ -173,13 +173,13 @@ static int pmbus_probe(struct i2c_client *client) return -ENOMEM; device_info = (struct pmbus_device_info *)i2c_match_id(pmbus_id, client)->driver_data; - if (device_info->flags & PMBUS_SKIP_STATUS_CHECK) { + if (device_info->flags) { pdata = devm_kzalloc(dev, sizeof(struct pmbus_platform_data), GFP_KERNEL); if (!pdata) return -ENOMEM; - pdata->flags = PMBUS_SKIP_STATUS_CHECK; + pdata->flags = device_info->flags; } info->pages = device_info->pages; @@ -193,22 +193,37 @@ static const struct pmbus_device_info pmbus_info_one = { .pages = 1, .flags = 0 }; + static const struct pmbus_device_info pmbus_info_zero = { .pages = 0, .flags = 0 }; + static const struct pmbus_device_info pmbus_info_one_skip = { .pages = 1, .flags = PMBUS_SKIP_STATUS_CHECK }; +static const struct pmbus_device_info pmbus_info_one_status = { + .pages = 1, + .flags = PMBUS_READ_STATUS_AFTER_FAILED_CHECK +}; + /* * Use driver_data to set the number of pages supported by the chip. */ static const struct i2c_device_id pmbus_id[] = { {"adp4000", (kernel_ulong_t)&pmbus_info_one}, + {"bmr310", (kernel_ulong_t)&pmbus_info_one_status}, {"bmr453", (kernel_ulong_t)&pmbus_info_one}, {"bmr454", (kernel_ulong_t)&pmbus_info_one}, + {"bmr456", (kernel_ulong_t)&pmbus_info_one}, + {"bmr457", (kernel_ulong_t)&pmbus_info_one}, + {"bmr458", (kernel_ulong_t)&pmbus_info_one_status}, + {"bmr480", (kernel_ulong_t)&pmbus_info_one_status}, + {"bmr490", (kernel_ulong_t)&pmbus_info_one_status}, + {"bmr491", (kernel_ulong_t)&pmbus_info_one_status}, + {"bmr492", (kernel_ulong_t)&pmbus_info_one}, {"dps460", (kernel_ulong_t)&pmbus_info_one_skip}, {"dps650ab", (kernel_ulong_t)&pmbus_info_one_skip}, {"dps800", (kernel_ulong_t)&pmbus_info_one_skip}, -- cgit v1.2.3 From 4943c6039d4ac1ae8535786da7c2a28c376c589c Mon Sep 17 00:00:00 2001 From: Zhen Lei Date: Tue, 11 May 2021 17:18:43 +0800 Subject: hwmon: (bt1-pvt) Remove redundant error printing in pvt_request_regs() When devm_ioremap_resource() fails, a clear enough error message will be printed by its subfunction __devm_ioremap_resource(). The error information contains the device name, failure cause, and possibly resource information. Therefore, remove the error printing here to simplify code and reduce the binary size. Reported-by: Hulk Robot Signed-off-by: Zhen Lei Link: https://lore.kernel.org/r/20210511091843.4561-1-thunder.leizhen@huawei.com Signed-off-by: Guenter Roeck --- drivers/hwmon/bt1-pvt.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/hwmon/bt1-pvt.c b/drivers/hwmon/bt1-pvt.c index 3e1d56585b91..74ce5211eb75 100644 --- a/drivers/hwmon/bt1-pvt.c +++ b/drivers/hwmon/bt1-pvt.c @@ -924,10 +924,8 @@ static int pvt_request_regs(struct pvt_hwmon *pvt) } pvt->regs = devm_ioremap_resource(pvt->dev, res); - if (IS_ERR(pvt->regs)) { - dev_err(pvt->dev, "Couldn't map PVT registers\n"); + if (IS_ERR(pvt->regs)) return PTR_ERR(pvt->regs); - } return 0; } -- cgit v1.2.3 From 0c1acde1d3d0032814be89c838483471582bc32e Mon Sep 17 00:00:00 2001 From: Vadim Pasternak Date: Tue, 11 May 2021 08:56:17 +0300 Subject: hwmon: (pmbus) Increase maximum number of phases per page Increase maximum number of phases from 8 to 10 to support multi-phase devices allowing up to 10 phases. Signed-off-by: Vadim Pasternak Link: https://lore.kernel.org/r/20210511055619.118104-2-vadimp@nvidia.com Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h index 3968924f8533..e0aa8aa46d8c 100644 --- a/drivers/hwmon/pmbus/pmbus.h +++ b/drivers/hwmon/pmbus/pmbus.h @@ -375,7 +375,7 @@ enum pmbus_sensor_classes { }; #define PMBUS_PAGES 32 /* Per PMBus specification */ -#define PMBUS_PHASES 8 /* Maximum number of phases per page */ +#define PMBUS_PHASES 10 /* Maximum number of phases per page */ /* Functionality bit mask */ #define PMBUS_HAVE_VIN BIT(0) -- cgit v1.2.3 From e4db7719d037b820024a213f74703ae1abf5b00c Mon Sep 17 00:00:00 2001 From: Vadim Pasternak Date: Tue, 11 May 2021 08:56:18 +0300 Subject: hwmon: (pmbus) Add support for MPS Multi-phase mp2888 controller Add support for mp2888 device from Monolithic Power Systems, Inc. (MPS) vendor. This is a digital, multi-phase, pulse-width modulation controller. This device supports: - One power rail. - Programmable Multi-Phase up to 10 Phases. - PWM-VID Interface - One pages 0 for telemetry. - Programmable pins for PMBus Address. - Built-In EEPROM to Store Custom Configurations. - Can configured VOUT readout in direct or VID format and allows setting of different formats on rails 1 and 2. For VID the following protocols are available: VR13 mode with 5-mV DAC; VR13 mode with 10-mV DAC, IMVP9 mode with 5-mV DAC. Signed-off-by: Vadim Pasternak Reported-by: kernel test robot Link: https://lore.kernel.org/r/20210511055619.118104-3-vadimp@nvidia.com [groeck: Add MODULE_IMPORT_NS] Signed-off-by: Guenter Roeck --- Documentation/hwmon/mp2888.rst | 113 ++++++++++++ drivers/hwmon/pmbus/Kconfig | 9 + drivers/hwmon/pmbus/Makefile | 1 + drivers/hwmon/pmbus/mp2888.c | 408 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 531 insertions(+) create mode 100644 Documentation/hwmon/mp2888.rst create mode 100644 drivers/hwmon/pmbus/mp2888.c diff --git a/Documentation/hwmon/mp2888.rst b/Documentation/hwmon/mp2888.rst new file mode 100644 index 000000000000..5e578fd7b147 --- /dev/null +++ b/Documentation/hwmon/mp2888.rst @@ -0,0 +1,113 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Kernel driver mp2888 +==================== + +Supported chips: + + * MPS MP12254 + + Prefix: 'mp2888' + +Author: + + Vadim Pasternak + +Description +----------- + +This driver implements support for Monolithic Power Systems, Inc. (MPS) +vendor dual-loop, digital, multi-phase controller MP2888. + +This device: supports: + +- One power rail. +- Programmable Multi-Phase up to 10 Phases. +- PWM-VID Interface +- One pages 0 for telemetry. +- Programmable pins for PMBus Address. +- Built-In EEPROM to Store Custom Configurations. + +Device complaint with: + +- PMBus rev 1.3 interface. + +Device supports direct format for reading output current, output voltage, +input and output power and temperature. +Device supports linear format for reading input voltage and input power. + +The driver provides the next attributes for the current: + +- for current out input and maximum alarm; +- for phase current: input and label. + +The driver exports the following attributes via the 'sysfs' files, where: + +- 'n' is number of configured phases (from 1 to 10); +- index 1 for "iout"; +- indexes 2 ... 1 + n for phases. + +**curr[1-{1+n}]_input** + +**curr[1-{1+n}]_label** + +**curr1_max** + +**curr1_max_alarm** + +The driver provides the next attributes for the voltage: + +- for voltage in: input, low and high critical thresholds, low and high + critical alarms; +- for voltage out: input and high alarm; + +The driver exports the following attributes via the 'sysfs' files, where + +**in1_crit** + +**in1_crit_alarm** + +**in1_input** + +**in1_label** + +**in1_min** + +**in1_min_alarm** + +**in2_alarm** + +**in2_input** + +**in2_label** + +The driver provides the next attributes for the power: + +- for power in alarm and input. +- for power out: cap, cap alarm an input. + +The driver exports the following attributes via the 'sysfs' files, where +- indexes 1 for "pin"; +- indexes 2 for "pout"; + +**power1_alarm** + +**power1_input** + +**power1_label** + +**power2_input** + +**power2_label** + +**power2_max** + +**power2_max_alarm** + +The driver provides the next attributes for the temperature: + +**temp1_input** + +**temp1_max** + +**temp1_max_alarm** diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 6275dcf78675..52d8cd63603e 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -249,6 +249,15 @@ config SENSORS_MAX8688 This driver can also be built as a module. If so, the module will be called max8688. +config SENSORS_MP2888 + tristate "MPS MP2888" + help + If you say yes here you get hardware monitoring support for MPS + MP2888 Digital, Multi-Phase, Pulse-Width Modulation Controller. + + This driver can also be built as a module. If so, the module will + be called mp2888. + config SENSORS_MP2975 tristate "MPS MP2975" help diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile index f8dcc27cd56a..35d293bb44bf 100644 --- a/drivers/hwmon/pmbus/Makefile +++ b/drivers/hwmon/pmbus/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_SENSORS_MAX20751) += max20751.o obj-$(CONFIG_SENSORS_MAX31785) += max31785.o obj-$(CONFIG_SENSORS_MAX34440) += max34440.o obj-$(CONFIG_SENSORS_MAX8688) += max8688.o +obj-$(CONFIG_SENSORS_MP2888) += mp2888.o obj-$(CONFIG_SENSORS_MP2975) += mp2975.o obj-$(CONFIG_SENSORS_PM6764TR) += pm6764tr.o obj-$(CONFIG_SENSORS_PXE1610) += pxe1610.o diff --git a/drivers/hwmon/pmbus/mp2888.c b/drivers/hwmon/pmbus/mp2888.c new file mode 100644 index 000000000000..8ecd4adfef40 --- /dev/null +++ b/drivers/hwmon/pmbus/mp2888.c @@ -0,0 +1,408 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for MPS Multi-phase Digital VR Controllers + * + * Copyright (C) 2020 Nvidia Technologies Ltd. + */ + +#include +#include +#include +#include +#include +#include "pmbus.h" + +/* Vendor specific registers. */ +#define MP2888_MFR_SYS_CONFIG 0x44 +#define MP2888_MFR_READ_CS1_2 0x73 +#define MP2888_MFR_READ_CS3_4 0x74 +#define MP2888_MFR_READ_CS5_6 0x75 +#define MP2888_MFR_READ_CS7_8 0x76 +#define MP2888_MFR_READ_CS9_10 0x77 +#define MP2888_MFR_VR_CONFIG1 0xe1 + +#define MP2888_TOTAL_CURRENT_RESOLUTION BIT(3) +#define MP2888_PHASE_CURRENT_RESOLUTION BIT(4) +#define MP2888_DRMOS_KCS GENMASK(2, 0) +#define MP2888_TEMP_UNIT 10 +#define MP2888_MAX_PHASE 10 + +struct mp2888_data { + struct pmbus_driver_info info; + int total_curr_resolution; + int phase_curr_resolution; + int curr_sense_gain; +}; + +#define to_mp2888_data(x) container_of(x, struct mp2888_data, info) + +static int mp2888_read_byte_data(struct i2c_client *client, int page, int reg) +{ + switch (reg) { + case PMBUS_VOUT_MODE: + /* Enforce VOUT direct format. */ + return PB_VOUT_MODE_DIRECT; + default: + return -ENODATA; + } +} + +static int +mp2888_current_sense_gain_and_resolution_get(struct i2c_client *client, struct mp2888_data *data) +{ + int ret; + + /* + * Obtain DrMOS current sense gain of power stage from the register + * , bits 0-2. The value is selected as below: + * 00b - 5µA/A, 01b - 8.5µA/A, 10b - 9.7µA/A, 11b - 10µA/A. Other + * values are reserved. + */ + ret = i2c_smbus_read_word_data(client, MP2888_MFR_SYS_CONFIG); + if (ret < 0) + return ret; + + switch (ret & MP2888_DRMOS_KCS) { + case 0: + data->curr_sense_gain = 85; + break; + case 1: + data->curr_sense_gain = 97; + break; + case 2: + data->curr_sense_gain = 100; + break; + case 3: + data->curr_sense_gain = 50; + break; + default: + return -EINVAL; + } + + /* + * Obtain resolution selector for total and phase current report and protection. + * 0: original resolution; 1: half resolution (in such case phase current value should + * be doubled. + */ + data->total_curr_resolution = (ret & MP2888_TOTAL_CURRENT_RESOLUTION) >> 3; + data->phase_curr_resolution = (ret & MP2888_PHASE_CURRENT_RESOLUTION) >> 4; + + return 0; +} + +static int +mp2888_read_phase(struct i2c_client *client, struct mp2888_data *data, int page, int phase, u8 reg) +{ + int ret; + + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + + if (!((phase + 1) % 2)) + ret >>= 8; + ret &= 0xff; + + /* + * Output value is calculated as: (READ_CSx / 80 – 1.23) / (Kcs * Rcs) + * where: + * - Kcs is the DrMOS current sense gain of power stage, which is obtained from the + * register MP2888_MFR_VR_CONFIG1, bits 13-12 with the following selection of DrMOS + * (data->curr_sense_gain): + * 00b - 5µA/A, 01b - 8.5µA/A, 10b - 9.7µA/A, 11b - 10µA/A. + * - Rcs is the internal phase current sense resistor. This parameter depends on hardware + * assembly. By default it is set to 1kΩ. In case of different assembly, user should + * scale this parameter by dividing it by Rcs. + * If phase current resolution bit is set to 1, READ_CSx value should be doubled. + * Note, that current phase sensing, providing by the device is not accurate. This is + * because sampling of current occurrence of bit weight has a big deviation, especially for + * light load. + */ + ret = DIV_ROUND_CLOSEST(ret * 100 - 9800, data->curr_sense_gain); + ret = (data->phase_curr_resolution) ? ret * 2 : ret; + /* Scale according to total current resolution. */ + ret = (data->total_curr_resolution) ? ret * 8 : ret * 4; + return ret; +} + +static int +mp2888_read_phases(struct i2c_client *client, struct mp2888_data *data, int page, int phase) +{ + int ret; + + switch (phase) { + case 0 ... 1: + ret = mp2888_read_phase(client, data, page, phase, MP2888_MFR_READ_CS1_2); + break; + case 2 ... 3: + ret = mp2888_read_phase(client, data, page, phase, MP2888_MFR_READ_CS3_4); + break; + case 4 ... 5: + ret = mp2888_read_phase(client, data, page, phase, MP2888_MFR_READ_CS5_6); + break; + case 6 ... 7: + ret = mp2888_read_phase(client, data, page, phase, MP2888_MFR_READ_CS7_8); + break; + case 8 ... 9: + ret = mp2888_read_phase(client, data, page, phase, MP2888_MFR_READ_CS9_10); + break; + default: + return -ENODATA; + } + return ret; +} + +static int mp2888_read_word_data(struct i2c_client *client, int page, int phase, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct mp2888_data *data = to_mp2888_data(info); + int ret; + + switch (reg) { + case PMBUS_READ_VIN: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret <= 0) + return ret; + + /* + * READ_VIN requires fixup to scale it to linear11 format. Register data format + * provides 10 bits for mantissa and 6 bits for exponent. Bits 15:10 are set with + * the fixed value 111011b. + */ + ret = (ret & GENMASK(9, 0)) | ((ret & GENMASK(31, 10)) << 1); + break; + case PMBUS_OT_WARN_LIMIT: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + /* + * Chip reports limits in degrees C, but the actual temperature in 10th of + * degrees C - scaling is needed to match both. + */ + ret *= MP2888_TEMP_UNIT; + break; + case PMBUS_READ_IOUT: + if (phase != 0xff) + return mp2888_read_phases(client, data, page, phase); + + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + /* + * READ_IOUT register has unused bits 15:12 with fixed value 1110b. Clear these + * bits and scale with total current resolution. Data is provided in direct format. + */ + ret &= GENMASK(11, 0); + ret = data->total_curr_resolution ? ret * 2 : ret; + break; + case PMBUS_IOUT_OC_WARN_LIMIT: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + ret &= GENMASK(9, 0); + /* + * Chip reports limits with resolution 1A or 2A, if total current resolution bit is + * set 1. Actual current is reported with 0.25A or respectively 0.5A resolution. + * Scaling is needed to match both. + */ + ret = data->total_curr_resolution ? ret * 8 : ret * 4; + break; + case PMBUS_READ_POUT: + case PMBUS_READ_PIN: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + ret = data->total_curr_resolution ? ret * 2 : ret; + break; + case PMBUS_POUT_OP_WARN_LIMIT: + ret = pmbus_read_word_data(client, page, phase, reg); + if (ret < 0) + return ret; + /* + * Chip reports limits with resolution 1W or 2W, if total current resolution bit is + * set 1. Actual power is reported with 0.5W or 1W respectively resolution. Scaling + * is needed to match both. + */ + ret = data->total_curr_resolution ? ret * 4 : ret * 2; + break; + /* + * The below registers are not implemented by device or implemented not according to the + * spec. Skip all of them to avoid exposing non-relevant inputs to sysfs. + */ + case PMBUS_OT_FAULT_LIMIT: + case PMBUS_UT_WARN_LIMIT: + case PMBUS_UT_FAULT_LIMIT: + case PMBUS_VIN_UV_FAULT_LIMIT: + case PMBUS_VOUT_UV_WARN_LIMIT: + case PMBUS_VOUT_OV_WARN_LIMIT: + case PMBUS_VOUT_UV_FAULT_LIMIT: + case PMBUS_VOUT_OV_FAULT_LIMIT: + case PMBUS_VIN_OV_WARN_LIMIT: + case PMBUS_IOUT_OC_LV_FAULT_LIMIT: + case PMBUS_IOUT_OC_FAULT_LIMIT: + case PMBUS_POUT_MAX: + case PMBUS_IOUT_UC_FAULT_LIMIT: + case PMBUS_POUT_OP_FAULT_LIMIT: + case PMBUS_PIN_OP_WARN_LIMIT: + case PMBUS_MFR_VIN_MIN: + case PMBUS_MFR_VOUT_MIN: + case PMBUS_MFR_VIN_MAX: + case PMBUS_MFR_VOUT_MAX: + case PMBUS_MFR_IIN_MAX: + case PMBUS_MFR_IOUT_MAX: + case PMBUS_MFR_PIN_MAX: + case PMBUS_MFR_POUT_MAX: + case PMBUS_MFR_MAX_TEMP_1: + return -ENXIO; + default: + return -ENODATA; + } + + return ret; +} + +static int mp2888_write_word_data(struct i2c_client *client, int page, int reg, u16 word) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct mp2888_data *data = to_mp2888_data(info); + + switch (reg) { + case PMBUS_OT_WARN_LIMIT: + word = DIV_ROUND_CLOSEST(word, MP2888_TEMP_UNIT); + /* Drop unused bits 15:8. */ + word = clamp_val(word, 0, GENMASK(7, 0)); + break; + case PMBUS_IOUT_OC_WARN_LIMIT: + /* Fix limit according to total curent resolution. */ + word = data->total_curr_resolution ? DIV_ROUND_CLOSEST(word, 8) : + DIV_ROUND_CLOSEST(word, 4); + /* Drop unused bits 15:10. */ + word = clamp_val(word, 0, GENMASK(9, 0)); + break; + case PMBUS_POUT_OP_WARN_LIMIT: + /* Fix limit according to total curent resolution. */ + word = data->total_curr_resolution ? DIV_ROUND_CLOSEST(word, 4) : + DIV_ROUND_CLOSEST(word, 2); + /* Drop unused bits 15:10. */ + word = clamp_val(word, 0, GENMASK(9, 0)); + break; + default: + return -ENODATA; + } + return pmbus_write_word_data(client, page, reg, word); +} + +static int +mp2888_identify_multiphase(struct i2c_client *client, struct mp2888_data *data, + struct pmbus_driver_info *info) +{ + int ret; + + ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0); + if (ret < 0) + return ret; + + /* Identify multiphase number - could be from 1 to 10. */ + ret = i2c_smbus_read_word_data(client, MP2888_MFR_VR_CONFIG1); + if (ret <= 0) + return ret; + + info->phases[0] = ret & GENMASK(3, 0); + + /* + * The device provides a total of 10 PWM pins, and can be configured to different phase + * count applications for rail. + */ + if (info->phases[0] > MP2888_MAX_PHASE) + return -EINVAL; + + return 0; +} + +static struct pmbus_driver_info mp2888_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = direct, + .format[PSC_TEMPERATURE] = direct, + .format[PSC_CURRENT_IN] = linear, + .format[PSC_CURRENT_OUT] = direct, + .format[PSC_POWER] = direct, + .m[PSC_TEMPERATURE] = 1, + .R[PSC_TEMPERATURE] = 1, + .m[PSC_VOLTAGE_OUT] = 1, + .R[PSC_VOLTAGE_OUT] = 3, + .m[PSC_CURRENT_OUT] = 4, + .m[PSC_POWER] = 1, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_IOUT | + PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | + PMBUS_HAVE_POUT | PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT | + PMBUS_PHASE_VIRTUAL, + .pfunc[0] = PMBUS_HAVE_IOUT, + .pfunc[1] = PMBUS_HAVE_IOUT, + .pfunc[2] = PMBUS_HAVE_IOUT, + .pfunc[3] = PMBUS_HAVE_IOUT, + .pfunc[4] = PMBUS_HAVE_IOUT, + .pfunc[5] = PMBUS_HAVE_IOUT, + .pfunc[6] = PMBUS_HAVE_IOUT, + .pfunc[7] = PMBUS_HAVE_IOUT, + .pfunc[8] = PMBUS_HAVE_IOUT, + .pfunc[9] = PMBUS_HAVE_IOUT, + .read_byte_data = mp2888_read_byte_data, + .read_word_data = mp2888_read_word_data, + .write_word_data = mp2888_write_word_data, +}; + +static int mp2888_probe(struct i2c_client *client) +{ + struct pmbus_driver_info *info; + struct mp2888_data *data; + int ret; + + data = devm_kzalloc(&client->dev, sizeof(struct mp2888_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + memcpy(&data->info, &mp2888_info, sizeof(*info)); + info = &data->info; + + /* Identify multiphase configuration. */ + ret = mp2888_identify_multiphase(client, data, info); + if (ret) + return ret; + + /* Obtain current sense gain of power stage and current resolution. */ + ret = mp2888_current_sense_gain_and_resolution_get(client, data); + if (ret) + return ret; + + return pmbus_do_probe(client, info); +} + +static const struct i2c_device_id mp2888_id[] = { + {"mp2888", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, mp2888_id); + +static const struct of_device_id __maybe_unused mp2888_of_match[] = { + {.compatible = "mps,mp2888"}, + {} +}; +MODULE_DEVICE_TABLE(of, mp2888_of_match); + +static struct i2c_driver mp2888_driver = { + .driver = { + .name = "mp2888", + .of_match_table = of_match_ptr(mp2888_of_match), + }, + .probe_new = mp2888_probe, + .id_table = mp2888_id, +}; + +module_i2c_driver(mp2888_driver); + +MODULE_AUTHOR("Vadim Pasternak "); +MODULE_DESCRIPTION("PMBus driver for MPS MP2888 device"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); -- cgit v1.2.3 From 9abfb52b502889f1528316cf0b7d4116d40abebe Mon Sep 17 00:00:00 2001 From: Vadim Pasternak Date: Tue, 11 May 2021 08:56:19 +0300 Subject: dt-bindings: Add MP2888 voltage regulator device Monolithic Power Systems, Inc. (MPS) dual-loop, digital, multi-phase controller. Signed-off-by: Vadim Pasternak Acked-by: Rob Herring Link: https://lore.kernel.org/r/20210511055619.118104-4-vadimp@nvidia.com Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/trivial-devices.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/trivial-devices.yaml b/Documentation/devicetree/bindings/trivial-devices.yaml index 8341e9d23c1e..f8824e1dd24c 100644 --- a/Documentation/devicetree/bindings/trivial-devices.yaml +++ b/Documentation/devicetree/bindings/trivial-devices.yaml @@ -103,6 +103,8 @@ properties: - fsl,mpl3115 # MPR121: Proximity Capacitive Touch Sensor Controller - fsl,mpr121 + # Monolithic Power Systems Inc. multi-phase controller mp2888 + - mps,mp2888 # Monolithic Power Systems Inc. multi-phase controller mp2975 - mps,mp2975 # G751: Digital Temperature Sensor and Thermal Watchdog with Two-Wire Interface -- cgit v1.2.3 From 9da9c2dc57b2fa2e65521894cb66df4bf615214d Mon Sep 17 00:00:00 2001 From: Chu Lin Date: Wed, 12 May 2021 17:10:43 +0000 Subject: hwmon: (adm1275) enable adm1272 temperature reporting adm1272 supports temperature reporting but it is disabled by default. Tested: ls temp1_* temp1_crit temp1_highest temp1_max temp1_crit_alarm temp1_input temp1_max_alarm cat temp1_input 26642 Signed-off-by: Chu Lin Link: https://lore.kernel.org/r/20210512171043.2433694-1-linchuyuan@google.com [groeck: Updated subject to reflect correct driver] Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/adm1275.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c index 980a3850b2f3..d311e0557401 100644 --- a/drivers/hwmon/pmbus/adm1275.c +++ b/drivers/hwmon/pmbus/adm1275.c @@ -611,11 +611,13 @@ static int adm1275_probe(struct i2c_client *client) tindex = 8; info->func[0] |= PMBUS_HAVE_PIN | PMBUS_HAVE_STATUS_INPUT | - PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT; + PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; - /* Enable VOUT if not enabled (it is disabled by default) */ - if (!(config & ADM1278_VOUT_EN)) { - config |= ADM1278_VOUT_EN; + /* Enable VOUT & TEMP1 if not enabled (disabled by default) */ + if ((config & (ADM1278_VOUT_EN | ADM1278_TEMP1_EN)) != + (ADM1278_VOUT_EN | ADM1278_TEMP1_EN)) { + config |= ADM1278_VOUT_EN | ADM1278_TEMP1_EN; ret = i2c_smbus_write_byte_data(client, ADM1275_PMON_CONFIG, config); @@ -625,10 +627,6 @@ static int adm1275_probe(struct i2c_client *client) return -ENODEV; } } - - if (config & ADM1278_TEMP1_EN) - info->func[0] |= - PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; if (config & ADM1278_VIN_EN) info->func[0] |= PMBUS_HAVE_VIN; break; -- cgit v1.2.3 From f20f7363e7e1d24defc27b1cb814071791a535b0 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Fri, 21 May 2021 14:22:18 -0300 Subject: docs: hwmon: Add an entry for mp2888 The entry for mp2888 is missing and it causes the following 'make htmldocs' build warning: Documentation/hwmon/mp2888.rst: WARNING: document isn't included in any toctree Add the mp2888 entry. Signed-off-by: Fabio Estevam Link: https://lore.kernel.org/r/20210521172218.37592-1-festevam@gmail.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index 9ed60fa84cbe..6925a8a70511 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -137,6 +137,7 @@ Hardware Monitoring Kernel Drivers mcp3021 menf21bmc mlxreg-fan + mp2888 mp2975 nct6683 nct6775 -- cgit v1.2.3 From 505c2549373f3aa9ee16493f872e57876ffb70b1 Mon Sep 17 00:00:00 2001 From: Navin Sankar Velliangiri Date: Mon, 24 May 2021 19:50:38 +0530 Subject: hwmon: Add sht4x Temperature and Humidity Sensor Driver This patch adds a hwmon driver for the SHT4x Temperature and Humidity sensor. Signed-off-by: Navin Sankar Velliangiri [groeck: dropped unnecessary empty line and continuation lines] Signed-off-by: Guenter Roeck --- Documentation/hwmon/index.rst | 1 + Documentation/hwmon/sht4x.rst | 45 +++++++ drivers/hwmon/Kconfig | 11 ++ drivers/hwmon/Makefile | 1 + drivers/hwmon/sht4x.c | 301 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 359 insertions(+) create mode 100644 Documentation/hwmon/sht4x.rst create mode 100644 drivers/hwmon/sht4x.c diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index 6925a8a70511..61e5d4532622 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -165,6 +165,7 @@ Hardware Monitoring Kernel Drivers sht15 sht21 sht3x + sht4x shtc1 sis5595 sl28cpld diff --git a/Documentation/hwmon/sht4x.rst b/Documentation/hwmon/sht4x.rst new file mode 100644 index 000000000000..3b37abcd4a46 --- /dev/null +++ b/Documentation/hwmon/sht4x.rst @@ -0,0 +1,45 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Kernel driver sht4x +=================== + +Supported Chips: + + * Sensirion SHT4X + + Prefix: 'sht4x' + + Addresses scanned: None + + Datasheet: + + English: https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/2_Humidity_Sensors/Datasheets/Sensirion_Humidity_Sensors_SHT4x_Datasheet.pdf + +Author: Navin Sankar Velliangiri + + +Description +----------- + +This driver implements support for the Sensirion SHT4x chip, a humidity +and temperature sensor. Temperature is measured in degree celsius, relative +humidity is expressed as a percentage. In sysfs interface, all values are +scaled by 1000, i.e. the value for 31.5 degrees celsius is 31500. + +Usage Notes +----------- + +The device communicates with the I2C protocol. Sensors can have the I2C +address 0x44. See Documentation/i2c/instantiating-devices.rst for methods +to instantiate the device. + +Sysfs entries +------------- + +=============== ============================================ +temp1_input Measured temperature in millidegrees Celcius +humidity1_input Measured humidity in %H +update_interval The minimum interval for polling the sensor, + in milliseconds. Writable. Must be at least + 2000. +============== ============================================= diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 87624902ea80..e3675377bc5d 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1583,6 +1583,17 @@ config SENSORS_SHT3x This driver can also be built as a module. If so, the module will be called sht3x. +config SENSORS_SHT4x + tristate "Sensiron humidity and temperature sensors. SHT4x and compat." + depends on I2C + select CRC8 + help + If you say yes here you get support for the Sensiron SHT40, SHT41 and + SHT45 humidity and temperature sensors. + + This driver can also be built as a module. If so, the module + will be called sht4x. + config SENSORS_SHTC1 tristate "Sensiron humidity and temperature sensors. SHTC1 and compat." depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 59e78bc212cf..d712c61c1f5e 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -171,6 +171,7 @@ obj-$(CONFIG_SENSORS_SL28CPLD) += sl28cpld-hwmon.o obj-$(CONFIG_SENSORS_SHT15) += sht15.o obj-$(CONFIG_SENSORS_SHT21) += sht21.o obj-$(CONFIG_SENSORS_SHT3x) += sht3x.o +obj-$(CONFIG_SENSORS_SHT4x) += sht4x.o obj-$(CONFIG_SENSORS_SHTC1) += shtc1.o obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o obj-$(CONFIG_SENSORS_SMM665) += smm665.o diff --git a/drivers/hwmon/sht4x.c b/drivers/hwmon/sht4x.c new file mode 100644 index 000000000000..1dc51ee2a72b --- /dev/null +++ b/drivers/hwmon/sht4x.c @@ -0,0 +1,301 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* + * Copyright (c) Linumiz 2021 + * + * sht4x.c - Linux hwmon driver for SHT4x Temperature and Humidity sensor + * + * Author: Navin Sankar Velliangiri + */ + +#include +#include +#include +#include +#include +#include + +/* + * Poll intervals (in milliseconds) + */ +#define SHT4X_MIN_POLL_INTERVAL 2000 + +/* + * I2C command delays (in microseconds) + */ +#define SHT4X_MEAS_DELAY 1000 +#define SHT4X_DELAY_EXTRA 10000 + +/* + * Command Bytes + */ +#define SHT4X_CMD_MEASURE_HPM 0b11111101 +#define SHT4X_CMD_RESET 0b10010100 + +#define SHT4X_CMD_LEN 1 +#define SHT4X_CRC8_LEN 1 +#define SHT4X_WORD_LEN 2 +#define SHT4X_RESPONSE_LENGTH 6 +#define SHT4X_CRC8_POLYNOMIAL 0x31 +#define SHT4X_CRC8_INIT 0xff +#define SHT4X_MIN_TEMPERATURE -45000 +#define SHT4X_MAX_TEMPERATURE 125000 +#define SHT4X_MIN_HUMIDITY 0 +#define SHT4X_MAX_HUMIDITY 100000 + +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 + * @update_interval: the minimum poll interval + * @last_updated: the previous time that the SHT4X was polled + * @temperature: the latest temperature value received from the SHT4X + * @humidity: the latest humidity value received from the SHT4X + */ +struct sht4x_data { + struct i2c_client *client; + struct mutex lock; /* atomic read data updates */ + bool valid; /* validity of fields below */ + long update_interval; /* in milli-seconds */ + long last_updated; /* in jiffies */ + s32 temperature; + s32 humidity; +}; + +/** + * sht4x_read_values() - read and parse the raw data from the SHT4X + * @sht4x_data: the struct sht4x_data to use for the lock + * Return: 0 if succesfull, 1 if not + */ +static int sht4x_read_values(struct sht4x_data *data) +{ + int ret = 0; + u16 t_ticks, rh_ticks; + unsigned long next_update; + struct i2c_client *client = data->client; + u8 crc, raw_data[SHT4X_RESPONSE_LENGTH], + cmd[] = {SHT4X_CMD_MEASURE_HPM}; + + mutex_lock(&data->lock); + next_update = data->last_updated + + msecs_to_jiffies(data->update_interval); + if (!data->valid || time_after(jiffies, next_update)) { + ret = i2c_master_send(client, cmd, SHT4X_CMD_LEN); + if (ret < 0) + goto unlock; + + usleep_range(SHT4X_MEAS_DELAY, + SHT4X_MEAS_DELAY + SHT4X_DELAY_EXTRA); + + ret = i2c_master_recv(client, raw_data, SHT4X_RESPONSE_LENGTH); + if (ret != SHT4X_RESPONSE_LENGTH) { + if (ret >= 0) + ret = -ENODATA; + + goto unlock; + } + + t_ticks = raw_data[0] << 8 | raw_data[1]; + rh_ticks = raw_data[3] << 8 | raw_data[4]; + + crc = crc8(sht4x_crc8_table, &raw_data[0], SHT4X_WORD_LEN, CRC8_INIT_VALUE); + if (crc != raw_data[2]) { + dev_err(&client->dev, "data integrity check failed\n"); + ret = -EIO; + goto unlock; + } + + crc = crc8(sht4x_crc8_table, &raw_data[3], SHT4X_WORD_LEN, CRC8_INIT_VALUE); + if (crc != raw_data[5]) { + dev_err(&client->dev, "data integrity check failed\n"); + ret = -EIO; + goto unlock; + } + + data->temperature = ((21875 * (int32_t)t_ticks) >> 13) - 45000; + data->humidity = ((15625 * (int32_t)rh_ticks) >> 13) - 6000; + data->last_updated = jiffies; + data->valid = true; + } + +unlock: + mutex_unlock(&data->lock); + return ret; +} + +static ssize_t sht4x_interval_write(struct sht4x_data *data, long val) +{ + data->update_interval = clamp_val(val, SHT4X_MIN_POLL_INTERVAL, UINT_MAX); + + return 0; +} + +/** + * sht4x_interval_read() - read the minimum poll interval + * in milliseconds + */ +static size_t sht4x_interval_read(struct sht4x_data *data, long *val) +{ + *val = data->update_interval; + return 0; +} + +/** + * sht4x_temperature1_read() - read the temperature in millidegrees + */ +static int sht4x_temperature1_read(struct sht4x_data *data, long *val) +{ + int ret; + + ret = sht4x_read_values(data); + if (ret < 0) + return ret; + + *val = data->temperature; + + return 0; +} + +/** + * sht4x_humidity1_read() - read a relative humidity in millipercent + */ +static int sht4x_humidity1_read(struct sht4x_data *data, long *val) +{ + int ret; + + ret = sht4x_read_values(data); + if (ret < 0) + return ret; + + *val = data->humidity; + + return 0; +} + +static umode_t sht4x_hwmon_visible(const void *data, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_temp: + case hwmon_humidity: + return 0444; + case hwmon_chip: + return 0644; + default: + return 0; + } +} + +static int sht4x_hwmon_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct sht4x_data *data = dev_get_drvdata(dev); + + switch (type) { + case hwmon_temp: + return sht4x_temperature1_read(data, val); + case hwmon_humidity: + return sht4x_humidity1_read(data, val); + case hwmon_chip: + return sht4x_interval_read(data, val); + default: + return -EOPNOTSUPP; + } +} + +static int sht4x_hwmon_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct sht4x_data *data = dev_get_drvdata(dev); + + switch (type) { + case hwmon_chip: + return sht4x_interval_write(data, val); + default: + return -EOPNOTSUPP; + } +} + +static const struct hwmon_channel_info *sht4x_info[] = { + HWMON_CHANNEL_INFO(chip, HWMON_C_UPDATE_INTERVAL), + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), + HWMON_CHANNEL_INFO(humidity, HWMON_H_INPUT), + NULL, +}; + +static const struct hwmon_ops sht4x_hwmon_ops = { + .is_visible = sht4x_hwmon_visible, + .read = sht4x_hwmon_read, + .write = sht4x_hwmon_write, +}; + +static const struct hwmon_chip_info sht4x_chip_info = { + .ops = &sht4x_hwmon_ops, + .info = sht4x_info, +}; + +static int sht4x_probe(struct i2c_client *client, + const struct i2c_device_id *sht4x_id) +{ + struct device *device = &client->dev; + struct device *hwmon_dev; + struct sht4x_data *data; + u8 cmd[] = {SHT4X_CMD_RESET}; + int ret; + + /* + * we require full i2c support since the sht4x uses multi-byte read and + * writes as well as multi-byte commands which are not supported by + * the smbus protocol + */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -EOPNOTSUPP; + + data = devm_kzalloc(device, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->update_interval = SHT4X_MIN_POLL_INTERVAL; + data->client = client; + + mutex_init(&data->lock); + + crc8_populate_msb(sht4x_crc8_table, SHT4X_CRC8_POLYNOMIAL); + + ret = i2c_master_send(client, cmd, SHT4X_CMD_LEN); + if (ret < 0) + return ret; + if (ret != SHT4X_CMD_LEN) + return -EIO; + + hwmon_dev = devm_hwmon_device_register_with_info(device, + client->name, + data, + &sht4x_chip_info, + NULL); + + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct i2c_device_id sht4x_id[] = { + { "sht4x", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, sht4x_id); + +static struct i2c_driver sht4x_driver = { + .driver = { + .name = "sht4x", + }, + .probe = sht4x_probe, + .id_table = sht4x_id, +}; + +module_i2c_driver(sht4x_driver); + +MODULE_AUTHOR("Navin Sankar Velliangiri "); +MODULE_DESCRIPTION("Sensirion SHT4x humidity and temperature sensor driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 07c6621a37352e38b4ad9addaba473ad90fbfe5e Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Sat, 29 May 2021 02:13:52 -0700 Subject: hwmon: (sht4x) Fix sht4x_read_values return value Kernel doc for sht4x_read_values() shows 0 on success, 1 on failure but the return value on success is actually always positive as it is set to SHT4X_RESPONSE_LENGTH by a successful call to i2c_master_recv(). Miscellanea: o Update the kernel doc for sht4x_read_values to 0 for success or -ERRNO o Remove incorrectly used kernel doc /** header for other _read functions o Typo fix succesfull->successful o Reverse a test to unindent a block and use goto unlock o Declare cmd[SHT4X_CMD_LEN] rather than cmd[] At least for gcc 10.2, object size is reduced a tiny bit. $ size drivers/hwmon/sht4x.o* text data bss dec hex filename 1752 404 256 2412 96c drivers/hwmon/sht4x.o.new 1825 404 256 2485 9b5 drivers/hwmon/sht4x.o.old Signed-off-by: Joe Perches Link: https://lore.kernel.org/r/60eedce497137eb34448c0c77e01ec9d9c972ad7.camel@perches.com Reviewed by: Navin Sankar Velliangiri Signed-off-by: Guenter Roeck --- drivers/hwmon/sht4x.c | 95 ++++++++++++++++++++++++--------------------------- 1 file changed, 45 insertions(+), 50 deletions(-) diff --git a/drivers/hwmon/sht4x.c b/drivers/hwmon/sht4x.c index 1dc51ee2a72b..09c2a0b06444 100644 --- a/drivers/hwmon/sht4x.c +++ b/drivers/hwmon/sht4x.c @@ -67,7 +67,7 @@ struct sht4x_data { /** * sht4x_read_values() - read and parse the raw data from the SHT4X * @sht4x_data: the struct sht4x_data to use for the lock - * Return: 0 if succesfull, 1 if not + * Return: 0 if successful, -ERRNO if not */ static int sht4x_read_values(struct sht4x_data *data) { @@ -75,51 +75,53 @@ static int sht4x_read_values(struct sht4x_data *data) u16 t_ticks, rh_ticks; unsigned long next_update; struct i2c_client *client = data->client; - u8 crc, raw_data[SHT4X_RESPONSE_LENGTH], - cmd[] = {SHT4X_CMD_MEASURE_HPM}; + u8 crc; + u8 cmd[SHT4X_CMD_LEN] = {SHT4X_CMD_MEASURE_HPM}; + u8 raw_data[SHT4X_RESPONSE_LENGTH]; mutex_lock(&data->lock); next_update = data->last_updated + msecs_to_jiffies(data->update_interval); - if (!data->valid || time_after(jiffies, next_update)) { - ret = i2c_master_send(client, cmd, SHT4X_CMD_LEN); - if (ret < 0) - goto unlock; - - usleep_range(SHT4X_MEAS_DELAY, - SHT4X_MEAS_DELAY + SHT4X_DELAY_EXTRA); - - ret = i2c_master_recv(client, raw_data, SHT4X_RESPONSE_LENGTH); - if (ret != SHT4X_RESPONSE_LENGTH) { - if (ret >= 0) - ret = -ENODATA; - - goto unlock; - } - - t_ticks = raw_data[0] << 8 | raw_data[1]; - rh_ticks = raw_data[3] << 8 | raw_data[4]; - - crc = crc8(sht4x_crc8_table, &raw_data[0], SHT4X_WORD_LEN, CRC8_INIT_VALUE); - if (crc != raw_data[2]) { - dev_err(&client->dev, "data integrity check failed\n"); - ret = -EIO; - goto unlock; - } - - crc = crc8(sht4x_crc8_table, &raw_data[3], SHT4X_WORD_LEN, CRC8_INIT_VALUE); - if (crc != raw_data[5]) { - dev_err(&client->dev, "data integrity check failed\n"); - ret = -EIO; - goto unlock; - } - - data->temperature = ((21875 * (int32_t)t_ticks) >> 13) - 45000; - data->humidity = ((15625 * (int32_t)rh_ticks) >> 13) - 6000; - data->last_updated = jiffies; - data->valid = true; + + 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, SHT4X_MEAS_DELAY + SHT4X_DELAY_EXTRA); + + ret = i2c_master_recv(client, raw_data, SHT4X_RESPONSE_LENGTH); + if (ret != SHT4X_RESPONSE_LENGTH) { + if (ret >= 0) + ret = -ENODATA; + goto unlock; + } + + t_ticks = raw_data[0] << 8 | raw_data[1]; + rh_ticks = raw_data[3] << 8 | raw_data[4]; + + crc = crc8(sht4x_crc8_table, &raw_data[0], SHT4X_WORD_LEN, CRC8_INIT_VALUE); + if (crc != raw_data[2]) { + dev_err(&client->dev, "data integrity check failed\n"); + ret = -EIO; + goto unlock; } + crc = crc8(sht4x_crc8_table, &raw_data[3], SHT4X_WORD_LEN, CRC8_INIT_VALUE); + if (crc != raw_data[5]) { + dev_err(&client->dev, "data integrity check failed\n"); + ret = -EIO; + goto unlock; + } + + data->temperature = ((21875 * (int32_t)t_ticks) >> 13) - 45000; + data->humidity = ((15625 * (int32_t)rh_ticks) >> 13) - 6000; + data->last_updated = jiffies; + data->valid = true; + ret = 0; + unlock: mutex_unlock(&data->lock); return ret; @@ -132,19 +134,14 @@ static ssize_t sht4x_interval_write(struct sht4x_data *data, long val) return 0; } -/** - * sht4x_interval_read() - read the minimum poll interval - * in milliseconds - */ +/* sht4x_interval_read() - read the minimum poll interval in milliseconds */ static size_t sht4x_interval_read(struct sht4x_data *data, long *val) { *val = data->update_interval; return 0; } -/** - * sht4x_temperature1_read() - read the temperature in millidegrees - */ +/* sht4x_temperature1_read() - read the temperature in millidegrees */ static int sht4x_temperature1_read(struct sht4x_data *data, long *val) { int ret; @@ -158,9 +155,7 @@ static int sht4x_temperature1_read(struct sht4x_data *data, long *val) return 0; } -/** - * sht4x_humidity1_read() - read a relative humidity in millipercent - */ +/* sht4x_humidity1_read() - read a relative humidity in millipercent */ static int sht4x_humidity1_read(struct sht4x_data *data, long *val) { int ret; -- cgit v1.2.3 From cbbf244f0515af3472084f22b6213121b4a63835 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 26 May 2021 08:40:16 -0700 Subject: hwmon: (max31790) Fix fan speed reporting for fan7..12 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fans 7..12 do not have their own set of configuration registers. So far the code ignored that and read beyond the end of the configuration register range to get the tachometer period. This resulted in more or less random fan speed values for those fans. The datasheet is quite vague when it comes to defining the tachometer period for fans 7..12. Experiments confirm that the period is the same for both fans associated with a given set of configuration registers. Fixes: 54187ff9d766 ("hwmon: (max31790) Convert to use new hwmon registration API") Fixes: 195a4b4298a7 ("hwmon: Driver for Maxim MAX31790") Cc: Jan Kundrát Reviewed-by: Jan Kundrát Cc: Václav Kubernát Reviewed-by: Jan Kundrát Signed-off-by: Guenter Roeck Link: https://lore.kernel.org/r/20210526154022.3223012-2-linux@roeck-us.net --- drivers/hwmon/max31790.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/max31790.c b/drivers/hwmon/max31790.c index 86e6c71db685..f6d4fc0a2f13 100644 --- a/drivers/hwmon/max31790.c +++ b/drivers/hwmon/max31790.c @@ -170,7 +170,7 @@ static int max31790_read_fan(struct device *dev, u32 attr, int channel, switch (attr) { case hwmon_fan_input: - sr = get_tach_period(data->fan_dynamics[channel]); + sr = get_tach_period(data->fan_dynamics[channel % NR_CHANNEL]); rpm = RPM_FROM_REG(data->tach[channel], sr); *val = rpm; return 0; -- cgit v1.2.3 From 897f6339893b741a5d68ae8e2475df65946041c2 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 26 May 2021 08:40:17 -0700 Subject: hwmon: (max31790) Report correct current pwm duty cycles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MAX31790 has two sets of registers for pwm duty cycles, one to request a duty cycle and one to read the actual current duty cycle. Both do not have to be the same. When reporting the pwm duty cycle to the user, the actual pwm duty cycle from pwm duty cycle registers needs to be reported. When setting it, the pwm target duty cycle needs to be written. Since we don't know the actual pwm duty cycle after a target pwm duty cycle has been written, set the valid flag to false to indicate that actual pwm duty cycle should be read from the chip instead of using cached values. Cc: Jan Kundrát Cc: Václav Kubernát Signed-off-by: Guenter Roeck Tested-by: Václav Kubernát Reviewed-by: Jan Kundrát Link: https://lore.kernel.org/r/20210526154022.3223012-3-linux@roeck-us.net --- Documentation/hwmon/max31790.rst | 3 ++- drivers/hwmon/max31790.c | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Documentation/hwmon/max31790.rst b/Documentation/hwmon/max31790.rst index f301385d8cef..54ff0f49e28f 100644 --- a/Documentation/hwmon/max31790.rst +++ b/Documentation/hwmon/max31790.rst @@ -39,5 +39,6 @@ fan[1-12]_input RO fan tachometer speed in RPM fan[1-12]_fault RO fan experienced fault fan[1-6]_target RW desired fan speed in RPM pwm[1-6]_enable RW regulator mode, 0=disabled, 1=manual mode, 2=rpm mode -pwm[1-6] RW fan target duty cycle (0-255) +pwm[1-6] RW read: current pwm duty cycle, + write: target pwm duty cycle (0-255) ================== === ======================================================= diff --git a/drivers/hwmon/max31790.c b/drivers/hwmon/max31790.c index f6d4fc0a2f13..693497e09ac0 100644 --- a/drivers/hwmon/max31790.c +++ b/drivers/hwmon/max31790.c @@ -104,7 +104,7 @@ static struct max31790_data *max31790_update_device(struct device *dev) data->tach[NR_CHANNEL + i] = rv; } else { rv = i2c_smbus_read_word_swapped(client, - MAX31790_REG_PWMOUT(i)); + MAX31790_REG_PWM_DUTY_CYCLE(i)); if (rv < 0) goto abort; data->pwm[i] = rv; @@ -299,10 +299,10 @@ static int max31790_write_pwm(struct device *dev, u32 attr, int channel, err = -EINVAL; break; } - data->pwm[channel] = val << 8; + data->valid = false; err = i2c_smbus_write_word_swapped(client, MAX31790_REG_PWMOUT(channel), - data->pwm[channel]); + val << 8); break; case hwmon_pwm_enable: fan_config = data->fan_config[channel]; -- cgit v1.2.3 From 148c847c9e5a54b99850617bf9c143af9a344f92 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 26 May 2021 08:40:18 -0700 Subject: hwmon: (max31790) Fix pwmX_enable attributes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pwmX_enable supports three possible values: 0: Fan control disabled. Duty cycle is fixed to 0% 1: Fan control enabled, pwm mode. Duty cycle is determined by values written into Target Duty Cycle registers. 2: Fan control enabled, rpm mode Duty cycle is adjusted such that fan speed matches the values in Target Count registers The current code does not do this; instead, it mixes pwm control configuration with fan speed monitoring configuration. Worse, it reports that pwm control would be disabled (pwmX_enable==0) when it is in fact enabled in pwm mode. Part of the problem may be that the chip sets the "TACH input enable" bit on its own whenever the mode bit is set to RPM mode, but that doesn't mean that "TACH input enable" accurately reflects the pwm mode. Fix it up and only handle pwm control with the pwmX_enable attributes. In the documentation, clarify that disabling pwm control (pwmX_enable=0) sets the pwm duty cycle to 0%. In the code, explain why TACH_INPUT_EN is set together with RPM_MODE. While at it, only update the configuration register if the configuration has changed, and only update the cached configuration if updating the chip configuration was successful. Cc: Jan Kundrát Cc: Václav Kubernát Signed-off-by: Guenter Roeck Tested-by: Václav Kubernát Reviewed-by: Jan Kundrát Link: https://lore.kernel.org/r/20210526154022.3223012-4-linux@roeck-us.net --- Documentation/hwmon/max31790.rst | 2 +- drivers/hwmon/max31790.c | 41 +++++++++++++++++++++++++--------------- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/Documentation/hwmon/max31790.rst b/Documentation/hwmon/max31790.rst index 54ff0f49e28f..7b097c3b9b90 100644 --- a/Documentation/hwmon/max31790.rst +++ b/Documentation/hwmon/max31790.rst @@ -38,7 +38,7 @@ Sysfs entries fan[1-12]_input RO fan tachometer speed in RPM fan[1-12]_fault RO fan experienced fault fan[1-6]_target RW desired fan speed in RPM -pwm[1-6]_enable RW regulator mode, 0=disabled, 1=manual mode, 2=rpm mode +pwm[1-6]_enable RW regulator mode, 0=disabled (duty cycle=0%), 1=manual mode, 2=rpm mode pwm[1-6] RW read: current pwm duty cycle, write: target pwm duty cycle (0-255) ================== === ======================================================= diff --git a/drivers/hwmon/max31790.c b/drivers/hwmon/max31790.c index 693497e09ac0..67677c437768 100644 --- a/drivers/hwmon/max31790.c +++ b/drivers/hwmon/max31790.c @@ -27,6 +27,7 @@ /* Fan Config register bits */ #define MAX31790_FAN_CFG_RPM_MODE 0x80 +#define MAX31790_FAN_CFG_CTRL_MON 0x10 #define MAX31790_FAN_CFG_TACH_INPUT_EN 0x08 #define MAX31790_FAN_CFG_TACH_INPUT 0x01 @@ -271,12 +272,12 @@ static int max31790_read_pwm(struct device *dev, u32 attr, int channel, *val = data->pwm[channel] >> 8; return 0; case hwmon_pwm_enable: - if (fan_config & MAX31790_FAN_CFG_RPM_MODE) + if (fan_config & MAX31790_FAN_CFG_CTRL_MON) + *val = 0; + else if (fan_config & MAX31790_FAN_CFG_RPM_MODE) *val = 2; - else if (fan_config & MAX31790_FAN_CFG_TACH_INPUT_EN) - *val = 1; else - *val = 0; + *val = 1; return 0; default: return -EOPNOTSUPP; @@ -307,23 +308,33 @@ static int max31790_write_pwm(struct device *dev, u32 attr, int channel, case hwmon_pwm_enable: fan_config = data->fan_config[channel]; if (val == 0) { - fan_config &= ~(MAX31790_FAN_CFG_TACH_INPUT_EN | - MAX31790_FAN_CFG_RPM_MODE); + fan_config |= MAX31790_FAN_CFG_CTRL_MON; + /* + * Disable RPM mode; otherwise disabling fan speed + * monitoring is not possible. + */ + fan_config &= ~MAX31790_FAN_CFG_RPM_MODE; } else if (val == 1) { - fan_config = (fan_config | - MAX31790_FAN_CFG_TACH_INPUT_EN) & - ~MAX31790_FAN_CFG_RPM_MODE; + fan_config &= ~(MAX31790_FAN_CFG_CTRL_MON | MAX31790_FAN_CFG_RPM_MODE); } else if (val == 2) { - fan_config |= MAX31790_FAN_CFG_TACH_INPUT_EN | - MAX31790_FAN_CFG_RPM_MODE; + fan_config &= ~MAX31790_FAN_CFG_CTRL_MON; + /* + * The chip sets MAX31790_FAN_CFG_TACH_INPUT_EN on its + * own if MAX31790_FAN_CFG_RPM_MODE is set. + * Do it here as well to reflect the actual register + * value in the cache. + */ + fan_config |= (MAX31790_FAN_CFG_RPM_MODE | MAX31790_FAN_CFG_TACH_INPUT_EN); } else { err = -EINVAL; break; } - data->fan_config[channel] = fan_config; - err = i2c_smbus_write_byte_data(client, - MAX31790_REG_FAN_CONFIG(channel), - fan_config); + if (fan_config != data->fan_config[channel]) { + err = i2c_smbus_write_byte_data(client, MAX31790_REG_FAN_CONFIG(channel), + fan_config); + if (!err) + data->fan_config[channel] = fan_config; + } break; default: err = -EOPNOTSUPP; -- cgit v1.2.3 From 2013607b85f03ff24a5a19933705905a1b324a31 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 26 May 2021 08:40:20 -0700 Subject: hwmon: (max31790) Clear fan fault after reporting it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fault bits in MAX31790 are sticky and have to be cleared explicitly. A write operation into either the 'Target Duty Cycle' register or the 'Target Count' register is necessary to clear a fault. At the same time, we can never clear cached fault status values before reading them because the companion fault status for any given fan is cleared as well when clearing a fault. Cc: Jan Kundrát Cc: Václav Kubernát Signed-off-by: Guenter Roeck Tested-by: Václav Kubernát Link: https://lore.kernel.org/r/20210526154022.3223012-6-linux@roeck-us.net --- drivers/hwmon/max31790.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/max31790.c b/drivers/hwmon/max31790.c index 67677c437768..7927468c5271 100644 --- a/drivers/hwmon/max31790.c +++ b/drivers/hwmon/max31790.c @@ -80,7 +80,7 @@ static struct max31790_data *max31790_update_device(struct device *dev) MAX31790_REG_FAN_FAULT_STATUS1); if (rv < 0) goto abort; - data->fault_status = rv & 0x3F; + data->fault_status |= rv & 0x3F; rv = i2c_smbus_read_byte_data(client, MAX31790_REG_FAN_FAULT_STATUS2); @@ -181,7 +181,21 @@ static int max31790_read_fan(struct device *dev, u32 attr, int channel, *val = rpm; return 0; case hwmon_fan_fault: + mutex_lock(&data->update_lock); *val = !!(data->fault_status & (1 << channel)); + data->fault_status &= ~(1 << channel); + /* + * If a fault bit is set, we need to write into one of the fan + * configuration registers to clear it. Note that this also + * clears the fault for the companion channel if enabled. + */ + if (*val) { + int reg = MAX31790_REG_TARGET_COUNT(channel % NR_CHANNEL); + + i2c_smbus_write_byte_data(data->client, reg, + data->target_count[channel % NR_CHANNEL] >> 8); + } + mutex_unlock(&data->update_lock); return 0; default: return -EOPNOTSUPP; -- cgit v1.2.3 From 1814c4e84de2a89d1c2e1e9bbd241240561075a4 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 26 May 2021 08:40:21 -0700 Subject: hwmon: (max31790) Detect and report zero fan speed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If a fan is not running or not connected, of if fan monitoring is disabled, the fan count register returns a fixed value of 0xffe0. So far this is then translated to a RPM value larger than 0. Since this is misleading and does not really make much sense, report a fan RPM of 0 in this situation. Cc: Jan Kundrát Cc: Václav Kubernát Signed-off-by: Guenter Roeck Tested-by: Václav Kubernát Link: https://lore.kernel.org/r/20210526154022.3223012-7-linux@roeck-us.net --- drivers/hwmon/max31790.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/max31790.c b/drivers/hwmon/max31790.c index 7927468c5271..7e9362f6dc29 100644 --- a/drivers/hwmon/max31790.c +++ b/drivers/hwmon/max31790.c @@ -40,6 +40,8 @@ #define FAN_RPM_MIN 120 #define FAN_RPM_MAX 7864320 +#define FAN_COUNT_REG_MAX 0xffe0 + #define RPM_FROM_REG(reg, sr) (((reg) >> 4) ? \ ((60 * (sr) * 8192) / ((reg) >> 4)) : \ FAN_RPM_MAX) @@ -172,7 +174,10 @@ static int max31790_read_fan(struct device *dev, u32 attr, int channel, switch (attr) { case hwmon_fan_input: sr = get_tach_period(data->fan_dynamics[channel % NR_CHANNEL]); - rpm = RPM_FROM_REG(data->tach[channel], sr); + if (data->tach[channel] == FAN_COUNT_REG_MAX) + rpm = 0; + else + rpm = RPM_FROM_REG(data->tach[channel], sr); *val = rpm; return 0; case hwmon_fan_target: -- cgit v1.2.3 From 6b6af85410cf2db95d39ad9aa1d812a35eb1651e Mon Sep 17 00:00:00 2001 From: Ninad Malwade Date: Fri, 4 Jun 2021 14:54:43 +0800 Subject: hwmon: (ina3221) use CVRF only for single-shot conversion As per current logic the wait time per conversion is arouns 430ms for 512 samples and around 860ms for 1024 samples for 3 channels considering 140us as the bus voltage and shunt voltage sampling conversion time. This waiting time is a lot for the continuous mode and even for the single shot mode. For continuous mode when moving average is considered the waiting for CVRF bit is not required and the data from the previous conversion is sufficuent. As mentioned in the datasheet the conversion ready bit is provided to help coordinate single-shot conversions, we can restrict the use to single-shot mode only. Also, the conversion time is for the averaged samples, the wait time for the polling can omit the number of samples consideration. Signed-off-by: Ninad Malwade Link: https://lore.kernel.org/r/1622789683-30931-1-git-send-email-nmalwade@nvidia.com Signed-off-by: Guenter Roeck --- drivers/hwmon/ina3221.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/hwmon/ina3221.c b/drivers/hwmon/ina3221.c index c602583d19f3..58d3828e2ec0 100644 --- a/drivers/hwmon/ina3221.c +++ b/drivers/hwmon/ina3221.c @@ -196,13 +196,11 @@ static inline u32 ina3221_reg_to_interval_us(u16 config) u32 channels = hweight16(config & INA3221_CONFIG_CHs_EN_MASK); u32 vbus_ct_idx = INA3221_CONFIG_VBUS_CT(config); u32 vsh_ct_idx = INA3221_CONFIG_VSH_CT(config); - u32 samples_idx = INA3221_CONFIG_AVG(config); - u32 samples = ina3221_avg_samples[samples_idx]; u32 vbus_ct = ina3221_conv_time[vbus_ct_idx]; u32 vsh_ct = ina3221_conv_time[vsh_ct_idx]; /* Calculate total conversion time */ - return channels * (vbus_ct + vsh_ct) * samples; + return channels * (vbus_ct + vsh_ct); } static inline int ina3221_wait_for_data(struct ina3221_data *ina) @@ -288,13 +286,14 @@ static int ina3221_read_in(struct device *dev, u32 attr, int channel, long *val) return -ENODATA; /* Write CONFIG register to trigger a single-shot measurement */ - if (ina->single_shot) + if (ina->single_shot) { regmap_write(ina->regmap, INA3221_CONFIG, ina->reg_config); - ret = ina3221_wait_for_data(ina); - if (ret) - return ret; + ret = ina3221_wait_for_data(ina); + if (ret) + return ret; + } ret = ina3221_read_value(ina, reg, ®val); if (ret) @@ -344,13 +343,14 @@ static int ina3221_read_curr(struct device *dev, u32 attr, return -ENODATA; /* Write CONFIG register to trigger a single-shot measurement */ - if (ina->single_shot) + if (ina->single_shot) { regmap_write(ina->regmap, INA3221_CONFIG, ina->reg_config); - ret = ina3221_wait_for_data(ina); - if (ret) - return ret; + ret = ina3221_wait_for_data(ina); + if (ret) + return ret; + } fallthrough; case hwmon_curr_crit: -- cgit v1.2.3 From 4e5418f787ec56d7fe3c6efee486b8f508c58baf Mon Sep 17 00:00:00 2001 From: Madhava Reddy Siddareddygari Date: Sat, 5 Jun 2021 07:27:02 +0200 Subject: hwmon: (pmbus_core) Check adapter PEC support Currently, for Packet Error Checking (PEC) only the controller is checked for support. This causes problems on the cisco-8000 platform where a SMBUS transaction errors are observed. This is because PEC has to be enabled only if both controller and adapter support it. Added code to check PEC capability for adapter and enable it only if both controller and adapter supports PEC. Signed-off-by: Madhava Reddy Siddareddygari [Upstream from SONiC https://github.com/Azure/sonic-linux-kernel/pull/215] Signed-off-by: Paul Menzel Link: https://lore.kernel.org/r/20210605052700.541455-1-pmenzel@molgen.mpg.de [groeck: Dropped unnecessary continuation line] Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus_core.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 1f7fa5337974..01a1ffc74bb6 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -2216,11 +2216,14 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, data->has_status_word = true; } - /* Enable PEC if the controller supports it */ + /* Enable PEC if the controller and bus supports it */ if (!(data->flags & PMBUS_NO_CAPABILITY)) { ret = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY); - if (ret >= 0 && (ret & PB_CAPABILITY_ERROR_CHECK)) - client->flags |= I2C_CLIENT_PEC; + if (ret >= 0 && (ret & PB_CAPABILITY_ERROR_CHECK)) { + if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_PEC)) { + client->flags |= I2C_CLIENT_PEC; + } + } } /* -- cgit v1.2.3 From ff53b77e1e1bc9fd21e087e37a8444e8559d8d36 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sat, 5 Jun 2021 15:18:21 +0200 Subject: docs: hwmon: adm1177.rst: avoid using ReSt :doc:`foo` markup The :doc:`foo` tag is auto-generated via automarkup.py. So, use the filename at the sources, instead of :doc:`foo`. Signed-off-by: Mauro Carvalho Chehab Link: https://lore.kernel.org/r/32b0db7e79a3ed0e817213113c607a1b819e3867.1622898327.git.mchehab+huawei@kernel.org Signed-off-by: Guenter Roeck --- Documentation/hwmon/adm1177.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/hwmon/adm1177.rst b/Documentation/hwmon/adm1177.rst index 471be1e98d6f..1c85a2af92bf 100644 --- a/Documentation/hwmon/adm1177.rst +++ b/Documentation/hwmon/adm1177.rst @@ -20,7 +20,8 @@ Usage Notes ----------- This driver does not auto-detect devices. You will have to instantiate the -devices explicitly. Please see :doc:`/i2c/instantiating-devices` for details. +devices explicitly. Please see Documentation/i2c/instantiating-devices.rst +for details. Sysfs entries -- cgit v1.2.3 From dbc0860f7a3d43604c380822a456d26ef6f70a06 Mon Sep 17 00:00:00 2001 From: Erik Rosen Date: Wed, 9 Jun 2021 11:32:05 +0200 Subject: hwmon: (pmbus) Add new pmbus flag 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. Signed-off-by: Erik Rosen Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus_core.c | 8 +++++--- include/linux/pmbus.h | 9 +++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 01a1ffc74bb6..a5367df1cee8 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -2231,9 +2231,11 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, * faults, and we should not try it. Also, in that case, writes into * limit registers need to be disabled. */ - ret = i2c_smbus_read_byte_data(client, PMBUS_WRITE_PROTECT); - if (ret > 0 && (ret & PB_WP_ANY)) - data->flags |= PMBUS_WRITE_PROTECTED | PMBUS_SKIP_STATUS_CHECK; + if (!(data->flags & PMBUS_NO_WRITE_PROTECT)) { + ret = i2c_smbus_read_byte_data(client, PMBUS_WRITE_PROTECT); + if (ret > 0 && (ret & PB_WP_ANY)) + data->flags |= PMBUS_WRITE_PROTECTED | PMBUS_SKIP_STATUS_CHECK; + } if (data->info->pages) pmbus_clear_faults(client); diff --git a/include/linux/pmbus.h b/include/linux/pmbus.h index edd7c84fef65..12c515a27d3a 100644 --- a/include/linux/pmbus.h +++ b/include/linux/pmbus.h @@ -56,6 +56,15 @@ */ #define PMBUS_READ_STATUS_AFTER_FAILED_CHECK BIT(3) +/* + * 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. + */ +#define PMBUS_NO_WRITE_PROTECT BIT(4) + struct pmbus_platform_data { u32 flags; /* Device specific flags */ -- cgit v1.2.3 From e8e00c83a268d5b7d2f5bd490c2269c1ede76a07 Mon Sep 17 00:00:00 2001 From: Erik Rosen Date: Wed, 9 Jun 2021 11:32:06 +0200 Subject: hwmon: (pmbus) Add support for reading direct mode coefficients Add support for reading and decoding direct format coefficients to the PMBus core driver. If the new flag PMBUS_USE_COEFFICIENTS_CMD is set, the driver will use the COEFFICIENTS register together with the information in the pmbus_sensor_attr structs to initialize relevant coefficients for the direct mode format. Signed-off-by: Erik Rosen [groeck: Initialize ret with -EINVAL in pmbus_init_coefficients()] Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus_core.c | 116 +++++++++++++++++++++++++++++++++++++++ include/linux/pmbus.h | 8 +++ 2 files changed, 124 insertions(+) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index a5367df1cee8..c4f557c8955b 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -2141,6 +2141,111 @@ static int pmbus_find_attributes(struct i2c_client *client, return ret; } +/* + * The pmbus_class_attr_map structure maps one sensor class to + * it's corresponding sensor attributes array. + */ +struct pmbus_class_attr_map { + enum pmbus_sensor_classes class; + int nattr; + const struct pmbus_sensor_attr *attr; +}; + +static const struct pmbus_class_attr_map class_attr_map[] = { + { + .class = PSC_VOLTAGE_IN, + .attr = voltage_attributes, + .nattr = ARRAY_SIZE(voltage_attributes), + }, { + .class = PSC_VOLTAGE_OUT, + .attr = voltage_attributes, + .nattr = ARRAY_SIZE(voltage_attributes), + }, { + .class = PSC_CURRENT_IN, + .attr = current_attributes, + .nattr = ARRAY_SIZE(current_attributes), + }, { + .class = PSC_CURRENT_OUT, + .attr = current_attributes, + .nattr = ARRAY_SIZE(current_attributes), + }, { + .class = PSC_POWER, + .attr = power_attributes, + .nattr = ARRAY_SIZE(power_attributes), + }, { + .class = PSC_TEMPERATURE, + .attr = temp_attributes, + .nattr = ARRAY_SIZE(temp_attributes), + } +}; + +/* + * Read the coefficients for direct mode. + */ +static int pmbus_read_coefficients(struct i2c_client *client, + struct pmbus_driver_info *info, + const struct pmbus_sensor_attr *attr) +{ + int rv; + union i2c_smbus_data data; + enum pmbus_sensor_classes class = attr->class; + s8 R; + s16 m, b; + + data.block[0] = 2; + data.block[1] = attr->reg; + data.block[2] = 0x01; + + rv = i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_WRITE, PMBUS_COEFFICIENTS, + I2C_SMBUS_BLOCK_PROC_CALL, &data); + + if (rv < 0) + return rv; + + if (data.block[0] != 5) + return -EIO; + + m = data.block[1] | (data.block[2] << 8); + b = data.block[3] | (data.block[4] << 8); + R = data.block[5]; + info->m[class] = m; + info->b[class] = b; + info->R[class] = R; + + return rv; +} + +static int pmbus_init_coefficients(struct i2c_client *client, + struct pmbus_driver_info *info) +{ + int i, n, ret = -EINVAL; + const struct pmbus_class_attr_map *map; + const struct pmbus_sensor_attr *attr; + + for (i = 0; i < ARRAY_SIZE(class_attr_map); i++) { + map = &class_attr_map[i]; + if (info->format[map->class] != direct) + continue; + for (n = 0; n < map->nattr; n++) { + attr = &map->attr[n]; + if (map->class != attr->class) + continue; + ret = pmbus_read_coefficients(client, info, attr); + if (ret >= 0) + break; + } + if (ret < 0) { + dev_err(&client->dev, + "No coefficients found for sensor class %d\n", + map->class); + return -EINVAL; + } + } + + return 0; +} + /* * Identify chip parameters. * This function is called for all chips. @@ -2262,6 +2367,17 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, return ret; } } + + if (data->flags & PMBUS_USE_COEFFICIENTS_CMD) { + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BLOCK_PROC_CALL)) + return -ENODEV; + + ret = pmbus_init_coefficients(client, info); + if (ret < 0) + return ret; + } + return 0; } diff --git a/include/linux/pmbus.h b/include/linux/pmbus.h index 12c515a27d3a..fa9f08164c36 100644 --- a/include/linux/pmbus.h +++ b/include/linux/pmbus.h @@ -65,6 +65,14 @@ */ #define PMBUS_NO_WRITE_PROTECT BIT(4) +/* + * 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. + */ +#define PMBUS_USE_COEFFICIENTS_CMD BIT(5) + struct pmbus_platform_data { u32 flags; /* Device specific flags */ -- cgit v1.2.3 From 5e86f128d9eb44b19e311e5a1e50452344fd5628 Mon Sep 17 00:00:00 2001 From: Erik Rosen Date: Wed, 9 Jun 2021 11:32:07 +0200 Subject: hwmon: (pmbus) Allow phase function even if it's not on page Allow the use of a phase function even if it does not exist on the associated page. Signed-off-by: Erik Rosen Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus_core.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index c4f557c8955b..776ee2237be2 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -1329,14 +1329,14 @@ static int pmbus_add_sensor_attrs(struct i2c_client *client, pages = paged ? info->pages : 1; for (page = 0; page < pages; page++) { - if (!(info->func[page] & attrs->func)) - continue; - ret = pmbus_add_sensor_attrs_one(client, data, info, - name, index, page, - 0xff, attrs, paged); - if (ret) - return ret; - index++; + if (info->func[page] & attrs->func) { + ret = pmbus_add_sensor_attrs_one(client, data, info, + name, index, page, + 0xff, attrs, paged); + if (ret) + return ret; + index++; + } if (info->phases[page]) { int phase; -- cgit v1.2.3 From 317f9d808a7a0dad28eba10d96527f536ff28347 Mon Sep 17 00:00:00 2001 From: Erik Rosen Date: Wed, 9 Jun 2021 11:32:08 +0200 Subject: hwmon: (pmbus/pim4328) Add PMBus driver for PIM4006, PIM4328 and PIM4820 Add hardware monitoring support for Flex power interface modules PIM4006, PIM4328 and PIM4820. Signed-off-by: Erik Rosen Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/Kconfig | 9 ++ drivers/hwmon/pmbus/Makefile | 1 + drivers/hwmon/pmbus/pim4328.c | 233 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 243 insertions(+) create mode 100644 drivers/hwmon/pmbus/pim4328.c diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 52d8cd63603e..10ef548f74a4 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -267,6 +267,15 @@ config SENSORS_MP2975 This driver can also be built as a module. If so, the module will be called mp2975. +config SENSORS_PIM4328 + tristate "Flex PIM4328 and compatibles" + help + If you say yes here you get hardware monitoring support for Flex + PIM4328, PIM4820 and PIM4006 Power Interface Modules. + + This driver can also be built as a module. If so, the module will + be called pim4328. + config SENSORS_PM6764TR tristate "ST PM6764TR" help diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile index 35d293bb44bf..b3354518f66f 100644 --- a/drivers/hwmon/pmbus/Makefile +++ b/drivers/hwmon/pmbus/Makefile @@ -40,3 +40,4 @@ obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o obj-$(CONFIG_SENSORS_XDPE122) += xdpe12284.o obj-$(CONFIG_SENSORS_ZL6100) += zl6100.o +obj-$(CONFIG_SENSORS_PIM4328) += pim4328.o diff --git a/drivers/hwmon/pmbus/pim4328.c b/drivers/hwmon/pmbus/pim4328.c new file mode 100644 index 000000000000..273ff6e57654 --- /dev/null +++ b/drivers/hwmon/pmbus/pim4328.c @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Hardware monitoring driver for PIM4006, PIM4328 and PIM4820 + * + * Copyright (c) 2021 Flextronics International Sweden AB + */ + +#include +#include +#include +#include +#include +#include +#include +#include "pmbus.h" + +enum chips { pim4006, pim4328, pim4820 }; + +struct pim4328_data { + enum chips id; + struct pmbus_driver_info info; +}; + +#define to_pim4328_data(x) container_of(x, struct pim4328_data, info) + +/* PIM4006 and PIM4328 */ +#define PIM4328_MFR_READ_VINA 0xd3 +#define PIM4328_MFR_READ_VINB 0xd4 + +/* PIM4006 */ +#define PIM4328_MFR_READ_IINA 0xd6 +#define PIM4328_MFR_READ_IINB 0xd7 +#define PIM4328_MFR_FET_CHECKSTATUS 0xd9 + +/* PIM4328 */ +#define PIM4328_MFR_STATUS_BITS 0xd5 + +/* PIM4820 */ +#define PIM4328_MFR_READ_STATUS 0xd0 + +static const struct i2c_device_id pim4328_id[] = { + {"bmr455", pim4328}, + {"pim4006", pim4006}, + {"pim4106", pim4006}, + {"pim4206", pim4006}, + {"pim4306", pim4006}, + {"pim4328", pim4328}, + {"pim4406", pim4006}, + {"pim4820", pim4820}, + {} +}; +MODULE_DEVICE_TABLE(i2c, pim4328_id); + +static int pim4328_read_word_data(struct i2c_client *client, int page, + int phase, int reg) +{ + int ret; + + if (page > 0) + return -ENXIO; + + if (phase == 0xff) + return -ENODATA; + + switch (reg) { + case PMBUS_READ_VIN: + ret = pmbus_read_word_data(client, page, phase, + phase == 0 ? PIM4328_MFR_READ_VINA + : PIM4328_MFR_READ_VINB); + break; + case PMBUS_READ_IIN: + ret = pmbus_read_word_data(client, page, phase, + phase == 0 ? PIM4328_MFR_READ_IINA + : PIM4328_MFR_READ_IINB); + break; + default: + ret = -ENODATA; + } + + return ret; +} + +static int pim4328_read_byte_data(struct i2c_client *client, int page, int reg) +{ + const struct pmbus_driver_info *info = pmbus_get_driver_info(client); + struct pim4328_data *data = to_pim4328_data(info); + int ret, status; + + if (page > 0) + return -ENXIO; + + switch (reg) { + case PMBUS_STATUS_BYTE: + ret = pmbus_read_byte_data(client, page, PMBUS_STATUS_BYTE); + if (ret < 0) + return ret; + if (data->id == pim4006) { + status = pmbus_read_word_data(client, page, 0xff, + PIM4328_MFR_FET_CHECKSTATUS); + if (status < 0) + return status; + if (status & 0x0630) /* Input UV */ + ret |= PB_STATUS_VIN_UV; + } else if (data->id == pim4328) { + status = pmbus_read_byte_data(client, page, + PIM4328_MFR_STATUS_BITS); + if (status < 0) + return status; + if (status & 0x04) /* Input UV */ + ret |= PB_STATUS_VIN_UV; + if (status & 0x40) /* Output UV */ + ret |= PB_STATUS_NONE_ABOVE; + } else if (data->id == pim4820) { + status = pmbus_read_byte_data(client, page, + PIM4328_MFR_READ_STATUS); + if (status < 0) + return status; + if (status & 0x05) /* Input OV or OC */ + ret |= PB_STATUS_NONE_ABOVE; + if (status & 0x1a) /* Input UV */ + ret |= PB_STATUS_VIN_UV; + if (status & 0x40) /* OT */ + ret |= PB_STATUS_TEMPERATURE; + } + break; + default: + ret = -ENODATA; + } + + return ret; +} + +static int pim4328_probe(struct i2c_client *client) +{ + int status; + u8 device_id[I2C_SMBUS_BLOCK_MAX + 1]; + const struct i2c_device_id *mid; + struct pim4328_data *data; + struct pmbus_driver_info *info; + struct pmbus_platform_data *pdata; + struct device *dev = &client->dev; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA + | I2C_FUNC_SMBUS_BLOCK_DATA)) + return -ENODEV; + + data = devm_kzalloc(&client->dev, sizeof(struct pim4328_data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + status = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, device_id); + if (status < 0) { + dev_err(&client->dev, "Failed to read Manufacturer Model\n"); + return status; + } + for (mid = pim4328_id; mid->name[0]; mid++) { + if (!strncasecmp(mid->name, device_id, strlen(mid->name))) + break; + } + if (!mid->name[0]) { + dev_err(&client->dev, "Unsupported device\n"); + return -ENODEV; + } + + if (strcmp(client->name, mid->name)) + dev_notice(&client->dev, + "Device mismatch: Configured %s, detected %s\n", + client->name, mid->name); + + data->id = mid->driver_data; + info = &data->info; + info->pages = 1; + info->read_byte_data = pim4328_read_byte_data; + info->read_word_data = pim4328_read_word_data; + + pdata = devm_kzalloc(dev, sizeof(struct pmbus_platform_data), + GFP_KERNEL); + if (!pdata) + return -ENOMEM; + dev->platform_data = pdata; + pdata->flags = PMBUS_NO_CAPABILITY | PMBUS_NO_WRITE_PROTECT; + + switch (data->id) { + case pim4006: + info->phases[0] = 2; + info->func[0] = PMBUS_PHASE_VIRTUAL | PMBUS_HAVE_VIN + | PMBUS_HAVE_TEMP | PMBUS_HAVE_IOUT; + info->pfunc[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN; + info->pfunc[1] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN; + break; + case pim4328: + info->phases[0] = 2; + info->func[0] = PMBUS_PHASE_VIRTUAL + | PMBUS_HAVE_VCAP | PMBUS_HAVE_VIN + | PMBUS_HAVE_TEMP | PMBUS_HAVE_IOUT; + info->pfunc[0] = PMBUS_HAVE_VIN; + info->pfunc[1] = PMBUS_HAVE_VIN; + info->format[PSC_VOLTAGE_IN] = direct; + info->format[PSC_TEMPERATURE] = direct; + info->format[PSC_CURRENT_OUT] = direct; + pdata->flags |= PMBUS_USE_COEFFICIENTS_CMD; + break; + case pim4820: + info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_TEMP + | PMBUS_HAVE_IIN; + info->format[PSC_VOLTAGE_IN] = direct; + info->format[PSC_TEMPERATURE] = direct; + info->format[PSC_CURRENT_IN] = direct; + pdata->flags |= PMBUS_USE_COEFFICIENTS_CMD; + break; + default: + return -ENODEV; + } + + return pmbus_do_probe(client, info); +} + +static struct i2c_driver pim4328_driver = { + .driver = { + .name = "pim4328", + }, + .probe_new = pim4328_probe, + .id_table = pim4328_id, +}; + +module_i2c_driver(pim4328_driver); + +MODULE_AUTHOR("Erik Rosen "); +MODULE_DESCRIPTION("PMBus driver for PIM4006, PIM4328, PIM4820 power interface modules"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); -- cgit v1.2.3 From bf8e0cd8d6b2c9be365ea53d36e9368f07880a2f Mon Sep 17 00:00:00 2001 From: Erik Rosen Date: Wed, 9 Jun 2021 11:32:09 +0200 Subject: hwmon: (pmbus/pim4328) Add documentation for the pim4328 PMBus driver Add documentation and index link for pim4328 PMBus driver. Signed-off-by: Erik Rosen Signed-off-by: Guenter Roeck --- Documentation/hwmon/index.rst | 1 + Documentation/hwmon/pim4328.rst | 105 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 Documentation/hwmon/pim4328.rst diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index 61e5d4532622..9d4f5b2b84b0 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -151,6 +151,7 @@ Hardware Monitoring Kernel Drivers pc87360 pc87427 pcf8591 + pim4328 pm6764tr pmbus powr1220 diff --git a/Documentation/hwmon/pim4328.rst b/Documentation/hwmon/pim4328.rst new file mode 100644 index 000000000000..70c9e7a6882c --- /dev/null +++ b/Documentation/hwmon/pim4328.rst @@ -0,0 +1,105 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Kernel driver pim4328 +===================== + +Supported chips: + + * Flex PIM4328 + + Prefix: 'pim4328', 'bmr455' + + Addresses scanned: - + + Datasheet: + +https://flexpowermodules.com/resources/fpm-techspec-pim4328 + + * Flex PIM4820 + + Prefixes: 'pim4820' + + Addresses scanned: - + + Datasheet: https://flexpowermodules.com/resources/fpm-techspec-pim4820 + + * Flex PIM4006, PIM4106, PIM4206, PIM4306, PIM4406 + + Prefixes: 'pim4006', 'pim4106', 'pim4206', 'pim4306', 'pim4406' + + Addresses scanned: - + + Datasheet: https://flexpowermodules.com/resources/fpm-techspec-pim4006 + +Author: Erik Rosen + + +Description +----------- + +This driver supports hardware monitoring for Flex PIM4328 and +compatible digital power interface modules. + +The driver is a client driver to the core PMBus driver. Please see +Documentation/hwmon/pmbus.rst and Documentation.hwmon/pmbus-core for details +on PMBus client drivers. + + +Usage Notes +----------- + +This driver does not auto-detect devices. You will have to instantiate the +devices explicitly. Please see Documentation/i2c/instantiating-devices.rst for +details. + + +Platform data support +--------------------- + +The driver supports standard PMBus driver platform data. + + +Sysfs entries +------------- + +The following attributes are supported. All attributes are read-only. + +======================= ======================================================== +in1_label "vin" +in1_input Measured input voltage. +in1_alarm Input voltage alarm. + +in2_label "vin.0" +in2_input Measured input voltage on input A. + + PIM4328 and PIM4X06 + +in3_label "vin.1" +in3_input Measured input voltage on input B. + + PIM4328 and PIM4X06 + +in4_label "vcap" +in4_input Measured voltage on holdup capacitor. + + PIM4328 + +curr1_label "iin.0" +curr1_input Measured input current on input A. + + PIM4X06 + +curr2_label "iin.1" +curr2_input Measured input current on input B. + + PIM4X06 + +currX_label "iout1" +currX_input Measured output current. +currX_alarm Output current alarm. + + X is 1 for PIM4820, 3 otherwise. + +temp1_input Measured temperature. +temp1_alarm High temperature alarm. +======================= ======================================================== -- cgit v1.2.3 From 3efbcee8d4029795fa0a1ef90dc5b9ea763ed207 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Mon, 7 Jun 2021 12:34:29 +0200 Subject: hwmon: (pmbus) Add driver for Delta DPS-920AB PSU This adds support for the Delta DPS-920AB PSU. Only missing feature is fan control which the PSU supports. Signed-off-by: Robert Marko Link: https://lore.kernel.org/r/20210607103431.2039073-1-robert.marko@sartura.hr [groeck: Add MODULE_IMPORT_NS(PMBUS);] Signed-off-by: Guenter Roeck --- Documentation/hwmon/dps920ab.rst | 73 ++++++++++++++ Documentation/hwmon/index.rst | 1 + drivers/hwmon/pmbus/Kconfig | 9 ++ drivers/hwmon/pmbus/Makefile | 1 + drivers/hwmon/pmbus/dps920ab.c | 208 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 292 insertions(+) create mode 100644 Documentation/hwmon/dps920ab.rst create mode 100644 drivers/hwmon/pmbus/dps920ab.c diff --git a/Documentation/hwmon/dps920ab.rst b/Documentation/hwmon/dps920ab.rst new file mode 100644 index 000000000000..c33b4cdc0a60 --- /dev/null +++ b/Documentation/hwmon/dps920ab.rst @@ -0,0 +1,73 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Kernel driver dps920ab +======================== + +Supported chips: + + * Delta DPS920AB + + Prefix: 'dps920ab' + + Addresses scanned: - + +Authors: + Robert Marko + + +Description +----------- + +This driver implements support for Delta DPS920AB 920W 54V DC single output +power supply with PMBus support. + +The driver is a client driver to the core PMBus driver. +Please see Documentation/hwmon/pmbus.rst for details on PMBus client drivers. + + +Usage Notes +----------- + +This driver does not auto-detect devices. You will have to instantiate the +devices explicitly. Please see Documentation/i2c/instantiating-devices.rst for +details. + + +Sysfs entries +------------- + +======================= ====================================================== +curr1_label "iin" +curr1_input Measured input current +curr1_alarm Input current high alarm + +curr2_label "iout1" +curr2_input Measured output current +curr2_max Maximum output current +curr2_rated_max Maximum rated output current + +in1_label "vin" +in1_input Measured input voltage +in1_alarm Input voltage alarm + +in2_label "vout1" +in2_input Measured output voltage +in2_rated_min Minimum rated output voltage +in2_rated_max Maximum rated output voltage +in2_alarm Output voltage alarm + +power1_label "pin" +power1_input Measured input power +power1_alarm Input power high alarm + +power2_label "pout1" +power2_input Measured output power +power2_rated_max Maximum rated output power + +temp[1-3]_input Measured temperature +temp[1-3]_alarm Temperature alarm + +fan1_alarm Fan 1 warning. +fan1_fault Fan 1 fault. +fan1_input Fan 1 speed in RPM. +======================= ====================================================== diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index 9d4f5b2b84b0..bc01601ea81a 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -53,6 +53,7 @@ Hardware Monitoring Kernel Drivers da9055 dell-smm-hwmon dme1737 + dps920ab drivetemp ds1621 ds620 diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 10ef548f74a4..ffb609cee3a4 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -86,6 +86,15 @@ config SENSORS_IBM_CFFPS This driver can also be built as a module. If so, the module will be called ibm-cffps. +config SENSORS_DPS920AB + tristate "Delta DPS920AB Power Supply" + help + If you say yes here you get hardware monitoring support for Delta + DPS920AB Power Supplies. + + This driver can also be built as a module. If so, the module will + be called dps920ab. + config SENSORS_INSPUR_IPSPS tristate "INSPUR Power System Power Supply" help diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile index b3354518f66f..0ed4d596a948 100644 --- a/drivers/hwmon/pmbus/Makefile +++ b/drivers/hwmon/pmbus/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_SENSORS_BEL_PFE) += bel-pfe.o obj-$(CONFIG_SENSORS_BPA_RS600) += bpa-rs600.o obj-$(CONFIG_SENSORS_FSP_3Y) += fsp-3y.o obj-$(CONFIG_SENSORS_IBM_CFFPS) += ibm-cffps.o +obj-$(CONFIG_SENSORS_DPS920AB) += dps920ab.o obj-$(CONFIG_SENSORS_INSPUR_IPSPS) += inspur-ipsps.o obj-$(CONFIG_SENSORS_IR35221) += ir35221.o obj-$(CONFIG_SENSORS_IR36021) += ir36021.o diff --git a/drivers/hwmon/pmbus/dps920ab.c b/drivers/hwmon/pmbus/dps920ab.c new file mode 100644 index 000000000000..bd2df2a3c8e3 --- /dev/null +++ b/drivers/hwmon/pmbus/dps920ab.c @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for Delta DPS920AB PSU + * + * Copyright (C) 2021 Delta Networks, Inc. + * Copyright (C) 2021 Sartura Ltd. + */ + +#include +#include +#include +#include +#include "pmbus.h" + +struct dps920ab_data { + char *mfr_model; + char *mfr_id; +}; + +static int dps920ab_read_word_data(struct i2c_client *client, int page, int phase, int reg) +{ + /* + * This masks commands which are not supported. + * PSU advertises that all features are supported, + * in reality that unfortunately is not true. + * So enable only those that the datasheet confirms. + */ + switch (reg) { + case PMBUS_FAN_COMMAND_1: + case PMBUS_IOUT_OC_WARN_LIMIT: + case PMBUS_STATUS_WORD: + case PMBUS_READ_VIN: + case PMBUS_READ_IIN: + case PMBUS_READ_VOUT: + case PMBUS_READ_IOUT: + case PMBUS_READ_TEMPERATURE_1: + case PMBUS_READ_TEMPERATURE_2: + case PMBUS_READ_TEMPERATURE_3: + case PMBUS_READ_FAN_SPEED_1: + case PMBUS_READ_POUT: + case PMBUS_READ_PIN: + case PMBUS_MFR_VOUT_MIN: + case PMBUS_MFR_VOUT_MAX: + case PMBUS_MFR_IOUT_MAX: + case PMBUS_MFR_POUT_MAX: + return pmbus_read_word_data(client, page, phase, reg); + default: + return -ENXIO; + } +} + +static int dps920ab_write_word_data(struct i2c_client *client, int page, int reg, + u16 word) +{ + /* + * This masks commands which are not supported. + * PSU only has one R/W register and that is + * for the fan. + */ + switch (reg) { + case PMBUS_FAN_COMMAND_1: + return pmbus_write_word_data(client, page, reg, word); + default: + return -EACCES; + } +} + +static struct pmbus_driver_info dps920ab_info = { + .pages = 1, + + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = linear, + .format[PSC_CURRENT_IN] = linear, + .format[PSC_CURRENT_OUT] = linear, + .format[PSC_POWER] = linear, + .format[PSC_FAN] = linear, + .format[PSC_TEMPERATURE] = linear, + + .func[0] = + PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | PMBUS_HAVE_PIN | + PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | PMBUS_HAVE_POUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3 | + PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12 | + PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP, + .read_word_data = dps920ab_read_word_data, + .write_word_data = dps920ab_write_word_data, +}; + +static int dps920ab_mfr_id_show(struct seq_file *s, void *data) +{ + struct dps920ab_data *priv = s->private; + + seq_printf(s, "%s\n", priv->mfr_id); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(dps920ab_mfr_id); + +static int dps920ab_mfr_model_show(struct seq_file *s, void *data) +{ + struct dps920ab_data *priv = s->private; + + seq_printf(s, "%s\n", priv->mfr_model); + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(dps920ab_mfr_model); + +static void dps920ab_init_debugfs(struct dps920ab_data *data, struct i2c_client *client) +{ + struct dentry *debugfs_dir; + struct dentry *root; + + root = pmbus_get_debugfs_dir(client); + if (!root) + return; + + debugfs_dir = debugfs_create_dir(client->name, root); + if (!debugfs_dir) + return; + + debugfs_create_file("mfr_id", + 0400, + debugfs_dir, + data, + &dps920ab_mfr_id_fops); + + debugfs_create_file("mfr_model", + 0400, + debugfs_dir, + data, + &dps920ab_mfr_model_fops); +} + +static int dps920ab_probe(struct i2c_client *client) +{ + u8 buf[I2C_SMBUS_BLOCK_MAX + 1]; + struct dps920ab_data *data; + int ret; + + data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf); + if (ret < 0) { + dev_err(&client->dev, "Failed to read Manufacturer ID\n"); + return ret; + } + + buf[ret] = '\0'; + if (ret != 5 || strncmp(buf, "DELTA", 5)) { + buf[ret] = '\0'; + dev_err(&client->dev, "Unsupported Manufacturer ID '%s'\n", buf); + return -ENODEV; + } + data->mfr_id = devm_kstrdup(&client->dev, buf, GFP_KERNEL); + if (!data->mfr_id) + return -ENOMEM; + + ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf); + if (ret < 0) { + dev_err(&client->dev, "Failed to read Manufacturer Model\n"); + return ret; + } + + buf[ret] = '\0'; + if (ret != 11 || strncmp(buf, "DPS-920AB", 9)) { + dev_err(&client->dev, "Unsupported Manufacturer Model '%s'\n", buf); + return -ENODEV; + } + data->mfr_model = devm_kstrdup(&client->dev, buf, GFP_KERNEL); + if (!data->mfr_model) + return -ENOMEM; + + ret = pmbus_do_probe(client, &dps920ab_info); + if (ret) + return ret; + + dps920ab_init_debugfs(data, client); + + return 0; +} + +static const struct of_device_id __maybe_unused dps920ab_of_match[] = { + { .compatible = "delta,dps920ab", }, + {} +}; + +MODULE_DEVICE_TABLE(of, dps920ab_of_match); + +static struct i2c_driver dps920ab_driver = { + .driver = { + .name = "dps920ab", + .of_match_table = of_match_ptr(dps920ab_of_match), + }, + .probe_new = dps920ab_probe, +}; + +module_i2c_driver(dps920ab_driver); + +MODULE_AUTHOR("Robert Marko "); +MODULE_DESCRIPTION("PMBus driver for Delta DPS920AB PSU"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PMBUS); -- cgit v1.2.3 From 8b1d61cd47ccea482a3f68c99d7358e3daea35fa Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Mon, 7 Jun 2021 12:34:30 +0200 Subject: dt-bindings: trivial-devices: Add Delta DPS920AB Add trivial device entry for Delta DPS920AB PSU. Signed-off-by: Robert Marko Link: https://lore.kernel.org/r/20210607103431.2039073-2-robert.marko@sartura.hr Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/trivial-devices.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/trivial-devices.yaml b/Documentation/devicetree/bindings/trivial-devices.yaml index f8824e1dd24c..37ac0a3ae3b4 100644 --- a/Documentation/devicetree/bindings/trivial-devices.yaml +++ b/Documentation/devicetree/bindings/trivial-devices.yaml @@ -73,6 +73,8 @@ properties: - dallas,ds4510 # Digital Thermometer and Thermostat - dallas,ds75 + # Delta Electronics DPS920AB 920W 54V Power Supply + - delta,dps920ab # 1/4 Brick DC/DC Regulated Power Module - delta,q54sj108a2 # Devantech SRF02 ultrasonic ranger in I2C mode -- cgit v1.2.3 From c5679f3e702ce6b7d3d0d95b5a7e2e4b5c780006 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Wed, 9 Jun 2021 11:59:23 -0700 Subject: MAINTAINERS: Add Delta DPS920AB PSU driver Add maintainers entry for the Delta DPS920AB PSU driver. Signed-off-by: Robert Marko Signed-off-by: Guenter Roeck --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index bc0ceef87b73..2c743057de3a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5189,6 +5189,13 @@ W: https://linuxtv.org T: git git://linuxtv.org/media_tree.git F: drivers/media/platform/sti/delta +DELTA DPS920AB PSU DRIVER +M: Robert Marko +L: linux-hwmon@vger.kernel.org +S: Maintained +F: Documentation/hwmon/dps920ab.rst +F: drivers/hwmon/pmbus/dps920ab.c + DENALI NAND DRIVER L: linux-mtd@lists.infradead.org S: Orphan -- cgit v1.2.3 From f0000797a3862eba99d06e65be846317c1ccbd8e Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Fri, 11 Jun 2021 15:22:57 +0100 Subject: hwmon: (ntc_thermistor) Drop unused headers. The IIO usage in this driver is purely consumer so it should only be including linux/iio/consumer.h Whilst here drop pm_runtime.h as there is no runtime power management in the driver. Found using include-what-you-use and manual inspection of the suggestions. Signed-off-by: Jonathan Cameron Cc: Guenter Roeck Link: https://lore.kernel.org/r/20210611142257.103094-1-jic23@kernel.org Signed-off-by: Guenter Roeck --- drivers/hwmon/ntc_thermistor.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/hwmon/ntc_thermistor.c b/drivers/hwmon/ntc_thermistor.c index 8587189c7f15..18fd6f12ca16 100644 --- a/drivers/hwmon/ntc_thermistor.c +++ b/drivers/hwmon/ntc_thermistor.c @@ -8,7 +8,6 @@ #include #include -#include #include #include #include @@ -17,9 +16,6 @@ #include -#include -#include -#include #include #include -- cgit v1.2.3 From 9e25f01b5f529d397be2e3f595b0b54ae9e80c58 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 18 Jun 2021 16:46:01 +0300 Subject: hwmon: (pmbus/dps920ab) Delete some dead code The debugfs_create_dir() function returns error pointers, it doesn't return NULL. But debugfs functions don't need to be checked in normal situations and we can just delete this code. Fixes: 1f442e213ce5 ("hwmon: (pmbus) Add driver for Delta DPS-920AB PSU") Signed-off-by: Dan Carpenter Link: https://lore.kernel.org/r/YMyjmR54ErLtc1sH@mwanda Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/dps920ab.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/hwmon/pmbus/dps920ab.c b/drivers/hwmon/pmbus/dps920ab.c index bd2df2a3c8e3..d3941f6eb29a 100644 --- a/drivers/hwmon/pmbus/dps920ab.c +++ b/drivers/hwmon/pmbus/dps920ab.c @@ -119,8 +119,6 @@ static void dps920ab_init_debugfs(struct dps920ab_data *data, struct i2c_client return; debugfs_dir = debugfs_create_dir(client->name, root); - if (!debugfs_dir) - return; debugfs_create_file("mfr_id", 0400, -- cgit v1.2.3 From d97fb837b8cce400892e7f0ccf4755edb225ad36 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Sat, 19 Jun 2021 00:54:52 +0300 Subject: hwmon: (lm90) Don't override interrupt trigger type The lm90 driver sets interrupt trigger type to level-low. This type is not suitable for sensors like NCT1008 that don't deassert interrupt line until temperature is back to normal, resulting in interrupt storm. The appropriate trigger type should come from OF device description and currently it's overridden by the driver's trigger type. Don't specify the trigger type in the driver code, letting interrupt core to use the device-specific trigger type. Signed-off-by: Dmitry Osipenko Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index ebbfd5f352c0..2e057fad05b4 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -1908,8 +1908,7 @@ static int lm90_probe(struct i2c_client *client) dev_dbg(dev, "IRQ: %d\n", client->irq); err = devm_request_threaded_irq(dev, client->irq, NULL, lm90_irq_thread, - IRQF_TRIGGER_LOW | IRQF_ONESHOT, - "lm90", client); + IRQF_ONESHOT, "lm90", client); if (err < 0) { dev_err(dev, "cannot request IRQ %d\n", client->irq); return err; -- cgit v1.2.3 From 94dbd23ed88ce70d7baacfa20d21bc0070d1a8da Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Sat, 19 Jun 2021 00:54:53 +0300 Subject: hwmon: (lm90) Use hwmon_notify_event() Use hwmon_notify_event() to notify userspace and thermal core about temperature changes. Suggested-by: Guenter Roeck Signed-off-by: Dmitry Osipenko Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 44 +++++++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 2e057fad05b4..e7b678a40b39 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -465,6 +465,7 @@ enum lm90_temp11_reg_index { struct lm90_data { struct i2c_client *client; + struct device *hwmon_dev; u32 channel_config[4]; struct hwmon_channel_info temp_info; const struct hwmon_channel_info *info[3]; @@ -1731,22 +1732,41 @@ static bool lm90_is_tripped(struct i2c_client *client, u16 *status) if ((st & (LM90_STATUS_LLOW | LM90_STATUS_LHIGH | LM90_STATUS_LTHRM)) || (st2 & MAX6696_STATUS2_LOT2)) - dev_warn(&client->dev, - "temp%d out of range, please check!\n", 1); + dev_dbg(&client->dev, + "temp%d out of range, please check!\n", 1); if ((st & (LM90_STATUS_RLOW | LM90_STATUS_RHIGH | LM90_STATUS_RTHRM)) || (st2 & MAX6696_STATUS2_ROT2)) - dev_warn(&client->dev, - "temp%d out of range, please check!\n", 2); + dev_dbg(&client->dev, + "temp%d out of range, please check!\n", 2); if (st & LM90_STATUS_ROPEN) - dev_warn(&client->dev, - "temp%d diode open, please check!\n", 2); + dev_dbg(&client->dev, + "temp%d diode open, please check!\n", 2); if (st2 & (MAX6696_STATUS2_R2LOW | MAX6696_STATUS2_R2HIGH | MAX6696_STATUS2_R2THRM | MAX6696_STATUS2_R2OT2)) - dev_warn(&client->dev, - "temp%d out of range, please check!\n", 3); + dev_dbg(&client->dev, + "temp%d out of range, please check!\n", 3); if (st2 & MAX6696_STATUS2_R2OPEN) - dev_warn(&client->dev, - "temp%d diode open, please check!\n", 3); + dev_dbg(&client->dev, + "temp%d diode open, please check!\n", 3); + + if (st & LM90_STATUS_LLOW) + hwmon_notify_event(data->hwmon_dev, hwmon_temp, + hwmon_temp_min, 0); + if (st & LM90_STATUS_RLOW) + hwmon_notify_event(data->hwmon_dev, hwmon_temp, + hwmon_temp_min, 1); + if (st2 & MAX6696_STATUS2_R2LOW) + hwmon_notify_event(data->hwmon_dev, hwmon_temp, + hwmon_temp_min, 2); + if (st & LM90_STATUS_LHIGH) + hwmon_notify_event(data->hwmon_dev, hwmon_temp, + hwmon_temp_max, 0); + if (st & LM90_STATUS_RHIGH) + hwmon_notify_event(data->hwmon_dev, hwmon_temp, + hwmon_temp_max, 1); + if (st2 & MAX6696_STATUS2_R2HIGH) + hwmon_notify_event(data->hwmon_dev, hwmon_temp, + hwmon_temp_max, 2); return true; } @@ -1904,6 +1924,8 @@ static int lm90_probe(struct i2c_client *client) if (IS_ERR(hwmon_dev)) return PTR_ERR(hwmon_dev); + data->hwmon_dev = hwmon_dev; + if (client->irq) { dev_dbg(dev, "IRQ: %d\n", client->irq); err = devm_request_threaded_irq(dev, client->irq, @@ -1940,7 +1962,7 @@ static void lm90_alert(struct i2c_client *client, enum i2c_alert_protocol type, lm90_update_confreg(data, data->config | 0x80); } } else { - dev_info(&client->dev, "Everything OK\n"); + dev_dbg(&client->dev, "Everything OK\n"); } } -- cgit v1.2.3 From 2abdc357c55d9e728f6710cf22618889f16a00f6 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Sat, 19 Jun 2021 00:54:54 +0300 Subject: hwmon: (lm90) Unmask hardware interrupt The ALERT interrupt is enabled by default after power-on, but it could be masked by bootloader. For example this is the case on Acer A500 tablet device. Unmask the hardware interrupt if interrupt is provided. Signed-off-by: Dmitry Osipenko Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index e7b678a40b39..658b486d2f5e 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -1704,6 +1704,13 @@ static int lm90_init_client(struct i2c_client *client, struct lm90_data *data) if (data->kind == max6696) config &= ~0x08; + /* + * Interrupt is enabled by default on reset, but it may be disabled + * by bootloader, unmask it. + */ + if (client->irq) + config &= ~0x80; + config &= 0xBF; /* run */ lm90_update_confreg(data, config); -- cgit v1.2.3 From 4c7f85a321a1ac265159c22a6998ef4f2a60c21d Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Sat, 19 Jun 2021 00:54:55 +0300 Subject: hwmon: (lm90) Disable interrupt on suspend I2C accesses are prohibited and will error out after suspending of the I2C controller, hence we need to ensure that interrupt won't fire on suspend when it's too late. Disable interrupt across suspend/resume. Signed-off-by: Dmitry Osipenko Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 658b486d2f5e..b53f17511b05 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -1973,11 +1973,36 @@ static void lm90_alert(struct i2c_client *client, enum i2c_alert_protocol type, } } +static int __maybe_unused lm90_suspend(struct device *dev) +{ + struct lm90_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + + if (client->irq) + disable_irq(client->irq); + + return 0; +} + +static int __maybe_unused lm90_resume(struct device *dev) +{ + struct lm90_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + + if (client->irq) + enable_irq(client->irq); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(lm90_pm_ops, lm90_suspend, lm90_resume); + static struct i2c_driver lm90_driver = { .class = I2C_CLASS_HWMON, .driver = { .name = "lm90", .of_match_table = of_match_ptr(lm90_of_match), + .pm = &lm90_pm_ops, }, .probe_new = lm90_probe, .alert = lm90_alert, -- cgit v1.2.3 From b50aa49638c7e12abf4ecc483f4e928c5cccc1b0 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 23 Jun 2021 07:22:30 +0300 Subject: hwmon: (lm90) Prevent integer underflows of temperature calculations The min/max/crit and all other temperature values that are passed to the driver are unlimited and value that is close to INT_MIN results in integer underflow of the temperature calculations made by the driver for LM99 sensor. Temperature hysteresis is among those values that need to be limited, but limiting of hysteresis is independent from the sensor version. Add the missing limits. Signed-off-by: Dmitry Osipenko Link: https://lore.kernel.org/r/20210623042231.16008-2-digetx@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index b53f17511b05..567b7c521f38 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -1029,8 +1029,11 @@ static int lm90_set_temp11(struct lm90_data *data, int index, long val) int err; /* +16 degrees offset for temp2 for the LM99 */ - if (data->kind == lm99 && index <= 2) + if (data->kind == lm99 && index <= 2) { + /* prevent integer underflow */ + val = max(val, -128000l); val -= 16000; + } if (data->kind == adt7461 || data->kind == tmp451) data->temp11[index] = temp_to_u16_adt7461(data, val); @@ -1089,8 +1092,11 @@ static int lm90_set_temp8(struct lm90_data *data, int index, long val) int err; /* +16 degrees offset for temp2 for the LM99 */ - if (data->kind == lm99 && index == 3) + if (data->kind == lm99 && index == 3) { + /* prevent integer underflow */ + val = max(val, -128000l); val -= 16000; + } if (data->kind == adt7461 || data->kind == tmp451) data->temp8[index] = temp_to_u8_adt7461(data, val); @@ -1137,6 +1143,9 @@ static int lm90_set_temphyst(struct lm90_data *data, long val) else temp = temp_from_s8(data->temp8[LOCAL_CRIT]); + /* prevent integer underflow */ + val = max(val, -128000l); + data->temp_hyst = hyst_to_reg(temp - val); err = i2c_smbus_write_byte_data(client, LM90_REG_W_TCRIT_HYST, data->temp_hyst); -- cgit v1.2.3 From a5f6c0f85a09f46c88c0ac53f3d2f70eef105a65 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 23 Jun 2021 07:22:31 +0300 Subject: hwmon: Support set_trips() of thermal device ops Support set_trips() callback of thermal device ops. This allows HWMON device to operatively notify thermal core about temperature changes, which is very handy to have in a case where HWMON sensor is used by CPU thermal zone that performs passive cooling and emergency shutdown on overheat. Thermal core will be able to react faster to temperature changes. The set_trips() callback is entirely optional. If HWMON sensor doesn't support setting thermal trips, then the callback is a NO-OP. The dummy callback has no effect on the thermal core. The temperature trips are either complement the temperature polling mechanism of thermal core or replace the polling if sensor can set the trips and polling is disabled by a particular device in a device-tree. Signed-off-by: Dmitry Osipenko Link: https://lore.kernel.org/r/20210623042231.16008-3-digetx@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/hwmon.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index fd47ab4e6892..8d3b1dae31df 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -153,8 +153,44 @@ static int hwmon_thermal_get_temp(void *data, int *temp) return 0; } +static int hwmon_thermal_set_trips(void *data, int low, int high) +{ + struct hwmon_thermal_data *tdata = data; + struct hwmon_device *hwdev = to_hwmon_device(tdata->dev); + const struct hwmon_chip_info *chip = hwdev->chip; + const struct hwmon_channel_info **info = chip->info; + unsigned int i; + int err; + + if (!chip->ops->write) + return 0; + + for (i = 0; info[i] && info[i]->type != hwmon_temp; i++) + continue; + + if (!info[i]) + return 0; + + if (info[i]->config[tdata->index] & HWMON_T_MIN) { + err = chip->ops->write(tdata->dev, hwmon_temp, + hwmon_temp_min, tdata->index, low); + if (err && err != -EOPNOTSUPP) + return err; + } + + if (info[i]->config[tdata->index] & HWMON_T_MAX) { + err = chip->ops->write(tdata->dev, hwmon_temp, + hwmon_temp_max, tdata->index, high); + if (err && err != -EOPNOTSUPP) + return err; + } + + return 0; +} + static const struct thermal_zone_of_device_ops hwmon_thermal_ops = { .get_temp = hwmon_thermal_get_temp, + .set_trips = hwmon_thermal_set_trips, }; static void hwmon_thermal_remove_sensor(void *data) -- cgit v1.2.3