mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-23 12:43:55 +08:00
hwmon updates for v5.12
- 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 (abx500) Decomission abx500 driver Various other minor fixes and improvements. -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEiHPvMQj9QTOCiqgVyx8mb86fmYEFAmAp+0kACgkQyx8mb86f mYGOCw//QEIrtGmHutRK+dvR64XAVfU6ygh8Jp7xgzlWzOmS8gJaA60Gm/MgsIjF rJC+zgzLenfGugyTR/rr8ZZ/awRyx70I+zWZ3cq6aGn8+ch2zKRn4W0sI9ol2XjP h1LElesRaQyHYSTxWVC9Q/2jyX3nibu+AJwTpDZtoOrE+X1KUCgsAA+cKKgHkhHd XpgUw7HMVtXm4EIX4+r7UDmjX3YIGtWQleXXqOn+4L7SQnO50DWipHAG593L42I/ 37cWbBzCRLDeHqXE5JG52LlR9KnxFG+fWs4BlNy/E0cDRE5SdDpjWILBlwqygkfp RsI8A4oAioCZ3qEoFTS0MmVIlq4fbRS13MogziYSEoHnYA5NDHFtDda7A9V7ahQv C4jN7z+d0AFEDcTFHZ7qhy7K9B1da8MdNhFbKC+8vIAj2b4t2z2l9ozpT+d74BmJ L3GbCtDHAwsXvDBJc+rngxH/ZG8Vksb3nQBRWgM/eGdVZweGwVJ9EvQLyTCCHGCr +D+5QPM3mf/binu6dDQ/o//f6nX7S3ww/aEAI8PLmr3amgJuaRVskGIId8XS0Myw GUWVxc3X47c3THIcdcSKjDOa2/YzLdhzJlQ07XC7NZIJHCgdfs4Y9tow5mBbToVQ Dqwkbned3nxFwQh095FDrUZiwraA6gO6Nxcl1n2KhIJcy5U27hI= =Spv2 -----END PGP SIGNATURE----- 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:
commit
a26a9d8ab4
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
|
||||
compile-time or via the shunt_resistor attribute in sysfs at run-time. Please
|
||||
refer to the Documentation/devicetree/bindings/hwmon/ina2xx.txt for bindings
|
||||
refer to the Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml for bindings
|
||||
if the device tree is used.
|
||||
|
||||
Additionally ina226 supports update_interval attribute as described in
|
||||
|
@ -18,10 +18,8 @@ Hardware Monitoring Kernel Drivers
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
ab8500
|
||||
abituguru
|
||||
abituguru3
|
||||
abx500
|
||||
acpi_power_meter
|
||||
ad7314
|
||||
adc128d818
|
||||
@ -39,6 +37,7 @@ Hardware Monitoring Kernel Drivers
|
||||
adt7462
|
||||
adt7470
|
||||
adt7475
|
||||
aht10
|
||||
amc6821
|
||||
amd_energy
|
||||
asb100
|
||||
@ -178,6 +177,7 @@ Hardware Monitoring Kernel Drivers
|
||||
tmp401
|
||||
tmp421
|
||||
tmp513
|
||||
tps23861
|
||||
tps40422
|
||||
tps53679
|
||||
twl4030-madc-hwmon
|
||||
|
@ -5,6 +5,14 @@ Kernel driver max16601
|
||||
|
||||
Supported chips:
|
||||
|
||||
* Maxim MAX16508
|
||||
|
||||
Prefix: 'max16508'
|
||||
|
||||
Addresses scanned: -
|
||||
|
||||
Datasheet: Not published
|
||||
|
||||
* Maxim MAX16601
|
||||
|
||||
Prefix: 'max16601'
|
||||
@ -19,8 +27,8 @@ Author: Guenter Roeck <linux@roeck-us.net>
|
||||
Description
|
||||
-----------
|
||||
|
||||
This driver supports the MAX16601 VR13.HC Dual-Output Voltage Regulator
|
||||
Chipset.
|
||||
This driver supports the MAX16508 VR13 Dual-Output Voltage Regulator
|
||||
as well as the MAX16601 VR13.HC Dual-Output Voltage Regulator chipsets.
|
||||
|
||||
The driver is a client driver to the core PMBus driver.
|
||||
Please see Documentation/hwmon/pmbus.rst for details on PMBus client drivers.
|
||||
@ -45,115 +53,76 @@ Sysfs entries
|
||||
|
||||
The following attributes are supported.
|
||||
|
||||
======================= =======================================================
|
||||
in1_label "vin1"
|
||||
in1_input VCORE input voltage.
|
||||
in1_alarm Input voltage alarm.
|
||||
=============================== ===============================================
|
||||
in1_label "vin1"
|
||||
in1_input VCORE input voltage.
|
||||
in1_alarm Input voltage alarm.
|
||||
|
||||
in2_label "vout1"
|
||||
in2_input VCORE output voltage.
|
||||
in2_alarm Output voltage alarm.
|
||||
in2_label "vout1"
|
||||
in2_input VCORE output voltage.
|
||||
in2_alarm Output voltage alarm.
|
||||
|
||||
curr1_label "iin1"
|
||||
curr1_input VCORE input current, derived from duty cycle and output
|
||||
current.
|
||||
curr1_max Maximum input current.
|
||||
curr1_max_alarm Current high alarm.
|
||||
curr1_label "iin1"
|
||||
curr1_input VCORE input current, derived from duty cycle
|
||||
and output current.
|
||||
curr1_max Maximum input current.
|
||||
curr1_max_alarm Current high alarm.
|
||||
|
||||
curr2_label "iin1.0"
|
||||
curr2_input VCORE phase 0 input current.
|
||||
curr[P+2]_label "iin1.P"
|
||||
curr[P+2]_input VCORE phase P input current.
|
||||
|
||||
curr3_label "iin1.1"
|
||||
curr3_input VCORE phase 1 input current.
|
||||
curr[N+2]_label "iin2"
|
||||
curr[N+2]_input VCORE input current, derived from sensor
|
||||
element.
|
||||
'N' is the number of enabled/populated phases.
|
||||
|
||||
curr4_label "iin1.2"
|
||||
curr4_input VCORE phase 2 input current.
|
||||
curr[N+3]_label "iin3"
|
||||
curr[N+3]_input VSA input current.
|
||||
|
||||
curr5_label "iin1.3"
|
||||
curr5_input VCORE phase 3 input current.
|
||||
curr[N+4]_label "iout1"
|
||||
curr[N+4]_input VCORE output current.
|
||||
curr[N+4]_crit Critical output current.
|
||||
curr[N+4]_crit_alarm Output current critical alarm.
|
||||
curr[N+4]_max Maximum output current.
|
||||
curr[N+4]_max_alarm Output current high alarm.
|
||||
|
||||
curr6_label "iin1.4"
|
||||
curr6_input VCORE phase 4 input current.
|
||||
curr[N+P+5]_label "iout1.P"
|
||||
curr[N+P+5]_input VCORE phase P output current.
|
||||
|
||||
curr7_label "iin1.5"
|
||||
curr7_input VCORE phase 5 input current.
|
||||
curr[2*N+5]_label "iout3"
|
||||
curr[2*N+5]_input VSA output current.
|
||||
curr[2*N+5]_highest Historical maximum VSA output current.
|
||||
curr[2*N+5]_reset_history Write any value to reset curr21_highest.
|
||||
curr[2*N+5]_crit Critical output current.
|
||||
curr[2*N+5]_crit_alarm Output current critical alarm.
|
||||
curr[2*N+5]_max Maximum output current.
|
||||
curr[2*N+5]_max_alarm Output current high alarm.
|
||||
|
||||
curr8_label "iin1.6"
|
||||
curr8_input VCORE phase 6 input current.
|
||||
power1_label "pin1"
|
||||
power1_input Input power, derived from duty cycle and output
|
||||
current.
|
||||
power1_alarm Input power alarm.
|
||||
|
||||
curr9_label "iin1.7"
|
||||
curr9_input VCORE phase 7 input current.
|
||||
power2_label "pin2"
|
||||
power2_input Input power, derived from input current sensor.
|
||||
|
||||
curr10_label "iin2"
|
||||
curr10_input VCORE input current, derived from sensor element.
|
||||
power3_label "pout"
|
||||
power3_input Output power.
|
||||
|
||||
curr11_label "iin3"
|
||||
curr11_input VSA input current.
|
||||
temp1_input VCORE temperature.
|
||||
temp1_crit Critical high temperature.
|
||||
temp1_crit_alarm Chip temperature critical high alarm.
|
||||
temp1_max Maximum temperature.
|
||||
temp1_max_alarm Chip temperature high alarm.
|
||||
|
||||
curr12_label "iout1"
|
||||
curr12_input VCORE output current.
|
||||
curr12_crit Critical output current.
|
||||
curr12_crit_alarm Output current critical alarm.
|
||||
curr12_max Maximum output current.
|
||||
curr12_max_alarm Output current high alarm.
|
||||
temp2_input TSENSE_0 temperature
|
||||
temp3_input TSENSE_1 temperature
|
||||
temp4_input TSENSE_2 temperature
|
||||
temp5_input TSENSE_3 temperature
|
||||
|
||||
curr13_label "iout1.0"
|
||||
curr13_input VCORE phase 0 output current.
|
||||
|
||||
curr14_label "iout1.1"
|
||||
curr14_input VCORE phase 1 output current.
|
||||
|
||||
curr15_label "iout1.2"
|
||||
curr15_input VCORE phase 2 output current.
|
||||
|
||||
curr16_label "iout1.3"
|
||||
curr16_input VCORE phase 3 output current.
|
||||
|
||||
curr17_label "iout1.4"
|
||||
curr17_input VCORE phase 4 output current.
|
||||
|
||||
curr18_label "iout1.5"
|
||||
curr18_input VCORE phase 5 output current.
|
||||
|
||||
curr19_label "iout1.6"
|
||||
curr19_input VCORE phase 6 output current.
|
||||
|
||||
curr20_label "iout1.7"
|
||||
curr20_input VCORE phase 7 output current.
|
||||
|
||||
curr21_label "iout3"
|
||||
curr21_input VSA output current.
|
||||
curr21_highest Historical maximum VSA output current.
|
||||
curr21_reset_history Write any value to reset curr21_highest.
|
||||
curr21_crit Critical output current.
|
||||
curr21_crit_alarm Output current critical alarm.
|
||||
curr21_max Maximum output current.
|
||||
curr21_max_alarm Output current high alarm.
|
||||
|
||||
power1_label "pin1"
|
||||
power1_input Input power, derived from duty cycle and output current.
|
||||
power1_alarm Input power alarm.
|
||||
|
||||
power2_label "pin2"
|
||||
power2_input Input power, derived from input current sensor.
|
||||
|
||||
power3_label "pout"
|
||||
power3_input Output power.
|
||||
|
||||
temp1_input VCORE temperature.
|
||||
temp1_crit Critical high temperature.
|
||||
temp1_crit_alarm Chip temperature critical high alarm.
|
||||
temp1_max Maximum temperature.
|
||||
temp1_max_alarm Chip temperature high alarm.
|
||||
|
||||
temp2_input TSENSE_0 temperature
|
||||
temp3_input TSENSE_1 temperature
|
||||
temp4_input TSENSE_2 temperature
|
||||
temp5_input TSENSE_3 temperature
|
||||
|
||||
temp6_input VSA temperature.
|
||||
temp6_crit Critical high temperature.
|
||||
temp6_crit_alarm Chip temperature critical high alarm.
|
||||
temp6_max Maximum temperature.
|
||||
temp6_max_alarm Chip temperature high alarm.
|
||||
======================= =======================================================
|
||||
temp6_input VSA temperature.
|
||||
temp6_crit Critical high temperature.
|
||||
temp6_crit_alarm Chip temperature critical high alarm.
|
||||
temp6_max Maximum temperature.
|
||||
temp6_max_alarm Chip temperature high alarm.
|
||||
=============================== ===============================================
|
||||
|
@ -61,5 +61,6 @@ Board Firmware version
|
||||
Intel DH87RL NCT6683D EC firmware version 1.0 build 04/03/13
|
||||
Intel DH87MC NCT6683D EC firmware version 1.0 build 04/03/13
|
||||
Intel DB85FL NCT6683D EC firmware version 1.0 build 04/03/13
|
||||
ASRock X570 NCT6683D EC firmware version 1.0 build 06/28/19
|
||||
MSI B550 NCT6687D EC firmware version 1.0 build 05/07/20
|
||||
=============== ===============================================
|
||||
|
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_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
|
||||
M: Hans Verkuil <hverkuil@xs4all.nl>
|
||||
L: linux-media@vger.kernel.org
|
||||
|
@ -38,19 +38,6 @@ config HWMON_DEBUG_CHIP
|
||||
|
||||
comment "Native drivers"
|
||||
|
||||
config SENSORS_AB8500
|
||||
tristate "AB8500 thermal monitoring"
|
||||
depends on AB8500_GPADC && AB8500_BM && (IIO = y)
|
||||
default n
|
||||
help
|
||||
If you say yes here you get support for the thermal sensor part
|
||||
of the AB8500 chip. The driver includes thermal management for
|
||||
AB8500 die and two GPADC channels. The GPADC channel are preferably
|
||||
used to access sensors outside the AB8500 chip.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called abx500-temp.
|
||||
|
||||
config SENSORS_ABITUGURU
|
||||
tristate "Abit uGuru (rev 1 & 2)"
|
||||
depends on X86 && DMI
|
||||
@ -257,6 +244,16 @@ config SENSORS_ADT7475
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called adt7475.
|
||||
|
||||
config SENSORS_AHT10
|
||||
tristate "Aosong AHT10"
|
||||
depends on I2C
|
||||
help
|
||||
If you say yes here, you get support for the Aosong AHT10
|
||||
temperature and humidity sensors
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called aht10.
|
||||
|
||||
config SENSORS_AS370
|
||||
tristate "Synaptics AS370 SoC hardware monitoring driver"
|
||||
help
|
||||
@ -1136,6 +1133,17 @@ config SENSORS_TC654
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called tc654.
|
||||
|
||||
config SENSORS_TPS23861
|
||||
tristate "Texas Instruments TPS23861 PoE PSE"
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
help
|
||||
If you say yes here you get support for Texas Instruments
|
||||
TPS23861 802.3at PoE PSE chips.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called tps23861.
|
||||
|
||||
config SENSORS_MENF21BMC_HWMON
|
||||
tristate "MEN 14F021P00 BMC Hardware Monitoring"
|
||||
depends on MFD_MENF21BMC
|
||||
|
@ -21,7 +21,6 @@ obj-$(CONFIG_SENSORS_W83795) += w83795.o
|
||||
obj-$(CONFIG_SENSORS_W83781D) += w83781d.o
|
||||
obj-$(CONFIG_SENSORS_W83791D) += w83791d.o
|
||||
|
||||
obj-$(CONFIG_SENSORS_AB8500) += abx500.o ab8500.o
|
||||
obj-$(CONFIG_SENSORS_ABITUGURU) += abituguru.o
|
||||
obj-$(CONFIG_SENSORS_ABITUGURU3)+= abituguru3.o
|
||||
obj-$(CONFIG_SENSORS_AD7314) += ad7314.o
|
||||
@ -45,6 +44,7 @@ obj-$(CONFIG_SENSORS_ADT7411) += adt7411.o
|
||||
obj-$(CONFIG_SENSORS_ADT7462) += adt7462.o
|
||||
obj-$(CONFIG_SENSORS_ADT7470) += adt7470.o
|
||||
obj-$(CONFIG_SENSORS_ADT7475) += adt7475.o
|
||||
obj-$(CONFIG_SENSORS_AHT10) += aht10.o
|
||||
obj-$(CONFIG_SENSORS_AMD_ENERGY) += amd_energy.o
|
||||
obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o
|
||||
obj-$(CONFIG_SENSORS_ARM_SCMI) += scmi-hwmon.o
|
||||
@ -144,6 +144,7 @@ obj-$(CONFIG_SENSORS_MAX31790) += max31790.o
|
||||
obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
|
||||
obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o
|
||||
obj-$(CONFIG_SENSORS_TC654) += tc654.o
|
||||
obj-$(CONFIG_SENSORS_TPS23861) += tps23861.o
|
||||
obj-$(CONFIG_SENSORS_MLXREG_FAN) += mlxreg-fan.o
|
||||
obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o
|
||||
obj-$(CONFIG_SENSORS_MR75203) += mr75203.o
|
||||
|
@ -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 = {
|
||||
X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x17, 0x31, NULL),
|
||||
X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x19, 0x01, NULL),
|
||||
X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x19, 0x30, NULL),
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(x86cpu, cpu_ids);
|
||||
|
@ -565,7 +565,7 @@ static int applesmc_init_index(struct applesmc_registers *s)
|
||||
static int applesmc_init_smcreg_try(void)
|
||||
{
|
||||
struct applesmc_registers *s = &smcreg;
|
||||
bool left_light_sensor = 0, right_light_sensor = 0;
|
||||
bool left_light_sensor = false, right_light_sensor = false;
|
||||
unsigned int count;
|
||||
u8 tmp[1];
|
||||
int ret;
|
||||
|
@ -620,7 +620,7 @@ static ssize_t rpm_show(struct device *dev, struct device_attribute *attr,
|
||||
static umode_t pwm_is_visible(struct kobject *kobj,
|
||||
struct attribute *a, int index)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct aspeed_pwm_tacho_data *priv = dev_get_drvdata(dev);
|
||||
|
||||
if (!priv->pwm_present[index])
|
||||
@ -631,7 +631,7 @@ static umode_t pwm_is_visible(struct kobject *kobj,
|
||||
static umode_t fan_dev_is_visible(struct kobject *kobj,
|
||||
struct attribute *a, int index)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct aspeed_pwm_tacho_data *priv = dev_get_drvdata(dev);
|
||||
|
||||
if (!priv->fan_tach_present[index])
|
||||
|
@ -299,7 +299,7 @@ static ssize_t label_show(struct device *dev,
|
||||
static umode_t da9052_channel_is_visible(struct kobject *kobj,
|
||||
struct attribute *attr, int index)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct da9052_hwmon *hwmon = dev_get_drvdata(dev);
|
||||
struct device_attribute *dattr = container_of(attr,
|
||||
struct device_attribute, attr);
|
||||
|
@ -1159,6 +1159,13 @@ static struct dmi_system_id i8k_blacklist_fan_support_dmi_table[] __initdata = {
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "XPS13 9333"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "Dell XPS 15 L502X",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Dell System XPS L502X"),
|
||||
},
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
|
@ -299,7 +299,7 @@ static DEVICE_ATTR(fan1_target, 0644, fan1_input_show, set_rpm);
|
||||
static umode_t gpio_fan_is_visible(struct kobject *kobj,
|
||||
struct attribute *attr, int index)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct gpio_fan_data *data = dev_get_drvdata(dev);
|
||||
|
||||
if (index == 0 && !data->alarm_gpio)
|
||||
|
@ -448,7 +448,8 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
data->is_zen = true;
|
||||
|
||||
switch (boot_cpu_data.x86_model) {
|
||||
case 0x0 ... 0x1: /* Zen3 */
|
||||
case 0x0 ... 0x1: /* Zen3 SP3/TR */
|
||||
case 0x21: /* Zen3 Ryzen Desktop */
|
||||
k10temp_get_ccd_support(pdev, data, 8);
|
||||
break;
|
||||
}
|
||||
|
@ -321,7 +321,7 @@ static SENSOR_DEVICE_ATTR_RO(gpio2_alarm, alarm, MAX6650_ALRM_GPIO2);
|
||||
static umode_t max6650_attrs_visible(struct kobject *kobj, struct attribute *a,
|
||||
int n)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct max6650_data *data = dev_get_drvdata(dev);
|
||||
struct device_attribute *devattr;
|
||||
|
||||
|
@ -169,6 +169,7 @@ superio_exit(int ioreg)
|
||||
#define NCT6683_CUSTOMER_ID_INTEL 0x805
|
||||
#define NCT6683_CUSTOMER_ID_MITAC 0xa0e
|
||||
#define NCT6683_CUSTOMER_ID_MSI 0x201
|
||||
#define NCT6683_CUSTOMER_ID_ASROCK 0xe2c
|
||||
|
||||
#define NCT6683_REG_BUILD_YEAR 0x604
|
||||
#define NCT6683_REG_BUILD_MONTH 0x605
|
||||
@ -1225,6 +1226,8 @@ static int nct6683_probe(struct platform_device *pdev)
|
||||
break;
|
||||
case NCT6683_CUSTOMER_ID_MSI:
|
||||
break;
|
||||
case NCT6683_CUSTOMER_ID_ASROCK:
|
||||
break;
|
||||
default:
|
||||
if (!force)
|
||||
return -ENODEV;
|
||||
|
@ -1700,8 +1700,8 @@ static int __init pc87360_device_add(unsigned short address)
|
||||
continue;
|
||||
res[res_count].start = extra_isa[i];
|
||||
res[res_count].end = extra_isa[i] + PC87360_EXTENT - 1;
|
||||
res[res_count].name = "pc87360",
|
||||
res[res_count].flags = IORESOURCE_IO,
|
||||
res[res_count].name = "pc87360";
|
||||
res[res_count].flags = IORESOURCE_IO;
|
||||
|
||||
err = acpi_check_resource_conflict(&res[res_count]);
|
||||
if (err)
|
||||
|
@ -158,10 +158,10 @@ config SENSORS_MAX16064
|
||||
be called max16064.
|
||||
|
||||
config SENSORS_MAX16601
|
||||
tristate "Maxim MAX16601"
|
||||
tristate "Maxim MAX16508, MAX16601"
|
||||
help
|
||||
If you say yes here you get hardware monitoring support for Maxim
|
||||
MAX16601.
|
||||
MAX16508 and MAX16601.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called max16601.
|
||||
|
@ -472,7 +472,7 @@ static struct pmbus_driver_info ibm_cffps_info[] = {
|
||||
};
|
||||
|
||||
static struct pmbus_platform_data ibm_cffps_pdata = {
|
||||
.flags = PMBUS_SKIP_STATUS_CHECK,
|
||||
.flags = PMBUS_SKIP_STATUS_CHECK | PMBUS_NO_CAPABILITY,
|
||||
};
|
||||
|
||||
static int ibm_cffps_probe(struct i2c_client *client)
|
||||
|
@ -371,21 +371,18 @@ static int lm25066_write_word_data(struct i2c_client *client, int page, int reg,
|
||||
case PMBUS_VIN_OV_WARN_LIMIT:
|
||||
word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit);
|
||||
ret = pmbus_write_word_data(client, 0, reg, word);
|
||||
pmbus_clear_cache(client);
|
||||
break;
|
||||
case PMBUS_IIN_OC_WARN_LIMIT:
|
||||
word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit);
|
||||
ret = pmbus_write_word_data(client, 0,
|
||||
LM25066_MFR_IIN_OC_WARN_LIMIT,
|
||||
word);
|
||||
pmbus_clear_cache(client);
|
||||
break;
|
||||
case PMBUS_PIN_OP_WARN_LIMIT:
|
||||
word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit);
|
||||
ret = pmbus_write_word_data(client, 0,
|
||||
LM25066_MFR_PIN_OP_WARN_LIMIT,
|
||||
word);
|
||||
pmbus_clear_cache(client);
|
||||
break;
|
||||
case PMBUS_VIRT_VMON_UV_WARN_LIMIT:
|
||||
/* Adjust from VIN coefficients (for LM25056) */
|
||||
@ -393,7 +390,6 @@ static int lm25066_write_word_data(struct i2c_client *client, int page, int reg,
|
||||
word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit);
|
||||
ret = pmbus_write_word_data(client, 0,
|
||||
LM25056_VAUX_UV_WARN_LIMIT, word);
|
||||
pmbus_clear_cache(client);
|
||||
break;
|
||||
case PMBUS_VIRT_VMON_OV_WARN_LIMIT:
|
||||
/* Adjust from VIN coefficients (for LM25056) */
|
||||
@ -401,7 +397,6 @@ static int lm25066_write_word_data(struct i2c_client *client, int page, int reg,
|
||||
word = ((s16)word < 0) ? 0 : clamp_val(word, 0, data->rlimit);
|
||||
ret = pmbus_write_word_data(client, 0,
|
||||
LM25056_VAUX_OV_WARN_LIMIT, word);
|
||||
pmbus_clear_cache(client);
|
||||
break;
|
||||
case PMBUS_VIRT_RESET_PIN_HISTORY:
|
||||
ret = pmbus_write_byte(client, 0, LM25066_CLEAR_PIN_PEAK);
|
||||
|
@ -1,11 +1,11 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Hardware monitoring driver for Maxim MAX16601
|
||||
* Hardware monitoring driver for Maxim MAX16508 and MAX16601.
|
||||
*
|
||||
* Implementation notes:
|
||||
*
|
||||
* Ths chip supports two rails, VCORE and VSA. Telemetry information for the
|
||||
* two rails is reported in two subsequent I2C addresses. The driver
|
||||
* This chip series supports two rails, VCORE and VSA. Telemetry information
|
||||
* for the two rails is reported in two subsequent I2C addresses. The driver
|
||||
* instantiates a dummy I2C client at the second I2C address to report
|
||||
* information for the VSA rail in a single instance of the driver.
|
||||
* Telemetry for the VSA rail is reported to the PMBus core in PMBus page 2.
|
||||
@ -31,6 +31,9 @@
|
||||
|
||||
#include "pmbus.h"
|
||||
|
||||
enum chips { max16508, max16601 };
|
||||
|
||||
#define REG_DEFAULT_NUM_POP 0xc4
|
||||
#define REG_SETPT_DVID 0xd1
|
||||
#define DAC_10MV_MODE BIT(4)
|
||||
#define REG_IOUT_AVG_PK 0xee
|
||||
@ -40,7 +43,10 @@
|
||||
#define CORE_RAIL_INDICATOR BIT(7)
|
||||
#define REG_PHASE_REPORTING 0xf4
|
||||
|
||||
#define MAX16601_NUM_PHASES 8
|
||||
|
||||
struct max16601_data {
|
||||
enum chips id;
|
||||
struct pmbus_driver_info info;
|
||||
struct i2c_client *vsa;
|
||||
int iout_avg_pkg;
|
||||
@ -185,6 +191,7 @@ static int max16601_write_word(struct i2c_client *client, int page, int reg,
|
||||
static int max16601_identify(struct i2c_client *client,
|
||||
struct pmbus_driver_info *info)
|
||||
{
|
||||
struct max16601_data *data = to_max16601_data(info);
|
||||
int reg;
|
||||
|
||||
reg = i2c_smbus_read_byte_data(client, REG_SETPT_DVID);
|
||||
@ -195,6 +202,21 @@ static int max16601_identify(struct i2c_client *client,
|
||||
else
|
||||
info->vrm_version[0] = vr12;
|
||||
|
||||
if (data->id != max16601)
|
||||
return 0;
|
||||
|
||||
reg = i2c_smbus_read_byte_data(client, REG_DEFAULT_NUM_POP);
|
||||
if (reg < 0)
|
||||
return reg;
|
||||
|
||||
/*
|
||||
* If REG_DEFAULT_NUM_POP returns 0, we don't know how many phases
|
||||
* are populated. Stick with the default in that case.
|
||||
*/
|
||||
reg &= 0x0f;
|
||||
if (reg && reg <= MAX16601_NUM_PHASES)
|
||||
info->phases[0] = reg;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -216,7 +238,7 @@ static struct pmbus_driver_info max16601_info = {
|
||||
.func[2] = PMBUS_HAVE_IIN | PMBUS_HAVE_STATUS_INPUT |
|
||||
PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
|
||||
PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | PMBUS_PAGE_VIRTUAL,
|
||||
.phases[0] = 8,
|
||||
.phases[0] = MAX16601_NUM_PHASES,
|
||||
.pfunc[0] = PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP,
|
||||
.pfunc[1] = PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT,
|
||||
.pfunc[2] = PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP,
|
||||
@ -239,28 +261,61 @@ static void max16601_remove(void *_data)
|
||||
i2c_unregister_device(data->vsa);
|
||||
}
|
||||
|
||||
static int max16601_probe(struct i2c_client *client)
|
||||
static const struct i2c_device_id max16601_id[] = {
|
||||
{"max16508", max16508},
|
||||
{"max16601", max16601},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, max16601_id);
|
||||
|
||||
static int max16601_get_id(struct i2c_client *client)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
u8 buf[I2C_SMBUS_BLOCK_MAX + 1];
|
||||
struct max16601_data *data;
|
||||
enum chips id;
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_block_data(client, PMBUS_IC_DEVICE_ID, buf);
|
||||
if (ret < 0 || ret < 11)
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* PMBUS_IC_DEVICE_ID is expected to return "MAX16601y.xx"
|
||||
* or "MAX16500y.xx".
|
||||
*/
|
||||
if (!strncmp(buf, "MAX16500", 8)) {
|
||||
id = max16508;
|
||||
} else if (!strncmp(buf, "MAX16601", 8)) {
|
||||
id = max16601;
|
||||
} else {
|
||||
buf[ret] = '\0';
|
||||
dev_err(dev, "Unsupported chip '%s'\n", buf);
|
||||
return -ENODEV;
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
static int max16601_probe(struct i2c_client *client)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
const struct i2c_device_id *id;
|
||||
struct max16601_data *data;
|
||||
int ret, chip_id;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_READ_BYTE_DATA |
|
||||
I2C_FUNC_SMBUS_READ_BLOCK_DATA))
|
||||
return -ENODEV;
|
||||
|
||||
ret = i2c_smbus_read_block_data(client, PMBUS_IC_DEVICE_ID, buf);
|
||||
if (ret < 0)
|
||||
return -ENODEV;
|
||||
chip_id = max16601_get_id(client);
|
||||
if (chip_id < 0)
|
||||
return chip_id;
|
||||
|
||||
/* PMBUS_IC_DEVICE_ID is expected to return "MAX16601y.xx" */
|
||||
if (ret < 11 || strncmp(buf, "MAX16601", 8)) {
|
||||
buf[ret] = '\0';
|
||||
dev_err(dev, "Unsupported chip '%s'\n", buf);
|
||||
return -ENODEV;
|
||||
}
|
||||
id = i2c_match_id(max16601_id, client);
|
||||
if (chip_id != id->driver_data)
|
||||
dev_warn(&client->dev,
|
||||
"Device mismatch: Configured %s (%d), detected %d\n",
|
||||
id->name, (int) id->driver_data, chip_id);
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, REG_PHASE_ID);
|
||||
if (ret < 0)
|
||||
@ -275,6 +330,7 @@ static int max16601_probe(struct i2c_client *client)
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->id = chip_id;
|
||||
data->iout_avg_pkg = 0xfc00;
|
||||
data->vsa = i2c_new_dummy_device(client->adapter, client->addr + 1);
|
||||
if (IS_ERR(data->vsa)) {
|
||||
@ -290,13 +346,6 @@ static int max16601_probe(struct i2c_client *client)
|
||||
return pmbus_do_probe(client, &data->info);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id max16601_id[] = {
|
||||
{"max16601", 0},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, max16601_id);
|
||||
|
||||
static struct i2c_driver max16601_driver = {
|
||||
.driver = {
|
||||
.name = "max16601",
|
||||
|
@ -17,6 +17,7 @@ enum max31785_regs {
|
||||
|
||||
#define MAX31785 0x3030
|
||||
#define MAX31785A 0x3040
|
||||
#define MAX31785B 0x3061
|
||||
|
||||
#define MFR_FAN_CONFIG_DUAL_TACH BIT(12)
|
||||
|
||||
@ -329,7 +330,7 @@ static int max31785_probe(struct i2c_client *client)
|
||||
struct device *dev = &client->dev;
|
||||
struct pmbus_driver_info *info;
|
||||
bool dual_tach = false;
|
||||
s64 ret;
|
||||
int ret;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_BYTE_DATA |
|
||||
@ -350,12 +351,14 @@ static int max31785_probe(struct i2c_client *client)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ret == MAX31785A) {
|
||||
if (ret == MAX31785A || ret == MAX31785B) {
|
||||
dual_tach = true;
|
||||
} else if (ret == MAX31785) {
|
||||
if (!strcmp("max31785a", client->name))
|
||||
dev_warn(dev, "Expected max3175a, found max31785: cannot provide secondary tachometer readings\n");
|
||||
if (!strcmp("max31785a", client->name) ||
|
||||
!strcmp("max31785b", client->name))
|
||||
dev_warn(dev, "Expected max31785a/b, found max31785: cannot provide secondary tachometer readings\n");
|
||||
} else {
|
||||
dev_err(dev, "Unrecognized MAX31785 revision: %x\n", ret);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
@ -371,6 +374,7 @@ static int max31785_probe(struct i2c_client *client)
|
||||
static const struct i2c_device_id max31785_id[] = {
|
||||
{ "max31785", 0 },
|
||||
{ "max31785a", 0 },
|
||||
{ "max31785b", 0 },
|
||||
{ },
|
||||
};
|
||||
|
||||
@ -379,6 +383,7 @@ MODULE_DEVICE_TABLE(i2c, max31785_id);
|
||||
static const struct of_device_id max31785_of_match[] = {
|
||||
{ .compatible = "maxim,max31785" },
|
||||
{ .compatible = "maxim,max31785a" },
|
||||
{ .compatible = "maxim,max31785b" },
|
||||
{ },
|
||||
};
|
||||
|
||||
|
@ -974,7 +974,7 @@ static ssize_t pmbus_set_sensor(struct device *dev,
|
||||
if (ret < 0)
|
||||
rv = ret;
|
||||
else
|
||||
sensor->data = regval;
|
||||
sensor->data = -ENODATA;
|
||||
mutex_unlock(&data->update_lock);
|
||||
return rv;
|
||||
}
|
||||
@ -1262,7 +1262,7 @@ static int pmbus_add_sensor_attrs_one(struct i2c_client *client,
|
||||
* which global bit is set) for this page is accessible.
|
||||
*/
|
||||
if (!ret && attr->gbit &&
|
||||
(!upper || (upper && data->has_status_word)) &&
|
||||
(!upper || data->has_status_word) &&
|
||||
pmbus_check_status_register(client, page)) {
|
||||
ret = pmbus_add_boolean(data, name, "alarm", index,
|
||||
NULL, NULL,
|
||||
@ -2204,9 +2204,11 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data,
|
||||
}
|
||||
|
||||
/* Enable PEC if the controller supports it */
|
||||
ret = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY);
|
||||
if (ret >= 0 && (ret & PB_CAPABILITY_ERROR_CHECK))
|
||||
client->flags |= I2C_CLIENT_PEC;
|
||||
if (!(data->flags & PMBUS_NO_CAPABILITY)) {
|
||||
ret = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY);
|
||||
if (ret >= 0 && (ret & PB_CAPABILITY_ERROR_CHECK))
|
||||
client->flags |= I2C_CLIENT_PEC;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the chip is write protected. If it is, we can not clear
|
||||
|
@ -21,15 +21,21 @@
|
||||
|
||||
#define MAX_PWM 255
|
||||
|
||||
struct pwm_fan_ctx {
|
||||
struct mutex lock;
|
||||
struct pwm_device *pwm;
|
||||
struct regulator *reg_en;
|
||||
|
||||
struct pwm_fan_tach {
|
||||
int irq;
|
||||
atomic_t pulses;
|
||||
unsigned int rpm;
|
||||
u8 pulses_per_revolution;
|
||||
};
|
||||
|
||||
struct pwm_fan_ctx {
|
||||
struct mutex lock;
|
||||
struct pwm_device *pwm;
|
||||
struct pwm_state pwm_state;
|
||||
struct regulator *reg_en;
|
||||
|
||||
int tach_count;
|
||||
struct pwm_fan_tach *tachs;
|
||||
ktime_t sample_start;
|
||||
struct timer_list rpm_timer;
|
||||
|
||||
@ -40,6 +46,7 @@ struct pwm_fan_ctx {
|
||||
struct thermal_cooling_device *cdev;
|
||||
|
||||
struct hwmon_chip_info info;
|
||||
struct hwmon_channel_info fan_channel;
|
||||
};
|
||||
|
||||
static const u32 pwm_fan_channel_config_pwm[] = {
|
||||
@ -52,22 +59,12 @@ static const struct hwmon_channel_info pwm_fan_channel_pwm = {
|
||||
.config = pwm_fan_channel_config_pwm,
|
||||
};
|
||||
|
||||
static const u32 pwm_fan_channel_config_fan[] = {
|
||||
HWMON_F_INPUT,
|
||||
0
|
||||
};
|
||||
|
||||
static const struct hwmon_channel_info pwm_fan_channel_fan = {
|
||||
.type = hwmon_fan,
|
||||
.config = pwm_fan_channel_config_fan,
|
||||
};
|
||||
|
||||
/* This handler assumes self resetting edge triggered interrupt. */
|
||||
static irqreturn_t pulse_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct pwm_fan_ctx *ctx = dev_id;
|
||||
struct pwm_fan_tach *tach = dev_id;
|
||||
|
||||
atomic_inc(&ctx->pulses);
|
||||
atomic_inc(&tach->pulses);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
@ -76,13 +73,18 @@ static void sample_timer(struct timer_list *t)
|
||||
{
|
||||
struct pwm_fan_ctx *ctx = from_timer(ctx, t, rpm_timer);
|
||||
unsigned int delta = ktime_ms_delta(ktime_get(), ctx->sample_start);
|
||||
int pulses;
|
||||
int i;
|
||||
|
||||
if (delta) {
|
||||
pulses = atomic_read(&ctx->pulses);
|
||||
atomic_sub(pulses, &ctx->pulses);
|
||||
ctx->rpm = (unsigned int)(pulses * 1000 * 60) /
|
||||
(ctx->pulses_per_revolution * delta);
|
||||
for (i = 0; i < ctx->tach_count; i++) {
|
||||
struct pwm_fan_tach *tach = &ctx->tachs[i];
|
||||
int pulses;
|
||||
|
||||
pulses = atomic_read(&tach->pulses);
|
||||
atomic_sub(pulses, &tach->pulses);
|
||||
tach->rpm = (unsigned int)(pulses * 1000 * 60) /
|
||||
(tach->pulses_per_revolution * delta);
|
||||
}
|
||||
|
||||
ctx->sample_start = ktime_get();
|
||||
}
|
||||
@ -94,18 +96,17 @@ static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
|
||||
{
|
||||
unsigned long period;
|
||||
int ret = 0;
|
||||
struct pwm_state state = { };
|
||||
struct pwm_state *state = &ctx->pwm_state;
|
||||
|
||||
mutex_lock(&ctx->lock);
|
||||
if (ctx->pwm_value == pwm)
|
||||
goto exit_set_pwm_err;
|
||||
|
||||
pwm_init_state(ctx->pwm, &state);
|
||||
period = ctx->pwm->args.period;
|
||||
state.duty_cycle = DIV_ROUND_UP(pwm * (period - 1), MAX_PWM);
|
||||
state.enabled = pwm ? true : false;
|
||||
period = state->period;
|
||||
state->duty_cycle = DIV_ROUND_UP(pwm * (period - 1), MAX_PWM);
|
||||
state->enabled = pwm ? true : false;
|
||||
|
||||
ret = pwm_apply_state(ctx->pwm, &state);
|
||||
ret = pwm_apply_state(ctx->pwm, state);
|
||||
if (!ret)
|
||||
ctx->pwm_value = pwm;
|
||||
exit_set_pwm_err:
|
||||
@ -152,7 +153,7 @@ static int pwm_fan_read(struct device *dev, enum hwmon_sensor_types type,
|
||||
return 0;
|
||||
|
||||
case hwmon_fan:
|
||||
*val = ctx->rpm;
|
||||
*val = ctx->tachs[channel].rpm;
|
||||
return 0;
|
||||
|
||||
default:
|
||||
@ -287,7 +288,9 @@ static void pwm_fan_regulator_disable(void *data)
|
||||
static void pwm_fan_pwm_disable(void *__ctx)
|
||||
{
|
||||
struct pwm_fan_ctx *ctx = __ctx;
|
||||
pwm_disable(ctx->pwm);
|
||||
|
||||
ctx->pwm_state.enabled = false;
|
||||
pwm_apply_state(ctx->pwm, &ctx->pwm_state);
|
||||
del_timer_sync(&ctx->rpm_timer);
|
||||
}
|
||||
|
||||
@ -298,9 +301,10 @@ static int pwm_fan_probe(struct platform_device *pdev)
|
||||
struct pwm_fan_ctx *ctx;
|
||||
struct device *hwmon;
|
||||
int ret;
|
||||
struct pwm_state state = { };
|
||||
int tach_count;
|
||||
const struct hwmon_channel_info **channels;
|
||||
u32 *fan_channel_config;
|
||||
int channel_count = 1; /* We always have a PWM channel. */
|
||||
int i;
|
||||
|
||||
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
@ -334,22 +338,20 @@ static int pwm_fan_probe(struct platform_device *pdev)
|
||||
|
||||
ctx->pwm_value = MAX_PWM;
|
||||
|
||||
pwm_init_state(ctx->pwm, &state);
|
||||
pwm_init_state(ctx->pwm, &ctx->pwm_state);
|
||||
|
||||
/*
|
||||
* __set_pwm assumes that MAX_PWM * (period - 1) fits into an unsigned
|
||||
* long. Check this here to prevent the fan running at a too low
|
||||
* frequency.
|
||||
*/
|
||||
if (state.period > ULONG_MAX / MAX_PWM + 1) {
|
||||
if (ctx->pwm_state.period > ULONG_MAX / MAX_PWM + 1) {
|
||||
dev_err(dev, "Configured period too big\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Set duty cycle to maximum allowed and enable PWM output */
|
||||
state.duty_cycle = ctx->pwm->args.period - 1;
|
||||
state.enabled = true;
|
||||
|
||||
ret = pwm_apply_state(ctx->pwm, &state);
|
||||
ret = __set_pwm(ctx, MAX_PWM);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to configure PWM: %d\n", ret);
|
||||
return ret;
|
||||
@ -359,27 +361,46 @@ static int pwm_fan_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
tach_count = platform_irq_count(pdev);
|
||||
if (tach_count < 0)
|
||||
return dev_err_probe(dev, tach_count,
|
||||
ctx->tach_count = platform_irq_count(pdev);
|
||||
if (ctx->tach_count < 0)
|
||||
return dev_err_probe(dev, ctx->tach_count,
|
||||
"Could not get number of fan tachometer inputs\n");
|
||||
dev_dbg(dev, "%d fan tachometer inputs\n", ctx->tach_count);
|
||||
|
||||
channels = devm_kcalloc(dev, tach_count + 2,
|
||||
if (ctx->tach_count) {
|
||||
channel_count++; /* We also have a FAN channel. */
|
||||
|
||||
ctx->tachs = devm_kcalloc(dev, ctx->tach_count,
|
||||
sizeof(struct pwm_fan_tach),
|
||||
GFP_KERNEL);
|
||||
if (!ctx->tachs)
|
||||
return -ENOMEM;
|
||||
|
||||
ctx->fan_channel.type = hwmon_fan;
|
||||
fan_channel_config = devm_kcalloc(dev, ctx->tach_count + 1,
|
||||
sizeof(u32), GFP_KERNEL);
|
||||
if (!fan_channel_config)
|
||||
return -ENOMEM;
|
||||
ctx->fan_channel.config = fan_channel_config;
|
||||
}
|
||||
|
||||
channels = devm_kcalloc(dev, channel_count + 1,
|
||||
sizeof(struct hwmon_channel_info *), GFP_KERNEL);
|
||||
if (!channels)
|
||||
return -ENOMEM;
|
||||
|
||||
channels[0] = &pwm_fan_channel_pwm;
|
||||
|
||||
if (tach_count > 0) {
|
||||
for (i = 0; i < ctx->tach_count; i++) {
|
||||
struct pwm_fan_tach *tach = &ctx->tachs[i];
|
||||
u32 ppr = 2;
|
||||
|
||||
ctx->irq = platform_get_irq(pdev, 0);
|
||||
if (ctx->irq == -EPROBE_DEFER)
|
||||
return ctx->irq;
|
||||
if (ctx->irq > 0) {
|
||||
ret = devm_request_irq(dev, ctx->irq, pulse_handler, 0,
|
||||
pdev->name, ctx);
|
||||
tach->irq = platform_get_irq(pdev, i);
|
||||
if (tach->irq == -EPROBE_DEFER)
|
||||
return tach->irq;
|
||||
if (tach->irq > 0) {
|
||||
ret = devm_request_irq(dev, tach->irq, pulse_handler, 0,
|
||||
pdev->name, tach);
|
||||
if (ret) {
|
||||
dev_err(dev,
|
||||
"Failed to request interrupt: %d\n",
|
||||
@ -388,22 +409,27 @@ static int pwm_fan_probe(struct platform_device *pdev)
|
||||
}
|
||||
}
|
||||
|
||||
of_property_read_u32(dev->of_node,
|
||||
"pulses-per-revolution",
|
||||
&ppr);
|
||||
ctx->pulses_per_revolution = ppr;
|
||||
if (!ctx->pulses_per_revolution) {
|
||||
of_property_read_u32_index(dev->of_node,
|
||||
"pulses-per-revolution",
|
||||
i,
|
||||
&ppr);
|
||||
tach->pulses_per_revolution = ppr;
|
||||
if (!tach->pulses_per_revolution) {
|
||||
dev_err(dev, "pulses-per-revolution can't be zero.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "tach: irq=%d, pulses_per_revolution=%d\n",
|
||||
ctx->irq, ctx->pulses_per_revolution);
|
||||
fan_channel_config[i] = HWMON_F_INPUT;
|
||||
|
||||
dev_dbg(dev, "tach%d: irq=%d, pulses_per_revolution=%d\n",
|
||||
i, tach->irq, tach->pulses_per_revolution);
|
||||
}
|
||||
|
||||
if (ctx->tach_count > 0) {
|
||||
ctx->sample_start = ktime_get();
|
||||
mod_timer(&ctx->rpm_timer, jiffies + HZ);
|
||||
|
||||
channels[1] = &pwm_fan_channel_fan;
|
||||
channels[1] = &ctx->fan_channel;
|
||||
}
|
||||
|
||||
ctx->info.ops = &pwm_fan_hwmon_ops;
|
||||
@ -441,17 +467,17 @@ static int pwm_fan_probe(struct platform_device *pdev)
|
||||
static int pwm_fan_disable(struct device *dev)
|
||||
{
|
||||
struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
|
||||
struct pwm_args args;
|
||||
int ret;
|
||||
|
||||
pwm_get_args(ctx->pwm, &args);
|
||||
|
||||
if (ctx->pwm_value) {
|
||||
ret = pwm_config(ctx->pwm, 0, args.period);
|
||||
/* keep ctx->pwm_state unmodified for pwm_fan_resume() */
|
||||
struct pwm_state state = ctx->pwm_state;
|
||||
|
||||
state.duty_cycle = 0;
|
||||
state.enabled = false;
|
||||
ret = pwm_apply_state(ctx->pwm, &state);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
pwm_disable(ctx->pwm);
|
||||
}
|
||||
|
||||
if (ctx->reg_en) {
|
||||
@ -479,8 +505,6 @@ static int pwm_fan_suspend(struct device *dev)
|
||||
static int pwm_fan_resume(struct device *dev)
|
||||
{
|
||||
struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
|
||||
struct pwm_args pargs;
|
||||
unsigned long duty;
|
||||
int ret;
|
||||
|
||||
if (ctx->reg_en) {
|
||||
@ -494,12 +518,7 @@ static int pwm_fan_resume(struct device *dev)
|
||||
if (ctx->pwm_value == 0)
|
||||
return 0;
|
||||
|
||||
pwm_get_args(ctx->pwm, &pargs);
|
||||
duty = DIV_ROUND_UP_ULL(ctx->pwm_value * (pargs.period - 1), MAX_PWM);
|
||||
ret = pwm_config(ctx->pwm, duty, pargs.period);
|
||||
if (ret)
|
||||
return ret;
|
||||
return pwm_enable(ctx->pwm);
|
||||
return pwm_apply_state(ctx->pwm, &ctx->pwm_state);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -682,7 +682,7 @@ static int __init smsc47m1_handle_resources(unsigned short address,
|
||||
/* Request the resources */
|
||||
if (!devm_request_region(dev, start, len, DRVNAME)) {
|
||||
dev_err(dev,
|
||||
"Region 0x%hx-0x%hx already in use!\n",
|
||||
"Region 0x%x-0x%x already in use!\n",
|
||||
start, start + len);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
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,
|
||||
struct attribute *a, int n)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct w83627ehf_data *data = dev_get_drvdata(dev);
|
||||
struct device_attribute *devattr;
|
||||
struct sensor_device_attribute *sda;
|
||||
|
@ -34,6 +34,15 @@
|
||||
*/
|
||||
#define PMBUS_WRITE_PROTECTED BIT(1)
|
||||
|
||||
/*
|
||||
* PMBUS_NO_CAPABILITY
|
||||
*
|
||||
* Some PMBus chips don't respond with valid data when reading the CAPABILITY
|
||||
* register. For such chips, this flag should be set so that the PMBus core
|
||||
* driver doesn't use CAPABILITY to determine it's behavior.
|
||||
*/
|
||||
#define PMBUS_NO_CAPABILITY BIT(2)
|
||||
|
||||
struct pmbus_platform_data {
|
||||
u32 flags; /* Device specific flags */
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user