From 4e15d05d9c439977e2f995b895e1701a8c47d800 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Tue, 14 Aug 2018 14:18:28 +0200 Subject: [PATCH 01/46] drivers: hwmon: (pmbus/ltc2978) Add support for LTM4686 uModule This patch adds support for LTM4686 Ultrathin Dual 10A or Single 20A uModule Regulator with Digital Power System Management. Datasheet: http://www.analog.com/ltm4686 Signed-off-by: Michael Hennerich Signed-off-by: Guenter Roeck --- Documentation/devicetree/bindings/hwmon/ltc2978.txt | 2 ++ Documentation/hwmon/ltc2978 | 5 +++++ drivers/hwmon/pmbus/Kconfig | 3 ++- drivers/hwmon/pmbus/ltc2978.c | 9 ++++++++- 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/hwmon/ltc2978.txt b/Documentation/devicetree/bindings/hwmon/ltc2978.txt index bf2a47bbdc58..b428a70a7cc0 100644 --- a/Documentation/devicetree/bindings/hwmon/ltc2978.txt +++ b/Documentation/devicetree/bindings/hwmon/ltc2978.txt @@ -15,6 +15,7 @@ Required properties: * "lltc,ltm2987" * "lltc,ltm4675" * "lltc,ltm4676" + * "lltc,ltm4686" - reg: I2C slave address Optional properties: @@ -30,6 +31,7 @@ Valid names of regulators depend on number of supplies supported per device: * ltc3880, ltc3882, ltc3886 : vout0 - vout1 * ltc3883 : vout0 * ltm4676 : vout0 - vout1 + * ltm4686 : vout0 - vout1 Example: ltc2978@5e { diff --git a/Documentation/hwmon/ltc2978 b/Documentation/hwmon/ltc2978 index 9a49d3c90cd1..dfb2caa401d9 100644 --- a/Documentation/hwmon/ltc2978 +++ b/Documentation/hwmon/ltc2978 @@ -55,6 +55,10 @@ Supported chips: Prefix: 'ltm4676' Addresses scanned: - Datasheet: http://www.linear.com/product/ltm4676 + * Analog Devices LTM4686 + Prefix: 'ltm4686' + Addresses scanned: - + Datasheet: http://www.analog.com/ltm4686 Author: Guenter Roeck @@ -76,6 +80,7 @@ additional components on a single die. The chip is instantiated and reported as two separate chips on two different I2C bus addresses. LTM4675 is a dual 9A or single 18A μModule regulator LTM4676 is a dual 13A or single 26A uModule regulator. +LTM4686 is a dual 10A or single 20A uModule regulator. Usage Notes diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index a82018aaf473..9cdb71baeda4 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -83,7 +83,8 @@ config SENSORS_LTC2978_REGULATOR depends on SENSORS_LTC2978 && REGULATOR help If you say yes here you get regulator support for Linear - Technology LTC2974, LTC2977, LTC2978, LTC3880, LTC3883, and LTM4676. + Technology LTC2974, LTC2977, LTC2978, LTC3880, LTC3883, LTM4676 + and LTM4686. config SENSORS_LTC3815 tristate "Linear Technologies LTC3815" diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c index 58b789c28b48..07afb92bb36b 100644 --- a/drivers/hwmon/pmbus/ltc2978.c +++ b/drivers/hwmon/pmbus/ltc2978.c @@ -4,6 +4,7 @@ * Copyright (c) 2011 Ericsson AB. * Copyright (c) 2013, 2014, 2015 Guenter Roeck * Copyright (c) 2015 Linear Technology + * Copyright (c) 2018 Analog Devices Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -28,7 +29,7 @@ #include "pmbus.h" enum chips { ltc2974, ltc2975, ltc2977, ltc2978, ltc2980, ltc3880, ltc3882, - ltc3883, ltc3886, ltc3887, ltm2987, ltm4675, ltm4676 }; + ltc3883, ltc3886, ltc3887, ltm2987, ltm4675, ltm4676, ltm4686 }; /* Common for all chips */ #define LTC2978_MFR_VOUT_PEAK 0xdd @@ -81,6 +82,7 @@ enum chips { ltc2974, ltc2975, ltc2977, ltc2978, ltc2980, ltc3880, ltc3882, #define LTM4676_ID_REV1 0x4400 #define LTM4676_ID_REV2 0x4480 #define LTM4676A_ID 0x47e0 +#define LTM4686_ID 0x4770 #define LTC2974_NUM_PAGES 4 #define LTC2978_NUM_PAGES 8 @@ -512,6 +514,7 @@ static const struct i2c_device_id ltc2978_id[] = { {"ltm2987", ltm2987}, {"ltm4675", ltm4675}, {"ltm4676", ltm4676}, + {"ltm4686", ltm4686}, {} }; MODULE_DEVICE_TABLE(i2c, ltc2978_id); @@ -588,6 +591,8 @@ static int ltc2978_get_id(struct i2c_client *client) else if (chip_id == LTM4676_ID_REV1 || chip_id == LTM4676_ID_REV2 || chip_id == LTM4676A_ID) return ltm4676; + else if (chip_id == LTM4686_ID) + return ltm4686; dev_err(&client->dev, "Unsupported chip ID 0x%x\n", chip_id); return -ENODEV; @@ -684,6 +689,7 @@ static int ltc2978_probe(struct i2c_client *client, case ltc3887: case ltm4675: case ltm4676: + case ltm4686: data->features |= FEAT_CLEAR_PEAKS | FEAT_NEEDS_POLLING; info->read_word_data = ltc3880_read_word_data; info->pages = LTC3880_NUM_PAGES; @@ -770,6 +776,7 @@ static const struct of_device_id ltc2978_of_match[] = { { .compatible = "lltc,ltm2987" }, { .compatible = "lltc,ltm4675" }, { .compatible = "lltc,ltm4676" }, + { .compatible = "lltc,ltm4686" }, { } }; MODULE_DEVICE_TABLE(of, ltc2978_of_match); From 3d13aa37ba4d04909c481ada8ac36aaf0a8ce9b8 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Sat, 25 Aug 2018 00:27:55 -0300 Subject: [PATCH 02/46] hwmon: (mc13783-adc) Remove the mention to 'Atlas' 'Atlas' was an internal name for the MC13783 PMIC only and does not apply to MC13892. To avoid confusion, remove the 'Atlas' term from the description. Signed-off-by: Fabio Estevam Signed-off-by: Guenter Roeck --- Documentation/hwmon/mc13783-adc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/hwmon/mc13783-adc b/Documentation/hwmon/mc13783-adc index d0e7b3fa9e75..2798a299c4bc 100644 --- a/Documentation/hwmon/mc13783-adc +++ b/Documentation/hwmon/mc13783-adc @@ -2,10 +2,10 @@ Kernel driver mc13783-adc ========================= Supported chips: - * Freescale Atlas MC13783 + * Freescale MC13783 Prefix: 'mc13783' Datasheet: http://www.freescale.com/files/rf_if/doc/data_sheet/MC13783.pdf?fsrch=1 - * Freescale Atlas MC13892 + * Freescale MC13892 Prefix: 'mc13892' Datasheet: http://cache.freescale.com/files/analog/doc/data_sheet/MC13892.pdf?fsrch=1&sr=1 From 23d3f131bf379b9460d2f079f0eb85ac669eded4 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Sat, 25 Aug 2018 00:27:56 -0300 Subject: [PATCH 03/46] hwmon: (mc13783-adc) Use nxp.com URLs Use the nxp.com URLs for the MC13783 and MC13892 datasheets. The original URLs are still valid, but the nxp.com one is shorter and more up-to-date. Signed-off-by: Fabio Estevam Signed-off-by: Guenter Roeck --- Documentation/hwmon/mc13783-adc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/hwmon/mc13783-adc b/Documentation/hwmon/mc13783-adc index 2798a299c4bc..05ccc9f159f1 100644 --- a/Documentation/hwmon/mc13783-adc +++ b/Documentation/hwmon/mc13783-adc @@ -4,10 +4,10 @@ Kernel driver mc13783-adc Supported chips: * Freescale MC13783 Prefix: 'mc13783' - Datasheet: http://www.freescale.com/files/rf_if/doc/data_sheet/MC13783.pdf?fsrch=1 + Datasheet: https://www.nxp.com/docs/en/data-sheet/MC13783.pdf * Freescale MC13892 Prefix: 'mc13892' - Datasheet: http://cache.freescale.com/files/analog/doc/data_sheet/MC13892.pdf?fsrch=1&sr=1 + Datasheet: https://www.nxp.com/docs/en/data-sheet/MC13892.pdf Authors: Sascha Hauer From 0debe4d0b86afa5eb9192595c820ed980cabe38b Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 27 Aug 2018 20:52:21 -0500 Subject: [PATCH 04/46] hwmon: Convert to using %pOFn instead of device_node.name In preparation to remove the node name pointer from struct device_node, convert printf users to use the %pOFn format specifier. Cc: Jean Delvare Cc: Guenter Roeck Cc: linux-hwmon@vger.kernel.org Signed-off-by: Rob Herring Signed-off-by: Guenter Roeck --- drivers/hwmon/aspeed-pwm-tacho.c | 2 +- drivers/hwmon/ibmpowernv.c | 4 ++-- drivers/hwmon/iio_hwmon.c | 9 ++++----- drivers/hwmon/npcm750-pwm-fan.c | 2 +- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/hwmon/aspeed-pwm-tacho.c b/drivers/hwmon/aspeed-pwm-tacho.c index 5e449eac788a..92de8139d398 100644 --- a/drivers/hwmon/aspeed-pwm-tacho.c +++ b/drivers/hwmon/aspeed-pwm-tacho.c @@ -852,7 +852,7 @@ static int aspeed_create_pwm_cooling(struct device *dev, dev_err(dev, "Property 'cooling-levels' cannot be read.\n"); return ret; } - snprintf(cdev->name, MAX_CDEV_NAME_LEN, "%s%d", child->name, pwm_port); + snprintf(cdev->name, MAX_CDEV_NAME_LEN, "%pOFn%d", child, pwm_port); cdev->tcdev = thermal_of_cooling_device_register(child, cdev->name, diff --git a/drivers/hwmon/ibmpowernv.c b/drivers/hwmon/ibmpowernv.c index 83472808c816..4935897f1527 100644 --- a/drivers/hwmon/ibmpowernv.c +++ b/drivers/hwmon/ibmpowernv.c @@ -603,8 +603,8 @@ static int create_device_attrs(struct platform_device *pdev) if (of_property_read_u32(np, "sensor-id", &sensor_id) && of_property_read_u32(np, "sensor-data", &sensor_id)) { dev_info(&pdev->dev, - "'sensor-id' missing in the node '%s'\n", - np->name); + "'sensor-id' missing in the node '%pOFn'\n", + np); continue; } diff --git a/drivers/hwmon/iio_hwmon.c b/drivers/hwmon/iio_hwmon.c index 2f3f875c06ac..7566991f1c04 100644 --- a/drivers/hwmon/iio_hwmon.c +++ b/drivers/hwmon/iio_hwmon.c @@ -65,13 +65,9 @@ static int iio_hwmon_probe(struct platform_device *pdev) int in_i = 1, temp_i = 1, curr_i = 1, humidity_i = 1; enum iio_chan_type type; struct iio_channel *channels; - const char *name = "iio_hwmon"; struct device *hwmon_dev; char *sname; - if (dev->of_node && dev->of_node->name) - name = dev->of_node->name; - channels = devm_iio_channel_get_all(dev); if (IS_ERR(channels)) { if (PTR_ERR(channels) == -ENODEV) @@ -141,7 +137,10 @@ static int iio_hwmon_probe(struct platform_device *pdev) st->attr_group.attrs = st->attrs; st->groups[0] = &st->attr_group; - sname = devm_kstrdup(dev, name, GFP_KERNEL); + if (dev->of_node) + sname = devm_kasprintf(dev, GFP_KERNEL, "%pOFn", dev->of_node); + else + sname = devm_kstrdup(dev, "iio_hwmon", GFP_KERNEL); if (!sname) return -ENOMEM; diff --git a/drivers/hwmon/npcm750-pwm-fan.c b/drivers/hwmon/npcm750-pwm-fan.c index 8474d601aa63..96634fd54e0b 100644 --- a/drivers/hwmon/npcm750-pwm-fan.c +++ b/drivers/hwmon/npcm750-pwm-fan.c @@ -861,7 +861,7 @@ static int npcm7xx_create_pwm_cooling(struct device *dev, dev_err(dev, "Property 'cooling-levels' cannot be read.\n"); return ret; } - snprintf(cdev->name, THERMAL_NAME_LENGTH, "%s%d", child->name, + snprintf(cdev->name, THERMAL_NAME_LENGTH, "%pOFn%d", child, pwm_port); cdev->tcdev = thermal_of_cooling_device_register(child, From 86103cffe8834fef0a342fcac82585ff67cda569 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 28 Aug 2018 11:41:41 -0700 Subject: [PATCH 05/46] hwmon: (iio_hwmon) Do not duplicate or sanitize fixed string Calling devm_kstrdup() on a fixed string is unnecessary, as is validating its contents. Rearrange the code to avoid both. Signed-off-by: Guenter Roeck --- drivers/hwmon/iio_hwmon.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/hwmon/iio_hwmon.c b/drivers/hwmon/iio_hwmon.c index 7566991f1c04..eed66e533ee2 100644 --- a/drivers/hwmon/iio_hwmon.c +++ b/drivers/hwmon/iio_hwmon.c @@ -137,14 +137,15 @@ static int iio_hwmon_probe(struct platform_device *pdev) st->attr_group.attrs = st->attrs; st->groups[0] = &st->attr_group; - if (dev->of_node) + if (dev->of_node) { sname = devm_kasprintf(dev, GFP_KERNEL, "%pOFn", dev->of_node); - else - sname = devm_kstrdup(dev, "iio_hwmon", GFP_KERNEL); - if (!sname) - return -ENOMEM; + if (!sname) + return -ENOMEM; + strreplace(sname, '-', '_'); + } else { + sname = "iio_hwmon"; + } - strreplace(sname, '-', '_'); hwmon_dev = devm_hwmon_device_register_with_groups(dev, sname, st, st->groups); return PTR_ERR_OR_ZERO(hwmon_dev); From e9d02c6414e1090c82c89df340d3516fbc09ddf4 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 29 Aug 2018 15:02:21 -0500 Subject: [PATCH 06/46] hwmon: (ibmpowernv) drop unnecessary OF name NULL checks Checking the child node names is pointless as the DT node name can never be NULL, so remove it. Cc: Jean Delvare Cc: Guenter Roeck Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Michael Ellerman Cc: linux-hwmon@vger.kernel.org Cc: linuxppc-dev@lists.ozlabs.org Signed-off-by: Rob Herring Signed-off-by: Guenter Roeck --- drivers/hwmon/ibmpowernv.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/hwmon/ibmpowernv.c b/drivers/hwmon/ibmpowernv.c index 4935897f1527..0ccca87f5271 100644 --- a/drivers/hwmon/ibmpowernv.c +++ b/drivers/hwmon/ibmpowernv.c @@ -458,9 +458,6 @@ static int populate_attr_groups(struct platform_device *pdev) for_each_child_of_node(opal, np) { const char *label; - if (np->name == NULL) - continue; - type = get_sensor_type(np); if (type == MAX_SENSOR_TYPE) continue; @@ -589,9 +586,6 @@ static int create_device_attrs(struct platform_device *pdev) const char *label; enum sensors type; - if (np->name == NULL) - continue; - type = get_sensor_type(np); if (type == MAX_SENSOR_TYPE) continue; From 53dfa0088edd2e2793afa21488532b12eb2dae48 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 2 Sep 2018 12:02:53 -0700 Subject: [PATCH 07/46] hwmon: (k10temp) Support all Family 15h Model 6xh and Model 7xh processors BIOS developer guides refer to Family 15h Models 60h-6fh and Family 15h Models 70h-7fh. So far the driver only checked for Models 60h and 70h. However, there are now processors with other model numbers in the same families. Example is A10-9620P family 15h model 65h. Follow the developer guides and mask the lower 4 bit of the model number to determine the registers to use for reading temperatures and temperature limits. Reported-by: Guglielmo Fanini Cc: Guglielmo Fanini Acked-by: Clemens Ladisch Signed-off-by: Guenter Roeck --- drivers/hwmon/k10temp.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c index bb15d7816a29..2cef0c37ff6f 100644 --- a/drivers/hwmon/k10temp.c +++ b/drivers/hwmon/k10temp.c @@ -325,8 +325,9 @@ static int k10temp_probe(struct pci_dev *pdev, data->pdev = pdev; - if (boot_cpu_data.x86 == 0x15 && (boot_cpu_data.x86_model == 0x60 || - boot_cpu_data.x86_model == 0x70)) { + if (boot_cpu_data.x86 == 0x15 && + ((boot_cpu_data.x86_model & 0xf0) == 0x60 || + (boot_cpu_data.x86_model & 0xf0) == 0x70)) { data->read_htcreg = read_htcreg_nb_f15; data->read_tempreg = read_tempreg_nb_f15; } else if (boot_cpu_data.x86 == 0x17) { From c3bf6076cc41659c7b0301d19f871a22e4633013 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 5 Sep 2018 10:46:55 +0300 Subject: [PATCH 08/46] hwmon: (nct6775) Clean up a condition I removed the "dsw_en &&" chunk of the condition because we know that "dsw_en" is set. Signed-off-by: Dan Carpenter Signed-off-by: Guenter Roeck --- drivers/hwmon/nct6775.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c index 78603b78cf41..2b493d0682af 100644 --- a/drivers/hwmon/nct6775.c +++ b/drivers/hwmon/nct6775.c @@ -3550,8 +3550,7 @@ nct6775_check_fan_inputs(struct nct6775_data *data) if (!fan6pin) fan6pin = (regval_2a & BIT(4)) && - (!dsw_en || - (dsw_en && (regval_ed & BIT(4)))); + (!dsw_en || (regval_ed & BIT(4))); if (!pwm6pin) pwm6pin = (regval_2a & BIT(3)) && (regval_ed & BIT(2)); From 9450f52cec784a93b93772bba3a158647ed9b4d8 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 6 Sep 2018 15:59:49 -0300 Subject: [PATCH 09/46] hwmon: (mc13783-adc) Switch to SPDX identifier Adopt the SPDX license identifier headers to ease license compliance management. Signed-off-by: Fabio Estevam Signed-off-by: Guenter Roeck --- drivers/hwmon/mc13783-adc.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/hwmon/mc13783-adc.c b/drivers/hwmon/mc13783-adc.c index 78fe8759d2a9..825b922a3f92 100644 --- a/drivers/hwmon/mc13783-adc.c +++ b/drivers/hwmon/mc13783-adc.c @@ -1,21 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Driver for the ADC on Freescale Semiconductor MC13783 and MC13892 PMICs. * * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. * Copyright (C) 2009 Sascha Hauer, Pengutronix - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 51 - * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include From 39ea6ea325dcd05daf65f3d9d8d205a55683668f Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 6 Sep 2018 15:59:50 -0300 Subject: [PATCH 10/46] hwmon: (sht15) Switch to SPDX identifier Adopt the SPDX license identifier headers to ease license compliance management. Signed-off-by: Fabio Estevam Signed-off-by: Guenter Roeck --- drivers/hwmon/sht15.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c index 2be77752cd56..c878242f3486 100644 --- a/drivers/hwmon/sht15.c +++ b/drivers/hwmon/sht15.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * sht15.c - support for the SHT15 Temperature and Humidity Sensor * @@ -9,10 +10,6 @@ * * Copyright (c) 2007 Wouter Horre * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * * For further information, see the Documentation/hwmon/sht15 file. */ From a54ca77a98a311bf0488fd9004c5b691454ff917 Mon Sep 17 00:00:00 2001 From: Kun Yi Date: Tue, 11 Sep 2018 13:25:58 -0700 Subject: [PATCH 11/46] hwmon: (lm75) Add MAX31725/6 support MAX31725/MAX31726 are local temperature sensors with +/- 0.5 degree Celsius accuracy and 16-bit (0.00390625 degrees Celsius) resolution. They have a register mapping and encoding compatible with the lm75 series drivers. Address scan and extended temperature range are not supported by this patch. Tested on real hardware and verified temperature readings are correct. Signed-off-by: Kun Yi Signed-off-by: Guenter Roeck --- Documentation/hwmon/lm75 | 6 +++--- drivers/hwmon/lm75.c | 18 ++++++++++++++++-- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/Documentation/hwmon/lm75 b/Documentation/hwmon/lm75 index ac95edfcd907..2f1120f88c16 100644 --- a/Documentation/hwmon/lm75 +++ b/Documentation/hwmon/lm75 @@ -17,8 +17,8 @@ Supported chips: Addresses scanned: none Datasheet: Publicly available at the Maxim website http://www.maximintegrated.com/ - * Maxim MAX6625, MAX6626 - Prefixes: 'max6625', 'max6626' + * Maxim MAX6625, MAX6626, MAX31725, MAX31726 + Prefixes: 'max6625', 'max6626', 'max31725', 'max31726' Addresses scanned: none Datasheet: Publicly available at the Maxim website http://www.maxim-ic.com/ @@ -86,7 +86,7 @@ The LM75 is essentially an industry standard; there may be other LM75 clones not listed here, with or without various enhancements, that are supported. The clones are not detected by the driver, unless they reproduce the exact register tricks of the original LM75, and must -therefore be instantiated explicitly. Higher resolution up to 12-bit +therefore be instantiated explicitly. Higher resolution up to 16-bit is supported by this driver, other specific enhancements are not. The LM77 is not supported, contrary to what we pretended for a long time. diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c index 49f4b33a5685..e6e5a1080f09 100644 --- a/drivers/hwmon/lm75.c +++ b/drivers/hwmon/lm75.c @@ -47,6 +47,7 @@ enum lm75_type { /* keep sorted in alphabetical order */ lm75b, max6625, max6626, + max31725, mcp980x, stds75, tcn75, @@ -64,7 +65,6 @@ enum lm75_type { /* keep sorted in alphabetical order */ static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; - /* The LM75 registers */ #define LM75_REG_TEMP 0x00 #define LM75_REG_CONF 0x01 @@ -76,7 +76,7 @@ struct lm75_data { struct i2c_client *client; struct regmap *regmap; u8 orig_conf; - u8 resolution; /* In bits, between 9 and 12 */ + u8 resolution; /* In bits, between 9 and 16 */ u8 resolution_limits; unsigned int sample_time; /* In ms */ }; @@ -339,6 +339,10 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id) data->resolution_limits = 9; data->sample_time = MSEC_PER_SEC / 4; break; + case max31725: + data->resolution = 16; + data->sample_time = MSEC_PER_SEC / 8; + break; case tcn75: data->resolution = 9; data->sample_time = MSEC_PER_SEC / 8; @@ -415,6 +419,8 @@ static const struct i2c_device_id lm75_ids[] = { { "lm75b", lm75b, }, { "max6625", max6625, }, { "max6626", max6626, }, + { "max31725", max31725, }, + { "max31726", max31725, }, { "mcp980x", mcp980x, }, { "stds75", stds75, }, { "tcn75", tcn75, }, @@ -471,6 +477,14 @@ static const struct of_device_id lm75_of_match[] = { .compatible = "maxim,max6626", .data = (void *)max6626 }, + { + .compatible = "maxim,max31725", + .data = (void *)max31725 + }, + { + .compatible = "maxim,max31726", + .data = (void *)max31725 + }, { .compatible = "maxim,mcp980x", .data = (void *)mcp980x From 2738b767be548d6397a74141c18182fddd8abfc3 Mon Sep 17 00:00:00 2001 From: zhong jiang Date: Tue, 11 Sep 2018 20:03:37 +0800 Subject: [PATCH 12/46] hwmon: (asus_atk0110) Use PTR_ERR_OR_ZERO instead of reimplementing its function PTR_ERR_OR_ZERO has implemented the same function. We prefer to use inlined function rather than code-opened implementation. Signed-off-by: zhong jiang Signed-off-by: Guenter Roeck --- drivers/hwmon/asus_atk0110.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/hwmon/asus_atk0110.c b/drivers/hwmon/asus_atk0110.c index a6636fe42189..a7cf00885c5d 100644 --- a/drivers/hwmon/asus_atk0110.c +++ b/drivers/hwmon/asus_atk0110.c @@ -1210,10 +1210,8 @@ static int atk_register_hwmon(struct atk_data *data) data->hwmon_dev = hwmon_device_register_with_groups(dev, "atk0110", data, data->attr_groups); - if (IS_ERR(data->hwmon_dev)) - return PTR_ERR(data->hwmon_dev); - return 0; + return PTR_ERR_OR_ZERO(data->hwmon_dev); } static int atk_probe_if(struct atk_data *data) From a31796c30e423f58d266df30a9bbf321fc071b30 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Sat, 15 Sep 2018 17:05:07 -0700 Subject: [PATCH 13/46] hwmon: (scmi) Remove redundant pointer check Clang warns when the address of a pointer is used in a boolean context as it will always return true. drivers/hwmon/scmi-hwmon.c:59:24: warning: address of array 'sensor->name' will always evaluate to 'true' [-Wpointer-bool-conversion] if (sensor && sensor->name) ~~ ~~~~~~~~^~~~ 1 warning generated. Remove the check as it isn't doing anything currently; if validation of the contents of the data structure was intended by the original author (since this line has been present from the first version of this driver), it can be added in a follow-up patch. Reported-by: Nick Desaulniers Signed-off-by: Nathan Chancellor Signed-off-by: Guenter Roeck --- drivers/hwmon/scmi-hwmon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/scmi-hwmon.c b/drivers/hwmon/scmi-hwmon.c index 91976b6ca300..2e005edee0c9 100644 --- a/drivers/hwmon/scmi-hwmon.c +++ b/drivers/hwmon/scmi-hwmon.c @@ -56,7 +56,7 @@ scmi_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type, const struct scmi_sensors *scmi_sensors = drvdata; sensor = *(scmi_sensors->info[type] + channel); - if (sensor && sensor->name) + if (sensor) return S_IRUGO; return 0; From 0665a1d6231207c363dd6068611121e76346f99f Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sun, 16 Sep 2018 07:45:21 -0700 Subject: [PATCH 14/46] hwmon: (lm92) Fix whitespace issues Smatch complains: drivers/hwmon/lm92.c:209 set_temp_hyst() warn: inconsistent indenting While at it, fix various other whitespace issues reported by checkpatch (double empty lines, missing empty lines, whitespace at empty line). Signed-off-by: Guenter Roeck --- drivers/hwmon/lm92.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/hwmon/lm92.c b/drivers/hwmon/lm92.c index d40fe5122e94..e7333f8e185c 100644 --- a/drivers/hwmon/lm92.c +++ b/drivers/hwmon/lm92.c @@ -127,8 +127,8 @@ static struct lm92_data *lm92_update_device(struct device *dev) mutex_lock(&data->update_lock); - if (time_after(jiffies, data->last_updated + HZ) - || !data->valid) { + if (time_after(jiffies, data->last_updated + HZ) || + !data->valid) { dev_dbg(&client->dev, "Updating lm92 data\n"); for (i = 0; i < t_num_regs; i++) { data->temp[i] = @@ -153,7 +153,7 @@ static ssize_t show_temp(struct device *dev, struct device_attribute *devattr, } static ssize_t set_temp(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) + const char *buf, size_t count) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct lm92_data *data = dev_get_drvdata(dev); @@ -161,7 +161,7 @@ static ssize_t set_temp(struct device *dev, struct device_attribute *devattr, int nr = attr->index; long val; int err; - + err = kstrtol(buf, 10, &val); if (err) return err; @@ -178,6 +178,7 @@ static ssize_t show_temp_hyst(struct device *dev, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct lm92_data *data = lm92_update_device(dev); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[attr->index]) - TEMP_FROM_REG(data->temp[t_hyst])); } @@ -186,6 +187,7 @@ static ssize_t temp1_min_hyst_show(struct device *dev, struct device_attribute *attr, char *buf) { struct lm92_data *data = lm92_update_device(dev); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[t_min]) + TEMP_FROM_REG(data->temp[t_hyst])); } @@ -206,7 +208,7 @@ static ssize_t set_temp_hyst(struct device *dev, val = clamp_val(val, -120000, 220000); mutex_lock(&data->update_lock); - data->temp[t_hyst] = + data->temp[t_hyst] = TEMP_TO_REG(TEMP_FROM_REG(data->temp[attr->index]) - val); i2c_smbus_write_word_swapped(client, LM92_REG_TEMP_HYST, data->temp[t_hyst]); @@ -218,6 +220,7 @@ static ssize_t alarms_show(struct device *dev, struct device_attribute *attr, char *buf) { struct lm92_data *data = lm92_update_device(dev); + return sprintf(buf, "%d\n", ALARMS_FROM_REG(data->temp[t_input])); } @@ -324,7 +327,6 @@ static int lm92_probe(struct i2c_client *new_client, return PTR_ERR_OR_ZERO(hwmon_dev); } - /* * Module and driver stuff */ From 61b6c66a8f740b5025ac49ddf1c2e29091a1274e Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 17 Sep 2018 09:24:11 -0700 Subject: [PATCH 15/46] hwmon: (nct6775) Only display fan speed tolerance conditionally A fan speed tolerance only makes sense if a fan target speed has been configured in the first place. Otherwise we get odd output such as fan1_target:0 fan1_tolerance:337500 Only display values other than 0 if a fan target speed has been configured. Signed-off-by: Guenter Roeck --- drivers/hwmon/nct6775.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c index 2b493d0682af..2a9fc8c9fb9e 100644 --- a/drivers/hwmon/nct6775.c +++ b/drivers/hwmon/nct6775.c @@ -2847,6 +2847,8 @@ store_temp_tolerance(struct device *dev, struct device_attribute *attr, * Fan speed tolerance is a tricky beast, since the associated register is * a tick counter, but the value is reported and configured as rpm. * Compute resulting low and high rpm values and report the difference. + * A fan speed tolerance only makes sense if a fan target speed has been + * configured, so only display values other than 0 if that is the case. */ static ssize_t show_speed_tolerance(struct device *dev, struct device_attribute *attr, @@ -2855,19 +2857,23 @@ show_speed_tolerance(struct device *dev, struct device_attribute *attr, struct nct6775_data *data = nct6775_update_device(dev); struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); int nr = sattr->index; - int low = data->target_speed[nr] - data->target_speed_tolerance[nr]; - int high = data->target_speed[nr] + data->target_speed_tolerance[nr]; - int tolerance; + int target = data->target_speed[nr]; + int tolerance = 0; - if (low <= 0) - low = 1; - if (high > 0xffff) - high = 0xffff; - if (high < low) - high = low; + if (target) { + int low = target - data->target_speed_tolerance[nr]; + int high = target + data->target_speed_tolerance[nr]; - tolerance = (fan_from_reg16(low, data->fan_div[nr]) - - fan_from_reg16(high, data->fan_div[nr])) / 2; + if (low <= 0) + low = 1; + if (high > 0xffff) + high = 0xffff; + if (high < low) + high = low; + + tolerance = (fan_from_reg16(low, data->fan_div[nr]) + - fan_from_reg16(high, data->fan_div[nr])) / 2; + } return sprintf(buf, "%d\n", tolerance); } From 0085a5108de17dc0bfd97abb2ab3354f5c7be08e Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 18 Sep 2018 09:31:21 -0700 Subject: [PATCH 16/46] hwmon: (nct6775) Rename configuration register variables Use variable names from chip datasheets (crXX) instead of regval_XX for configuration register variables. This is shorter and, together with subsequent changes, makes the code easier to read. No functional change. Signed-off-by: Guenter Roeck --- drivers/hwmon/nct6775.c | 42 ++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c index 2a9fc8c9fb9e..7a3a1d59553e 100644 --- a/drivers/hwmon/nct6775.c +++ b/drivers/hwmon/nct6775.c @@ -3497,7 +3497,7 @@ nct6775_check_fan_inputs(struct nct6775_data *data) pwm3pin = regval & 0x08; } else { /* NCT6779D, NCT6791D, NCT6792D, NCT6793D, NCT6795D, NCT6796D */ - int regval_1b, regval_2a, regval_2f; + int cr1b, cr2a, cr2f; bool dsw_en; regval = superio_inb(sioreg, 0x1c); @@ -3520,20 +3520,20 @@ nct6775_check_fan_inputs(struct nct6775_data *data) case nct6793: case nct6795: case nct6796: - regval_1b = superio_inb(sioreg, 0x1b); - regval_2a = superio_inb(sioreg, 0x2a); - regval_2f = superio_inb(sioreg, 0x2f); - dsw_en = regval_2f & BIT(3); + cr1b = superio_inb(sioreg, 0x1b); + cr2a = superio_inb(sioreg, 0x2a); + cr2f = superio_inb(sioreg, 0x2f); + dsw_en = cr2f & BIT(3); if (!pwm5pin) pwm5pin = regval & BIT(7); if (!fan5pin) - fan5pin = regval_1b & BIT(5); + fan5pin = cr1b & BIT(5); superio_select(sioreg, NCT6775_LD_12); if (data->kind != nct6796) { - int regval_eb = superio_inb(sioreg, 0xeb); + int creb = superio_inb(sioreg, 0xeb); if (!dsw_en) { fan6pin = regval & BIT(1); @@ -3541,33 +3541,33 @@ nct6775_check_fan_inputs(struct nct6775_data *data) } if (!fan5pin) - fan5pin = regval_eb & BIT(5); + fan5pin = creb & BIT(5); if (!pwm5pin) - pwm5pin = (regval_eb & BIT(4)) && - !(regval_2a & BIT(0)); + pwm5pin = (creb & BIT(4)) && + !(cr2a & BIT(0)); if (!fan6pin) - fan6pin = regval_eb & BIT(3); + fan6pin = creb & BIT(3); if (!pwm6pin) - pwm6pin = regval_eb & BIT(2); + pwm6pin = creb & BIT(2); } if (data->kind == nct6795 || data->kind == nct6796) { - int regval_ed = superio_inb(sioreg, 0xed); + int cred = superio_inb(sioreg, 0xed); if (!fan6pin) - fan6pin = (regval_2a & BIT(4)) && - (!dsw_en || (regval_ed & BIT(4))); + fan6pin = (cr2a & BIT(4)) && + (!dsw_en || (cred & BIT(4))); if (!pwm6pin) - pwm6pin = (regval_2a & BIT(3)) && - (regval_ed & BIT(2)); + pwm6pin = (cr2a & BIT(3)) && + (cred & BIT(2)); } if (data->kind == nct6796) { - int regval_1d = superio_inb(sioreg, 0x1d); - int regval_2b = superio_inb(sioreg, 0x2b); + int cr1d = superio_inb(sioreg, 0x1d); + int cr2b = superio_inb(sioreg, 0x2b); - fan7pin = !(regval_2b & BIT(2)); - pwm7pin = !(regval_1d & (BIT(2) | BIT(3))); + fan7pin = !(cr2b & BIT(2)); + pwm7pin = !(cr1d & (BIT(2) | BIT(3))); } break; From a6c54f2cde3c80b7bc0d247fc9d161bad64b53a9 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 18 Sep 2018 09:34:06 -0700 Subject: [PATCH 17/46] hwmon: (nct6775) Replace 'regval' with variables named after config registers Using variables named after configuration registers makes it more obvious which configuration register value is used, especially if more than one configuration register value is used to determine a configuration detail. No functional change. Signed-off-by: Guenter Roeck --- drivers/hwmon/nct6775.c | 43 ++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c index 7a3a1d59553e..bd1c4772994a 100644 --- a/drivers/hwmon/nct6775.c +++ b/drivers/hwmon/nct6775.c @@ -3436,7 +3436,8 @@ nct6775_check_fan_inputs(struct nct6775_data *data) bool pwm3pin = false, pwm4pin = false, pwm5pin = false; bool pwm6pin = false, pwm7pin = false; int sioreg = data->sioreg; - int regval; + int cr24; + int cr2c; /* Store SIO_REG_ENABLE for use during resume */ superio_select(sioreg, NCT6775_LD_HWM); @@ -3444,10 +3445,10 @@ nct6775_check_fan_inputs(struct nct6775_data *data) /* fan4 and fan5 share some pins with the GPIO and serial flash */ if (data->kind == nct6775) { - regval = superio_inb(sioreg, 0x2c); + cr2c = superio_inb(sioreg, 0x2c); - fan3pin = regval & BIT(6); - pwm3pin = regval & BIT(7); + fan3pin = cr2c & BIT(6); + pwm3pin = cr2c & BIT(7); /* On NCT6775, fan4 shares pins with the fdc interface */ fan4pin = !(superio_inb(sioreg, 0x2A) & 0x80); @@ -3492,30 +3493,32 @@ nct6775_check_fan_inputs(struct nct6775_data *data) fan4min = fan4pin; pwm3pin = fan3pin; } else if (data->kind == nct6106) { - regval = superio_inb(sioreg, 0x24); - fan3pin = !(regval & 0x80); - pwm3pin = regval & 0x08; + cr24 = superio_inb(sioreg, 0x24); + fan3pin = !(cr24 & 0x80); + pwm3pin = cr24 & 0x08; } else { /* NCT6779D, NCT6791D, NCT6792D, NCT6793D, NCT6795D, NCT6796D */ int cr1b, cr2a, cr2f; + int cr1c; + int cr2d; bool dsw_en; - regval = superio_inb(sioreg, 0x1c); + cr1c = superio_inb(sioreg, 0x1c); - fan3pin = !(regval & BIT(5)); - fan4pin = !(regval & BIT(6)); - fan5pin = !(regval & BIT(7)); + fan3pin = !(cr1c & BIT(5)); + fan4pin = !(cr1c & BIT(6)); + fan5pin = !(cr1c & BIT(7)); - pwm3pin = !(regval & BIT(0)); - pwm4pin = !(regval & BIT(1)); - pwm5pin = !(regval & BIT(2)); + pwm3pin = !(cr1c & BIT(0)); + pwm4pin = !(cr1c & BIT(1)); + pwm5pin = !(cr1c & BIT(2)); - regval = superio_inb(sioreg, 0x2d); + cr2d = superio_inb(sioreg, 0x2d); switch (data->kind) { case nct6791: case nct6792: - fan6pin = regval & BIT(1); - pwm6pin = regval & BIT(0); + fan6pin = cr2d & BIT(1); + pwm6pin = cr2d & BIT(0); break; case nct6793: case nct6795: @@ -3526,7 +3529,7 @@ nct6775_check_fan_inputs(struct nct6775_data *data) dsw_en = cr2f & BIT(3); if (!pwm5pin) - pwm5pin = regval & BIT(7); + pwm5pin = cr2d & BIT(7); if (!fan5pin) fan5pin = cr1b & BIT(5); @@ -3536,8 +3539,8 @@ nct6775_check_fan_inputs(struct nct6775_data *data) int creb = superio_inb(sioreg, 0xeb); if (!dsw_en) { - fan6pin = regval & BIT(1); - pwm6pin = regval & BIT(0); + fan6pin = cr2d & BIT(1); + pwm6pin = cr2d & BIT(0); } if (!fan5pin) From 97ce6df46874331b46e279ae18bd07365e6cd586 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 18 Sep 2018 09:46:24 -0700 Subject: [PATCH 18/46] hwmon: (nct6775) Move config variable declarations and initializations Group configuration variable declarations and initialization together. While this results in reading more registers than necessary for a given chip, it improves code readability and simplifies extending the code. Signed-off-by: Guenter Roeck --- drivers/hwmon/nct6775.c | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c index bd1c4772994a..aa8a44608ca3 100644 --- a/drivers/hwmon/nct6775.c +++ b/drivers/hwmon/nct6775.c @@ -3436,8 +3436,6 @@ nct6775_check_fan_inputs(struct nct6775_data *data) bool pwm3pin = false, pwm4pin = false, pwm5pin = false; bool pwm6pin = false, pwm7pin = false; int sioreg = data->sioreg; - int cr24; - int cr2c; /* Store SIO_REG_ENABLE for use during resume */ superio_select(sioreg, NCT6775_LD_HWM); @@ -3445,7 +3443,7 @@ nct6775_check_fan_inputs(struct nct6775_data *data) /* fan4 and fan5 share some pins with the GPIO and serial flash */ if (data->kind == nct6775) { - cr2c = superio_inb(sioreg, 0x2c); + int cr2c = superio_inb(sioreg, 0x2c); fan3pin = cr2c & BIT(6); pwm3pin = cr2c & BIT(7); @@ -3493,17 +3491,20 @@ nct6775_check_fan_inputs(struct nct6775_data *data) fan4min = fan4pin; pwm3pin = fan3pin; } else if (data->kind == nct6106) { - cr24 = superio_inb(sioreg, 0x24); + int cr24 = superio_inb(sioreg, 0x24); + fan3pin = !(cr24 & 0x80); pwm3pin = cr24 & 0x08; } else { /* NCT6779D, NCT6791D, NCT6792D, NCT6793D, NCT6795D, NCT6796D */ - int cr1b, cr2a, cr2f; - int cr1c; - int cr2d; - bool dsw_en; - - cr1c = superio_inb(sioreg, 0x1c); + int cr1b = superio_inb(sioreg, 0x1b); + int cr1c = superio_inb(sioreg, 0x1c); + int cr1d = superio_inb(sioreg, 0x1d); + int cr2a = superio_inb(sioreg, 0x2a); + int cr2b = superio_inb(sioreg, 0x2b); + int cr2d = superio_inb(sioreg, 0x2d); + int cr2f = superio_inb(sioreg, 0x2f); + bool dsw_en = cr2f & BIT(3); fan3pin = !(cr1c & BIT(5)); fan4pin = !(cr1c & BIT(6)); @@ -3513,7 +3514,6 @@ nct6775_check_fan_inputs(struct nct6775_data *data) pwm4pin = !(cr1c & BIT(1)); pwm5pin = !(cr1c & BIT(2)); - cr2d = superio_inb(sioreg, 0x2d); switch (data->kind) { case nct6791: case nct6792: @@ -3523,11 +3523,6 @@ nct6775_check_fan_inputs(struct nct6775_data *data) case nct6793: case nct6795: case nct6796: - cr1b = superio_inb(sioreg, 0x1b); - cr2a = superio_inb(sioreg, 0x2a); - cr2f = superio_inb(sioreg, 0x2f); - dsw_en = cr2f & BIT(3); - if (!pwm5pin) pwm5pin = cr2d & BIT(7); @@ -3566,9 +3561,6 @@ nct6775_check_fan_inputs(struct nct6775_data *data) } if (data->kind == nct6796) { - int cr1d = superio_inb(sioreg, 0x1d); - int cr2b = superio_inb(sioreg, 0x2b); - fan7pin = !(cr2b & BIT(2)); pwm7pin = !(cr1d & (BIT(2) | BIT(3))); } From 2d888c5f22b49bba174e0343f8e4ddfb374fa816 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 18 Sep 2018 09:49:29 -0700 Subject: [PATCH 19/46] hwmon: (nct6775) Declare and initialize LDN specific config variables earlier Declare and initialize LDN / chip specific configuration variables earlier. This simplifies re-using the configuration variables for multiple chips and makes the code easier to read. Signed-off-by: Guenter Roeck --- drivers/hwmon/nct6775.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c index aa8a44608ca3..984b22d4999a 100644 --- a/drivers/hwmon/nct6775.c +++ b/drivers/hwmon/nct6775.c @@ -3505,6 +3505,12 @@ nct6775_check_fan_inputs(struct nct6775_data *data) int cr2d = superio_inb(sioreg, 0x2d); int cr2f = superio_inb(sioreg, 0x2f); bool dsw_en = cr2f & BIT(3); + int creb; + int cred; + + superio_select(sioreg, NCT6775_LD_12); + creb = superio_inb(sioreg, 0xeb); + cred = superio_inb(sioreg, 0xed); fan3pin = !(cr1c & BIT(5)); fan4pin = !(cr1c & BIT(6)); @@ -3529,10 +3535,7 @@ nct6775_check_fan_inputs(struct nct6775_data *data) if (!fan5pin) fan5pin = cr1b & BIT(5); - superio_select(sioreg, NCT6775_LD_12); if (data->kind != nct6796) { - int creb = superio_inb(sioreg, 0xeb); - if (!dsw_en) { fan6pin = cr2d & BIT(1); pwm6pin = cr2d & BIT(0); @@ -3550,8 +3553,6 @@ nct6775_check_fan_inputs(struct nct6775_data *data) } if (data->kind == nct6795 || data->kind == nct6796) { - int cred = superio_inb(sioreg, 0xed); - if (!fan6pin) fan6pin = (cr2a & BIT(4)) && (!dsw_en || (cred & BIT(4))); From a4e0a080cad80bb2522eb541868cc48c5e68ba72 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 18 Sep 2018 09:53:06 -0700 Subject: [PATCH 20/46] hwmon: (nct6775) Use logical or instead of if statements where possible Use boolean |= ; instead of if (!boolean) boolean = ; to assign values to boolean variables. No functional change. Signed-off-by: Guenter Roeck --- drivers/hwmon/nct6775.c | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c index 984b22d4999a..0f20f7c2a96b 100644 --- a/drivers/hwmon/nct6775.c +++ b/drivers/hwmon/nct6775.c @@ -3529,36 +3529,24 @@ nct6775_check_fan_inputs(struct nct6775_data *data) case nct6793: case nct6795: case nct6796: - if (!pwm5pin) - pwm5pin = cr2d & BIT(7); + pwm5pin |= cr2d & BIT(7); - if (!fan5pin) - fan5pin = cr1b & BIT(5); + fan5pin |= cr1b & BIT(5); if (data->kind != nct6796) { - if (!dsw_en) { - fan6pin = cr2d & BIT(1); - pwm6pin = cr2d & BIT(0); - } + fan6pin = !dsw_en && (cr2d & BIT(1)); + pwm6pin = !dsw_en && (cr2d & BIT(0)); - if (!fan5pin) - fan5pin = creb & BIT(5); - if (!pwm5pin) - pwm5pin = (creb & BIT(4)) && - !(cr2a & BIT(0)); - if (!fan6pin) - fan6pin = creb & BIT(3); - if (!pwm6pin) - pwm6pin = creb & BIT(2); + fan5pin |= creb & BIT(5); + pwm5pin |= (creb & BIT(4)) && !(cr2a & BIT(0)); + fan6pin |= creb & BIT(3); + pwm6pin |= creb & BIT(2); } if (data->kind == nct6795 || data->kind == nct6796) { - if (!fan6pin) - fan6pin = (cr2a & BIT(4)) && - (!dsw_en || (cred & BIT(4))); - if (!pwm6pin) - pwm6pin = (cr2a & BIT(3)) && - (cred & BIT(2)); + fan6pin |= (cr2a & BIT(4)) && + (!dsw_en || (cred & BIT(4))); + pwm6pin |= (cr2a & BIT(3)) && (cred & BIT(2)); } if (data->kind == nct6796) { From b2833f397dc1443b4db8c7b363d8dc41af805054 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 18 Sep 2018 09:57:11 -0700 Subject: [PATCH 21/46] hwmon: (nct6775) Improve instruction grouping When determining support for a given fan or pwm control, the code is easier to read if the necessary instructions are grouped together. No functional change. Signed-off-by: Guenter Roeck --- drivers/hwmon/nct6775.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c index 0f20f7c2a96b..0ad4bf0ab8be 100644 --- a/drivers/hwmon/nct6775.c +++ b/drivers/hwmon/nct6775.c @@ -3530,16 +3530,16 @@ nct6775_check_fan_inputs(struct nct6775_data *data) case nct6795: case nct6796: pwm5pin |= cr2d & BIT(7); - fan5pin |= cr1b & BIT(5); if (data->kind != nct6796) { - fan6pin = !dsw_en && (cr2d & BIT(1)); - pwm6pin = !dsw_en && (cr2d & BIT(0)); - fan5pin |= creb & BIT(5); pwm5pin |= (creb & BIT(4)) && !(cr2a & BIT(0)); + + fan6pin = !dsw_en && (cr2d & BIT(1)); fan6pin |= creb & BIT(3); + + pwm6pin = !dsw_en && (cr2d & BIT(0)); pwm6pin |= creb & BIT(2); } From 7dcdbdeb1b45b9071ad986bf20d8c2da6a057eb6 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 18 Sep 2018 10:52:55 -0700 Subject: [PATCH 22/46] hwmon: (nct6775) Fix fan6/pwm6 detection for NCT6792D Per datasheet, AUXFANIN3 (fan6) and AUXFANOUT3 (pwm6) are only connected if DSW_EN is false. Signed-off-by: Guenter Roeck --- drivers/hwmon/nct6775.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c index 0ad4bf0ab8be..6e11df697e6e 100644 --- a/drivers/hwmon/nct6775.c +++ b/drivers/hwmon/nct6775.c @@ -3522,10 +3522,13 @@ nct6775_check_fan_inputs(struct nct6775_data *data) switch (data->kind) { case nct6791: - case nct6792: fan6pin = cr2d & BIT(1); pwm6pin = cr2d & BIT(0); break; + case nct6792: + fan6pin = !dsw_en && (cr2d & BIT(1)); + pwm6pin = !dsw_en && (cr2d & BIT(0)); + break; case nct6793: case nct6795: case nct6796: From 2d99925a15b639026b67bd96419df6f9d760b212 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 18 Sep 2018 11:03:25 -0700 Subject: [PATCH 23/46] hwmon: (nct6775) Separate fan/pwm configuration detection for NCT6793D While detecting the configuration for multiple chips in one go reduces code size, it also increases code complexity. Separate chip detection to improve code readability. As first step, separate detection for NCT6793D. No functional change. Signed-off-by: Guenter Roeck --- drivers/hwmon/nct6775.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c index 6e11df697e6e..556b4cfacbd1 100644 --- a/drivers/hwmon/nct6775.c +++ b/drivers/hwmon/nct6775.c @@ -3530,6 +3530,17 @@ nct6775_check_fan_inputs(struct nct6775_data *data) pwm6pin = !dsw_en && (cr2d & BIT(0)); break; case nct6793: + fan5pin |= cr1b & BIT(5); + fan5pin |= creb & BIT(5); + + fan6pin = creb & BIT(3); + + pwm5pin |= cr2d & BIT(7); + pwm5pin |= (creb & BIT(4)) && !(cr2a & BIT(0)); + + pwm6pin = !dsw_en && (cr2d & BIT(0)); + pwm6pin |= creb & BIT(2); + break; case nct6795: case nct6796: pwm5pin |= cr2d & BIT(7); @@ -3546,11 +3557,9 @@ nct6775_check_fan_inputs(struct nct6775_data *data) pwm6pin |= creb & BIT(2); } - if (data->kind == nct6795 || data->kind == nct6796) { - fan6pin |= (cr2a & BIT(4)) && - (!dsw_en || (cred & BIT(4))); - pwm6pin |= (cr2a & BIT(3)) && (cred & BIT(2)); - } + fan6pin |= (cr2a & BIT(4)) && + (!dsw_en || (cred & BIT(4))); + pwm6pin |= (cr2a & BIT(3)) && (cred & BIT(2)); if (data->kind == nct6796) { fan7pin = !(cr2b & BIT(2)); From b75a806389901efa93c355238f787d922ba074b3 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 18 Sep 2018 11:18:30 -0700 Subject: [PATCH 24/46] hwmon: (nct6775) Separate fan/pwm configuration detection for NCT6795D Separate fan/pwm configuration detection for NCT6795D into separate case statement to make the code easier to read. No functional change. Signed-off-by: Guenter Roeck --- drivers/hwmon/nct6775.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c index 556b4cfacbd1..d9c2b934321d 100644 --- a/drivers/hwmon/nct6775.c +++ b/drivers/hwmon/nct6775.c @@ -3542,6 +3542,19 @@ nct6775_check_fan_inputs(struct nct6775_data *data) pwm6pin |= creb & BIT(2); break; case nct6795: + fan5pin |= cr1b & BIT(5); + fan5pin |= creb & BIT(5); + + fan6pin = (cr2a & BIT(4)) && + (!dsw_en || (cred & BIT(4))); + fan6pin |= creb & BIT(3); + + pwm5pin |= cr2d & BIT(7); + pwm5pin |= (creb & BIT(4)) && !(cr2a & BIT(0)); + + pwm6pin = (cr2a & BIT(3)) && (cred & BIT(2)); + pwm6pin |= creb & BIT(2); + break; case nct6796: pwm5pin |= cr2d & BIT(7); fan5pin |= cr1b & BIT(5); @@ -3561,10 +3574,8 @@ nct6775_check_fan_inputs(struct nct6775_data *data) (!dsw_en || (cred & BIT(4))); pwm6pin |= (cr2a & BIT(3)) && (cred & BIT(2)); - if (data->kind == nct6796) { - fan7pin = !(cr2b & BIT(2)); - pwm7pin = !(cr1d & (BIT(2) | BIT(3))); - } + fan7pin = !(cr2b & BIT(2)); + pwm7pin = !(cr1d & (BIT(2) | BIT(3))); break; default: /* NCT6779D */ From 3fdb06aff5044ad4ac3c2895d7869b85dadb23b6 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 18 Sep 2018 20:21:40 -0700 Subject: [PATCH 25/46] hwmon: (nct6796) Clean up and amend fan/pwm configuration for NCT6796D Now that everything is separated, clean up fan and pwm configuration for NCT6796D. While doing that, take the forgotten configuration register cre0 into account to determine if AUXFANIN2 (fan5) and AUXFANOUT2 (pwm5) are connected. Signed-off-by: Guenter Roeck --- drivers/hwmon/nct6775.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c index d9c2b934321d..c07414dd38dd 100644 --- a/drivers/hwmon/nct6775.c +++ b/drivers/hwmon/nct6775.c @@ -3505,10 +3505,12 @@ nct6775_check_fan_inputs(struct nct6775_data *data) int cr2d = superio_inb(sioreg, 0x2d); int cr2f = superio_inb(sioreg, 0x2f); bool dsw_en = cr2f & BIT(3); + int cre0; int creb; int cred; superio_select(sioreg, NCT6775_LD_12); + cre0 = superio_inb(sioreg, 0xe0); creb = superio_inb(sioreg, 0xeb); cred = superio_inb(sioreg, 0xed); @@ -3556,27 +3558,24 @@ nct6775_check_fan_inputs(struct nct6775_data *data) pwm6pin |= creb & BIT(2); break; case nct6796: - pwm5pin |= cr2d & BIT(7); fan5pin |= cr1b & BIT(5); + fan5pin |= (cre0 & BIT(3)) && !(cr1b & BIT(0)); + fan5pin |= creb & BIT(5); - if (data->kind != nct6796) { - fan5pin |= creb & BIT(5); - pwm5pin |= (creb & BIT(4)) && !(cr2a & BIT(0)); - - fan6pin = !dsw_en && (cr2d & BIT(1)); - fan6pin |= creb & BIT(3); - - pwm6pin = !dsw_en && (cr2d & BIT(0)); - pwm6pin |= creb & BIT(2); - } - - fan6pin |= (cr2a & BIT(4)) && + fan6pin = (cr2a & BIT(4)) && (!dsw_en || (cred & BIT(4))); - pwm6pin |= (cr2a & BIT(3)) && (cred & BIT(2)); + fan6pin |= creb & BIT(3); fan7pin = !(cr2b & BIT(2)); - pwm7pin = !(cr1d & (BIT(2) | BIT(3))); + pwm5pin |= cr2d & BIT(7); + pwm5pin |= (cre0 & BIT(4)) && !(cr1b & BIT(0)); + pwm5pin |= (creb & BIT(4)) && !(cr2a & BIT(0)); + + pwm6pin = (cr2a & BIT(3)) && (cred & BIT(2)); + pwm6pin |= creb & BIT(2); + + pwm7pin = !(cr1d & (BIT(2) | BIT(3))); break; default: /* NCT6779D */ break; From 3be8c9d103534fadc72b3e174613f37aa19fa423 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 19 Sep 2018 21:52:49 -0700 Subject: [PATCH 26/46] hwmon: (nct6775) Fix names of DIMM temperature sources For NCT6795D and NCT6796D, the DIMM temperature sources are named "Agent[01] Dimm [01]" per datasheet. Match names in datasheets to avoid confusion. Signed-off-by: Guenter Roeck --- drivers/hwmon/nct6775.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c index c07414dd38dd..77255f7da974 100644 --- a/drivers/hwmon/nct6775.c +++ b/drivers/hwmon/nct6775.c @@ -704,10 +704,10 @@ static const char *const nct6795_temp_label[] = { "PCH_CHIP_TEMP", "PCH_CPU_TEMP", "PCH_MCH_TEMP", - "PCH_DIM0_TEMP", - "PCH_DIM1_TEMP", - "PCH_DIM2_TEMP", - "PCH_DIM3_TEMP", + "Agent0 Dimm0", + "Agent0 Dimm1", + "Agent1 Dimm0", + "Agent1 Dimm1", "BYTE_TEMP0", "BYTE_TEMP1", "PECI Agent 0 Calibration", @@ -742,10 +742,10 @@ static const char *const nct6796_temp_label[] = { "PCH_CHIP_TEMP", "PCH_CPU_TEMP", "PCH_MCH_TEMP", - "PCH_DIM0_TEMP", - "PCH_DIM1_TEMP", - "PCH_DIM2_TEMP", - "PCH_DIM3_TEMP", + "Agent0 Dimm0", + "Agent0 Dimm1", + "Agent1 Dimm0", + "Agent1 Dimm1", "BYTE_TEMP0", "BYTE_TEMP1", "PECI Agent 0 Calibration", From e41da286a2fd9a5459d7a02203f776aef5e2bd8a Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 18 Sep 2018 20:48:29 -0700 Subject: [PATCH 27/46] hwmon: (nct6775) Add support for NCT6797D Add support for NCT6797D. With the exception of fan/pwm configuration registers, it is mostly compatible with NCT6795D. Signed-off-by: Guenter Roeck --- drivers/hwmon/nct6775.c | 45 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c index 77255f7da974..9569acf80e18 100644 --- a/drivers/hwmon/nct6775.c +++ b/drivers/hwmon/nct6775.c @@ -42,6 +42,7 @@ * nct6793d 15 6 6 2+6 0xd120 0xc1 0x5ca3 * nct6795d 14 6 6 2+6 0xd350 0xc1 0x5ca3 * nct6796d 14 7 7 2+6 0xd420 0xc1 0x5ca3 + * nct6797d 14 7 7 2+6 0xd450 0xc1 0x5ca3 * * #temp lists the number of monitored temperature sources (first value) plus * the number of directly connectable temperature sensors (second value). @@ -69,7 +70,7 @@ #define USE_ALTERNATE enum kinds { nct6106, nct6775, nct6776, nct6779, nct6791, nct6792, nct6793, - nct6795, nct6796 }; + nct6795, nct6796, nct6797 }; /* used to set data->name = nct6775_device_names[data->sio_kind] */ static const char * const nct6775_device_names[] = { @@ -82,6 +83,7 @@ static const char * const nct6775_device_names[] = { "nct6793", "nct6795", "nct6796", + "nct6797", }; static const char * const nct6775_sio_names[] __initconst = { @@ -94,6 +96,7 @@ static const char * const nct6775_sio_names[] __initconst = { "NCT6793D", "NCT6795D", "NCT6796D", + "NCT6797D", }; static unsigned short force_id; @@ -129,6 +132,7 @@ MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal"); #define SIO_NCT6793_ID 0xd120 #define SIO_NCT6795_ID 0xd350 #define SIO_NCT6796_ID 0xd420 +#define SIO_NCT6797_ID 0xd450 #define SIO_ID_MASK 0xFFF0 enum pwm_enable { off, manual, thermal_cruise, speed_cruise, sf3, sf4 }; @@ -504,7 +508,7 @@ static const s8 NCT6779_BEEP_BITS[] = { static const u16 NCT6779_REG_FAN[] = { 0x4c0, 0x4c2, 0x4c4, 0x4c6, 0x4c8, 0x4ca, 0x4ce }; static const u16 NCT6779_REG_FAN_PULSES[NUM_FAN] = { - 0x644, 0x645, 0x646, 0x647, 0x648, 0x649 }; + 0x644, 0x645, 0x646, 0x647, 0x648, 0x649, 0x64f }; static const u16 NCT6779_REG_CRITICAL_PWM_ENABLE[] = { 0x136, 0x236, 0x336, 0x836, 0x936, 0xa36, 0xb36 }; @@ -1288,6 +1292,7 @@ static bool is_word_sized(struct nct6775_data *data, u16 reg) case nct6793: case nct6795: case nct6796: + case nct6797: return reg == 0x150 || reg == 0x153 || reg == 0x155 || (reg & 0xfff0) == 0x4c0 || reg == 0x402 || @@ -1643,6 +1648,7 @@ static void nct6775_update_pwm_limits(struct device *dev) case nct6793: case nct6795: case nct6796: + case nct6797: reg = nct6775_read_value(data, data->REG_CRITICAL_PWM_ENABLE[i]); if (reg & data->CRITICAL_PWM_ENABLE_MASK) @@ -3077,6 +3083,7 @@ store_auto_pwm(struct device *dev, struct device_attribute *attr, case nct6793: case nct6795: case nct6796: + case nct6797: nct6775_write_value(data, data->REG_CRITICAL_PWM[nr], val); reg = nct6775_read_value(data, @@ -3496,7 +3503,11 @@ nct6775_check_fan_inputs(struct nct6775_data *data) fan3pin = !(cr24 & 0x80); pwm3pin = cr24 & 0x08; } else { - /* NCT6779D, NCT6791D, NCT6792D, NCT6793D, NCT6795D, NCT6796D */ + /* + * NCT6779D, NCT6791D, NCT6792D, NCT6793D, NCT6795D, NCT6796D, + * NCT6797D + */ + int cr1a = superio_inb(sioreg, 0x1a); int cr1b = superio_inb(sioreg, 0x1b); int cr1c = superio_inb(sioreg, 0x1c); int cr1d = superio_inb(sioreg, 0x1d); @@ -3505,6 +3516,7 @@ nct6775_check_fan_inputs(struct nct6775_data *data) int cr2d = superio_inb(sioreg, 0x2d); int cr2f = superio_inb(sioreg, 0x2f); bool dsw_en = cr2f & BIT(3); + bool ddr4_en = cr2f & BIT(4); int cre0; int creb; int cred; @@ -3577,6 +3589,23 @@ nct6775_check_fan_inputs(struct nct6775_data *data) pwm7pin = !(cr1d & (BIT(2) | BIT(3))); break; + case nct6797: + fan5pin |= !ddr4_en && (cr1b & BIT(5)); + fan5pin |= creb & BIT(5); + + fan6pin = cr2a & BIT(4); + fan6pin |= creb & BIT(3); + + fan7pin = cr1a & BIT(1); + + pwm5pin |= (creb & BIT(4)) && !(cr2a & BIT(0)); + pwm5pin |= !ddr4_en && (cr2d & BIT(7)); + + pwm6pin = creb & BIT(2); + pwm6pin |= cred & BIT(2); + + pwm7pin = cr1d & BIT(4); + break; default: /* NCT6779D */ break; } @@ -3954,8 +3983,10 @@ static int nct6775_probe(struct platform_device *pdev) case nct6793: case nct6795: case nct6796: + case nct6797: data->in_num = 15; - data->pwm_num = (data->kind == nct6796) ? 7 : 6; + data->pwm_num = (data->kind == nct6796 || + data->kind == nct6797) ? 7 : 6; data->auto_pwm_num = 4; data->has_fan_div = false; data->temp_fixed_num = 6; @@ -3989,6 +4020,7 @@ static int nct6775_probe(struct platform_device *pdev) data->virt_temp_mask = NCT6793_VIRT_TEMP_MASK; break; case nct6795: + case nct6797: data->temp_label = nct6795_temp_label; data->temp_mask = NCT6795_TEMP_MASK; data->virt_temp_mask = NCT6795_VIRT_TEMP_MASK; @@ -4267,6 +4299,7 @@ static int nct6775_probe(struct platform_device *pdev) case nct6793: case nct6795: case nct6796: + case nct6797: break; } @@ -4302,6 +4335,7 @@ static int nct6775_probe(struct platform_device *pdev) case nct6793: case nct6795: case nct6796: + case nct6797: tmp |= 0x7e; break; } @@ -4504,6 +4538,9 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data) case SIO_NCT6796_ID: sio_data->kind = nct6796; break; + case SIO_NCT6797_ID: + sio_data->kind = nct6797; + break; default: if (val != 0xffff) pr_debug("unsupported chip ID: 0x%04x\n", val); From 0599682b826ff7bbf9d5804fa37bcef36b0c9404 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 19 Sep 2018 20:26:16 -0700 Subject: [PATCH 28/46] hwmon: (nct6775) Add support for NCT6798D NCT6798D is, with the exception of fan and pwm channel configuration registers, similar to other chips of the series. One interesting difference is the chip ID, which is now extended to 13 bit (the 12-bit chip ID value overlaps with the chip ID of NCT6797D). Signed-off-by: Guenter Roeck --- drivers/hwmon/nct6775.c | 84 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 4 deletions(-) diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c index 9569acf80e18..c3040079b1cb 100644 --- a/drivers/hwmon/nct6775.c +++ b/drivers/hwmon/nct6775.c @@ -43,6 +43,9 @@ * nct6795d 14 6 6 2+6 0xd350 0xc1 0x5ca3 * nct6796d 14 7 7 2+6 0xd420 0xc1 0x5ca3 * nct6797d 14 7 7 2+6 0xd450 0xc1 0x5ca3 + * (0xd451) + * nct6798d 14 7 7 2+6 0xd458 0xc1 0x5ca3 + * (0xd459) * * #temp lists the number of monitored temperature sources (first value) plus * the number of directly connectable temperature sensors (second value). @@ -70,7 +73,7 @@ #define USE_ALTERNATE enum kinds { nct6106, nct6775, nct6776, nct6779, nct6791, nct6792, nct6793, - nct6795, nct6796, nct6797 }; + nct6795, nct6796, nct6797, nct6798 }; /* used to set data->name = nct6775_device_names[data->sio_kind] */ static const char * const nct6775_device_names[] = { @@ -84,6 +87,7 @@ static const char * const nct6775_device_names[] = { "nct6795", "nct6796", "nct6797", + "nct6798", }; static const char * const nct6775_sio_names[] __initconst = { @@ -97,6 +101,7 @@ static const char * const nct6775_sio_names[] __initconst = { "NCT6795D", "NCT6796D", "NCT6797D", + "NCT6798D", }; static unsigned short force_id; @@ -133,7 +138,8 @@ MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal"); #define SIO_NCT6795_ID 0xd350 #define SIO_NCT6796_ID 0xd420 #define SIO_NCT6797_ID 0xd450 -#define SIO_ID_MASK 0xFFF0 +#define SIO_NCT6798_ID 0xd458 +#define SIO_ID_MASK 0xFFF8 enum pwm_enable { off, manual, thermal_cruise, speed_cruise, sf3, sf4 }; @@ -761,6 +767,44 @@ static const char *const nct6796_temp_label[] = { #define NCT6796_TEMP_MASK 0xbfff0ffe #define NCT6796_VIRT_TEMP_MASK 0x80000c00 +static const char *const nct6798_temp_label[] = { + "", + "SYSTIN", + "CPUTIN", + "AUXTIN0", + "AUXTIN1", + "AUXTIN2", + "AUXTIN3", + "AUXTIN4", + "SMBUSMASTER 0", + "SMBUSMASTER 1", + "Virtual_TEMP", + "Virtual_TEMP", + "", + "", + "", + "", + "PECI Agent 0", + "PECI Agent 1", + "PCH_CHIP_CPU_MAX_TEMP", + "PCH_CHIP_TEMP", + "PCH_CPU_TEMP", + "PCH_MCH_TEMP", + "Agent0 Dimm0", + "Agent0 Dimm1", + "Agent1 Dimm0", + "Agent1 Dimm1", + "BYTE_TEMP0", + "BYTE_TEMP1", + "", + "", + "", + "Virtual_TEMP" +}; + +#define NCT6798_TEMP_MASK 0x8fff0ffe +#define NCT6798_VIRT_TEMP_MASK 0x80000c00 + /* NCT6102D/NCT6106D specific data */ #define NCT6106_REG_VBAT 0x318 @@ -1293,6 +1337,7 @@ static bool is_word_sized(struct nct6775_data *data, u16 reg) case nct6795: case nct6796: case nct6797: + case nct6798: return reg == 0x150 || reg == 0x153 || reg == 0x155 || (reg & 0xfff0) == 0x4c0 || reg == 0x402 || @@ -1649,6 +1694,7 @@ static void nct6775_update_pwm_limits(struct device *dev) case nct6795: case nct6796: case nct6797: + case nct6798: reg = nct6775_read_value(data, data->REG_CRITICAL_PWM_ENABLE[i]); if (reg & data->CRITICAL_PWM_ENABLE_MASK) @@ -3084,6 +3130,7 @@ store_auto_pwm(struct device *dev, struct device_attribute *attr, case nct6795: case nct6796: case nct6797: + case nct6798: nct6775_write_value(data, data->REG_CRITICAL_PWM[nr], val); reg = nct6775_read_value(data, @@ -3505,7 +3552,7 @@ nct6775_check_fan_inputs(struct nct6775_data *data) } else { /* * NCT6779D, NCT6791D, NCT6792D, NCT6793D, NCT6795D, NCT6796D, - * NCT6797D + * NCT6797D, NCT6798D */ int cr1a = superio_inb(sioreg, 0x1a); int cr1b = superio_inb(sioreg, 0x1b); @@ -3606,6 +3653,23 @@ nct6775_check_fan_inputs(struct nct6775_data *data) pwm7pin = cr1d & BIT(4); break; + case nct6798: + fan6pin = !(cr1b & BIT(0)) && (cre0 & BIT(3)); + fan6pin |= cr2a & BIT(4); + fan6pin |= creb & BIT(5); + + fan7pin = cr1b & BIT(5); + fan7pin |= !(cr2b & BIT(2)); + fan7pin |= creb & BIT(3); + + pwm6pin = !(cr1b & BIT(0)) && (cre0 & BIT(4)); + pwm6pin |= !(cred & BIT(2)) && (cr2a & BIT(3)); + pwm6pin |= (creb & BIT(4)) && !(cr2a & BIT(0)); + + pwm7pin = !(cr1d & (BIT(2) | BIT(3))); + pwm7pin |= cr2d & BIT(7); + pwm7pin |= creb & BIT(2); + break; default: /* NCT6779D */ break; } @@ -3984,9 +4048,11 @@ static int nct6775_probe(struct platform_device *pdev) case nct6795: case nct6796: case nct6797: + case nct6798: data->in_num = 15; data->pwm_num = (data->kind == nct6796 || - data->kind == nct6797) ? 7 : 6; + data->kind == nct6797 || + data->kind == nct6798) ? 7 : 6; data->auto_pwm_num = 4; data->has_fan_div = false; data->temp_fixed_num = 6; @@ -4030,6 +4096,11 @@ static int nct6775_probe(struct platform_device *pdev) data->temp_mask = NCT6796_TEMP_MASK; data->virt_temp_mask = NCT6796_VIRT_TEMP_MASK; break; + case nct6798: + data->temp_label = nct6798_temp_label; + data->temp_mask = NCT6798_TEMP_MASK; + data->virt_temp_mask = NCT6798_VIRT_TEMP_MASK; + break; } data->REG_CONFIG = NCT6775_REG_CONFIG; @@ -4300,6 +4371,7 @@ static int nct6775_probe(struct platform_device *pdev) case nct6795: case nct6796: case nct6797: + case nct6798: break; } @@ -4336,6 +4408,7 @@ static int nct6775_probe(struct platform_device *pdev) case nct6795: case nct6796: case nct6797: + case nct6798: tmp |= 0x7e; break; } @@ -4541,6 +4614,9 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data) case SIO_NCT6797_ID: sio_data->kind = nct6797; break; + case SIO_NCT6798_ID: + sio_data->kind = nct6798; + break; default: if (val != 0xffff) pr_debug("unsupported chip ID: 0x%04x\n", val); From 3045b5d61a60b606f89e3f26ae8e126a8f12b531 Mon Sep 17 00:00:00 2001 From: zhong jiang Date: Fri, 21 Sep 2018 09:30:03 +0800 Subject: [PATCH 29/46] hwmon: (scpi-hwmon) remove redundant continue The continue will not truely skip any code. hence it is safe to remove it. Signed-off-by: zhong jiang Signed-off-by: Guenter Roeck --- drivers/hwmon/scpi-hwmon.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/hwmon/scpi-hwmon.c b/drivers/hwmon/scpi-hwmon.c index 7e49da50bc69..111d521e2189 100644 --- a/drivers/hwmon/scpi-hwmon.c +++ b/drivers/hwmon/scpi-hwmon.c @@ -286,10 +286,8 @@ static int scpi_hwmon_probe(struct platform_device *pdev) * any thermal zones or if the thermal subsystem is * not configured. */ - if (IS_ERR(z)) { + if (IS_ERR(z)) devm_kfree(dev, zone); - continue; - } } return 0; From 9f67f7583e77fe5dc57aab3a6159c2642544eaad Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 21 Sep 2018 12:10:47 +0200 Subject: [PATCH 30/46] hwmon: (pwm-fan) Silence error on probe deferral Probe deferrals aren't actual errors, so silence the error message in case the PWM cannot yet be acquired. Signed-off-by: Thierry Reding Signed-off-by: Guenter Roeck --- drivers/hwmon/pwm-fan.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c index 7838af58f92d..936aaf76dd6e 100644 --- a/drivers/hwmon/pwm-fan.c +++ b/drivers/hwmon/pwm-fan.c @@ -221,8 +221,12 @@ static int pwm_fan_probe(struct platform_device *pdev) ctx->pwm = devm_of_pwm_get(&pdev->dev, pdev->dev.of_node, NULL); if (IS_ERR(ctx->pwm)) { - dev_err(&pdev->dev, "Could not get PWM\n"); - return PTR_ERR(ctx->pwm); + ret = PTR_ERR(ctx->pwm); + + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, "Could not get PWM: %d\n", ret); + + return ret; } platform_set_drvdata(pdev, ctx); From 95dcd64bc5a27080beaa344edfe5bdcca3d2e7dc Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 21 Sep 2018 12:10:48 +0200 Subject: [PATCH 31/46] hwmon: (pwm-fan) Set fan speed to 0 on suspend Technically this is not required because disabling the PWM should be enough. However, when support for atomic operations was implemented in the PWM subsystem, only actual changes to the PWM channel are applied during pwm_config(), which means that during after resume from suspend the old settings won't be applied. One possible solution is for the PWM driver to implement its own PM operations such that settings from before suspend get applied on resume. This has the disadvantage of completely ignoring any particular ordering requirements that PWM user drivers might have, so it is best to leave it up to the user drivers to apply the settings that they want at the appropriate time. Another way to solve this would be to read back the current state of the PWM at the time of resume. That way, in case the configuration was lost during suspend, applying the old settings in PWM user drivers would actually get them applied because they differ from the current settings. However, not all PWM drivers support reading the hardware state, and not all hardware may support it. The best workaround at this point seems to be to let PWM user drivers tell the PWM subsystem that the PWM is turned off by, in addition to disabling it, also setting the duty cycle to 0. This causes the resume operation to apply a configuration that is different from the current configuration, resulting in the proper state from before suspend getting restored. Signed-off-by: Thierry Reding Signed-off-by: Guenter Roeck --- drivers/hwmon/pwm-fan.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c index 936aaf76dd6e..7da6a160d45a 100644 --- a/drivers/hwmon/pwm-fan.c +++ b/drivers/hwmon/pwm-fan.c @@ -294,9 +294,19 @@ static int pwm_fan_remove(struct platform_device *pdev) static int pwm_fan_suspend(struct device *dev) { struct pwm_fan_ctx *ctx = dev_get_drvdata(dev); + struct pwm_args args; + int ret; + + pwm_get_args(ctx->pwm, &args); + + if (ctx->pwm_value) { + ret = pwm_config(ctx->pwm, 0, args.period); + if (ret < 0) + return ret; - if (ctx->pwm_value) pwm_disable(ctx->pwm); + } + return 0; } From cbc2a23803193f6cbc415bf79f3ec162f3cfc52a Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Fri, 28 Sep 2018 18:58:36 -0700 Subject: [PATCH 32/46] MAINTAINERS: Update PMBUS maintainer entry MAX20751 is a PMBUS driver, which has its own maintainer entry. On top of that, the entry for MAX20751 points to a non-existing file. Drop it. Add various missing driver documentation files. Reported-by: Joe Perches Cc: Joe Perches Signed-off-by: Guenter Roeck --- MAINTAINERS | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 48a65c3a4189..c0ca7f74f8e8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8846,13 +8846,6 @@ S: Maintained F: Documentation/hwmon/max16065 F: drivers/hwmon/max16065.c -MAX20751 HARDWARE MONITOR DRIVER -M: Guenter Roeck -L: linux-hwmon@vger.kernel.org -S: Maintained -F: Documentation/hwmon/max20751 -F: drivers/hwmon/max20751.c - MAX2175 SDR TUNER DRIVER M: Ramesh Shanmugasundaram L: linux-media@vger.kernel.org @@ -11574,7 +11567,26 @@ W: http://hwmon.wiki.kernel.org/ W: http://www.roeck-us.net/linux/drivers/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git S: Maintained +F: Documentation/devicetree/bindings/hwmon/ibm,cffps1.txt +F: Documentation/devicetree/bindings/hwmon/max31785.txt +F: Documentation/devicetree/bindings/hwmon/ltc2978.txt +F: Documentation/hwmon/adm1275 +F: Documentation/hwmon/ibm-cffps +F: Documentation/hwmon/ir35221 +F: Documentation/hwmon/lm25066 +F: Documentation/hwmon/ltc2978 +F: Documentation/hwmon/ltc3815 +F: Documentation/hwmon/max16064 +F: Documentation/hwmon/max20751 +F: Documentation/hwmon/max31785 +F: Documentation/hwmon/max34440 +F: Documentation/hwmon/max8688 F: Documentation/hwmon/pmbus +F: Documentation/hwmon/pmbus-core +F: Documentation/hwmon/tps40422 +F: Documentation/hwmon/ucd9000 +F: Documentation/hwmon/ucd9200 +F: Documentation/hwmon/zl6100 F: drivers/hwmon/pmbus/ F: include/linux/pmbus.h From c20217b30d675bb8ce3f55768c3cff629641a868 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Sat, 29 Sep 2018 14:44:05 -0700 Subject: [PATCH 33/46] hwmon: (ina3221) Add INA3221_CONFIG to volatile_table The MSB (15th bit) of INA3221_CONFIG is a self-clear reset bit. So this register should be added to the volatile_table of the regmap_config. Otherwise, we will see this bit is sticky in the regcache which might accidentally reset the chip when an actual write happens to the register. This might not be a severe bug for the current code line since there's no second place touching the INA3221_CONFIG except the reset routine in the probe(). Signed-off-by: Nicolin Chen Signed-off-by: Guenter Roeck --- drivers/hwmon/ina3221.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/ina3221.c b/drivers/hwmon/ina3221.c index e6b49500c52a..cfe65ff01051 100644 --- a/drivers/hwmon/ina3221.c +++ b/drivers/hwmon/ina3221.c @@ -353,7 +353,7 @@ static struct attribute *ina3221_attrs[] = { ATTRIBUTE_GROUPS(ina3221); static const struct regmap_range ina3221_yes_ranges[] = { - regmap_reg_range(INA3221_SHUNT1, INA3221_BUS3), + regmap_reg_range(INA3221_CONFIG, INA3221_BUS3), regmap_reg_range(INA3221_MASK_ENABLE, INA3221_MASK_ENABLE), }; From 791ebc9d34e9d212fc03742c31654b017d385926 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Sat, 29 Sep 2018 14:44:06 -0700 Subject: [PATCH 34/46] hwmon: (ina3221) Fix INA3221_CONFIG_MODE macros The three INA3221_CONFIG_MODE macros are not correctly defined here. The MODE3-1 bits are located at BIT 2-0 according to the datasheet. So this patch just fixes them by shifting all of them with a correct offset. However, this isn't a crital bug fix as the driver does not use any of them at this point. Signed-off-by: Nicolin Chen Signed-off-by: Guenter Roeck --- drivers/hwmon/ina3221.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/hwmon/ina3221.c b/drivers/hwmon/ina3221.c index cfe65ff01051..e0c4f4d83f4e 100644 --- a/drivers/hwmon/ina3221.c +++ b/drivers/hwmon/ina3221.c @@ -38,9 +38,9 @@ #define INA3221_WARN3 0x0c #define INA3221_MASK_ENABLE 0x0f -#define INA3221_CONFIG_MODE_SHUNT BIT(1) -#define INA3221_CONFIG_MODE_BUS BIT(2) -#define INA3221_CONFIG_MODE_CONTINUOUS BIT(3) +#define INA3221_CONFIG_MODE_SHUNT BIT(0) +#define INA3221_CONFIG_MODE_BUS BIT(1) +#define INA3221_CONFIG_MODE_CONTINUOUS BIT(2) #define INA3221_RSHUNT_DEFAULT 10000 From 59d608e152e582604e384beebbd607686e4265cf Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Sat, 29 Sep 2018 14:44:07 -0700 Subject: [PATCH 35/46] hwmon: (ina3221) Add suspend and resume functions Depending on the hardware design, an INA3221 chip might lose its power during system suspend/resume. So this patch adds a set of suspend and resume functions to cache the register values including config register value and limit settings. Signed-off-by: Nicolin Chen [groeck: Moved call to dev_set_drvdata()] Signed-off-by: Guenter Roeck --- drivers/hwmon/ina3221.c | 63 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/drivers/hwmon/ina3221.c b/drivers/hwmon/ina3221.c index e0c4f4d83f4e..1e38b4c43fbf 100644 --- a/drivers/hwmon/ina3221.c +++ b/drivers/hwmon/ina3221.c @@ -38,6 +38,8 @@ #define INA3221_WARN3 0x0c #define INA3221_MASK_ENABLE 0x0f +#define INA3221_CONFIG_MODE_MASK GENMASK(2, 0) +#define INA3221_CONFIG_MODE_POWERDOWN 0 #define INA3221_CONFIG_MODE_SHUNT BIT(0) #define INA3221_CONFIG_MODE_BUS BIT(1) #define INA3221_CONFIG_MODE_CONTINUOUS BIT(2) @@ -91,11 +93,13 @@ static const unsigned int register_channel[] = { * @regmap: Register map of the device * @fields: Register fields of the device * @shunt_resistors: Array of resistor values per channel + * @reg_config: Register value of INA3221_CONFIG */ struct ina3221_data { struct regmap *regmap; struct regmap_field *fields[F_MAX_FIELDS]; int shunt_resistors[INA3221_NUM_CHANNELS]; + u32 reg_config; }; static int ina3221_read_value(struct ina3221_data *ina, unsigned int reg, @@ -407,6 +411,8 @@ static int ina3221_probe(struct i2c_client *client, return ret; } + dev_set_drvdata(dev, ina); + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, ina, ina3221_groups); @@ -418,6 +424,62 @@ static int ina3221_probe(struct i2c_client *client, return 0; } +#ifdef CONFIG_PM +static int ina3221_suspend(struct device *dev) +{ + struct ina3221_data *ina = dev_get_drvdata(dev); + int ret; + + /* Save config register value and enable cache-only */ + ret = regmap_read(ina->regmap, INA3221_CONFIG, &ina->reg_config); + if (ret) + return ret; + + /* Set to power-down mode for power saving */ + ret = regmap_update_bits(ina->regmap, INA3221_CONFIG, + INA3221_CONFIG_MODE_MASK, + INA3221_CONFIG_MODE_POWERDOWN); + if (ret) + return ret; + + regcache_cache_only(ina->regmap, true); + regcache_mark_dirty(ina->regmap); + + return 0; +} + +static int ina3221_resume(struct device *dev) +{ + struct ina3221_data *ina = dev_get_drvdata(dev); + int ret; + + regcache_cache_only(ina->regmap, false); + + /* Software reset the chip */ + ret = regmap_field_write(ina->fields[F_RST], true); + if (ret) { + dev_err(dev, "Unable to reset device\n"); + return ret; + } + + /* Restore cached register values to hardware */ + ret = regcache_sync(ina->regmap); + if (ret) + return ret; + + /* Restore config register value to hardware */ + ret = regmap_write(ina->regmap, INA3221_CONFIG, ina->reg_config); + if (ret) + return ret; + + return 0; +} +#endif + +static const struct dev_pm_ops ina3221_pm = { + SET_SYSTEM_SLEEP_PM_OPS(ina3221_suspend, ina3221_resume) +}; + static const struct of_device_id ina3221_of_match_table[] = { { .compatible = "ti,ina3221", }, { /* sentinel */ } @@ -435,6 +497,7 @@ static struct i2c_driver ina3221_i2c_driver = { .driver = { .name = INA3221_DRIVER_NAME, .of_match_table = ina3221_of_match_table, + .pm = &ina3221_pm, }, .id_table = ina3221_ids, }; From 8b949c64d014c28b00472cb8eb9af78b95fc1d8c Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Mon, 1 Oct 2018 18:05:22 -0700 Subject: [PATCH 36/46] dt-bindings: hwmon: Add ina3221 documentation Texas Instruments INA3221 is a triple-channel shunt and bus voltage monitor. This patch adds a DT binding doc for it. Signed-off-by: Nicolin Chen Reviewed-by: Rob Herring Signed-off-by: Guenter Roeck --- .../devicetree/bindings/hwmon/ina3221.txt | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 Documentation/devicetree/bindings/hwmon/ina3221.txt diff --git a/Documentation/devicetree/bindings/hwmon/ina3221.txt b/Documentation/devicetree/bindings/hwmon/ina3221.txt new file mode 100644 index 000000000000..a7b25caa2b8e --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/ina3221.txt @@ -0,0 +1,44 @@ +Texas Instruments INA3221 Device Tree Bindings + +1) ina3221 node + Required properties: + - compatible: Must be "ti,ina3221" + - reg: I2C address + + Optional properties: + = The node contains optional child nodes for three channels = + = Each child node describes the information of input source = + + - #address-cells: Required only if a child node is present. Must be 1. + - #size-cells: Required only if a child node is present. Must be 0. + +2) child nodes + Required properties: + - reg: Must be 0, 1 or 2, corresponding to IN1, IN2 or IN3 port of INA3221 + + Optional properties: + - label: Name of the input source + - shunt-resistor-micro-ohms: Shunt resistor value in micro-Ohm + +Example: + +ina3221@40 { + compatible = "ti,ina3221"; + reg = <0x40>; + #address-cells = <1>; + #size-cells = <0>; + + input@0 { + reg = <0x0>; + status = "disabled"; + }; + input@1 { + reg = <0x1>; + shunt-resistor-micro-ohms = <5000>; + }; + input@2 { + reg = <0x2>; + label = "VDD_5V"; + shunt-resistor-micro-ohms = <5000>; + }; +}; From a9e9dd9c6de5d80c244fcb39086cfece54519150 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Mon, 1 Oct 2018 18:05:23 -0700 Subject: [PATCH 37/46] hwmon: (ina3221) Read channel input source info from DT An ina3221 chip has three input ports. Each port is used to measure the voltage and current of its input source. The DT binding now has defined bindings for their input sources, so the driver should read these information and handle accordingly. This patch adds a new structure of input source specific information including input source label, shunt resistor value and its connection status. It exposes these labels via in[123]_label sysfs nodes upon available, and also disables those channels where there are no input source being connected. Meanwhile, it also adds in[123]_enable sysfs nodes for users to get control of three channels, and returns -ENODATA code for any sensor read according to hwmon ABI. Signed-off-by: Nicolin Chen Signed-off-by: Guenter Roeck --- Documentation/hwmon/ina3221 | 2 + drivers/hwmon/ina3221.c | 235 +++++++++++++++++++++++++++++++++--- 2 files changed, 223 insertions(+), 14 deletions(-) diff --git a/Documentation/hwmon/ina3221 b/Documentation/hwmon/ina3221 index 0ff74854cb2e..4b82cbfb551c 100644 --- a/Documentation/hwmon/ina3221 +++ b/Documentation/hwmon/ina3221 @@ -21,6 +21,8 @@ and power are calculated host-side from these. Sysfs entries ------------- +in[123]_label Voltage channel labels +in[123]_enable Voltage channel enable controls in[123]_input Bus voltage(mV) channels curr[123]_input Current(mA) measurement channels shunt[123]_resistor Shunt resistance(uOhm) channels diff --git a/drivers/hwmon/ina3221.c b/drivers/hwmon/ina3221.c index 1e38b4c43fbf..88e65d2b4fa5 100644 --- a/drivers/hwmon/ina3221.c +++ b/drivers/hwmon/ina3221.c @@ -43,6 +43,7 @@ #define INA3221_CONFIG_MODE_SHUNT BIT(0) #define INA3221_CONFIG_MODE_BUS BIT(1) #define INA3221_CONFIG_MODE_CONTINUOUS BIT(2) +#define INA3221_CONFIG_CHx_EN(x) BIT(14 - (x)) #define INA3221_RSHUNT_DEFAULT 10000 @@ -77,6 +78,9 @@ enum ina3221_channels { }; static const unsigned int register_channel[] = { + [INA3221_BUS1] = INA3221_CHANNEL1, + [INA3221_BUS2] = INA3221_CHANNEL2, + [INA3221_BUS3] = INA3221_CHANNEL3, [INA3221_SHUNT1] = INA3221_CHANNEL1, [INA3221_SHUNT2] = INA3221_CHANNEL2, [INA3221_SHUNT3] = INA3221_CHANNEL3, @@ -88,20 +92,89 @@ static const unsigned int register_channel[] = { [INA3221_WARN3] = INA3221_CHANNEL3, }; +/** + * struct ina3221_input - channel input source specific information + * @label: label of channel input source + * @shunt_resistor: shunt resistor value of channel input source + * @disconnected: connection status of channel input source + */ +struct ina3221_input { + const char *label; + int shunt_resistor; + bool disconnected; +}; + /** * struct ina3221_data - device specific information * @regmap: Register map of the device * @fields: Register fields of the device - * @shunt_resistors: Array of resistor values per channel + * @inputs: Array of channel input source specific structures * @reg_config: Register value of INA3221_CONFIG */ struct ina3221_data { struct regmap *regmap; struct regmap_field *fields[F_MAX_FIELDS]; - int shunt_resistors[INA3221_NUM_CHANNELS]; + struct ina3221_input inputs[INA3221_NUM_CHANNELS]; u32 reg_config; }; +static inline bool ina3221_is_enabled(struct ina3221_data *ina, int channel) +{ + return ina->reg_config & INA3221_CONFIG_CHx_EN(channel); +} + +static ssize_t ina3221_show_label(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); + struct ina3221_data *ina = dev_get_drvdata(dev); + unsigned int channel = sd_attr->index; + struct ina3221_input *input = &ina->inputs[channel]; + + return snprintf(buf, PAGE_SIZE, "%s\n", input->label); +} + +static ssize_t ina3221_show_enable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); + struct ina3221_data *ina = dev_get_drvdata(dev); + unsigned int channel = sd_attr->index; + + return snprintf(buf, PAGE_SIZE, "%d\n", + ina3221_is_enabled(ina, channel)); +} + +static ssize_t ina3221_set_enable(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); + struct ina3221_data *ina = dev_get_drvdata(dev); + unsigned int channel = sd_attr->index; + u16 config, mask = INA3221_CONFIG_CHx_EN(channel); + bool enable; + int ret; + + ret = kstrtobool(buf, &enable); + if (ret) + return ret; + + config = enable ? mask : 0; + + /* Enable or disable the channel */ + ret = regmap_update_bits(ina->regmap, INA3221_CONFIG, mask, config); + if (ret) + return ret; + + /* Cache the latest config register value */ + ret = regmap_read(ina->regmap, INA3221_CONFIG, &ina->reg_config); + if (ret) + return ret; + + return count; +} + static int ina3221_read_value(struct ina3221_data *ina, unsigned int reg, int *val) { @@ -124,8 +197,13 @@ static ssize_t ina3221_show_bus_voltage(struct device *dev, struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); struct ina3221_data *ina = dev_get_drvdata(dev); unsigned int reg = sd_attr->index; + unsigned int channel = register_channel[reg]; int val, voltage_mv, ret; + /* No data for read-only attribute if channel is disabled */ + if (!attr->store && !ina3221_is_enabled(ina, channel)) + return -ENODATA; + ret = ina3221_read_value(ina, reg, &val); if (ret) return ret; @@ -142,8 +220,13 @@ static ssize_t ina3221_show_shunt_voltage(struct device *dev, struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); struct ina3221_data *ina = dev_get_drvdata(dev); unsigned int reg = sd_attr->index; + unsigned int channel = register_channel[reg]; int val, voltage_uv, ret; + /* No data for read-only attribute if channel is disabled */ + if (!attr->store && !ina3221_is_enabled(ina, channel)) + return -ENODATA; + ret = ina3221_read_value(ina, reg, &val); if (ret) return ret; @@ -159,9 +242,14 @@ static ssize_t ina3221_show_current(struct device *dev, struct ina3221_data *ina = dev_get_drvdata(dev); unsigned int reg = sd_attr->index; unsigned int channel = register_channel[reg]; - int resistance_uo = ina->shunt_resistors[channel]; + struct ina3221_input *input = &ina->inputs[channel]; + int resistance_uo = input->shunt_resistor; int val, current_ma, voltage_nv, ret; + /* No data for read-only attribute if channel is disabled */ + if (!attr->store && !ina3221_is_enabled(ina, channel)) + return -ENODATA; + ret = ina3221_read_value(ina, reg, &val); if (ret) return ret; @@ -180,7 +268,8 @@ static ssize_t ina3221_set_current(struct device *dev, struct ina3221_data *ina = dev_get_drvdata(dev); unsigned int reg = sd_attr->index; unsigned int channel = register_channel[reg]; - int resistance_uo = ina->shunt_resistors[channel]; + struct ina3221_input *input = &ina->inputs[channel]; + int resistance_uo = input->shunt_resistor; int val, current_ma, voltage_uv, ret; ret = kstrtoint(buf, 0, ¤t_ma); @@ -213,11 +302,9 @@ static ssize_t ina3221_show_shunt(struct device *dev, struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); struct ina3221_data *ina = dev_get_drvdata(dev); unsigned int channel = sd_attr->index; - unsigned int resistance_uo; + struct ina3221_input *input = &ina->inputs[channel]; - resistance_uo = ina->shunt_resistors[channel]; - - return snprintf(buf, PAGE_SIZE, "%d\n", resistance_uo); + return snprintf(buf, PAGE_SIZE, "%d\n", input->shunt_resistor); } static ssize_t ina3221_set_shunt(struct device *dev, @@ -227,6 +314,7 @@ static ssize_t ina3221_set_shunt(struct device *dev, struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); struct ina3221_data *ina = dev_get_drvdata(dev); unsigned int channel = sd_attr->index; + struct ina3221_input *input = &ina->inputs[channel]; int val; int ret; @@ -236,7 +324,7 @@ static ssize_t ina3221_set_shunt(struct device *dev, val = clamp_val(val, 1, INT_MAX); - ina->shunt_resistors[channel] = val; + input->shunt_resistor = val; return count; } @@ -257,6 +345,22 @@ static ssize_t ina3221_show_alert(struct device *dev, return snprintf(buf, PAGE_SIZE, "%d\n", regval); } +/* input channel label */ +static SENSOR_DEVICE_ATTR(in1_label, 0444, + ina3221_show_label, NULL, INA3221_CHANNEL1); +static SENSOR_DEVICE_ATTR(in2_label, 0444, + ina3221_show_label, NULL, INA3221_CHANNEL2); +static SENSOR_DEVICE_ATTR(in3_label, 0444, + ina3221_show_label, NULL, INA3221_CHANNEL3); + +/* voltage channel enable */ +static SENSOR_DEVICE_ATTR(in1_enable, 0644, + ina3221_show_enable, ina3221_set_enable, INA3221_CHANNEL1); +static SENSOR_DEVICE_ATTR(in2_enable, 0644, + ina3221_show_enable, ina3221_set_enable, INA3221_CHANNEL2); +static SENSOR_DEVICE_ATTR(in3_enable, 0644, + ina3221_show_enable, ina3221_set_enable, INA3221_CHANNEL3); + /* bus voltage */ static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, ina3221_show_bus_voltage, NULL, INA3221_BUS1); @@ -322,7 +426,9 @@ static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, ina3221_show_shunt_voltage, NULL, INA3221_SHUNT3); static struct attribute *ina3221_attrs[] = { - /* channel 1 */ + /* channel 1 -- make sure label at first */ + &sensor_dev_attr_in1_label.dev_attr.attr, + &sensor_dev_attr_in1_enable.dev_attr.attr, &sensor_dev_attr_in1_input.dev_attr.attr, &sensor_dev_attr_curr1_input.dev_attr.attr, &sensor_dev_attr_shunt1_resistor.dev_attr.attr, @@ -332,7 +438,9 @@ static struct attribute *ina3221_attrs[] = { &sensor_dev_attr_curr1_max_alarm.dev_attr.attr, &sensor_dev_attr_in4_input.dev_attr.attr, - /* channel 2 */ + /* channel 2 -- make sure label at first */ + &sensor_dev_attr_in2_label.dev_attr.attr, + &sensor_dev_attr_in2_enable.dev_attr.attr, &sensor_dev_attr_in2_input.dev_attr.attr, &sensor_dev_attr_curr2_input.dev_attr.attr, &sensor_dev_attr_shunt2_resistor.dev_attr.attr, @@ -342,7 +450,9 @@ static struct attribute *ina3221_attrs[] = { &sensor_dev_attr_curr2_max_alarm.dev_attr.attr, &sensor_dev_attr_in5_input.dev_attr.attr, - /* channel 3 */ + /* channel 3 -- make sure label at first */ + &sensor_dev_attr_in3_label.dev_attr.attr, + &sensor_dev_attr_in3_enable.dev_attr.attr, &sensor_dev_attr_in3_input.dev_attr.attr, &sensor_dev_attr_curr3_input.dev_attr.attr, &sensor_dev_attr_shunt3_resistor.dev_attr.attr, @@ -354,7 +464,30 @@ static struct attribute *ina3221_attrs[] = { NULL, }; -ATTRIBUTE_GROUPS(ina3221); + +static umode_t ina3221_attr_is_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + const int max_attrs = ARRAY_SIZE(ina3221_attrs) - 1; + const int num_attrs = max_attrs / INA3221_NUM_CHANNELS; + struct device *dev = kobj_to_dev(kobj); + struct ina3221_data *ina = dev_get_drvdata(dev); + enum ina3221_channels channel = n / num_attrs; + struct ina3221_input *input = &ina->inputs[channel]; + int index = n % num_attrs; + + /* Hide label node if label is not provided */ + if (index == 0 && !input->label) + return 0; + + return attr->mode; +} + +static const struct attribute_group ina3221_group = { + .is_visible = ina3221_attr_is_visible, + .attrs = ina3221_attrs, +}; +__ATTRIBUTE_GROUPS(ina3221); static const struct regmap_range ina3221_yes_ranges[] = { regmap_reg_range(INA3221_CONFIG, INA3221_BUS3), @@ -374,6 +507,60 @@ static const struct regmap_config ina3221_regmap_config = { .volatile_table = &ina3221_volatile_table, }; +static int ina3221_probe_child_from_dt(struct device *dev, + struct device_node *child, + struct ina3221_data *ina) +{ + struct ina3221_input *input; + u32 val; + int ret; + + ret = of_property_read_u32(child, "reg", &val); + if (ret) { + dev_err(dev, "missing reg property of %s\n", child->name); + return ret; + } else if (val > INA3221_CHANNEL3) { + dev_err(dev, "invalid reg %d of %s\n", val, child->name); + return ret; + } + + input = &ina->inputs[val]; + + /* Log the disconnected channel input */ + if (!of_device_is_available(child)) { + input->disconnected = true; + return 0; + } + + /* Save the connected input label if available */ + of_property_read_string(child, "label", &input->label); + + /* Overwrite default shunt resistor value optionally */ + if (!of_property_read_u32(child, "shunt-resistor-micro-ohms", &val)) + input->shunt_resistor = val; + + return 0; +} + +static int ina3221_probe_from_dt(struct device *dev, struct ina3221_data *ina) +{ + const struct device_node *np = dev->of_node; + struct device_node *child; + int ret; + + /* Compatible with non-DT platforms */ + if (!np) + return 0; + + for_each_child_of_node(np, child) { + ret = ina3221_probe_child_from_dt(dev, child, ina); + if (ret) + return ret; + } + + return 0; +} + static int ina3221_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -403,7 +590,13 @@ static int ina3221_probe(struct i2c_client *client, } for (i = 0; i < INA3221_NUM_CHANNELS; i++) - ina->shunt_resistors[i] = INA3221_RSHUNT_DEFAULT; + ina->inputs[i].shunt_resistor = INA3221_RSHUNT_DEFAULT; + + ret = ina3221_probe_from_dt(dev, ina); + if (ret) { + dev_err(dev, "Unable to probe from device tree\n"); + return ret; + } ret = regmap_field_write(ina->fields[F_RST], true); if (ret) { @@ -411,6 +604,20 @@ static int ina3221_probe(struct i2c_client *client, return ret; } + /* Sync config register after reset */ + ret = regmap_read(ina->regmap, INA3221_CONFIG, &ina->reg_config); + if (ret) + return ret; + + /* Disable channels if their inputs are disconnected */ + for (i = 0; i < INA3221_NUM_CHANNELS; i++) { + if (ina->inputs[i].disconnected) + ina->reg_config &= ~INA3221_CONFIG_CHx_EN(i); + } + ret = regmap_write(ina->regmap, INA3221_CONFIG, ina->reg_config); + if (ret) + return ret; + dev_set_drvdata(dev, ina); hwmon_dev = devm_hwmon_device_register_with_groups(dev, From ead21c77d709cfba593c42f4d71c09e469a19448 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 2 Oct 2018 23:10:47 +0200 Subject: [PATCH 38/46] hwmon: (ina3221) mark PM functions as __maybe_unused When CONFIG_PM_SLEEP is disabled, we get a warning about unused suspend/resume functions: drivers/hwmon/ina3221.c:451:12: error: 'ina3221_resume' defined but not used [-Werror=unused-function] static int ina3221_resume(struct device *dev) drivers/hwmon/ina3221.c:428:12: error: 'ina3221_suspend' defined but not used [-Werror=unused-function] static int ina3221_suspend(struct device *dev) Picking the correct #ifdef check is hard, so let's remove that check and instead mark the functions as __maybe_unused to let the compiler silently drop them instead. Fixes: 7de1ab9dac8e ("hwmon: (ina3221) Add suspend and resume functions") Signed-off-by: Arnd Bergmann Signed-off-by: Guenter Roeck --- drivers/hwmon/ina3221.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/hwmon/ina3221.c b/drivers/hwmon/ina3221.c index 88e65d2b4fa5..c3a497aed345 100644 --- a/drivers/hwmon/ina3221.c +++ b/drivers/hwmon/ina3221.c @@ -631,8 +631,7 @@ static int ina3221_probe(struct i2c_client *client, return 0; } -#ifdef CONFIG_PM -static int ina3221_suspend(struct device *dev) +static int __maybe_unused ina3221_suspend(struct device *dev) { struct ina3221_data *ina = dev_get_drvdata(dev); int ret; @@ -655,7 +654,7 @@ static int ina3221_suspend(struct device *dev) return 0; } -static int ina3221_resume(struct device *dev) +static int __maybe_unused ina3221_resume(struct device *dev) { struct ina3221_data *ina = dev_get_drvdata(dev); int ret; @@ -681,7 +680,6 @@ static int ina3221_resume(struct device *dev) return 0; } -#endif static const struct dev_pm_ops ina3221_pm = { SET_SYSTEM_SLEEP_PM_OPS(ina3221_suspend, ina3221_resume) From 68c0d69dee594e1488aebe12aa50fd79a3e5e5b5 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Fri, 5 Oct 2018 16:59:04 -0700 Subject: [PATCH 39/46] hwmon: (core) Add hwmon_in_enable attribute According to hwmon ABI, in%d_enable is a sysfs interface that allows user space to enable and disable the input sensor. So this patch just simply adds the attribute to the list. Signed-off-by: Nicolin Chen Signed-off-by: Guenter Roeck --- drivers/hwmon/hwmon.c | 1 + include/linux/hwmon.h | 2 ++ 2 files changed, 3 insertions(+) diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index 33d51281272b..ac1cdf88840f 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -356,6 +356,7 @@ static const char * const hwmon_in_attr_templates[] = { [hwmon_in_max_alarm] = "in%d_max_alarm", [hwmon_in_lcrit_alarm] = "in%d_lcrit_alarm", [hwmon_in_crit_alarm] = "in%d_crit_alarm", + [hwmon_in_enable] = "in%d_enable", }; static const char * const hwmon_curr_attr_templates[] = { diff --git a/include/linux/hwmon.h b/include/linux/hwmon.h index 9493d4a388db..99e0c1b0b5fb 100644 --- a/include/linux/hwmon.h +++ b/include/linux/hwmon.h @@ -118,6 +118,7 @@ enum hwmon_in_attributes { hwmon_in_max_alarm, hwmon_in_lcrit_alarm, hwmon_in_crit_alarm, + hwmon_in_enable, }; #define HWMON_I_INPUT BIT(hwmon_in_input) @@ -135,6 +136,7 @@ enum hwmon_in_attributes { #define HWMON_I_MAX_ALARM BIT(hwmon_in_max_alarm) #define HWMON_I_LCRIT_ALARM BIT(hwmon_in_lcrit_alarm) #define HWMON_I_CRIT_ALARM BIT(hwmon_in_crit_alarm) +#define HWMON_I_ENABLE BIT(hwmon_in_enable) enum hwmon_curr_attributes { hwmon_curr_input, From 8b9bf554dd530a40cd06a709edb8ec96b4de1d1a Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 9 Oct 2018 13:11:57 +0100 Subject: [PATCH 40/46] hwmon: (tmp421) make const array 'names' static The const array 'names' can be made static, saves populating it on the stack and will make it read-only. Signed-off-by: Colin Ian King Signed-off-by: Guenter Roeck --- drivers/hwmon/tmp421.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/tmp421.c b/drivers/hwmon/tmp421.c index e36399213324..8844c9565d2a 100644 --- a/drivers/hwmon/tmp421.c +++ b/drivers/hwmon/tmp421.c @@ -226,8 +226,10 @@ static int tmp421_detect(struct i2c_client *client, { enum chips kind; struct i2c_adapter *adapter = client->adapter; - const char * const names[] = { "TMP421", "TMP422", "TMP423", - "TMP441", "TMP442" }; + static const char * const names[] = { + "TMP421", "TMP422", "TMP423", + "TMP441", "TMP442" + }; int addr = client->addr; u8 reg; From a6e43263ed01fa617e0a0c13ea8a5f1538573380 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Mon, 8 Oct 2018 14:24:51 -0700 Subject: [PATCH 41/46] hwmon: (ina3221) Validate shunt resistor value from DT The input->shunt_resistor is int type while the value from DT is unsigned int. Meanwhile, a divide-by-zero error would happen if the value is 0. So this patch just simply validates the value. Signed-off-by: Nicolin Chen Signed-off-by: Guenter Roeck --- drivers/hwmon/ina3221.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/ina3221.c b/drivers/hwmon/ina3221.c index c3a497aed345..4f3ed24efe8e 100644 --- a/drivers/hwmon/ina3221.c +++ b/drivers/hwmon/ina3221.c @@ -536,8 +536,14 @@ static int ina3221_probe_child_from_dt(struct device *dev, of_property_read_string(child, "label", &input->label); /* Overwrite default shunt resistor value optionally */ - if (!of_property_read_u32(child, "shunt-resistor-micro-ohms", &val)) + if (!of_property_read_u32(child, "shunt-resistor-micro-ohms", &val)) { + if (val < 1 || val > INT_MAX) { + dev_err(dev, "invalid shunt resistor value %u of %s\n", + val, child->name); + return -EINVAL; + } input->shunt_resistor = val; + } return 0; } From f21c8e753b1dcb8f9e5b096db1f7f4e6fdfa7258 Mon Sep 17 00:00:00 2001 From: Kun Yi Date: Mon, 8 Oct 2018 14:49:25 -0700 Subject: [PATCH 42/46] hwmon: (npcm-750-pwm-fan) Change initial pwm target to 255 Change initial PWM target to 255 to prevent overheating, for example when BMC hangs in userspace or when userspace fan control application is not implemented yet. Signed-off-by: Kun Yi Signed-off-by: Guenter Roeck --- drivers/hwmon/npcm750-pwm-fan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/npcm750-pwm-fan.c b/drivers/hwmon/npcm750-pwm-fan.c index 96634fd54e0b..c0fab54c0094 100644 --- a/drivers/hwmon/npcm750-pwm-fan.c +++ b/drivers/hwmon/npcm750-pwm-fan.c @@ -52,7 +52,7 @@ /* Define the Counter Register, value = 100 for match 100% */ #define NPCM7XX_PWM_COUNTER_DEFAULT_NUM 255 -#define NPCM7XX_PWM_CMR_DEFAULT_NUM 127 +#define NPCM7XX_PWM_CMR_DEFAULT_NUM 255 #define NPCM7XX_PWM_CMR_MAX 255 /* default all PWM channels PRESCALE2 = 1 */ From d4b0166d28a8966ab6938d8bb7360c383b519256 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Mon, 8 Oct 2018 13:14:24 -0700 Subject: [PATCH 43/46] hwmon: (ina3221) Use _info API to register hwmon device The hwmon core has a newer API which abstracts most of common things in the core so as to simplify the hwmon device drivers. This patch implements this _info API to ina3221 hwmon driver. It also reduces the binary size: text data bss dec hex filename 5114 1712 0 6826 1aaa drivers/hwmon/ina3221_before.o 4456 440 0 4896 1320 drivers/hwmon/ina3221.o Signed-off-by: Nicolin Chen Signed-off-by: Guenter Roeck --- drivers/hwmon/ina3221.c | 520 ++++++++++++++++++---------------------- 1 file changed, 236 insertions(+), 284 deletions(-) diff --git a/drivers/hwmon/ina3221.c b/drivers/hwmon/ina3221.c index 4f3ed24efe8e..d61688f04594 100644 --- a/drivers/hwmon/ina3221.c +++ b/drivers/hwmon/ina3221.c @@ -77,21 +77,6 @@ enum ina3221_channels { INA3221_NUM_CHANNELS }; -static const unsigned int register_channel[] = { - [INA3221_BUS1] = INA3221_CHANNEL1, - [INA3221_BUS2] = INA3221_CHANNEL2, - [INA3221_BUS3] = INA3221_CHANNEL3, - [INA3221_SHUNT1] = INA3221_CHANNEL1, - [INA3221_SHUNT2] = INA3221_CHANNEL2, - [INA3221_SHUNT3] = INA3221_CHANNEL3, - [INA3221_CRIT1] = INA3221_CHANNEL1, - [INA3221_CRIT2] = INA3221_CHANNEL2, - [INA3221_CRIT3] = INA3221_CHANNEL3, - [INA3221_WARN1] = INA3221_CHANNEL1, - [INA3221_WARN2] = INA3221_CHANNEL2, - [INA3221_WARN3] = INA3221_CHANNEL3, -}; - /** * struct ina3221_input - channel input source specific information * @label: label of channel input source @@ -123,58 +108,6 @@ static inline bool ina3221_is_enabled(struct ina3221_data *ina, int channel) return ina->reg_config & INA3221_CONFIG_CHx_EN(channel); } -static ssize_t ina3221_show_label(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); - struct ina3221_data *ina = dev_get_drvdata(dev); - unsigned int channel = sd_attr->index; - struct ina3221_input *input = &ina->inputs[channel]; - - return snprintf(buf, PAGE_SIZE, "%s\n", input->label); -} - -static ssize_t ina3221_show_enable(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); - struct ina3221_data *ina = dev_get_drvdata(dev); - unsigned int channel = sd_attr->index; - - return snprintf(buf, PAGE_SIZE, "%d\n", - ina3221_is_enabled(ina, channel)); -} - -static ssize_t ina3221_set_enable(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); - struct ina3221_data *ina = dev_get_drvdata(dev); - unsigned int channel = sd_attr->index; - u16 config, mask = INA3221_CONFIG_CHx_EN(channel); - bool enable; - int ret; - - ret = kstrtobool(buf, &enable); - if (ret) - return ret; - - config = enable ? mask : 0; - - /* Enable or disable the channel */ - ret = regmap_update_bits(ina->regmap, INA3221_CONFIG, mask, config); - if (ret) - return ret; - - /* Cache the latest config register value */ - ret = regmap_read(ina->regmap, INA3221_CONFIG, &ina->reg_config); - if (ret) - return ret; - - return count; -} - static int ina3221_read_value(struct ina3221_data *ina, unsigned int reg, int *val) { @@ -190,94 +123,104 @@ static int ina3221_read_value(struct ina3221_data *ina, unsigned int reg, return 0; } -static ssize_t ina3221_show_bus_voltage(struct device *dev, - struct device_attribute *attr, - char *buf) +static const u8 ina3221_in_reg[] = { + INA3221_BUS1, + INA3221_BUS2, + INA3221_BUS3, + INA3221_SHUNT1, + INA3221_SHUNT2, + INA3221_SHUNT3, +}; + +static int ina3221_read_in(struct device *dev, u32 attr, int channel, long *val) { - struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); + const bool is_shunt = channel > INA3221_CHANNEL3; struct ina3221_data *ina = dev_get_drvdata(dev); - unsigned int reg = sd_attr->index; - unsigned int channel = register_channel[reg]; - int val, voltage_mv, ret; + u8 reg = ina3221_in_reg[channel]; + int regval, ret; - /* No data for read-only attribute if channel is disabled */ - if (!attr->store && !ina3221_is_enabled(ina, channel)) - return -ENODATA; + /* Translate shunt channel index to sensor channel index */ + channel %= INA3221_NUM_CHANNELS; - ret = ina3221_read_value(ina, reg, &val); - if (ret) - return ret; + switch (attr) { + case hwmon_in_input: + if (!ina3221_is_enabled(ina, channel)) + return -ENODATA; - voltage_mv = val * 8; + ret = ina3221_read_value(ina, reg, ®val); + if (ret) + return ret; - return snprintf(buf, PAGE_SIZE, "%d\n", voltage_mv); + /* + * Scale of shunt voltage (uV): LSB is 40uV + * Scale of bus voltage (mV): LSB is 8mV + */ + *val = regval * (is_shunt ? 40 : 8); + return 0; + case hwmon_in_enable: + *val = ina3221_is_enabled(ina, channel); + return 0; + default: + return -EOPNOTSUPP; + } } -static ssize_t ina3221_show_shunt_voltage(struct device *dev, - struct device_attribute *attr, - char *buf) +static const u8 ina3221_curr_reg[][INA3221_NUM_CHANNELS] = { + [hwmon_curr_input] = { INA3221_SHUNT1, INA3221_SHUNT2, INA3221_SHUNT3 }, + [hwmon_curr_max] = { INA3221_WARN1, INA3221_WARN2, INA3221_WARN3 }, + [hwmon_curr_crit] = { INA3221_CRIT1, INA3221_CRIT2, INA3221_CRIT3 }, + [hwmon_curr_max_alarm] = { F_WF1, F_WF2, F_WF3 }, + [hwmon_curr_crit_alarm] = { F_CF1, F_CF2, F_CF3 }, +}; + +static int ina3221_read_curr(struct device *dev, u32 attr, + int channel, long *val) { - struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); struct ina3221_data *ina = dev_get_drvdata(dev); - unsigned int reg = sd_attr->index; - unsigned int channel = register_channel[reg]; - int val, voltage_uv, ret; - - /* No data for read-only attribute if channel is disabled */ - if (!attr->store && !ina3221_is_enabled(ina, channel)) - return -ENODATA; - - ret = ina3221_read_value(ina, reg, &val); - if (ret) - return ret; - voltage_uv = val * 40; - - return snprintf(buf, PAGE_SIZE, "%d\n", voltage_uv); -} - -static ssize_t ina3221_show_current(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); - struct ina3221_data *ina = dev_get_drvdata(dev); - unsigned int reg = sd_attr->index; - unsigned int channel = register_channel[reg]; struct ina3221_input *input = &ina->inputs[channel]; int resistance_uo = input->shunt_resistor; - int val, current_ma, voltage_nv, ret; + u8 reg = ina3221_curr_reg[attr][channel]; + int regval, voltage_nv, ret; - /* No data for read-only attribute if channel is disabled */ - if (!attr->store && !ina3221_is_enabled(ina, channel)) - return -ENODATA; + switch (attr) { + case hwmon_curr_input: + if (!ina3221_is_enabled(ina, channel)) + return -ENODATA; + /* fall through */ + case hwmon_curr_crit: + case hwmon_curr_max: + ret = ina3221_read_value(ina, reg, ®val); + if (ret) + return ret; - ret = ina3221_read_value(ina, reg, &val); - if (ret) - return ret; - voltage_nv = val * 40000; - - current_ma = DIV_ROUND_CLOSEST(voltage_nv, resistance_uo); - - return snprintf(buf, PAGE_SIZE, "%d\n", current_ma); + /* Scale of shunt voltage: LSB is 40uV (40000nV) */ + voltage_nv = regval * 40000; + /* Return current in mA */ + *val = DIV_ROUND_CLOSEST(voltage_nv, resistance_uo); + return 0; + case hwmon_curr_crit_alarm: + case hwmon_curr_max_alarm: + ret = regmap_field_read(ina->fields[reg], ®val); + if (ret) + return ret; + *val = regval; + return 0; + default: + return -EOPNOTSUPP; + } } -static ssize_t ina3221_set_current(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static int ina3221_write_curr(struct device *dev, u32 attr, + int channel, long val) { - struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); struct ina3221_data *ina = dev_get_drvdata(dev); - unsigned int reg = sd_attr->index; - unsigned int channel = register_channel[reg]; struct ina3221_input *input = &ina->inputs[channel]; int resistance_uo = input->shunt_resistor; - int val, current_ma, voltage_uv, ret; - - ret = kstrtoint(buf, 0, ¤t_ma); - if (ret) - return ret; + u8 reg = ina3221_curr_reg[attr][channel]; + int regval, current_ma, voltage_uv; /* clamp current */ - current_ma = clamp_val(current_ma, + current_ma = clamp_val(val, INT_MIN / resistance_uo, INT_MAX / resistance_uo); @@ -287,15 +230,168 @@ static ssize_t ina3221_set_current(struct device *dev, voltage_uv = clamp_val(voltage_uv, -163800, 163800); /* 1 / 40uV(scale) << 3(register shift) = 5 */ - val = DIV_ROUND_CLOSEST(voltage_uv, 5) & 0xfff8; + regval = DIV_ROUND_CLOSEST(voltage_uv, 5) & 0xfff8; - ret = regmap_write(ina->regmap, reg, val); + return regmap_write(ina->regmap, reg, regval); +} + +static int ina3221_write_enable(struct device *dev, int channel, bool enable) +{ + struct ina3221_data *ina = dev_get_drvdata(dev); + u16 config, mask = INA3221_CONFIG_CHx_EN(channel); + int ret; + + config = enable ? mask : 0; + + /* Enable or disable the channel */ + ret = regmap_update_bits(ina->regmap, INA3221_CONFIG, mask, config); if (ret) return ret; - return count; + /* Cache the latest config register value */ + ret = regmap_read(ina->regmap, INA3221_CONFIG, &ina->reg_config); + if (ret) + return ret; + + return 0; } +static int ina3221_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + switch (type) { + case hwmon_in: + /* 0-align channel ID */ + return ina3221_read_in(dev, attr, channel - 1, val); + case hwmon_curr: + return ina3221_read_curr(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} + +static int ina3221_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + switch (type) { + case hwmon_in: + /* 0-align channel ID */ + return ina3221_write_enable(dev, channel - 1, val); + case hwmon_curr: + return ina3221_write_curr(dev, attr, channel, val); + default: + return -EOPNOTSUPP; + } +} + +static int ina3221_read_string(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) +{ + struct ina3221_data *ina = dev_get_drvdata(dev); + int index = channel - 1; + + *str = ina->inputs[index].label; + + return 0; +} + +static umode_t ina3221_is_visible(const void *drvdata, + enum hwmon_sensor_types type, + u32 attr, int channel) +{ + const struct ina3221_data *ina = drvdata; + const struct ina3221_input *input = NULL; + + switch (type) { + case hwmon_in: + /* Ignore in0_ */ + if (channel == 0) + return 0; + + switch (attr) { + case hwmon_in_label: + if (channel - 1 <= INA3221_CHANNEL3) + input = &ina->inputs[channel - 1]; + /* Hide label node if label is not provided */ + return (input && input->label) ? 0444 : 0; + case hwmon_in_input: + return 0444; + case hwmon_in_enable: + return 0644; + default: + return 0; + } + case hwmon_curr: + switch (attr) { + case hwmon_curr_input: + case hwmon_curr_crit_alarm: + case hwmon_curr_max_alarm: + return 0444; + case hwmon_curr_crit: + case hwmon_curr_max: + return 0644; + default: + return 0; + } + default: + return 0; + } +} + +static const u32 ina3221_in_config[] = { + /* 0: dummy, skipped in is_visible */ + HWMON_I_INPUT, + /* 1-3: input voltage Channels */ + HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL, + /* 4-6: shunt voltage Channels */ + HWMON_I_INPUT, + HWMON_I_INPUT, + HWMON_I_INPUT, + 0 +}; + +static const struct hwmon_channel_info ina3221_in = { + .type = hwmon_in, + .config = ina3221_in_config, +}; + +#define INA3221_HWMON_CURR_CONFIG (HWMON_C_INPUT | \ + HWMON_C_CRIT | HWMON_C_CRIT_ALARM | \ + HWMON_C_MAX | HWMON_C_MAX_ALARM) + +static const u32 ina3221_curr_config[] = { + INA3221_HWMON_CURR_CONFIG, + INA3221_HWMON_CURR_CONFIG, + INA3221_HWMON_CURR_CONFIG, + 0 +}; + +static const struct hwmon_channel_info ina3221_curr = { + .type = hwmon_curr, + .config = ina3221_curr_config, +}; + +static const struct hwmon_channel_info *ina3221_info[] = { + &ina3221_in, + &ina3221_curr, + NULL +}; + +static const struct hwmon_ops ina3221_hwmon_ops = { + .is_visible = ina3221_is_visible, + .read_string = ina3221_read_string, + .read = ina3221_read, + .write = ina3221_write, +}; + +static const struct hwmon_chip_info ina3221_chip_info = { + .ops = &ina3221_hwmon_ops, + .info = ina3221_info, +}; + +/* Extra attribute groups */ static ssize_t ina3221_show_shunt(struct device *dev, struct device_attribute *attr, char *buf) { @@ -329,54 +425,6 @@ static ssize_t ina3221_set_shunt(struct device *dev, return count; } -static ssize_t ina3221_show_alert(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct sensor_device_attribute *sd_attr = to_sensor_dev_attr(attr); - struct ina3221_data *ina = dev_get_drvdata(dev); - unsigned int field = sd_attr->index; - unsigned int regval; - int ret; - - ret = regmap_field_read(ina->fields[field], ®val); - if (ret) - return ret; - - return snprintf(buf, PAGE_SIZE, "%d\n", regval); -} - -/* input channel label */ -static SENSOR_DEVICE_ATTR(in1_label, 0444, - ina3221_show_label, NULL, INA3221_CHANNEL1); -static SENSOR_DEVICE_ATTR(in2_label, 0444, - ina3221_show_label, NULL, INA3221_CHANNEL2); -static SENSOR_DEVICE_ATTR(in3_label, 0444, - ina3221_show_label, NULL, INA3221_CHANNEL3); - -/* voltage channel enable */ -static SENSOR_DEVICE_ATTR(in1_enable, 0644, - ina3221_show_enable, ina3221_set_enable, INA3221_CHANNEL1); -static SENSOR_DEVICE_ATTR(in2_enable, 0644, - ina3221_show_enable, ina3221_set_enable, INA3221_CHANNEL2); -static SENSOR_DEVICE_ATTR(in3_enable, 0644, - ina3221_show_enable, ina3221_set_enable, INA3221_CHANNEL3); - -/* bus voltage */ -static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, - ina3221_show_bus_voltage, NULL, INA3221_BUS1); -static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, - ina3221_show_bus_voltage, NULL, INA3221_BUS2); -static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, - ina3221_show_bus_voltage, NULL, INA3221_BUS3); - -/* calculated current */ -static SENSOR_DEVICE_ATTR(curr1_input, S_IRUGO, - ina3221_show_current, NULL, INA3221_SHUNT1); -static SENSOR_DEVICE_ATTR(curr2_input, S_IRUGO, - ina3221_show_current, NULL, INA3221_SHUNT2); -static SENSOR_DEVICE_ATTR(curr3_input, S_IRUGO, - ina3221_show_current, NULL, INA3221_SHUNT3); - /* shunt resistance */ static SENSOR_DEVICE_ATTR(shunt1_resistor, S_IRUGO | S_IWUSR, ina3221_show_shunt, ina3221_set_shunt, INA3221_CHANNEL1); @@ -385,109 +433,13 @@ static SENSOR_DEVICE_ATTR(shunt2_resistor, S_IRUGO | S_IWUSR, static SENSOR_DEVICE_ATTR(shunt3_resistor, S_IRUGO | S_IWUSR, ina3221_show_shunt, ina3221_set_shunt, INA3221_CHANNEL3); -/* critical current */ -static SENSOR_DEVICE_ATTR(curr1_crit, S_IRUGO | S_IWUSR, - ina3221_show_current, ina3221_set_current, INA3221_CRIT1); -static SENSOR_DEVICE_ATTR(curr2_crit, S_IRUGO | S_IWUSR, - ina3221_show_current, ina3221_set_current, INA3221_CRIT2); -static SENSOR_DEVICE_ATTR(curr3_crit, S_IRUGO | S_IWUSR, - ina3221_show_current, ina3221_set_current, INA3221_CRIT3); - -/* critical current alert */ -static SENSOR_DEVICE_ATTR(curr1_crit_alarm, S_IRUGO, - ina3221_show_alert, NULL, F_CF1); -static SENSOR_DEVICE_ATTR(curr2_crit_alarm, S_IRUGO, - ina3221_show_alert, NULL, F_CF2); -static SENSOR_DEVICE_ATTR(curr3_crit_alarm, S_IRUGO, - ina3221_show_alert, NULL, F_CF3); - -/* warning current */ -static SENSOR_DEVICE_ATTR(curr1_max, S_IRUGO | S_IWUSR, - ina3221_show_current, ina3221_set_current, INA3221_WARN1); -static SENSOR_DEVICE_ATTR(curr2_max, S_IRUGO | S_IWUSR, - ina3221_show_current, ina3221_set_current, INA3221_WARN2); -static SENSOR_DEVICE_ATTR(curr3_max, S_IRUGO | S_IWUSR, - ina3221_show_current, ina3221_set_current, INA3221_WARN3); - -/* warning current alert */ -static SENSOR_DEVICE_ATTR(curr1_max_alarm, S_IRUGO, - ina3221_show_alert, NULL, F_WF1); -static SENSOR_DEVICE_ATTR(curr2_max_alarm, S_IRUGO, - ina3221_show_alert, NULL, F_WF2); -static SENSOR_DEVICE_ATTR(curr3_max_alarm, S_IRUGO, - ina3221_show_alert, NULL, F_WF3); - -/* shunt voltage */ -static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, - ina3221_show_shunt_voltage, NULL, INA3221_SHUNT1); -static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, - ina3221_show_shunt_voltage, NULL, INA3221_SHUNT2); -static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, - ina3221_show_shunt_voltage, NULL, INA3221_SHUNT3); - static struct attribute *ina3221_attrs[] = { - /* channel 1 -- make sure label at first */ - &sensor_dev_attr_in1_label.dev_attr.attr, - &sensor_dev_attr_in1_enable.dev_attr.attr, - &sensor_dev_attr_in1_input.dev_attr.attr, - &sensor_dev_attr_curr1_input.dev_attr.attr, &sensor_dev_attr_shunt1_resistor.dev_attr.attr, - &sensor_dev_attr_curr1_crit.dev_attr.attr, - &sensor_dev_attr_curr1_crit_alarm.dev_attr.attr, - &sensor_dev_attr_curr1_max.dev_attr.attr, - &sensor_dev_attr_curr1_max_alarm.dev_attr.attr, - &sensor_dev_attr_in4_input.dev_attr.attr, - - /* channel 2 -- make sure label at first */ - &sensor_dev_attr_in2_label.dev_attr.attr, - &sensor_dev_attr_in2_enable.dev_attr.attr, - &sensor_dev_attr_in2_input.dev_attr.attr, - &sensor_dev_attr_curr2_input.dev_attr.attr, &sensor_dev_attr_shunt2_resistor.dev_attr.attr, - &sensor_dev_attr_curr2_crit.dev_attr.attr, - &sensor_dev_attr_curr2_crit_alarm.dev_attr.attr, - &sensor_dev_attr_curr2_max.dev_attr.attr, - &sensor_dev_attr_curr2_max_alarm.dev_attr.attr, - &sensor_dev_attr_in5_input.dev_attr.attr, - - /* channel 3 -- make sure label at first */ - &sensor_dev_attr_in3_label.dev_attr.attr, - &sensor_dev_attr_in3_enable.dev_attr.attr, - &sensor_dev_attr_in3_input.dev_attr.attr, - &sensor_dev_attr_curr3_input.dev_attr.attr, &sensor_dev_attr_shunt3_resistor.dev_attr.attr, - &sensor_dev_attr_curr3_crit.dev_attr.attr, - &sensor_dev_attr_curr3_crit_alarm.dev_attr.attr, - &sensor_dev_attr_curr3_max.dev_attr.attr, - &sensor_dev_attr_curr3_max_alarm.dev_attr.attr, - &sensor_dev_attr_in6_input.dev_attr.attr, - NULL, }; - -static umode_t ina3221_attr_is_visible(struct kobject *kobj, - struct attribute *attr, int n) -{ - const int max_attrs = ARRAY_SIZE(ina3221_attrs) - 1; - const int num_attrs = max_attrs / INA3221_NUM_CHANNELS; - struct device *dev = kobj_to_dev(kobj); - struct ina3221_data *ina = dev_get_drvdata(dev); - enum ina3221_channels channel = n / num_attrs; - struct ina3221_input *input = &ina->inputs[channel]; - int index = n % num_attrs; - - /* Hide label node if label is not provided */ - if (index == 0 && !input->label) - return 0; - - return attr->mode; -} - -static const struct attribute_group ina3221_group = { - .is_visible = ina3221_attr_is_visible, - .attrs = ina3221_attrs, -}; -__ATTRIBUTE_GROUPS(ina3221); +ATTRIBUTE_GROUPS(ina3221); static const struct regmap_range ina3221_yes_ranges[] = { regmap_reg_range(INA3221_CONFIG, INA3221_BUS3), @@ -626,9 +578,9 @@ static int ina3221_probe(struct i2c_client *client, dev_set_drvdata(dev, ina); - hwmon_dev = devm_hwmon_device_register_with_groups(dev, - client->name, - ina, ina3221_groups); + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, ina, + &ina3221_chip_info, + ina3221_groups); if (IS_ERR(hwmon_dev)) { dev_err(dev, "Unable to register hwmon device\n"); return PTR_ERR(hwmon_dev); From 61b8ab2c5481dc48e8df9a13c297636c1d369554 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Tue, 9 Oct 2018 14:42:19 -0700 Subject: [PATCH 44/46] hwmon: (core) Add trace events to _attr_show/store functions Trace events are useful for people who collect data from the Ftrace outputs. There're people who analyse the relationship of cpufreq, thermal and hwmon (power/voltage/current) using the convenient and timestamped Ftrace outputs, while unlike cpufreq and thermal subsystems the hwmon does not have trace events supported yet. So this patch adds initial trace events for the hwmon core. To call hwmon_attr_base() for aligned attr index numbers, it also moves the function upward. Ftrace outputs: ...: hwmon_attr_show_string: index=2, attr_name=in2_label, val=VDD_5V ...: hwmon_attr_show: index=2, attr_name=in2_input, val=5112 ...: hwmon_attr_show: index=2, attr_name=curr2_input, val=440 Note that the _attr_show and _attr_store functions are tied to the _with_info API. So a hwmon driver requiring the trace events feature should use _with_info API to register a hwmon device. Signed-off-by: Nicolin Chen Signed-off-by: Guenter Roeck --- MAINTAINERS | 1 + drivers/hwmon/hwmon.c | 27 ++++++++++---- include/trace/events/hwmon.h | 71 ++++++++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+), 7 deletions(-) create mode 100644 include/trace/events/hwmon.h diff --git a/MAINTAINERS b/MAINTAINERS index c0ca7f74f8e8..20a64eb40b55 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6454,6 +6454,7 @@ F: Documentation/devicetree/bindings/hwmon/ F: Documentation/hwmon/ F: drivers/hwmon/ F: include/linux/hwmon*.h +F: include/trace/events/hwmon*.h HARDWARE RANDOM NUMBER GENERATOR CORE M: Matt Mackall diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index ac1cdf88840f..975c95169884 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -24,6 +24,9 @@ #include #include +#define CREATE_TRACE_POINTS +#include + #define HWMON_ID_PREFIX "hwmon" #define HWMON_ID_FORMAT HWMON_ID_PREFIX "%d" @@ -171,6 +174,13 @@ static int hwmon_thermal_add_sensor(struct device *dev, } #endif /* IS_REACHABLE(CONFIG_THERMAL) && ... */ +static int hwmon_attr_base(enum hwmon_sensor_types type) +{ + if (type == hwmon_in) + return 0; + return 1; +} + /* sysfs attribute management */ static ssize_t hwmon_attr_show(struct device *dev, @@ -185,6 +195,9 @@ static ssize_t hwmon_attr_show(struct device *dev, if (ret < 0) return ret; + trace_hwmon_attr_show(hattr->index + hwmon_attr_base(hattr->type), + hattr->name, val); + return sprintf(buf, "%ld\n", val); } @@ -193,6 +206,7 @@ static ssize_t hwmon_attr_show_string(struct device *dev, char *buf) { struct hwmon_device_attribute *hattr = to_hwmon_attr(devattr); + enum hwmon_sensor_types type = hattr->type; const char *s; int ret; @@ -201,6 +215,9 @@ static ssize_t hwmon_attr_show_string(struct device *dev, if (ret < 0) return ret; + trace_hwmon_attr_show_string(hattr->index + hwmon_attr_base(type), + hattr->name, s); + return sprintf(buf, "%s\n", s); } @@ -221,14 +238,10 @@ static ssize_t hwmon_attr_store(struct device *dev, if (ret < 0) return ret; - return count; -} + trace_hwmon_attr_store(hattr->index + hwmon_attr_base(hattr->type), + hattr->name, val); -static int hwmon_attr_base(enum hwmon_sensor_types type) -{ - if (type == hwmon_in) - return 0; - return 1; + return count; } static bool is_string_attr(enum hwmon_sensor_types type, u32 attr) diff --git a/include/trace/events/hwmon.h b/include/trace/events/hwmon.h new file mode 100644 index 000000000000..d7a1d0ffb679 --- /dev/null +++ b/include/trace/events/hwmon.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM hwmon + +#if !defined(_TRACE_HWMON_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_HWMON_H + +#include + +DECLARE_EVENT_CLASS(hwmon_attr_class, + + TP_PROTO(int index, const char *attr_name, long val), + + TP_ARGS(index, attr_name, val), + + TP_STRUCT__entry( + __field(int, index) + __string(attr_name, attr_name) + __field(long, val) + ), + + TP_fast_assign( + __entry->index = index; + __assign_str(attr_name, attr_name); + __entry->val = val; + ), + + TP_printk("index=%d, attr_name=%s, val=%ld", + __entry->index, __get_str(attr_name), __entry->val) +); + +DEFINE_EVENT(hwmon_attr_class, hwmon_attr_show, + + TP_PROTO(int index, const char *attr_name, long val), + + TP_ARGS(index, attr_name, val) +); + +DEFINE_EVENT(hwmon_attr_class, hwmon_attr_store, + + TP_PROTO(int index, const char *attr_name, long val), + + TP_ARGS(index, attr_name, val) +); + +TRACE_EVENT(hwmon_attr_show_string, + + TP_PROTO(int index, const char *attr_name, const char *s), + + TP_ARGS(index, attr_name, s), + + TP_STRUCT__entry( + __field(int, index) + __string(attr_name, attr_name) + __string(label, s) + ), + + TP_fast_assign( + __entry->index = index; + __assign_str(attr_name, attr_name); + __assign_str(label, s); + ), + + TP_printk("index=%d, attr_name=%s, val=%s", + __entry->index, __get_str(attr_name), __get_str(label)) +); + +#endif /* _TRACE_HWMON_H */ + +/* This part must be outside protection */ +#include From 04e79eb704b20ec330e96e93727ac27e66f66dcb Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Wed, 10 Oct 2018 16:35:57 +0200 Subject: [PATCH 45/46] hwmon: (pmbus) remove redundant 'default n' from Kconfig 'default n' is the default value for any bool or tristate Kconfig setting so there is no need to write it explicitly. Also since commit f467c5640c29 ("kconfig: only write '# CONFIG_FOO is not set' for visible symbols") the Kconfig behavior is the same regardless of 'default n' being present or not: ... One side effect of (and the main motivation for) this change is making the following two definitions behave exactly the same: config FOO bool config FOO bool default n With this change, neither of these will generate a '# CONFIG_FOO is not set' line (assuming FOO isn't selected/implied). That might make it clearer to people that a bare 'default n' is redundant. ... Signed-off-by: Bartlomiej Zolnierkiewicz Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/Kconfig | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 9cdb71baeda4..629cb45f8557 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -5,7 +5,6 @@ menuconfig PMBUS tristate "PMBus support" depends on I2C - default n help Say yes here if you want to enable PMBus support. @@ -28,7 +27,6 @@ config SENSORS_PMBUS config SENSORS_ADM1275 tristate "Analog Devices ADM1275 and compatibles" - default n help If you say yes here you get hardware monitoring support for Analog Devices ADM1075, ADM1272, ADM1275, ADM1276, ADM1278, ADM1293, @@ -49,7 +47,6 @@ config SENSORS_IBM_CFFPS config SENSORS_IR35221 tristate "Infineon IR35221" - default n help If you say yes here you get hardware monitoring support for the Infineon IR35221 controller. @@ -59,7 +56,6 @@ config SENSORS_IR35221 config SENSORS_LM25066 tristate "National Semiconductor LM25066 and compatibles" - default n help If you say yes here you get hardware monitoring support for National Semiconductor LM25056, LM25066, LM5064, and LM5066. @@ -69,7 +65,6 @@ config SENSORS_LM25066 config SENSORS_LTC2978 tristate "Linear Technologies LTC2978 and compatibles" - default n help If you say yes here you get hardware monitoring support for Linear Technology LTC2974, LTC2975, LTC2977, LTC2978, LTC2980, LTC3880, @@ -88,7 +83,6 @@ config SENSORS_LTC2978_REGULATOR config SENSORS_LTC3815 tristate "Linear Technologies LTC3815" - default n help If you say yes here you get hardware monitoring support for Linear Technology LTC3815. @@ -98,7 +92,6 @@ config SENSORS_LTC3815 config SENSORS_MAX16064 tristate "Maxim MAX16064" - default n help If you say yes here you get hardware monitoring support for Maxim MAX16064. @@ -108,7 +101,6 @@ config SENSORS_MAX16064 config SENSORS_MAX20751 tristate "Maxim MAX20751" - default n help If you say yes here you get hardware monitoring support for Maxim MAX20751. @@ -118,7 +110,6 @@ config SENSORS_MAX20751 config SENSORS_MAX31785 tristate "Maxim MAX31785 and compatibles" - default n help If you say yes here you get hardware monitoring support for Maxim MAX31785. @@ -128,7 +119,6 @@ config SENSORS_MAX31785 config SENSORS_MAX34440 tristate "Maxim MAX34440 and compatibles" - default n help If you say yes here you get hardware monitoring support for Maxim MAX34440, MAX34441, MAX34446, MAX34451, MAX34460, and MAX34461. @@ -138,7 +128,6 @@ config SENSORS_MAX34440 config SENSORS_MAX8688 tristate "Maxim MAX8688" - default n help If you say yes here you get hardware monitoring support for Maxim MAX8688. @@ -148,7 +137,6 @@ config SENSORS_MAX8688 config SENSORS_TPS40422 tristate "TI TPS40422" - default n help If you say yes here you get hardware monitoring support for TI TPS40422. @@ -167,7 +155,6 @@ config SENSORS_TPS53679 config SENSORS_UCD9000 tristate "TI UCD90120, UCD90124, UCD90160, UCD9090, UCD90910" - default n help If you say yes here you get hardware monitoring support for TI UCD90120, UCD90124, UCD90160, UCD9090, UCD90910, Sequencer and System @@ -178,7 +165,6 @@ config SENSORS_UCD9000 config SENSORS_UCD9200 tristate "TI UCD9220, UCD9222, UCD9224, UCD9240, UCD9244, UCD9246, UCD9248" - default n help If you say yes here you get hardware monitoring support for TI UCD9220, UCD9222, UCD9224, UCD9240, UCD9244, UCD9246, and UCD9248 @@ -189,7 +175,6 @@ config SENSORS_UCD9200 config SENSORS_ZL6100 tristate "Intersil ZL6100 and compatibles" - default n help If you say yes here you get hardware monitoring support for Intersil ZL2004, ZL2005, ZL2006, ZL2008, ZL2105, ZL2106, ZL6100, ZL6105, From e7c6a55606b5c46b449d76588968b4d8caae903f Mon Sep 17 00:00:00 2001 From: Dmitry Bazhenov Date: Mon, 15 Oct 2018 14:21:22 +0500 Subject: [PATCH 46/46] hwmon: (pmbus) Fix page count auto-detection. Devices with compatible="pmbus" field have zero initial page count, and pmbus_clear_faults() being called before the page count auto- detection does not actually clear faults because it depends on the page count. Non-cleared faults in its turn may fail the subsequent page count auto-detection. This patch fixes this problem by calling pmbus_clear_fault_page() for currently set page and calling pmbus_clear_faults() after the page count was detected. Cc: stable@vger.kernel.org Signed-off-by: Dmitry Bazhenov Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus.c | 2 ++ drivers/hwmon/pmbus/pmbus_core.c | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/pmbus/pmbus.c b/drivers/hwmon/pmbus/pmbus.c index 7718e58dbda5..7688dab32f6e 100644 --- a/drivers/hwmon/pmbus/pmbus.c +++ b/drivers/hwmon/pmbus/pmbus.c @@ -118,6 +118,8 @@ static int pmbus_identify(struct i2c_client *client, } else { info->pages = 1; } + + pmbus_clear_faults(client); } if (pmbus_check_byte_register(client, 0, PMBUS_VOUT_MODE)) { diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 82c3754e21e3..2e2b5851139c 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -2015,7 +2015,10 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, if (ret >= 0 && (ret & PB_CAPABILITY_ERROR_CHECK)) client->flags |= I2C_CLIENT_PEC; - pmbus_clear_faults(client); + if (data->info->pages) + pmbus_clear_faults(client); + else + pmbus_clear_fault_page(client, -1); if (info->identify) { ret = (*info->identify)(client, info);