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>; + }; + }; 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/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/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 diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index fcb870ce6286..8d5a2df1ecb6 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 @@ -39,6 +37,7 @@ Hardware Monitoring Kernel Drivers adt7462 adt7470 adt7475 + aht10 amc6821 amd_energy asb100 @@ -178,6 +177,7 @@ Hardware Monitoring Kernel Drivers tmp401 tmp421 tmp513 + tps23861 tps40422 tps53679 twl4030-madc-hwmon diff --git a/Documentation/hwmon/max16601.rst b/Documentation/hwmon/max16601.rst index 346e74674c51..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. @@ -45,115 +53,76 @@ Sysfs entries The following attributes are supported. -======================= ======================================================= -in1_label "vin1" -in1_input VCORE input voltage. -in1_alarm Input voltage 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. +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. +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. +curr[P+2]_label "iin1.P" +curr[P+2]_input VCORE phase P input current. -curr3_label "iin1.1" -curr3_input VCORE phase 1 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. -curr4_label "iin1.2" -curr4_input VCORE phase 2 input current. +curr[N+3]_label "iin3" +curr[N+3]_input VSA input current. -curr5_label "iin1.3" -curr5_input VCORE phase 3 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. -curr6_label "iin1.4" -curr6_input VCORE phase 4 input current. +curr[N+P+5]_label "iout1.P" +curr[N+P+5]_input VCORE phase P output current. -curr7_label "iin1.5" -curr7_input VCORE phase 5 input 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. -curr8_label "iin1.6" -curr8_input VCORE phase 6 input current. +power1_label "pin1" +power1_input Input power, derived from duty cycle and output + current. +power1_alarm Input power alarm. -curr9_label "iin1.7" -curr9_input VCORE phase 7 input current. +power2_label "pin2" +power2_input Input power, derived from input current sensor. -curr10_label "iin2" -curr10_input VCORE input current, derived from sensor element. +power3_label "pout" +power3_input Output power. -curr11_label "iin3" -curr11_input VSA input current. +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. -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. +temp2_input TSENSE_0 temperature +temp3_input TSENSE_1 temperature +temp4_input TSENSE_2 temperature +temp5_input TSENSE_3 temperature -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. -======================= ======================================================= +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/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/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/MAINTAINERS b/MAINTAINERS index 39838096ce60..66facb3ce444 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17620,6 +17620,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 diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 1ecf697d8d99..54f04e61fb83 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 @@ -257,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 @@ -1136,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 09a86c5e1d29..fe38e8a5c979 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 @@ -45,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 @@ -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/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 */ diff --git a/drivers/hwmon/aht10.c b/drivers/hwmon/aht10.c new file mode 100644 index 000000000000..2d9770cb4401 --- /dev/null +++ b/drivers/hwmon/aht10.c @@ -0,0 +1,348 @@ +// 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) { + mutex_unlock(&data->lock); + 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"); 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); 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; 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]) 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); 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"), + }, + }, { } }; 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) 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; } 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; 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; 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) 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/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) 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); diff --git a/drivers/hwmon/pmbus/max16601.c b/drivers/hwmon/pmbus/max16601.c index a960b86e72d2..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,9 @@ #include "pmbus.h" +enum chips { max16508, max16601 }; + +#define REG_DEFAULT_NUM_POP 0xc4 #define REG_SETPT_DVID 0xd1 #define DAC_10MV_MODE BIT(4) #define REG_IOUT_AVG_PK 0xee @@ -40,7 +43,10 @@ #define CORE_RAIL_INDICATOR BIT(7) #define REG_PHASE_REPORTING 0xf4 +#define MAX16601_NUM_PHASES 8 + struct max16601_data { + enum chips id; struct pmbus_driver_info info; struct i2c_client *vsa; int iout_avg_pkg; @@ -185,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); @@ -195,6 +202,21 @@ 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; + + /* + * 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 +238,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, @@ -239,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; + ret = i2c_smbus_read_block_data(client, PMBUS_IC_DEVICE_ID, buf); + if (ret < 0 || ret < 11) + return -ENODEV; + + /* + * 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; - ret = i2c_smbus_read_block_data(client, PMBUS_IC_DEVICE_ID, buf); - if (ret < 0) - return -ENODEV; + chip_id = max16601_get_id(client); + if (chip_id < 0) + return chip_id; - /* PMBUS_IC_DEVICE_ID is expected to return "MAX16601y.xx" */ - if (ret < 11 || strncmp(buf, "MAX16601", 8)) { - buf[ret] = '\0'; - dev_err(dev, "Unsupported chip '%s'\n", buf); - return -ENODEV; - } + 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) @@ -275,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)) { @@ -290,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", 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" }, { }, }; diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 192442b3b7a2..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; } @@ -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, @@ -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/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c index 111a91dc6b79..8c1e38c748ec 100644 --- a/drivers/hwmon/pwm-fan.c +++ b/drivers/hwmon/pwm-fan.c @@ -21,15 +21,21 @@ #define MAX_PWM 255 -struct pwm_fan_ctx { - struct mutex lock; - struct pwm_device *pwm; - struct regulator *reg_en; - +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 pwm_state pwm_state; + struct regulator *reg_en; + + int tach_count; + struct pwm_fan_tach *tachs; ktime_t sample_start; struct timer_list rpm_timer; @@ -40,6 +46,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[] = { @@ -52,22 +59,12 @@ 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) { - 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; } @@ -76,13 +73,18 @@ static void sample_timer(struct timer_list *t) { struct pwm_fan_ctx *ctx = from_timer(ctx, t, rpm_timer); unsigned int delta = ktime_ms_delta(ktime_get(), ctx->sample_start); - int pulses; + int i; if (delta) { - pulses = atomic_read(&ctx->pulses); - atomic_sub(pulses, &ctx->pulses); - ctx->rpm = (unsigned int)(pulses * 1000 * 60) / - (ctx->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(); } @@ -94,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: @@ -152,7 +153,7 @@ static int pwm_fan_read(struct device *dev, enum hwmon_sensor_types type, return 0; case hwmon_fan: - *val = ctx->rpm; + *val = ctx->tachs[channel].rpm; return 0; default: @@ -287,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); } @@ -298,9 +301,10 @@ static int pwm_fan_probe(struct platform_device *pdev) struct pwm_fan_ctx *ctx; 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) @@ -334,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; @@ -359,27 +361,46 @@ 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) { + for (i = 0; i < ctx->tach_count; i++) { + struct pwm_fan_tach *tach = &ctx->tachs[i]; 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->irq = platform_get_irq(pdev, i); + 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", @@ -388,22 +409,27 @@ 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) { + 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", - ctx->irq, ctx->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; @@ -441,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) { @@ -479,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) { @@ -494,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 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; } 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"); 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; 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 */