mirror of
https://github.com/tbsdtv/linux_media.git
synced 2025-07-23 20:51:03 +02:00
Merge tag 'hwmon-for-v5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging
Pull hwmon updates from Guenter Roeck: "New drivers: - Texas Instruments TPS23861 driver - AHT10 Temperature and Humidity Sensor Driver Support for new chips/variants to existing drivers: - Add AMD family 19h model 30h x86 match to amd_energy driver - Add Zen3 Ryzen Desktop CPUs support to k10temp driver - Add support for MAX16508 to max16601 driver - Support revision "B" of max31785 - Add support for ASRock boards to nct6683 driver Driver removals: - Decomission abx500 driver Various other minor fixes and improvements" * tag 'hwmon-for-v5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (30 commits) MAINTAINERS: Add entry for Texas Instruments TPS23861 PoE PSE hwmon: add Texas Instruments TPS23861 driver dt-bindings: hwmon: Add TI TPS23861 bindings hwmon: (da9052) Switch to using the new API kobj_to_dev() hwmon: (amd_energy) Add AMD family 19h model 30h x86 match hwmon: (pmbus/max31785) Support revision "B" hwmon: (pmbus/lm25066) Remove unnecessary pmbus_clear_cache function call hwmon: (pmbus) Clear sensor data after chip write hwmon: (pmbus/max16601) Add support for MAX16508 hwmon: (pmbus/max16601) Determine and use number of populated phases hwmon: (pmbus) Simplify the calculation of variables hwmon: (aht10) Unlock on error in aht10_read_values() hwmon: (pwm-fan) stop using legacy PWM functions and some cleanups hwmon: Add AHT10 Temperature and Humidity Sensor Driver hwmon: (applesmc) Assign boolean values to a bool variable hwmon: (nct6683) Support ASRock boards hwmon: (aspeed-pwm-tacho) Switch to using the new API kobj_to_dev() hwmon: (max6650) Switch to using the new API kobj_to_dev() hwmon: (pwm-fan) Support multiple fan tachometers hwmon: (pwm-fan) Store tach data separately ...
This commit is contained in:
51
Documentation/devicetree/bindings/hwmon/ti,tps23861.yaml
Normal file
51
Documentation/devicetree/bindings/hwmon/ti,tps23861.yaml
Normal file
@@ -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 <robert.marko@sartura.hr>
|
||||||
|
|
||||||
|
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>;
|
||||||
|
};
|
||||||
|
};
|
@@ -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 <martin.persson@stericsson.com>
|
|
||||||
- Hongbo Zhang <hongbo.zhang@linaro.org>
|
|
||||||
|
|
||||||
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.
|
|
@@ -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 <martin.persson@stericsson.com>
|
|
||||||
Hongbo Zhang <hongbo.zhang@linaro.org>
|
|
||||||
|
|
||||||
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.
|
|
46
Documentation/hwmon/aht10.rst
Normal file
46
Documentation/hwmon/aht10.rst
Normal file
@@ -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 <jcdra1@gmail.com>
|
||||||
|
|
||||||
|
|
||||||
|
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.
|
||||||
|
=============== ============================================
|
@@ -74,7 +74,7 @@ bus supply voltage.
|
|||||||
|
|
||||||
The shunt value in micro-ohms can be set via platform data or device tree at
|
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
|
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.
|
if the device tree is used.
|
||||||
|
|
||||||
Additionally ina226 supports update_interval attribute as described in
|
Additionally ina226 supports update_interval attribute as described in
|
||||||
|
@@ -18,10 +18,8 @@ Hardware Monitoring Kernel Drivers
|
|||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
|
|
||||||
ab8500
|
|
||||||
abituguru
|
abituguru
|
||||||
abituguru3
|
abituguru3
|
||||||
abx500
|
|
||||||
acpi_power_meter
|
acpi_power_meter
|
||||||
ad7314
|
ad7314
|
||||||
adc128d818
|
adc128d818
|
||||||
@@ -39,6 +37,7 @@ Hardware Monitoring Kernel Drivers
|
|||||||
adt7462
|
adt7462
|
||||||
adt7470
|
adt7470
|
||||||
adt7475
|
adt7475
|
||||||
|
aht10
|
||||||
amc6821
|
amc6821
|
||||||
amd_energy
|
amd_energy
|
||||||
asb100
|
asb100
|
||||||
@@ -178,6 +177,7 @@ Hardware Monitoring Kernel Drivers
|
|||||||
tmp401
|
tmp401
|
||||||
tmp421
|
tmp421
|
||||||
tmp513
|
tmp513
|
||||||
|
tps23861
|
||||||
tps40422
|
tps40422
|
||||||
tps53679
|
tps53679
|
||||||
twl4030-madc-hwmon
|
twl4030-madc-hwmon
|
||||||
|
@@ -5,6 +5,14 @@ Kernel driver max16601
|
|||||||
|
|
||||||
Supported chips:
|
Supported chips:
|
||||||
|
|
||||||
|
* Maxim MAX16508
|
||||||
|
|
||||||
|
Prefix: 'max16508'
|
||||||
|
|
||||||
|
Addresses scanned: -
|
||||||
|
|
||||||
|
Datasheet: Not published
|
||||||
|
|
||||||
* Maxim MAX16601
|
* Maxim MAX16601
|
||||||
|
|
||||||
Prefix: 'max16601'
|
Prefix: 'max16601'
|
||||||
@@ -19,8 +27,8 @@ Author: Guenter Roeck <linux@roeck-us.net>
|
|||||||
Description
|
Description
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
This driver supports the MAX16601 VR13.HC Dual-Output Voltage Regulator
|
This driver supports the MAX16508 VR13 Dual-Output Voltage Regulator
|
||||||
Chipset.
|
as well as the MAX16601 VR13.HC Dual-Output Voltage Regulator chipsets.
|
||||||
|
|
||||||
The driver is a client driver to the core PMBus driver.
|
The driver is a client driver to the core PMBus driver.
|
||||||
Please see Documentation/hwmon/pmbus.rst for details on PMBus client drivers.
|
Please see Documentation/hwmon/pmbus.rst for details on PMBus client drivers.
|
||||||
@@ -45,115 +53,76 @@ Sysfs entries
|
|||||||
|
|
||||||
The following attributes are supported.
|
The following attributes are supported.
|
||||||
|
|
||||||
======================= =======================================================
|
=============================== ===============================================
|
||||||
in1_label "vin1"
|
in1_label "vin1"
|
||||||
in1_input VCORE input voltage.
|
in1_input VCORE input voltage.
|
||||||
in1_alarm Input voltage alarm.
|
in1_alarm Input voltage alarm.
|
||||||
|
|
||||||
in2_label "vout1"
|
in2_label "vout1"
|
||||||
in2_input VCORE output voltage.
|
in2_input VCORE output voltage.
|
||||||
in2_alarm Output voltage alarm.
|
in2_alarm Output voltage alarm.
|
||||||
|
|
||||||
curr1_label "iin1"
|
curr1_label "iin1"
|
||||||
curr1_input VCORE input current, derived from duty cycle and output
|
curr1_input VCORE input current, derived from duty cycle
|
||||||
current.
|
and output current.
|
||||||
curr1_max Maximum input current.
|
curr1_max Maximum input current.
|
||||||
curr1_max_alarm Current high alarm.
|
curr1_max_alarm Current high alarm.
|
||||||
|
|
||||||
curr2_label "iin1.0"
|
curr[P+2]_label "iin1.P"
|
||||||
curr2_input VCORE phase 0 input current.
|
curr[P+2]_input VCORE phase P input current.
|
||||||
|
|
||||||
curr3_label "iin1.1"
|
curr[N+2]_label "iin2"
|
||||||
curr3_input VCORE phase 1 input current.
|
curr[N+2]_input VCORE input current, derived from sensor
|
||||||
|
element.
|
||||||
|
'N' is the number of enabled/populated phases.
|
||||||
|
|
||||||
curr4_label "iin1.2"
|
curr[N+3]_label "iin3"
|
||||||
curr4_input VCORE phase 2 input current.
|
curr[N+3]_input VSA input current.
|
||||||
|
|
||||||
curr5_label "iin1.3"
|
curr[N+4]_label "iout1"
|
||||||
curr5_input VCORE phase 3 input current.
|
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"
|
curr[N+P+5]_label "iout1.P"
|
||||||
curr6_input VCORE phase 4 input current.
|
curr[N+P+5]_input VCORE phase P output current.
|
||||||
|
|
||||||
curr7_label "iin1.5"
|
curr[2*N+5]_label "iout3"
|
||||||
curr7_input VCORE phase 5 input current.
|
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"
|
power1_label "pin1"
|
||||||
curr8_input VCORE phase 6 input current.
|
power1_input Input power, derived from duty cycle and output
|
||||||
|
current.
|
||||||
|
power1_alarm Input power alarm.
|
||||||
|
|
||||||
curr9_label "iin1.7"
|
power2_label "pin2"
|
||||||
curr9_input VCORE phase 7 input current.
|
power2_input Input power, derived from input current sensor.
|
||||||
|
|
||||||
curr10_label "iin2"
|
power3_label "pout"
|
||||||
curr10_input VCORE input current, derived from sensor element.
|
power3_input Output power.
|
||||||
|
|
||||||
curr11_label "iin3"
|
temp1_input VCORE temperature.
|
||||||
curr11_input VSA input current.
|
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"
|
temp2_input TSENSE_0 temperature
|
||||||
curr12_input VCORE output current.
|
temp3_input TSENSE_1 temperature
|
||||||
curr12_crit Critical output current.
|
temp4_input TSENSE_2 temperature
|
||||||
curr12_crit_alarm Output current critical alarm.
|
temp5_input TSENSE_3 temperature
|
||||||
curr12_max Maximum output current.
|
|
||||||
curr12_max_alarm Output current high alarm.
|
|
||||||
|
|
||||||
curr13_label "iout1.0"
|
temp6_input VSA temperature.
|
||||||
curr13_input VCORE phase 0 output current.
|
temp6_crit Critical high temperature.
|
||||||
|
temp6_crit_alarm Chip temperature critical high alarm.
|
||||||
curr14_label "iout1.1"
|
temp6_max Maximum temperature.
|
||||||
curr14_input VCORE phase 1 output current.
|
temp6_max_alarm Chip temperature high alarm.
|
||||||
|
=============================== ===============================================
|
||||||
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.
|
|
||||||
======================= =======================================================
|
|
||||||
|
@@ -61,5 +61,6 @@ Board Firmware version
|
|||||||
Intel DH87RL NCT6683D EC firmware version 1.0 build 04/03/13
|
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 DH87MC NCT6683D EC firmware version 1.0 build 04/03/13
|
||||||
Intel DB85FL 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
|
MSI B550 NCT6687D EC firmware version 1.0 build 05/07/20
|
||||||
=============== ===============================================
|
=============== ===============================================
|
||||||
|
41
Documentation/hwmon/tps23861.rst
Normal file
41
Documentation/hwmon/tps23861.rst
Normal file
@@ -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 <robert.marko@sartura.hr>
|
||||||
|
|
||||||
|
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]
|
||||||
|
======================= =====================================================================
|
@@ -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_inta_msi.h
|
||||||
F: include/linux/soc/ti/ti_sci_protocol.h
|
F: include/linux/soc/ti/ti_sci_protocol.h
|
||||||
|
|
||||||
|
TEXAS INSTRUMENTS TPS23861 PoE PSE DRIVER
|
||||||
|
M: Robert Marko <robert.marko@sartura.hr>
|
||||||
|
M: Luka Perkov <luka.perkov@sartura.hr>
|
||||||
|
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
|
THANKO'S RAREMONO AM/FM/SW RADIO RECEIVER USB DRIVER
|
||||||
M: Hans Verkuil <hverkuil@xs4all.nl>
|
M: Hans Verkuil <hverkuil@xs4all.nl>
|
||||||
L: linux-media@vger.kernel.org
|
L: linux-media@vger.kernel.org
|
||||||
|
@@ -38,19 +38,6 @@ config HWMON_DEBUG_CHIP
|
|||||||
|
|
||||||
comment "Native drivers"
|
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
|
config SENSORS_ABITUGURU
|
||||||
tristate "Abit uGuru (rev 1 & 2)"
|
tristate "Abit uGuru (rev 1 & 2)"
|
||||||
depends on X86 && DMI
|
depends on X86 && DMI
|
||||||
@@ -257,6 +244,16 @@ config SENSORS_ADT7475
|
|||||||
This driver can also be built as a module. If so, the module
|
This driver can also be built as a module. If so, the module
|
||||||
will be called adt7475.
|
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
|
config SENSORS_AS370
|
||||||
tristate "Synaptics AS370 SoC hardware monitoring driver"
|
tristate "Synaptics AS370 SoC hardware monitoring driver"
|
||||||
help
|
help
|
||||||
@@ -1136,6 +1133,17 @@ config SENSORS_TC654
|
|||||||
This driver can also be built as a module. If so, the module
|
This driver can also be built as a module. If so, the module
|
||||||
will be called tc654.
|
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
|
config SENSORS_MENF21BMC_HWMON
|
||||||
tristate "MEN 14F021P00 BMC Hardware Monitoring"
|
tristate "MEN 14F021P00 BMC Hardware Monitoring"
|
||||||
depends on MFD_MENF21BMC
|
depends on MFD_MENF21BMC
|
||||||
|
@@ -21,7 +21,6 @@ obj-$(CONFIG_SENSORS_W83795) += w83795.o
|
|||||||
obj-$(CONFIG_SENSORS_W83781D) += w83781d.o
|
obj-$(CONFIG_SENSORS_W83781D) += w83781d.o
|
||||||
obj-$(CONFIG_SENSORS_W83791D) += w83791d.o
|
obj-$(CONFIG_SENSORS_W83791D) += w83791d.o
|
||||||
|
|
||||||
obj-$(CONFIG_SENSORS_AB8500) += abx500.o ab8500.o
|
|
||||||
obj-$(CONFIG_SENSORS_ABITUGURU) += abituguru.o
|
obj-$(CONFIG_SENSORS_ABITUGURU) += abituguru.o
|
||||||
obj-$(CONFIG_SENSORS_ABITUGURU3)+= abituguru3.o
|
obj-$(CONFIG_SENSORS_ABITUGURU3)+= abituguru3.o
|
||||||
obj-$(CONFIG_SENSORS_AD7314) += ad7314.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_ADT7462) += adt7462.o
|
||||||
obj-$(CONFIG_SENSORS_ADT7470) += adt7470.o
|
obj-$(CONFIG_SENSORS_ADT7470) += adt7470.o
|
||||||
obj-$(CONFIG_SENSORS_ADT7475) += adt7475.o
|
obj-$(CONFIG_SENSORS_ADT7475) += adt7475.o
|
||||||
|
obj-$(CONFIG_SENSORS_AHT10) += aht10.o
|
||||||
obj-$(CONFIG_SENSORS_AMD_ENERGY) += amd_energy.o
|
obj-$(CONFIG_SENSORS_AMD_ENERGY) += amd_energy.o
|
||||||
obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o
|
obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o
|
||||||
obj-$(CONFIG_SENSORS_ARM_SCMI) += scmi-hwmon.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_MC13783_ADC)+= mc13783-adc.o
|
||||||
obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o
|
obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o
|
||||||
obj-$(CONFIG_SENSORS_TC654) += tc654.o
|
obj-$(CONFIG_SENSORS_TC654) += tc654.o
|
||||||
|
obj-$(CONFIG_SENSORS_TPS23861) += tps23861.o
|
||||||
obj-$(CONFIG_SENSORS_MLXREG_FAN) += mlxreg-fan.o
|
obj-$(CONFIG_SENSORS_MLXREG_FAN) += mlxreg-fan.o
|
||||||
obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o
|
obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o
|
||||||
obj-$(CONFIG_SENSORS_MR75203) += mr75203.o
|
obj-$(CONFIG_SENSORS_MR75203) += mr75203.o
|
||||||
|
@@ -1,224 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-only
|
|
||||||
/*
|
|
||||||
* Copyright (C) ST-Ericsson 2010 - 2013
|
|
||||||
* Author: Martin Persson <martin.persson@stericsson.com>
|
|
||||||
* Hongbo Zhang <hongbo.zhang@linaro.org>
|
|
||||||
*
|
|
||||||
* 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 <linux/err.h>
|
|
||||||
#include <linux/hwmon.h>
|
|
||||||
#include <linux/hwmon-sysfs.h>
|
|
||||||
#include <linux/mfd/abx500.h>
|
|
||||||
#include <linux/mfd/abx500/ab8500-bm.h>
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/platform_device.h>
|
|
||||||
#include <linux/power/ab8500.h>
|
|
||||||
#include <linux/reboot.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/sysfs.h>
|
|
||||||
#include <linux/iio/consumer.h>
|
|
||||||
#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 <hongbo.zhang@linaro.org>");
|
|
||||||
MODULE_DESCRIPTION("AB8500 temperature driver");
|
|
||||||
MODULE_LICENSE("GPL");
|
|
@@ -1,487 +0,0 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-only
|
|
||||||
/*
|
|
||||||
* Copyright (C) ST-Ericsson 2010 - 2013
|
|
||||||
* Author: Martin Persson <martin.persson@stericsson.com>
|
|
||||||
* Hongbo Zhang <hongbo.zhang@linaro.org>
|
|
||||||
*
|
|
||||||
* 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 <linux/err.h>
|
|
||||||
#include <linux/hwmon.h>
|
|
||||||
#include <linux/hwmon-sysfs.h>
|
|
||||||
#include <linux/interrupt.h>
|
|
||||||
#include <linux/jiffies.h>
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/mutex.h>
|
|
||||||
#include <linux/of.h>
|
|
||||||
#include <linux/platform_device.h>
|
|
||||||
#include <linux/pm.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/sysfs.h>
|
|
||||||
#include <linux/workqueue.h>
|
|
||||||
#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 <martin.persson@stericsson.com>");
|
|
||||||
MODULE_DESCRIPTION("ABX500 temperature driver");
|
|
||||||
MODULE_LICENSE("GPL");
|
|
@@ -1,69 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
|
||||||
/*
|
|
||||||
* Copyright (C) ST-Ericsson 2010 - 2013
|
|
||||||
* Author: Martin Persson <martin.persson@stericsson.com>
|
|
||||||
* Hongbo Zhang <hongbo.zhang@linaro.com>
|
|
||||||
*/
|
|
||||||
|
|
||||||
#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 */
|
|
348
drivers/hwmon/aht10.c
Normal file
348
drivers/hwmon/aht10.c
Normal file
@@ -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 <linux/delay.h>
|
||||||
|
#include <linux/hwmon.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/ktime.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
|
||||||
|
#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 <jcdra1@gmail.com>");
|
||||||
|
MODULE_DESCRIPTION("AHT10 Temperature and Humidity sensor driver");
|
||||||
|
MODULE_VERSION("1.0");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
@@ -333,6 +333,7 @@ static struct platform_device *amd_energy_platdev;
|
|||||||
static const struct x86_cpu_id cpu_ids[] __initconst = {
|
static const struct x86_cpu_id cpu_ids[] __initconst = {
|
||||||
X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x17, 0x31, NULL),
|
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, 0x01, NULL),
|
||||||
|
X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x19, 0x30, NULL),
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
MODULE_DEVICE_TABLE(x86cpu, cpu_ids);
|
MODULE_DEVICE_TABLE(x86cpu, cpu_ids);
|
||||||
|
@@ -565,7 +565,7 @@ static int applesmc_init_index(struct applesmc_registers *s)
|
|||||||
static int applesmc_init_smcreg_try(void)
|
static int applesmc_init_smcreg_try(void)
|
||||||
{
|
{
|
||||||
struct applesmc_registers *s = &smcreg;
|
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;
|
unsigned int count;
|
||||||
u8 tmp[1];
|
u8 tmp[1];
|
||||||
int ret;
|
int ret;
|
||||||
|
@@ -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,
|
static umode_t pwm_is_visible(struct kobject *kobj,
|
||||||
struct attribute *a, int index)
|
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);
|
struct aspeed_pwm_tacho_data *priv = dev_get_drvdata(dev);
|
||||||
|
|
||||||
if (!priv->pwm_present[index])
|
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,
|
static umode_t fan_dev_is_visible(struct kobject *kobj,
|
||||||
struct attribute *a, int index)
|
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);
|
struct aspeed_pwm_tacho_data *priv = dev_get_drvdata(dev);
|
||||||
|
|
||||||
if (!priv->fan_tach_present[index])
|
if (!priv->fan_tach_present[index])
|
||||||
|
@@ -299,7 +299,7 @@ static ssize_t label_show(struct device *dev,
|
|||||||
static umode_t da9052_channel_is_visible(struct kobject *kobj,
|
static umode_t da9052_channel_is_visible(struct kobject *kobj,
|
||||||
struct attribute *attr, int index)
|
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 da9052_hwmon *hwmon = dev_get_drvdata(dev);
|
||||||
struct device_attribute *dattr = container_of(attr,
|
struct device_attribute *dattr = container_of(attr,
|
||||||
struct device_attribute, attr);
|
struct device_attribute, attr);
|
||||||
|
@@ -1159,6 +1159,13 @@ static struct dmi_system_id i8k_blacklist_fan_support_dmi_table[] __initdata = {
|
|||||||
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "XPS13 9333"),
|
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"),
|
||||||
|
},
|
||||||
|
},
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -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,
|
static umode_t gpio_fan_is_visible(struct kobject *kobj,
|
||||||
struct attribute *attr, int index)
|
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);
|
struct gpio_fan_data *data = dev_get_drvdata(dev);
|
||||||
|
|
||||||
if (index == 0 && !data->alarm_gpio)
|
if (index == 0 && !data->alarm_gpio)
|
||||||
|
@@ -448,7 +448,8 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
|||||||
data->is_zen = true;
|
data->is_zen = true;
|
||||||
|
|
||||||
switch (boot_cpu_data.x86_model) {
|
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);
|
k10temp_get_ccd_support(pdev, data, 8);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@@ -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,
|
static umode_t max6650_attrs_visible(struct kobject *kobj, struct attribute *a,
|
||||||
int n)
|
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 max6650_data *data = dev_get_drvdata(dev);
|
||||||
struct device_attribute *devattr;
|
struct device_attribute *devattr;
|
||||||
|
|
||||||
|
@@ -169,6 +169,7 @@ superio_exit(int ioreg)
|
|||||||
#define NCT6683_CUSTOMER_ID_INTEL 0x805
|
#define NCT6683_CUSTOMER_ID_INTEL 0x805
|
||||||
#define NCT6683_CUSTOMER_ID_MITAC 0xa0e
|
#define NCT6683_CUSTOMER_ID_MITAC 0xa0e
|
||||||
#define NCT6683_CUSTOMER_ID_MSI 0x201
|
#define NCT6683_CUSTOMER_ID_MSI 0x201
|
||||||
|
#define NCT6683_CUSTOMER_ID_ASROCK 0xe2c
|
||||||
|
|
||||||
#define NCT6683_REG_BUILD_YEAR 0x604
|
#define NCT6683_REG_BUILD_YEAR 0x604
|
||||||
#define NCT6683_REG_BUILD_MONTH 0x605
|
#define NCT6683_REG_BUILD_MONTH 0x605
|
||||||
@@ -1225,6 +1226,8 @@ static int nct6683_probe(struct platform_device *pdev)
|
|||||||
break;
|
break;
|
||||||
case NCT6683_CUSTOMER_ID_MSI:
|
case NCT6683_CUSTOMER_ID_MSI:
|
||||||
break;
|
break;
|
||||||
|
case NCT6683_CUSTOMER_ID_ASROCK:
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
if (!force)
|
if (!force)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
@@ -1700,8 +1700,8 @@ static int __init pc87360_device_add(unsigned short address)
|
|||||||
continue;
|
continue;
|
||||||
res[res_count].start = extra_isa[i];
|
res[res_count].start = extra_isa[i];
|
||||||
res[res_count].end = extra_isa[i] + PC87360_EXTENT - 1;
|
res[res_count].end = extra_isa[i] + PC87360_EXTENT - 1;
|
||||||
res[res_count].name = "pc87360",
|
res[res_count].name = "pc87360";
|
||||||
res[res_count].flags = IORESOURCE_IO,
|
res[res_count].flags = IORESOURCE_IO;
|
||||||
|
|
||||||
err = acpi_check_resource_conflict(&res[res_count]);
|
err = acpi_check_resource_conflict(&res[res_count]);
|
||||||
if (err)
|
if (err)
|
||||||
|
@@ -158,10 +158,10 @@ config SENSORS_MAX16064
|
|||||||
be called max16064.
|
be called max16064.
|
||||||
|
|
||||||
config SENSORS_MAX16601
|
config SENSORS_MAX16601
|
||||||
tristate "Maxim MAX16601"
|
tristate "Maxim MAX16508, MAX16601"
|
||||||
help
|
help
|
||||||
If you say yes here you get hardware monitoring support for Maxim
|
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
|
This driver can also be built as a module. If so, the module will
|
||||||
be called max16601.
|
be called max16601.
|
||||||
|
@@ -472,7 +472,7 @@ static struct pmbus_driver_info ibm_cffps_info[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static struct pmbus_platform_data ibm_cffps_pdata = {
|
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)
|
static int ibm_cffps_probe(struct i2c_client *client)
|
||||||
|
@@ -371,21 +371,18 @@ static int lm25066_write_word_data(struct i2c_client *client, int page, int reg,
|
|||||||
case PMBUS_VIN_OV_WARN_LIMIT:
|
case PMBUS_VIN_OV_WARN_LIMIT:
|
||||||
word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit);
|
word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit);
|
||||||
ret = pmbus_write_word_data(client, 0, reg, word);
|
ret = pmbus_write_word_data(client, 0, reg, word);
|
||||||
pmbus_clear_cache(client);
|
|
||||||
break;
|
break;
|
||||||
case PMBUS_IIN_OC_WARN_LIMIT:
|
case PMBUS_IIN_OC_WARN_LIMIT:
|
||||||
word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit);
|
word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit);
|
||||||
ret = pmbus_write_word_data(client, 0,
|
ret = pmbus_write_word_data(client, 0,
|
||||||
LM25066_MFR_IIN_OC_WARN_LIMIT,
|
LM25066_MFR_IIN_OC_WARN_LIMIT,
|
||||||
word);
|
word);
|
||||||
pmbus_clear_cache(client);
|
|
||||||
break;
|
break;
|
||||||
case PMBUS_PIN_OP_WARN_LIMIT:
|
case PMBUS_PIN_OP_WARN_LIMIT:
|
||||||
word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit);
|
word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit);
|
||||||
ret = pmbus_write_word_data(client, 0,
|
ret = pmbus_write_word_data(client, 0,
|
||||||
LM25066_MFR_PIN_OP_WARN_LIMIT,
|
LM25066_MFR_PIN_OP_WARN_LIMIT,
|
||||||
word);
|
word);
|
||||||
pmbus_clear_cache(client);
|
|
||||||
break;
|
break;
|
||||||
case PMBUS_VIRT_VMON_UV_WARN_LIMIT:
|
case PMBUS_VIRT_VMON_UV_WARN_LIMIT:
|
||||||
/* Adjust from VIN coefficients (for LM25056) */
|
/* 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);
|
word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit);
|
||||||
ret = pmbus_write_word_data(client, 0,
|
ret = pmbus_write_word_data(client, 0,
|
||||||
LM25056_VAUX_UV_WARN_LIMIT, word);
|
LM25056_VAUX_UV_WARN_LIMIT, word);
|
||||||
pmbus_clear_cache(client);
|
|
||||||
break;
|
break;
|
||||||
case PMBUS_VIRT_VMON_OV_WARN_LIMIT:
|
case PMBUS_VIRT_VMON_OV_WARN_LIMIT:
|
||||||
/* Adjust from VIN coefficients (for LM25056) */
|
/* 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);
|
word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit);
|
||||||
ret = pmbus_write_word_data(client, 0,
|
ret = pmbus_write_word_data(client, 0,
|
||||||
LM25056_VAUX_OV_WARN_LIMIT, word);
|
LM25056_VAUX_OV_WARN_LIMIT, word);
|
||||||
pmbus_clear_cache(client);
|
|
||||||
break;
|
break;
|
||||||
case PMBUS_VIRT_RESET_PIN_HISTORY:
|
case PMBUS_VIRT_RESET_PIN_HISTORY:
|
||||||
ret = pmbus_write_byte(client, 0, LM25066_CLEAR_PIN_PEAK);
|
ret = pmbus_write_byte(client, 0, LM25066_CLEAR_PIN_PEAK);
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
/*
|
/*
|
||||||
* Hardware monitoring driver for Maxim MAX16601
|
* Hardware monitoring driver for Maxim MAX16508 and MAX16601.
|
||||||
*
|
*
|
||||||
* Implementation notes:
|
* Implementation notes:
|
||||||
*
|
*
|
||||||
* Ths chip supports two rails, VCORE and VSA. Telemetry information for the
|
* This chip series supports two rails, VCORE and VSA. Telemetry information
|
||||||
* two rails is reported in two subsequent I2C addresses. The driver
|
* 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
|
* instantiates a dummy I2C client at the second I2C address to report
|
||||||
* information for the VSA rail in a single instance of the driver.
|
* 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.
|
* Telemetry for the VSA rail is reported to the PMBus core in PMBus page 2.
|
||||||
@@ -31,6 +31,9 @@
|
|||||||
|
|
||||||
#include "pmbus.h"
|
#include "pmbus.h"
|
||||||
|
|
||||||
|
enum chips { max16508, max16601 };
|
||||||
|
|
||||||
|
#define REG_DEFAULT_NUM_POP 0xc4
|
||||||
#define REG_SETPT_DVID 0xd1
|
#define REG_SETPT_DVID 0xd1
|
||||||
#define DAC_10MV_MODE BIT(4)
|
#define DAC_10MV_MODE BIT(4)
|
||||||
#define REG_IOUT_AVG_PK 0xee
|
#define REG_IOUT_AVG_PK 0xee
|
||||||
@@ -40,7 +43,10 @@
|
|||||||
#define CORE_RAIL_INDICATOR BIT(7)
|
#define CORE_RAIL_INDICATOR BIT(7)
|
||||||
#define REG_PHASE_REPORTING 0xf4
|
#define REG_PHASE_REPORTING 0xf4
|
||||||
|
|
||||||
|
#define MAX16601_NUM_PHASES 8
|
||||||
|
|
||||||
struct max16601_data {
|
struct max16601_data {
|
||||||
|
enum chips id;
|
||||||
struct pmbus_driver_info info;
|
struct pmbus_driver_info info;
|
||||||
struct i2c_client *vsa;
|
struct i2c_client *vsa;
|
||||||
int iout_avg_pkg;
|
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,
|
static int max16601_identify(struct i2c_client *client,
|
||||||
struct pmbus_driver_info *info)
|
struct pmbus_driver_info *info)
|
||||||
{
|
{
|
||||||
|
struct max16601_data *data = to_max16601_data(info);
|
||||||
int reg;
|
int reg;
|
||||||
|
|
||||||
reg = i2c_smbus_read_byte_data(client, REG_SETPT_DVID);
|
reg = i2c_smbus_read_byte_data(client, REG_SETPT_DVID);
|
||||||
@@ -195,6 +202,21 @@ static int max16601_identify(struct i2c_client *client,
|
|||||||
else
|
else
|
||||||
info->vrm_version[0] = vr12;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,7 +238,7 @@ static struct pmbus_driver_info max16601_info = {
|
|||||||
.func[2] = PMBUS_HAVE_IIN | PMBUS_HAVE_STATUS_INPUT |
|
.func[2] = PMBUS_HAVE_IIN | PMBUS_HAVE_STATUS_INPUT |
|
||||||
PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
|
PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
|
||||||
PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | PMBUS_PAGE_VIRTUAL,
|
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[0] = PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP,
|
||||||
.pfunc[1] = PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT,
|
.pfunc[1] = PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT,
|
||||||
.pfunc[2] = PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP,
|
.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);
|
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;
|
struct device *dev = &client->dev;
|
||||||
u8 buf[I2C_SMBUS_BLOCK_MAX + 1];
|
u8 buf[I2C_SMBUS_BLOCK_MAX + 1];
|
||||||
struct max16601_data *data;
|
enum chips id;
|
||||||
int ret;
|
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,
|
if (!i2c_check_functionality(client->adapter,
|
||||||
I2C_FUNC_SMBUS_READ_BYTE_DATA |
|
I2C_FUNC_SMBUS_READ_BYTE_DATA |
|
||||||
I2C_FUNC_SMBUS_READ_BLOCK_DATA))
|
I2C_FUNC_SMBUS_READ_BLOCK_DATA))
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
ret = i2c_smbus_read_block_data(client, PMBUS_IC_DEVICE_ID, buf);
|
chip_id = max16601_get_id(client);
|
||||||
if (ret < 0)
|
if (chip_id < 0)
|
||||||
return -ENODEV;
|
return chip_id;
|
||||||
|
|
||||||
/* PMBUS_IC_DEVICE_ID is expected to return "MAX16601y.xx" */
|
id = i2c_match_id(max16601_id, client);
|
||||||
if (ret < 11 || strncmp(buf, "MAX16601", 8)) {
|
if (chip_id != id->driver_data)
|
||||||
buf[ret] = '\0';
|
dev_warn(&client->dev,
|
||||||
dev_err(dev, "Unsupported chip '%s'\n", buf);
|
"Device mismatch: Configured %s (%d), detected %d\n",
|
||||||
return -ENODEV;
|
id->name, (int) id->driver_data, chip_id);
|
||||||
}
|
|
||||||
|
|
||||||
ret = i2c_smbus_read_byte_data(client, REG_PHASE_ID);
|
ret = i2c_smbus_read_byte_data(client, REG_PHASE_ID);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
@@ -275,6 +330,7 @@ static int max16601_probe(struct i2c_client *client)
|
|||||||
if (!data)
|
if (!data)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
data->id = chip_id;
|
||||||
data->iout_avg_pkg = 0xfc00;
|
data->iout_avg_pkg = 0xfc00;
|
||||||
data->vsa = i2c_new_dummy_device(client->adapter, client->addr + 1);
|
data->vsa = i2c_new_dummy_device(client->adapter, client->addr + 1);
|
||||||
if (IS_ERR(data->vsa)) {
|
if (IS_ERR(data->vsa)) {
|
||||||
@@ -290,13 +346,6 @@ static int max16601_probe(struct i2c_client *client)
|
|||||||
return pmbus_do_probe(client, &data->info);
|
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 = {
|
static struct i2c_driver max16601_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "max16601",
|
.name = "max16601",
|
||||||
|
@@ -17,6 +17,7 @@ enum max31785_regs {
|
|||||||
|
|
||||||
#define MAX31785 0x3030
|
#define MAX31785 0x3030
|
||||||
#define MAX31785A 0x3040
|
#define MAX31785A 0x3040
|
||||||
|
#define MAX31785B 0x3061
|
||||||
|
|
||||||
#define MFR_FAN_CONFIG_DUAL_TACH BIT(12)
|
#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 device *dev = &client->dev;
|
||||||
struct pmbus_driver_info *info;
|
struct pmbus_driver_info *info;
|
||||||
bool dual_tach = false;
|
bool dual_tach = false;
|
||||||
s64 ret;
|
int ret;
|
||||||
|
|
||||||
if (!i2c_check_functionality(client->adapter,
|
if (!i2c_check_functionality(client->adapter,
|
||||||
I2C_FUNC_SMBUS_BYTE_DATA |
|
I2C_FUNC_SMBUS_BYTE_DATA |
|
||||||
@@ -350,12 +351,14 @@ static int max31785_probe(struct i2c_client *client)
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
if (ret == MAX31785A) {
|
if (ret == MAX31785A || ret == MAX31785B) {
|
||||||
dual_tach = true;
|
dual_tach = true;
|
||||||
} else if (ret == MAX31785) {
|
} else if (ret == MAX31785) {
|
||||||
if (!strcmp("max31785a", client->name))
|
if (!strcmp("max31785a", client->name) ||
|
||||||
dev_warn(dev, "Expected max3175a, found max31785: cannot provide secondary tachometer readings\n");
|
!strcmp("max31785b", client->name))
|
||||||
|
dev_warn(dev, "Expected max31785a/b, found max31785: cannot provide secondary tachometer readings\n");
|
||||||
} else {
|
} else {
|
||||||
|
dev_err(dev, "Unrecognized MAX31785 revision: %x\n", ret);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -371,6 +374,7 @@ static int max31785_probe(struct i2c_client *client)
|
|||||||
static const struct i2c_device_id max31785_id[] = {
|
static const struct i2c_device_id max31785_id[] = {
|
||||||
{ "max31785", 0 },
|
{ "max31785", 0 },
|
||||||
{ "max31785a", 0 },
|
{ "max31785a", 0 },
|
||||||
|
{ "max31785b", 0 },
|
||||||
{ },
|
{ },
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -379,6 +383,7 @@ MODULE_DEVICE_TABLE(i2c, max31785_id);
|
|||||||
static const struct of_device_id max31785_of_match[] = {
|
static const struct of_device_id max31785_of_match[] = {
|
||||||
{ .compatible = "maxim,max31785" },
|
{ .compatible = "maxim,max31785" },
|
||||||
{ .compatible = "maxim,max31785a" },
|
{ .compatible = "maxim,max31785a" },
|
||||||
|
{ .compatible = "maxim,max31785b" },
|
||||||
{ },
|
{ },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -974,7 +974,7 @@ static ssize_t pmbus_set_sensor(struct device *dev,
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
rv = ret;
|
rv = ret;
|
||||||
else
|
else
|
||||||
sensor->data = regval;
|
sensor->data = -ENODATA;
|
||||||
mutex_unlock(&data->update_lock);
|
mutex_unlock(&data->update_lock);
|
||||||
return rv;
|
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.
|
* which global bit is set) for this page is accessible.
|
||||||
*/
|
*/
|
||||||
if (!ret && attr->gbit &&
|
if (!ret && attr->gbit &&
|
||||||
(!upper || (upper && data->has_status_word)) &&
|
(!upper || data->has_status_word) &&
|
||||||
pmbus_check_status_register(client, page)) {
|
pmbus_check_status_register(client, page)) {
|
||||||
ret = pmbus_add_boolean(data, name, "alarm", index,
|
ret = pmbus_add_boolean(data, name, "alarm", index,
|
||||||
NULL, NULL,
|
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 */
|
/* Enable PEC if the controller supports it */
|
||||||
ret = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY);
|
if (!(data->flags & PMBUS_NO_CAPABILITY)) {
|
||||||
if (ret >= 0 && (ret & PB_CAPABILITY_ERROR_CHECK))
|
ret = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY);
|
||||||
client->flags |= I2C_CLIENT_PEC;
|
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
|
* Check if the chip is write protected. If it is, we can not clear
|
||||||
|
@@ -21,15 +21,21 @@
|
|||||||
|
|
||||||
#define MAX_PWM 255
|
#define MAX_PWM 255
|
||||||
|
|
||||||
struct pwm_fan_ctx {
|
struct pwm_fan_tach {
|
||||||
struct mutex lock;
|
|
||||||
struct pwm_device *pwm;
|
|
||||||
struct regulator *reg_en;
|
|
||||||
|
|
||||||
int irq;
|
int irq;
|
||||||
atomic_t pulses;
|
atomic_t pulses;
|
||||||
unsigned int rpm;
|
unsigned int rpm;
|
||||||
u8 pulses_per_revolution;
|
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;
|
ktime_t sample_start;
|
||||||
struct timer_list rpm_timer;
|
struct timer_list rpm_timer;
|
||||||
|
|
||||||
@@ -40,6 +46,7 @@ struct pwm_fan_ctx {
|
|||||||
struct thermal_cooling_device *cdev;
|
struct thermal_cooling_device *cdev;
|
||||||
|
|
||||||
struct hwmon_chip_info info;
|
struct hwmon_chip_info info;
|
||||||
|
struct hwmon_channel_info fan_channel;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const u32 pwm_fan_channel_config_pwm[] = {
|
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,
|
.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. */
|
/* This handler assumes self resetting edge triggered interrupt. */
|
||||||
static irqreturn_t pulse_handler(int irq, void *dev_id)
|
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;
|
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);
|
struct pwm_fan_ctx *ctx = from_timer(ctx, t, rpm_timer);
|
||||||
unsigned int delta = ktime_ms_delta(ktime_get(), ctx->sample_start);
|
unsigned int delta = ktime_ms_delta(ktime_get(), ctx->sample_start);
|
||||||
int pulses;
|
int i;
|
||||||
|
|
||||||
if (delta) {
|
if (delta) {
|
||||||
pulses = atomic_read(&ctx->pulses);
|
for (i = 0; i < ctx->tach_count; i++) {
|
||||||
atomic_sub(pulses, &ctx->pulses);
|
struct pwm_fan_tach *tach = &ctx->tachs[i];
|
||||||
ctx->rpm = (unsigned int)(pulses * 1000 * 60) /
|
int pulses;
|
||||||
(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();
|
ctx->sample_start = ktime_get();
|
||||||
}
|
}
|
||||||
@@ -94,18 +96,17 @@ static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
|
|||||||
{
|
{
|
||||||
unsigned long period;
|
unsigned long period;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
struct pwm_state state = { };
|
struct pwm_state *state = &ctx->pwm_state;
|
||||||
|
|
||||||
mutex_lock(&ctx->lock);
|
mutex_lock(&ctx->lock);
|
||||||
if (ctx->pwm_value == pwm)
|
if (ctx->pwm_value == pwm)
|
||||||
goto exit_set_pwm_err;
|
goto exit_set_pwm_err;
|
||||||
|
|
||||||
pwm_init_state(ctx->pwm, &state);
|
period = state->period;
|
||||||
period = ctx->pwm->args.period;
|
state->duty_cycle = DIV_ROUND_UP(pwm * (period - 1), MAX_PWM);
|
||||||
state.duty_cycle = DIV_ROUND_UP(pwm * (period - 1), MAX_PWM);
|
state->enabled = pwm ? true : false;
|
||||||
state.enabled = pwm ? true : false;
|
|
||||||
|
|
||||||
ret = pwm_apply_state(ctx->pwm, &state);
|
ret = pwm_apply_state(ctx->pwm, state);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
ctx->pwm_value = pwm;
|
ctx->pwm_value = pwm;
|
||||||
exit_set_pwm_err:
|
exit_set_pwm_err:
|
||||||
@@ -152,7 +153,7 @@ static int pwm_fan_read(struct device *dev, enum hwmon_sensor_types type,
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
case hwmon_fan:
|
case hwmon_fan:
|
||||||
*val = ctx->rpm;
|
*val = ctx->tachs[channel].rpm;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -287,7 +288,9 @@ static void pwm_fan_regulator_disable(void *data)
|
|||||||
static void pwm_fan_pwm_disable(void *__ctx)
|
static void pwm_fan_pwm_disable(void *__ctx)
|
||||||
{
|
{
|
||||||
struct pwm_fan_ctx *ctx = __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);
|
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 pwm_fan_ctx *ctx;
|
||||||
struct device *hwmon;
|
struct device *hwmon;
|
||||||
int ret;
|
int ret;
|
||||||
struct pwm_state state = { };
|
|
||||||
int tach_count;
|
|
||||||
const struct hwmon_channel_info **channels;
|
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);
|
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
|
||||||
if (!ctx)
|
if (!ctx)
|
||||||
@@ -334,22 +338,20 @@ static int pwm_fan_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
ctx->pwm_value = MAX_PWM;
|
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
|
* __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
|
* long. Check this here to prevent the fan running at a too low
|
||||||
* frequency.
|
* 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");
|
dev_err(dev, "Configured period too big\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set duty cycle to maximum allowed and enable PWM output */
|
/* Set duty cycle to maximum allowed and enable PWM output */
|
||||||
state.duty_cycle = ctx->pwm->args.period - 1;
|
ret = __set_pwm(ctx, MAX_PWM);
|
||||||
state.enabled = true;
|
|
||||||
|
|
||||||
ret = pwm_apply_state(ctx->pwm, &state);
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev, "Failed to configure PWM: %d\n", ret);
|
dev_err(dev, "Failed to configure PWM: %d\n", ret);
|
||||||
return ret;
|
return ret;
|
||||||
@@ -359,27 +361,46 @@ static int pwm_fan_probe(struct platform_device *pdev)
|
|||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
tach_count = platform_irq_count(pdev);
|
ctx->tach_count = platform_irq_count(pdev);
|
||||||
if (tach_count < 0)
|
if (ctx->tach_count < 0)
|
||||||
return dev_err_probe(dev, tach_count,
|
return dev_err_probe(dev, ctx->tach_count,
|
||||||
"Could not get number of fan tachometer inputs\n");
|
"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);
|
sizeof(struct hwmon_channel_info *), GFP_KERNEL);
|
||||||
if (!channels)
|
if (!channels)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
channels[0] = &pwm_fan_channel_pwm;
|
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;
|
u32 ppr = 2;
|
||||||
|
|
||||||
ctx->irq = platform_get_irq(pdev, 0);
|
tach->irq = platform_get_irq(pdev, i);
|
||||||
if (ctx->irq == -EPROBE_DEFER)
|
if (tach->irq == -EPROBE_DEFER)
|
||||||
return ctx->irq;
|
return tach->irq;
|
||||||
if (ctx->irq > 0) {
|
if (tach->irq > 0) {
|
||||||
ret = devm_request_irq(dev, ctx->irq, pulse_handler, 0,
|
ret = devm_request_irq(dev, tach->irq, pulse_handler, 0,
|
||||||
pdev->name, ctx);
|
pdev->name, tach);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev,
|
dev_err(dev,
|
||||||
"Failed to request interrupt: %d\n",
|
"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,
|
of_property_read_u32_index(dev->of_node,
|
||||||
"pulses-per-revolution",
|
"pulses-per-revolution",
|
||||||
&ppr);
|
i,
|
||||||
ctx->pulses_per_revolution = ppr;
|
&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");
|
dev_err(dev, "pulses-per-revolution can't be zero.\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_dbg(dev, "tach: irq=%d, pulses_per_revolution=%d\n",
|
fan_channel_config[i] = HWMON_F_INPUT;
|
||||||
ctx->irq, ctx->pulses_per_revolution);
|
|
||||||
|
|
||||||
|
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();
|
ctx->sample_start = ktime_get();
|
||||||
mod_timer(&ctx->rpm_timer, jiffies + HZ);
|
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;
|
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)
|
static int pwm_fan_disable(struct device *dev)
|
||||||
{
|
{
|
||||||
struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
|
struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
|
||||||
struct pwm_args args;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
pwm_get_args(ctx->pwm, &args);
|
|
||||||
|
|
||||||
if (ctx->pwm_value) {
|
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)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
pwm_disable(ctx->pwm);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx->reg_en) {
|
if (ctx->reg_en) {
|
||||||
@@ -479,8 +505,6 @@ static int pwm_fan_suspend(struct device *dev)
|
|||||||
static int pwm_fan_resume(struct device *dev)
|
static int pwm_fan_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
|
struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
|
||||||
struct pwm_args pargs;
|
|
||||||
unsigned long duty;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (ctx->reg_en) {
|
if (ctx->reg_en) {
|
||||||
@@ -494,12 +518,7 @@ static int pwm_fan_resume(struct device *dev)
|
|||||||
if (ctx->pwm_value == 0)
|
if (ctx->pwm_value == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
pwm_get_args(ctx->pwm, &pargs);
|
return pwm_apply_state(ctx->pwm, &ctx->pwm_state);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@@ -682,7 +682,7 @@ static int __init smsc47m1_handle_resources(unsigned short address,
|
|||||||
/* Request the resources */
|
/* Request the resources */
|
||||||
if (!devm_request_region(dev, start, len, DRVNAME)) {
|
if (!devm_request_region(dev, start, len, DRVNAME)) {
|
||||||
dev_err(dev,
|
dev_err(dev,
|
||||||
"Region 0x%hx-0x%hx already in use!\n",
|
"Region 0x%x-0x%x already in use!\n",
|
||||||
start, start + len);
|
start, start + len);
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
}
|
}
|
||||||
|
601
drivers/hwmon/tps23861.c
Normal file
601
drivers/hwmon/tps23861.c
Normal file
@@ -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 <robert.marko@sartura.hr>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/bitfield.h>
|
||||||
|
#include <linux/debugfs.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/hwmon-sysfs.h>
|
||||||
|
#include <linux/hwmon.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
|
||||||
|
#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 <robert.marko@sartura.hr>");
|
||||||
|
MODULE_DESCRIPTION("TI TPS23861 PoE PSE");
|
@@ -1110,7 +1110,7 @@ clear_caseopen(struct device *dev, struct w83627ehf_data *data, int channel,
|
|||||||
static umode_t w83627ehf_attrs_visible(struct kobject *kobj,
|
static umode_t w83627ehf_attrs_visible(struct kobject *kobj,
|
||||||
struct attribute *a, int n)
|
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 w83627ehf_data *data = dev_get_drvdata(dev);
|
||||||
struct device_attribute *devattr;
|
struct device_attribute *devattr;
|
||||||
struct sensor_device_attribute *sda;
|
struct sensor_device_attribute *sda;
|
||||||
|
@@ -34,6 +34,15 @@
|
|||||||
*/
|
*/
|
||||||
#define PMBUS_WRITE_PROTECTED BIT(1)
|
#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 {
|
struct pmbus_platform_data {
|
||||||
u32 flags; /* Device specific flags */
|
u32 flags; /* Device specific flags */
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user