From f6aed68e8a2a646c78801f6c545f9c4db2f4e610 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 13 Jan 2021 11:59:17 +0100 Subject: hwmon: (ina2) update ti,ina2xx.yaml reference in documentation Changeset 94f1ab944565 ("dt-bindings: hwmon: convert TI INA2xx bindings to dt-schema") renamed: Documentation/devicetree/bindings/hwmon/ina2xx.txt to: Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml. Update its cross-reference accordingly. Signed-off-by: Mauro Carvalho Chehab Link: https://lore.kernel.org/r/886bd248721b146d844d46e26ddd4cd277f51446.1610535350.git.mchehab+huawei@kernel.org Signed-off-by: Guenter Roeck --- Documentation/hwmon/ina2xx.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/hwmon/ina2xx.rst b/Documentation/hwmon/ina2xx.rst index f78a5cd44c4c..27d2e39bc8ac 100644 --- a/Documentation/hwmon/ina2xx.rst +++ b/Documentation/hwmon/ina2xx.rst @@ -74,7 +74,7 @@ bus supply voltage. The shunt value in micro-ohms can be set via platform data or device tree at compile-time or via the shunt_resistor attribute in sysfs at run-time. Please -refer to the Documentation/devicetree/bindings/hwmon/ina2xx.txt for bindings +refer to the Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml for bindings if the device tree is used. Additionally ina226 supports update_interval attribute as described in -- cgit v1.2.3 From 4008bc7d39537bb3be166d8a3129c4980e1dd7dc Mon Sep 17 00:00:00 2001 From: Thomas Hebb Date: Sat, 23 Jan 2021 18:46:08 -0800 Subject: hwmon: (dell-smm) Add XPS 15 L502X to fan control blacklist MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It has been reported[0] that the Dell XPS 15 L502X exhibits similar freezing behavior to the other systems[1] on this blacklist. The issue was exposed by a prior change of mine to automatically load dell_smm_hwmon on a wider set of XPS models. To fix the regression, add this model to the blacklist. [0] https://bugzilla.kernel.org/show_bug.cgi?id=211081 [1] https://bugzilla.kernel.org/show_bug.cgi?id=195751 Fixes: b8a13e5e8f37 ("hwmon: (dell-smm) Use one DMI match for all XPS models") Cc: stable@vger.kernel.org Reported-by: Bob Hepple Tested-by: Bob Hepple Signed-off-by: Thomas Hebb Reviewed-by: Pali Rohár Link: https://lore.kernel.org/r/a09eea7616881d40d2db2fb5fa2770dc6166bdae.1611456351.git.tommyhebb@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/dell-smm-hwmon.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/hwmon/dell-smm-hwmon.c b/drivers/hwmon/dell-smm-hwmon.c index ec448f5f2dc3..73b9db9e3aab 100644 --- a/drivers/hwmon/dell-smm-hwmon.c +++ b/drivers/hwmon/dell-smm-hwmon.c @@ -1159,6 +1159,13 @@ static struct dmi_system_id i8k_blacklist_fan_support_dmi_table[] __initdata = { DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "XPS13 9333"), }, }, + { + .ident = "Dell XPS 15 L502X", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Dell System XPS L502X"), + }, + }, { } }; -- cgit v1.2.3 From 94c08e067ab05085fe900530d5107263a7bbd87a Mon Sep 17 00:00:00 2001 From: Zheng Yongjun Date: Mon, 14 Dec 2020 21:31:14 +0800 Subject: hwmon: (pc87360) convert comma to semicolon Replace a comma between expression statements by a semicolon. Signed-off-by: Zheng Yongjun Link: https://lore.kernel.org/r/20201214133114.3516-1-zhengyongjun3@huawei.com Signed-off-by: Guenter Roeck --- drivers/hwmon/pc87360.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/pc87360.c b/drivers/hwmon/pc87360.c index 94f4b8b4a2ba..6a9ba23cd302 100644 --- a/drivers/hwmon/pc87360.c +++ b/drivers/hwmon/pc87360.c @@ -1700,8 +1700,8 @@ static int __init pc87360_device_add(unsigned short address) continue; res[res_count].start = extra_isa[i]; res[res_count].end = extra_isa[i] + PC87360_EXTENT - 1; - res[res_count].name = "pc87360", - res[res_count].flags = IORESOURCE_IO, + res[res_count].name = "pc87360"; + res[res_count].flags = IORESOURCE_IO; err = acpi_check_resource_conflict(&res[res_count]); if (err) -- cgit v1.2.3 From 87da1ed26c335908d4593b3777aae8f0e6446c4e Mon Sep 17 00:00:00 2001 From: Tom Rix Date: Tue, 15 Dec 2020 10:32:37 -0800 Subject: hwmon: (smsc47m1) Remove 'h' from printk format specifier See Documentation/core-api/printk-formats.rst. h should no longer be used in the format specifier for printk. Signed-off-by: Tom Rix Link: https://lore.kernel.org/r/20201215183237.2071770-1-trix@redhat.com [groeck: Updated subject] Signed-off-by: Guenter Roeck --- drivers/hwmon/smsc47m1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/smsc47m1.c b/drivers/hwmon/smsc47m1.c index b637836b58a1..37531b5c8254 100644 --- a/drivers/hwmon/smsc47m1.c +++ b/drivers/hwmon/smsc47m1.c @@ -682,7 +682,7 @@ static int __init smsc47m1_handle_resources(unsigned short address, /* Request the resources */ if (!devm_request_region(dev, start, len, DRVNAME)) { dev_err(dev, - "Region 0x%hx-0x%hx already in use!\n", + "Region 0x%x-0x%x already in use!\n", start, start + len); return -EBUSY; } -- cgit v1.2.3 From 5707c8dbdea30be916e3cd831a6dd84ca007407f Mon Sep 17 00:00:00 2001 From: Tian Tao Date: Mon, 14 Dec 2020 09:02:15 +0800 Subject: hwmon: (w83627ehf) Switch to using the new API kobj_to_dev() Switch to using the new API kobj_to_dev() to fix the below warnning: drivers/hwmon/w83627ehf.c:1113:60-61: WARNING opportunity for kobj_to_dev(). Signed-off-by: Tian Tao Link: https://lore.kernel.org/r/1607907735-17510-1-git-send-email-tiantao6@hisilicon.com Signed-off-by: Guenter Roeck --- drivers/hwmon/w83627ehf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c index 3964ceab2817..8618aaf32350 100644 --- a/drivers/hwmon/w83627ehf.c +++ b/drivers/hwmon/w83627ehf.c @@ -1110,7 +1110,7 @@ clear_caseopen(struct device *dev, struct w83627ehf_data *data, int channel, static umode_t w83627ehf_attrs_visible(struct kobject *kobj, struct attribute *a, int n) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct w83627ehf_data *data = dev_get_drvdata(dev); struct device_attribute *devattr; struct sensor_device_attribute *sda; -- cgit v1.2.3 From d349626b42f5dbd08ffcb3f2c383b1f6f433b3c1 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 21 Dec 2020 10:59:33 -0800 Subject: hwmon: (abx500) Decomission abx500 driver This deletes the ABx500 hwmon driver, the only supported variant being the AB8500. This driver has been replaced by generic frameworks. By inspecting the abx500 sysfs files we see that it contains things such as temp1_max, temp1_max_alarm, temp1_max_hyst, temp1_max_hyst_alarm, temp1_min, temp1_min_alarm. It becomes obvious that the abx500.c is a reimplementation of thermal zones. This is not very strange as the generic thermal zones were not invented when this driver was merged so people were rolling their own. The ab8500.c driver contains conversion tables for handling a thermistor on ADC channels AUX1 and AUX2. I managed to replace the functionality of the driver with: - Activation of the ntc_thermistor.c driver, CONFIG_SENSORS_NTC_THERMISTOR - Activation of thermal zones, CONFIG_THERMAL - In the device tree, connecting the NTC driver to the processed IIO channels from the AB8500 GPADC ADC forming two instances of NTC sensors. - Connecting the two NTC sensors to a "chassis" thermal zone in the device tree and setting that to hit the CPU frequency at 50 degrees celsius and do a critical shutdown at 70 degrees celsius, deploying a policy using the sensors. After talking to the original authors we concluded that the driver was never properly parameterized in production so what we now have in the device tree is already puts the thermistors to better use than what the hwmon driver did. The two remaining channels for two battery temperatures is already handled in the charging algorithms but can be optionally extended to thermal zones as well if we want these to trigger critical shutdown for the platform. Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20201221125521.768082-1-linus.walleij@linaro.org [groeck: Removed documentation and fixed up Makefile, Kconfig] Signed-off-by: Guenter Roeck --- Documentation/hwmon/ab8500.rst | 26 --- Documentation/hwmon/abx500.rst | 32 --- Documentation/hwmon/index.rst | 2 - drivers/hwmon/Kconfig | 13 -- drivers/hwmon/Makefile | 1 - drivers/hwmon/ab8500.c | 224 ------------------- drivers/hwmon/abx500.c | 487 ----------------------------------------- drivers/hwmon/abx500.h | 69 ------ 8 files changed, 854 deletions(-) delete mode 100644 Documentation/hwmon/ab8500.rst delete mode 100644 Documentation/hwmon/abx500.rst delete mode 100644 drivers/hwmon/ab8500.c delete mode 100644 drivers/hwmon/abx500.c delete mode 100644 drivers/hwmon/abx500.h diff --git a/Documentation/hwmon/ab8500.rst b/Documentation/hwmon/ab8500.rst deleted file mode 100644 index 33f93a9cec04..000000000000 --- a/Documentation/hwmon/ab8500.rst +++ /dev/null @@ -1,26 +0,0 @@ -Kernel driver ab8500 -==================== - -Supported chips: - - * ST-Ericsson AB8500 - - Prefix: 'ab8500' - - Addresses scanned: - - - Datasheet: http://www.stericsson.com/developers/documentation.jsp - -Authors: - - Martin Persson - - Hongbo Zhang - -Description ------------ - -See also Documentation/hwmon/abx500.rst. This is the ST-Ericsson AB8500 specific -driver. - -Currently only the AB8500 internal sensor and one external sensor for battery -temperature are monitored. Other GPADC channels can also be monitored if needed -in future. diff --git a/Documentation/hwmon/abx500.rst b/Documentation/hwmon/abx500.rst deleted file mode 100644 index 3d88b2ce0f00..000000000000 --- a/Documentation/hwmon/abx500.rst +++ /dev/null @@ -1,32 +0,0 @@ -Kernel driver abx500 -==================== - -Supported chips: - - * ST-Ericsson ABx500 series - - Prefix: 'abx500' - - Addresses scanned: - - - Datasheet: http://www.stericsson.com/developers/documentation.jsp - -Authors: - Martin Persson - Hongbo Zhang - -Description ------------ - -Every ST-Ericsson Ux500 SOC consists of both ABx500 and DBx500 physically, -this is kernel hwmon driver for ABx500. - -There are some GPADCs inside ABx500 which are designed for connecting to -thermal sensors, and there is also a thermal sensor inside ABx500 too, which -raises interrupt when critical temperature reached. - -This abx500 is a common layer which can monitor all of the sensors, every -specific abx500 chip has its special configurations in its own file, e.g. some -sensors can be configured invisible if they are not available on that chip, and -the corresponding gpadc_addr should be set to 0, thus this sensor won't be -polled. diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index fcb870ce6286..ebe53bb771dd 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -18,10 +18,8 @@ Hardware Monitoring Kernel Drivers .. toctree:: :maxdepth: 1 - ab8500 abituguru abituguru3 - abx500 acpi_power_meter ad7314 adc128d818 diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 1ecf697d8d99..c6c4f404399b 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -38,19 +38,6 @@ config HWMON_DEBUG_CHIP comment "Native drivers" -config SENSORS_AB8500 - tristate "AB8500 thermal monitoring" - depends on AB8500_GPADC && AB8500_BM && (IIO = y) - default n - help - If you say yes here you get support for the thermal sensor part - of the AB8500 chip. The driver includes thermal management for - AB8500 die and two GPADC channels. The GPADC channel are preferably - used to access sensors outside the AB8500 chip. - - This driver can also be built as a module. If so, the module - will be called abx500-temp. - config SENSORS_ABITUGURU tristate "Abit uGuru (rev 1 & 2)" depends on X86 && DMI diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 09a86c5e1d29..990891a8c89d 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -21,7 +21,6 @@ obj-$(CONFIG_SENSORS_W83795) += w83795.o obj-$(CONFIG_SENSORS_W83781D) += w83781d.o obj-$(CONFIG_SENSORS_W83791D) += w83791d.o -obj-$(CONFIG_SENSORS_AB8500) += abx500.o ab8500.o obj-$(CONFIG_SENSORS_ABITUGURU) += abituguru.o obj-$(CONFIG_SENSORS_ABITUGURU3)+= abituguru3.o obj-$(CONFIG_SENSORS_AD7314) += ad7314.o diff --git a/drivers/hwmon/ab8500.c b/drivers/hwmon/ab8500.c deleted file mode 100644 index 53f3379d799d..000000000000 --- a/drivers/hwmon/ab8500.c +++ /dev/null @@ -1,224 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson 2010 - 2013 - * Author: Martin Persson - * Hongbo Zhang - * - * When the AB8500 thermal warning temperature is reached (threshold cannot - * be changed by SW), an interrupt is set, and if no further action is taken - * within a certain time frame, kernel_power_off will be called. - * - * When AB8500 thermal shutdown temperature is reached a hardware shutdown of - * the AB8500 will occur. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "abx500.h" - -#define DEFAULT_POWER_OFF_DELAY (HZ * 10) -#define THERMAL_VCC 1800 -#define PULL_UP_RESISTOR 47000 - -#define AB8500_SENSOR_AUX1 0 -#define AB8500_SENSOR_AUX2 1 -#define AB8500_SENSOR_BTEMP_BALL 2 -#define AB8500_SENSOR_BAT_CTRL 3 -#define NUM_MONITORED_SENSORS 4 - -struct ab8500_gpadc_cfg { - const struct abx500_res_to_temp *temp_tbl; - int tbl_sz; - int vcc; - int r_up; -}; - -struct ab8500_temp { - struct iio_channel *aux1; - struct iio_channel *aux2; - struct ab8500_btemp *btemp; - struct delayed_work power_off_work; - struct ab8500_gpadc_cfg cfg; - struct abx500_temp *abx500_data; -}; - -/* - * The hardware connection is like this: - * VCC----[ R_up ]-----[ NTC ]----GND - * where R_up is pull-up resistance, and GPADC measures voltage on NTC. - * and res_to_temp table is strictly sorted by falling resistance values. - */ -static int ab8500_voltage_to_temp(struct ab8500_gpadc_cfg *cfg, - int v_ntc, int *temp) -{ - int r_ntc, i = 0, tbl_sz = cfg->tbl_sz; - const struct abx500_res_to_temp *tbl = cfg->temp_tbl; - - if (cfg->vcc < 0 || v_ntc >= cfg->vcc) - return -EINVAL; - - r_ntc = v_ntc * cfg->r_up / (cfg->vcc - v_ntc); - if (r_ntc > tbl[0].resist || r_ntc < tbl[tbl_sz - 1].resist) - return -EINVAL; - - while (!(r_ntc <= tbl[i].resist && r_ntc > tbl[i + 1].resist) && - i < tbl_sz - 2) - i++; - - /* return milli-Celsius */ - *temp = tbl[i].temp * 1000 + ((tbl[i + 1].temp - tbl[i].temp) * 1000 * - (r_ntc - tbl[i].resist)) / (tbl[i + 1].resist - tbl[i].resist); - - return 0; -} - -static int ab8500_read_sensor(struct abx500_temp *data, u8 sensor, int *temp) -{ - int voltage, ret; - struct ab8500_temp *ab8500_data = data->plat_data; - - if (sensor == AB8500_SENSOR_BTEMP_BALL) { - *temp = ab8500_btemp_get_temp(ab8500_data->btemp); - } else if (sensor == AB8500_SENSOR_BAT_CTRL) { - *temp = ab8500_btemp_get_batctrl_temp(ab8500_data->btemp); - } else if (sensor == AB8500_SENSOR_AUX1) { - ret = iio_read_channel_processed(ab8500_data->aux1, &voltage); - if (ret < 0) - return ret; - ret = ab8500_voltage_to_temp(&ab8500_data->cfg, voltage, temp); - if (ret < 0) - return ret; - } else if (sensor == AB8500_SENSOR_AUX2) { - ret = iio_read_channel_processed(ab8500_data->aux2, &voltage); - if (ret < 0) - return ret; - ret = ab8500_voltage_to_temp(&ab8500_data->cfg, voltage, temp); - if (ret < 0) - return ret; - } - - return 0; -} - -static void ab8500_thermal_power_off(struct work_struct *work) -{ - struct ab8500_temp *ab8500_data = container_of(work, - struct ab8500_temp, power_off_work.work); - struct abx500_temp *abx500_data = ab8500_data->abx500_data; - - dev_warn(&abx500_data->pdev->dev, "Power off due to critical temp\n"); - - kernel_power_off(); -} - -static ssize_t ab8500_show_name(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - return sprintf(buf, "ab8500\n"); -} - -static ssize_t ab8500_show_label(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - char *label; - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - int index = attr->index; - - switch (index) { - case 1: - label = "ext_adc1"; - break; - case 2: - label = "ext_adc2"; - break; - case 3: - label = "bat_temp"; - break; - case 4: - label = "bat_ctrl"; - break; - default: - return -EINVAL; - } - - return sprintf(buf, "%s\n", label); -} - -static int ab8500_temp_irq_handler(int irq, struct abx500_temp *data) -{ - struct ab8500_temp *ab8500_data = data->plat_data; - - dev_warn(&data->pdev->dev, "Power off in %d s\n", - DEFAULT_POWER_OFF_DELAY / HZ); - - schedule_delayed_work(&ab8500_data->power_off_work, - DEFAULT_POWER_OFF_DELAY); - return 0; -} - -int abx500_hwmon_init(struct abx500_temp *data) -{ - struct ab8500_temp *ab8500_data; - - ab8500_data = devm_kzalloc(&data->pdev->dev, sizeof(*ab8500_data), - GFP_KERNEL); - if (!ab8500_data) - return -ENOMEM; - - ab8500_data->btemp = ab8500_btemp_get(); - if (IS_ERR(ab8500_data->btemp)) - return PTR_ERR(ab8500_data->btemp); - - INIT_DELAYED_WORK(&ab8500_data->power_off_work, - ab8500_thermal_power_off); - - ab8500_data->cfg.vcc = THERMAL_VCC; - ab8500_data->cfg.r_up = PULL_UP_RESISTOR; - ab8500_data->cfg.temp_tbl = ab8500_temp_tbl_a_thermistor; - ab8500_data->cfg.tbl_sz = ab8500_temp_tbl_a_size; - - data->plat_data = ab8500_data; - ab8500_data->aux1 = devm_iio_channel_get(&data->pdev->dev, "aux1"); - if (IS_ERR(ab8500_data->aux1)) { - if (PTR_ERR(ab8500_data->aux1) == -ENODEV) - return -EPROBE_DEFER; - dev_err(&data->pdev->dev, "failed to get AUX1 ADC channel\n"); - return PTR_ERR(ab8500_data->aux1); - } - ab8500_data->aux2 = devm_iio_channel_get(&data->pdev->dev, "aux2"); - if (IS_ERR(ab8500_data->aux2)) { - if (PTR_ERR(ab8500_data->aux2) == -ENODEV) - return -EPROBE_DEFER; - dev_err(&data->pdev->dev, "failed to get AUX2 ADC channel\n"); - return PTR_ERR(ab8500_data->aux2); - } - - data->gpadc_addr[0] = AB8500_SENSOR_AUX1; - data->gpadc_addr[1] = AB8500_SENSOR_AUX2; - data->gpadc_addr[2] = AB8500_SENSOR_BTEMP_BALL; - data->gpadc_addr[3] = AB8500_SENSOR_BAT_CTRL; - data->monitored_sensors = NUM_MONITORED_SENSORS; - - data->ops.read_sensor = ab8500_read_sensor; - data->ops.irq_handler = ab8500_temp_irq_handler; - data->ops.show_name = ab8500_show_name; - data->ops.show_label = ab8500_show_label; - data->ops.is_visible = NULL; - - return 0; -} -EXPORT_SYMBOL(abx500_hwmon_init); - -MODULE_AUTHOR("Hongbo Zhang "); -MODULE_DESCRIPTION("AB8500 temperature driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/abx500.c b/drivers/hwmon/abx500.c deleted file mode 100644 index 4b9648819836..000000000000 --- a/drivers/hwmon/abx500.c +++ /dev/null @@ -1,487 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) ST-Ericsson 2010 - 2013 - * Author: Martin Persson - * Hongbo Zhang - * - * ABX500 does not provide auto ADC, so to monitor the required temperatures, - * a periodic work is used. It is more important to not wake up the CPU than - * to perform this job, hence the use of a deferred delay. - * - * A deferred delay for thermal monitor is considered safe because: - * If the chip gets too hot during a sleep state it's most likely due to - * external factors, such as the surrounding temperature. I.e. no SW decisions - * will make any difference. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "abx500.h" - -#define DEFAULT_MONITOR_DELAY HZ -#define DEFAULT_MAX_TEMP 130 - -static inline void schedule_monitor(struct abx500_temp *data) -{ - data->work_active = true; - schedule_delayed_work(&data->work, DEFAULT_MONITOR_DELAY); -} - -static void threshold_updated(struct abx500_temp *data) -{ - int i; - for (i = 0; i < data->monitored_sensors; i++) - if (data->max[i] != 0 || data->min[i] != 0) { - schedule_monitor(data); - return; - } - - dev_dbg(&data->pdev->dev, "No active thresholds.\n"); - cancel_delayed_work_sync(&data->work); - data->work_active = false; -} - -static void gpadc_monitor(struct work_struct *work) -{ - int temp, i, ret; - char alarm_node[30]; - bool updated_min_alarm, updated_max_alarm; - struct abx500_temp *data; - - data = container_of(work, struct abx500_temp, work.work); - mutex_lock(&data->lock); - - for (i = 0; i < data->monitored_sensors; i++) { - /* Thresholds are considered inactive if set to 0 */ - if (data->max[i] == 0 && data->min[i] == 0) - continue; - - if (data->max[i] < data->min[i]) - continue; - - ret = data->ops.read_sensor(data, data->gpadc_addr[i], &temp); - if (ret < 0) { - dev_err(&data->pdev->dev, "GPADC read failed\n"); - continue; - } - - updated_min_alarm = false; - updated_max_alarm = false; - - if (data->min[i] != 0) { - if (temp < data->min[i]) { - if (data->min_alarm[i] == false) { - data->min_alarm[i] = true; - updated_min_alarm = true; - } - } else { - if (data->min_alarm[i] == true) { - data->min_alarm[i] = false; - updated_min_alarm = true; - } - } - } - if (data->max[i] != 0) { - if (temp > data->max[i]) { - if (data->max_alarm[i] == false) { - data->max_alarm[i] = true; - updated_max_alarm = true; - } - } else if (temp < data->max[i] - data->max_hyst[i]) { - if (data->max_alarm[i] == true) { - data->max_alarm[i] = false; - updated_max_alarm = true; - } - } - } - - if (updated_min_alarm) { - ret = sprintf(alarm_node, "temp%d_min_alarm", i + 1); - sysfs_notify(&data->pdev->dev.kobj, NULL, alarm_node); - } - if (updated_max_alarm) { - ret = sprintf(alarm_node, "temp%d_max_alarm", i + 1); - sysfs_notify(&data->pdev->dev.kobj, NULL, alarm_node); - } - } - - schedule_monitor(data); - mutex_unlock(&data->lock); -} - -/* HWMON sysfs interfaces */ -static ssize_t name_show(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct abx500_temp *data = dev_get_drvdata(dev); - /* Show chip name */ - return data->ops.show_name(dev, devattr, buf); -} - -static ssize_t label_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct abx500_temp *data = dev_get_drvdata(dev); - /* Show each sensor label */ - return data->ops.show_label(dev, devattr, buf); -} - -static ssize_t input_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - int ret, temp; - struct abx500_temp *data = dev_get_drvdata(dev); - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - u8 gpadc_addr = data->gpadc_addr[attr->index]; - - ret = data->ops.read_sensor(data, gpadc_addr, &temp); - if (ret < 0) - return ret; - - return sprintf(buf, "%d\n", temp); -} - -/* Set functions (RW nodes) */ -static ssize_t min_store(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) -{ - unsigned long val; - struct abx500_temp *data = dev_get_drvdata(dev); - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - int res = kstrtol(buf, 10, &val); - if (res < 0) - return res; - - val = clamp_val(val, 0, DEFAULT_MAX_TEMP); - - mutex_lock(&data->lock); - data->min[attr->index] = val; - threshold_updated(data); - mutex_unlock(&data->lock); - - return count; -} - -static ssize_t max_store(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) -{ - unsigned long val; - struct abx500_temp *data = dev_get_drvdata(dev); - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - int res = kstrtol(buf, 10, &val); - if (res < 0) - return res; - - val = clamp_val(val, 0, DEFAULT_MAX_TEMP); - - mutex_lock(&data->lock); - data->max[attr->index] = val; - threshold_updated(data); - mutex_unlock(&data->lock); - - return count; -} - -static ssize_t max_hyst_store(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) -{ - unsigned long val; - struct abx500_temp *data = dev_get_drvdata(dev); - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - int res = kstrtoul(buf, 10, &val); - if (res < 0) - return res; - - val = clamp_val(val, 0, DEFAULT_MAX_TEMP); - - mutex_lock(&data->lock); - data->max_hyst[attr->index] = val; - threshold_updated(data); - mutex_unlock(&data->lock); - - return count; -} - -/* Show functions (RO nodes) */ -static ssize_t min_show(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct abx500_temp *data = dev_get_drvdata(dev); - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - - return sprintf(buf, "%lu\n", data->min[attr->index]); -} - -static ssize_t max_show(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct abx500_temp *data = dev_get_drvdata(dev); - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - - return sprintf(buf, "%lu\n", data->max[attr->index]); -} - -static ssize_t max_hyst_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct abx500_temp *data = dev_get_drvdata(dev); - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - - return sprintf(buf, "%lu\n", data->max_hyst[attr->index]); -} - -static ssize_t min_alarm_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct abx500_temp *data = dev_get_drvdata(dev); - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - - return sprintf(buf, "%d\n", data->min_alarm[attr->index]); -} - -static ssize_t max_alarm_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct abx500_temp *data = dev_get_drvdata(dev); - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - - return sprintf(buf, "%d\n", data->max_alarm[attr->index]); -} - -static umode_t abx500_attrs_visible(struct kobject *kobj, - struct attribute *attr, int n) -{ - struct device *dev = kobj_to_dev(kobj); - struct abx500_temp *data = dev_get_drvdata(dev); - - if (data->ops.is_visible) - return data->ops.is_visible(attr, n); - - return attr->mode; -} - -/* Chip name, required by hwmon */ -static SENSOR_DEVICE_ATTR_RO(name, name, 0); - -/* GPADC - SENSOR1 */ -static SENSOR_DEVICE_ATTR_RO(temp1_label, label, 0); -static SENSOR_DEVICE_ATTR_RO(temp1_input, input, 0); -static SENSOR_DEVICE_ATTR_RW(temp1_min, min, 0); -static SENSOR_DEVICE_ATTR_RW(temp1_max, max, 0); -static SENSOR_DEVICE_ATTR_RW(temp1_max_hyst, max_hyst, 0); -static SENSOR_DEVICE_ATTR_RO(temp1_min_alarm, min_alarm, 0); -static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, max_alarm, 0); - -/* GPADC - SENSOR2 */ -static SENSOR_DEVICE_ATTR_RO(temp2_label, label, 1); -static SENSOR_DEVICE_ATTR_RO(temp2_input, input, 1); -static SENSOR_DEVICE_ATTR_RW(temp2_min, min, 1); -static SENSOR_DEVICE_ATTR_RW(temp2_max, max, 1); -static SENSOR_DEVICE_ATTR_RW(temp2_max_hyst, max_hyst, 1); -static SENSOR_DEVICE_ATTR_RO(temp2_min_alarm, min_alarm, 1); -static SENSOR_DEVICE_ATTR_RO(temp2_max_alarm, max_alarm, 1); - -/* GPADC - SENSOR3 */ -static SENSOR_DEVICE_ATTR_RO(temp3_label, label, 2); -static SENSOR_DEVICE_ATTR_RO(temp3_input, input, 2); -static SENSOR_DEVICE_ATTR_RW(temp3_min, min, 2); -static SENSOR_DEVICE_ATTR_RW(temp3_max, max, 2); -static SENSOR_DEVICE_ATTR_RW(temp3_max_hyst, max_hyst, 2); -static SENSOR_DEVICE_ATTR_RO(temp3_min_alarm, min_alarm, 2); -static SENSOR_DEVICE_ATTR_RO(temp3_max_alarm, max_alarm, 2); - -/* GPADC - SENSOR4 */ -static SENSOR_DEVICE_ATTR_RO(temp4_label, label, 3); -static SENSOR_DEVICE_ATTR_RO(temp4_input, input, 3); -static SENSOR_DEVICE_ATTR_RW(temp4_min, min, 3); -static SENSOR_DEVICE_ATTR_RW(temp4_max, max, 3); -static SENSOR_DEVICE_ATTR_RW(temp4_max_hyst, max_hyst, 3); -static SENSOR_DEVICE_ATTR_RO(temp4_min_alarm, min_alarm, 3); -static SENSOR_DEVICE_ATTR_RO(temp4_max_alarm, max_alarm, 3); - -static struct attribute *abx500_temp_attributes[] = { - &sensor_dev_attr_name.dev_attr.attr, - - &sensor_dev_attr_temp1_label.dev_attr.attr, - &sensor_dev_attr_temp1_input.dev_attr.attr, - &sensor_dev_attr_temp1_min.dev_attr.attr, - &sensor_dev_attr_temp1_max.dev_attr.attr, - &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, - &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, - &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, - - &sensor_dev_attr_temp2_label.dev_attr.attr, - &sensor_dev_attr_temp2_input.dev_attr.attr, - &sensor_dev_attr_temp2_min.dev_attr.attr, - &sensor_dev_attr_temp2_max.dev_attr.attr, - &sensor_dev_attr_temp2_max_hyst.dev_attr.attr, - &sensor_dev_attr_temp2_min_alarm.dev_attr.attr, - &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, - - &sensor_dev_attr_temp3_label.dev_attr.attr, - &sensor_dev_attr_temp3_input.dev_attr.attr, - &sensor_dev_attr_temp3_min.dev_attr.attr, - &sensor_dev_attr_temp3_max.dev_attr.attr, - &sensor_dev_attr_temp3_max_hyst.dev_attr.attr, - &sensor_dev_attr_temp3_min_alarm.dev_attr.attr, - &sensor_dev_attr_temp3_max_alarm.dev_attr.attr, - - &sensor_dev_attr_temp4_label.dev_attr.attr, - &sensor_dev_attr_temp4_input.dev_attr.attr, - &sensor_dev_attr_temp4_min.dev_attr.attr, - &sensor_dev_attr_temp4_max.dev_attr.attr, - &sensor_dev_attr_temp4_max_hyst.dev_attr.attr, - &sensor_dev_attr_temp4_min_alarm.dev_attr.attr, - &sensor_dev_attr_temp4_max_alarm.dev_attr.attr, - NULL -}; - -static const struct attribute_group abx500_temp_group = { - .attrs = abx500_temp_attributes, - .is_visible = abx500_attrs_visible, -}; - -static irqreturn_t abx500_temp_irq_handler(int irq, void *irq_data) -{ - struct platform_device *pdev = irq_data; - struct abx500_temp *data = platform_get_drvdata(pdev); - - data->ops.irq_handler(irq, data); - return IRQ_HANDLED; -} - -static int setup_irqs(struct platform_device *pdev) -{ - int ret; - int irq = platform_get_irq_byname(pdev, "ABX500_TEMP_WARM"); - - if (irq < 0) { - dev_err(&pdev->dev, "Get irq by name failed\n"); - return irq; - } - - ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, - abx500_temp_irq_handler, 0, "abx500-temp", pdev); - if (ret < 0) - dev_err(&pdev->dev, "Request threaded irq failed (%d)\n", ret); - - return ret; -} - -static int abx500_temp_probe(struct platform_device *pdev) -{ - struct abx500_temp *data; - int err; - - data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; - - data->pdev = pdev; - mutex_init(&data->lock); - - /* Chip specific initialization */ - err = abx500_hwmon_init(data); - if (err < 0 || !data->ops.read_sensor || !data->ops.show_name || - !data->ops.show_label) - return err; - - INIT_DEFERRABLE_WORK(&data->work, gpadc_monitor); - - platform_set_drvdata(pdev, data); - - err = sysfs_create_group(&pdev->dev.kobj, &abx500_temp_group); - if (err < 0) { - dev_err(&pdev->dev, "Create sysfs group failed (%d)\n", err); - return err; - } - - data->hwmon_dev = hwmon_device_register(&pdev->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - dev_err(&pdev->dev, "Class registration failed (%d)\n", err); - goto exit_sysfs_group; - } - - if (data->ops.irq_handler) { - err = setup_irqs(pdev); - if (err < 0) - goto exit_hwmon_reg; - } - return 0; - -exit_hwmon_reg: - hwmon_device_unregister(data->hwmon_dev); -exit_sysfs_group: - sysfs_remove_group(&pdev->dev.kobj, &abx500_temp_group); - return err; -} - -static int abx500_temp_remove(struct platform_device *pdev) -{ - struct abx500_temp *data = platform_get_drvdata(pdev); - - cancel_delayed_work_sync(&data->work); - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&pdev->dev.kobj, &abx500_temp_group); - - return 0; -} - -static int abx500_temp_suspend(struct platform_device *pdev, - pm_message_t state) -{ - struct abx500_temp *data = platform_get_drvdata(pdev); - - if (data->work_active) - cancel_delayed_work_sync(&data->work); - - return 0; -} - -static int abx500_temp_resume(struct platform_device *pdev) -{ - struct abx500_temp *data = platform_get_drvdata(pdev); - - if (data->work_active) - schedule_monitor(data); - - return 0; -} - -#ifdef CONFIG_OF -static const struct of_device_id abx500_temp_match[] = { - { .compatible = "stericsson,abx500-temp" }, - {}, -}; -MODULE_DEVICE_TABLE(of, abx500_temp_match); -#endif - -static struct platform_driver abx500_temp_driver = { - .driver = { - .name = "abx500-temp", - .of_match_table = of_match_ptr(abx500_temp_match), - }, - .suspend = abx500_temp_suspend, - .resume = abx500_temp_resume, - .probe = abx500_temp_probe, - .remove = abx500_temp_remove, -}; - -module_platform_driver(abx500_temp_driver); - -MODULE_AUTHOR("Martin Persson "); -MODULE_DESCRIPTION("ABX500 temperature driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/abx500.h b/drivers/hwmon/abx500.h deleted file mode 100644 index 4517594260f2..000000000000 --- a/drivers/hwmon/abx500.h +++ /dev/null @@ -1,69 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (C) ST-Ericsson 2010 - 2013 - * Author: Martin Persson - * Hongbo Zhang - */ - -#ifndef _ABX500_H -#define _ABX500_H - -#define NUM_SENSORS 5 - -struct abx500_temp; - -/* - * struct abx500_temp_ops - abx500 chip specific ops - * @read_sensor: reads gpadc output - * @irq_handler: irq handler - * @show_name: hwmon device name - * @show_label: hwmon attribute label - * @is_visible: is attribute visible - */ -struct abx500_temp_ops { - int (*read_sensor)(struct abx500_temp *, u8, int *); - int (*irq_handler)(int, struct abx500_temp *); - ssize_t (*show_name)(struct device *, - struct device_attribute *, char *); - ssize_t (*show_label) (struct device *, - struct device_attribute *, char *); - int (*is_visible)(struct attribute *, int); -}; - -/* - * struct abx500_temp - representation of temp mon device - * @pdev: platform device - * @hwmon_dev: hwmon device - * @ops: abx500 chip specific ops - * @gpadc_addr: gpadc channel address - * @min: sensor temperature min value - * @max: sensor temperature max value - * @max_hyst: sensor temperature hysteresis value for max limit - * @min_alarm: sensor temperature min alarm - * @max_alarm: sensor temperature max alarm - * @work: delayed work scheduled to monitor temperature periodically - * @work_active: True if work is active - * @lock: mutex - * @monitored_sensors: number of monitored sensors - * @plat_data: private usage, usually points to platform specific data - */ -struct abx500_temp { - struct platform_device *pdev; - struct device *hwmon_dev; - struct abx500_temp_ops ops; - u8 gpadc_addr[NUM_SENSORS]; - unsigned long min[NUM_SENSORS]; - unsigned long max[NUM_SENSORS]; - unsigned long max_hyst[NUM_SENSORS]; - bool min_alarm[NUM_SENSORS]; - bool max_alarm[NUM_SENSORS]; - struct delayed_work work; - bool work_active; - struct mutex lock; - int monitored_sensors; - void *plat_data; -}; - -int abx500_hwmon_init(struct abx500_temp *data); - -#endif /* _ABX500_H */ -- cgit v1.2.3 From c8d0d3fa946976c6bc69589375d7d063f0cb1492 Mon Sep 17 00:00:00 2001 From: Gabriel Craciunescu Date: Wed, 23 Dec 2020 01:53:15 +0100 Subject: hwmon: (k10temp) Zen3 Ryzen Desktop CPUs support The module has only support for Zen3 server CPUs right now. Add support for Family 0x19, model 0x21 which are Zen3 Ryzen Desktop CPUs. Tested on 5800x, 5900x and 5950x CPUs. Signed-off-by: Gabriel Craciunescu Link: https://lore.kernel.org/r/20201223005315.20077-1-nix.or.die@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/k10temp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c index 3bc2551577a3..5ff3669c2b60 100644 --- a/drivers/hwmon/k10temp.c +++ b/drivers/hwmon/k10temp.c @@ -448,7 +448,8 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id) data->is_zen = true; switch (boot_cpu_data.x86_model) { - case 0x0 ... 0x1: /* Zen3 */ + case 0x0 ... 0x1: /* Zen3 SP3/TR */ + case 0x21: /* Zen3 Ryzen Desktop */ k10temp_get_ccd_support(pdev, data, 8); break; } -- cgit v1.2.3 From e5befc024cb4515d815662ed8746712cc5366bfc Mon Sep 17 00:00:00 2001 From: Eddie James Date: Tue, 22 Dec 2020 09:26:39 -0600 Subject: hwmon: (pmbus) Add a PMBUS_NO_CAPABILITY platform data flag Some PMBus chips don't respond with valid data when reading the CAPABILITY register. Add a flag that device drivers can set so that the PMBus core driver doesn't use CAPABILITY to determine it's behavior. Signed-off-by: Eddie James Link: https://lore.kernel.org/r/20201222152640.27749-2-eajames@linux.ibm.com Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus_core.c | 8 +++++--- include/linux/pmbus.h | 9 +++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 192442b3b7a2..906c9fec9cce 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -2204,9 +2204,11 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, } /* Enable PEC if the controller supports it */ - ret = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY); - if (ret >= 0 && (ret & PB_CAPABILITY_ERROR_CHECK)) - client->flags |= I2C_CLIENT_PEC; + if (!(data->flags & PMBUS_NO_CAPABILITY)) { + ret = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY); + if (ret >= 0 && (ret & PB_CAPABILITY_ERROR_CHECK)) + client->flags |= I2C_CLIENT_PEC; + } /* * Check if the chip is write protected. If it is, we can not clear diff --git a/include/linux/pmbus.h b/include/linux/pmbus.h index 1ea5bae708a1..12cbbf305969 100644 --- a/include/linux/pmbus.h +++ b/include/linux/pmbus.h @@ -34,6 +34,15 @@ */ #define PMBUS_WRITE_PROTECTED BIT(1) +/* + * PMBUS_NO_CAPABILITY + * + * Some PMBus chips don't respond with valid data when reading the CAPABILITY + * register. For such chips, this flag should be set so that the PMBus core + * driver doesn't use CAPABILITY to determine it's behavior. + */ +#define PMBUS_NO_CAPABILITY BIT(2) + struct pmbus_platform_data { u32 flags; /* Device specific flags */ -- cgit v1.2.3 From f7a652182cc7f07cf658b8eee8d545ac109fd02e Mon Sep 17 00:00:00 2001 From: Eddie James Date: Tue, 22 Dec 2020 09:26:40 -0600 Subject: hwmon: (pmbus/ibm-cffps) Set the PMBUS_NO_CAPABILITY flag Several power supplies supported by the IBM CFFPS driver don't report valid data in the CAPABILITY register. This results in PEC being enabled when it's not supported by the device, and since the automatic version detection might fail, disable use of the CAPABILITY register across the board for this driver. Signed-off-by: Eddie James Link: https://lore.kernel.org/r/20201222152640.27749-3-eajames@linux.ibm.com Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/ibm-cffps.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/pmbus/ibm-cffps.c b/drivers/hwmon/pmbus/ibm-cffps.c index d6bbbb223871..ffde5aaa5036 100644 --- a/drivers/hwmon/pmbus/ibm-cffps.c +++ b/drivers/hwmon/pmbus/ibm-cffps.c @@ -472,7 +472,7 @@ static struct pmbus_driver_info ibm_cffps_info[] = { }; static struct pmbus_platform_data ibm_cffps_pdata = { - .flags = PMBUS_SKIP_STATUS_CHECK, + .flags = PMBUS_SKIP_STATUS_CHECK | PMBUS_NO_CAPABILITY, }; static int ibm_cffps_probe(struct i2c_client *client) -- cgit v1.2.3 From 8bfc731e476793d94312044907708920077a3a3a Mon Sep 17 00:00:00 2001 From: Tian Tao Date: Tue, 29 Dec 2020 11:17:17 +0800 Subject: hwmon: (gpio-fan) Switch to using the new API kobj_to_dev() fixed the following coccicheck: drivers/hwmon/gpio-fan.c:302:60-61: WARNING opportunity for kobj_to_dev() Signed-off-by: Tian Tao Link: https://lore.kernel.org/r/1609211837-54930-1-git-send-email-tiantao6@hisilicon.com Signed-off-by: Guenter Roeck --- drivers/hwmon/gpio-fan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c index 3ea4021f267c..befe989ca7b9 100644 --- a/drivers/hwmon/gpio-fan.c +++ b/drivers/hwmon/gpio-fan.c @@ -299,7 +299,7 @@ static DEVICE_ATTR(fan1_target, 0644, fan1_input_show, set_rpm); static umode_t gpio_fan_is_visible(struct kobject *kobj, struct attribute *attr, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct gpio_fan_data *data = dev_get_drvdata(dev); if (index == 0 && !data->alarm_gpio) -- cgit v1.2.3 From 01695410d4529e38fea6159fa9f1260c598290b0 Mon Sep 17 00:00:00 2001 From: Paul Barker Date: Sat, 12 Dec 2020 19:50:07 +0000 Subject: hwmon: (pwm-fan) Store tach data separately The data for the (optional) fan tachometer input is moved to a separate structure which is only allocated if an input is actually configured. After this change the pulse IRQ handler takes a pointer to the tachometer data structure instead of the whole device context. Signed-off-by: Paul Barker Link: https://lore.kernel.org/r/20201212195008.6036-2-pbarker@konsulko.com Signed-off-by: Guenter Roeck --- drivers/hwmon/pwm-fan.c | 52 ++++++++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c index 111a91dc6b79..a7b7bcac1b40 100644 --- a/drivers/hwmon/pwm-fan.c +++ b/drivers/hwmon/pwm-fan.c @@ -21,15 +21,19 @@ #define MAX_PWM 255 +struct pwm_fan_tach { + int irq; + atomic_t pulses; + unsigned int rpm; + u8 pulses_per_revolution; +}; + struct pwm_fan_ctx { struct mutex lock; struct pwm_device *pwm; struct regulator *reg_en; - int irq; - atomic_t pulses; - unsigned int rpm; - u8 pulses_per_revolution; + struct pwm_fan_tach *tach; ktime_t sample_start; struct timer_list rpm_timer; @@ -65,9 +69,9 @@ static const struct hwmon_channel_info pwm_fan_channel_fan = { /* This handler assumes self resetting edge triggered interrupt. */ static irqreturn_t pulse_handler(int irq, void *dev_id) { - struct pwm_fan_ctx *ctx = dev_id; + struct pwm_fan_tach *tach = dev_id; - atomic_inc(&ctx->pulses); + atomic_inc(&tach->pulses); return IRQ_HANDLED; } @@ -75,14 +79,15 @@ static irqreturn_t pulse_handler(int irq, void *dev_id) static void sample_timer(struct timer_list *t) { struct pwm_fan_ctx *ctx = from_timer(ctx, t, rpm_timer); + struct pwm_fan_tach *tach = ctx->tach; unsigned int delta = ktime_ms_delta(ktime_get(), ctx->sample_start); int pulses; if (delta) { - pulses = atomic_read(&ctx->pulses); - atomic_sub(pulses, &ctx->pulses); - ctx->rpm = (unsigned int)(pulses * 1000 * 60) / - (ctx->pulses_per_revolution * delta); + pulses = atomic_read(&tach->pulses); + atomic_sub(pulses, &tach->pulses); + tach->rpm = (unsigned int)(pulses * 1000 * 60) / + (tach->pulses_per_revolution * delta); ctx->sample_start = ktime_get(); } @@ -152,7 +157,7 @@ static int pwm_fan_read(struct device *dev, enum hwmon_sensor_types type, return 0; case hwmon_fan: - *val = ctx->rpm; + *val = ctx->tach->rpm; return 0; default: @@ -372,14 +377,21 @@ static int pwm_fan_probe(struct platform_device *pdev) channels[0] = &pwm_fan_channel_pwm; if (tach_count > 0) { + struct pwm_fan_tach *tach; u32 ppr = 2; - ctx->irq = platform_get_irq(pdev, 0); - if (ctx->irq == -EPROBE_DEFER) - return ctx->irq; - if (ctx->irq > 0) { - ret = devm_request_irq(dev, ctx->irq, pulse_handler, 0, - pdev->name, ctx); + tach = devm_kzalloc(dev, sizeof(struct pwm_fan_tach), + GFP_KERNEL); + if (!tach) + return -ENOMEM; + ctx->tach = tach; + + tach->irq = platform_get_irq(pdev, 0); + if (tach->irq == -EPROBE_DEFER) + return tach->irq; + if (tach->irq > 0) { + ret = devm_request_irq(dev, tach->irq, pulse_handler, 0, + pdev->name, tach); if (ret) { dev_err(dev, "Failed to request interrupt: %d\n", @@ -391,14 +403,14 @@ static int pwm_fan_probe(struct platform_device *pdev) of_property_read_u32(dev->of_node, "pulses-per-revolution", &ppr); - ctx->pulses_per_revolution = ppr; - if (!ctx->pulses_per_revolution) { + tach->pulses_per_revolution = ppr; + if (!tach->pulses_per_revolution) { dev_err(dev, "pulses-per-revolution can't be zero.\n"); return -EINVAL; } dev_dbg(dev, "tach: irq=%d, pulses_per_revolution=%d\n", - ctx->irq, ctx->pulses_per_revolution); + tach->irq, tach->pulses_per_revolution); ctx->sample_start = ktime_get(); mod_timer(&ctx->rpm_timer, jiffies + HZ); -- cgit v1.2.3 From f0dc7cb6b4c9cd24a69f80423f6d7a03825b72e4 Mon Sep 17 00:00:00 2001 From: Paul Barker Date: Sat, 12 Dec 2020 19:50:08 +0000 Subject: hwmon: (pwm-fan) Support multiple fan tachometers The pwm-fan driver is extended to support multiple fan tachometer signals connected to GPIO inputs. This is intended to support the case where a single PWM output signal is routed to multiple fans, each of which have a tachometer output connected back to a GPIO pin. The number of fan tachometer inputs is determined by the number of interrupt sources configured for the pwm-fan device. The number of pulses-per-revolution entries should match the number of interrupt sources so that each input has a value assigned. The fan tachometer measurements are exposed as sysfs files fan1_input, fan2_input, etc up to the number of configured inputs. Signed-off-by: Paul Barker Link: https://lore.kernel.org/r/20201212195008.6036-3-pbarker@konsulko.com Signed-off-by: Guenter Roeck --- drivers/hwmon/pwm-fan.c | 91 ++++++++++++++++++++++++++++--------------------- 1 file changed, 53 insertions(+), 38 deletions(-) diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c index a7b7bcac1b40..b5204aeb8085 100644 --- a/drivers/hwmon/pwm-fan.c +++ b/drivers/hwmon/pwm-fan.c @@ -33,7 +33,8 @@ struct pwm_fan_ctx { struct pwm_device *pwm; struct regulator *reg_en; - struct pwm_fan_tach *tach; + int tach_count; + struct pwm_fan_tach *tachs; ktime_t sample_start; struct timer_list rpm_timer; @@ -44,6 +45,7 @@ struct pwm_fan_ctx { struct thermal_cooling_device *cdev; struct hwmon_chip_info info; + struct hwmon_channel_info fan_channel; }; static const u32 pwm_fan_channel_config_pwm[] = { @@ -56,16 +58,6 @@ static const struct hwmon_channel_info pwm_fan_channel_pwm = { .config = pwm_fan_channel_config_pwm, }; -static const u32 pwm_fan_channel_config_fan[] = { - HWMON_F_INPUT, - 0 -}; - -static const struct hwmon_channel_info pwm_fan_channel_fan = { - .type = hwmon_fan, - .config = pwm_fan_channel_config_fan, -}; - /* This handler assumes self resetting edge triggered interrupt. */ static irqreturn_t pulse_handler(int irq, void *dev_id) { @@ -79,15 +71,19 @@ static irqreturn_t pulse_handler(int irq, void *dev_id) static void sample_timer(struct timer_list *t) { struct pwm_fan_ctx *ctx = from_timer(ctx, t, rpm_timer); - struct pwm_fan_tach *tach = ctx->tach; unsigned int delta = ktime_ms_delta(ktime_get(), ctx->sample_start); - int pulses; + int i; if (delta) { - pulses = atomic_read(&tach->pulses); - atomic_sub(pulses, &tach->pulses); - tach->rpm = (unsigned int)(pulses * 1000 * 60) / - (tach->pulses_per_revolution * delta); + for (i = 0; i < ctx->tach_count; i++) { + struct pwm_fan_tach *tach = &ctx->tachs[i]; + int pulses; + + pulses = atomic_read(&tach->pulses); + atomic_sub(pulses, &tach->pulses); + tach->rpm = (unsigned int)(pulses * 1000 * 60) / + (tach->pulses_per_revolution * delta); + } ctx->sample_start = ktime_get(); } @@ -157,7 +153,7 @@ static int pwm_fan_read(struct device *dev, enum hwmon_sensor_types type, return 0; case hwmon_fan: - *val = ctx->tach->rpm; + *val = ctx->tachs[channel].rpm; return 0; default: @@ -304,8 +300,10 @@ static int pwm_fan_probe(struct platform_device *pdev) struct device *hwmon; int ret; struct pwm_state state = { }; - int tach_count; const struct hwmon_channel_info **channels; + u32 *fan_channel_config; + int channel_count = 1; /* We always have a PWM channel. */ + int i; ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) @@ -364,29 +362,41 @@ static int pwm_fan_probe(struct platform_device *pdev) if (ret) return ret; - tach_count = platform_irq_count(pdev); - if (tach_count < 0) - return dev_err_probe(dev, tach_count, + ctx->tach_count = platform_irq_count(pdev); + if (ctx->tach_count < 0) + return dev_err_probe(dev, ctx->tach_count, "Could not get number of fan tachometer inputs\n"); + dev_dbg(dev, "%d fan tachometer inputs\n", ctx->tach_count); - channels = devm_kcalloc(dev, tach_count + 2, + if (ctx->tach_count) { + channel_count++; /* We also have a FAN channel. */ + + ctx->tachs = devm_kcalloc(dev, ctx->tach_count, + sizeof(struct pwm_fan_tach), + GFP_KERNEL); + if (!ctx->tachs) + return -ENOMEM; + + ctx->fan_channel.type = hwmon_fan; + fan_channel_config = devm_kcalloc(dev, ctx->tach_count + 1, + sizeof(u32), GFP_KERNEL); + if (!fan_channel_config) + return -ENOMEM; + ctx->fan_channel.config = fan_channel_config; + } + + channels = devm_kcalloc(dev, channel_count + 1, sizeof(struct hwmon_channel_info *), GFP_KERNEL); if (!channels) return -ENOMEM; channels[0] = &pwm_fan_channel_pwm; - if (tach_count > 0) { - struct pwm_fan_tach *tach; + for (i = 0; i < ctx->tach_count; i++) { + struct pwm_fan_tach *tach = &ctx->tachs[i]; u32 ppr = 2; - tach = devm_kzalloc(dev, sizeof(struct pwm_fan_tach), - GFP_KERNEL); - if (!tach) - return -ENOMEM; - ctx->tach = tach; - - tach->irq = platform_get_irq(pdev, 0); + tach->irq = platform_get_irq(pdev, i); if (tach->irq == -EPROBE_DEFER) return tach->irq; if (tach->irq > 0) { @@ -400,22 +410,27 @@ static int pwm_fan_probe(struct platform_device *pdev) } } - of_property_read_u32(dev->of_node, - "pulses-per-revolution", - &ppr); + of_property_read_u32_index(dev->of_node, + "pulses-per-revolution", + i, + &ppr); tach->pulses_per_revolution = ppr; if (!tach->pulses_per_revolution) { dev_err(dev, "pulses-per-revolution can't be zero.\n"); return -EINVAL; } - dev_dbg(dev, "tach: irq=%d, pulses_per_revolution=%d\n", - tach->irq, tach->pulses_per_revolution); + fan_channel_config[i] = HWMON_F_INPUT; + + dev_dbg(dev, "tach%d: irq=%d, pulses_per_revolution=%d\n", + i, tach->irq, tach->pulses_per_revolution); + } + if (ctx->tach_count > 0) { ctx->sample_start = ktime_get(); mod_timer(&ctx->rpm_timer, jiffies + HZ); - channels[1] = &pwm_fan_channel_fan; + channels[1] = &ctx->fan_channel; } ctx->info.ops = &pwm_fan_hwmon_ops; -- cgit v1.2.3 From f1fc688c65fd3abe058420c785276c81d4a9c66e Mon Sep 17 00:00:00 2001 From: Tian Tao Date: Thu, 31 Dec 2020 09:03:41 +0800 Subject: hwmon: (max6650) Switch to using the new API kobj_to_dev() Switch to using the new API kobj_to_dev() to fix the below warnning: drivers/hwmon/max6650.c:324:60-61: WARNING opportunity for kobj_to_dev(). Signed-off-by: Tian Tao Link: https://lore.kernel.org/r/1609376621-46463-1-git-send-email-tiantao6@hisilicon.com Signed-off-by: Guenter Roeck --- drivers/hwmon/max6650.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/max6650.c b/drivers/hwmon/max6650.c index cc7f2980fe83..f8d4534ce172 100644 --- a/drivers/hwmon/max6650.c +++ b/drivers/hwmon/max6650.c @@ -321,7 +321,7 @@ static SENSOR_DEVICE_ATTR_RO(gpio2_alarm, alarm, MAX6650_ALRM_GPIO2); static umode_t max6650_attrs_visible(struct kobject *kobj, struct attribute *a, int n) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct max6650_data *data = dev_get_drvdata(dev); struct device_attribute *devattr; -- cgit v1.2.3 From da75b2245281ec28b74117f6da219405464928be Mon Sep 17 00:00:00 2001 From: Tian Tao Date: Mon, 4 Jan 2021 10:05:45 +0800 Subject: hwmon: (aspeed-pwm-tacho) Switch to using the new API kobj_to_dev() fixed the following coccicheck: drivers/hwmon/aspeed-pwm-tacho.c:634:60-61: WARNING opportunity for kobj_to_dev() drivers/hwmon/aspeed-pwm-tacho.c:623:60-61: WARNING opportunity for kobj_to_dev() Signed-off-by: Tian Tao Acked-by: Joel Stanley Link: https://lore.kernel.org/r/1609725945-13895-1-git-send-email-tiantao6@hisilicon.com Signed-off-by: Guenter Roeck --- drivers/hwmon/aspeed-pwm-tacho.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/aspeed-pwm-tacho.c b/drivers/hwmon/aspeed-pwm-tacho.c index 3d8239fd66ed..3cb88d6fbec0 100644 --- a/drivers/hwmon/aspeed-pwm-tacho.c +++ b/drivers/hwmon/aspeed-pwm-tacho.c @@ -620,7 +620,7 @@ static ssize_t rpm_show(struct device *dev, struct device_attribute *attr, static umode_t pwm_is_visible(struct kobject *kobj, struct attribute *a, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct aspeed_pwm_tacho_data *priv = dev_get_drvdata(dev); if (!priv->pwm_present[index]) @@ -631,7 +631,7 @@ static umode_t pwm_is_visible(struct kobject *kobj, static umode_t fan_dev_is_visible(struct kobject *kobj, struct attribute *a, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct aspeed_pwm_tacho_data *priv = dev_get_drvdata(dev); if (!priv->fan_tach_present[index]) -- cgit v1.2.3 From bd433537fef88d76e7f427bafda18791ae60e721 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Tue, 19 Jan 2021 14:12:41 +0900 Subject: hwmon: (nct6683) Support ASRock boards MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tested with ASRock X570 Phantom Gaming-ITX/TB3. It also appears on other ASRock boards. Signed-off-by: Blaž Hrastnik Link: https://lore.kernel.org/r/b08d641c-3fb5-4845-85f7-e1753149cd7d@www.fastmail.com Signed-off-by: Guenter Roeck --- Documentation/hwmon/nct6683.rst | 1 + drivers/hwmon/nct6683.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/Documentation/hwmon/nct6683.rst b/Documentation/hwmon/nct6683.rst index 8646ad519fcd..2e1408d174bd 100644 --- a/Documentation/hwmon/nct6683.rst +++ b/Documentation/hwmon/nct6683.rst @@ -61,5 +61,6 @@ Board Firmware version Intel DH87RL NCT6683D EC firmware version 1.0 build 04/03/13 Intel DH87MC NCT6683D EC firmware version 1.0 build 04/03/13 Intel DB85FL NCT6683D EC firmware version 1.0 build 04/03/13 +ASRock X570 NCT6683D EC firmware version 1.0 build 06/28/19 MSI B550 NCT6687D EC firmware version 1.0 build 05/07/20 =============== =============================================== diff --git a/drivers/hwmon/nct6683.c b/drivers/hwmon/nct6683.c index 7f7e30f0de7b..a23047a3bfe2 100644 --- a/drivers/hwmon/nct6683.c +++ b/drivers/hwmon/nct6683.c @@ -169,6 +169,7 @@ superio_exit(int ioreg) #define NCT6683_CUSTOMER_ID_INTEL 0x805 #define NCT6683_CUSTOMER_ID_MITAC 0xa0e #define NCT6683_CUSTOMER_ID_MSI 0x201 +#define NCT6683_CUSTOMER_ID_ASROCK 0xe2c #define NCT6683_REG_BUILD_YEAR 0x604 #define NCT6683_REG_BUILD_MONTH 0x605 @@ -1225,6 +1226,8 @@ static int nct6683_probe(struct platform_device *pdev) break; case NCT6683_CUSTOMER_ID_MSI: break; + case NCT6683_CUSTOMER_ID_ASROCK: + break; default: if (!force) return -ENODEV; -- cgit v1.2.3 From 0bf6a1e85ddd4000f461086d08260452aaf30302 Mon Sep 17 00:00:00 2001 From: Jiapeng Zhong Date: Wed, 20 Jan 2021 14:41:10 +0800 Subject: hwmon: (applesmc) Assign boolean values to a bool variable Fix the following coccicheck warnings: ./drivers/hwmon/applesmc.c:568:6-23: WARNING: Assignment of 0/1 to bool variable. Reported-by: Abaci Robot Signed-off-by: Jiapeng Zhong Link: https://lore.kernel.org/r/1611124870-125658-1-git-send-email-abaci-bugfix@linux.alibaba.com Signed-off-by: Guenter Roeck --- drivers/hwmon/applesmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c index 89207af81c48..28b137eedf2e 100644 --- a/drivers/hwmon/applesmc.c +++ b/drivers/hwmon/applesmc.c @@ -565,7 +565,7 @@ static int applesmc_init_index(struct applesmc_registers *s) static int applesmc_init_smcreg_try(void) { struct applesmc_registers *s = &smcreg; - bool left_light_sensor = 0, right_light_sensor = 0; + bool left_light_sensor = false, right_light_sensor = false; unsigned int count; u8 tmp[1]; int ret; -- cgit v1.2.3 From 8c78f0dee4371ab3b0422edf08597525c6219512 Mon Sep 17 00:00:00 2001 From: "Johannes Cornelis Draaijer (datdenkikniet)" Date: Thu, 7 Jan 2021 20:40:14 +0100 Subject: hwmon: Add AHT10 Temperature and Humidity Sensor Driver This patch adds a hwmon driver for the AHT10 Temperature and Humidity sensor. It has a maximum sample rate, as the datasheet states that the chip may heat up if it is sampled more than once every two seconds. Has been tested a to work on a raspberrypi0w Signed-off-by: Johannes Cornelis Draaijer (datdenkikniet) Link: https://lore.kernel.org/r/20210107194014.GA88780@desktop [groeck: dropped AHT10_ADDR (unused) and use AHT10_MEAS_SIZE where appropriate; dropped change log] Signed-off-by: Guenter Roeck --- Documentation/hwmon/aht10.rst | 46 ++++++ Documentation/hwmon/index.rst | 1 + drivers/hwmon/Kconfig | 10 ++ drivers/hwmon/Makefile | 1 + drivers/hwmon/aht10.c | 346 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 404 insertions(+) create mode 100644 Documentation/hwmon/aht10.rst create mode 100644 drivers/hwmon/aht10.c diff --git a/Documentation/hwmon/aht10.rst b/Documentation/hwmon/aht10.rst new file mode 100644 index 000000000000..482262ca117c --- /dev/null +++ b/Documentation/hwmon/aht10.rst @@ -0,0 +1,46 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Kernel driver aht10 +===================== + +Supported chips: + + * Aosong AHT10 + + Prefix: 'aht10' + + Addresses scanned: None + + Datasheet: + + Chinese: http://www.aosong.com/userfiles/files/media/AHT10%E4%BA%A7%E5%93%81%E6%89%8B%E5%86%8C%20A3%2020201210.pdf + English: https://server4.eca.ir/eshop/AHT10/Aosong_AHT10_en_draft_0c.pdf + +Author: Johannes Cornelis Draaijer + + +Description +----------- + +The AHT10 is a Temperature and Humidity sensor + +The address of this i2c device may only be 0x38 + +Usage Notes +----------- + +This driver does not probe for AHT10 devices, as there is no reliable +way to determine if an i2c chip is or isn't an AHT10. The device has +to be instantiated explicitly with the address 0x38. See +Documentation/i2c/instantiating-devices.rst for details. + +Sysfs entries +------------- + +=============== ============================================ +temp1_input Measured temperature in millidegrees Celcius +humidity1_input Measured humidity in %H +update_interval The minimum interval for polling the sensor, + in milliseconds. Writable. Must be at + least 2000. +=============== ============================================ diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index ebe53bb771dd..3589151bcefb 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -37,6 +37,7 @@ Hardware Monitoring Kernel Drivers adt7462 adt7470 adt7475 + aht10 amc6821 amd_energy asb100 diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index c6c4f404399b..fd0da6295d5e 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -244,6 +244,16 @@ config SENSORS_ADT7475 This driver can also be built as a module. If so, the module will be called adt7475. +config SENSORS_AHT10 + tristate "Aosong AHT10" + depends on I2C + help + If you say yes here, you get support for the Aosong AHT10 + temperature and humidity sensors + + This driver can also be built as a module. If so, the module + will be called aht10. + config SENSORS_AS370 tristate "Synaptics AS370 SoC hardware monitoring driver" help diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 990891a8c89d..51dd97437bfd 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -44,6 +44,7 @@ obj-$(CONFIG_SENSORS_ADT7411) += adt7411.o obj-$(CONFIG_SENSORS_ADT7462) += adt7462.o obj-$(CONFIG_SENSORS_ADT7470) += adt7470.o obj-$(CONFIG_SENSORS_ADT7475) += adt7475.o +obj-$(CONFIG_SENSORS_AHT10) += aht10.o obj-$(CONFIG_SENSORS_AMD_ENERGY) += amd_energy.o obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o obj-$(CONFIG_SENSORS_ARM_SCMI) += scmi-hwmon.o diff --git a/drivers/hwmon/aht10.c b/drivers/hwmon/aht10.c new file mode 100644 index 000000000000..c70d8c2d0c1f --- /dev/null +++ b/drivers/hwmon/aht10.c @@ -0,0 +1,346 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* + * aht10.c - Linux hwmon driver for AHT10 Temperature and Humidity sensor + * Copyright (C) 2020 Johannes Cornelis Draaijer + */ + +#include +#include +#include +#include +#include + +#define AHT10_MEAS_SIZE 6 + +/* + * Poll intervals (in milliseconds) + */ +#define AHT10_DEFAULT_MIN_POLL_INTERVAL 2000 +#define AHT10_MIN_POLL_INTERVAL 2000 + +/* + * I2C command delays (in microseconds) + */ +#define AHT10_MEAS_DELAY 80000 +#define AHT10_CMD_DELAY 350000 +#define AHT10_DELAY_EXTRA 100000 + +/* + * Command bytes + */ +#define AHT10_CMD_INIT 0b11100001 +#define AHT10_CMD_MEAS 0b10101100 +#define AHT10_CMD_RST 0b10111010 + +/* + * Flags in the answer byte/command + */ +#define AHT10_CAL_ENABLED BIT(3) +#define AHT10_BUSY BIT(7) +#define AHT10_MODE_NOR (BIT(5) | BIT(6)) +#define AHT10_MODE_CYC BIT(5) +#define AHT10_MODE_CMD BIT(6) + +#define AHT10_MAX_POLL_INTERVAL_LEN 30 + +/** + * struct aht10_data - All the data required to operate an AHT10 chip + * @client: the i2c client associated with the AHT10 + * @lock: a mutex that is used to prevent parallel access to the + * i2c client + * @min_poll_interval: the minimum poll interval + * While the poll rate limit is not 100% necessary, + * the datasheet recommends that a measurement + * is not performed too often to prevent + * the chip from warming up due to the heat it generates. + * If it's unwanted, it can be ignored setting it to + * it to 0. Default value is 2000 ms + * @previous_poll_time: the previous time that the AHT10 + * was polled + * @temperature: the latest temperature value received from + * the AHT10 + * @humidity: the latest humidity value received from the + * AHT10 + */ + +struct aht10_data { + struct i2c_client *client; + /* + * Prevent simultaneous access to the i2c + * client and previous_poll_time + */ + struct mutex lock; + ktime_t min_poll_interval; + ktime_t previous_poll_time; + int temperature; + int humidity; +}; + +/** + * aht10_init() - Initialize an AHT10 chip + * @client: the i2c client associated with the AHT10 + * @data: the data associated with this AHT10 chip + * Return: 0 if succesfull, 1 if not + */ +static int aht10_init(struct aht10_data *data) +{ + const u8 cmd_init[] = {AHT10_CMD_INIT, AHT10_CAL_ENABLED | AHT10_MODE_CYC, + 0x00}; + int res; + u8 status; + struct i2c_client *client = data->client; + + res = i2c_master_send(client, cmd_init, 3); + if (res < 0) + return res; + + usleep_range(AHT10_CMD_DELAY, AHT10_CMD_DELAY + + AHT10_DELAY_EXTRA); + + res = i2c_master_recv(client, &status, 1); + if (res != 1) + return -ENODATA; + + if (status & AHT10_BUSY) + return -EBUSY; + + return 0; +} + +/** + * aht10_polltime_expired() - check if the minimum poll interval has + * expired + * @data: the data containing the time to compare + * Return: 1 if the minimum poll interval has expired, 0 if not + */ +static int aht10_polltime_expired(struct aht10_data *data) +{ + ktime_t current_time = ktime_get_boottime(); + ktime_t difference = ktime_sub(current_time, data->previous_poll_time); + + return ktime_after(difference, data->min_poll_interval); +} + +/** + * aht10_read_values() - read and parse the raw data from the AHT10 + * @aht10_data: the struct aht10_data to use for the lock + * Return: 0 if succesfull, 1 if not + */ +static int aht10_read_values(struct aht10_data *data) +{ + const u8 cmd_meas[] = {AHT10_CMD_MEAS, 0x33, 0x00}; + u32 temp, hum; + int res; + u8 raw_data[AHT10_MEAS_SIZE]; + struct i2c_client *client = data->client; + + mutex_lock(&data->lock); + if (aht10_polltime_expired(data)) { + res = i2c_master_send(client, cmd_meas, sizeof(cmd_meas)); + if (res < 0) + return res; + + usleep_range(AHT10_MEAS_DELAY, + AHT10_MEAS_DELAY + AHT10_DELAY_EXTRA); + + res = i2c_master_recv(client, raw_data, AHT10_MEAS_SIZE); + if (res != AHT10_MEAS_SIZE) { + mutex_unlock(&data->lock); + if (res >= 0) + return -ENODATA; + else + return res; + } + + hum = ((u32)raw_data[1] << 12u) | + ((u32)raw_data[2] << 4u) | + ((raw_data[3] & 0xF0u) >> 4u); + + temp = ((u32)(raw_data[3] & 0x0Fu) << 16u) | + ((u32)raw_data[4] << 8u) | + raw_data[5]; + + temp = ((temp * 625) >> 15u) * 10; + hum = ((hum * 625) >> 16u) * 10; + + data->temperature = (int)temp - 50000; + data->humidity = hum; + data->previous_poll_time = ktime_get_boottime(); + } + mutex_unlock(&data->lock); + return 0; +} + +/** + * aht10_interval_write() - store the given minimum poll interval. + * Return: 0 on success, -EINVAL if a value lower than the + * AHT10_MIN_POLL_INTERVAL is given + */ +static ssize_t aht10_interval_write(struct aht10_data *data, + long val) +{ + data->min_poll_interval = ms_to_ktime(clamp_val(val, 2000, LONG_MAX)); + return 0; +} + +/** + * aht10_interval_read() - read the minimum poll interval + * in milliseconds + */ +static ssize_t aht10_interval_read(struct aht10_data *data, + long *val) +{ + *val = ktime_to_ms(data->min_poll_interval); + return 0; +} + +/** + * aht10_temperature1_read() - read the temperature in millidegrees + */ +static int aht10_temperature1_read(struct aht10_data *data, long *val) +{ + int res; + + res = aht10_read_values(data); + if (res < 0) + return res; + + *val = data->temperature; + return 0; +} + +/** + * aht10_humidity1_read() - read the relative humidity in millipercent + */ +static int aht10_humidity1_read(struct aht10_data *data, long *val) +{ + int res; + + res = aht10_read_values(data); + if (res < 0) + return res; + + *val = data->humidity; + return 0; +} + +static umode_t aht10_hwmon_visible(const void *data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_temp: + case hwmon_humidity: + return 0444; + case hwmon_chip: + return 0644; + default: + return 0; + } +} + +static int aht10_hwmon_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct aht10_data *data = dev_get_drvdata(dev); + + switch (type) { + case hwmon_temp: + return aht10_temperature1_read(data, val); + case hwmon_humidity: + return aht10_humidity1_read(data, val); + case hwmon_chip: + return aht10_interval_read(data, val); + default: + return -EOPNOTSUPP; + } +} + +static int aht10_hwmon_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct aht10_data *data = dev_get_drvdata(dev); + + switch (type) { + case hwmon_chip: + return aht10_interval_write(data, val); + default: + return -EOPNOTSUPP; + } +} + +static const struct hwmon_channel_info *aht10_info[] = { + HWMON_CHANNEL_INFO(chip, HWMON_C_UPDATE_INTERVAL), + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), + HWMON_CHANNEL_INFO(humidity, HWMON_H_INPUT), + NULL, +}; + +static const struct hwmon_ops aht10_hwmon_ops = { + .is_visible = aht10_hwmon_visible, + .read = aht10_hwmon_read, + .write = aht10_hwmon_write, +}; + +static const struct hwmon_chip_info aht10_chip_info = { + .ops = &aht10_hwmon_ops, + .info = aht10_info, +}; + +static int aht10_probe(struct i2c_client *client, + const struct i2c_device_id *aht10_id) +{ + struct device *device = &client->dev; + struct device *hwmon_dev; + struct aht10_data *data; + int res; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENOENT; + + data = devm_kzalloc(device, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->min_poll_interval = ms_to_ktime(AHT10_DEFAULT_MIN_POLL_INTERVAL); + data->client = client; + + mutex_init(&data->lock); + + res = aht10_init(data); + if (res < 0) + return res; + + res = aht10_read_values(data); + if (res < 0) + return res; + + hwmon_dev = devm_hwmon_device_register_with_info(device, + client->name, + data, + &aht10_chip_info, + NULL); + + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct i2c_device_id aht10_id[] = { + { "aht10", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, aht10_id); + +static struct i2c_driver aht10_driver = { + .driver = { + .name = "aht10", + }, + .probe = aht10_probe, + .id_table = aht10_id, +}; + +module_i2c_driver(aht10_driver); + +MODULE_AUTHOR("Johannes Cornelis Draaijer "); +MODULE_DESCRIPTION("AHT10 Temperature and Humidity sensor driver"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 86585c61972f0997ed5fef54a2eb5de0b268ad86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 12 Jan 2021 20:13:14 +0100 Subject: hwmon: (pwm-fan) stop using legacy PWM functions and some cleanups MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pwm_apply_state() does what the legacy functions pwm_config() and pwm_{en,dis}able() do in a single function call. This simplifies error handling and is more efficient for new-style PWM hardware drivers. Instead of repeatedly querying the PWM framework about the initial PWM configuration, cache the settings in driver data. Also use __set_pwm() in .probe() to have the algorithm calculating the PWM state in a single place. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20210112191314.124686-1-u.kleine-koenig@pengutronix.de [groeck: Added missing empty line after declaration] Signed-off-by: Guenter Roeck --- drivers/hwmon/pwm-fan.c | 48 ++++++++++++++++++++---------------------------- 1 file changed, 20 insertions(+), 28 deletions(-) diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c index b5204aeb8085..8c1e38c748ec 100644 --- a/drivers/hwmon/pwm-fan.c +++ b/drivers/hwmon/pwm-fan.c @@ -31,6 +31,7 @@ struct pwm_fan_tach { struct pwm_fan_ctx { struct mutex lock; struct pwm_device *pwm; + struct pwm_state pwm_state; struct regulator *reg_en; int tach_count; @@ -95,18 +96,17 @@ static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm) { unsigned long period; int ret = 0; - struct pwm_state state = { }; + struct pwm_state *state = &ctx->pwm_state; mutex_lock(&ctx->lock); if (ctx->pwm_value == pwm) goto exit_set_pwm_err; - pwm_init_state(ctx->pwm, &state); - period = ctx->pwm->args.period; - state.duty_cycle = DIV_ROUND_UP(pwm * (period - 1), MAX_PWM); - state.enabled = pwm ? true : false; + period = state->period; + state->duty_cycle = DIV_ROUND_UP(pwm * (period - 1), MAX_PWM); + state->enabled = pwm ? true : false; - ret = pwm_apply_state(ctx->pwm, &state); + ret = pwm_apply_state(ctx->pwm, state); if (!ret) ctx->pwm_value = pwm; exit_set_pwm_err: @@ -288,7 +288,9 @@ static void pwm_fan_regulator_disable(void *data) static void pwm_fan_pwm_disable(void *__ctx) { struct pwm_fan_ctx *ctx = __ctx; - pwm_disable(ctx->pwm); + + ctx->pwm_state.enabled = false; + pwm_apply_state(ctx->pwm, &ctx->pwm_state); del_timer_sync(&ctx->rpm_timer); } @@ -299,7 +301,6 @@ static int pwm_fan_probe(struct platform_device *pdev) struct pwm_fan_ctx *ctx; struct device *hwmon; int ret; - struct pwm_state state = { }; const struct hwmon_channel_info **channels; u32 *fan_channel_config; int channel_count = 1; /* We always have a PWM channel. */ @@ -337,22 +338,20 @@ static int pwm_fan_probe(struct platform_device *pdev) ctx->pwm_value = MAX_PWM; - pwm_init_state(ctx->pwm, &state); + pwm_init_state(ctx->pwm, &ctx->pwm_state); + /* * __set_pwm assumes that MAX_PWM * (period - 1) fits into an unsigned * long. Check this here to prevent the fan running at a too low * frequency. */ - if (state.period > ULONG_MAX / MAX_PWM + 1) { + if (ctx->pwm_state.period > ULONG_MAX / MAX_PWM + 1) { dev_err(dev, "Configured period too big\n"); return -EINVAL; } /* Set duty cycle to maximum allowed and enable PWM output */ - state.duty_cycle = ctx->pwm->args.period - 1; - state.enabled = true; - - ret = pwm_apply_state(ctx->pwm, &state); + ret = __set_pwm(ctx, MAX_PWM); if (ret) { dev_err(dev, "Failed to configure PWM: %d\n", ret); return ret; @@ -468,17 +467,17 @@ static int pwm_fan_probe(struct platform_device *pdev) static int pwm_fan_disable(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); + /* keep ctx->pwm_state unmodified for pwm_fan_resume() */ + struct pwm_state state = ctx->pwm_state; + + state.duty_cycle = 0; + state.enabled = false; + ret = pwm_apply_state(ctx->pwm, &state); if (ret < 0) return ret; - - pwm_disable(ctx->pwm); } if (ctx->reg_en) { @@ -506,8 +505,6 @@ static int pwm_fan_suspend(struct device *dev) static int pwm_fan_resume(struct device *dev) { struct pwm_fan_ctx *ctx = dev_get_drvdata(dev); - struct pwm_args pargs; - unsigned long duty; int ret; if (ctx->reg_en) { @@ -521,12 +518,7 @@ static int pwm_fan_resume(struct device *dev) if (ctx->pwm_value == 0) return 0; - pwm_get_args(ctx->pwm, &pargs); - duty = DIV_ROUND_UP_ULL(ctx->pwm_value * (pargs.period - 1), MAX_PWM); - ret = pwm_config(ctx->pwm, duty, pargs.period); - if (ret) - return ret; - return pwm_enable(ctx->pwm); + return pwm_apply_state(ctx->pwm, &ctx->pwm_state); } #endif -- cgit v1.2.3 From cbfc6c6177f9208762c7c4acec394214b1e8be0c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 27 Jan 2021 08:25:26 +0300 Subject: hwmon: (aht10) Unlock on error in aht10_read_values() This error path needs to drop the lock before returning. Fixes: afd018716398 ("hwmon: Add AHT10 Temperature and Humidity Sensor Driver") Signed-off-by: Dan Carpenter Link: https://lore.kernel.org/r/YBD5Ro549hMJSnW4@mwanda Signed-off-by: Guenter Roeck --- drivers/hwmon/aht10.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/aht10.c b/drivers/hwmon/aht10.c index c70d8c2d0c1f..2d9770cb4401 100644 --- a/drivers/hwmon/aht10.c +++ b/drivers/hwmon/aht10.c @@ -138,8 +138,10 @@ static int aht10_read_values(struct aht10_data *data) mutex_lock(&data->lock); if (aht10_polltime_expired(data)) { res = i2c_master_send(client, cmd_meas, sizeof(cmd_meas)); - if (res < 0) + if (res < 0) { + mutex_unlock(&data->lock); return res; + } usleep_range(AHT10_MEAS_DELAY, AHT10_MEAS_DELAY + AHT10_DELAY_EXTRA); -- cgit v1.2.3 From 906ace80c2849d6ef114cfdb98178bd667f16aed Mon Sep 17 00:00:00 2001 From: Jiapeng Zhong Date: Tue, 26 Jan 2021 14:21:40 +0800 Subject: hwmon: (pmbus) Simplify the calculation of variables Fix the following coccicheck warnings: ./drivers/hwmon/pmbus/pmbus_core.c:1265:24-26: WARNING !A || A && B is equivalent to !A || B. Reported-by: Abaci Robot Signed-off-by: Jiapeng Zhong Link: https://lore.kernel.org/r/1611642100-29937-1-git-send-email-abaci-bugfix@linux.alibaba.com Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 906c9fec9cce..1cf7d9e9d089 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -1262,7 +1262,7 @@ static int pmbus_add_sensor_attrs_one(struct i2c_client *client, * which global bit is set) for this page is accessible. */ if (!ret && attr->gbit && - (!upper || (upper && data->has_status_word)) && + (!upper || data->has_status_word) && pmbus_check_status_register(client, page)) { ret = pmbus_add_boolean(data, name, "alarm", index, NULL, NULL, -- cgit v1.2.3 From 220c404dc7a5f94779db741fa96cb63ef03b6d08 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 25 Jan 2021 10:53:26 -0800 Subject: hwmon: (pmbus/max16601) Determine and use number of populated phases The MAX16601 can report the number of populated phases. Use this information to only create sysfs attributes for populated phases. Cc: Alex Qiu Cc: Ugur Usug Signed-off-by: Guenter Roeck Link: https://lore.kernel.org/r/20210125185327.93282-1-linux@roeck-us.net Reviewed-by: Alex Qiu Signed-off-by: Guenter Roeck --- Documentation/hwmon/max16601.rst | 185 +++++++++++++++------------------------ drivers/hwmon/pmbus/max16601.c | 17 +++- 2 files changed, 89 insertions(+), 113 deletions(-) diff --git a/Documentation/hwmon/max16601.rst b/Documentation/hwmon/max16601.rst index 346e74674c51..4813af0cc39d 100644 --- a/Documentation/hwmon/max16601.rst +++ b/Documentation/hwmon/max16601.rst @@ -45,115 +45,76 @@ Sysfs entries The following attributes are supported. -======================= ======================================================= -in1_label "vin1" -in1_input VCORE input voltage. -in1_alarm Input voltage alarm. - -in2_label "vout1" -in2_input VCORE output voltage. -in2_alarm Output voltage alarm. - -curr1_label "iin1" -curr1_input VCORE input current, derived from duty cycle and output - current. -curr1_max Maximum input current. -curr1_max_alarm Current high alarm. - -curr2_label "iin1.0" -curr2_input VCORE phase 0 input current. - -curr3_label "iin1.1" -curr3_input VCORE phase 1 input current. - -curr4_label "iin1.2" -curr4_input VCORE phase 2 input current. - -curr5_label "iin1.3" -curr5_input VCORE phase 3 input current. - -curr6_label "iin1.4" -curr6_input VCORE phase 4 input current. - -curr7_label "iin1.5" -curr7_input VCORE phase 5 input current. - -curr8_label "iin1.6" -curr8_input VCORE phase 6 input current. - -curr9_label "iin1.7" -curr9_input VCORE phase 7 input current. - -curr10_label "iin2" -curr10_input VCORE input current, derived from sensor element. - -curr11_label "iin3" -curr11_input VSA input current. - -curr12_label "iout1" -curr12_input VCORE output current. -curr12_crit Critical output current. -curr12_crit_alarm Output current critical alarm. -curr12_max Maximum output current. -curr12_max_alarm Output current high alarm. - -curr13_label "iout1.0" -curr13_input VCORE phase 0 output current. - -curr14_label "iout1.1" -curr14_input VCORE phase 1 output current. - -curr15_label "iout1.2" -curr15_input VCORE phase 2 output current. - -curr16_label "iout1.3" -curr16_input VCORE phase 3 output current. - -curr17_label "iout1.4" -curr17_input VCORE phase 4 output current. - -curr18_label "iout1.5" -curr18_input VCORE phase 5 output current. - -curr19_label "iout1.6" -curr19_input VCORE phase 6 output current. - -curr20_label "iout1.7" -curr20_input VCORE phase 7 output current. - -curr21_label "iout3" -curr21_input VSA output current. -curr21_highest Historical maximum VSA output current. -curr21_reset_history Write any value to reset curr21_highest. -curr21_crit Critical output current. -curr21_crit_alarm Output current critical alarm. -curr21_max Maximum output current. -curr21_max_alarm Output current high alarm. - -power1_label "pin1" -power1_input Input power, derived from duty cycle and output current. -power1_alarm Input power alarm. - -power2_label "pin2" -power2_input Input power, derived from input current sensor. - -power3_label "pout" -power3_input Output power. - -temp1_input VCORE temperature. -temp1_crit Critical high temperature. -temp1_crit_alarm Chip temperature critical high alarm. -temp1_max Maximum temperature. -temp1_max_alarm Chip temperature high alarm. - -temp2_input TSENSE_0 temperature -temp3_input TSENSE_1 temperature -temp4_input TSENSE_2 temperature -temp5_input TSENSE_3 temperature - -temp6_input VSA temperature. -temp6_crit Critical high temperature. -temp6_crit_alarm Chip temperature critical high alarm. -temp6_max Maximum temperature. -temp6_max_alarm Chip temperature high alarm. -======================= ======================================================= +=============================== =============================================== +in1_label "vin1" +in1_input VCORE input voltage. +in1_alarm Input voltage alarm. + +in2_label "vout1" +in2_input VCORE output voltage. +in2_alarm Output voltage alarm. + +curr1_label "iin1" +curr1_input VCORE input current, derived from duty cycle + and output current. +curr1_max Maximum input current. +curr1_max_alarm Current high alarm. + +curr[P+2]_label "iin1.P" +curr[P+2]_input VCORE phase P input current. + +curr[N+2]_label "iin2" +curr[N+2]_input VCORE input current, derived from sensor + element. + 'N' is the number of enabled/populated phases. + +curr[N+3]_label "iin3" +curr[N+3]_input VSA input current. + +curr[N+4]_label "iout1" +curr[N+4]_input VCORE output current. +curr[N+4]_crit Critical output current. +curr[N+4]_crit_alarm Output current critical alarm. +curr[N+4]_max Maximum output current. +curr[N+4]_max_alarm Output current high alarm. + +curr[N+P+5]_label "iout1.P" +curr[N+P+5]_input VCORE phase P output current. + +curr[2*N+5]_label "iout3" +curr[2*N+5]_input VSA output current. +curr[2*N+5]_highest Historical maximum VSA output current. +curr[2*N+5]_reset_history Write any value to reset curr21_highest. +curr[2*N+5]_crit Critical output current. +curr[2*N+5]_crit_alarm Output current critical alarm. +curr[2*N+5]_max Maximum output current. +curr[2*N+5]_max_alarm Output current high alarm. + +power1_label "pin1" +power1_input Input power, derived from duty cycle and output + current. +power1_alarm Input power alarm. + +power2_label "pin2" +power2_input Input power, derived from input current sensor. + +power3_label "pout" +power3_input Output power. + +temp1_input VCORE temperature. +temp1_crit Critical high temperature. +temp1_crit_alarm Chip temperature critical high alarm. +temp1_max Maximum temperature. +temp1_max_alarm Chip temperature high alarm. + +temp2_input TSENSE_0 temperature +temp3_input TSENSE_1 temperature +temp4_input TSENSE_2 temperature +temp5_input TSENSE_3 temperature + +temp6_input VSA temperature. +temp6_crit Critical high temperature. +temp6_crit_alarm Chip temperature critical high alarm. +temp6_max Maximum temperature. +temp6_max_alarm Chip temperature high alarm. +=============================== =============================================== diff --git a/drivers/hwmon/pmbus/max16601.c b/drivers/hwmon/pmbus/max16601.c index a960b86e72d2..efe6da3bc8d0 100644 --- a/drivers/hwmon/pmbus/max16601.c +++ b/drivers/hwmon/pmbus/max16601.c @@ -31,6 +31,7 @@ #include "pmbus.h" +#define REG_DEFAULT_NUM_POP 0xc4 #define REG_SETPT_DVID 0xd1 #define DAC_10MV_MODE BIT(4) #define REG_IOUT_AVG_PK 0xee @@ -40,6 +41,8 @@ #define CORE_RAIL_INDICATOR BIT(7) #define REG_PHASE_REPORTING 0xf4 +#define MAX16601_NUM_PHASES 8 + struct max16601_data { struct pmbus_driver_info info; struct i2c_client *vsa; @@ -195,6 +198,18 @@ static int max16601_identify(struct i2c_client *client, else info->vrm_version[0] = vr12; + reg = i2c_smbus_read_byte_data(client, REG_DEFAULT_NUM_POP); + if (reg < 0) + return reg; + + /* + * If REG_DEFAULT_NUM_POP returns 0, we don't know how many phases + * are populated. Stick with the default in that case. + */ + reg &= 0x0f; + if (reg && reg <= MAX16601_NUM_PHASES) + info->phases[0] = reg; + return 0; } @@ -216,7 +231,7 @@ static struct pmbus_driver_info max16601_info = { .func[2] = PMBUS_HAVE_IIN | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | PMBUS_PAGE_VIRTUAL, - .phases[0] = 8, + .phases[0] = MAX16601_NUM_PHASES, .pfunc[0] = PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP, .pfunc[1] = PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT, .pfunc[2] = PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP, -- cgit v1.2.3 From 66102281f94afdf1f41cf6147c7ddce73a8e75f2 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 25 Jan 2021 10:53:27 -0800 Subject: hwmon: (pmbus/max16601) Add support for MAX16508 MAX16508 is quite similar to MAX16601, except that it does not support the DEFAULT_NUM_POP register and we thus can not dynamically determine the number of populated phases. Cc: Alex Qiu Cc: Ugur Usug Signed-off-by: Guenter Roeck Link: https://lore.kernel.org/r/20210125185327.93282-2-linux@roeck-us.net Reviewed-by: Alex Qiu Tested-by: Alex Qiu Signed-off-by: Guenter Roeck --- Documentation/hwmon/max16601.rst | 12 +++++-- drivers/hwmon/pmbus/Kconfig | 4 +-- drivers/hwmon/pmbus/max16601.c | 74 +++++++++++++++++++++++++++++----------- 3 files changed, 66 insertions(+), 24 deletions(-) diff --git a/Documentation/hwmon/max16601.rst b/Documentation/hwmon/max16601.rst index 4813af0cc39d..92c0a7d7808c 100644 --- a/Documentation/hwmon/max16601.rst +++ b/Documentation/hwmon/max16601.rst @@ -5,6 +5,14 @@ Kernel driver max16601 Supported chips: + * Maxim MAX16508 + + Prefix: 'max16508' + + Addresses scanned: - + + Datasheet: Not published + * Maxim MAX16601 Prefix: 'max16601' @@ -19,8 +27,8 @@ Author: Guenter Roeck Description ----------- -This driver supports the MAX16601 VR13.HC Dual-Output Voltage Regulator -Chipset. +This driver supports the MAX16508 VR13 Dual-Output Voltage Regulator +as well as the MAX16601 VR13.HC Dual-Output Voltage Regulator chipsets. The driver is a client driver to the core PMBus driver. Please see Documentation/hwmon/pmbus.rst for details on PMBus client drivers. diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 03606d4298a4..32d2fc850621 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -158,10 +158,10 @@ config SENSORS_MAX16064 be called max16064. config SENSORS_MAX16601 - tristate "Maxim MAX16601" + tristate "Maxim MAX16508, MAX16601" help If you say yes here you get hardware monitoring support for Maxim - MAX16601. + MAX16508 and MAX16601. This driver can also be built as a module. If so, the module will be called max16601. diff --git a/drivers/hwmon/pmbus/max16601.c b/drivers/hwmon/pmbus/max16601.c index efe6da3bc8d0..0d1204c2dd54 100644 --- a/drivers/hwmon/pmbus/max16601.c +++ b/drivers/hwmon/pmbus/max16601.c @@ -1,11 +1,11 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Hardware monitoring driver for Maxim MAX16601 + * Hardware monitoring driver for Maxim MAX16508 and MAX16601. * * Implementation notes: * - * Ths chip supports two rails, VCORE and VSA. Telemetry information for the - * two rails is reported in two subsequent I2C addresses. The driver + * This chip series supports two rails, VCORE and VSA. Telemetry information + * for the two rails is reported in two subsequent I2C addresses. The driver * instantiates a dummy I2C client at the second I2C address to report * information for the VSA rail in a single instance of the driver. * Telemetry for the VSA rail is reported to the PMBus core in PMBus page 2. @@ -31,6 +31,8 @@ #include "pmbus.h" +enum chips { max16508, max16601 }; + #define REG_DEFAULT_NUM_POP 0xc4 #define REG_SETPT_DVID 0xd1 #define DAC_10MV_MODE BIT(4) @@ -44,6 +46,7 @@ #define MAX16601_NUM_PHASES 8 struct max16601_data { + enum chips id; struct pmbus_driver_info info; struct i2c_client *vsa; int iout_avg_pkg; @@ -188,6 +191,7 @@ static int max16601_write_word(struct i2c_client *client, int page, int reg, static int max16601_identify(struct i2c_client *client, struct pmbus_driver_info *info) { + struct max16601_data *data = to_max16601_data(info); int reg; reg = i2c_smbus_read_byte_data(client, REG_SETPT_DVID); @@ -198,6 +202,9 @@ static int max16601_identify(struct i2c_client *client, else info->vrm_version[0] = vr12; + if (data->id != max16601) + return 0; + reg = i2c_smbus_read_byte_data(client, REG_DEFAULT_NUM_POP); if (reg < 0) return reg; @@ -254,28 +261,61 @@ static void max16601_remove(void *_data) i2c_unregister_device(data->vsa); } -static int max16601_probe(struct i2c_client *client) +static const struct i2c_device_id max16601_id[] = { + {"max16508", max16508}, + {"max16601", max16601}, + {} +}; +MODULE_DEVICE_TABLE(i2c, max16601_id); + +static int max16601_get_id(struct i2c_client *client) { struct device *dev = &client->dev; u8 buf[I2C_SMBUS_BLOCK_MAX + 1]; - struct max16601_data *data; + enum chips id; int ret; - if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_READ_BYTE_DATA | - I2C_FUNC_SMBUS_READ_BLOCK_DATA)) - return -ENODEV; - ret = i2c_smbus_read_block_data(client, PMBUS_IC_DEVICE_ID, buf); - if (ret < 0) + if (ret < 0 || ret < 11) return -ENODEV; - /* PMBUS_IC_DEVICE_ID is expected to return "MAX16601y.xx" */ - if (ret < 11 || strncmp(buf, "MAX16601", 8)) { + /* + * PMBUS_IC_DEVICE_ID is expected to return "MAX16601y.xx" + * or "MAX16500y.xx". + */ + if (!strncmp(buf, "MAX16500", 8)) { + id = max16508; + } else if (!strncmp(buf, "MAX16601", 8)) { + id = max16601; + } else { buf[ret] = '\0'; dev_err(dev, "Unsupported chip '%s'\n", buf); return -ENODEV; } + return id; +} + +static int max16601_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + const struct i2c_device_id *id; + struct max16601_data *data; + int ret, chip_id; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA | + I2C_FUNC_SMBUS_READ_BLOCK_DATA)) + return -ENODEV; + + chip_id = max16601_get_id(client); + if (chip_id < 0) + return chip_id; + + id = i2c_match_id(max16601_id, client); + if (chip_id != id->driver_data) + dev_warn(&client->dev, + "Device mismatch: Configured %s (%d), detected %d\n", + id->name, (int) id->driver_data, chip_id); ret = i2c_smbus_read_byte_data(client, REG_PHASE_ID); if (ret < 0) @@ -290,6 +330,7 @@ static int max16601_probe(struct i2c_client *client) if (!data) return -ENOMEM; + data->id = chip_id; data->iout_avg_pkg = 0xfc00; data->vsa = i2c_new_dummy_device(client->adapter, client->addr + 1); if (IS_ERR(data->vsa)) { @@ -305,13 +346,6 @@ static int max16601_probe(struct i2c_client *client) return pmbus_do_probe(client, &data->info); } -static const struct i2c_device_id max16601_id[] = { - {"max16601", 0}, - {} -}; - -MODULE_DEVICE_TABLE(i2c, max16601_id); - static struct i2c_driver max16601_driver = { .driver = { .name = "max16601", -- cgit v1.2.3 From 1ae5aaf5d1c5473616413f3aa8ce05e9c739a298 Mon Sep 17 00:00:00 2001 From: Erik Rosen Date: Mon, 1 Feb 2021 20:59:28 +0100 Subject: hwmon: (pmbus) Clear sensor data after chip write Set the sensor->data field to -ENODATA to force a chip access next time the sensor value is read. Signed-off-by: Erik Rosen Link: https://lore.kernel.org/r/20210201195929.1200-2-erik.rosen@metormote.com Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 1cf7d9e9d089..aadea85fe630 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -974,7 +974,7 @@ static ssize_t pmbus_set_sensor(struct device *dev, if (ret < 0) rv = ret; else - sensor->data = regval; + sensor->data = -ENODATA; mutex_unlock(&data->update_lock); return rv; } -- cgit v1.2.3 From 43d147482788c7e5d26fe5ba2893fea2e0ae3900 Mon Sep 17 00:00:00 2001 From: Erik Rosen Date: Mon, 1 Feb 2021 20:59:29 +0100 Subject: hwmon: (pmbus/lm25066) Remove unnecessary pmbus_clear_cache function call It is no longer necessary to clear the cache to update the sensor value from the chip. Signed-off-by: Erik Rosen Link: https://lore.kernel.org/r/20210201195929.1200-3-erik.rosen@metormote.com Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/lm25066.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/hwmon/pmbus/lm25066.c b/drivers/hwmon/pmbus/lm25066.c index c75a6bf39641..e9a66fd9e144 100644 --- a/drivers/hwmon/pmbus/lm25066.c +++ b/drivers/hwmon/pmbus/lm25066.c @@ -371,21 +371,18 @@ static int lm25066_write_word_data(struct i2c_client *client, int page, int reg, case PMBUS_VIN_OV_WARN_LIMIT: word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit); ret = pmbus_write_word_data(client, 0, reg, word); - pmbus_clear_cache(client); break; case PMBUS_IIN_OC_WARN_LIMIT: word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit); ret = pmbus_write_word_data(client, 0, LM25066_MFR_IIN_OC_WARN_LIMIT, word); - pmbus_clear_cache(client); break; case PMBUS_PIN_OP_WARN_LIMIT: word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit); ret = pmbus_write_word_data(client, 0, LM25066_MFR_PIN_OP_WARN_LIMIT, word); - pmbus_clear_cache(client); break; case PMBUS_VIRT_VMON_UV_WARN_LIMIT: /* Adjust from VIN coefficients (for LM25056) */ @@ -393,7 +390,6 @@ static int lm25066_write_word_data(struct i2c_client *client, int page, int reg, word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit); ret = pmbus_write_word_data(client, 0, LM25056_VAUX_UV_WARN_LIMIT, word); - pmbus_clear_cache(client); break; case PMBUS_VIRT_VMON_OV_WARN_LIMIT: /* Adjust from VIN coefficients (for LM25056) */ @@ -401,7 +397,6 @@ static int lm25066_write_word_data(struct i2c_client *client, int page, int reg, word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit); ret = pmbus_write_word_data(client, 0, LM25056_VAUX_OV_WARN_LIMIT, word); - pmbus_clear_cache(client); break; case PMBUS_VIRT_RESET_PIN_HISTORY: ret = pmbus_write_byte(client, 0, LM25066_CLEAR_PIN_PEAK); -- cgit v1.2.3 From 996dc09c8e773f348086d03ded4cb96950f116bb Mon Sep 17 00:00:00 2001 From: Matthew Barth Date: Mon, 1 Feb 2021 15:22:17 -0600 Subject: hwmon: (pmbus/max31785) Support revision "B" There was an issue in how the tach feedbacks of dual rotor fans were reported during any change in fan speeds with revision "A" of the MAX31785. When the fan speeds would transition to a new target speed, the rotor not wired to the TACH input when TACHSEL = 0 would report a speed of 0 until the new target was reached. This has been fixed, resulting in a revision "B" update where the MFR_REVISION of "B" is 0x3061. Signed-off-by: Matthew Barth Link: https://lore.kernel.org/r/20210201212217.73721-1-msbarth@linux.ibm.com [groeck: Change 'ret' variable type to int] Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/max31785.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/hwmon/pmbus/max31785.c b/drivers/hwmon/pmbus/max31785.c index e5a9f4019cd5..17489abc49d5 100644 --- a/drivers/hwmon/pmbus/max31785.c +++ b/drivers/hwmon/pmbus/max31785.c @@ -17,6 +17,7 @@ enum max31785_regs { #define MAX31785 0x3030 #define MAX31785A 0x3040 +#define MAX31785B 0x3061 #define MFR_FAN_CONFIG_DUAL_TACH BIT(12) @@ -329,7 +330,7 @@ static int max31785_probe(struct i2c_client *client) struct device *dev = &client->dev; struct pmbus_driver_info *info; bool dual_tach = false; - s64 ret; + int ret; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA | @@ -350,12 +351,14 @@ static int max31785_probe(struct i2c_client *client) if (ret < 0) return ret; - if (ret == MAX31785A) { + if (ret == MAX31785A || ret == MAX31785B) { dual_tach = true; } else if (ret == MAX31785) { - if (!strcmp("max31785a", client->name)) - dev_warn(dev, "Expected max3175a, found max31785: cannot provide secondary tachometer readings\n"); + if (!strcmp("max31785a", client->name) || + !strcmp("max31785b", client->name)) + dev_warn(dev, "Expected max31785a/b, found max31785: cannot provide secondary tachometer readings\n"); } else { + dev_err(dev, "Unrecognized MAX31785 revision: %x\n", ret); return -ENODEV; } @@ -371,6 +374,7 @@ static int max31785_probe(struct i2c_client *client) static const struct i2c_device_id max31785_id[] = { { "max31785", 0 }, { "max31785a", 0 }, + { "max31785b", 0 }, { }, }; @@ -379,6 +383,7 @@ MODULE_DEVICE_TABLE(i2c, max31785_id); static const struct of_device_id max31785_of_match[] = { { .compatible = "maxim,max31785" }, { .compatible = "maxim,max31785a" }, + { .compatible = "maxim,max31785b" }, { }, }; -- cgit v1.2.3 From 9f56b8eb85927c6391216e4f35a7abb34847f0fd Mon Sep 17 00:00:00 2001 From: Naveen Krishna Chatradhi Date: Thu, 11 Feb 2021 22:50:54 +0530 Subject: hwmon: (amd_energy) Add AMD family 19h model 30h x86 match Add X86 CPU match for AMD family 19h model 30h. This is necessary to enable support for energy reporting via the amd_energy module. Signed-off-by: Naveen Krishna Chatradhi Link: https://lore.kernel.org/r/20210211172054.246276-1-nchatrad@amd.com Signed-off-by: Guenter Roeck --- drivers/hwmon/amd_energy.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hwmon/amd_energy.c b/drivers/hwmon/amd_energy.c index 822c2e74b98d..a86cc8d6d93d 100644 --- a/drivers/hwmon/amd_energy.c +++ b/drivers/hwmon/amd_energy.c @@ -333,6 +333,7 @@ static struct platform_device *amd_energy_platdev; static const struct x86_cpu_id cpu_ids[] __initconst = { X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x17, 0x31, NULL), X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x19, 0x01, NULL), + X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x19, 0x30, NULL), {} }; MODULE_DEVICE_TABLE(x86cpu, cpu_ids); -- cgit v1.2.3 From 8c646ef145136a7b1781976c42802391d5b8522b Mon Sep 17 00:00:00 2001 From: Tian Tao Date: Tue, 9 Feb 2021 16:43:28 +0800 Subject: hwmon: (da9052) Switch to using the new API kobj_to_dev() Switch to using the new API kobj_to_dev() to fix the below warnning: /drivers/hwmon/da9052-hwmon.c:302:60-61: WARNING opportunity for kobj_to_dev(). Signed-off-by: Tian Tao Link: https://lore.kernel.org/r/1612860208-51088-1-git-send-email-tiantao6@hisilicon.com Signed-off-by: Guenter Roeck --- drivers/hwmon/da9052-hwmon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/da9052-hwmon.c b/drivers/hwmon/da9052-hwmon.c index 4af2fc309c28..ed6c5df94fdf 100644 --- a/drivers/hwmon/da9052-hwmon.c +++ b/drivers/hwmon/da9052-hwmon.c @@ -299,7 +299,7 @@ static ssize_t label_show(struct device *dev, static umode_t da9052_channel_is_visible(struct kobject *kobj, struct attribute *attr, int index) { - struct device *dev = container_of(kobj, struct device, kobj); + struct device *dev = kobj_to_dev(kobj); struct da9052_hwmon *hwmon = dev_get_drvdata(dev); struct device_attribute *dattr = container_of(attr, struct device_attribute, attr); -- cgit v1.2.3 From f02e897bf37d7db57d337094a7c0a9d8f2f5d4fb Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Thu, 21 Jan 2021 14:44:32 +0100 Subject: dt-bindings: hwmon: Add TI TPS23861 bindings Document bindings for the Texas Instruments TPS23861 driver. Signed-off-by: Robert Marko Cc: Luka Perkov Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20210121134434.2782405-1-robert.marko@sartura.hr Signed-off-by: Guenter Roeck --- .../devicetree/bindings/hwmon/ti,tps23861.yaml | 51 ++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 Documentation/devicetree/bindings/hwmon/ti,tps23861.yaml diff --git a/Documentation/devicetree/bindings/hwmon/ti,tps23861.yaml b/Documentation/devicetree/bindings/hwmon/ti,tps23861.yaml new file mode 100644 index 000000000000..3bc8e73dfbf0 --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/ti,tps23861.yaml @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- + +$id: http://devicetree.org/schemas/hwmon/ti,tps23861.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TI TPS23861 PoE PSE + +maintainers: + - Robert Marko + +description: | + The TPS23861 is a IEEE 802.3at Quad Port Power-over-Ethernet PSE Controller. + + Datasheets: + https://www.ti.com/lit/gpn/tps23861 + + +properties: + compatible: + enum: + - ti,tps23861 + + reg: + maxItems: 1 + + shunt-resistor-micro-ohms: + description: The value of curent sense resistor in microohms. + default: 255000 + minimum: 250000 + maximum: 255000 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + tps23861@30 { + compatible = "ti,tps23861"; + reg = <0x30>; + shunt-resistor-micro-ohms = <255000>; + }; + }; -- cgit v1.2.3 From fff7b8ab225547828db9c57cdf05a03d5b4a7153 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Thu, 21 Jan 2021 14:44:33 +0100 Subject: hwmon: add Texas Instruments TPS23861 driver Add basic monitoring support as well as port on/off control for Texas Instruments TPS23861 PoE PSE IC. Signed-off-by: Robert Marko Cc: Luka Perkov Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20210121134434.2782405-2-robert.marko@sartura.hr Signed-off-by: Guenter Roeck --- Documentation/hwmon/index.rst | 1 + Documentation/hwmon/tps23861.rst | 41 +++ drivers/hwmon/Kconfig | 11 + drivers/hwmon/Makefile | 1 + drivers/hwmon/tps23861.c | 601 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 655 insertions(+) create mode 100644 Documentation/hwmon/tps23861.rst create mode 100644 drivers/hwmon/tps23861.c diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index 3589151bcefb..8d5a2df1ecb6 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -177,6 +177,7 @@ Hardware Monitoring Kernel Drivers tmp401 tmp421 tmp513 + tps23861 tps40422 tps53679 twl4030-madc-hwmon diff --git a/Documentation/hwmon/tps23861.rst b/Documentation/hwmon/tps23861.rst new file mode 100644 index 000000000000..46d121ff3f31 --- /dev/null +++ b/Documentation/hwmon/tps23861.rst @@ -0,0 +1,41 @@ +.. SPDX-License-Identifier: GPL-2.0-only + +Kernel driver tps23861 +====================== + +Supported chips: + * Texas Instruments TPS23861 + + Prefix: 'tps23861' + + Datasheet: https://www.ti.com/lit/gpn/tps23861 + +Author: Robert Marko + +Description +----------- + +This driver supports hardware monitoring for Texas Instruments TPS23861 PoE PSE. + +TPS23861 is a quad port IEEE802.3at PSE controller with optional I2C control +and monitoring capabilities. + +TPS23861 offers three modes of operation: Auto, Semi-Auto and Manual. + +This driver only supports the Auto mode of operation providing monitoring +as well as enabling/disabling the four ports. + +Sysfs entries +------------- + +======================= ===================================================================== +in[0-3]_input Voltage on ports [1-4] +in[0-3]_label "Port[1-4]" +in4_input IC input voltage +in4_label "Input" +temp1_input IC die temperature +temp1_label "Die" +curr[1-4]_input Current on ports [1-4] +in[1-4]_label "Port[1-4]" +in[0-3]_enable Enable/disable ports [1-4] +======================= ===================================================================== diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index fd0da6295d5e..54f04e61fb83 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1133,6 +1133,17 @@ config SENSORS_TC654 This driver can also be built as a module. If so, the module will be called tc654. +config SENSORS_TPS23861 + tristate "Texas Instruments TPS23861 PoE PSE" + depends on I2C + select REGMAP_I2C + help + If you say yes here you get support for Texas Instruments + TPS23861 802.3at PoE PSE chips. + + This driver can also be built as a module. If so, the module + will be called tps23861. + config SENSORS_MENF21BMC_HWMON tristate "MEN 14F021P00 BMC Hardware Monitoring" depends on MFD_MENF21BMC diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 51dd97437bfd..fe38e8a5c979 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -144,6 +144,7 @@ obj-$(CONFIG_SENSORS_MAX31790) += max31790.o obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o obj-$(CONFIG_SENSORS_TC654) += tc654.o +obj-$(CONFIG_SENSORS_TPS23861) += tps23861.o obj-$(CONFIG_SENSORS_MLXREG_FAN) += mlxreg-fan.o obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o obj-$(CONFIG_SENSORS_MR75203) += mr75203.o diff --git a/drivers/hwmon/tps23861.c b/drivers/hwmon/tps23861.c new file mode 100644 index 000000000000..c2484f15298b --- /dev/null +++ b/drivers/hwmon/tps23861.c @@ -0,0 +1,601 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020 Sartura Ltd. + * + * Driver for the TI TPS23861 PoE PSE. + * + * Author: Robert Marko + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TEMPERATURE 0x2c +#define INPUT_VOLTAGE_LSB 0x2e +#define INPUT_VOLTAGE_MSB 0x2f +#define PORT_1_CURRENT_LSB 0x30 +#define PORT_1_CURRENT_MSB 0x31 +#define PORT_1_VOLTAGE_LSB 0x32 +#define PORT_1_VOLTAGE_MSB 0x33 +#define PORT_2_CURRENT_LSB 0x34 +#define PORT_2_CURRENT_MSB 0x35 +#define PORT_2_VOLTAGE_LSB 0x36 +#define PORT_2_VOLTAGE_MSB 0x37 +#define PORT_3_CURRENT_LSB 0x38 +#define PORT_3_CURRENT_MSB 0x39 +#define PORT_3_VOLTAGE_LSB 0x3a +#define PORT_3_VOLTAGE_MSB 0x3b +#define PORT_4_CURRENT_LSB 0x3c +#define PORT_4_CURRENT_MSB 0x3d +#define PORT_4_VOLTAGE_LSB 0x3e +#define PORT_4_VOLTAGE_MSB 0x3f +#define PORT_N_CURRENT_LSB_OFFSET 0x04 +#define PORT_N_VOLTAGE_LSB_OFFSET 0x04 +#define VOLTAGE_CURRENT_MASK GENMASK(13, 0) +#define PORT_1_RESISTANCE_LSB 0x60 +#define PORT_1_RESISTANCE_MSB 0x61 +#define PORT_2_RESISTANCE_LSB 0x62 +#define PORT_2_RESISTANCE_MSB 0x63 +#define PORT_3_RESISTANCE_LSB 0x64 +#define PORT_3_RESISTANCE_MSB 0x65 +#define PORT_4_RESISTANCE_LSB 0x66 +#define PORT_4_RESISTANCE_MSB 0x67 +#define PORT_N_RESISTANCE_LSB_OFFSET 0x02 +#define PORT_RESISTANCE_MASK GENMASK(13, 0) +#define PORT_RESISTANCE_RSN_MASK GENMASK(15, 14) +#define PORT_RESISTANCE_RSN_OTHER 0 +#define PORT_RESISTANCE_RSN_LOW 1 +#define PORT_RESISTANCE_RSN_OPEN 2 +#define PORT_RESISTANCE_RSN_SHORT 3 +#define PORT_1_STATUS 0x0c +#define PORT_2_STATUS 0x0d +#define PORT_3_STATUS 0x0e +#define PORT_4_STATUS 0x0f +#define PORT_STATUS_CLASS_MASK GENMASK(7, 4) +#define PORT_STATUS_DETECT_MASK GENMASK(3, 0) +#define PORT_CLASS_UNKNOWN 0 +#define PORT_CLASS_1 1 +#define PORT_CLASS_2 2 +#define PORT_CLASS_3 3 +#define PORT_CLASS_4 4 +#define PORT_CLASS_RESERVED 5 +#define PORT_CLASS_0 6 +#define PORT_CLASS_OVERCURRENT 7 +#define PORT_CLASS_MISMATCH 8 +#define PORT_DETECT_UNKNOWN 0 +#define PORT_DETECT_SHORT 1 +#define PORT_DETECT_RESERVED 2 +#define PORT_DETECT_RESISTANCE_LOW 3 +#define PORT_DETECT_RESISTANCE_OK 4 +#define PORT_DETECT_RESISTANCE_HIGH 5 +#define PORT_DETECT_OPEN_CIRCUIT 6 +#define PORT_DETECT_RESERVED_2 7 +#define PORT_DETECT_MOSFET_FAULT 8 +#define PORT_DETECT_LEGACY 9 +/* Measurment beyond clamp voltage */ +#define PORT_DETECT_CAPACITANCE_INVALID_BEYOND 10 +/* Insufficient voltage delta */ +#define PORT_DETECT_CAPACITANCE_INVALID_DELTA 11 +#define PORT_DETECT_CAPACITANCE_OUT_OF_RANGE 12 +#define POE_PLUS 0x40 +#define OPERATING_MODE 0x12 +#define OPERATING_MODE_OFF 0 +#define OPERATING_MODE_MANUAL 1 +#define OPERATING_MODE_SEMI 2 +#define OPERATING_MODE_AUTO 3 +#define OPERATING_MODE_PORT_1_MASK GENMASK(1, 0) +#define OPERATING_MODE_PORT_2_MASK GENMASK(3, 2) +#define OPERATING_MODE_PORT_3_MASK GENMASK(5, 4) +#define OPERATING_MODE_PORT_4_MASK GENMASK(7, 6) + +#define DETECT_CLASS_RESTART 0x18 +#define POWER_ENABLE 0x19 +#define TPS23861_NUM_PORTS 4 + +#define TEMPERATURE_LSB 652 /* 0.652 degrees Celsius */ +#define VOLTAGE_LSB 3662 /* 3.662 mV */ +#define SHUNT_RESISTOR_DEFAULT 255000 /* 255 mOhm */ +#define CURRENT_LSB_255 62260 /* 62.260 uA */ +#define CURRENT_LSB_250 61039 /* 61.039 uA */ +#define RESISTANCE_LSB 110966 /* 11.0966 Ohm*/ +#define RESISTANCE_LSB_LOW 157216 /* 15.7216 Ohm*/ + +struct tps23861_data { + struct regmap *regmap; + u32 shunt_resistor; + struct i2c_client *client; + struct dentry *debugfs_dir; +}; + +static struct regmap_config tps23861_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static int tps23861_read_temp(struct tps23861_data *data, long *val) +{ + unsigned int regval; + int err; + + err = regmap_read(data->regmap, TEMPERATURE, ®val); + if (err < 0) + return err; + + *val = (regval * TEMPERATURE_LSB) - 20000; + + return 0; +} + +static int tps23861_read_voltage(struct tps23861_data *data, int channel, + long *val) +{ + unsigned int regval; + int err; + + if (channel < TPS23861_NUM_PORTS) { + err = regmap_bulk_read(data->regmap, + PORT_1_VOLTAGE_LSB + channel * PORT_N_VOLTAGE_LSB_OFFSET, + ®val, 2); + } else { + err = regmap_bulk_read(data->regmap, + INPUT_VOLTAGE_LSB, + ®val, 2); + } + if (err < 0) + return err; + + *val = (FIELD_GET(VOLTAGE_CURRENT_MASK, regval) * VOLTAGE_LSB) / 1000; + + return 0; +} + +static int tps23861_read_current(struct tps23861_data *data, int channel, + long *val) +{ + unsigned int current_lsb; + unsigned int regval; + int err; + + if (data->shunt_resistor == SHUNT_RESISTOR_DEFAULT) + current_lsb = CURRENT_LSB_255; + else + current_lsb = CURRENT_LSB_250; + + err = regmap_bulk_read(data->regmap, + PORT_1_CURRENT_LSB + channel * PORT_N_CURRENT_LSB_OFFSET, + ®val, 2); + if (err < 0) + return err; + + *val = (FIELD_GET(VOLTAGE_CURRENT_MASK, regval) * current_lsb) / 1000000; + + return 0; +} + +static int tps23861_port_disable(struct tps23861_data *data, int channel) +{ + unsigned int regval = 0; + int err; + + regval |= BIT(channel + 4); + err = regmap_write(data->regmap, POWER_ENABLE, regval); + + return err; +} + +static int tps23861_port_enable(struct tps23861_data *data, int channel) +{ + unsigned int regval = 0; + int err; + + regval |= BIT(channel); + regval |= BIT(channel + 4); + err = regmap_write(data->regmap, DETECT_CLASS_RESTART, regval); + + return err; +} + +static umode_t tps23861_is_visible(const void *data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + case hwmon_temp_label: + return 0444; + default: + return 0; + } + case hwmon_in: + switch (attr) { + case hwmon_in_input: + case hwmon_in_label: + return 0444; + case hwmon_in_enable: + return 0200; + default: + return 0; + } + case hwmon_curr: + switch (attr) { + case hwmon_curr_input: + case hwmon_curr_label: + return 0444; + default: + return 0; + } + default: + return 0; + } +} + +static int tps23861_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct tps23861_data *data = dev_get_drvdata(dev); + int err; + + switch (type) { + case hwmon_in: + switch (attr) { + case hwmon_in_enable: + if (val == 0) + err = tps23861_port_disable(data, channel); + else if (val == 1) + err = tps23861_port_enable(data, channel); + else + err = -EINVAL; + break; + default: + return -EOPNOTSUPP; + } + break; + default: + return -EOPNOTSUPP; + } + + return err; +} + +static int tps23861_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct tps23861_data *data = dev_get_drvdata(dev); + int err; + + switch (type) { + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + err = tps23861_read_temp(data, val); + break; + default: + return -EOPNOTSUPP; + } + break; + case hwmon_in: + switch (attr) { + case hwmon_in_input: + err = tps23861_read_voltage(data, channel, val); + break; + default: + return -EOPNOTSUPP; + } + break; + case hwmon_curr: + switch (attr) { + case hwmon_curr_input: + err = tps23861_read_current(data, channel, val); + break; + default: + return -EOPNOTSUPP; + } + break; + default: + return -EOPNOTSUPP; + } + + return err; +} + +static const char * const tps23861_port_label[] = { + "Port1", + "Port2", + "Port3", + "Port4", + "Input", +}; + +static int tps23861_read_string(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) +{ + switch (type) { + case hwmon_in: + case hwmon_curr: + *str = tps23861_port_label[channel]; + break; + case hwmon_temp: + *str = "Die"; + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static const struct hwmon_channel_info *tps23861_info[] = { + HWMON_CHANNEL_INFO(chip, + HWMON_C_REGISTER_TZ), + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT | HWMON_T_LABEL), + HWMON_CHANNEL_INFO(in, + 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, + HWMON_I_INPUT | HWMON_I_ENABLE | HWMON_I_LABEL, + HWMON_I_INPUT | HWMON_I_LABEL), + HWMON_CHANNEL_INFO(curr, + HWMON_C_INPUT | HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_LABEL, + HWMON_C_INPUT | HWMON_C_LABEL), + NULL +}; + +static const struct hwmon_ops tps23861_hwmon_ops = { + .is_visible = tps23861_is_visible, + .write = tps23861_write, + .read = tps23861_read, + .read_string = tps23861_read_string, +}; + +static const struct hwmon_chip_info tps23861_chip_info = { + .ops = &tps23861_hwmon_ops, + .info = tps23861_info, +}; + +static char *tps23861_port_operating_mode(struct tps23861_data *data, int port) +{ + unsigned int regval; + int mode; + + regmap_read(data->regmap, OPERATING_MODE, ®val); + + switch (port) { + case 1: + mode = FIELD_GET(OPERATING_MODE_PORT_1_MASK, regval); + break; + case 2: + mode = FIELD_GET(OPERATING_MODE_PORT_2_MASK, regval); + break; + case 3: + mode = FIELD_GET(OPERATING_MODE_PORT_3_MASK, regval); + break; + case 4: + mode = FIELD_GET(OPERATING_MODE_PORT_4_MASK, regval); + break; + default: + mode = -EINVAL; + } + + switch (mode) { + case OPERATING_MODE_OFF: + return "Off"; + case OPERATING_MODE_MANUAL: + return "Manual"; + case OPERATING_MODE_SEMI: + return "Semi-Auto"; + case OPERATING_MODE_AUTO: + return "Auto"; + default: + return "Invalid"; + } +} + +static char *tps23861_port_detect_status(struct tps23861_data *data, int port) +{ + unsigned int regval; + + regmap_read(data->regmap, + PORT_1_STATUS + (port - 1), + ®val); + + switch (FIELD_GET(PORT_STATUS_DETECT_MASK, regval)) { + case PORT_DETECT_UNKNOWN: + return "Unknown device"; + case PORT_DETECT_SHORT: + return "Short circuit"; + case PORT_DETECT_RESISTANCE_LOW: + return "Too low resistance"; + case PORT_DETECT_RESISTANCE_OK: + return "Valid resistance"; + case PORT_DETECT_RESISTANCE_HIGH: + return "Too high resistance"; + case PORT_DETECT_OPEN_CIRCUIT: + return "Open circuit"; + case PORT_DETECT_MOSFET_FAULT: + return "MOSFET fault"; + case PORT_DETECT_LEGACY: + return "Legacy device"; + case PORT_DETECT_CAPACITANCE_INVALID_BEYOND: + return "Invalid capacitance, beyond clamp voltage"; + case PORT_DETECT_CAPACITANCE_INVALID_DELTA: + return "Invalid capacitance, insufficient voltage delta"; + case PORT_DETECT_CAPACITANCE_OUT_OF_RANGE: + return "Valid capacitance, outside of legacy range"; + case PORT_DETECT_RESERVED: + case PORT_DETECT_RESERVED_2: + default: + return "Invalid"; + } +} + +static char *tps23861_port_class_status(struct tps23861_data *data, int port) +{ + unsigned int regval; + + regmap_read(data->regmap, + PORT_1_STATUS + (port - 1), + ®val); + + switch (FIELD_GET(PORT_STATUS_CLASS_MASK, regval)) { + case PORT_CLASS_UNKNOWN: + return "Unknown"; + case PORT_CLASS_RESERVED: + case PORT_CLASS_0: + return "0"; + case PORT_CLASS_1: + return "1"; + case PORT_CLASS_2: + return "2"; + case PORT_CLASS_3: + return "3"; + case PORT_CLASS_4: + return "4"; + case PORT_CLASS_OVERCURRENT: + return "Overcurrent"; + case PORT_CLASS_MISMATCH: + return "Mismatch"; + default: + return "Invalid"; + } +} + +static char *tps23861_port_poe_plus_status(struct tps23861_data *data, int port) +{ + unsigned int regval; + + regmap_read(data->regmap, POE_PLUS, ®val); + + if (BIT(port + 3) & regval) + return "Yes"; + else + return "No"; +} + +static int tps23861_port_resistance(struct tps23861_data *data, int port) +{ + u16 regval; + + regmap_bulk_read(data->regmap, + PORT_1_RESISTANCE_LSB + PORT_N_RESISTANCE_LSB_OFFSET * (port - 1), + ®val, + 2); + + switch (FIELD_GET(PORT_RESISTANCE_RSN_MASK, regval)) { + case PORT_RESISTANCE_RSN_OTHER: + return (FIELD_GET(PORT_RESISTANCE_MASK, regval) * RESISTANCE_LSB) / 10000; + case PORT_RESISTANCE_RSN_LOW: + return (FIELD_GET(PORT_RESISTANCE_MASK, regval) * RESISTANCE_LSB_LOW) / 10000; + case PORT_RESISTANCE_RSN_SHORT: + case PORT_RESISTANCE_RSN_OPEN: + default: + return 0; + } +} + +static int tps23861_port_status_show(struct seq_file *s, void *data) +{ + struct tps23861_data *priv = s->private; + int i; + + for (i = 1; i < TPS23861_NUM_PORTS + 1; i++) { + seq_printf(s, "Port: \t\t%d\n", i); + seq_printf(s, "Operating mode: %s\n", tps23861_port_operating_mode(priv, i)); + seq_printf(s, "Detected: \t%s\n", tps23861_port_detect_status(priv, i)); + seq_printf(s, "Class: \t\t%s\n", tps23861_port_class_status(priv, i)); + seq_printf(s, "PoE Plus: \t%s\n", tps23861_port_poe_plus_status(priv, i)); + seq_printf(s, "Resistance: \t%d\n", tps23861_port_resistance(priv, i)); + seq_putc(s, '\n'); + } + + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(tps23861_port_status); + +static void tps23861_init_debugfs(struct tps23861_data *data) +{ + data->debugfs_dir = debugfs_create_dir(data->client->name, NULL); + + debugfs_create_file("port_status", + 0400, + data->debugfs_dir, + data, + &tps23861_port_status_fops); +} + +static int tps23861_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct tps23861_data *data; + struct device *hwmon_dev; + u32 shunt_resistor; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->client = client; + i2c_set_clientdata(client, data); + + data->regmap = devm_regmap_init_i2c(client, &tps23861_regmap_config); + if (IS_ERR(data->regmap)) { + dev_err(dev, "failed to allocate register map\n"); + return PTR_ERR(data->regmap); + } + + if (!of_property_read_u32(dev->of_node, "shunt-resistor-micro-ohms", &shunt_resistor)) + data->shunt_resistor = shunt_resistor; + else + data->shunt_resistor = SHUNT_RESISTOR_DEFAULT; + + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, + data, &tps23861_chip_info, + NULL); + if (IS_ERR(hwmon_dev)) + return PTR_ERR(hwmon_dev); + + tps23861_init_debugfs(data); + + return 0; +} + +static int tps23861_remove(struct i2c_client *client) +{ + struct tps23861_data *data = i2c_get_clientdata(client); + + debugfs_remove_recursive(data->debugfs_dir); + + return 0; +} + +static const struct of_device_id __maybe_unused tps23861_of_match[] = { + { .compatible = "ti,tps23861", }, + { }, +}; +MODULE_DEVICE_TABLE(of, tps23861_of_match); + +static struct i2c_driver tps23861_driver = { + .probe_new = tps23861_probe, + .remove = tps23861_remove, + .driver = { + .name = "tps23861", + .of_match_table = of_match_ptr(tps23861_of_match), + }, +}; +module_i2c_driver(tps23861_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Robert Marko "); +MODULE_DESCRIPTION("TI TPS23861 PoE PSE"); -- cgit v1.2.3 From 6ab3332cc692ad04dfa30c92d3391aea8b971ef2 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Thu, 21 Jan 2021 14:44:34 +0100 Subject: MAINTAINERS: Add entry for Texas Instruments TPS23861 PoE PSE Add maintainers entry for the Texas Instruments TPS23861 PoE PSE driver. Signed-off-by: Robert Marko Cc: Luka Perkov Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20210121134434.2782405-3-robert.marko@sartura.hr Signed-off-by: Guenter Roeck --- MAINTAINERS | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 00836f6452f0..e5c85366106f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17585,6 +17585,15 @@ F: include/dt-bindings/soc/ti,sci_pm_domain.h F: include/linux/soc/ti/ti_sci_inta_msi.h F: include/linux/soc/ti/ti_sci_protocol.h +TEXAS INSTRUMENTS TPS23861 PoE PSE DRIVER +M: Robert Marko +M: Luka Perkov +L: linux-hwmon@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/hwmon/ti,tps23861.yaml +F: Documentation/hwmon/tps23861.rst +F: drivers/hwmon/tps23861.c + THANKO'S RAREMONO AM/FM/SW RADIO RECEIVER USB DRIVER M: Hans Verkuil L: linux-media@vger.kernel.org -- cgit v1.2.3