mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
hwmon updates for v7.4
* New drivers
- Axiado AX3000 and AX3005 PWM fan controller
- AX3000/AX3005 TSADC
- HONOR FMI-XX
- Minisforum UM780 XTX EC monitoring and fan control
- pmbus: MAX20826, MAX20855B, MAX20908, MAX20912, MAX20826, and MAX20916
- pmbus: Infineon TDA38740/TDA38725
* Added support to existing drivers
- asus-ec-sensors: ROG STRIX X670E-A GAMING WIFI and
ROG STRIX Z490-A GAMING
- asus_rog_ryujin: ROG Ryujin III 360
- dell-smm: Dell 16 DC16250, Dell OptiPlex 7090, Dell Precision 3650
Tower, Add Latitude 5420
- it87: IT8613E
- k10temp: 1ah/80h devices. Drop support for Hygon devices (support to be
re-added with separate patches later)
- lm63: Sensylink CTF2301
- nct6683: MSI MAG B550M MORTAR MAX WIFI, MSI MAG B650 TOMAHAWK WIFI,
MSI MAG B650M MORTAR WIFI (MS-7D76), and MSI MAG B850M MORTAR WIFI
- nct6775: NCT6122D and NCT6126D
- pmbus/isl68137: Renesas RAA229639 and RAA229640
- pmbus/max34440: ADPM12300
- pmbus/tps25990: TPS1689
- pmbus/tps53679: TPS53622, TPS53659 and TPS536C7
- pmbus/xdpe1a2g7b: xdpe1a2g7c
- sht4x: Sensirion STS4x
- socfpga: Agilex 5
- yogafan: Lenovo Yoga Pro 9 16IMH9, Yoga 14cACN 2021, IdeaPad 3 15ALC6 Ub,
Yoga 740-15IML
* Core updates
- Support for currX_emergency and inX_[l]emergency attributes
* Notable bug fixes and changes
- Add missing locks to corsair-cpro, ftsteutates, nct7904, and npcm750-pwm-fan
drivers
- Fix jiffies wraparound in tmp102 and tmp108 drivers
- Documentation updates (document missing chip support, typo fixes,
clarifications)
- pmbus/ltc4286: Add writable shunt_resistor sysfs attribute
- coretemp: Document sysfs numbering and label meaning, fix handling of
isolated SMT siblings, read TjMax only when refreshing the temperature,
refresh the temperature on the first read, and return -ENODATA for
invalid temperature readings
- spd5118: Avoid probing when 16-bit addressing is enabled, and select
page 0 unconditionally during probe
- arctic_fan_controller: Default PWM cache to MCU 40%, Dual-license
GPL-2.0-or-later OR BSD-2-Clause, and update maintainer address
* Various other minor bug fixes and improvements
-----BEGIN PGP SIGNATURE-----
iQIzBAABCAAdFiEEiHPvMQj9QTOCiqgVyx8mb86fmYEFAmrYzwAACgkQyx8mb86f
mYFHsBAAgX61R4qrYoUFDnJQRKoE4629TZX6cd3mVaIXRA68HKqRVrj8Gh3gKlB9
S9wh56eWA9k4rkIXdsBl+tmpJJPjSnxMY0O+BGnX4e5f+hvjhYxXcq1TBomUkMEe
ojLefp985Fj49QBeQoeJZF7Ps041q4KUGzef5N8hQ9VzNbo8OzlSCK4qyM0PHh4t
gq9xVwa+JYfinHyDF42ZWkA1dcASaeTI1OilDzGEwX1zkHmKgSJ7o7KOJB6X6lXx
Bluf9afB5KJTv7zLc5g1IhPyIKLKSEKubX3nLX8QPtk1UgfWq/aOmDZbrHL2Mv3M
NYHgWVue5RtyxxCQbsHQTtX3nJI7VIdWZm6HjNyKO7dJ2+HsAn7b3yqidO7J3Yep
yQqrKd6S3HIpWlLlu7K9ecaT/ZWfyR2WLF1ps0k7xtHYh1qHfJtu4wdSSNZ757+v
QJNWnvJYN5l9vWvnTLibgOlSt6tppiLU57L+vGBmwHdnbq9kNhhklwKHEdrmHptD
RUWsf495UBRiD8tMveNOEmE03v6QOIUJY9Eovvi9wvl7fzOypOW/egZxDL12PKF4
EpeWANjGWrWZAP3ipB4nY1g8oNIgn0yjNj2LEJBdebN+6BEaI75Ck2KimJMtitTR
h1DKqnTK6fgYvKRnhzo9LhANEvplDXGoof0k4UqmNeuA8gZo1UI=
=NNwk
-----END PGP SIGNATURE-----
Merge tag 'hwmon-for-v7.4' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging
Pull hwmon updates from Guenter Roeck:
"New drivers:
- Axiado AX3000 and AX3005 PWM fan controller
- AX3000/AX3005 TSADC
- HONOR FMI-XX
- Minisforum UM780 XTX EC monitoring and fan control
- pmbus: MAX20826, MAX20855B, MAX20908, MAX20912, MAX20826, and
MAX20916
- pmbus: Infineon TDA38740/TDA38725
Added support to existing drivers:
- asus-ec-sensors: ROG STRIX X670E-A GAMING WIFI and ROG STRIX Z490-A
GAMING
- asus_rog_ryujin: ROG Ryujin III 360
- dell-smm: Dell 16 DC16250, Dell OptiPlex 7090, Dell Precision 3650
Tower, Add Latitude 5420
- it87: IT8613E
- k10temp: 1ah/80h devices. Drop support for Hygon devices (support
to be re-added with separate patches later)
- lm63: Sensylink CTF2301
- nct6683: MSI MAG B550M MORTAR MAX WIFI, MSI MAG B650 TOMAHAWK WIFI,
MSI MAG B650M MORTAR WIFI (MS-7D76), and MSI MAG B850M MORTAR WIFI
- nct6775: NCT6122D and NCT6126D
- pmbus/isl68137: Renesas RAA229639 and RAA229640
- pmbus/max34440: ADPM12300
- pmbus/tps25990: TPS1689
- pmbus/tps53679: TPS53622, TPS53659 and TPS536C7
- pmbus/xdpe1a2g7b: xdpe1a2g7c
- sht4x: Sensirion STS4x
- socfpga: Agilex 5
- yogafan: Lenovo Yoga Pro 9 16IMH9, Yoga 14cACN 2021, IdeaPad 3
15ALC6 Ub, Yoga 740-15IML
Core updates:
- Support for currX_emergency and inX_[l]emergency attributes
Notable bug fixes and changes:
- Add missing locks to corsair-cpro, ftsteutates, nct7904, and
npcm750-pwm-fan drivers
- Fix jiffies wraparound in tmp102 and tmp108 drivers
- Documentation updates (document missing chip support, typo fixes,
clarifications)
- pmbus/ltc4286: Add writable shunt_resistor sysfs attribute
- coretemp: Document sysfs numbering and label meaning, fix handling
of isolated SMT siblings, read TjMax only when refreshing the
temperature, refresh the temperature on the first read, and return
-ENODATA for invalid temperature readings
- spd5118: Avoid probing when 16-bit addressing is enabled, and
select page 0 unconditionally during probe
- arctic_fan_controller: Default PWM cache to MCU 40%, Dual-license
GPL-2.0-or-later OR BSD-2-Clause, and update maintainer address
Various other minor bug fixes and improvements"
* tag 'hwmon-for-v7.4' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (97 commits)
hwmon: (npcm750-pwm-fan) serialize PWM register updates
hwmon: (nct7904) serialise the watchdog and sensors
hwmon: (ftsteutates) serialise the watchdog and sensors
hwmon: (acpi_power_meter) Fix 18.45 TW bug
hwmon: (sht3x) Return the consumed count for heater writes
hwmon: (mcp9982) Update the interval cache only after successful writes
hwmon: (gigabyte_waterforce) reject short input reports
hwmon: (coretemp) Fix handling of isolated SMT siblings
hwmon: (coretemp) Document sysfs numbering and label meaning
hwmon: (asus_wmi_sensors) Reword BIOS upgrade advice
hwmon: Replace __MODULE_STRING() with __stringify()
hwmon:(pmbus/xdpe1a2g7b) Add support for xdpe1a2g7c controller
dt-bindings: trivial-devices: Add support for XDPE1A2G7C
hwmon: (pmbus/isl68137) Add Renesas RAA229639 and RAA229640
dt-bindings: hwmon: pmbus/isl68137: Add Renesas RAA229639 and RAA229640
hwmon: (coretemp) Read TjMax only when refreshing the temperature
hwmon: (pmbus/ibm-cffps) Avoid format truncation warnings
hwmon: (dell-smm) Add Dell 16 DC16250 to fan control whitelist
hwmon: (nct6683) Add more MSI boards to list of tested boards
hwmon: (nct6683) Add MSI B850M to list of tested boards
...
This commit is contained in:
commit
7313d94c67
|
|
@ -61,6 +61,18 @@ Description:
|
|||
take drastic action such as power down or reset. At the very
|
||||
least, it should report a fault.
|
||||
|
||||
What: /sys/class/hwmon/hwmonX/inY_lemergency
|
||||
Description:
|
||||
Voltage emergency min value.
|
||||
|
||||
Unit: millivolt
|
||||
|
||||
RW
|
||||
|
||||
If voltage drops to or below this limit, the system is
|
||||
expected to take drastic action such as immediate power
|
||||
down or reset. At the very least, it should report a fault.
|
||||
|
||||
What: /sys/class/hwmon/hwmonX/inY_max
|
||||
Description:
|
||||
Voltage max value.
|
||||
|
|
@ -81,6 +93,18 @@ Description:
|
|||
take drastic action such as power down or reset. At the very
|
||||
least, it should report a fault.
|
||||
|
||||
What: /sys/class/hwmon/hwmonX/inY_emergency
|
||||
Description:
|
||||
Voltage emergency max value.
|
||||
|
||||
Unit: millivolt
|
||||
|
||||
RW
|
||||
|
||||
If voltage reaches or exceeds this limit, the system is expected
|
||||
to take drastic action such as immediate power down or reset.
|
||||
At the very least, it should report a fault.
|
||||
|
||||
What: /sys/class/hwmon/hwmonX/inY_input
|
||||
Description:
|
||||
Voltage input value.
|
||||
|
|
@ -647,6 +671,18 @@ Description:
|
|||
|
||||
RW
|
||||
|
||||
What: /sys/class/hwmon/hwmonX/currY_emergency
|
||||
Description:
|
||||
Current emergency high value.
|
||||
|
||||
Unit: milliampere
|
||||
|
||||
RW
|
||||
|
||||
If a current reaches or exceeds this limit, the system is
|
||||
expected to take drastic action such as immediate power down
|
||||
or reset. At the very least, it should report a fault.
|
||||
|
||||
What: /sys/class/hwmon/hwmonX/currY_input
|
||||
Description:
|
||||
Current input value
|
||||
|
|
|
|||
|
|
@ -0,0 +1,72 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/hwmon/axiado,ax3000-pwm-fan.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Axiado AX3000 and AX3005 PWM Fan Controller
|
||||
|
||||
maintainers:
|
||||
- Petar Stepanovic <pstepanovic@axiado.com>
|
||||
- Akhila Kavi <akavi@axiado.com>
|
||||
- Prasad Bolisetty <pbolisetty@axiado.com>
|
||||
|
||||
description:
|
||||
The Axiado AX3000 and AX3005 fan controllers use a hardware tachometer
|
||||
block to measure fan speed. Fan speed is controlled through a PWM signal
|
||||
supplied by an external PWM controller.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- const: axiado,ax3000-pwm-fan
|
||||
- items:
|
||||
- const: axiado,ax3005-pwm-fan
|
||||
- const: axiado,ax3000-pwm-fan
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
fan:
|
||||
type: object
|
||||
$ref: fan-common.yaml#
|
||||
unevaluatedProperties: false
|
||||
properties:
|
||||
pulses-per-revolution:
|
||||
minimum: 1
|
||||
default: 2
|
||||
|
||||
required:
|
||||
- pwms
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- clocks
|
||||
- fan
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
|
||||
fan-controller@33001400 {
|
||||
compatible = "axiado,ax3000-pwm-fan";
|
||||
reg = <0x33001400 0x400>;
|
||||
interrupts = <GIC_SPI 150 IRQ_TYPE_LEVEL_HIGH>;
|
||||
clocks = <&clk>;
|
||||
|
||||
fan {
|
||||
pwms = <&pwm0 0 400>;
|
||||
pulses-per-revolution = <2>;
|
||||
};
|
||||
};
|
||||
...
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/hwmon/axiado,ax3000-tsadc.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Axiado AX3000 and AX3005 TSADC
|
||||
|
||||
maintainers:
|
||||
- Petar Stepanovic <pstepanovic@axiado.com>
|
||||
- Akhila Kavi <akavi@axiado.com>
|
||||
- Prasad Bolisetty <pbolisetty@axiado.com>
|
||||
|
||||
description: |
|
||||
The Axiado AX3000 and AX3005 Temperature Sensor ADC (TSADC) controllers
|
||||
monitor the on-die temperature of the SoC.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- const: axiado,ax3000-tsadc
|
||||
- items:
|
||||
- const: axiado,ax3005-tsadc
|
||||
- const: axiado,ax3000-tsadc
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- clocks
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
temperature-sensor@33021000 {
|
||||
compatible = "axiado,ax3000-tsadc";
|
||||
reg = <0x33021000 0x100>;
|
||||
clocks = <&refclk>;
|
||||
};
|
||||
55
Documentation/devicetree/bindings/hwmon/national,lm63.yaml
Normal file
55
Documentation/devicetree/bindings/hwmon/national,lm63.yaml
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/hwmon/national,lm63.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: LM63-compatible temperature sensors with integrated fan control
|
||||
|
||||
maintainers:
|
||||
- Guenter Roeck <linux@roeck-us.net>
|
||||
|
||||
description: |
|
||||
The LM63 family provides local and remote temperature monitoring together
|
||||
with fan speed monitoring and PWM fan control. Supported devices include
|
||||
the National Semiconductor LM63, LM64 and LM96163, and the Sensylink
|
||||
CTF2301.
|
||||
|
||||
allOf:
|
||||
- $ref: hwmon-common.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- national,lm63
|
||||
- national,lm64
|
||||
- national,lm96163
|
||||
- sensylink,ctf2301
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
"#cooling-cells":
|
||||
const: 2
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
temperature-sensor@4c {
|
||||
compatible = "national,lm63";
|
||||
reg = <0x4c>;
|
||||
#cooling-cells = <2>;
|
||||
};
|
||||
};
|
||||
|
|
@ -113,6 +113,23 @@ allOf:
|
|||
properties:
|
||||
ti,extended-range-enable: false
|
||||
|
||||
- if:
|
||||
not:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- adi,adt7481
|
||||
- dallas,max6695
|
||||
- dallas,max6696
|
||||
then:
|
||||
patternProperties:
|
||||
"^channel@[0-1]$":
|
||||
properties:
|
||||
reg:
|
||||
enum: [0, 1]
|
||||
"channel@2": false
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
|
|
@ -134,6 +151,11 @@ allOf:
|
|||
"^channel@([0-2])$":
|
||||
properties:
|
||||
temperature-offset-millicelsius: false
|
||||
else:
|
||||
patternProperties:
|
||||
"channel@0":
|
||||
properties:
|
||||
temperature-offset-millicelsius: false
|
||||
|
||||
- if:
|
||||
properties:
|
||||
|
|
@ -149,7 +171,7 @@ allOf:
|
|||
- onnn,nct1008
|
||||
then:
|
||||
patternProperties:
|
||||
"^channel@([0-2])$":
|
||||
"^channel@([1-2])$":
|
||||
properties:
|
||||
temperature-offset-millicelsius:
|
||||
maximum: 127750
|
||||
|
|
@ -172,7 +194,7 @@ allOf:
|
|||
- winbond,w83l771
|
||||
then:
|
||||
patternProperties:
|
||||
"^channel@([0-2])$":
|
||||
"^channel@([1-2])$":
|
||||
properties:
|
||||
temperature-offset-millicelsius:
|
||||
maximum: 127875
|
||||
|
|
@ -186,7 +208,7 @@ allOf:
|
|||
- ti,tmp461
|
||||
then:
|
||||
patternProperties:
|
||||
"^channel@([0-2])$":
|
||||
"^channel@([1-2])$":
|
||||
properties:
|
||||
temperature-offset-millicelsius:
|
||||
maximum: 127937
|
||||
|
|
|
|||
102
Documentation/devicetree/bindings/hwmon/pmbus/adi,max20826.yaml
Normal file
102
Documentation/devicetree/bindings/hwmon/pmbus/adi,max20826.yaml
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/hwmon/pmbus/adi,max20826.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices MAX20826 PMBus Voltage Regulator
|
||||
|
||||
maintainers:
|
||||
- Nuno Sá <nuno.sa@analog.com>
|
||||
|
||||
description:
|
||||
Analog Devices MAX20826 and similar devices are Dual-Output Multiphase
|
||||
High-Current Controller.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,max20826
|
||||
- adi,max20855b
|
||||
- adi,max20908
|
||||
- adi,max20912
|
||||
- adi,max20916
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
vdd3p3-supply:
|
||||
description:
|
||||
3.3V bias supply. It is the input of the internal 1.8V linear regulator
|
||||
which supplies the controller.
|
||||
|
||||
avren-gpios:
|
||||
description: GPIO pin to enable/disable the output voltage on Rail A.
|
||||
maxItems: 1
|
||||
|
||||
bvren-gpios:
|
||||
description: GPIO pin to enable/disable the output voltage on Rail B.
|
||||
maxItems: 1
|
||||
|
||||
adi,rail-a-high-speed:
|
||||
type: boolean
|
||||
description:
|
||||
The output voltage of Rail A is controlled by the high-speed processor
|
||||
voltage-control interface the device powered up in (PWMVID, SVID, SVI3 or
|
||||
AVSBus). If not given, PMBus controls the output voltage of Rail A.
|
||||
|
||||
adi,rail-b-high-speed:
|
||||
type: boolean
|
||||
description:
|
||||
Same as adi,rail-a-high-speed but for Rail B.
|
||||
|
||||
regulators:
|
||||
type: object
|
||||
description:
|
||||
List of regulators provided by this controller. vout0 is Rail A and
|
||||
vout1 is Rail B.
|
||||
|
||||
patternProperties:
|
||||
"^vout[0-1]$":
|
||||
$ref: /schemas/regulator/regulator.yaml#
|
||||
type: object
|
||||
unevaluatedProperties: false
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- vdd3p3-supply
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
regulator@64 {
|
||||
compatible = "adi,max20826";
|
||||
reg = <0x64>;
|
||||
|
||||
vdd3p3-supply = <&vdd3p3>;
|
||||
|
||||
avren-gpios = <&gpio 0 GPIO_ACTIVE_HIGH>;
|
||||
bvren-gpios = <&gpio 1 GPIO_ACTIVE_HIGH>;
|
||||
|
||||
adi,rail-a-high-speed;
|
||||
|
||||
regulators {
|
||||
vout0 {
|
||||
regulator-name = "vcore_a";
|
||||
};
|
||||
|
||||
vout1 {
|
||||
regulator-name = "vcore_b";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/hwmon/pmbus/infineon,tda38740.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Infineon TDA38740 and TDA38725 PMBus Synchronous Buck Regulator
|
||||
|
||||
maintainers:
|
||||
- Colin Huang <u8813345@gmail.com>
|
||||
|
||||
description: |
|
||||
The Infineon TDA38740/TDA38725 is a 40A/25A single-voltage synchronous
|
||||
buck regulator with a PMBus interface designed for industrial use.
|
||||
|
||||
Datasheet:
|
||||
https://www.infineon.com/row/public/documents/24/49/infineon-tda38740-tda38725-datasheet-en.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- const: infineon,tda38740
|
||||
- items:
|
||||
- const: infineon,tda38725
|
||||
- const: infineon,tda38740
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
regulator@40 {
|
||||
compatible = "infineon,tda38725",
|
||||
"infineon,tda38740";
|
||||
reg = <0x40>;
|
||||
|
||||
interrupt-parent = <&sgpiom0>;
|
||||
interrupts = <10 IRQ_TYPE_LEVEL_LOW>;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
@ -65,6 +65,8 @@ properties:
|
|||
- enum:
|
||||
- renesas,raa228942
|
||||
- renesas,raa228943
|
||||
- renesas,raa229639
|
||||
- renesas,raa229640
|
||||
- const: renesas,raa228244
|
||||
|
||||
reg:
|
||||
|
|
|
|||
|
|
@ -5,18 +5,20 @@
|
|||
$id: http://devicetree.org/schemas/hwmon/pmbus/ti,tps25990.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Texas Instruments TPS25990 Stackable eFuse
|
||||
title: Texas Instruments Stackable eFuses
|
||||
|
||||
maintainers:
|
||||
- Jerome Brunet <jbrunet@baylibre.com>
|
||||
|
||||
description:
|
||||
The TI TPS25990 is an integrated, high-current circuit
|
||||
The TI TPS25990 and TPS1689 are integrated, high-current circuit
|
||||
protection and power management device with PMBUS interface
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: ti,tps25990
|
||||
enum:
|
||||
- ti,tps1689
|
||||
- ti,tps25990
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
|
|
|||
|
|
@ -4,15 +4,21 @@
|
|||
$id: http://devicetree.org/schemas/hwmon/ti,tmp102.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: TMP102 temperature sensor
|
||||
title: TMP102/TMP103/TMP110/TMP113 temperature sensor
|
||||
|
||||
maintainers:
|
||||
- Krzysztof Kozlowski <krzk@kernel.org>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- ti,tmp102
|
||||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- ti,tmp110
|
||||
- ti,tmp113
|
||||
- const: ti,tmp102
|
||||
- const: ti,tmp102
|
||||
- const: ti,tmp103
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
|
@ -25,10 +31,10 @@ properties:
|
|||
A descriptive name for this channel, like "ambient" or "psu".
|
||||
|
||||
"#thermal-sensor-cells":
|
||||
const: 1
|
||||
enum: [0, 1]
|
||||
|
||||
vcc-supply:
|
||||
description: Power supply for tmp102
|
||||
description: Power supply for the sensor
|
||||
|
||||
required:
|
||||
- compatible
|
||||
|
|
|
|||
|
|
@ -50,6 +50,9 @@ properties:
|
|||
minimum: 0
|
||||
maximum: 15
|
||||
|
||||
"#thermal-sensor-cells":
|
||||
const: 1
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
|
|
|
|||
|
|
@ -176,6 +176,7 @@ properties:
|
|||
# Infineon Multi-phase Digital VR Controller xdpe1a2g7b
|
||||
- infineon,xdpe1a2g5b
|
||||
- infineon,xdpe1a2g7b
|
||||
- infineon,xdpe1a2g7c
|
||||
# Injoinic IP5108 2.0A Power Bank IC with I2C
|
||||
- injoinic,ip5108
|
||||
# Injoinic IP5109 2.1A Power Bank IC with I2C
|
||||
|
|
@ -370,16 +371,10 @@ properties:
|
|||
- mps,mpq82d00
|
||||
# Murata D1U74T-W power supply unit
|
||||
- murata,d1u74t
|
||||
# Temperature sensor with integrated fan control
|
||||
- national,lm63
|
||||
# Temperature sensor with integrated fan control
|
||||
- national,lm64
|
||||
# Temperature sensor
|
||||
- national,lm95235
|
||||
# Temperature sensor
|
||||
- national,lm95245
|
||||
# Temperature sensor with integrated fan control
|
||||
- national,lm96163
|
||||
# Serial Interface ACPI-Compatible Microprocessor System Hardware Monitor
|
||||
- national,lm80
|
||||
# Serial Interface ACPI-Compatible Microprocessor System Hardware Monitor
|
||||
|
|
@ -429,6 +424,8 @@ properties:
|
|||
- sensirion,sht21
|
||||
- sensirion,sht25
|
||||
- sensirion,sht4x
|
||||
# Sensirion temperature sensor with I2C interface
|
||||
- sensirion,sts40
|
||||
# Sensortek 3 axis accelerometer
|
||||
- sensortek,stk8312
|
||||
# Sensortek 3 axis accelerometer
|
||||
|
|
@ -506,16 +503,18 @@ properties:
|
|||
- ti,lm74
|
||||
# Temperature sensor with integrated fan control
|
||||
- ti,lm96000
|
||||
# Low Power Digital Temperature Sensor with SMBUS/Two Wire Serial Interface
|
||||
- ti,tmp103
|
||||
# Thermometer with SPI interface
|
||||
- ti,tmp121
|
||||
- ti,tmp122
|
||||
- ti,tmp125
|
||||
# TI DC-DC converter on PMBus
|
||||
- ti,tps40400
|
||||
# TI Dual channel DCAP+ multiphase controller TPS53622
|
||||
- ti,tps53622
|
||||
# TI DCAP+ multiphase controller
|
||||
- ti,tps53647
|
||||
# TI Dual channel DCAP+ multiphase controller TPS53659
|
||||
- ti,tps53659
|
||||
# TI DCAP+ multiphase controller
|
||||
- ti,tps53667
|
||||
# TI Dual channel DCAP+ multiphase controller TPS53676 with AVSBus
|
||||
|
|
@ -528,6 +527,8 @@ properties:
|
|||
- ti,tps53685
|
||||
# TI Dual channel DCAP+ multiphase controller TPS53688
|
||||
- ti,tps53688
|
||||
# TI Dual channel DCAP+ multiphase controller TPS536C7
|
||||
- ti,tps536c7
|
||||
# TI DC-DC converters on PMBus
|
||||
- ti,tps544b20
|
||||
- ti,tps544b25
|
||||
|
|
|
|||
|
|
@ -1511,6 +1511,8 @@ patternProperties:
|
|||
description: Sensirion AG
|
||||
"^sensortek,.*":
|
||||
description: Sensortek Technology Corporation
|
||||
"^sensylink,.*":
|
||||
description: Sensylink Microelectronics Technology Co., Ltd.
|
||||
"^sercomm,.*":
|
||||
description: Sercomm (Suzhou) Corporation
|
||||
"^sff,.*":
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0-or-later
|
||||
.. SPDX-License-Identifier: GPL-2.0-or-later OR BSD-2-Clause
|
||||
|
||||
Kernel driver arctic_fan_controller
|
||||
=====================================
|
||||
|
|
@ -29,18 +29,16 @@ Usage notes
|
|||
Since it is a USB device, hotplug is supported. The device is autodetected.
|
||||
|
||||
The device does not support GET_REPORT, so the driver cannot read back the
|
||||
current hardware PWM state at probe time. The cached PWM values (readable
|
||||
via pwm[1-10]) start at 0 and reflect only values that have been
|
||||
successfully written. Because each OUT report carries all 10 channel values,
|
||||
writing a single channel also sends the cached values for all other channels.
|
||||
Users should set all channels to the desired values before relying on the
|
||||
cached state.
|
||||
current hardware PWM state at probe time. Each OUT report carries all 10
|
||||
channels, so pwm[1-10] is a host cache. It starts at the MCU factory
|
||||
default of 40% (sysfs 102). After a successful write, the cache reflects
|
||||
that value. Users should set all channels to the desired values before
|
||||
relying on the cached state.
|
||||
|
||||
On system suspend, the device may lose power and reset its PWM channels to
|
||||
hardware defaults. The driver clears its cached duty values on resume so
|
||||
that reads reflect the unknown hardware state rather than stale pre-suspend
|
||||
values. Userspace is responsible for re-applying the desired duty cycles
|
||||
after resume.
|
||||
On system suspend, the device may lose power and reset PWM to the factory
|
||||
default. The driver restores the cache to 40% on resume. If the device
|
||||
kept power across suspend, userspace should re-apply the desired duty
|
||||
cycles.
|
||||
|
||||
Sysfs entries
|
||||
-------------
|
||||
|
|
@ -51,6 +49,6 @@ pwm[1-10] PWM duty cycle (0-255). Write: sends an OUT report setting the
|
|||
duty cycle (scaled from 0-255 to 0-100% for the device);
|
||||
the cached value is updated only after the device ACKs the
|
||||
command with a success status. Read: returns the last
|
||||
successfully written value; initialized to 0 at driver load
|
||||
and after resume (hardware state unknown).
|
||||
successfully written value; initialized to 102 (40%) at
|
||||
driver load and after resume (MCU factory default).
|
||||
================ ==============================================================
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ Supported boards:
|
|||
* ROG STRIX X570-E GAMING WIFI II
|
||||
* ROG STRIX X570-F GAMING
|
||||
* ROG STRIX X570-I GAMING
|
||||
* ROG STRIX X670E-A GAMING WIFI
|
||||
* ROG STRIX X670E-E GAMING WIFI
|
||||
* ROG STRIX X670E-I GAMING WIFI
|
||||
* ROG STRIX X870-F GAMING WIFI
|
||||
|
|
@ -54,6 +55,7 @@ Supported boards:
|
|||
* ROG STRIX X870E-H GAMING WIFI7
|
||||
* ROG STRIX Z390-E GAMING
|
||||
* ROG STRIX Z390-F GAMING
|
||||
* ROG STRIX Z490-A GAMING
|
||||
* ROG STRIX Z490-F GAMING
|
||||
* ROG STRIX Z690-A GAMING WIFI D4
|
||||
* ROG STRIX Z690-E GAMING WIFI
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ Kernel driver asus_rog_ryujin
|
|||
Supported devices:
|
||||
|
||||
* ASUS ROG RYUJIN II 360
|
||||
* ASUS ROG RYUJIN III 360
|
||||
* ASUS ROG RYUJIN III EXTREME
|
||||
* ASUS ROG RYUJIN III EVA EDITION
|
||||
* ASUS ROG RYUJIN III WHITE EDITION
|
||||
|
|
|
|||
|
|
@ -72,7 +72,6 @@ Known Issues:
|
|||
X470 Pro seems particularly bad for this. The more frequently the WMI
|
||||
interface is polled the greater the potential for this to happen. Until you
|
||||
have subjected your computer to an extended soak test while polling the
|
||||
sensors frequently, don't leave you computer unattended. Upgrading to new
|
||||
BIOS version with method version greater than or equal to two should
|
||||
rectify the issue.
|
||||
sensors frequently, don't leave your computer unattended. Upgrading to a
|
||||
more recent BIOS version might help.
|
||||
* A few boards report 12v voltages to be ~10v.
|
||||
|
|
|
|||
38
Documentation/hwmon/axiado-pwm-fan.rst
Normal file
38
Documentation/hwmon/axiado-pwm-fan.rst
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
Kernel driver axiado-pwm-fan
|
||||
============================
|
||||
|
||||
Supported chips:
|
||||
|
||||
* Axiado AX3000
|
||||
* Axiado AX3005
|
||||
|
||||
Datasheet: Not publicly available
|
||||
|
||||
Author:
|
||||
|
||||
* Petar Stepanovic <pstepanovic@axiado.com>
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The Axiado AX3000 and AX3005 PWM fan controllers measure the rotational
|
||||
speed of one fan using a hardware tachometer block. Fan speed is controlled
|
||||
through a PWM signal supplied by an external PWM controller.
|
||||
|
||||
The number of tachometer pulses generated per fan revolution is configured
|
||||
through the ``pulses-per-revolution`` devicetree property. If the property
|
||||
is not specified, the driver uses two pulses per revolution.
|
||||
|
||||
Sysfs attributes
|
||||
----------------
|
||||
|
||||
The driver provides the following standard hwmon attributes:
|
||||
|
||||
=============== ====== =====================================================
|
||||
fan1_input RO Fan speed in revolutions per minute (RPM).
|
||||
|
||||
pwm1 RW Relative PWM control value from 0 to 255. A value of
|
||||
255 selects the maximum PWM duty cycle.
|
||||
=============== ====== =====================================================
|
||||
41
Documentation/hwmon/axiado-tsadc.rst
Normal file
41
Documentation/hwmon/axiado-tsadc.rst
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
Kernel driver axiado-tsadc
|
||||
==========================
|
||||
|
||||
Supported chips:
|
||||
|
||||
* Axiado AX3000 and AX3005 on-chip temperature sensor (TSADC)
|
||||
|
||||
Prefix: 'axiado_tsadc'
|
||||
|
||||
Addresses scanned: -
|
||||
|
||||
Datasheet: Not publicly available
|
||||
|
||||
Authors:
|
||||
|
||||
- Petar Stepanovic <pstepanovic@axiado.com>
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
This driver supports the Temperature Sensor ADC (TSADC) block found on Axiado
|
||||
AX3000 and AX3005 SoCs. The block monitors the on-die temperature of the SoC.
|
||||
|
||||
An SoC may instantiate several TSADC blocks at different locations on the die.
|
||||
Each block is described by its own device tree node and is registered as a
|
||||
separate hwmon device with a single temperature channel.
|
||||
|
||||
The sensor is configured for continuous conversion at probe time, averaging
|
||||
16 samples per conversion. The averaged 12-bit ADC code is converted to
|
||||
millidegrees Celsius using a lookup table with linear interpolation between
|
||||
entries. The table covers -40 to +125 degrees Celsius; readings outside that
|
||||
range are clamped to the nearest supported temperature.
|
||||
|
||||
sysfs entries
|
||||
-------------
|
||||
|
||||
======================= =======================================================
|
||||
temp1_input Die temperature in millidegrees Celsius (read-only)
|
||||
======================= =======================================================
|
||||
|
|
@ -46,18 +46,25 @@ mechanism will perform actions to forcibly cool down the processor. Alarm
|
|||
may be raised, if the temperature grows enough (more than TjMax) to trigger
|
||||
the Out-Of-Spec bit. The following table summarizes the exported sysfs files:
|
||||
|
||||
All sysfs entries are named with their core_id (represented here by 'X').
|
||||
Per-core sysfs entries are numbered core_id + 2 (represented here by 'X').
|
||||
The package entries, if available, are always temp1_*.
|
||||
|
||||
================= ========================================================
|
||||
tempX_input Core temperature (in millidegrees Celsius).
|
||||
Reading fails with -ENODATA while the CPU reports the
|
||||
temperature as invalid. Some CPUs do this when the
|
||||
temperature is too low to be measured.
|
||||
tempX_max Maximum recommended operating temperature (Tcontrol).
|
||||
All cooling devices should be turned on.
|
||||
tempX_crit Maximum junction temperature (in millidegrees Celsius).
|
||||
tempX_crit_alarm Set when Out-of-spec bit is set, never clears.
|
||||
Correct CPU operation is no longer guaranteed.
|
||||
tempX_label Contains string "Core X", where X is processor
|
||||
number. For Package temp, this will be "Package id Y",
|
||||
where Y is the package number.
|
||||
tempX_label Contains string "Core N", where N is the core_id
|
||||
(X - 2), as shown in
|
||||
/sys/devices/system/cpu/cpu*/topology/core_id. This
|
||||
is not the logical CPU number, and core_id values
|
||||
may be sparse. For Package temp, this will be
|
||||
"Package id Y", where Y is the package number.
|
||||
================= ========================================================
|
||||
|
||||
On modern CPUs (Nehalem and newer), TjMax is read from the
|
||||
|
|
|
|||
32
Documentation/hwmon/honor-fmi.rst
Normal file
32
Documentation/hwmon/honor-fmi.rst
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
Kernel driver honor-fmi
|
||||
=======================
|
||||
|
||||
Supported systems:
|
||||
|
||||
* HONOR FMI-XX
|
||||
|
||||
Author: Nikita Dubrovskih <testname142@gmail.com>
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The driver provides read-only monitoring of the fan speed on the HONOR FMI-XX.
|
||||
The system firmware implements a ``GFNS`` ACPI method which returns the speed
|
||||
of one of two firmware fan channels in RPM. Embedded Controller access and
|
||||
serialization are handled by the firmware method.
|
||||
|
||||
The driver does not expose fan control or direct Embedded Controller access.
|
||||
|
||||
Sysfs entries
|
||||
-------------
|
||||
|
||||
The following attributes are supported:
|
||||
|
||||
======================= ======= =============================================
|
||||
Name Perm Description
|
||||
======================= ======= =============================================
|
||||
``fan1_input`` RO Fan channel 0 speed in RPM
|
||||
``fan2_input`` RO Fan channel 1 speed in RPM
|
||||
======================= ======= =============================================
|
||||
|
|
@ -51,6 +51,8 @@ Hardware Monitoring Kernel Drivers
|
|||
asus_ec_sensors
|
||||
asus_rog_ryujin
|
||||
asus_wmi_sensors
|
||||
axiado-pwm-fan
|
||||
axiado-tsadc
|
||||
bcm54140
|
||||
bel-pfe
|
||||
bpa-rs600
|
||||
|
|
@ -90,6 +92,7 @@ Hardware Monitoring Kernel Drivers
|
|||
gxp-fan-ctrl
|
||||
hac300s
|
||||
hih6130
|
||||
honor-fmi
|
||||
hp-wmi-sensors
|
||||
hs3001
|
||||
htu31
|
||||
|
|
@ -165,6 +168,7 @@ Hardware Monitoring Kernel Drivers
|
|||
max197
|
||||
max20730
|
||||
max20751
|
||||
max20826
|
||||
max20830
|
||||
max20860a
|
||||
max31722
|
||||
|
|
@ -186,6 +190,7 @@ Hardware Monitoring Kernel Drivers
|
|||
mcp3021
|
||||
mcp9982
|
||||
menf21bmc
|
||||
minisforum-um780xtx
|
||||
mlxreg-fan
|
||||
mp2856
|
||||
mp2869
|
||||
|
|
@ -263,6 +268,7 @@ Hardware Monitoring Kernel Drivers
|
|||
sy7636a-hwmon
|
||||
tc654
|
||||
tc74
|
||||
tda38740
|
||||
thmc50
|
||||
tmp102
|
||||
tmp103
|
||||
|
|
|
|||
|
|
@ -11,6 +11,14 @@ Supported chips:
|
|||
|
||||
Datasheet: Not publicly available
|
||||
|
||||
* IT8613E
|
||||
|
||||
Prefix: 'it8613'
|
||||
|
||||
Addresses scanned: from Super I/O config space (8 I/O ports)
|
||||
|
||||
Datasheet: Not publicly available
|
||||
|
||||
* IT8620E
|
||||
|
||||
Prefix: 'it8620'
|
||||
|
|
|
|||
|
|
@ -28,8 +28,6 @@ Supported chips:
|
|||
|
||||
* AMD Family 17h processors: "Zen", "Zen 2"
|
||||
|
||||
* AMD Family 18h processors: "Hygon Dhyana"
|
||||
|
||||
* AMD Family 19h processors: "Zen 3"
|
||||
|
||||
Prefix: 'k10temp'
|
||||
|
|
@ -128,7 +126,7 @@ it.
|
|||
Models from 17h family report relative temperature, the driver aims to
|
||||
compensate and report the real temperature.
|
||||
|
||||
On Family 17h and Family 18h CPUs, additional temperature sensors may report
|
||||
Core Complex Die (CCD) temperatures. Up to 8 such temperatures are reported
|
||||
as temp{3..10}_input, labeled Tccd{1..8}. Actual support depends on the CPU
|
||||
On Family 17h CPUs, additional temperature sensors may report Core Complex
|
||||
Die (CCD) temperatures. Up to 8 such temperatures are reported as
|
||||
temp{3..10}_input, labeled Tccd{1..8}. Actual support depends on the CPU
|
||||
variant.
|
||||
|
|
|
|||
|
|
@ -33,6 +33,16 @@ Supported chips:
|
|||
|
||||
http://www.national.com/pf/LM/LM96163.html
|
||||
|
||||
* Sensylink CTF2301
|
||||
|
||||
Prefix: 'ctf2301'
|
||||
|
||||
Addresses scanned: none
|
||||
|
||||
Datasheet: https://www.sensylink.com/upload/1/net.sensylink.portal/1689557281035.pdf
|
||||
|
||||
Register description: https://github.com/TroyMitchell911/ctf2301-datasheet
|
||||
|
||||
|
||||
Author: Jean Delvare <jdelvare@suse.de>
|
||||
|
||||
|
|
@ -62,6 +72,8 @@ value have to be masked out. The value is still 16 bit in width.
|
|||
|
||||
All temperature values are given in degrees Celsius. Resolution is 1.0
|
||||
degree for the local temperature, 0.125 degree for the remote temperature.
|
||||
The CTF2301 local temperature input and limit have a resolution of 0.0625
|
||||
degree.
|
||||
|
||||
The fan speed is measured using a tachometer. Contrary to most chips which
|
||||
store the value in an 8-bit register and have a selectable clock divider
|
||||
|
|
@ -93,3 +105,13 @@ support these GPIO lines at present.
|
|||
The LM96163 is an enhanced version of LM63 with improved temperature accuracy
|
||||
and better PWM resolution. For LM96163, the external temperature sensor type is
|
||||
configurable as CPU embedded diode(1) or 3904 transistor(2).
|
||||
|
||||
The CTF2301 is register-compatible with the LM63 family and provides 12 fan
|
||||
control lookup table entries. It supports 8-bit PWM resolution when configured
|
||||
for a 22.5 kHz PWM frequency.
|
||||
|
||||
Device tree nodes may declare ``#cooling-cells`` to register the fan controller
|
||||
with the thermal framework. The cooling state is mapped to the existing
|
||||
``pwm1`` range from 0 to 255. The driver keeps the current automatic or manual
|
||||
fan control mode during probe and switches to manual control when the thermal
|
||||
framework first requests a cooling state.
|
||||
|
|
|
|||
139
Documentation/hwmon/max20826.rst
Normal file
139
Documentation/hwmon/max20826.rst
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
Kernel driver max20826
|
||||
======================
|
||||
|
||||
Supported chips:
|
||||
|
||||
* Analog Devices MAX20826
|
||||
|
||||
Prefix: 'max20826'
|
||||
|
||||
Addresses scanned: -
|
||||
|
||||
Datasheet: Datasheet is not publicly available.
|
||||
|
||||
* Analog Devices MAX20855B
|
||||
|
||||
Prefix: 'max20855b'
|
||||
|
||||
Addresses scanned: -
|
||||
|
||||
Datasheet: Datasheet is not publicly available.
|
||||
|
||||
* Analog Devices MAX20908
|
||||
|
||||
Prefix: 'max20908'
|
||||
|
||||
Addresses scanned: -
|
||||
|
||||
Datasheet: Datasheet is not publicly available.
|
||||
|
||||
* Analog Devices MAX20912
|
||||
|
||||
Prefix: 'max20912'
|
||||
|
||||
Addresses scanned: -
|
||||
|
||||
Datasheet: Datasheet is not publicly available.
|
||||
|
||||
* Analog Devices MAX20916
|
||||
|
||||
Prefix: 'max20916'
|
||||
|
||||
Addresses scanned: -
|
||||
|
||||
Datasheet: Datasheet is not publicly available.
|
||||
|
||||
Author:
|
||||
|
||||
- Nuno Sá <nuno.sa@analog.com>
|
||||
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
This driver supports hardware monitoring for Analog Devices MAX20826,
|
||||
MAX20855B, MAX20908, MAX20912, and MAX20916 multiphase voltage regulator
|
||||
controllers with PMBus interface.
|
||||
|
||||
The devices are dual-loop, multiphase controllers. Depending on the device and
|
||||
configuration, the high-speed processor voltage-control interface can be
|
||||
Nvidia PWMVID, Intel SVID, AMD SVI3, or AVSBus. PMBus is used for monitoring,
|
||||
configuration, status, and fault reporting.
|
||||
|
||||
The driver detects whether the device uses PMBus page mode or direct address
|
||||
mode. In direct address mode, rail B is accessed at the rail A I2C address plus
|
||||
one. If rail B is present, the driver exposes a second PMBus page.
|
||||
|
||||
The driver detects the active number of phases and exposes per-phase input and
|
||||
output current attributes through the PMBus virtual phase support.
|
||||
|
||||
Usage Notes
|
||||
-----------
|
||||
|
||||
This driver does not auto-detect devices. You will have to instantiate the
|
||||
devices explicitly. Please see Documentation/i2c/instantiating-devices.rst for
|
||||
details.
|
||||
|
||||
The optional ``avren`` and ``bvren`` GPIOs may be provided to control the rail A
|
||||
and rail B hardware enable pins. If regulator support is enabled, the rails are
|
||||
also registered through the PMBus regulator framework.
|
||||
|
||||
The source of the output voltage of each rail, i.e. either the high-speed
|
||||
processor voltage-control interface or PMBus, is normally selected once, before
|
||||
the processor boots. Hence it is described in firmware with the
|
||||
``adi,rail-a-high-speed`` and ``adi,rail-b-high-speed`` properties. If the
|
||||
property is not given for a rail, PMBus controls its output voltage.
|
||||
|
||||
Sysfs entries
|
||||
-------------
|
||||
|
||||
The following attributes are supported. Limits, alarms, and per-phase entries
|
||||
are exposed depending on device capabilities, rail configuration, and detected
|
||||
phase count.
|
||||
|
||||
=========================== ================================================
|
||||
in1_label "vin"
|
||||
in1_input Measured input voltage
|
||||
in1_alarm Input voltage alarm
|
||||
in[2-3]_label "vout[1-2]"
|
||||
in[2-3]_input Measured output voltage
|
||||
in[2-3]_alarm Output voltage alarm
|
||||
currX_label "iinN", "iinN.P", "ioutN", or "ioutN.P"
|
||||
currX_input Measured input/output current
|
||||
currX_alarm Current alarm
|
||||
powerX_label "pinN" or "poutN"
|
||||
powerX_input Measured input/output power
|
||||
temp[1-2]_input Measured temperature
|
||||
temp[1-2]_alarm Temperature alarm
|
||||
=========================== ================================================
|
||||
|
||||
Notes
|
||||
-----
|
||||
|
||||
``N`` is the rail number, starting at 1. ``P`` is the phase number, starting at
|
||||
0. The exact ``currX`` indices depend on the number of present rails and on the
|
||||
detected phase count.
|
||||
|
||||
Debugfs entries
|
||||
---------------
|
||||
|
||||
In addition to the entries provided by the PMBus core, the following entries are
|
||||
available for debug purposes only. They are not part of any ABI and may change
|
||||
or go away at any time.
|
||||
|
||||
=========================== ================================================
|
||||
in[2-3]_high_speed_en Enable high-speed voltage-control interface
|
||||
in_high_speed_bus Active high-speed voltage-control interface
|
||||
=========================== ================================================
|
||||
|
||||
``in[2-3]_high_speed_en`` is a per-rail read/write entry matching the standard
|
||||
hwmon voltage channels for the output rails. ``in2_high_speed_en`` controls rail
|
||||
A / ``vout1``. ``in3_high_speed_en`` controls rail B / ``vout2`` and is only
|
||||
present if rail B is detected. Writing 1 selects the high-speed voltage-control
|
||||
interface for that rail; writing 0 selects PMBus voltage control.
|
||||
|
||||
``in_high_speed_bus`` is a read-only entry reporting the high-speed interface
|
||||
used by the device, for example ``Nvidia PWMVID``, ``Intel SVID``, ``AMD SVI3``,
|
||||
or ``AVSBus``.
|
||||
|
|
@ -27,6 +27,14 @@ Supported chips:
|
|||
|
||||
Datasheet: -
|
||||
|
||||
* ADI ADPM12300
|
||||
|
||||
Prefixes: 'adpm12300'
|
||||
|
||||
Addresses scanned: -
|
||||
|
||||
Datasheet: -
|
||||
|
||||
* Maxim MAX34440
|
||||
|
||||
Prefixes: 'max34440'
|
||||
|
|
@ -105,11 +113,11 @@ This driver supports multiple devices: hardware monitoring for Maxim MAX34440
|
|||
PMBus 6-Channel Power-Supply Manager, MAX34441 PMBus 5-Channel Power-Supply
|
||||
Manager and Intelligent Fan Controller, and MAX34446 PMBus Power-Supply Data
|
||||
Logger; PMBus Voltage Monitor and Sequencers for MAX34451, MAX34452, MAX34460,
|
||||
and MAX34461; PMBus DC/DC Power Module ADPM12160, ADPM12200, and ADPM12250. The
|
||||
MAX34451 and MAX34452 support monitoring voltage or current of 16 channels based
|
||||
on GIN pins. The MAX34460 supports 12 voltage channels, and the MAX34461 supports
|
||||
16 voltage channels. The ADPM12160, ADPM12200, and ADPM12250 also monitor both
|
||||
input and output of voltage and current.
|
||||
and MAX34461; PMBus DC/DC Power Module ADPM12160, ADPM12200, ADPM12250 and
|
||||
ADPM12300. The MAX34451 and MAX34452 support monitoring voltage or current of 16
|
||||
channels based on GIN pins. The MAX34460 supports 12 voltage channels, and the
|
||||
MAX34461 supports 16 voltage channels. The ADPM12160, ADPM12200, ADPM12250 and
|
||||
ADPM12300 also monitor both input and output of voltage and current.
|
||||
|
||||
The driver is a client driver to the core PMBus driver. Please see
|
||||
Documentation/hwmon/pmbus.rst for details on PMBus client drivers.
|
||||
|
|
@ -167,8 +175,8 @@ in[1-6]_reset_history Write any value to reset history.
|
|||
.. note::
|
||||
|
||||
- MAX34446 only supports in[1-4].
|
||||
- ADPM12160, ADPM12200, and ADPM12250 only supports in[1-2]. Label is "vin1"
|
||||
and "vout1" respectively.
|
||||
- ADPM12160, ADPM12200, ADPM12250, and ADPM12300 only supports in[1-2].
|
||||
Label is "vin1" and "vout1" respectively.
|
||||
|
||||
Curr
|
||||
~~~~
|
||||
|
|
@ -190,9 +198,9 @@ curr[1-6]_reset_history Write any value to reset history.
|
|||
|
||||
- in6 and curr6 attributes only exist for MAX34440.
|
||||
- MAX34446 only supports curr[1-4].
|
||||
- For ADPM12160, ADPM12200, and ADPM12250, curr[1] is "iin1"
|
||||
- For ADPM12160, ADPM12200, ADPM12250, and ADPM12300, curr[1] is "iin1"
|
||||
- For ADPM12160, and ADPM12200 curr[2-6] are "iout[1-5]".
|
||||
- For ADPM12250, curr[2-4] are "iout[1-3]".
|
||||
- For ADPM12250, and ADPM12300 curr[2-4] are "iout[1-3]".
|
||||
|
||||
Power
|
||||
~~~~~
|
||||
|
|
@ -228,7 +236,7 @@ temp[1-8]_reset_history Write any value to reset history.
|
|||
.. note::
|
||||
- temp7 and temp8 attributes only exist for MAX34440.
|
||||
- MAX34446 only supports temp[1-3].
|
||||
- ADPM12160, ADPM12200, and ADPM12250 only supports temp[1].
|
||||
- ADPM12160, ADPM12200, ADPM12250, and ADPM12300 only supports temp[1].
|
||||
|
||||
|
||||
.. note::
|
||||
|
|
|
|||
68
Documentation/hwmon/minisforum-um780xtx.rst
Normal file
68
Documentation/hwmon/minisforum-um780xtx.rst
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
====================================
|
||||
Kernel driver minisforum-um780xtx
|
||||
====================================
|
||||
|
||||
Supported systems:
|
||||
|
||||
* Minisforum UM780 XTX
|
||||
|
||||
* DMI product name: ``Venus series``
|
||||
* Mainboard: ``F7BSD``, revision ``1.1``
|
||||
* BIOS version: ``1.06``
|
||||
|
||||
Author: Sebastián Peyrott <speyrott@gmail.com>
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
This driver exposes hardware monitoring and fan-control data cached by the
|
||||
IT5571E embedded controller. It uses the ACPI EC transport and only binds to
|
||||
the exact system and firmware identity listed above.
|
||||
|
||||
The two temperature channels are the values used by the EC's fan-control
|
||||
loops. Their physical sensor placement is not known. The CPU channel is the
|
||||
filtered AMD SB-TSI temperature, while the system channel is an external
|
||||
thermistor input.
|
||||
|
||||
The fan tachometers are read through OEM EC commands. Since each byte is
|
||||
returned by a separate command, the driver uses a high-low-high sequence and
|
||||
retries if the high byte changes.
|
||||
|
||||
Sysfs entries
|
||||
-------------
|
||||
|
||||
========================== ==============================================
|
||||
``temp1_input`` CPU-fan control temperature
|
||||
``temp2_input`` System-fan control temperature
|
||||
``fan1_input`` CPU fan speed in RPM
|
||||
``fan2_input`` System fan speed in RPM
|
||||
``pwm1_enable`` CPU profile: 2 is OEM B1, 3 is OEM B2
|
||||
``pwm2_auto_point1_temp`` Off-to-low system-fan transition temperature
|
||||
``pwm2_auto_point2_temp`` Low-to-high system-fan transition temperature
|
||||
========================== ==============================================
|
||||
|
||||
CPU fan profiles
|
||||
----------------
|
||||
|
||||
Values 2 and 3 of ``pwm1_enable`` select the two complete automatic profiles
|
||||
implemented by the firmware. Writing either value reloads the whole CPU fan
|
||||
curve, including firmware state which is not visible through the ACPI EC
|
||||
window. Other values are rejected.
|
||||
|
||||
System fan thresholds
|
||||
---------------------
|
||||
|
||||
The two writable system-fan temperatures are rounded to whole degrees Celsius
|
||||
and clamped to preserve strict ordering between both visible thresholds and
|
||||
the firmware's internal third threshold. The third threshold does not select
|
||||
a new PWM target and is therefore not exposed as another auto point.
|
||||
|
||||
Resume state preservation
|
||||
-------------------------
|
||||
|
||||
Fan settings are held in EC RAM. On the supported firmware, initialization
|
||||
during s2idle resume reloads the factory system-fan curve. The driver retains
|
||||
the last coherent CPU profile and system-fan thresholds selected through its
|
||||
interface and restores them synchronously at resume.
|
||||
|
|
@ -67,6 +67,10 @@ ASRock X670E NCT6686D EC firmware version 1.0 build 05/19/22
|
|||
ASRock B650 Steel Legend WiFi NCT6686D EC firmware version 1.0 build 11/09/23
|
||||
ASRock Z590 Taichi NCT6686D EC firmware version 1.0 build 01/25/21
|
||||
MSI B550 NCT6687D EC firmware version 1.0 build 05/07/20
|
||||
MSI B550M NCT6687D EC firmware version 1.0 build 05/07/20
|
||||
MSI B650 NCT6687D EC firmware version 0.0 build 09/27/22
|
||||
MSI B650M NCT6687D EC firmware version 0.0 build 09/27/22
|
||||
MSI B850M NCT6687D EC firmware version 0.0 build 03/10/25
|
||||
MSI X670-P NCT6687D EC firmware version 0.0 build 09/27/22
|
||||
MSI X870E NCT6687D EC firmware version 0.0 build 11/13/24
|
||||
=============================== ===============================================
|
||||
|
|
|
|||
|
|
@ -16,6 +16,38 @@ Supported chips:
|
|||
|
||||
Datasheet: Available from the Nuvoton web site
|
||||
|
||||
* Nuvoton NCT6112D/NCT6114D/NCT6116D
|
||||
|
||||
Prefix: 'nct6116'
|
||||
|
||||
Addresses scanned: ISA address retrieved from Super I/O registers
|
||||
|
||||
Datasheet: Available from the Nuvoton web site
|
||||
|
||||
* Nuvoton NCT6122D/NCT6126D
|
||||
|
||||
Prefix: 'nct6126'
|
||||
|
||||
Addresses scanned: ISA address retrieved from Super I/O registers
|
||||
|
||||
Datasheet: Available from the Nuvoton web site
|
||||
|
||||
* Nuvoton NCT6112D/NCT6114D/NCT6116D
|
||||
|
||||
Prefix: 'nct6116'
|
||||
|
||||
Addresses scanned: ISA address retrieved from Super I/O registers
|
||||
|
||||
Datasheet: Available from the Nuvoton web site
|
||||
|
||||
* Nuvoton NCT6122D/NCT6126D
|
||||
|
||||
Prefix: 'nct6126'
|
||||
|
||||
Addresses scanned: ISA address retrieved from Super I/O registers
|
||||
|
||||
Datasheet: Available from the Nuvoton web site
|
||||
|
||||
* Nuvoton NCT5572D/NCT6771F/NCT6772F/NCT6775F/W83677HG-I
|
||||
|
||||
Prefix: 'nct6775'
|
||||
|
|
|
|||
|
|
@ -15,6 +15,16 @@ Supported Chips:
|
|||
|
||||
English: https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/2_Humidity_Sensors/Datasheets/Sensirion_Humidity_Sensors_SHT4x_Datasheet.pdf
|
||||
|
||||
* Sensirion STS4X
|
||||
|
||||
Prefix: 'sts4x'
|
||||
|
||||
Addresses scanned: None
|
||||
|
||||
Datasheet:
|
||||
|
||||
English: https://sensirion.com/resource/datasheet/sts4x
|
||||
|
||||
Author: Navin Sankar Velliangiri <navin@linumiz.com>
|
||||
|
||||
|
||||
|
|
@ -22,9 +32,10 @@ Description
|
|||
-----------
|
||||
|
||||
This driver implements support for the Sensirion SHT4x chip, a humidity
|
||||
and temperature sensor. Temperature is measured in degree celsius, relative
|
||||
humidity is expressed as a percentage. In sysfs interface, all values are
|
||||
scaled by 1000, i.e. the value for 31.5 degrees celsius is 31500.
|
||||
and temperature sensor, and the Sensirion STS4x chip, a temperature sensor.
|
||||
Temperature is measured in degree celsius, relative humidity is expressed as a
|
||||
percentage (on SHT4x only). In sysfs interface, all values are scaled by 1000,
|
||||
i.e. the value for 31.5 degrees celsius is 31500.
|
||||
|
||||
Usage Notes
|
||||
-----------
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ Supported chips:
|
|||
|
||||
* Altera Stratix 10 SoC FPGA
|
||||
* Altera Agilex SoC FPGA
|
||||
* Altera Agilex 5 SoC FPGA
|
||||
|
||||
Authors:
|
||||
- Nazim Amirul <muhammad.nazim.amirul.nazle.asmade@altera.com>
|
||||
|
|
@ -32,3 +33,54 @@ driver based on the service layer compatible string:
|
|||
|
||||
* intel,stratix10-svc
|
||||
* intel,agilex-svc
|
||||
* intel,agilex5-svc
|
||||
|
||||
Channel mappings are fixed in the driver (not described in DT). The tables
|
||||
below list the SDM page/channel encodings used for each family.
|
||||
|
||||
Temperature channels
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
========== ==== ======= =================================
|
||||
Family Page Channel Label
|
||||
========== ==== ======= =================================
|
||||
Stratix 10 0 0 Main Die SDM
|
||||
Agilex 0 0 Main Die SDM
|
||||
Agilex 1 0 Main Die corner bottom left max
|
||||
Agilex 2 0 Main Die corner top left max
|
||||
Agilex 3 0 Main Die corner bottom right max
|
||||
Agilex 4 0 Main Die corner top right max
|
||||
Agilex 5 0 0 Main Die SDM
|
||||
Agilex 5 1 0 Main Die corner bottom left max
|
||||
Agilex 5 3 0 Main Die corner bottom right max
|
||||
Agilex 5 4 0 Main Die corner top right max
|
||||
========== ==== ======= =================================
|
||||
|
||||
Agilex 5 omits SDM temperature channel 2 (top-left corner on Agilex)
|
||||
because that sensor is not present in hardware. The remaining sensors keep
|
||||
the same channel numbers as Agilex.
|
||||
|
||||
Voltage channels
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
========== ==== ======= =================
|
||||
Family Page Channel Label
|
||||
========== ==== ======= =================
|
||||
Stratix 10 0 2 0.8V VCC
|
||||
Stratix 10 0 3 1.8V VCCIO_SDM
|
||||
Stratix 10 0 6 0.9V VCCERAM
|
||||
Agilex 0 2 0.8V VCC
|
||||
Agilex 0 3 1.8V VCCIO_SDM
|
||||
Agilex 0 4 1.8V VCCPT
|
||||
Agilex 0 5 1.2V VCCCRCORE
|
||||
Agilex 0 6 0.9V VCCH
|
||||
Agilex 0 7 0.8V VCCL
|
||||
Agilex 5 0 2 0.8V VCC
|
||||
Agilex 5 0 3 1.8V VCCIO_SDM
|
||||
Agilex 5 0 4 1.8V VCCPT
|
||||
Agilex 5 0 5 1.2V VCCCRCORE
|
||||
Agilex 5 0 6 0.9V VCCH
|
||||
Agilex 5 0 7 0.8V VCCL
|
||||
========== ==== ======= =================
|
||||
|
||||
Agilex 5 reuses the Agilex voltage SDM page/channel layout and labels.
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ There is only one value per file, unlike the older /proc specification.
|
|||
The common scheme for files naming is: <type><number>_<item>. Usual
|
||||
types for sensor chips are "in" (voltage), "temp" (temperature) and
|
||||
"fan" (fan). Usual items are "input" (measured value), "max" (high
|
||||
threshold, "min" (low threshold). Numbering usually starts from 1,
|
||||
threshold), "min" (low threshold). Numbering usually starts from 1,
|
||||
except for voltages which start from 0 (because most data sheets use
|
||||
this). A number is always used for elements that can be present more
|
||||
than once, even if there is a single element of the given type on the
|
||||
|
|
@ -121,12 +121,18 @@ Voltages
|
|||
`in[0-*]_lcrit`
|
||||
Voltage critical min value.
|
||||
|
||||
`in[0-*]_lemergency`
|
||||
Voltage emergency min value.
|
||||
|
||||
`in[0-*]_max`
|
||||
Voltage max value.
|
||||
|
||||
`in[0-*]_crit`
|
||||
Voltage critical max value.
|
||||
|
||||
`in[0-*]_emergency`
|
||||
Voltage emergency max value.
|
||||
|
||||
`in[0-*]_input`
|
||||
Voltage input value.
|
||||
|
||||
|
|
@ -332,6 +338,9 @@ Currents
|
|||
`curr[1-*]_crit`
|
||||
Current critical high value.
|
||||
|
||||
`curr[1-*]_emergency`
|
||||
Current emergency high value.
|
||||
|
||||
`curr[1-*]_input`
|
||||
Current input value.
|
||||
|
||||
|
|
@ -527,12 +536,15 @@ implementation.
|
|||
+-------------------------------+-----------------------+
|
||||
| **`in[0-*]_min_alarm`, | Limit alarm |
|
||||
| `in[0-*]_max_alarm`, | |
|
||||
| `in[0-*]_lemergency_alarm`, | |
|
||||
| `in[0-*]_lcrit_alarm`, | - 0: no alarm |
|
||||
| `in[0-*]_crit_alarm`, | - 1: alarm |
|
||||
| `in[0-*]_emergency_alarm`, | |
|
||||
| `curr[1-*]_min_alarm`, | |
|
||||
| `curr[1-*]_max_alarm`, | RO |
|
||||
| `curr[1-*]_lcrit_alarm`, | |
|
||||
| `curr[1-*]_crit_alarm`, | |
|
||||
| `curr[1-*]_emergency_alarm`, | |
|
||||
| `power[1-*]_cap_alarm`, | |
|
||||
| `power[1-*]_max_alarm`, | |
|
||||
| `power[1-*]_crit_alarm`, | |
|
||||
|
|
|
|||
65
Documentation/hwmon/tda38740.rst
Normal file
65
Documentation/hwmon/tda38740.rst
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
.. SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
====================================
|
||||
Kernel driver tda38740
|
||||
====================================
|
||||
|
||||
Supported chips:
|
||||
* Infineon TDA38740
|
||||
* Infineon TDA38725
|
||||
|
||||
Prefix: 'tda38740'
|
||||
|
||||
Addresses scanned: -
|
||||
|
||||
Datasheet:
|
||||
https://www.infineon.com/row/public/documents/24/49/infineon-tda38740-tda38725-datasheet-en.pdf
|
||||
|
||||
Author: Colin Huang <u8813345@gmail.com>
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The TDA38740 and TDA38725 are single-output synchronous buck regulators with a
|
||||
PMBus interface. They expose basic voltage, current, power and temperature
|
||||
telemetry through the standard PMBus hwmon interfaces.
|
||||
|
||||
Sysfs entries
|
||||
-------------
|
||||
|
||||
======================= =======================================================
|
||||
curr1_alarm Input current alarm
|
||||
curr1_input Measured input current
|
||||
curr1_label "iin"
|
||||
|
||||
curr2_crit Critical maximum output current
|
||||
curr2_crit_alarm Output current critical high alarm
|
||||
curr2_input Measured output current
|
||||
curr2_label "iout"
|
||||
|
||||
in1_crit Critical maximum input voltage
|
||||
in1_crit_alarm Input voltage critical high alarm
|
||||
in1_input Measured input voltage
|
||||
in1_label "vin"
|
||||
|
||||
in2_crit Critical maximum output voltage
|
||||
in2_crit_alarm Output voltage critical high alarm
|
||||
in2_input Measured output voltage
|
||||
in2_label "vout"
|
||||
in2_lcrit Critical minimum output voltage
|
||||
in2_lcrit_alarm Output voltage critical low alarm
|
||||
|
||||
power1_alarm Input power alarm
|
||||
power1_input Measured input power
|
||||
power1_label "pin"
|
||||
|
||||
power2_input Measured output power
|
||||
power2_label "pout"
|
||||
|
||||
temp1_crit Critical high temperature
|
||||
temp1_crit_alarm Chip temperature critical high alarm
|
||||
temp1_input Measured temperature
|
||||
temp1_max Maximum temperature
|
||||
temp1_max_alarm Chip temperature high alarm
|
||||
======================= =======================================================
|
||||
|
||||
|
|
@ -9,26 +9,31 @@ Supported chips:
|
|||
|
||||
Prefix: 'tps25990'
|
||||
|
||||
* Datasheet
|
||||
Datasheet: Publicly available at Texas Instruments website: https://www.ti.com/lit/gpn/tps25990
|
||||
|
||||
Publicly available at Texas Instruments website: https://www.ti.com/lit/gpn/tps25990
|
||||
* TI TPS1689
|
||||
|
||||
Prefix: 'tps1689'
|
||||
|
||||
Datasheet: Publicly available at Texas Instruments website: https://www.ti.com/lit/gpn/tps1689
|
||||
|
||||
Author:
|
||||
|
||||
Jerome Brunet <jbrunet@baylibre.com>
|
||||
Stoyan Bogdanov <sbogdanov@baylibre.com>
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
This driver implements support for TI TPS25990 eFuse.
|
||||
This driver implements support for TI TPS25990 and TI TPS1689 eFuse chips.
|
||||
This is an integrated, high-current circuit protection and power
|
||||
management device with PMBUS interface
|
||||
|
||||
Device compliant with:
|
||||
Devices are compliant with:
|
||||
|
||||
- PMBus rev 1.3 interface.
|
||||
|
||||
Device supports direct format for reading input voltages,
|
||||
Devices supports direct format for reading input voltages,
|
||||
output voltage, input current, input power and temperature.
|
||||
|
||||
Due to the specificities of the chip, all history reset attributes
|
||||
|
|
|
|||
|
|
@ -3,6 +3,14 @@ Kernel driver tps53679
|
|||
|
||||
Supported chips:
|
||||
|
||||
* Texas Instruments TPS53622
|
||||
|
||||
Prefix: 'tps53622'
|
||||
|
||||
Addresses scanned: -
|
||||
|
||||
Datasheet: https://www.ti.com/lit/gpn/TPS53622
|
||||
|
||||
* Texas Instruments TPS53647
|
||||
|
||||
Prefix: 'tps53647'
|
||||
|
|
@ -11,6 +19,14 @@ Supported chips:
|
|||
|
||||
Datasheet: https://www.ti.com/lit/gpn/tps53647
|
||||
|
||||
* Texas Instruments TPS53659
|
||||
|
||||
Prefix: 'tps53659'
|
||||
|
||||
Addresses scanned: -
|
||||
|
||||
Datasheet: https://www.ti.com/lit/gpn/TPS53659
|
||||
|
||||
* Texas Instruments TPS53667
|
||||
|
||||
Prefix: 'tps53667'
|
||||
|
|
@ -59,6 +75,14 @@ Supported chips:
|
|||
|
||||
Datasheet: Available under NDA
|
||||
|
||||
* Texas Instruments TPS536C7
|
||||
|
||||
Prefix: 'tps536c7'
|
||||
|
||||
Addresses scanned: -
|
||||
|
||||
Datasheet: https://www.ti.com/lit/gpn/TPS536C7
|
||||
|
||||
|
||||
Authors:
|
||||
Vadim Pasternak <vadimp@mellanox.com>
|
||||
|
|
@ -69,7 +93,12 @@ Description
|
|||
-----------
|
||||
|
||||
Chips in this series are multi-phase step-down converters with one or two
|
||||
output channels and up to 8 phases per channel.
|
||||
output channels and up to 12 phases in total, depending on the device.
|
||||
|
||||
For TPS536C7 only aggregate per-channel telemetry is exposed; the
|
||||
individual per-phase output currents the hardware can report are not.
|
||||
Attributes for the second output channel are present only when channel B
|
||||
is configured.
|
||||
|
||||
|
||||
Usage Notes
|
||||
|
|
@ -108,7 +137,7 @@ in1_crit_alarm Input voltage critical high alarm.
|
|||
in[N]_label "vout[1-2]"
|
||||
|
||||
- TPS53647, TPS53667: N=2
|
||||
- TPS53679, TPS53588: N=2,3
|
||||
- TPS53622, TPS53659, TPS53679, TPS53688, TPS536C7: N=2,3
|
||||
|
||||
in[N]_input Measured output voltage.
|
||||
|
||||
|
|
@ -135,7 +164,7 @@ in[N]_crit_alarm Output voltage critical high alarm.
|
|||
temp[N]_input Measured temperature.
|
||||
|
||||
- TPS53647, TPS53667: N=1
|
||||
- TPS53679, TPS53681, TPS53588: N=1,2
|
||||
- TPS53622, TPS53659, TPS53679, TPS53681, TPS53688, TPS536C7: N=1,2
|
||||
|
||||
temp[N]_max Maximum temperature.
|
||||
|
||||
|
|
@ -152,7 +181,7 @@ power1_input Measured input power.
|
|||
power[N]_label "pout[1-2]".
|
||||
|
||||
- TPS53647, TPS53667: N=2
|
||||
- TPS53676, TPS53679, TPS53681, TPS53588: N=2,3
|
||||
- TPS53622, TPS53659, TPS53676, TPS53679, TPS53681, TPS53688, TPS536C7: N=2,3
|
||||
|
||||
power[N]_input Measured output power.
|
||||
|
||||
|
|
@ -175,7 +204,7 @@ curr[N]_label "iout[1-2]" or "iout1.[0-5]".
|
|||
telemetry supported on TPS53676 and TPS53681 only.
|
||||
|
||||
- TPS53647, TPS53667: N=2
|
||||
- TPS53679, TPS53588: N=2,3
|
||||
- TPS53622, TPS53659, TPS53679, TPS53688, TPS536C7: N=2,3
|
||||
- TPS53676: N=2-8
|
||||
- TPS53681: N=2-9
|
||||
|
||||
|
|
|
|||
|
|
@ -38,4 +38,4 @@ via a custom interface, abstracted by the "vexpress_config" API.
|
|||
|
||||
As these devices are non-discoverable, they must be described in a Device
|
||||
Tree passed to the kernel. Details of the DT binding for them can be found
|
||||
in Documentation/devicetree/bindings/hwmon/vexpress.txt.
|
||||
in Documentation/devicetree/bindings/arm/vexpress-config.yaml.
|
||||
|
|
|
|||
|
|
@ -85,27 +85,30 @@ immediately to ensure the user knows the fan has stopped.
|
|||
|
||||
::
|
||||
|
||||
MODEL (DMI PN) | FAMILY / SERIES | EC OFFSET | FULL ACPI OBJECT PATH | WIDTH | MULTiplier
|
||||
----------------------------------------------------------------------------------------------------
|
||||
82N7 | Yoga 14cACN | 0x06 | \_SB.PCI0.LPC0.EC0.FANS | 8-bit | 100
|
||||
80V2 / 81C3 | Yoga 710/720 | 0x06 | \_SB.PCI0.LPC0.EC0.FAN0 | 8-bit | 100
|
||||
83E2 / 83DN | Yoga Pro 7/9 | 0xFE | \_SB.PCI0.LPC0.EC0.FANS | 8-bit | 100
|
||||
82A2 / 82A3 | Yoga Slim 7 | 0x06 | \_SB.PCI0.LPC0.EC0.FANS | 8-bit | 100
|
||||
81YM / 82FG | IdeaPad 5 | 0x06 | \_SB.PCI0.LPC0.EC0.FAN0 | 8-bit | 100
|
||||
82JW / 82JU | Legion 5 (AMD) | 0xFE/0xFF | \_SB.PCI0.LPC0.EC0.FANS (Fan1) | 16-bit | 1
|
||||
82JW / 82JU | Legion 5 (AMD) | 0xFE/0xFF | \_SB.PCI0.LPC0.EC0.FA2S (Fan2) | 16-bit | 1
|
||||
82WQ | Legion 7i (Int) | 0xFE/0xFF | \_SB.PCI0.LPC0.EC0.FANS (Fan1) | 16-bit | 1
|
||||
82WQ | Legion 7i (Int) | 0xFE/0xFF | \_SB.PCI0.LPC0.EC0.FA2S (Fan2) | 16-bit | 1
|
||||
82XV / 83DV | LOQ 15/16 | 0xFE/0xFF | \_SB.PCI0.LPC0.EC0.FANS /FA2S | 16-bit | 1
|
||||
83AK | ThinkBook G6 | 0x06 | \_SB.PCI0.LPC0.EC0.FAN0 | 8-bit | 100
|
||||
81X1 | Flex 5 | 0x06 | \_SB.PCI0.LPC0.EC0.FAN0 | 8-bit | 100
|
||||
83KF | XiaoXinPro 13ARE | 0x06/0xFE | \_SB.PCI0.LPC0.EC0.FANS/FA2S | 8-bit | 100
|
||||
82KU | IdeaPad 3 15ALC6 | 0x06 | \_SB.PCI0.LPC0.EC0.FAN0 | 8-bit | 100
|
||||
83RU | Legion Pro 7 16 | 0x03/0x06 | \_SB.PCI0.LPC0.EC0.FANS/FA2S | 8-bit | 100
|
||||
83KF | Yoga Pro 7 14IAH | 0x06 | \_SB.PC00.LPCB.EC0.FANS | 8-bit | 100
|
||||
83BS | Yoga 7 16ARP8 | 0x03/0x06 | \_SB.PCI0.LPC0.EC0.FANS/FA2S | 8-bit | 100
|
||||
*Legacy* | Pre-2020 Models | 0x06 | \_SB.PCI0.LPC.EC.FAN0 | 8-bit | 100
|
||||
----------------------------------------------------------------------------------------------------
|
||||
MODEL (DMI PN) | FAMILY / SERIES | EC OFFSET | FULL ACPI OBJECT PATH | WIDTH | MULTiplier
|
||||
-------------------------------------------------------------------------------------------------------
|
||||
82N7 | Yoga 14cACN 2021 | 0x06 | \_SB.PCI0.LPC0.EC0.FANS | 8-bit | 100
|
||||
81TD | Yoga 740-15IML | 0x06 | \_SB.PCI0.LPCB.EC0.FANS | 8-bit | 100
|
||||
82KU | IdeaPad 3 15ALC6 Ub | 0x06 | \_SB.PCI0.LPC0.EC0.FANS/FA2S | 8-bit | 100
|
||||
80V2 / 81C3 | Yoga 710/720 | 0x06 | \_SB.PCI0.LPC0.EC0.FAN0 | 8-bit | 100
|
||||
83E2 | Yoga Pro 7 | 0xFE | \_SB.PCI0.LPC0.EC0.FANS | 8-bit | 100
|
||||
83DN | Yoga Pro 9 16IMH9 | 0x06/0xFE | \_SB.PC00.LPCB.EC0.FANS/FA2S | 8-bit | 100
|
||||
82A2 / 82A3 | Yoga Slim 7 | 0x06 | \_SB.PCI0.LPC0.EC0.FANS | 8-bit | 100
|
||||
81YM / 82FG | IdeaPad 5 | 0x06 | \_SB.PCI0.LPC0.EC0.FAN0 | 8-bit | 100
|
||||
82JW / 82JU | Legion 5 (AMD) | 0xFE/0xFF | \_SB.PCI0.LPC0.EC0.FANS (Fan1) | 16-bit | 1
|
||||
82JW / 82JU | Legion 5 (AMD) | 0xFE/0xFF | \_SB.PCI0.LPC0.EC0.FA2S (Fan2) | 16-bit | 1
|
||||
82WQ | Legion 7i (Int) | 0xFE/0xFF | \_SB.PCI0.LPC0.EC0.FANS (Fan1) | 16-bit | 1
|
||||
82WQ | Legion 7i (Int) | 0xFE/0xFF | \_SB.PCI0.LPC0.EC0.FA2S (Fan2) | 16-bit | 1
|
||||
82XV / 83DV | LOQ 15/16 | 0xFE/0xFF | \_SB.PCI0.LPC0.EC0.FANS /FA2S | 16-bit | 1
|
||||
83AK | ThinkBook G6 | 0x06 | \_SB.PCI0.LPC0.EC0.FAN0 | 8-bit | 100
|
||||
81X1 | Flex 5 | 0x06 | \_SB.PCI0.LPC0.EC0.FAN0 | 8-bit | 100
|
||||
83KF | XiaoXinPro 13ARE | 0x06/0xFE | \_SB.PCI0.LPC0.EC0.FANS/FA2S | 8-bit | 100
|
||||
82KU | IdeaPad 3 15ALC6 | 0x06 | \_SB.PCI0.LPC0.EC0.FAN0 | 8-bit | 100
|
||||
83RU | Legion Pro 7 16 | 0x03/0x06 | \_SB.PCI0.LPC0.EC0.FANS/FA2S | 8-bit | 100
|
||||
83KF | Yoga Pro 7 14IAH | 0x06 | \_SB.PC00.LPCB.EC0.FANS | 8-bit | 100
|
||||
83BS | Yoga 7 16ARP8 | 0x03/0x06 | \_SB.PCI0.LPC0.EC0.FANS/FA2S | 8-bit | 100
|
||||
*Legacy* | Pre-2020 Models | 0x06 | \_SB.PCI0.LPC.EC.FAN0 | 8-bit | 100
|
||||
-------------------------------------------------------------------------------------------------------
|
||||
|
||||
METHODOLOGY & IDENTIFICATION:
|
||||
|
||||
|
|
@ -158,6 +161,7 @@ Contributors & DSDT Providers:
|
|||
- **Sarbajit Sarkar** (Lenovo LOQ 15IAX9)
|
||||
- **HinataKato** (XiaoXin Pro 13ARE 2020 - 83KF)
|
||||
- **PenPenIsGod** (IdeaPad 3 15ALC6 - 82KU & Legion Pro 7 16AFR10H - 83RU)
|
||||
- **wizard-28** (IdeaPad 3 15ALC6 Ub - 82KU)
|
||||
- **unlockxiaom** (Legion Pro 7 16AFR10H - 83RU, Yoga Pro 7 14IAH10 - 83KF, ThinkCentre M80q)
|
||||
- **Phani Pavan K** (Yoga Pro 7 14IAH10 - 83KF)
|
||||
- **Splarkszter** (Yoga 7 16ARP8 - 83BS)
|
||||
|
|
|
|||
45
MAINTAINERS
45
MAINTAINERS
|
|
@ -2228,7 +2228,7 @@ F: drivers/net/arcnet/
|
|||
F: include/uapi/linux/if_arcnet.h
|
||||
|
||||
ARCTIC FAN CONTROLLER DRIVER
|
||||
M: Aureo Serrano de Souza <aureo.serrano@arctic.de>
|
||||
M: ARCTIC (HK) <arctic.it.hk@arctic.de>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/hwmon/arctic_fan_controller.rst
|
||||
|
|
@ -4589,6 +4589,16 @@ W: https://ez.analog.com/linux-software-drivers
|
|||
F: Documentation/devicetree/bindings/pwm/adi,axi-pwmgen.yaml
|
||||
F: drivers/pwm/pwm-axi-pwmgen.c
|
||||
|
||||
AXIADO PWM FAN CONTROLLER DRIVER
|
||||
M: Petar Stepanovic <pstepanovic@axiado.com>
|
||||
M: Akhila Kavi <akavi@axiado.com>
|
||||
M: Prasad Bolisetty <pbolisetty@axiado.com>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
S: Supported
|
||||
F: Documentation/devicetree/bindings/hwmon/axiado,ax3000-pwm-fan.yaml
|
||||
F: Documentation/hwmon/axiado-pwm-fan.rst
|
||||
F: drivers/hwmon/axiado-pwm-fan.c
|
||||
|
||||
AXIADO SPI DB DRIVER
|
||||
M: Vladimir Moravcevic <vmoravcevic@axiado.com>
|
||||
M: Tzu-Hao Wei <twei@axiado.com>
|
||||
|
|
@ -4600,6 +4610,16 @@ F: Documentation/devicetree/bindings/spi/axiado,ax3000-spi.yaml
|
|||
F: drivers/spi/spi-axiado.c
|
||||
F: drivers/spi/spi-axiado.h
|
||||
|
||||
AXIADO TSADC DRIVER
|
||||
M: Petar Stepanovic <pstepanovic@axiado.com>
|
||||
M: Akhila Kavi <akavi@axiado.com>
|
||||
M: Prasad Bolisetty <pbolisetty@axiado.com>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
S: Supported
|
||||
F: Documentation/devicetree/bindings/hwmon/axiado,ax3000-tsadc.yaml
|
||||
F: Documentation/hwmon/axiado-tsadc.rst
|
||||
F: drivers/hwmon/axiado-tsadc.c
|
||||
|
||||
AYANEO PLATFORM EC DRIVER
|
||||
M: Antheas Kapenekakis <lkml@antheas.dev>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
|
|
@ -12173,6 +12193,13 @@ S: Maintained
|
|||
F: Documentation/devicetree/bindings/iio/pressure/honeywell,mprls0025pa.yaml
|
||||
F: drivers/iio/pressure/mprls0025pa*
|
||||
|
||||
HONOR FMI-XX HARDWARE MONITOR DRIVER
|
||||
M: Nikita Dubrovskih <testname142@gmail.com>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/hwmon/honor-fmi.rst
|
||||
F: drivers/hwmon/honor-fmi.c
|
||||
|
||||
HP BIOSCFG DRIVER
|
||||
M: Jorge Lopez <jorge.lopez2@hp.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
|
|
@ -16259,6 +16286,15 @@ F: Documentation/devicetree/bindings/hwmon/pmbus/adi,max17616.yaml
|
|||
F: Documentation/hwmon/max17616.rst
|
||||
F: drivers/hwmon/pmbus/max17616.c
|
||||
|
||||
MAX20826 HARDWARE MONITOR CONTROLLER DRIVER
|
||||
M: Nuno Sá <nuno.sa@analog.com>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
S: Supported
|
||||
W: https://ez.analog.com/linux-software-drivers
|
||||
F: Documentation/devicetree/bindings/hwmon/pmbus/adi,max20826.yaml
|
||||
F: Documentation/hwmon/max20826.rst
|
||||
F: drivers/hwmon/pmbus/max20826.c
|
||||
|
||||
MAX20830 HARDWARE MONITOR DRIVER
|
||||
M: Alexis Czezar Torreno <alexisczezar.torreno@analog.com>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
|
|
@ -18488,6 +18524,13 @@ F: include/linux/min_heap.h
|
|||
F: lib/min_heap.c
|
||||
F: lib/tests/min_heap_kunit.c
|
||||
|
||||
MINISFORUM UM780 XTX EC HARDWARE MONITOR DRIVER
|
||||
M: Sebastián Peyrott <speyrott@gmail.com>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/hwmon/minisforum-um780xtx.rst
|
||||
F: drivers/hwmon/minisforum-um780xtx.c
|
||||
|
||||
MIPI CCS, SMIA AND SMIA++ IMAGE SENSOR DRIVER
|
||||
M: Sakari Ailus <sakari.ailus@linux.intel.com>
|
||||
L: linux-media@vger.kernel.org
|
||||
|
|
|
|||
|
|
@ -311,6 +311,29 @@ config SENSORS_AXI_FAN_CONTROL
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called axi-fan-control
|
||||
|
||||
config SENSORS_AXIADO_PWM_FAN
|
||||
tristate "Axiado PWM fan controller"
|
||||
depends on ARCH_AXIADO || COMPILE_TEST
|
||||
depends on PWM
|
||||
help
|
||||
This driver supports the Axiado PWM fan controller. It uses a PWM
|
||||
output to control fan speed and a tachometer interrupt to report fan
|
||||
speed as RPM through the hardware monitoring sysfs interface.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called axiado-pwm-fan.
|
||||
|
||||
config SENSORS_AXIADO_TSADC
|
||||
tristate "Axiado AX3000 TSADC temperature sensor"
|
||||
depends on ARCH_AXIADO || COMPILE_TEST
|
||||
help
|
||||
If you say yes here you get support for the on-chip temperature
|
||||
sensor (TSADC) found on Axiado AX3000 and AX3005 SoCs. The die
|
||||
temperature is reported through the hwmon sysfs interface.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called axiado-tsadc.
|
||||
|
||||
config SENSORS_K8TEMP
|
||||
tristate "AMD Athlon64/FX or Opteron temperature sensor"
|
||||
depends on X86 && PCI
|
||||
|
|
@ -1490,6 +1513,22 @@ config SENSORS_MENF21BMC_HWMON
|
|||
This driver can also be built as a module. If so the module
|
||||
will be called menf21bmc_hwmon.
|
||||
|
||||
config SENSORS_MINISFORUM_UM780XTX
|
||||
tristate "Minisforum UM780 XTX EC hardware monitoring"
|
||||
depends on ACPI_EC && DMI && X86
|
||||
help
|
||||
If you say yes here you get support for the hardware monitoring
|
||||
features of the embedded controller in the Minisforum UM780 XTX.
|
||||
This includes CPU and system fan speeds, their control temperatures,
|
||||
two CPU fan profiles, system fan temperature thresholds, and
|
||||
preservation of the selected settings across suspend and resume.
|
||||
|
||||
This driver only binds to explicitly supported board and firmware
|
||||
versions.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called minisforum-um780xtx.
|
||||
|
||||
config SENSORS_MR75203
|
||||
tristate "Moortec Semiconductor MR75203 PVT Controller"
|
||||
select REGMAP_MMIO
|
||||
|
|
@ -1521,10 +1560,10 @@ config SENSORS_LM63
|
|||
depends on I2C
|
||||
help
|
||||
If you say yes here you get support for the National
|
||||
Semiconductor LM63, LM64, and LM96163 remote diode digital temperature
|
||||
sensors with integrated fan control. Such chips are found
|
||||
on the Tyan S4882 (Thunder K8QS Pro) motherboard, among
|
||||
others.
|
||||
Semiconductor LM63, LM64, LM96163, and Sensylink CTF2301
|
||||
remote diode digital temperature sensors with integrated fan
|
||||
control. Such chips are found on the Tyan S4882 (Thunder K8QS
|
||||
Pro) motherboard, among others.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called lm63.
|
||||
|
|
@ -2827,6 +2866,16 @@ config SENSORS_ASUS_EC
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called asus_ec_sensors.
|
||||
|
||||
config SENSORS_HONOR_FMI
|
||||
tristate "HONOR FMI-XX fan monitor"
|
||||
depends on X86 && ACPI
|
||||
help
|
||||
If you say yes here, you get support for fan speed monitoring on
|
||||
the HONOR FMI-XX laptop through its firmware ACPI method.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called honor-fmi.
|
||||
|
||||
config SENSORS_HP_WMI
|
||||
tristate "HP WMI Sensors"
|
||||
depends on ACPI_WMI
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ obj-$(CONFIG_SENSORS_ACPI_POWER) += acpi_power_meter.o
|
|||
obj-$(CONFIG_SENSORS_ATK0110) += asus_atk0110.o
|
||||
obj-$(CONFIG_SENSORS_ASUS_EC) += asus-ec-sensors.o
|
||||
obj-$(CONFIG_SENSORS_ASUS_WMI) += asus_wmi_sensors.o
|
||||
obj-$(CONFIG_SENSORS_HONOR_FMI) += honor-fmi.o
|
||||
obj-$(CONFIG_SENSORS_HP_WMI) += hp-wmi-sensors.o
|
||||
|
||||
# Native drivers
|
||||
|
|
@ -59,6 +60,8 @@ obj-$(CONFIG_SENSORS_ASPEED_G6) += aspeed-g6-pwm-tach.o
|
|||
obj-$(CONFIG_SENSORS_ASUS_ROG_RYUJIN) += asus_rog_ryujin.o
|
||||
obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o
|
||||
obj-$(CONFIG_SENSORS_AXI_FAN_CONTROL) += axi-fan-control.o
|
||||
obj-$(CONFIG_SENSORS_AXIADO_PWM_FAN) += axiado-pwm-fan.o
|
||||
obj-$(CONFIG_SENSORS_AXIADO_TSADC) += axiado-tsadc.o
|
||||
obj-$(CONFIG_SENSORS_CGBC) += cgbc-hwmon.o
|
||||
obj-$(CONFIG_SENSORS_CHIPCAP2) += chipcap2.o
|
||||
obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o
|
||||
|
|
@ -180,6 +183,7 @@ 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_MINISFORUM_UM780XTX) += minisforum-um780xtx.o
|
||||
obj-$(CONFIG_SENSORS_MR75203) += mr75203.o
|
||||
obj-$(CONFIG_SENSORS_NCT6683) += nct6683.o
|
||||
obj-$(CONFIG_SENSORS_NCT6694) += nct6694-hwmon.o
|
||||
|
|
|
|||
|
|
@ -475,7 +475,7 @@ abituguru_detect_bank1_sensor_type(struct abituguru_data *data,
|
|||
u8 val, test_flag, buf[3];
|
||||
int i, ret = -ENODEV; /* error is the most common used retval :| */
|
||||
|
||||
/* If overriden by the user return the user selected type */
|
||||
/* If overridden by the user return the user selected type */
|
||||
if (bank1_types[sensor_addr] >= ABIT_UGURU_IN_SENSOR &&
|
||||
bank1_types[sensor_addr] <= ABIT_UGURU_NC) {
|
||||
ABIT_UGURU_DEBUG(2, "assuming sensor type %d for bank1 sensor "
|
||||
|
|
@ -1262,7 +1262,7 @@ static int abituguru_probe(struct platform_device *pdev)
|
|||
|
||||
/*
|
||||
* El weirdo probe order, to keep the sysfs order identical to the
|
||||
* BIOS and window-appliction listing order.
|
||||
* BIOS and window-application listing order.
|
||||
*/
|
||||
static const u8 probe_order[ABIT_UGURU_MAX_BANK1_SENSORS] = {
|
||||
0x00, 0x01, 0x03, 0x04, 0x0A, 0x08, 0x0E, 0x02,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/limits.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/slab.h>
|
||||
|
|
@ -30,7 +31,12 @@
|
|||
#define POWER_METER_CAN_NOTIFY (1 << 3)
|
||||
#define POWER_METER_IS_BATTERY (1 << 8)
|
||||
#define UNKNOWN_HYSTERESIS 0xFFFFFFFF
|
||||
#define UNKNOWN_POWER 0xFFFFFFFF
|
||||
/*
|
||||
* ACPI integers can either be 32-bit or 64-bit wide, so
|
||||
* we have to check for both values.
|
||||
*/
|
||||
#define UNKNOWN_POWER_32 U32_MAX
|
||||
#define UNKNOWN_POWER_64 U64_MAX
|
||||
|
||||
#define METER_NOTIFY_CONFIG 0x80
|
||||
#define METER_NOTIFY_TRIP 0x81
|
||||
|
|
@ -54,8 +60,8 @@ static int can_cap_in_hardware(void)
|
|||
}
|
||||
|
||||
static const struct acpi_device_id power_meter_ids[] = {
|
||||
{"ACPI000D", 0},
|
||||
{"", 0},
|
||||
{ .id = "ACPI000D" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, power_meter_ids);
|
||||
|
||||
|
|
@ -452,7 +458,7 @@ static int power_meter_read(struct device *dev, enum hwmon_sensor_types type,
|
|||
ret = update_meter(res);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (res->power == UNKNOWN_POWER)
|
||||
if (res->power == UNKNOWN_POWER_32 || res->power == UNKNOWN_POWER_64)
|
||||
return -ENODATA;
|
||||
*val = res->power * 1000;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ static int adm1177_read(struct device *dev, enum hwmon_sensor_types type,
|
|||
return ret;
|
||||
dummy = (data[0] << 4) | (data[2] >> 4);
|
||||
/*
|
||||
* convert to millivolts based on resistor devision
|
||||
* convert to millivolts based on resistor division
|
||||
* (V_fullscale / 4096) * raw
|
||||
*/
|
||||
if (st->vrange_high)
|
||||
|
|
|
|||
|
|
@ -338,7 +338,7 @@ int adt7x10_probe(struct device *dev, const char *name, int irq,
|
|||
data->oldconfig = config;
|
||||
|
||||
/*
|
||||
* Set to 16 bit resolution, continous conversion and comparator mode.
|
||||
* Set to 16 bit resolution, continuous conversion and comparator mode.
|
||||
*/
|
||||
data->config = data->oldconfig;
|
||||
data->config &= ~(ADT7X10_MODE_MASK | ADT7X10_CT_POLARITY |
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later OR BSD-2-Clause
|
||||
/*
|
||||
* Linux hwmon driver for ARCTIC Fan Controller
|
||||
*
|
||||
|
|
@ -35,6 +35,8 @@
|
|||
* Measured over 500 iterations: max ~563 ms. Keep 1 s as margin.
|
||||
*/
|
||||
#define ARCTIC_ACK_TIMEOUT_MS 1000
|
||||
/* MCU factory default; 40% of 0-255 is 102. */
|
||||
#define ARCTIC_PWM_DEFAULT 102
|
||||
|
||||
struct arctic_fan_data {
|
||||
struct hid_device *hdev;
|
||||
|
|
@ -164,7 +166,7 @@ static int arctic_fan_write(struct device *dev, enum hwmon_sensor_types type,
|
|||
|
||||
/*
|
||||
* Build the buffer and arm write_pending under in_report_lock so that
|
||||
* reset_resume() cannot clear pwm_duty[] between the pwm_duty[] read
|
||||
* reset_resume() cannot replace pwm_duty[] between the pwm_duty[] read
|
||||
* and the buffer write, and raw_event() cannot deliver a stale ACK
|
||||
* from a previous write into this write's completion.
|
||||
*
|
||||
|
|
@ -256,14 +258,14 @@ static int arctic_fan_reset_resume(struct hid_device *hdev)
|
|||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* The device resets its PWM channels to hardware defaults on power
|
||||
* loss during suspend. Clear the cached duty values so they reflect
|
||||
* the unknown hardware state, consistent with probe-time behaviour
|
||||
* (the device has no GET_REPORT support). Hold in_report_lock so
|
||||
* this does not race with a concurrent pwm read or write callback.
|
||||
* The device resets its PWM channels to the MCU factory default
|
||||
* (40%) on power loss during suspend. Restore the cache to that
|
||||
* same default, consistent with probe-time behaviour (the device
|
||||
* has no GET_REPORT support). Hold in_report_lock so this does
|
||||
* not race with a concurrent pwm read or write callback.
|
||||
*/
|
||||
spin_lock_irqsave(&priv->in_report_lock, flags);
|
||||
memset(priv->pwm_duty, 0, sizeof(priv->pwm_duty));
|
||||
memset(priv->pwm_duty, ARCTIC_PWM_DEFAULT, sizeof(priv->pwm_duty));
|
||||
spin_unlock_irqrestore(&priv->in_report_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -288,6 +290,8 @@ static int arctic_fan_probe(struct hid_device *hdev,
|
|||
priv->hdev = hdev;
|
||||
spin_lock_init(&priv->in_report_lock);
|
||||
init_completion(&priv->in_report_received);
|
||||
/* Same MCU factory default as reset_resume(); see ARCTIC_PWM_DEFAULT above. */
|
||||
memset(priv->pwm_duty, ARCTIC_PWM_DEFAULT, sizeof(priv->pwm_duty));
|
||||
hid_set_drvdata(hdev, priv);
|
||||
|
||||
ret = hid_hw_start(hdev, HID_CONNECT_DRIVER);
|
||||
|
|
@ -371,4 +375,4 @@ module_hid_driver(arctic_fan_driver);
|
|||
|
||||
MODULE_AUTHOR("Aureo Serrano de Souza <aureo.serrano@arctic.de>");
|
||||
MODULE_DESCRIPTION("HID hwmon driver for ARCTIC Fan Controller");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
|
|
|
|||
|
|
@ -962,6 +962,8 @@ static const struct dmi_system_id dmi_table[] = {
|
|||
&board_info_strix_x570_f_gaming),
|
||||
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X570-I GAMING",
|
||||
&board_info_strix_x570_i_gaming),
|
||||
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X670E-A GAMING WIFI",
|
||||
&board_info_strix_x670e_e_gaming_wifi),
|
||||
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X670E-E GAMING WIFI",
|
||||
&board_info_strix_x670e_e_gaming_wifi),
|
||||
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X670E-I GAMING WIFI",
|
||||
|
|
@ -980,6 +982,8 @@ static const struct dmi_system_id dmi_table[] = {
|
|||
&board_info_strix_z390_f_gaming),
|
||||
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX Z390-F GAMING",
|
||||
&board_info_strix_z390_f_gaming),
|
||||
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX Z490-A GAMING",
|
||||
&board_info_strix_z490_f_gaming),
|
||||
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX Z490-F GAMING",
|
||||
&board_info_strix_z490_f_gaming),
|
||||
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX Z690-A GAMING WIFI D4",
|
||||
|
|
|
|||
|
|
@ -141,8 +141,8 @@ typedef ssize_t (*sysfs_show_func)(struct device *dev,
|
|||
struct device_attribute *attr, char *buf);
|
||||
|
||||
static const struct acpi_device_id atk_ids[] = {
|
||||
{ATK_HID, 0},
|
||||
{"", 0},
|
||||
{ .id = ATK_HID },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, atk_ids);
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#define USB_PRODUCT_ID_RYUJIN_III_EXTREME 0x1bcb
|
||||
#define USB_PRODUCT_ID_RYUJIN_III_EVA 0x1ade
|
||||
#define USB_PRODUCT_ID_RYUJIN_III_WHITE 0x1ada
|
||||
#define USB_PRODUCT_ID_RYUJIN_III 0x1aa2
|
||||
|
||||
struct rog_ryujin_device_info {
|
||||
u8 temp_offset;
|
||||
|
|
@ -613,6 +614,8 @@ static const struct hid_device_id rog_ryujin_table[] = {
|
|||
.driver_data = (kernel_ulong_t)&rog_ryujin_iii_info },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUS_ROG, USB_PRODUCT_ID_RYUJIN_III_WHITE),
|
||||
.driver_data = (kernel_ulong_t)&rog_ryujin_iii_info },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUS_ROG, USB_PRODUCT_ID_RYUJIN_III),
|
||||
.driver_data = (kernel_ulong_t)&rog_ryujin_iii_info },
|
||||
{ }
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -573,7 +573,7 @@ static int asus_wmi_configure_sensor_setup(struct device *dev,
|
|||
|
||||
sensor_data->wmi.info[type] = devm_kcalloc(dev,
|
||||
nr_count[type],
|
||||
sizeof(*sensor_data->wmi.info),
|
||||
sizeof(*sensor_data->wmi.info[type]),
|
||||
GFP_KERNEL);
|
||||
if (!sensor_data->wmi.info[type])
|
||||
return -ENOMEM;
|
||||
|
|
|
|||
416
drivers/hwmon/axiado-pwm-fan.c
Normal file
416
drivers/hwmon/axiado-pwm-fan.c
Normal file
|
|
@ -0,0 +1,416 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (C) 2021-2026 Axiado Corporation.
|
||||
*/
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/pwm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#define AX_TACH_PWM_VAL_MAX 255
|
||||
|
||||
/* TACH Register offsets */
|
||||
#define AX_TACH_CTRL_REG 0x00
|
||||
#define AX_TACH_TIMER_COUNT_REG 0x04
|
||||
#define AX_TACH_COUNT_REG 0x08
|
||||
#define AX_TACH_INT_STATUS_REG 0x0c
|
||||
|
||||
#define AX_TACH_CTRL_ENABLE BIT(0)
|
||||
#define AX_TACH_CTRL_INT_ENABLE BIT(1)
|
||||
|
||||
#define AX_TACH_INT_PENDING BIT(0)
|
||||
|
||||
struct axiado_pwm_fan_tach {
|
||||
int irq;
|
||||
u32 pulses_per_revolution;
|
||||
u32 timer_count;
|
||||
u32 count;
|
||||
};
|
||||
|
||||
struct axiado_pwm_fan_ctx {
|
||||
/* Serializes PWM updates with PM and shutdown operations. */
|
||||
struct mutex pwm_lock;
|
||||
/* Protects tachometer count updated from interrupt context. */
|
||||
spinlock_t tach_lock;
|
||||
struct pwm_device *pwm;
|
||||
struct pwm_state pwm_state;
|
||||
void __iomem *tach_base;
|
||||
struct axiado_pwm_fan_tach tach;
|
||||
unsigned int pwm_value;
|
||||
};
|
||||
|
||||
static const struct hwmon_channel_info * const pwm_fan_info[] = {
|
||||
HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT),
|
||||
HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT),
|
||||
NULL
|
||||
};
|
||||
|
||||
static irqreturn_t axiado_tach_irq_handler(int irq, void *dev)
|
||||
{
|
||||
struct axiado_pwm_fan_ctx *ctx = dev;
|
||||
u32 status;
|
||||
|
||||
status = ioread32(ctx->tach_base + AX_TACH_INT_STATUS_REG);
|
||||
|
||||
if (!(status & AX_TACH_INT_PENDING))
|
||||
return IRQ_NONE;
|
||||
|
||||
scoped_guard(spinlock_irqsave, &ctx->tach_lock) {
|
||||
ctx->tach.count = ioread32(ctx->tach_base + AX_TACH_COUNT_REG);
|
||||
}
|
||||
|
||||
iowrite32(AX_TACH_INT_PENDING, ctx->tach_base + AX_TACH_INT_STATUS_REG);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int axiado_set_pwm(struct axiado_pwm_fan_ctx *ctx, unsigned int pwm)
|
||||
{
|
||||
struct pwm_state state;
|
||||
int ret;
|
||||
|
||||
guard(mutex)(&ctx->pwm_lock);
|
||||
|
||||
if (ctx->pwm_value == pwm)
|
||||
return 0;
|
||||
|
||||
state = ctx->pwm_state;
|
||||
state.duty_cycle = DIV_ROUND_UP_ULL((u64)pwm * state.period,
|
||||
AX_TACH_PWM_VAL_MAX);
|
||||
/*
|
||||
* Keep the PWM enabled even at zero duty cycle so the output stays
|
||||
* driven low instead of being left floating by the PWM hardware.
|
||||
*/
|
||||
state.enabled = true;
|
||||
|
||||
ret = pwm_apply_might_sleep(ctx->pwm, &state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ctx->pwm_state = state;
|
||||
ctx->pwm_value = pwm;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int axiado_pwm_fan_write(struct device *dev,
|
||||
enum hwmon_sensor_types type, u32 attr,
|
||||
int channel, long val)
|
||||
{
|
||||
struct axiado_pwm_fan_ctx *ctx = dev_get_drvdata(dev);
|
||||
|
||||
if (type != hwmon_pwm || attr != hwmon_pwm_input)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (val < 0 || val > AX_TACH_PWM_VAL_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
return axiado_set_pwm(ctx, val);
|
||||
}
|
||||
|
||||
static unsigned int axiado_tach_get_rpm(struct axiado_pwm_fan_ctx *ctx)
|
||||
{
|
||||
u32 pulses_per_revolution = ctx->tach.pulses_per_revolution;
|
||||
u64 pulses, rpm;
|
||||
|
||||
scoped_guard(spinlock_irqsave, &ctx->tach_lock)
|
||||
pulses = ctx->tach.count;
|
||||
|
||||
if (!pulses_per_revolution)
|
||||
return 0;
|
||||
|
||||
rpm = pulses * 60;
|
||||
do_div(rpm, pulses_per_revolution);
|
||||
|
||||
return rpm;
|
||||
}
|
||||
|
||||
static int axiado_pwm_fan_read(struct device *dev, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel, long *val)
|
||||
{
|
||||
struct axiado_pwm_fan_ctx *ctx = dev_get_drvdata(dev);
|
||||
|
||||
switch (type) {
|
||||
case hwmon_pwm:
|
||||
if (attr != hwmon_pwm_input)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
*val = ctx->pwm_value;
|
||||
|
||||
return 0;
|
||||
|
||||
case hwmon_fan:
|
||||
if (attr != hwmon_fan_input)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
*val = axiado_tach_get_rpm(ctx);
|
||||
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
static umode_t axiado_pwm_fan_is_visible(const void *data,
|
||||
enum hwmon_sensor_types type, u32 attr,
|
||||
int channel)
|
||||
{
|
||||
if (type == hwmon_fan && attr == hwmon_fan_input)
|
||||
return 0444;
|
||||
|
||||
if (type == hwmon_pwm && attr == hwmon_pwm_input)
|
||||
return 0644;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct hwmon_ops pwm_fan_hwmon_ops = {
|
||||
.is_visible = axiado_pwm_fan_is_visible,
|
||||
.read = axiado_pwm_fan_read,
|
||||
.write = axiado_pwm_fan_write,
|
||||
};
|
||||
|
||||
static const struct hwmon_chip_info pwm_fan_chip_info = {
|
||||
.ops = &pwm_fan_hwmon_ops,
|
||||
.info = pwm_fan_info,
|
||||
};
|
||||
|
||||
static int axiado_pwm_apply_disabled(struct axiado_pwm_fan_ctx *ctx)
|
||||
{
|
||||
struct pwm_state state;
|
||||
|
||||
guard(mutex)(&ctx->pwm_lock);
|
||||
|
||||
if (!ctx->pwm_state.enabled)
|
||||
return 0;
|
||||
|
||||
state = ctx->pwm_state;
|
||||
state.duty_cycle = 0;
|
||||
state.enabled = false;
|
||||
|
||||
return pwm_apply_might_sleep(ctx->pwm, &state);
|
||||
}
|
||||
|
||||
static void axiado_pwm_disable(void *data)
|
||||
{
|
||||
struct axiado_pwm_fan_ctx *ctx = data;
|
||||
|
||||
axiado_pwm_apply_disabled(ctx);
|
||||
}
|
||||
|
||||
static void axiado_tach_enable(struct axiado_pwm_fan_ctx *ctx)
|
||||
{
|
||||
iowrite32(AX_TACH_INT_PENDING,
|
||||
ctx->tach_base + AX_TACH_INT_STATUS_REG);
|
||||
iowrite32(ctx->tach.timer_count,
|
||||
ctx->tach_base + AX_TACH_TIMER_COUNT_REG);
|
||||
iowrite32(AX_TACH_CTRL_ENABLE | AX_TACH_CTRL_INT_ENABLE,
|
||||
ctx->tach_base + AX_TACH_CTRL_REG);
|
||||
}
|
||||
|
||||
static void axiado_tach_disable(void *data)
|
||||
{
|
||||
struct axiado_pwm_fan_ctx *ctx = data;
|
||||
|
||||
iowrite32(0, ctx->tach_base + AX_TACH_CTRL_REG);
|
||||
iowrite32(AX_TACH_INT_PENDING,
|
||||
ctx->tach_base + AX_TACH_INT_STATUS_REG);
|
||||
}
|
||||
|
||||
static int axiado_pwm_fan_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct fwnode_handle *fan_node;
|
||||
struct axiado_pwm_fan_ctx *ctx;
|
||||
unsigned long tach_clk_rate;
|
||||
struct device *hwmon;
|
||||
struct clk *tach_clk;
|
||||
int ret;
|
||||
|
||||
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
|
||||
if (!ctx)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&ctx->pwm_lock);
|
||||
spin_lock_init(&ctx->tach_lock);
|
||||
|
||||
ctx->tach_base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(ctx->tach_base))
|
||||
return PTR_ERR(ctx->tach_base);
|
||||
|
||||
fan_node = device_get_named_child_node(dev, "fan");
|
||||
if (!fan_node)
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"Missing fan child node\n");
|
||||
|
||||
ctx->pwm = devm_fwnode_pwm_get(dev, fan_node, NULL);
|
||||
if (IS_ERR(ctx->pwm)) {
|
||||
ret = dev_err_probe(dev, PTR_ERR(ctx->pwm),
|
||||
"Could not get PWM\n");
|
||||
goto put_fan_node;
|
||||
}
|
||||
|
||||
ctx->tach.pulses_per_revolution = 2;
|
||||
|
||||
ret = fwnode_property_read_u32(fan_node, "pulses-per-revolution",
|
||||
&ctx->tach.pulses_per_revolution);
|
||||
if (ret && ret != -EINVAL) {
|
||||
ret = dev_err_probe(dev, ret,
|
||||
"Failed to read pulses-per-revolution\n");
|
||||
goto put_fan_node;
|
||||
}
|
||||
|
||||
if (!ctx->tach.pulses_per_revolution) {
|
||||
ret = dev_err_probe(dev, -EINVAL,
|
||||
"pulses-per-revolution cannot be zero\n");
|
||||
goto put_fan_node;
|
||||
}
|
||||
|
||||
fwnode_handle_put(fan_node);
|
||||
|
||||
platform_set_drvdata(pdev, ctx);
|
||||
|
||||
pwm_init_state(ctx->pwm, &ctx->pwm_state);
|
||||
|
||||
if (!ctx->pwm_state.period)
|
||||
return dev_err_probe(dev, -EINVAL, "PWM period is zero\n");
|
||||
|
||||
tach_clk = devm_clk_get_enabled(dev, NULL);
|
||||
if (IS_ERR(tach_clk))
|
||||
return dev_err_probe(dev, PTR_ERR(tach_clk),
|
||||
"Failed to get tachometer clock\n");
|
||||
|
||||
tach_clk_rate = clk_get_rate(tach_clk);
|
||||
if (!tach_clk_rate || tach_clk_rate > U32_MAX)
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"Invalid tachometer clock rate: %lu\n",
|
||||
tach_clk_rate);
|
||||
|
||||
ctx->tach.timer_count = tach_clk_rate;
|
||||
|
||||
ctx->tach.irq = platform_get_irq(pdev, 0);
|
||||
if (ctx->tach.irq < 0)
|
||||
return dev_err_probe(dev, ctx->tach.irq,
|
||||
"Failed to get tachometer IRQ\n");
|
||||
|
||||
ret = devm_request_irq(dev, ctx->tach.irq, axiado_tach_irq_handler, 0,
|
||||
dev_name(dev), ctx);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to request tach IRQ\n");
|
||||
|
||||
ret = devm_add_action_or_reset(dev, axiado_tach_disable, ctx);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_dbg(dev, "Fan tachometer: irq=%d, pulses_per_revolution=%u\n",
|
||||
ctx->tach.irq, ctx->tach.pulses_per_revolution);
|
||||
|
||||
axiado_tach_enable(ctx);
|
||||
|
||||
ret = axiado_set_pwm(ctx, AX_TACH_PWM_VAL_MAX);
|
||||
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to configure PWM\n");
|
||||
|
||||
ret = devm_add_action_or_reset(dev, axiado_pwm_disable, ctx);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"Failed to add PWM disable action\n");
|
||||
|
||||
hwmon = devm_hwmon_device_register_with_info(dev, "axpwmfan", ctx,
|
||||
&pwm_fan_chip_info, NULL);
|
||||
if (IS_ERR(hwmon))
|
||||
return dev_err_probe(dev, PTR_ERR(hwmon),
|
||||
"Failed to register hwmon device\n");
|
||||
|
||||
return 0;
|
||||
|
||||
put_fan_node:
|
||||
fwnode_handle_put(fan_node);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int axiado_pwm_fan_disable(struct device *dev)
|
||||
{
|
||||
struct axiado_pwm_fan_ctx *ctx = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = axiado_pwm_apply_disabled(ctx);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
axiado_tach_disable(ctx);
|
||||
synchronize_irq(ctx->tach.irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void axiado_pwm_fan_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
struct axiado_pwm_fan_ctx *ctx = platform_get_drvdata(pdev);
|
||||
|
||||
/* Best effort during shutdown. */
|
||||
axiado_pwm_apply_disabled(ctx);
|
||||
|
||||
axiado_tach_disable(ctx);
|
||||
synchronize_irq(ctx->tach.irq);
|
||||
}
|
||||
|
||||
static int axiado_pwm_fan_suspend(struct device *dev)
|
||||
{
|
||||
return axiado_pwm_fan_disable(dev);
|
||||
}
|
||||
|
||||
static int axiado_pwm_fan_resume(struct device *dev)
|
||||
{
|
||||
struct axiado_pwm_fan_ctx *ctx = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
axiado_tach_enable(ctx);
|
||||
|
||||
scoped_guard(mutex, &ctx->pwm_lock)
|
||||
ret = pwm_apply_might_sleep(ctx->pwm, &ctx->pwm_state);
|
||||
|
||||
if (ret)
|
||||
axiado_tach_disable(ctx);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(axiado_pwm_fan_pm,
|
||||
axiado_pwm_fan_suspend,
|
||||
axiado_pwm_fan_resume);
|
||||
|
||||
static const struct of_device_id axiado_pwm_fan_match[] = {
|
||||
{ .compatible = "axiado,ax3000-pwm-fan" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, axiado_pwm_fan_match);
|
||||
|
||||
static struct platform_driver axiado_pwm_fan_driver = {
|
||||
.probe = axiado_pwm_fan_probe,
|
||||
.shutdown = axiado_pwm_fan_shutdown,
|
||||
.driver = {
|
||||
.name = "axiado-pwm-fan",
|
||||
.pm = pm_sleep_ptr(&axiado_pwm_fan_pm),
|
||||
.of_match_table = axiado_pwm_fan_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(axiado_pwm_fan_driver);
|
||||
|
||||
MODULE_AUTHOR("Axiado Corporation");
|
||||
MODULE_DESCRIPTION("Axiado PWM fan controller driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
275
drivers/hwmon/axiado-tsadc.c
Normal file
275
drivers/hwmon/axiado-tsadc.c
Normal file
|
|
@ -0,0 +1,275 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2021-2026 Axiado Corporation
|
||||
*
|
||||
* Driver for the Axiado on-chip temperature sensor (TSADC)
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
/* TSADC register offsets */
|
||||
#define AXIADO_TSADC_MODE 0x0000
|
||||
#define AXIADO_TSADC_FUNC_CTRL 0x0004
|
||||
#define AXIADO_TSADC_AVG_DOUT 0x000c
|
||||
#define AXIADO_TSADC_INTR_MASK 0x0010
|
||||
#define AXIADO_TSADC_ANA_REG 0x0020
|
||||
#define AXIADO_TSADC_SEL 0x0028
|
||||
#define AXIADO_TSADC_TSEN_EN 0x002c
|
||||
#define AXIADO_TSADC_EN 0x0030
|
||||
|
||||
/* TSADC mode register: continuous conversion */
|
||||
#define AXIADO_TSADC_MODE_CONT 0x2
|
||||
|
||||
/*
|
||||
* TSADC functional control register: bit 0 starts the conversion and
|
||||
* bits [31:16] specify the number of samples. Average over 16 samples.
|
||||
*/
|
||||
#define AXIADO_TSADC_FUNC_CTRL_START BIT(0)
|
||||
#define AXIADO_TSADC_FUNC_CTRL_NUM_SAMPLES GENMASK(31, 16)
|
||||
#define AXIADO_TSADC_FUNC_CTRL_CLR 0x0
|
||||
#define AXIADO_TSADC_FUNC_CTRL_SET \
|
||||
(FIELD_PREP(AXIADO_TSADC_FUNC_CTRL_NUM_SAMPLES, 16) | \
|
||||
AXIADO_TSADC_FUNC_CTRL_START)
|
||||
|
||||
/* TSADC average output register: 12-bit ADC result */
|
||||
#define AXIADO_TSADC_AVG_DOUT_MASK GENMASK(11, 0)
|
||||
|
||||
/* Interrupt mask bits */
|
||||
#define AXIADO_TSADC_INTR_BIST_DONE BIT(0)
|
||||
#define AXIADO_TSADC_INTR_LOOP_DONE BIT(1)
|
||||
#define AXIADO_TSADC_INTR_THRESHOLD BIT(2)
|
||||
#define AXIADO_TSADC_INTR_MASK_ALL \
|
||||
(AXIADO_TSADC_INTR_BIST_DONE | \
|
||||
AXIADO_TSADC_INTR_LOOP_DONE | \
|
||||
AXIADO_TSADC_INTR_THRESHOLD)
|
||||
|
||||
/* TSADC analog block configuration required for temperature measurement */
|
||||
#define AXIADO_TSADC_ANA_REG_INIT 0x6
|
||||
|
||||
/* TSADC SEL: select temperature measurement */
|
||||
#define AXIADO_TSADC_SEL_TEMP 0x00
|
||||
|
||||
/* TSADC EN / TSEN_EN field values */
|
||||
#define AXIADO_TSADC_DIS 0x00
|
||||
#define AXIADO_TSADC_ENA 0x01
|
||||
|
||||
struct axiado_tsadc {
|
||||
void __iomem *regs;
|
||||
};
|
||||
|
||||
/*
|
||||
* Lookup table: raw ADC output (12-bit) vs temperature in millidegrees
|
||||
* Celsius. Raw values decrease as temperature increases, from -40°C
|
||||
* to 125°C in 5°C steps.
|
||||
*/
|
||||
struct axiado_tsadc_point {
|
||||
u32 raw;
|
||||
int temp_mc;
|
||||
};
|
||||
|
||||
static const struct axiado_tsadc_point axiado_tsadc_table[] = {
|
||||
{ .raw = 2776, .temp_mc = -40000 },
|
||||
{ .raw = 2748, .temp_mc = -35000 },
|
||||
{ .raw = 2721, .temp_mc = -30000 },
|
||||
{ .raw = 2694, .temp_mc = -25000 },
|
||||
{ .raw = 2667, .temp_mc = -20000 },
|
||||
{ .raw = 2640, .temp_mc = -15000 },
|
||||
{ .raw = 2612, .temp_mc = -10000 },
|
||||
{ .raw = 2584, .temp_mc = -5000 },
|
||||
{ .raw = 2556, .temp_mc = 0 },
|
||||
{ .raw = 2528, .temp_mc = 5000 },
|
||||
{ .raw = 2500, .temp_mc = 10000 },
|
||||
{ .raw = 2472, .temp_mc = 15000 },
|
||||
{ .raw = 2444, .temp_mc = 20000 },
|
||||
{ .raw = 2416, .temp_mc = 25000 },
|
||||
{ .raw = 2389, .temp_mc = 30000 },
|
||||
{ .raw = 2362, .temp_mc = 35000 },
|
||||
{ .raw = 2335, .temp_mc = 40000 },
|
||||
{ .raw = 2307, .temp_mc = 45000 },
|
||||
{ .raw = 2279, .temp_mc = 50000 },
|
||||
{ .raw = 2251, .temp_mc = 55000 },
|
||||
{ .raw = 2223, .temp_mc = 60000 },
|
||||
{ .raw = 2195, .temp_mc = 65000 },
|
||||
{ .raw = 2167, .temp_mc = 70000 },
|
||||
{ .raw = 2138, .temp_mc = 75000 },
|
||||
{ .raw = 2110, .temp_mc = 80000 },
|
||||
{ .raw = 2081, .temp_mc = 85000 },
|
||||
{ .raw = 2052, .temp_mc = 90000 },
|
||||
{ .raw = 2024, .temp_mc = 95000 },
|
||||
{ .raw = 1996, .temp_mc = 100000 },
|
||||
{ .raw = 1967, .temp_mc = 105000 },
|
||||
{ .raw = 1938, .temp_mc = 110000 },
|
||||
{ .raw = 1909, .temp_mc = 115000 },
|
||||
{ .raw = 1880, .temp_mc = 120000 },
|
||||
{ .raw = 1852, .temp_mc = 125000 },
|
||||
};
|
||||
|
||||
/* Convert raw ADC code to millidegrees Celsius with linear interpolation. */
|
||||
static int axiado_tsadc_raw_to_mc(u32 raw, long *val)
|
||||
{
|
||||
int i, n = ARRAY_SIZE(axiado_tsadc_table);
|
||||
long num, denom;
|
||||
|
||||
if (raw >= axiado_tsadc_table[0].raw) {
|
||||
*val = axiado_tsadc_table[0].temp_mc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (raw <= axiado_tsadc_table[n - 1].raw) {
|
||||
*val = axiado_tsadc_table[n - 1].temp_mc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Find i such that axiado_tsadc_table[i].raw >= raw > axiado_tsadc_table[i+1].raw */
|
||||
for (i = 0; i < n - 1; i++) {
|
||||
if (raw >= axiado_tsadc_table[i + 1].raw)
|
||||
break;
|
||||
}
|
||||
|
||||
if (raw == axiado_tsadc_table[i].raw) {
|
||||
*val = axiado_tsadc_table[i].temp_mc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Linear interpolation within the interval */
|
||||
num = (long)(axiado_tsadc_table[i + 1].temp_mc - axiado_tsadc_table[i].temp_mc) *
|
||||
(axiado_tsadc_table[i].raw - raw);
|
||||
denom = axiado_tsadc_table[i].raw - axiado_tsadc_table[i + 1].raw;
|
||||
*val = axiado_tsadc_table[i].temp_mc + num / denom;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int axiado_tsadc_read(struct device *dev, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel, long *val)
|
||||
{
|
||||
struct axiado_tsadc *tsadc = dev_get_drvdata(dev);
|
||||
u32 raw;
|
||||
|
||||
if (type != hwmon_temp)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
switch (attr) {
|
||||
case hwmon_temp_input:
|
||||
if (ioread32(tsadc->regs + AXIADO_TSADC_SEL) != AXIADO_TSADC_SEL_TEMP ||
|
||||
ioread32(tsadc->regs + AXIADO_TSADC_EN) != AXIADO_TSADC_ENA)
|
||||
return -ENODATA;
|
||||
|
||||
raw = ioread32(tsadc->regs + AXIADO_TSADC_AVG_DOUT) &
|
||||
AXIADO_TSADC_AVG_DOUT_MASK;
|
||||
|
||||
return axiado_tsadc_raw_to_mc(raw, val);
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
static umode_t axiado_tsadc_is_visible(const void *data,
|
||||
enum hwmon_sensor_types type, u32 attr,
|
||||
int channel)
|
||||
{
|
||||
if (type == hwmon_temp) {
|
||||
switch (attr) {
|
||||
case hwmon_temp_input:
|
||||
return 0444;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct hwmon_channel_info * const axiado_tsadc_info[] = {
|
||||
HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct hwmon_ops axiado_tsadc_hwmon_ops = {
|
||||
.is_visible = axiado_tsadc_is_visible,
|
||||
.read = axiado_tsadc_read,
|
||||
};
|
||||
|
||||
static const struct hwmon_chip_info axiado_tsadc_chip_info = {
|
||||
.ops = &axiado_tsadc_hwmon_ops,
|
||||
.info = axiado_tsadc_info,
|
||||
};
|
||||
|
||||
static void axiado_tsadc_disable(void *data)
|
||||
{
|
||||
struct axiado_tsadc *tsadc = data;
|
||||
|
||||
iowrite32(AXIADO_TSADC_DIS, tsadc->regs + AXIADO_TSADC_TSEN_EN);
|
||||
iowrite32(AXIADO_TSADC_DIS, tsadc->regs + AXIADO_TSADC_EN);
|
||||
}
|
||||
|
||||
static int axiado_tsadc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct axiado_tsadc *tsadc;
|
||||
struct device *hwmon_dev;
|
||||
struct clk *refclk;
|
||||
int ret;
|
||||
|
||||
tsadc = devm_kzalloc(dev, sizeof(*tsadc), GFP_KERNEL);
|
||||
if (!tsadc)
|
||||
return -ENOMEM;
|
||||
|
||||
tsadc->regs = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(tsadc->regs))
|
||||
return dev_err_probe(dev, PTR_ERR(tsadc->regs),
|
||||
"failed to map registers\n");
|
||||
|
||||
refclk = devm_clk_get_enabled(dev, NULL);
|
||||
if (IS_ERR(refclk))
|
||||
return dev_err_probe(dev, PTR_ERR(refclk),
|
||||
"failed to enable reference clock\n");
|
||||
|
||||
/* Mask all interrupts before setup */
|
||||
iowrite32(AXIADO_TSADC_INTR_MASK_ALL, tsadc->regs + AXIADO_TSADC_INTR_MASK);
|
||||
|
||||
iowrite32(AXIADO_TSADC_ANA_REG_INIT, tsadc->regs + AXIADO_TSADC_ANA_REG);
|
||||
iowrite32(AXIADO_TSADC_ENA, tsadc->regs + AXIADO_TSADC_EN);
|
||||
iowrite32(AXIADO_TSADC_SEL_TEMP, tsadc->regs + AXIADO_TSADC_SEL);
|
||||
iowrite32(AXIADO_TSADC_MODE_CONT, tsadc->regs + AXIADO_TSADC_MODE);
|
||||
iowrite32(AXIADO_TSADC_FUNC_CTRL_CLR, tsadc->regs + AXIADO_TSADC_FUNC_CTRL);
|
||||
iowrite32(AXIADO_TSADC_FUNC_CTRL_SET, tsadc->regs + AXIADO_TSADC_FUNC_CTRL);
|
||||
|
||||
/* Enable the bandgap circuit once the block is fully configured */
|
||||
iowrite32(AXIADO_TSADC_ENA, tsadc->regs + AXIADO_TSADC_TSEN_EN);
|
||||
|
||||
ret = devm_add_action_or_reset(dev, axiado_tsadc_disable, tsadc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
hwmon_dev = devm_hwmon_device_register_with_info(dev, "axiado_tsadc",
|
||||
tsadc,
|
||||
&axiado_tsadc_chip_info,
|
||||
NULL);
|
||||
return PTR_ERR_OR_ZERO(hwmon_dev);
|
||||
}
|
||||
|
||||
static const struct of_device_id axiado_tsadc_match[] = {
|
||||
{ .compatible = "axiado,ax3000-tsadc" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, axiado_tsadc_match);
|
||||
|
||||
static struct platform_driver axiado_tsadc_driver = {
|
||||
.driver = {
|
||||
.name = "axiado-tsadc",
|
||||
.of_match_table = axiado_tsadc_match,
|
||||
},
|
||||
.probe = axiado_tsadc_probe,
|
||||
};
|
||||
module_platform_driver(axiado_tsadc_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Axiado TSADC temperature sensor driver");
|
||||
MODULE_AUTHOR("Axiado Corporation");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -60,6 +60,8 @@ enum coretemp_attr_index {
|
|||
|
||||
/*
|
||||
* Per-Core Temperature Data
|
||||
* @temp_valid: Whether the CPU reported @temp as valid. @temp is not
|
||||
* reported when this is false.
|
||||
* @tjmax: The static tjmax value when tjmax cannot be retrieved from
|
||||
* IA32_TEMPERATURE_TARGET MSR.
|
||||
* @last_updated: The time when the current temperature value was updated
|
||||
|
|
@ -72,6 +74,7 @@ enum coretemp_attr_index {
|
|||
*/
|
||||
struct temp_data {
|
||||
int temp;
|
||||
bool temp_valid;
|
||||
int tjmax;
|
||||
unsigned long last_updated;
|
||||
unsigned int cpu;
|
||||
|
|
@ -398,26 +401,32 @@ static ssize_t show_temp(struct device *dev,
|
|||
{
|
||||
struct msr val;
|
||||
struct temp_data *tdata = container_of(devattr, struct temp_data, sd_attrs[ATTR_TEMP]);
|
||||
int tjmax;
|
||||
int tjmax, temp;
|
||||
bool valid;
|
||||
|
||||
mutex_lock(&tdata->update_lock);
|
||||
|
||||
tjmax = get_tjmax(tdata, dev);
|
||||
/* Check whether the time interval has elapsed */
|
||||
if (time_after(jiffies, tdata->last_updated + HZ)) {
|
||||
tjmax = get_tjmax(tdata, dev);
|
||||
rdmsrq_on_cpu(tdata->cpu, tdata->status_reg, &val.q);
|
||||
/*
|
||||
* Ignore the valid bit. In all observed cases the register
|
||||
* value is either low or zero if the valid bit is 0.
|
||||
* Return it instead of reporting an error which doesn't
|
||||
* really help at all.
|
||||
* Bit 31 indicates whether the reading is valid. Some CPUs
|
||||
* clear it while the temperature is too low to be measured.
|
||||
* Such a reading is not a temperature. Do not report it.
|
||||
*/
|
||||
tdata->temp_valid = (val.l >> 31) & 1;
|
||||
tdata->temp = tjmax - ((val.l >> 16) & 0xff) * 1000;
|
||||
tdata->last_updated = jiffies;
|
||||
}
|
||||
|
||||
valid = tdata->temp_valid;
|
||||
temp = tdata->temp;
|
||||
mutex_unlock(&tdata->update_lock);
|
||||
return sprintf(buf, "%d\n", tdata->temp);
|
||||
|
||||
if (!valid)
|
||||
return -ENODATA;
|
||||
return sprintf(buf, "%d\n", temp);
|
||||
}
|
||||
|
||||
static int create_core_attrs(struct temp_data *tdata, struct device *dev)
|
||||
|
|
@ -511,6 +520,11 @@ init_temp_data(struct platform_data *pdata, unsigned int cpu, int pkg_flag)
|
|||
tdata->cpu = cpu;
|
||||
tdata->cpu_core_id = topology_core_id(cpu);
|
||||
tdata->attr_size = MAX_CORE_ATTRS;
|
||||
/*
|
||||
* A zero timestamp does not look stale on 32-bit, where jiffies
|
||||
* starts just short of wrapping. Backdate it instead.
|
||||
*/
|
||||
tdata->last_updated = jiffies - HZ - 1;
|
||||
mutex_init(&tdata->update_lock);
|
||||
return tdata;
|
||||
}
|
||||
|
|
@ -554,9 +568,6 @@ static int create_core_data(struct platform_device *pdev, unsigned int cpu,
|
|||
u64 val;
|
||||
int err;
|
||||
|
||||
if (!housekeeping_cpu(cpu, HK_TYPE_MISC))
|
||||
return 0;
|
||||
|
||||
tdata = init_temp_data(pdata, cpu, pkg_flag);
|
||||
if (!tdata)
|
||||
return -ENOMEM;
|
||||
|
|
@ -676,6 +687,14 @@ static int coretemp_cpu_online(unsigned int cpu)
|
|||
if (!cpu_has(c, X86_FEATURE_DTHERM))
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* Keep isolated CPUs out of pdata->cpumask. Besides tracking the CPUs
|
||||
* available to this driver, the mask is used to select the CPU on which
|
||||
* to read the temperature MSRs.
|
||||
*/
|
||||
if (!housekeeping_cpu(cpu, HK_TYPE_MISC))
|
||||
return 0;
|
||||
|
||||
pdata = platform_get_drvdata(pdev);
|
||||
if (!pdata->hwmon_dev) {
|
||||
struct device *hwmon;
|
||||
|
|
@ -705,8 +724,8 @@ static int coretemp_cpu_online(unsigned int cpu)
|
|||
}
|
||||
|
||||
/*
|
||||
* Check whether a thread sibling is already online. If not add the
|
||||
* interface for this CPU core.
|
||||
* Check whether a housekeeping thread sibling is already online. If
|
||||
* not, add the interface for this CPU core.
|
||||
*/
|
||||
if (!cpumask_intersects(&pdata->cpumask, topology_sibling_cpumask(cpu)))
|
||||
coretemp_add_core(pdev, cpu, 0);
|
||||
|
|
@ -736,9 +755,9 @@ static int coretemp_cpu_offline(unsigned int cpu)
|
|||
cpumask_clear_cpu(cpu, &pd->cpumask);
|
||||
|
||||
/*
|
||||
* If this is the last thread sibling, remove the CPU core
|
||||
* interface, If there is still a sibling online, transfer the
|
||||
* target cpu of that core interface to it.
|
||||
* If this is the last housekeeping thread sibling, remove the CPU core
|
||||
* interface. If there is still a housekeeping sibling online, transfer
|
||||
* the target CPU of that core interface to it.
|
||||
*/
|
||||
target = cpumask_any_and(&pd->cpumask, topology_sibling_cpumask(cpu));
|
||||
if (target >= nr_cpu_ids) {
|
||||
|
|
@ -750,7 +769,8 @@ static int coretemp_cpu_offline(unsigned int cpu)
|
|||
}
|
||||
|
||||
/*
|
||||
* If all cores in this pkg are offline, remove the interface.
|
||||
* If all housekeeping cores in this pkg are offline, remove the
|
||||
* interface.
|
||||
*/
|
||||
tdata = get_temp_data(pd, -1);
|
||||
if (cpumask_empty(&pd->cpumask)) {
|
||||
|
|
@ -763,7 +783,7 @@ static int coretemp_cpu_offline(unsigned int cpu)
|
|||
|
||||
/*
|
||||
* Check whether this core is the target for the package
|
||||
* interface. We need to assign it to some other cpu.
|
||||
* interface. We need to assign it to some other housekeeping CPU.
|
||||
*/
|
||||
if (tdata && tdata->cpu == cpu) {
|
||||
target = cpumask_first(&pd->cpumask);
|
||||
|
|
|
|||
|
|
@ -135,9 +135,9 @@ static int send_usb_cmd(struct ccp_device *ccp, u8 command, u8 byte1, u8 byte2,
|
|||
* the raw event parsing and marked the ccp->wait_input_report
|
||||
* completion as done.
|
||||
*/
|
||||
spin_lock_bh(&ccp->wait_input_report_lock);
|
||||
spin_lock_irq(&ccp->wait_input_report_lock);
|
||||
reinit_completion(&ccp->wait_input_report);
|
||||
spin_unlock_bh(&ccp->wait_input_report_lock);
|
||||
spin_unlock_irq(&ccp->wait_input_report_lock);
|
||||
|
||||
ret = hid_hw_output_report(ccp->hdev, ccp->cmd_buffer, OUT_BUFFER_SIZE);
|
||||
if (ret < 0)
|
||||
|
|
@ -156,15 +156,16 @@ static int send_usb_cmd(struct ccp_device *ccp, u8 command, u8 byte1, u8 byte2,
|
|||
static int ccp_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size)
|
||||
{
|
||||
struct ccp_device *ccp = hid_get_drvdata(hdev);
|
||||
unsigned long flags;
|
||||
|
||||
/* only copy buffer when requested */
|
||||
spin_lock(&ccp->wait_input_report_lock);
|
||||
spin_lock_irqsave(&ccp->wait_input_report_lock, flags);
|
||||
if (!completion_done(&ccp->wait_input_report)) {
|
||||
memcpy(ccp->buffer, data, min(IN_BUFFER_SIZE, size));
|
||||
ccp->buffer_recv_size = size;
|
||||
complete_all(&ccp->wait_input_report);
|
||||
}
|
||||
spin_unlock(&ccp->wait_input_report_lock);
|
||||
spin_unlock_irqrestore(&ccp->wait_input_report_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1543,6 +1543,14 @@ static const struct dmi_system_id i8k_whitelist_fan_control[] __initconst = {
|
|||
},
|
||||
.driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3],
|
||||
},
|
||||
{
|
||||
.ident = "Dell Latitude 5420",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Latitude 5420"),
|
||||
},
|
||||
.driver_data = (void *)&i8k_fan_control_data[I8K_FAN_30A3_31A3],
|
||||
},
|
||||
{
|
||||
.ident = "Dell Latitude 5480",
|
||||
.matches = {
|
||||
|
|
@ -1631,6 +1639,22 @@ static const struct dmi_system_id i8k_whitelist_fan_control[] __initconst = {
|
|||
},
|
||||
.driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3],
|
||||
},
|
||||
{
|
||||
.ident = "Dell Precision 3650 Tower",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Precision 3650 Tower"),
|
||||
},
|
||||
.driver_data = (void *)&i8k_fan_control_data[I8K_FAN_30A3_31A3],
|
||||
},
|
||||
{
|
||||
.ident = "Dell Optiplex 7090",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "OptiPlex 7090"),
|
||||
},
|
||||
.driver_data = (void *)&i8k_fan_control_data[I8K_FAN_30A3_31A3],
|
||||
},
|
||||
{
|
||||
.ident = "Dell XPS 9315",
|
||||
.matches = {
|
||||
|
|
@ -1655,6 +1679,14 @@ static const struct dmi_system_id i8k_whitelist_fan_control[] __initconst = {
|
|||
},
|
||||
.driver_data = (void *)&i8k_fan_control_data[I8K_FAN_30A3_31A3],
|
||||
},
|
||||
{
|
||||
.ident = "Dell 16 DC16250",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Dell 16 DC16250"),
|
||||
},
|
||||
.driver_data = (void *)&i8k_fan_control_data[I8K_FAN_30A3_31A3],
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ static const u8 REG_TEMP_MAX[4] = { 0x34, 0x30, 0x31, 0x32 };
|
|||
#define REG_PRODUCT_ID 0xfd
|
||||
#define REG_MFG_ID 0xfe
|
||||
|
||||
/* equation 4 from datasheet: rpm = (3932160 * multipler) / count */
|
||||
/* equation 4 from datasheet: rpm = (3932160 * multiplier) / count */
|
||||
#define FAN_RPM_FACTOR 3932160
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@
|
|||
#include <linux/miscdevice.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/kref.h>
|
||||
#include <linux/stringify.h>
|
||||
|
||||
/* Addresses to scan */
|
||||
static const unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END };
|
||||
|
|
@ -43,7 +44,7 @@ static const unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END };
|
|||
static bool nowayout = WATCHDOG_NOWAYOUT;
|
||||
module_param(nowayout, bool, 0);
|
||||
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
|
||||
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
|
||||
__stringify(WATCHDOG_NOWAYOUT) ")");
|
||||
|
||||
enum chips { fscpos, fscher, fscscy, fschrc, fschmd, fschds, fscsyl };
|
||||
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ enum WATCHDOG_RESOLUTION {
|
|||
|
||||
struct fts_data {
|
||||
struct i2c_client *client;
|
||||
struct device *hwmon_dev;
|
||||
unsigned long last_updated; /* in jiffies */
|
||||
struct watchdog_device wdd;
|
||||
enum WATCHDOG_RESOLUTION resolution;
|
||||
|
|
@ -240,6 +241,8 @@ static int fts_wd_set_timeout(struct watchdog_device *wdd, unsigned int timeout)
|
|||
resolution = minutes;
|
||||
}
|
||||
|
||||
guard(hwmon_lock)(data->hwmon_dev);
|
||||
|
||||
ret = fts_wd_set_resolution(data, resolution);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
|
@ -252,6 +255,8 @@ static int fts_wd_start(struct watchdog_device *wdd)
|
|||
{
|
||||
struct fts_data *data = watchdog_get_drvdata(wdd);
|
||||
|
||||
guard(hwmon_lock)(data->hwmon_dev);
|
||||
|
||||
return fts_write_byte(data->client, FTS_WATCHDOG_TIME_PRESET,
|
||||
wdd->timeout / (u8)data->resolution);
|
||||
}
|
||||
|
|
@ -261,6 +266,9 @@ static int fts_wd_stop(struct watchdog_device *wdd)
|
|||
struct fts_data *data;
|
||||
|
||||
data = watchdog_get_drvdata(wdd);
|
||||
|
||||
guard(hwmon_lock)(data->hwmon_dev);
|
||||
|
||||
return fts_write_byte(data->client, FTS_WATCHDOG_TIME_PRESET, 0);
|
||||
}
|
||||
|
||||
|
|
@ -276,11 +284,11 @@ static const struct watchdog_ops fts_wd_ops = {
|
|||
.set_timeout = fts_wd_set_timeout,
|
||||
};
|
||||
|
||||
static int fts_watchdog_init(struct fts_data *data)
|
||||
static int fts_watchdog_read_state(struct fts_data *data)
|
||||
{
|
||||
int timeout, ret;
|
||||
|
||||
watchdog_set_drvdata(&data->wdd, data);
|
||||
guard(hwmon_lock)(data->hwmon_dev);
|
||||
|
||||
timeout = fts_read_byte(data->client, FTS_WATCHDOG_TIME_PRESET);
|
||||
if (timeout < 0)
|
||||
|
|
@ -302,6 +310,19 @@ static int fts_watchdog_init(struct fts_data *data)
|
|||
set_bit(WDOG_HW_RUNNING, &data->wdd.status);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fts_watchdog_init(struct fts_data *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
watchdog_set_drvdata(&data->wdd, data);
|
||||
|
||||
ret = fts_watchdog_read_state(data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Register our watchdog part */
|
||||
data->wdd.info = &fts_wd_info;
|
||||
data->wdd.ops = &fts_wd_ops;
|
||||
|
|
@ -629,6 +650,9 @@ static int fts_probe(struct i2c_client *client)
|
|||
if (IS_ERR(hwmon_dev))
|
||||
return PTR_ERR(hwmon_dev);
|
||||
|
||||
/* The watchdog code takes its lock */
|
||||
data->hwmon_dev = hwmon_dev;
|
||||
|
||||
err = fts_watchdog_init(data);
|
||||
if (err)
|
||||
return err;
|
||||
|
|
|
|||
|
|
@ -257,6 +257,9 @@ static int waterforce_raw_event(struct hid_device *hdev, struct hid_report *repo
|
|||
{
|
||||
struct waterforce_data *priv = hid_get_drvdata(hdev);
|
||||
|
||||
if (size <= FIRMWARE_VER_START_OFFSET_2)
|
||||
return 0;
|
||||
|
||||
if (data[0] == get_firmware_ver_cmd[0] && data[1] == get_firmware_ver_cmd[1]) {
|
||||
/* Received a firmware version report */
|
||||
priv->firmware_version =
|
||||
|
|
@ -269,6 +272,8 @@ static int waterforce_raw_event(struct hid_device *hdev, struct hid_report *repo
|
|||
|
||||
if (data[0] != get_status_cmd[0] || data[1] != get_status_cmd[1])
|
||||
return 0;
|
||||
if (size <= WATERFORCE_TEMP_SENSOR)
|
||||
return 0;
|
||||
|
||||
priv->temp_input[0] = data[WATERFORCE_TEMP_SENSOR] * 1000;
|
||||
priv->speed_input[0] = get_unaligned_le16(data + WATERFORCE_FAN_SPEED);
|
||||
|
|
|
|||
|
|
@ -168,7 +168,7 @@ static ssize_t hih6130_temperature_show(struct device *dev,
|
|||
ret = hih6130_update_measurements(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return sprintf(buf, "%d\n", hih6130->temperature);
|
||||
return sysfs_emit(buf, "%d\n", hih6130->temperature);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -189,7 +189,7 @@ static ssize_t hih6130_humidity_show(struct device *dev,
|
|||
ret = hih6130_update_measurements(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return sprintf(buf, "%d\n", hih6130->humidity);
|
||||
return sysfs_emit(buf, "%d\n", hih6130->humidity);
|
||||
}
|
||||
|
||||
/* sysfs attributes */
|
||||
|
|
|
|||
178
drivers/hwmon/honor-fmi.c
Normal file
178
drivers/hwmon/honor-fmi.c
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Read-only fan monitoring for the HONOR FMI-XX.
|
||||
*
|
||||
* The firmware-provided \GFNS ACPI method accepts a three-byte buffer.
|
||||
* Byte 2 selects fan 0 or 1. It returns a status byte followed by a
|
||||
* little-endian 16-bit fan speed in RPM. The method owns all Embedded
|
||||
* Controller access and serialization; this driver deliberately exposes no
|
||||
* fan control interface.
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#define HONOR_FMI_GFNS_RESULT_SIZE 3
|
||||
|
||||
struct honor_fmi_data {
|
||||
acpi_handle gfns;
|
||||
};
|
||||
|
||||
static const struct dmi_system_id honor_fmi_dmi_table[] = {
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "HONOR"),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "FMI-XX"),
|
||||
},
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(dmi, honor_fmi_dmi_table);
|
||||
|
||||
static int honor_fmi_read_rpm(struct honor_fmi_data *data, int channel,
|
||||
long *rpm)
|
||||
{
|
||||
union acpi_object input = {
|
||||
.buffer = {
|
||||
.type = ACPI_TYPE_BUFFER,
|
||||
.length = 3,
|
||||
},
|
||||
};
|
||||
struct acpi_object_list arguments = {
|
||||
.count = 1,
|
||||
.pointer = &input,
|
||||
};
|
||||
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object *result;
|
||||
u8 input_bytes[3] = { 0, 0, channel };
|
||||
acpi_status status;
|
||||
int ret = 0;
|
||||
|
||||
input.buffer.pointer = input_bytes;
|
||||
|
||||
status = acpi_evaluate_object(data->gfns, NULL, &arguments, &output);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
result = output.pointer;
|
||||
if (!result || result->type != ACPI_TYPE_BUFFER ||
|
||||
result->buffer.length < HONOR_FMI_GFNS_RESULT_SIZE) {
|
||||
ret = -EPROTO;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
if (result->buffer.pointer[0]) {
|
||||
ret = -EIO;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
*rpm = result->buffer.pointer[1] |
|
||||
(result->buffer.pointer[2] << 8);
|
||||
|
||||
out_free:
|
||||
kfree(output.pointer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static umode_t honor_fmi_is_visible(const void *data,
|
||||
enum hwmon_sensor_types type, u32 attr,
|
||||
int channel)
|
||||
{
|
||||
return 0444;
|
||||
}
|
||||
|
||||
static int honor_fmi_read(struct device *dev, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel, long *value)
|
||||
{
|
||||
struct honor_fmi_data *data = dev_get_drvdata(dev);
|
||||
|
||||
return honor_fmi_read_rpm(data, channel, value);
|
||||
}
|
||||
|
||||
static const struct hwmon_ops honor_fmi_hwmon_ops = {
|
||||
.is_visible = honor_fmi_is_visible,
|
||||
.read = honor_fmi_read,
|
||||
};
|
||||
|
||||
static const struct hwmon_channel_info * const honor_fmi_hwmon_info[] = {
|
||||
HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT, HWMON_F_INPUT),
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct hwmon_chip_info honor_fmi_chip_info = {
|
||||
.ops = &honor_fmi_hwmon_ops,
|
||||
.info = honor_fmi_hwmon_info,
|
||||
};
|
||||
|
||||
static int honor_fmi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct honor_fmi_data *data;
|
||||
struct device *hwmon_dev;
|
||||
acpi_status status;
|
||||
|
||||
if (!dmi_check_system(honor_fmi_dmi_table))
|
||||
return -ENODEV;
|
||||
|
||||
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
status = acpi_get_handle(NULL, "\\GFNS", &data->gfns);
|
||||
if (ACPI_FAILURE(status))
|
||||
return dev_err_probe(&pdev->dev, -ENODEV,
|
||||
"firmware does not provide \\GFNS\n");
|
||||
|
||||
hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, "honor_fmi",
|
||||
data,
|
||||
&honor_fmi_chip_info,
|
||||
NULL);
|
||||
return PTR_ERR_OR_ZERO(hwmon_dev);
|
||||
}
|
||||
|
||||
static struct platform_driver honor_fmi_driver = {
|
||||
.probe = honor_fmi_probe,
|
||||
.driver = {
|
||||
.name = "honor-fmi-hwmon",
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device *honor_fmi_device;
|
||||
|
||||
static int __init honor_fmi_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!dmi_check_system(honor_fmi_dmi_table))
|
||||
return -ENODEV;
|
||||
|
||||
ret = platform_driver_register(&honor_fmi_driver);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
honor_fmi_device = platform_device_register_simple("honor-fmi-hwmon",
|
||||
PLATFORM_DEVID_NONE,
|
||||
NULL, 0);
|
||||
if (IS_ERR(honor_fmi_device)) {
|
||||
platform_driver_unregister(&honor_fmi_driver);
|
||||
return PTR_ERR(honor_fmi_device);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit honor_fmi_exit(void)
|
||||
{
|
||||
platform_device_unregister(honor_fmi_device);
|
||||
platform_driver_unregister(&honor_fmi_driver);
|
||||
}
|
||||
|
||||
module_init(honor_fmi_init);
|
||||
module_exit(honor_fmi_exit);
|
||||
|
||||
MODULE_AUTHOR("Nikita Dubrovskih <testname142@gmail.com>");
|
||||
MODULE_DESCRIPTION("HONOR FMI-XX fan speed monitor");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -1804,7 +1804,7 @@ static int init_numeric_sensors(struct hp_wmi_sensors *state,
|
|||
|
||||
if (!info_map[type]) {
|
||||
info_map[type] = devm_kcalloc(dev, channel_count[type],
|
||||
sizeof(*info_map),
|
||||
sizeof(*info_map[type]),
|
||||
GFP_KERNEL);
|
||||
if (!info_map[type])
|
||||
return -ENOMEM;
|
||||
|
|
|
|||
|
|
@ -625,6 +625,8 @@ static const char * const hwmon_in_attr_templates[] = {
|
|||
[hwmon_in_max] = "in%d_max",
|
||||
[hwmon_in_lcrit] = "in%d_lcrit",
|
||||
[hwmon_in_crit] = "in%d_crit",
|
||||
[hwmon_in_lemergency] = "in%d_lemergency",
|
||||
[hwmon_in_emergency] = "in%d_emergency",
|
||||
[hwmon_in_average] = "in%d_average",
|
||||
[hwmon_in_lowest] = "in%d_lowest",
|
||||
[hwmon_in_highest] = "in%d_highest",
|
||||
|
|
@ -635,6 +637,8 @@ static const char * const hwmon_in_attr_templates[] = {
|
|||
[hwmon_in_max_alarm] = "in%d_max_alarm",
|
||||
[hwmon_in_lcrit_alarm] = "in%d_lcrit_alarm",
|
||||
[hwmon_in_crit_alarm] = "in%d_crit_alarm",
|
||||
[hwmon_in_lemergency_alarm] = "in%d_lemergency_alarm",
|
||||
[hwmon_in_emergency_alarm] = "in%d_emergency_alarm",
|
||||
[hwmon_in_rated_min] = "in%d_rated_min",
|
||||
[hwmon_in_rated_max] = "in%d_rated_max",
|
||||
[hwmon_in_beep] = "in%d_beep",
|
||||
|
|
@ -648,6 +652,7 @@ static const char * const hwmon_curr_attr_templates[] = {
|
|||
[hwmon_curr_max] = "curr%d_max",
|
||||
[hwmon_curr_lcrit] = "curr%d_lcrit",
|
||||
[hwmon_curr_crit] = "curr%d_crit",
|
||||
[hwmon_curr_emergency] = "curr%d_emergency",
|
||||
[hwmon_curr_average] = "curr%d_average",
|
||||
[hwmon_curr_lowest] = "curr%d_lowest",
|
||||
[hwmon_curr_highest] = "curr%d_highest",
|
||||
|
|
@ -658,6 +663,7 @@ static const char * const hwmon_curr_attr_templates[] = {
|
|||
[hwmon_curr_max_alarm] = "curr%d_max_alarm",
|
||||
[hwmon_curr_lcrit_alarm] = "curr%d_lcrit_alarm",
|
||||
[hwmon_curr_crit_alarm] = "curr%d_crit_alarm",
|
||||
[hwmon_curr_emergency_alarm] = "curr%d_emergency_alarm",
|
||||
[hwmon_curr_rated_min] = "curr%d_rated_min",
|
||||
[hwmon_curr_rated_max] = "curr%d_rated_max",
|
||||
[hwmon_curr_beep] = "curr%d_beep",
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@
|
|||
* IT8790E Super I/O chip w/LPC interface
|
||||
* IT8792E Super I/O chip w/LPC interface
|
||||
* IT87952E Super I/O chip w/LPC interface
|
||||
* IT8613E Super I/O chip w/LPC interface
|
||||
* Sis950 A clone of the IT8705F
|
||||
*
|
||||
* Copyright (C) 2001 Chris Gauthron
|
||||
|
|
@ -65,7 +66,7 @@
|
|||
|
||||
enum chips { it87, it8712, it8716, it8718, it8720, it8721, it8728, it8732,
|
||||
it8771, it8772, it8781, it8782, it8783, it8786, it8790,
|
||||
it8792, it8603, it8620, it8622, it8628, it8689, it87952 };
|
||||
it8792, it8603, it8613, it8620, it8622, it8628, it8689, it87952 };
|
||||
|
||||
static struct platform_device *it87_pdev[2];
|
||||
|
||||
|
|
@ -159,6 +160,7 @@ static inline void superio_exit(int ioreg, bool noexit)
|
|||
#define IT8786E_DEVID 0x8786
|
||||
#define IT8790E_DEVID 0x8790
|
||||
#define IT8603E_DEVID 0x8603
|
||||
#define IT8613E_DEVID 0x8613
|
||||
#define IT8620E_DEVID 0x8620
|
||||
#define IT8622E_DEVID 0x8622
|
||||
#define IT8623E_DEVID 0x8623
|
||||
|
|
@ -252,6 +254,7 @@ static const u8 IT87_REG_TEMP_OFFSET[] = { 0x56, 0x57, 0x59 };
|
|||
#define IT87_REG_FAN_MAIN_CTRL 0x13
|
||||
#define IT87_REG_FAN_CTL 0x14
|
||||
static const u8 IT87_REG_PWM[] = { 0x15, 0x16, 0x17, 0x7f, 0xa7, 0xaf };
|
||||
static const u8 IT87_REG_PWM_8665[] = { 0x15, 0x16, 0x17, 0x1e, 0x1f, 0x92 };
|
||||
static const u8 IT87_REG_PWM_DUTY[] = { 0x63, 0x6b, 0x73, 0x7b, 0xa3, 0xab };
|
||||
|
||||
static const u8 IT87_REG_VIN[] = { 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26,
|
||||
|
|
@ -283,6 +286,7 @@ static const u8 IT87_REG_AUTO_BASE[] = { 0x60, 0x68, 0x70, 0x78, 0xa0, 0xa8 };
|
|||
#define NUM_TEMP 6
|
||||
#define NUM_TEMP_OFFSET ARRAY_SIZE(IT87_REG_TEMP_OFFSET)
|
||||
#define NUM_TEMP_LIMIT 3
|
||||
#define IT87_PWM_OLD_NUM_TEMP 3
|
||||
#define NUM_FAN ARRAY_SIZE(IT87_REG_FAN)
|
||||
#define NUM_FAN_DIV 3
|
||||
#define NUM_PWM ARRAY_SIZE(IT87_REG_PWM)
|
||||
|
|
@ -292,6 +296,8 @@ struct it87_devices {
|
|||
const char *name;
|
||||
const char * const model;
|
||||
u32 features;
|
||||
const u8 *reg_pwm;
|
||||
u8 num_temp_map;
|
||||
u8 peci_mask;
|
||||
u8 old_peci_mask;
|
||||
u8 smbus_bitmap; /* SMBus enable bits in extra config register */
|
||||
|
|
@ -328,6 +334,8 @@ struct it87_devices {
|
|||
#define FEAT_FOUR_PWM BIT(21) /* Supports four fan controls */
|
||||
#define FEAT_FOUR_TEMP BIT(22)
|
||||
#define FEAT_FANCTL_ONOFF BIT(23) /* chip has FAN_CTL ON/OFF */
|
||||
#define FEAT_NEW_TEMPMAP BIT(24) /* PWM uses extended temp map */
|
||||
#define FEAT_11MV_ADC BIT(25)
|
||||
|
||||
static const struct it87_devices it87_devices[] = {
|
||||
[it87] = {
|
||||
|
|
@ -335,12 +343,16 @@ static const struct it87_devices it87_devices[] = {
|
|||
.model = "IT87F",
|
||||
.features = FEAT_OLD_AUTOPWM | FEAT_FANCTL_ONOFF,
|
||||
/* may need to overwrite */
|
||||
.reg_pwm = IT87_REG_PWM,
|
||||
.num_temp_map = 3,
|
||||
},
|
||||
[it8712] = {
|
||||
.name = "it8712",
|
||||
.model = "IT8712F",
|
||||
.features = FEAT_OLD_AUTOPWM | FEAT_VID | FEAT_FANCTL_ONOFF,
|
||||
/* may need to overwrite */
|
||||
.reg_pwm = IT87_REG_PWM,
|
||||
.num_temp_map = 3,
|
||||
},
|
||||
[it8716] = {
|
||||
.name = "it8716",
|
||||
|
|
@ -348,6 +360,8 @@ static const struct it87_devices it87_devices[] = {
|
|||
.features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_VID
|
||||
| FEAT_FAN16_CONFIG | FEAT_FIVE_FANS | FEAT_PWM_FREQ2
|
||||
| FEAT_FANCTL_ONOFF,
|
||||
.reg_pwm = IT87_REG_PWM,
|
||||
.num_temp_map = 3,
|
||||
},
|
||||
[it8718] = {
|
||||
.name = "it8718",
|
||||
|
|
@ -355,6 +369,8 @@ static const struct it87_devices it87_devices[] = {
|
|||
.features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_VID
|
||||
| FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG | FEAT_FIVE_FANS
|
||||
| FEAT_PWM_FREQ2 | FEAT_FANCTL_ONOFF,
|
||||
.reg_pwm = IT87_REG_PWM,
|
||||
.num_temp_map = 3,
|
||||
.old_peci_mask = 0x4,
|
||||
},
|
||||
[it8720] = {
|
||||
|
|
@ -363,6 +379,8 @@ static const struct it87_devices it87_devices[] = {
|
|||
.features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_VID
|
||||
| FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG | FEAT_FIVE_FANS
|
||||
| FEAT_PWM_FREQ2 | FEAT_FANCTL_ONOFF,
|
||||
.reg_pwm = IT87_REG_PWM,
|
||||
.num_temp_map = 3,
|
||||
.old_peci_mask = 0x4,
|
||||
},
|
||||
[it8721] = {
|
||||
|
|
@ -372,6 +390,8 @@ static const struct it87_devices it87_devices[] = {
|
|||
| FEAT_TEMP_OFFSET | FEAT_TEMP_OLD_PECI | FEAT_TEMP_PECI
|
||||
| FEAT_FAN16_CONFIG | FEAT_FIVE_FANS | FEAT_IN7_INTERNAL
|
||||
| FEAT_PWM_FREQ2 | FEAT_FANCTL_ONOFF,
|
||||
.reg_pwm = IT87_REG_PWM,
|
||||
.num_temp_map = 3,
|
||||
.peci_mask = 0x05,
|
||||
.old_peci_mask = 0x02, /* Actually reports PCH */
|
||||
},
|
||||
|
|
@ -382,6 +402,8 @@ static const struct it87_devices it87_devices[] = {
|
|||
| FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_FIVE_FANS
|
||||
| FEAT_IN7_INTERNAL | FEAT_PWM_FREQ2
|
||||
| FEAT_FANCTL_ONOFF,
|
||||
.reg_pwm = IT87_REG_PWM,
|
||||
.num_temp_map = 3,
|
||||
.peci_mask = 0x07,
|
||||
},
|
||||
[it8732] = {
|
||||
|
|
@ -391,6 +413,8 @@ static const struct it87_devices it87_devices[] = {
|
|||
| FEAT_TEMP_OFFSET | FEAT_TEMP_OLD_PECI | FEAT_TEMP_PECI
|
||||
| FEAT_10_9MV_ADC | FEAT_IN7_INTERNAL | FEAT_FOUR_FANS
|
||||
| FEAT_FOUR_PWM | FEAT_FANCTL_ONOFF,
|
||||
.reg_pwm = IT87_REG_PWM,
|
||||
.num_temp_map = 3,
|
||||
.peci_mask = 0x07,
|
||||
.old_peci_mask = 0x02, /* Actually reports PCH */
|
||||
},
|
||||
|
|
@ -404,6 +428,8 @@ static const struct it87_devices it87_devices[] = {
|
|||
/* 12mV ADC (OHM) */
|
||||
/* 16 bit fans (OHM) */
|
||||
/* three fans, always 16 bit (guesswork) */
|
||||
.reg_pwm = IT87_REG_PWM,
|
||||
.num_temp_map = 3,
|
||||
.peci_mask = 0x07,
|
||||
},
|
||||
[it8772] = {
|
||||
|
|
@ -416,6 +442,8 @@ static const struct it87_devices it87_devices[] = {
|
|||
/* 12mV ADC (HWSensors4, OHM) */
|
||||
/* 16 bit fans (HWSensors4, OHM) */
|
||||
/* three fans, always 16 bit (datasheet) */
|
||||
.reg_pwm = IT87_REG_PWM,
|
||||
.num_temp_map = 3,
|
||||
.peci_mask = 0x07,
|
||||
},
|
||||
[it8781] = {
|
||||
|
|
@ -424,6 +452,8 @@ static const struct it87_devices it87_devices[] = {
|
|||
.features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET
|
||||
| FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG | FEAT_PWM_FREQ2
|
||||
| FEAT_FANCTL_ONOFF,
|
||||
.reg_pwm = IT87_REG_PWM,
|
||||
.num_temp_map = 3,
|
||||
.old_peci_mask = 0x4,
|
||||
},
|
||||
[it8782] = {
|
||||
|
|
@ -432,6 +462,8 @@ static const struct it87_devices it87_devices[] = {
|
|||
.features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET
|
||||
| FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG | FEAT_PWM_FREQ2
|
||||
| FEAT_FANCTL_ONOFF,
|
||||
.reg_pwm = IT87_REG_PWM,
|
||||
.num_temp_map = 3,
|
||||
.old_peci_mask = 0x4,
|
||||
},
|
||||
[it8783] = {
|
||||
|
|
@ -440,6 +472,8 @@ static const struct it87_devices it87_devices[] = {
|
|||
.features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET
|
||||
| FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG | FEAT_PWM_FREQ2
|
||||
| FEAT_FANCTL_ONOFF,
|
||||
.reg_pwm = IT87_REG_PWM,
|
||||
.num_temp_map = 3,
|
||||
.old_peci_mask = 0x4,
|
||||
},
|
||||
[it8786] = {
|
||||
|
|
@ -448,6 +482,8 @@ static const struct it87_devices it87_devices[] = {
|
|||
.features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
|
||||
| FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL
|
||||
| FEAT_PWM_FREQ2 | FEAT_FANCTL_ONOFF,
|
||||
.reg_pwm = IT87_REG_PWM,
|
||||
.num_temp_map = 3,
|
||||
.peci_mask = 0x07,
|
||||
},
|
||||
[it8790] = {
|
||||
|
|
@ -456,6 +492,8 @@ static const struct it87_devices it87_devices[] = {
|
|||
.features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
|
||||
| FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL
|
||||
| FEAT_PWM_FREQ2 | FEAT_FANCTL_ONOFF | FEAT_NOCONF,
|
||||
.reg_pwm = IT87_REG_PWM,
|
||||
.num_temp_map = 3,
|
||||
.peci_mask = 0x07,
|
||||
},
|
||||
[it8792] = {
|
||||
|
|
@ -465,6 +503,8 @@ static const struct it87_devices it87_devices[] = {
|
|||
| FEAT_TEMP_OFFSET | FEAT_TEMP_OLD_PECI | FEAT_TEMP_PECI
|
||||
| FEAT_10_9MV_ADC | FEAT_IN7_INTERNAL | FEAT_FANCTL_ONOFF
|
||||
| FEAT_NOCONF,
|
||||
.reg_pwm = IT87_REG_PWM,
|
||||
.num_temp_map = 3,
|
||||
.peci_mask = 0x07,
|
||||
.old_peci_mask = 0x02, /* Actually reports PCH */
|
||||
},
|
||||
|
|
@ -474,6 +514,20 @@ static const struct it87_devices it87_devices[] = {
|
|||
.features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS
|
||||
| FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL
|
||||
| FEAT_AVCC3 | FEAT_PWM_FREQ2,
|
||||
.reg_pwm = IT87_REG_PWM,
|
||||
.num_temp_map = 4,
|
||||
.peci_mask = 0x07,
|
||||
},
|
||||
[it8613] = {
|
||||
.name = "it8613",
|
||||
.model = "IT8613E",
|
||||
/* Only three temperature inputs are currently known. */
|
||||
.features = FEAT_NEWER_AUTOPWM | FEAT_11MV_ADC | FEAT_16BIT_FANS
|
||||
| FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_FIVE_FANS
|
||||
| FEAT_FIVE_PWM | FEAT_IN7_INTERNAL | FEAT_PWM_FREQ2
|
||||
| FEAT_AVCC3 | FEAT_NEW_TEMPMAP,
|
||||
.reg_pwm = IT87_REG_PWM_8665,
|
||||
.num_temp_map = 6,
|
||||
.peci_mask = 0x07,
|
||||
},
|
||||
[it8620] = {
|
||||
|
|
@ -483,6 +537,8 @@ static const struct it87_devices it87_devices[] = {
|
|||
| FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_SIX_FANS
|
||||
| FEAT_IN7_INTERNAL | FEAT_SIX_PWM | FEAT_PWM_FREQ2
|
||||
| FEAT_SIX_TEMP | FEAT_VIN3_5V | FEAT_FANCTL_ONOFF,
|
||||
.reg_pwm = IT87_REG_PWM,
|
||||
.num_temp_map = 3,
|
||||
.peci_mask = 0x07,
|
||||
},
|
||||
[it8622] = {
|
||||
|
|
@ -492,6 +548,8 @@ static const struct it87_devices it87_devices[] = {
|
|||
| FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_FIVE_FANS
|
||||
| FEAT_FIVE_PWM | FEAT_IN7_INTERNAL | FEAT_PWM_FREQ2
|
||||
| FEAT_AVCC3 | FEAT_VIN3_5V | FEAT_FOUR_TEMP,
|
||||
.reg_pwm = IT87_REG_PWM_8665,
|
||||
.num_temp_map = 4,
|
||||
.peci_mask = 0x07,
|
||||
.smbus_bitmap = BIT(1) | BIT(2),
|
||||
},
|
||||
|
|
@ -502,6 +560,8 @@ static const struct it87_devices it87_devices[] = {
|
|||
| FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_SIX_FANS
|
||||
| FEAT_IN7_INTERNAL | FEAT_SIX_PWM | FEAT_PWM_FREQ2
|
||||
| FEAT_SIX_TEMP | FEAT_VIN3_5V | FEAT_FANCTL_ONOFF,
|
||||
.reg_pwm = IT87_REG_PWM,
|
||||
.num_temp_map = 3,
|
||||
.peci_mask = 0x07,
|
||||
},
|
||||
[it8689] = {
|
||||
|
|
@ -511,6 +571,8 @@ static const struct it87_devices it87_devices[] = {
|
|||
| FEAT_TEMP_OFFSET | FEAT_SIX_FANS | FEAT_IN7_INTERNAL
|
||||
| FEAT_SIX_PWM | FEAT_PWM_FREQ2 | FEAT_SIX_TEMP | FEAT_AVCC3
|
||||
| FEAT_FANCTL_ONOFF,
|
||||
.reg_pwm = IT87_REG_PWM,
|
||||
.num_temp_map = 3,
|
||||
.smbus_bitmap = BIT(1) | BIT(2),
|
||||
},
|
||||
[it87952] = {
|
||||
|
|
@ -520,6 +582,8 @@ static const struct it87_devices it87_devices[] = {
|
|||
| FEAT_TEMP_OFFSET | FEAT_TEMP_OLD_PECI | FEAT_TEMP_PECI
|
||||
| FEAT_10_9MV_ADC | FEAT_IN7_INTERNAL | FEAT_FANCTL_ONOFF
|
||||
| FEAT_NOCONF,
|
||||
.reg_pwm = IT87_REG_PWM,
|
||||
.num_temp_map = 3,
|
||||
.peci_mask = 0x07,
|
||||
.old_peci_mask = 0x02, /* Actually reports PCH */
|
||||
},
|
||||
|
|
@ -528,6 +592,7 @@ static const struct it87_devices it87_devices[] = {
|
|||
#define has_16bit_fans(data) ((data)->features & FEAT_16BIT_FANS)
|
||||
#define has_12mv_adc(data) ((data)->features & FEAT_12MV_ADC)
|
||||
#define has_10_9mv_adc(data) ((data)->features & FEAT_10_9MV_ADC)
|
||||
#define has_11mv_adc(data) ((data)->features & FEAT_11MV_ADC)
|
||||
#define has_newer_autopwm(data) ((data)->features & FEAT_NEWER_AUTOPWM)
|
||||
#define has_old_autopwm(data) ((data)->features & FEAT_OLD_AUTOPWM)
|
||||
#define has_temp_offset(data) ((data)->features & FEAT_TEMP_OFFSET)
|
||||
|
|
@ -558,8 +623,10 @@ static const struct it87_devices it87_devices[] = {
|
|||
#define has_vin3_5v(data) ((data)->features & FEAT_VIN3_5V)
|
||||
#define has_noconf(data) ((data)->features & FEAT_NOCONF)
|
||||
#define has_scaling(data) ((data)->features & (FEAT_12MV_ADC | \
|
||||
FEAT_10_9MV_ADC))
|
||||
FEAT_10_9MV_ADC | \
|
||||
FEAT_11MV_ADC))
|
||||
#define has_fanctl_onoff(data) ((data)->features & FEAT_FANCTL_ONOFF)
|
||||
#define has_new_tempmap(data) ((data)->features & FEAT_NEW_TEMPMAP)
|
||||
|
||||
struct it87_sio_data {
|
||||
int sioaddr;
|
||||
|
|
@ -589,6 +656,8 @@ struct it87_data {
|
|||
int sioaddr;
|
||||
enum chips type;
|
||||
u32 features;
|
||||
const u8 *reg_pwm;
|
||||
u8 num_temp_map;
|
||||
u8 peci_mask;
|
||||
u8 old_peci_mask;
|
||||
|
||||
|
|
@ -635,7 +704,9 @@ struct it87_data {
|
|||
u8 has_pwm; /* Bitfield, pwm control enabled */
|
||||
u8 pwm_ctrl[NUM_PWM]; /* Register value */
|
||||
u8 pwm_duty[NUM_PWM]; /* Manual PWM value set by user */
|
||||
u8 pwm_temp_map[NUM_PWM];/* PWM to temp. chan. mapping (bits 1-0) */
|
||||
u8 pwm_temp_map[NUM_PWM];/* PWM to temp. chan. mapping */
|
||||
u8 pwm_temp_map_mask;
|
||||
u8 pwm_temp_map_shift;
|
||||
|
||||
/* Automatic fan speed control registers */
|
||||
u8 auto_pwm[NUM_AUTO_PWM][4]; /* [nr][3] is hard-coded */
|
||||
|
|
@ -658,6 +729,8 @@ static int adc_lsb(const struct it87_data *data, int nr)
|
|||
lsb = 120;
|
||||
else if (has_10_9mv_adc(data))
|
||||
lsb = 109;
|
||||
else if (has_11mv_adc(data))
|
||||
lsb = 110;
|
||||
else
|
||||
lsb = 160;
|
||||
if (data->in_scaled & BIT(nr))
|
||||
|
|
@ -717,6 +790,77 @@ static int pwm_from_reg(const struct it87_data *data, u8 reg)
|
|||
return (reg & 0x7f) << 1;
|
||||
}
|
||||
|
||||
static inline u8 pwm_temp_map_get(const struct it87_data *data, u8 ctrl)
|
||||
{
|
||||
return (ctrl >> data->pwm_temp_map_shift) &
|
||||
data->pwm_temp_map_mask;
|
||||
}
|
||||
|
||||
static inline u8 pwm_temp_map_set(const struct it87_data *data, u8 ctrl,
|
||||
u8 map)
|
||||
{
|
||||
ctrl &= ~(data->pwm_temp_map_mask << data->pwm_temp_map_shift);
|
||||
return ctrl | ((map & data->pwm_temp_map_mask)
|
||||
<< data->pwm_temp_map_shift);
|
||||
}
|
||||
|
||||
static inline u8 pwm_num_temp_map(const struct it87_data *data)
|
||||
{
|
||||
return data->num_temp_map;
|
||||
}
|
||||
|
||||
static inline bool uses_global_temp_map(const struct it87_data *data)
|
||||
{
|
||||
return has_new_tempmap(data) ||
|
||||
pwm_num_temp_map(data) != IT87_PWM_OLD_NUM_TEMP;
|
||||
}
|
||||
|
||||
static unsigned int pwm_temp_channel(const struct it87_data *data,
|
||||
int nr, u8 map)
|
||||
{
|
||||
if (uses_global_temp_map(data)) {
|
||||
u8 num = pwm_num_temp_map(data);
|
||||
|
||||
if (map >= num)
|
||||
map = 0;
|
||||
return map;
|
||||
}
|
||||
|
||||
if (map >= IT87_PWM_OLD_NUM_TEMP)
|
||||
map = 0;
|
||||
|
||||
if (nr >= IT87_PWM_OLD_NUM_TEMP)
|
||||
map += IT87_PWM_OLD_NUM_TEMP;
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
static int pwm_temp_map_from_channel(const struct it87_data *data, int nr,
|
||||
unsigned int channel, u8 *map)
|
||||
{
|
||||
if (uses_global_temp_map(data)) {
|
||||
u8 num = pwm_num_temp_map(data);
|
||||
|
||||
if (channel >= num)
|
||||
return -EINVAL;
|
||||
*map = channel;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (nr >= IT87_PWM_OLD_NUM_TEMP) {
|
||||
if (channel < IT87_PWM_OLD_NUM_TEMP ||
|
||||
channel >= 2 * IT87_PWM_OLD_NUM_TEMP)
|
||||
return -EINVAL;
|
||||
channel -= IT87_PWM_OLD_NUM_TEMP;
|
||||
} else {
|
||||
if (channel >= IT87_PWM_OLD_NUM_TEMP)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*map = channel;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int DIV_TO_REG(int val)
|
||||
{
|
||||
int answer = 0;
|
||||
|
|
@ -728,6 +872,11 @@ static int DIV_TO_REG(int val)
|
|||
|
||||
#define DIV_FROM_REG(val) BIT(val)
|
||||
|
||||
static inline u16 it87_reg_pwm(const struct it87_data *data, int nr)
|
||||
{
|
||||
return data->reg_pwm[nr];
|
||||
}
|
||||
|
||||
/*
|
||||
* PWM base frequencies. The frequency has to be divided by either 128 or 256,
|
||||
* depending on the chip type, to calculate the actual PWM frequency.
|
||||
|
|
@ -808,16 +957,23 @@ static void it87_write_value(struct it87_data *data, u8 reg, u8 value)
|
|||
|
||||
static void it87_update_pwm_ctrl(struct it87_data *data, int nr)
|
||||
{
|
||||
data->pwm_ctrl[nr] = it87_read_value(data, IT87_REG_PWM[nr]);
|
||||
data->pwm_ctrl[nr] = it87_read_value(data, it87_reg_pwm(data, nr));
|
||||
if (has_newer_autopwm(data)) {
|
||||
data->pwm_temp_map[nr] = data->pwm_ctrl[nr] & 0x03;
|
||||
data->pwm_temp_map[nr] =
|
||||
pwm_temp_map_get(data, data->pwm_ctrl[nr]);
|
||||
if (uses_global_temp_map(data) &&
|
||||
data->pwm_temp_map[nr] >= pwm_num_temp_map(data))
|
||||
data->pwm_temp_map[nr] = 0;
|
||||
data->pwm_duty[nr] = it87_read_value(data,
|
||||
IT87_REG_PWM_DUTY[nr]);
|
||||
} else {
|
||||
if (data->pwm_ctrl[nr] & 0x80) /* Automatic mode */
|
||||
data->pwm_temp_map[nr] = data->pwm_ctrl[nr] & 0x03;
|
||||
else /* Manual mode */
|
||||
data->pwm_duty[nr] = data->pwm_ctrl[nr] & 0x7f;
|
||||
} else if (data->pwm_ctrl[nr] & 0x80) { /* Automatic mode */
|
||||
data->pwm_temp_map[nr] =
|
||||
pwm_temp_map_get(data, data->pwm_ctrl[nr]);
|
||||
if (uses_global_temp_map(data) &&
|
||||
data->pwm_temp_map[nr] >= pwm_num_temp_map(data))
|
||||
data->pwm_temp_map[nr] = 0;
|
||||
} else { /* Manual mode */
|
||||
data->pwm_duty[nr] = data->pwm_ctrl[nr] & 0x7f;
|
||||
}
|
||||
|
||||
if (has_old_autopwm(data)) {
|
||||
|
|
@ -1567,27 +1723,32 @@ static ssize_t set_pwm_enable(struct device *dev, struct device_attribute *attr,
|
|||
data->pwm_duty[nr]);
|
||||
/* and set manual mode */
|
||||
if (has_newer_autopwm(data)) {
|
||||
ctrl = (data->pwm_ctrl[nr] & 0x7c) |
|
||||
data->pwm_temp_map[nr];
|
||||
ctrl = pwm_temp_map_set(data,
|
||||
data->pwm_ctrl[nr] &
|
||||
~0x80,
|
||||
data->pwm_temp_map[nr]);
|
||||
} else {
|
||||
ctrl = data->pwm_duty[nr];
|
||||
}
|
||||
data->pwm_ctrl[nr] = ctrl;
|
||||
it87_write_value(data, IT87_REG_PWM[nr], ctrl);
|
||||
it87_write_value(data, it87_reg_pwm(data, nr), ctrl);
|
||||
}
|
||||
} else {
|
||||
u8 ctrl;
|
||||
|
||||
if (has_newer_autopwm(data)) {
|
||||
ctrl = (data->pwm_ctrl[nr] & 0x7c) |
|
||||
data->pwm_temp_map[nr];
|
||||
ctrl = pwm_temp_map_set(data,
|
||||
data->pwm_ctrl[nr] & ~0x80,
|
||||
data->pwm_temp_map[nr]);
|
||||
if (val != 1)
|
||||
ctrl |= 0x80;
|
||||
} else {
|
||||
ctrl = (val == 1 ? data->pwm_duty[nr] : 0x80);
|
||||
ctrl = val == 1 ? data->pwm_duty[nr] :
|
||||
pwm_temp_map_set(data, 0x80,
|
||||
data->pwm_temp_map[nr]);
|
||||
}
|
||||
data->pwm_ctrl[nr] = ctrl;
|
||||
it87_write_value(data, IT87_REG_PWM[nr], ctrl);
|
||||
it87_write_value(data, it87_reg_pwm(data, nr), ctrl);
|
||||
|
||||
if (has_fanctl_onoff(data) && nr < 3) {
|
||||
/* set SmartGuardian mode */
|
||||
|
|
@ -1638,7 +1799,7 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
|
|||
*/
|
||||
if (!(data->pwm_ctrl[nr] & 0x80)) {
|
||||
data->pwm_ctrl[nr] = data->pwm_duty[nr];
|
||||
it87_write_value(data, IT87_REG_PWM[nr],
|
||||
it87_write_value(data, it87_reg_pwm(data, nr),
|
||||
data->pwm_ctrl[nr]);
|
||||
}
|
||||
}
|
||||
|
|
@ -1693,18 +1854,14 @@ static ssize_t show_pwm_temp_map(struct device *dev,
|
|||
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
|
||||
struct it87_data *data = it87_update_device(dev);
|
||||
int nr = sensor_attr->index;
|
||||
int map;
|
||||
unsigned int channel;
|
||||
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
|
||||
map = data->pwm_temp_map[nr];
|
||||
if (map >= 3)
|
||||
map = 0; /* Should never happen */
|
||||
if (nr >= 3) /* pwm channels 3..6 map to temp4..6 */
|
||||
map += 3;
|
||||
channel = pwm_temp_channel(data, nr, data->pwm_temp_map[nr]);
|
||||
|
||||
return sprintf(buf, "%d\n", (int)BIT(map));
|
||||
return sprintf(buf, "%d\n", (int)BIT(channel));
|
||||
}
|
||||
|
||||
static ssize_t set_pwm_temp_map(struct device *dev,
|
||||
|
|
@ -1716,42 +1873,33 @@ static ssize_t set_pwm_temp_map(struct device *dev,
|
|||
int nr = sensor_attr->index;
|
||||
long val;
|
||||
int err;
|
||||
u8 reg;
|
||||
unsigned int channel;
|
||||
u8 map;
|
||||
|
||||
if (kstrtol(buf, 10, &val) < 0)
|
||||
if (kstrtol(buf, 10, &val) < 0 || val <= 0 || !is_power_of_2(val))
|
||||
return -EINVAL;
|
||||
|
||||
if (nr >= 3)
|
||||
val -= 3;
|
||||
|
||||
switch (val) {
|
||||
case BIT(0):
|
||||
reg = 0x00;
|
||||
break;
|
||||
case BIT(1):
|
||||
reg = 0x01;
|
||||
break;
|
||||
case BIT(2):
|
||||
reg = 0x02;
|
||||
break;
|
||||
default:
|
||||
channel = __ffs(val);
|
||||
if (pwm_temp_map_from_channel(data, nr, channel, &map))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = it87_lock(data);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
it87_update_pwm_ctrl(data, nr);
|
||||
data->pwm_temp_map[nr] = reg;
|
||||
data->pwm_temp_map[nr] = map;
|
||||
/*
|
||||
* If we are in automatic mode, write the temp mapping immediately;
|
||||
* otherwise, just store it for later use.
|
||||
* Newer controllers keep the duty cycle in a separate register, so
|
||||
* their temperature mapping can be updated in any mode. On older
|
||||
* controllers, defer the update until automatic mode is enabled.
|
||||
*/
|
||||
if (data->pwm_ctrl[nr] & 0x80) {
|
||||
data->pwm_ctrl[nr] = (data->pwm_ctrl[nr] & 0xfc) |
|
||||
data->pwm_temp_map[nr];
|
||||
it87_write_value(data, IT87_REG_PWM[nr], data->pwm_ctrl[nr]);
|
||||
if (has_newer_autopwm(data) || (data->pwm_ctrl[nr] & 0x80)) {
|
||||
data->pwm_ctrl[nr] = pwm_temp_map_set(data,
|
||||
data->pwm_ctrl[nr],
|
||||
data->pwm_temp_map[nr]);
|
||||
it87_write_value(data, it87_reg_pwm(data, nr),
|
||||
data->pwm_ctrl[nr]);
|
||||
}
|
||||
it87_unlock(data);
|
||||
return count;
|
||||
|
|
@ -2790,6 +2938,9 @@ static int __init it87_find(int sioaddr, unsigned short *address,
|
|||
case IT8623E_DEVID:
|
||||
sio_data->type = it8603;
|
||||
break;
|
||||
case IT8613E_DEVID:
|
||||
sio_data->type = it8613;
|
||||
break;
|
||||
case IT8620E_DEVID:
|
||||
sio_data->type = it8620;
|
||||
break;
|
||||
|
|
@ -2967,6 +3118,43 @@ static int __init it87_find(int sioaddr, unsigned short *address,
|
|||
sio_data->skip_in |= BIT(5); /* No VIN5 */
|
||||
sio_data->skip_in |= BIT(6); /* No VIN6 */
|
||||
|
||||
sio_data->beep_pin = superio_inb(sioaddr,
|
||||
IT87_SIO_BEEP_PIN_REG) & 0x3f;
|
||||
} else if (sio_data->type == it8613) {
|
||||
int reg27, reg29, reg2a;
|
||||
|
||||
superio_select(sioaddr, GPIO);
|
||||
|
||||
/* Check for pwm3, fan3, pwm5, fan5 */
|
||||
reg27 = superio_inb(sioaddr, IT87_SIO_GPIO3_REG);
|
||||
if (!(reg27 & BIT(1)))
|
||||
sio_data->skip_fan |= BIT(4);
|
||||
if (reg27 & BIT(3))
|
||||
sio_data->skip_pwm |= BIT(4);
|
||||
if (reg27 & BIT(6))
|
||||
sio_data->skip_pwm |= BIT(2);
|
||||
if (reg27 & BIT(7))
|
||||
sio_data->skip_fan |= BIT(2);
|
||||
|
||||
/* Check for pwm2, fan2 */
|
||||
reg29 = superio_inb(sioaddr, IT87_SIO_GPIO5_REG);
|
||||
if (reg29 & BIT(1))
|
||||
sio_data->skip_pwm |= BIT(1);
|
||||
if (reg29 & BIT(2))
|
||||
sio_data->skip_fan |= BIT(1);
|
||||
|
||||
/* Check for pwm4, fan4 */
|
||||
reg2a = superio_inb(sioaddr, IT87_SIO_PINX1_REG);
|
||||
if (!(reg2a & BIT(0)) || (reg29 & BIT(7))) {
|
||||
sio_data->skip_fan |= BIT(3);
|
||||
sio_data->skip_pwm |= BIT(3);
|
||||
}
|
||||
|
||||
sio_data->skip_pwm |= BIT(0); /* No pwm1 */
|
||||
sio_data->skip_fan |= BIT(0); /* No fan1 */
|
||||
sio_data->skip_in |= BIT(3); /* No VIN3 */
|
||||
sio_data->skip_in |= BIT(6); /* No VIN6 */
|
||||
|
||||
sio_data->beep_pin = superio_inb(sioaddr,
|
||||
IT87_SIO_BEEP_PIN_REG) & 0x3f;
|
||||
} else if (sio_data->type == it8620 || sio_data->type == it8628) {
|
||||
|
|
@ -3350,7 +3538,10 @@ static void it87_init_device(struct platform_device *pdev)
|
|||
* manual duty cycle.
|
||||
*/
|
||||
for (i = 0; i < NUM_AUTO_PWM; i++) {
|
||||
data->pwm_temp_map[i] = i;
|
||||
if (uses_global_temp_map(data))
|
||||
data->pwm_temp_map[i] = 0;
|
||||
else
|
||||
data->pwm_temp_map[i] = i % IT87_PWM_OLD_NUM_TEMP;
|
||||
data->pwm_duty[i] = 0x7f; /* Full speed */
|
||||
data->auto_pwm[i][3] = 0x7f; /* Full speed, hard-coded */
|
||||
}
|
||||
|
|
@ -3461,6 +3652,7 @@ static int it87_probe(struct platform_device *pdev)
|
|||
struct resource *res;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct it87_sio_data *sio_data = dev_get_platdata(dev);
|
||||
const struct it87_devices *chip;
|
||||
int enable_pwm_interface;
|
||||
struct device *hwmon_dev;
|
||||
int err;
|
||||
|
|
@ -3483,9 +3675,19 @@ static int it87_probe(struct platform_device *pdev)
|
|||
data->type = sio_data->type;
|
||||
data->smbus_bitmap = sio_data->smbus_bitmap;
|
||||
data->ec_special_config = sio_data->ec_special_config;
|
||||
data->features = it87_devices[sio_data->type].features;
|
||||
data->peci_mask = it87_devices[sio_data->type].peci_mask;
|
||||
data->old_peci_mask = it87_devices[sio_data->type].old_peci_mask;
|
||||
chip = &it87_devices[sio_data->type];
|
||||
data->features = chip->features;
|
||||
data->reg_pwm = chip->reg_pwm;
|
||||
data->peci_mask = chip->peci_mask;
|
||||
data->old_peci_mask = chip->old_peci_mask;
|
||||
data->num_temp_map = chip->num_temp_map;
|
||||
if (has_new_tempmap(data)) {
|
||||
data->pwm_temp_map_mask = 0x07;
|
||||
data->pwm_temp_map_shift = 3;
|
||||
} else {
|
||||
data->pwm_temp_map_mask = 0x03;
|
||||
data->pwm_temp_map_shift = 0;
|
||||
}
|
||||
/*
|
||||
* IT8705F Datasheet 0.4.1, 3h == Version G.
|
||||
* IT8712F Datasheet 0.9.1, section 8.3.5 indicates 8h == Version J.
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ MODULE_PARM_DESC(force, "force loading on processors with erratum 319");
|
|||
#define F15H_M60H_HARDWARE_TEMP_CTRL_OFFSET 0xd8200c64
|
||||
#define F15H_M60H_REPORTED_TEMP_CTRL_OFFSET 0xd8200ca4
|
||||
|
||||
/* Common for Zen CPU families (Family 17h and 18h and 19h and 1Ah) */
|
||||
/* Common for Zen CPU families (Family 17h, 19h and 1Ah) */
|
||||
#define ZEN_REPORTED_TEMP_CTRL_BASE 0x00059800
|
||||
|
||||
#define ZEN_CCD_TEMP(offset, x) (ZEN_REPORTED_TEMP_CTRL_BASE + \
|
||||
|
|
@ -94,6 +94,7 @@ MODULE_PARM_DESC(force, "force loading on processors with erratum 319");
|
|||
* Defining locally as IDs are not shared.
|
||||
*/
|
||||
#define PCI_DEVICE_ID_AMD_1AH_M50H_DF_F3 0x12cb
|
||||
#define PCI_DEVICE_ID_AMD_1AH_M80H_DF_F3 0x1243
|
||||
#define PCI_DEVICE_ID_AMD_1AH_M90H_DF_F3 0x127b
|
||||
|
||||
struct k10temp_data {
|
||||
|
|
@ -475,7 +476,7 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
|||
data->read_tempreg = read_tempreg_pci;
|
||||
}
|
||||
|
||||
if (boot_cpu_data.x86 == 0x17 || boot_cpu_data.x86 == 0x18) {
|
||||
if (boot_cpu_data.x86 == 0x17) {
|
||||
switch (boot_cpu_data.x86_model) {
|
||||
case 0x1: /* Zen */
|
||||
case 0x8: /* Zen+ */
|
||||
|
|
@ -582,8 +583,8 @@ static const struct pci_device_id k10temp_id_table[] = {
|
|||
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_1AH_M50H_DF_F3) },
|
||||
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_1AH_M60H_DF_F3) },
|
||||
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_1AH_M70H_DF_F3) },
|
||||
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_1AH_M80H_DF_F3) },
|
||||
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_1AH_M90H_DF_F3) },
|
||||
{ PCI_VDEVICE(HYGON, PCI_DEVICE_ID_AMD_17H_DF_F3) },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, k10temp_id_table);
|
||||
|
|
|
|||
|
|
@ -35,13 +35,14 @@
|
|||
#include <linux/mutex.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/thermal.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/*
|
||||
* Addresses to scan
|
||||
* Address is fully defined internally and cannot be changed except for
|
||||
* LM64 which has one pin dedicated to address selection.
|
||||
* LM63 and LM96163 have address 0x4c.
|
||||
* LM63, LM96163, and CTF2301 have address 0x4c.
|
||||
* LM64 can have address 0x18 or 0x4e.
|
||||
*/
|
||||
|
||||
|
|
@ -70,6 +71,9 @@ static const unsigned short normal_i2c[] = { 0x18, 0x4c, 0x4e, I2C_CLIENT_END };
|
|||
#define LM63_REG_LOCAL_TEMP 0x00
|
||||
#define LM63_REG_LOCAL_HIGH 0x05
|
||||
|
||||
#define CTF2301_REG_LOCAL_TEMP_LSB 0x15
|
||||
#define CTF2301_REG_LOCAL_HIGH_LSB 0x06
|
||||
|
||||
#define LM63_REG_REMOTE_TEMP_MSB 0x01
|
||||
#define LM63_REG_REMOTE_TEMP_LSB 0x10
|
||||
#define LM63_REG_REMOTE_OFFSET_MSB 0x11
|
||||
|
|
@ -94,6 +98,7 @@ static const unsigned short normal_i2c[] = { 0x18, 0x4c, 0x4e, I2C_CLIENT_END };
|
|||
|
||||
#define LM63_PWM_BASE_FAST_HZ 180000
|
||||
#define LM63_PWM_BASE_SLOW_HZ 700
|
||||
#define LM63_MAX_PWM 255
|
||||
|
||||
#define LM63_MAX_CONVRATE 9
|
||||
|
||||
|
|
@ -121,6 +126,9 @@ static const unsigned short normal_i2c[] = { 0x18, 0x4c, 0x4e, I2C_CLIENT_END };
|
|||
127000), 1000)
|
||||
#define TEMP8U_TO_REG(val) DIV_ROUND_CLOSEST(clamp_val((val), 0, \
|
||||
255000), 1000)
|
||||
#define TEMP12_FROM_REG(reg) DIV_ROUND_CLOSEST((s16)(reg) * 1000, 256)
|
||||
#define TEMP12_TO_REG(val) (DIV_ROUND_CLOSEST(clamp_val((val), -128000, \
|
||||
127937) * 16, 1000) * 16)
|
||||
#define TEMP11_FROM_REG(reg) ((reg) / 32 * 125)
|
||||
#define TEMP11_TO_REG(val) (DIV_ROUND_CLOSEST(clamp_val((val), -128000, \
|
||||
127875), 125) * 32)
|
||||
|
|
@ -132,7 +140,11 @@ static const unsigned short normal_i2c[] = { 0x18, 0x4c, 0x4e, I2C_CLIENT_END };
|
|||
#define UPDATE_INTERVAL(max, rate) \
|
||||
((1000 << (LM63_MAX_CONVRATE - (rate))) / (max))
|
||||
|
||||
enum chips { lm63, lm64, lm96163 };
|
||||
enum chips { ctf2301, lm63, lm64, lm96163 };
|
||||
|
||||
static const unsigned int ctf2301_update_intervals[] = {
|
||||
20000, 10000, 4902, 2463, 1000, 615, 307, 154, 107
|
||||
};
|
||||
|
||||
/*
|
||||
* Client data (each client gets its own)
|
||||
|
|
@ -164,6 +176,8 @@ struct lm63_data {
|
|||
1: local high limit
|
||||
2: remote critical limit
|
||||
3-14: lookup table */
|
||||
s16 temp1_input; /* local input for chips with extended resolution */
|
||||
s16 temp1_max; /* local limit for chips with extended resolution */
|
||||
s16 temp11[4]; /* 0: remote input
|
||||
1: remote low limit
|
||||
2: remote high limit
|
||||
|
|
@ -229,6 +243,7 @@ static struct lm63_data *lm63_update_device(struct device *dev)
|
|||
struct lm63_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
unsigned long next_update;
|
||||
int msb, lsb;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
|
|
@ -253,10 +268,26 @@ static struct lm63_data *lm63_update_device(struct device *dev)
|
|||
data->pwm1[0] = i2c_smbus_read_byte_data(client,
|
||||
LM63_REG_PWM_VALUE);
|
||||
|
||||
data->temp8[0] = i2c_smbus_read_byte_data(client,
|
||||
LM63_REG_LOCAL_TEMP);
|
||||
data->temp8[1] = i2c_smbus_read_byte_data(client,
|
||||
LM63_REG_LOCAL_HIGH);
|
||||
if (data->kind == ctf2301) {
|
||||
msb = i2c_smbus_read_byte_data(client,
|
||||
LM63_REG_LOCAL_TEMP);
|
||||
lsb = i2c_smbus_read_byte_data(client,
|
||||
CTF2301_REG_LOCAL_TEMP_LSB);
|
||||
if (msb >= 0 && lsb >= 0)
|
||||
data->temp1_input = ((u16)(u8)msb << 8) | lsb;
|
||||
|
||||
msb = i2c_smbus_read_byte_data(client,
|
||||
LM63_REG_LOCAL_HIGH);
|
||||
lsb = i2c_smbus_read_byte_data(client,
|
||||
CTF2301_REG_LOCAL_HIGH_LSB);
|
||||
if (msb >= 0 && lsb >= 0)
|
||||
data->temp1_max = ((u16)(u8)msb << 8) | lsb;
|
||||
} else {
|
||||
data->temp8[0] = i2c_smbus_read_byte_data(client,
|
||||
LM63_REG_LOCAL_TEMP);
|
||||
data->temp8[1] = i2c_smbus_read_byte_data(client,
|
||||
LM63_REG_LOCAL_HIGH);
|
||||
}
|
||||
|
||||
/* order matters for temp2_input */
|
||||
data->temp11[0] = i2c_smbus_read_byte_data(client,
|
||||
|
|
@ -276,7 +307,7 @@ static struct lm63_data *lm63_update_device(struct device *dev)
|
|||
| i2c_smbus_read_byte_data(client,
|
||||
LM63_REG_REMOTE_OFFSET_LSB);
|
||||
|
||||
if (data->kind == lm96163)
|
||||
if (data->kind == lm96163 || data->kind == ctf2301)
|
||||
data->temp11u = (i2c_smbus_read_byte_data(client,
|
||||
LM96163_REG_REMOTE_TEMP_U_MSB) << 8)
|
||||
| i2c_smbus_read_byte_data(client,
|
||||
|
|
@ -366,6 +397,49 @@ static ssize_t set_fan(struct device *dev, struct device_attribute *dummy,
|
|||
return count;
|
||||
}
|
||||
|
||||
static int lm63_pwm_from_reg(struct lm63_data *data, u8 reg)
|
||||
{
|
||||
if (data->pwm_highres)
|
||||
return reg;
|
||||
|
||||
return reg >= 2 * data->pwm1_freq ?
|
||||
LM63_MAX_PWM :
|
||||
(reg * LM63_MAX_PWM + data->pwm1_freq) / (2 * data->pwm1_freq);
|
||||
}
|
||||
|
||||
static u8 lm63_pwm_to_reg(struct lm63_data *data, unsigned long val)
|
||||
{
|
||||
val = clamp_val(val, 0, LM63_MAX_PWM);
|
||||
|
||||
if (data->pwm_highres)
|
||||
return val;
|
||||
|
||||
return (val * data->pwm1_freq * 2 + LM63_MAX_PWM / 2) / LM63_MAX_PWM;
|
||||
}
|
||||
|
||||
static void lm63_update_pwm_highres(struct lm63_data *data,
|
||||
int config_enhanced)
|
||||
{
|
||||
data->pwm_highres = !(data->config_fan & 0x08) &&
|
||||
data->pwm1_freq == 8 &&
|
||||
(config_enhanced & 0x10);
|
||||
}
|
||||
|
||||
static int lm63_write_pwm(struct lm63_data *data, int nr, unsigned long val)
|
||||
{
|
||||
struct i2c_client *client = data->client;
|
||||
u8 reg = nr ? LM63_REG_LUT_PWM(nr - 1) : LM63_REG_PWM_VALUE;
|
||||
u8 regval = lm63_pwm_to_reg(data, val);
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client, reg, regval);
|
||||
if (ret)
|
||||
return ret;
|
||||
data->pwm1[nr] = regval;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t show_pwm1(struct device *dev, struct device_attribute *devattr,
|
||||
char *buf)
|
||||
{
|
||||
|
|
@ -375,12 +449,7 @@ static ssize_t show_pwm1(struct device *dev, struct device_attribute *devattr,
|
|||
int pwm;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
if (data->pwm_highres)
|
||||
pwm = data->pwm1[nr];
|
||||
else
|
||||
pwm = data->pwm1[nr] >= 2 * data->pwm1_freq ?
|
||||
255 : (data->pwm1[nr] * 255 + data->pwm1_freq) /
|
||||
(2 * data->pwm1_freq);
|
||||
pwm = lm63_pwm_from_reg(data, data->pwm1[nr]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return sprintf(buf, "%d\n", pwm);
|
||||
|
|
@ -391,11 +460,9 @@ static ssize_t set_pwm1(struct device *dev, struct device_attribute *devattr,
|
|||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct lm63_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
int nr = attr->index;
|
||||
unsigned long val;
|
||||
int err;
|
||||
u8 reg;
|
||||
|
||||
if (!(data->config_fan & 0x20)) /* register is read-only */
|
||||
return -EPERM;
|
||||
|
|
@ -404,15 +471,11 @@ static ssize_t set_pwm1(struct device *dev, struct device_attribute *devattr,
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
reg = nr ? LM63_REG_LUT_PWM(nr - 1) : LM63_REG_PWM_VALUE;
|
||||
val = clamp_val(val, 0, 255);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->pwm1[nr] = data->pwm_highres ? val :
|
||||
(val * data->pwm1_freq * 2 + 127) / 255;
|
||||
i2c_smbus_write_byte_data(client, reg, data->pwm1[nr]);
|
||||
err = lm63_write_pwm(data, nr, val);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
|
||||
return err ? err : count;
|
||||
}
|
||||
|
||||
static ssize_t pwm1_enable_show(struct device *dev,
|
||||
|
|
@ -485,7 +548,9 @@ static ssize_t pwm1_freq_store(struct device *dev,
|
|||
struct lm63_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
unsigned long val, pfr_fast, pfr_slow, err_fast, err_slow, pfr;
|
||||
u8 config_fan;
|
||||
bool slow_clock;
|
||||
int config_enhanced = 0;
|
||||
int ret;
|
||||
|
||||
ret = kstrtoul(buf, 10, &val);
|
||||
|
|
@ -511,35 +576,58 @@ static ssize_t pwm1_freq_store(struct device *dev,
|
|||
|
||||
mutex_lock(&data->update_lock);
|
||||
ret = i2c_smbus_read_byte_data(client, LM63_REG_CONFIG_FAN);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&data->update_lock);
|
||||
return ret;
|
||||
}
|
||||
data->config_fan = ret;
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
config_fan = ret;
|
||||
data->config_fan = config_fan;
|
||||
|
||||
if (!(data->config_fan & 0x20)) { /* register is read-only */
|
||||
mutex_unlock(&data->update_lock);
|
||||
return -EPERM;
|
||||
if (!(config_fan & 0x20)) { /* register is read-only */
|
||||
ret = -EPERM;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (data->kind == lm96163) {
|
||||
ret = i2c_smbus_read_byte_data(client, LM96163_REG_CONFIG_ENHANCED);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&data->update_lock);
|
||||
return ret;
|
||||
if (data->kind == lm96163 || data->kind == ctf2301) {
|
||||
ret = i2c_smbus_read_byte_data(client,
|
||||
LM96163_REG_CONFIG_ENHANCED);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
config_enhanced = ret;
|
||||
lm63_update_pwm_highres(data, config_enhanced);
|
||||
|
||||
if (data->kind == ctf2301) {
|
||||
if (!slow_clock && pfr == 8)
|
||||
config_enhanced |= 0x10;
|
||||
else
|
||||
config_enhanced &= ~0x10;
|
||||
ret = i2c_smbus_write_byte_data(client,
|
||||
LM96163_REG_CONFIG_ENHANCED,
|
||||
config_enhanced);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
lm63_update_pwm_highres(data, config_enhanced);
|
||||
}
|
||||
data->pwm_highres = !slow_clock && pfr == 8 && (ret & 0x10);
|
||||
}
|
||||
|
||||
if (slow_clock)
|
||||
data->config_fan |= 0x08;
|
||||
config_fan |= 0x08;
|
||||
else
|
||||
data->config_fan &= ~0x08;
|
||||
i2c_smbus_write_byte_data(client, LM63_REG_CONFIG_FAN, data->config_fan);
|
||||
i2c_smbus_write_byte_data(client, LM63_REG_PWM_FREQ, pfr);
|
||||
config_fan &= ~0x08;
|
||||
ret = i2c_smbus_write_byte_data(client, LM63_REG_PWM_FREQ, pfr);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
data->pwm1_freq = pfr;
|
||||
lm63_update_pwm_highres(data, config_enhanced);
|
||||
ret = i2c_smbus_write_byte_data(client, LM63_REG_CONFIG_FAN,
|
||||
config_fan);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
data->config_fan = config_fan;
|
||||
lm63_update_pwm_highres(data, config_enhanced);
|
||||
unlock:
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
|
||||
return ret ? ret : count;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -554,7 +642,17 @@ static ssize_t show_local_temp8(struct device *dev,
|
|||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct lm63_data *data = lm63_update_device(dev);
|
||||
return sprintf(buf, "%d\n", TEMP8_FROM_REG(data->temp8[attr->index]));
|
||||
int temp;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
if (data->kind == ctf2301)
|
||||
temp = TEMP12_FROM_REG(attr->index ? data->temp1_max :
|
||||
data->temp1_input);
|
||||
else
|
||||
temp = TEMP8_FROM_REG(data->temp8[attr->index]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return sprintf(buf, "%d\n", temp);
|
||||
}
|
||||
|
||||
static ssize_t show_remote_temp8(struct device *dev,
|
||||
|
|
@ -594,6 +692,21 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr,
|
|||
return err;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
if (data->kind == ctf2301 && nr == 1) {
|
||||
temp = TEMP12_TO_REG(val);
|
||||
err = i2c_smbus_write_byte_data(client, LM63_REG_LOCAL_HIGH,
|
||||
temp >> 8);
|
||||
if (!err)
|
||||
err = i2c_smbus_write_byte_data(client,
|
||||
CTF2301_REG_LOCAL_HIGH_LSB,
|
||||
temp & 0xf0);
|
||||
if (!err)
|
||||
data->temp1_max = temp;
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return err ? err : count;
|
||||
}
|
||||
|
||||
switch (nr) {
|
||||
case 2:
|
||||
reg = LM63_REG_REMOTE_TCRIT;
|
||||
|
|
@ -610,10 +723,12 @@ static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr,
|
|||
reg = LM63_REG_LUT_TEMP(nr - 3);
|
||||
temp = lut_temp_to_reg(data, val);
|
||||
}
|
||||
data->temp8[nr] = temp;
|
||||
i2c_smbus_write_byte_data(client, reg, temp);
|
||||
err = i2c_smbus_write_byte_data(client, reg, temp);
|
||||
if (!err)
|
||||
data->temp8[nr] = temp;
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
|
||||
return err ? err : count;
|
||||
}
|
||||
|
||||
static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr,
|
||||
|
|
@ -787,11 +902,40 @@ static ssize_t temp2_crit_hyst_store(struct device *dev,
|
|||
* Set conversion rate.
|
||||
* client->update_lock must be held when calling this function.
|
||||
*/
|
||||
static void lm63_set_convrate(struct lm63_data *data, unsigned int interval)
|
||||
static unsigned int lm63_update_interval(struct lm63_data *data, u8 rate)
|
||||
{
|
||||
if (data->kind == ctf2301)
|
||||
return ctf2301_update_intervals[min_t(unsigned int, rate,
|
||||
ARRAY_SIZE(ctf2301_update_intervals) - 1)];
|
||||
|
||||
return UPDATE_INTERVAL(data->max_convrate_hz, rate);
|
||||
}
|
||||
|
||||
static int lm63_set_convrate(struct lm63_data *data, unsigned int interval)
|
||||
{
|
||||
struct i2c_client *client = data->client;
|
||||
unsigned int update_interval;
|
||||
int i;
|
||||
unsigned int update_interval, error, best_error = ~0U;
|
||||
unsigned int best = 0;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
if (data->kind == ctf2301) {
|
||||
for (i = 0; i < ARRAY_SIZE(ctf2301_update_intervals); i++) {
|
||||
error = abs_diff(interval, ctf2301_update_intervals[i]);
|
||||
if (error < best_error) {
|
||||
best_error = error;
|
||||
best = i;
|
||||
}
|
||||
}
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client, LM63_REG_CONVRATE,
|
||||
best);
|
||||
if (ret)
|
||||
return ret;
|
||||
data->update_interval = ctf2301_update_intervals[best];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Shift calculations to avoid rounding errors */
|
||||
interval <<= 6;
|
||||
|
|
@ -803,8 +947,12 @@ static void lm63_set_convrate(struct lm63_data *data, unsigned int interval)
|
|||
if (interval >= update_interval * 3 / 4)
|
||||
break;
|
||||
|
||||
i2c_smbus_write_byte_data(client, LM63_REG_CONVRATE, i);
|
||||
ret = i2c_smbus_write_byte_data(client, LM63_REG_CONVRATE, i);
|
||||
if (ret)
|
||||
return ret;
|
||||
data->update_interval = UPDATE_INTERVAL(data->max_convrate_hz, i);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t update_interval_show(struct device *dev,
|
||||
|
|
@ -828,10 +976,10 @@ static ssize_t update_interval_store(struct device *dev,
|
|||
return err;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
lm63_set_convrate(data, clamp_val(val, 0, 100000));
|
||||
err = lm63_set_convrate(data, clamp_val(val, 0, 100000));
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return count;
|
||||
return err ? err : count;
|
||||
}
|
||||
|
||||
static ssize_t temp2_type_show(struct device *dev,
|
||||
|
|
@ -996,6 +1144,75 @@ static DEVICE_ATTR_RO(alarms);
|
|||
|
||||
static DEVICE_ATTR_RW(update_interval);
|
||||
|
||||
static int lm63_get_max_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long *state)
|
||||
{
|
||||
*state = LM63_MAX_PWM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lm63_get_cur_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long *state)
|
||||
{
|
||||
struct lm63_data *data = cdev->devdata;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
ret = i2c_smbus_read_byte_data(data->client, LM63_REG_PWM_VALUE);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
|
||||
data->pwm1[0] = ret;
|
||||
*state = lm63_pwm_from_reg(data, data->pwm1[0]);
|
||||
unlock:
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return ret < 0 ? ret : 0;
|
||||
}
|
||||
|
||||
static int lm63_set_cur_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long state)
|
||||
{
|
||||
struct lm63_data *data = cdev->devdata;
|
||||
int config_fan;
|
||||
int ret;
|
||||
|
||||
if (state > LM63_MAX_PWM)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
config_fan = i2c_smbus_read_byte_data(data->client,
|
||||
LM63_REG_CONFIG_FAN);
|
||||
if (config_fan < 0) {
|
||||
ret = config_fan;
|
||||
goto unlock;
|
||||
}
|
||||
data->config_fan = config_fan;
|
||||
|
||||
if (!(config_fan & 0x20)) {
|
||||
config_fan |= 0x20;
|
||||
ret = i2c_smbus_write_byte_data(data->client,
|
||||
LM63_REG_CONFIG_FAN,
|
||||
config_fan);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
data->config_fan = config_fan;
|
||||
}
|
||||
|
||||
ret = lm63_write_pwm(data, 0, state);
|
||||
unlock:
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct thermal_cooling_device_ops lm63_cooling_ops = {
|
||||
.get_max_state = lm63_get_max_state,
|
||||
.get_cur_state = lm63_get_cur_state,
|
||||
.set_cur_state = lm63_set_cur_state,
|
||||
};
|
||||
|
||||
static struct attribute *lm63_attributes[] = {
|
||||
&sensor_dev_attr_pwm1.dev_attr.attr,
|
||||
&dev_attr_pwm1_enable.attr,
|
||||
|
|
@ -1078,8 +1295,8 @@ static const struct attribute_group lm63_group_extra_lut = {
|
|||
* On LM63, temp2_crit can be set only once, which should be job
|
||||
* of the bootloader.
|
||||
* On LM64, temp2_crit can always be set.
|
||||
* On LM96163, temp2_crit can be set if bit 1 of the configuration
|
||||
* register is true.
|
||||
* On LM96163 and CTF2301, temp2_crit can be set if bit 1 of the
|
||||
* configuration register is true.
|
||||
*/
|
||||
static umode_t lm63_attribute_mode(struct kobject *kobj,
|
||||
struct attribute *attr, int index)
|
||||
|
|
@ -1089,7 +1306,8 @@ static umode_t lm63_attribute_mode(struct kobject *kobj,
|
|||
|
||||
if (attr == &sensor_dev_attr_temp2_crit.dev_attr.attr
|
||||
&& (data->kind == lm64 ||
|
||||
(data->kind == lm96163 && (data->config & 0x02))))
|
||||
((data->kind == lm96163 || data->kind == ctf2301) &&
|
||||
(data->config & 0x02))))
|
||||
return attr->mode | S_IWUSR;
|
||||
|
||||
return attr->mode;
|
||||
|
|
@ -1164,29 +1382,48 @@ static int lm63_detect(struct i2c_client *client,
|
|||
* Ideally we shouldn't have to initialize anything, since the BIOS
|
||||
* should have taken care of everything
|
||||
*/
|
||||
static void lm63_init_client(struct lm63_data *data)
|
||||
static int lm63_init_client(struct lm63_data *data)
|
||||
{
|
||||
struct i2c_client *client = data->client;
|
||||
struct device *dev = &client->dev;
|
||||
u8 convrate;
|
||||
int ret;
|
||||
|
||||
data->config = i2c_smbus_read_byte_data(client, LM63_REG_CONFIG1);
|
||||
data->config_fan = i2c_smbus_read_byte_data(client,
|
||||
LM63_REG_CONFIG_FAN);
|
||||
ret = i2c_smbus_read_byte_data(client, LM63_REG_CONFIG1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->config = ret;
|
||||
ret = i2c_smbus_read_byte_data(client, LM63_REG_CONFIG_FAN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->config_fan = ret;
|
||||
|
||||
/* Start converting if needed */
|
||||
if (data->config & 0x40) { /* standby */
|
||||
dev_dbg(dev, "Switching to operational mode\n");
|
||||
data->config &= 0xA7;
|
||||
i2c_smbus_write_byte_data(client, LM63_REG_CONFIG1,
|
||||
data->config);
|
||||
ret = i2c_smbus_write_byte_data(client, LM63_REG_CONFIG1,
|
||||
data->config);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
/* Tachometer is always enabled on LM64 */
|
||||
if (data->kind == lm64)
|
||||
data->config |= 0x04;
|
||||
if (data->kind == ctf2301 && !client->irq && !(data->config & 0x04)) {
|
||||
dev_dbg(dev, "Configuring ALERT/TACH pin for tachometer input\n");
|
||||
data->config |= 0x04;
|
||||
ret = i2c_smbus_write_byte_data(client, LM63_REG_CONFIG1,
|
||||
data->config);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* We may need pwm1_freq before ever updating the client data */
|
||||
data->pwm1_freq = i2c_smbus_read_byte_data(client, LM63_REG_PWM_FREQ) & 0x1f;
|
||||
ret = i2c_smbus_read_byte_data(client, LM63_REG_PWM_FREQ);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->pwm1_freq = ret & 0x1f;
|
||||
if (data->pwm1_freq == 0)
|
||||
data->pwm1_freq = 1;
|
||||
|
||||
|
|
@ -1199,25 +1436,38 @@ static void lm63_init_client(struct lm63_data *data)
|
|||
case lm96163:
|
||||
data->max_convrate_hz = LM96163_MAX_CONVRATE_HZ;
|
||||
data->lut_size = 12;
|
||||
data->trutherm
|
||||
= i2c_smbus_read_byte_data(client,
|
||||
LM96163_REG_TRUTHERM) & 0x02;
|
||||
ret = i2c_smbus_read_byte_data(client, LM96163_REG_TRUTHERM);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
data->trutherm = ret & 0x02;
|
||||
break;
|
||||
case ctf2301:
|
||||
data->lut_size = 12;
|
||||
break;
|
||||
}
|
||||
convrate = i2c_smbus_read_byte_data(client, LM63_REG_CONVRATE);
|
||||
if (unlikely(convrate > LM63_MAX_CONVRATE))
|
||||
ret = i2c_smbus_read_byte_data(client, LM63_REG_CONVRATE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
convrate = ret;
|
||||
if (data->kind == ctf2301 &&
|
||||
convrate >= ARRAY_SIZE(ctf2301_update_intervals))
|
||||
convrate = ARRAY_SIZE(ctf2301_update_intervals) - 1;
|
||||
else if (unlikely(convrate > LM63_MAX_CONVRATE))
|
||||
convrate = LM63_MAX_CONVRATE;
|
||||
data->update_interval = UPDATE_INTERVAL(data->max_convrate_hz,
|
||||
convrate);
|
||||
data->update_interval = lm63_update_interval(data, convrate);
|
||||
|
||||
/*
|
||||
* For LM96163, check if high resolution PWM
|
||||
* For LM96163 and CTF2301, check if high resolution PWM
|
||||
* and unsigned temperature format is enabled.
|
||||
*/
|
||||
if (data->kind == lm96163) {
|
||||
u8 config_enhanced
|
||||
= i2c_smbus_read_byte_data(client,
|
||||
LM96163_REG_CONFIG_ENHANCED);
|
||||
if (data->kind == lm96163 || data->kind == ctf2301) {
|
||||
u8 config_enhanced;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client,
|
||||
LM96163_REG_CONFIG_ENHANCED);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
config_enhanced = ret;
|
||||
if (config_enhanced & 0x20)
|
||||
data->lut_temp_highres = true;
|
||||
if ((config_enhanced & 0x10)
|
||||
|
|
@ -1238,6 +1488,8 @@ static void lm63_init_client(struct lm63_data *data)
|
|||
dev_dbg(dev, "PWM output active %s, %s mode\n",
|
||||
(data->config_fan & 0x10) ? "low" : "high",
|
||||
(data->config_fan & 0x20) ? "manual" : "auto");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id lm63_id[];
|
||||
|
|
@ -1245,9 +1497,11 @@ static const struct i2c_device_id lm63_id[];
|
|||
static int lm63_probe(struct i2c_client *client)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct thermal_cooling_device *cdev;
|
||||
struct device *hwmon_dev;
|
||||
struct lm63_data *data;
|
||||
int groups = 0;
|
||||
int ret;
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(struct lm63_data), GFP_KERNEL);
|
||||
if (!data)
|
||||
|
|
@ -1262,7 +1516,9 @@ static int lm63_probe(struct i2c_client *client)
|
|||
data->temp2_offset = 16000;
|
||||
|
||||
/* Initialize chip */
|
||||
lm63_init_client(data);
|
||||
ret = lm63_init_client(data);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "failed to initialize device\n");
|
||||
|
||||
/* Register sysfs hooks */
|
||||
data->groups[groups++] = &lm63_group;
|
||||
|
|
@ -1272,11 +1528,26 @@ static int lm63_probe(struct i2c_client *client)
|
|||
if (data->kind == lm96163) {
|
||||
data->groups[groups++] = &lm63_group_temp2_type;
|
||||
data->groups[groups++] = &lm63_group_extra_lut;
|
||||
} else if (data->kind == ctf2301) {
|
||||
data->groups[groups++] = &lm63_group_extra_lut;
|
||||
}
|
||||
|
||||
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
|
||||
data, data->groups);
|
||||
return PTR_ERR_OR_ZERO(hwmon_dev);
|
||||
if (IS_ERR(hwmon_dev))
|
||||
return PTR_ERR(hwmon_dev);
|
||||
|
||||
if (IS_ENABLED(CONFIG_THERMAL_OF) &&
|
||||
of_property_present(dev->of_node, "#cooling-cells")) {
|
||||
cdev = devm_thermal_of_cooling_device_register(dev, 0,
|
||||
client->name,
|
||||
data,
|
||||
&lm63_cooling_ops);
|
||||
if (IS_ERR(cdev))
|
||||
return PTR_ERR(cdev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1287,6 +1558,7 @@ static const struct i2c_device_id lm63_id[] = {
|
|||
{ .name = "lm63", .driver_data = lm63 },
|
||||
{ .name = "lm64", .driver_data = lm64 },
|
||||
{ .name = "lm96163", .driver_data = lm96163 },
|
||||
{ .name = "ctf2301", .driver_data = ctf2301 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, lm63_id);
|
||||
|
|
@ -1304,6 +1576,10 @@ static const struct of_device_id __maybe_unused lm63_of_match[] = {
|
|||
.compatible = "national,lm96163",
|
||||
.data = (void *)lm96163
|
||||
},
|
||||
{
|
||||
.compatible = "sensylink,ctf2301",
|
||||
.data = (void *)ctf2301
|
||||
},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, lm63_of_match);
|
||||
|
|
|
|||
|
|
@ -96,21 +96,21 @@ static ssize_t temp1_input_show(struct device *dev,
|
|||
*/
|
||||
switch (p_lm70->chip) {
|
||||
case LM70_CHIP_LM70:
|
||||
val = ((int)raw / 32) * 250;
|
||||
val = ((int)raw >> 5) * 250;
|
||||
break;
|
||||
|
||||
case LM70_CHIP_TMP121:
|
||||
case LM70_CHIP_TMP122:
|
||||
case LM70_CHIP_LM74:
|
||||
val = ((int)raw / 8) * 625 / 10;
|
||||
val = ((int)raw >> 3) * 625 / 10;
|
||||
break;
|
||||
|
||||
case LM70_CHIP_LM71:
|
||||
val = ((int)raw / 4) * 3125 / 100;
|
||||
val = ((int)raw >> 2) * 3125 / 100;
|
||||
break;
|
||||
|
||||
case LM70_CHIP_TMP125:
|
||||
val = (sign_extend32(raw, 14) / 32) * 250;
|
||||
val = (sign_extend32(raw, 14) >> 5) * 250;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1445,7 +1445,7 @@ static void lm85_init_client(struct i2c_client *client)
|
|||
static int lm85_is_fake(struct i2c_client *client)
|
||||
{
|
||||
/*
|
||||
* Differenciate between real LM96000 and Winbond WPCD377I. The latter
|
||||
* Differentiate between real LM96000 and Winbond WPCD377I. The latter
|
||||
* emulate the former except that it has no hardware monitoring function
|
||||
* so the readings are always 0.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -2712,6 +2712,11 @@ static int lm90_probe_channel(struct i2c_client *client,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (id == 2 && !(data->flags & LM90_HAVE_TEMP3)) {
|
||||
dev_err(dev, "channel %d is not supported for this chip in %pfw\n", id, child);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = fwnode_property_read_string(child, "label", &data->channel_label[id]);
|
||||
if (err == -ENODATA || err == -EILSEQ) {
|
||||
dev_err(dev, "invalid label property in %pfw\n", child);
|
||||
|
|
|
|||
|
|
@ -96,9 +96,9 @@ static int temp_from_reg_unsigned(u8 val_h, u8 val_l)
|
|||
|
||||
static int temp_from_reg_signed(u8 val_h, u8 val_l)
|
||||
{
|
||||
if (val_h & 0x80)
|
||||
return (val_h - 0x100) * 1000;
|
||||
return temp_from_reg_unsigned(val_h, val_l);
|
||||
s16 val_hl = (val_h << 8) | val_l;
|
||||
|
||||
return val_hl * 1000 / 256;
|
||||
}
|
||||
|
||||
static int lm95245_read_conversion_rate(struct lm95245_data *data)
|
||||
|
|
|
|||
|
|
@ -624,6 +624,7 @@ static int mcp9982_write(struct device *dev, enum hwmon_sensor_types type, u32 a
|
|||
{
|
||||
struct mcp9982_priv *priv = dev_get_drvdata(dev);
|
||||
unsigned int idx;
|
||||
int ret;
|
||||
u8 addr;
|
||||
|
||||
switch (type) {
|
||||
|
|
@ -640,9 +641,13 @@ static int mcp9982_write(struct device *dev, enum hwmon_sensor_types type, u32 a
|
|||
|
||||
idx = find_closest_descending(val, mcp9982_update_interval,
|
||||
ARRAY_SIZE(mcp9982_update_interval));
|
||||
ret = regmap_write(priv->regmap, MCP9982_CONV_ADDR, idx);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
priv->interval_idx = idx;
|
||||
|
||||
return regmap_write(priv->regmap, MCP9982_CONV_ADDR, idx);
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
|
|||
503
drivers/hwmon/minisforum-um780xtx.c
Normal file
503
drivers/hwmon/minisforum-um780xtx.c
Normal file
|
|
@ -0,0 +1,503 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Minisforum UM780 XTX (F7BSD) embedded-controller hwmon driver.
|
||||
*
|
||||
* Copyright (C) 2026 Sebastián Peyrott <speyrott@gmail.com>
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#define UM780XTX_EC_TEMP_SYS 0x05
|
||||
#define UM780XTX_EC_TEMP_CPU 0x09
|
||||
#define UM780XTX_EC_CPU_PROFILE 0x2f
|
||||
#define UM780XTX_EC_SYS_POINT1 0x31
|
||||
#define UM780XTX_EC_SYS_POINT2 0x34
|
||||
#define UM780XTX_EC_SYS_POINT3 0x37
|
||||
|
||||
#define UM780XTX_EC_PROFILE_B1 0xb1
|
||||
#define UM780XTX_EC_PROFILE_B2 0xb2
|
||||
|
||||
#define UM780XTX_EC_FAN1_LO 0xb6
|
||||
#define UM780XTX_EC_FAN1_HI 0xb7
|
||||
#define UM780XTX_EC_FAN2_LO 0xb9
|
||||
#define UM780XTX_EC_FAN2_HI 0xba
|
||||
|
||||
#define UM780XTX_EC_MAX_RPM 9000
|
||||
#define UM780XTX_EC_RPM_RETRIES 3
|
||||
|
||||
static struct platform_device *um780xtx_pdev;
|
||||
|
||||
struct um780xtx_data {
|
||||
struct device *hwmon_dev;
|
||||
u8 saved_profile;
|
||||
u8 saved_sys_point1;
|
||||
u8 saved_sys_point2;
|
||||
u8 sys_point3;
|
||||
};
|
||||
|
||||
static const struct dmi_system_id um780xtx_dmi_table[] = {
|
||||
{
|
||||
.matches = {
|
||||
DMI_EXACT_MATCH(DMI_SYS_VENDOR,
|
||||
"Micro Computer (HK) Tech Limited"),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Venus series"),
|
||||
DMI_EXACT_MATCH(DMI_BOARD_VENDOR,
|
||||
"Shenzhen Meigao Electronic Equipment Co.,Ltd"),
|
||||
DMI_EXACT_MATCH(DMI_BOARD_NAME, "F7BSD"),
|
||||
},
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(dmi, um780xtx_dmi_table);
|
||||
|
||||
static bool um780xtx_firmware_match(void)
|
||||
{
|
||||
const char *board_version = dmi_get_system_info(DMI_BOARD_VERSION);
|
||||
const char *bios_version = dmi_get_system_info(DMI_BIOS_VERSION);
|
||||
|
||||
return board_version && bios_version &&
|
||||
!strcmp(board_version, "1.1") && !strcmp(bios_version, "1.06");
|
||||
}
|
||||
|
||||
static int um780xtx_oem_read(u8 command, u8 *value)
|
||||
{
|
||||
return ec_transaction(command, NULL, 0, value, 1);
|
||||
}
|
||||
|
||||
static int um780xtx_read_rpm(u8 command_hi, u8 command_lo, long *rpm)
|
||||
{
|
||||
u8 hi_before;
|
||||
u8 hi_after;
|
||||
u8 lo;
|
||||
unsigned int value;
|
||||
int attempt;
|
||||
int ret;
|
||||
|
||||
for (attempt = 0; attempt < UM780XTX_EC_RPM_RETRIES; attempt++) {
|
||||
ret = um780xtx_oem_read(command_hi, &hi_before);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = um780xtx_oem_read(command_lo, &lo);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = um780xtx_oem_read(command_hi, &hi_after);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (hi_before != hi_after)
|
||||
continue;
|
||||
|
||||
value = (hi_after << 8) | lo;
|
||||
if (value > UM780XTX_EC_MAX_RPM)
|
||||
continue;
|
||||
|
||||
*rpm = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int um780xtx_read_profile(long *mode)
|
||||
{
|
||||
u8 profile;
|
||||
int ret;
|
||||
|
||||
ret = ec_read(UM780XTX_EC_CPU_PROFILE, &profile);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (profile == UM780XTX_EC_PROFILE_B1) {
|
||||
*mode = 2;
|
||||
return 0;
|
||||
}
|
||||
if (profile == UM780XTX_EC_PROFILE_B2) {
|
||||
*mode = 3;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
static int um780xtx_write_profile(struct um780xtx_data *data, long mode)
|
||||
{
|
||||
u8 expected;
|
||||
u8 profile;
|
||||
int ret;
|
||||
|
||||
if (mode != 2 && mode != 3)
|
||||
return -EINVAL;
|
||||
expected = mode == 2 ? UM780XTX_EC_PROFILE_B1 : UM780XTX_EC_PROFILE_B2;
|
||||
|
||||
ret = ec_transaction(expected, NULL, 0, NULL, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = ec_read(UM780XTX_EC_CPU_PROFILE, &profile);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (profile != expected)
|
||||
return -EIO;
|
||||
|
||||
data->saved_profile = profile;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const u8 um780xtx_sys_point_offsets[] = {
|
||||
UM780XTX_EC_SYS_POINT1,
|
||||
UM780XTX_EC_SYS_POINT2,
|
||||
};
|
||||
|
||||
static ssize_t um780xtx_sys_point_temp_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
|
||||
u8 value;
|
||||
int ret;
|
||||
|
||||
guard(hwmon_lock)(dev);
|
||||
ret = ec_read(um780xtx_sys_point_offsets[sattr->index], &value);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return sysfs_emit(buf, "%u\n", value * 1000);
|
||||
}
|
||||
|
||||
static ssize_t um780xtx_sys_point_temp_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct um780xtx_data *data = dev_get_drvdata(dev);
|
||||
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
|
||||
u8 points[2];
|
||||
u8 readback;
|
||||
long value, lo, hi;
|
||||
int ret;
|
||||
|
||||
ret = kstrtol(buf, 10, &value);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
guard(hwmon_lock)(dev);
|
||||
/* Validate and clamp against the live peer threshold. */
|
||||
ret = ec_read(UM780XTX_EC_SYS_POINT1, &points[0]);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = ec_read(UM780XTX_EC_SYS_POINT2, &points[1]);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (points[0] >= points[1] || points[1] >= data->sys_point3)
|
||||
return -EIO;
|
||||
|
||||
if (!sattr->index) {
|
||||
lo = 0;
|
||||
hi = points[1] - 1;
|
||||
} else {
|
||||
lo = points[0] + 1;
|
||||
hi = data->sys_point3 - 1;
|
||||
}
|
||||
value = DIV_ROUND_CLOSEST(clamp_val(value, 0, 255000), 1000);
|
||||
points[sattr->index] = clamp_val(value, lo, hi);
|
||||
|
||||
ret = ec_write(um780xtx_sys_point_offsets[sattr->index],
|
||||
points[sattr->index]);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = ec_read(um780xtx_sys_point_offsets[sattr->index], &readback);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (readback != points[sattr->index])
|
||||
return -EIO;
|
||||
|
||||
data->saved_sys_point1 = points[0];
|
||||
data->saved_sys_point2 = points[1];
|
||||
return count;
|
||||
}
|
||||
|
||||
static SENSOR_DEVICE_ATTR_RW(pwm2_auto_point1_temp,
|
||||
um780xtx_sys_point_temp, 0);
|
||||
static SENSOR_DEVICE_ATTR_RW(pwm2_auto_point2_temp,
|
||||
um780xtx_sys_point_temp, 1);
|
||||
|
||||
static struct attribute *um780xtx_extra_attrs[] = {
|
||||
&sensor_dev_attr_pwm2_auto_point1_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm2_auto_point2_temp.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group um780xtx_extra_group = {
|
||||
.attrs = um780xtx_extra_attrs,
|
||||
};
|
||||
|
||||
static const struct attribute_group *um780xtx_extra_groups[] = {
|
||||
&um780xtx_extra_group,
|
||||
NULL
|
||||
};
|
||||
|
||||
static umode_t um780xtx_is_visible(const void *data,
|
||||
enum hwmon_sensor_types type,
|
||||
u32 attr, int channel)
|
||||
{
|
||||
if (type == hwmon_temp || type == hwmon_fan)
|
||||
return 0444;
|
||||
if (type == hwmon_pwm && attr == hwmon_pwm_enable)
|
||||
return 0644;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int um780xtx_read(struct device *dev, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel, long *value)
|
||||
{
|
||||
u8 raw;
|
||||
int ret;
|
||||
|
||||
if (type == hwmon_temp) {
|
||||
ret = ec_read(channel ? UM780XTX_EC_TEMP_SYS :
|
||||
UM780XTX_EC_TEMP_CPU, &raw);
|
||||
if (ret)
|
||||
return ret;
|
||||
*value = raw * 1000L;
|
||||
return 0;
|
||||
}
|
||||
if (type == hwmon_fan) {
|
||||
if (!channel)
|
||||
ret = um780xtx_read_rpm(UM780XTX_EC_FAN1_HI,
|
||||
UM780XTX_EC_FAN1_LO, value);
|
||||
else
|
||||
ret = um780xtx_read_rpm(UM780XTX_EC_FAN2_HI,
|
||||
UM780XTX_EC_FAN2_LO, value);
|
||||
return ret;
|
||||
}
|
||||
if (type == hwmon_pwm)
|
||||
return um780xtx_read_profile(value);
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static int um780xtx_write(struct device *dev, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel, long value)
|
||||
{
|
||||
struct um780xtx_data *data = dev_get_drvdata(dev);
|
||||
|
||||
if (type == hwmon_pwm && attr == hwmon_pwm_enable)
|
||||
return um780xtx_write_profile(data, value);
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static int um780xtx_read_string(struct device *dev,
|
||||
enum hwmon_sensor_types type,
|
||||
u32 attr, int channel, const char **str)
|
||||
{
|
||||
static const char * const labels[] = { "CPU", "SYS" };
|
||||
|
||||
if (type == hwmon_temp && attr == hwmon_temp_label)
|
||||
*str = labels[channel];
|
||||
else if (type == hwmon_fan && attr == hwmon_fan_label)
|
||||
*str = labels[channel];
|
||||
else
|
||||
return -EOPNOTSUPP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct hwmon_ops um780xtx_hwmon_ops = {
|
||||
.is_visible = um780xtx_is_visible,
|
||||
.read = um780xtx_read,
|
||||
.write = um780xtx_write,
|
||||
.read_string = um780xtx_read_string,
|
||||
};
|
||||
|
||||
static const struct hwmon_channel_info * const um780xtx_info[] = {
|
||||
HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_LABEL,
|
||||
HWMON_T_INPUT | HWMON_T_LABEL),
|
||||
HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_LABEL,
|
||||
HWMON_F_INPUT | HWMON_F_LABEL),
|
||||
HWMON_CHANNEL_INFO(pwm, HWMON_PWM_ENABLE),
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct hwmon_chip_info um780xtx_chip_info = {
|
||||
.ops = &um780xtx_hwmon_ops,
|
||||
.info = um780xtx_info,
|
||||
};
|
||||
|
||||
static int um780xtx_write_sys_point(u8 offset, u8 value)
|
||||
{
|
||||
u8 readback;
|
||||
int ret;
|
||||
|
||||
ret = ec_write(offset, value);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = ec_read(offset, &readback);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return readback == value ? 0 : -EIO;
|
||||
}
|
||||
|
||||
static int um780xtx_restore_sys_points(struct um780xtx_data *data,
|
||||
u8 current_point2)
|
||||
{
|
||||
u8 point1 = data->saved_sys_point1;
|
||||
u8 point2 = data->saved_sys_point2;
|
||||
int ret;
|
||||
|
||||
/* Keep strict ordering valid after each individual EC write. */
|
||||
if (point1 >= current_point2) {
|
||||
ret = um780xtx_write_sys_point(UM780XTX_EC_SYS_POINT2, point2);
|
||||
if (ret)
|
||||
return ret;
|
||||
return um780xtx_write_sys_point(UM780XTX_EC_SYS_POINT1,
|
||||
point1);
|
||||
}
|
||||
|
||||
ret = um780xtx_write_sys_point(UM780XTX_EC_SYS_POINT1, point1);
|
||||
if (ret)
|
||||
return ret;
|
||||
return um780xtx_write_sys_point(UM780XTX_EC_SYS_POINT2, point2);
|
||||
}
|
||||
|
||||
static int um780xtx_read_initial_state(struct um780xtx_data *data)
|
||||
{
|
||||
u8 profile;
|
||||
u8 point1;
|
||||
u8 point2;
|
||||
int ret;
|
||||
|
||||
ret = ec_read(UM780XTX_EC_CPU_PROFILE, &profile);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (profile != UM780XTX_EC_PROFILE_B1 &&
|
||||
profile != UM780XTX_EC_PROFILE_B2)
|
||||
return -EINVAL;
|
||||
|
||||
ret = ec_read(UM780XTX_EC_SYS_POINT1, &point1);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = ec_read(UM780XTX_EC_SYS_POINT2, &point2);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (point1 >= point2 || point2 >= data->sys_point3)
|
||||
return -EINVAL;
|
||||
|
||||
data->saved_profile = profile;
|
||||
data->saved_sys_point1 = point1;
|
||||
data->saved_sys_point2 = point2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int um780xtx_restore_state(struct um780xtx_data *data)
|
||||
{
|
||||
u8 readback;
|
||||
u8 point2;
|
||||
int ret;
|
||||
|
||||
ret = ec_transaction(data->saved_profile, NULL, 0, NULL, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = ec_read(UM780XTX_EC_CPU_PROFILE, &readback);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (readback != data->saved_profile)
|
||||
return -EIO;
|
||||
|
||||
ret = ec_read(UM780XTX_EC_SYS_POINT2, &point2);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return um780xtx_restore_sys_points(data, point2);
|
||||
}
|
||||
|
||||
static int um780xtx_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct um780xtx_data *data;
|
||||
struct device *hwmon;
|
||||
int ret;
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
platform_set_drvdata(pdev, data);
|
||||
|
||||
ret = ec_read(UM780XTX_EC_SYS_POINT3, &data->sys_point3);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"failed to read system fan limit\n");
|
||||
ret = um780xtx_read_initial_state(data);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"failed to read initial fan settings\n");
|
||||
|
||||
hwmon = devm_hwmon_device_register_with_info(dev, "um780xtx_ec", data,
|
||||
&um780xtx_chip_info,
|
||||
um780xtx_extra_groups);
|
||||
if (IS_ERR(hwmon))
|
||||
return PTR_ERR(hwmon);
|
||||
data->hwmon_dev = hwmon;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int um780xtx_resume(struct device *dev)
|
||||
{
|
||||
struct um780xtx_data *data = dev_get_drvdata(dev);
|
||||
|
||||
guard(hwmon_lock)(data->hwmon_dev);
|
||||
return um780xtx_restore_state(data);
|
||||
}
|
||||
|
||||
static DEFINE_SIMPLE_DEV_PM_OPS(um780xtx_pm_ops, NULL, um780xtx_resume);
|
||||
|
||||
static struct platform_driver um780xtx_driver = {
|
||||
.driver = {
|
||||
.name = "um780xtx-ec-hwmon",
|
||||
.pm = pm_sleep_ptr(&um780xtx_pm_ops),
|
||||
},
|
||||
.probe = um780xtx_probe,
|
||||
};
|
||||
|
||||
static int __init um780xtx_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!dmi_check_system(um780xtx_dmi_table) ||
|
||||
!um780xtx_firmware_match() || !ec_get_handle())
|
||||
return -ENODEV;
|
||||
ret = platform_driver_register(&um780xtx_driver);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
um780xtx_pdev = platform_device_register_simple("um780xtx-ec-hwmon",
|
||||
PLATFORM_DEVID_NONE,
|
||||
NULL, 0);
|
||||
if (IS_ERR(um780xtx_pdev)) {
|
||||
ret = PTR_ERR(um780xtx_pdev);
|
||||
platform_driver_unregister(&um780xtx_driver);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit um780xtx_exit(void)
|
||||
{
|
||||
platform_device_unregister(um780xtx_pdev);
|
||||
platform_driver_unregister(&um780xtx_driver);
|
||||
}
|
||||
|
||||
module_init(um780xtx_init);
|
||||
module_exit(um780xtx_exit);
|
||||
|
||||
MODULE_AUTHOR("Sebastián Peyrott <speyrott@gmail.com>");
|
||||
MODULE_DESCRIPTION("Minisforum UM780 XTX EC hwmon driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -21,6 +21,8 @@
|
|||
* Chip #vin #fan #pwm #temp chip IDs man ID
|
||||
* nct6106d 9 3 3 6+3 0xc450 0xc1 0x5ca3
|
||||
* nct6116d 9 5 5 3+3 0xd280 0xc1 0x5ca3
|
||||
* nct6126d 10 5 5 3+3 0xd283 0xc1 0x5ca3
|
||||
* (0xd284)
|
||||
* nct6775f 9 4 3 6+3 0xb470 0xc1 0x5ca3
|
||||
* nct6776f 9 5 3 6+3 0xc330 0xc1 0x5ca3
|
||||
* nct6779d 15 5 5 2+6 0xc560 0xc1 0x5ca3
|
||||
|
|
@ -66,6 +68,7 @@
|
|||
static const char * const nct6775_device_names[] = {
|
||||
[nct6106] = "nct6106",
|
||||
[nct6116] = "nct6116",
|
||||
[nct6126] = "nct6126",
|
||||
[nct6775] = "nct6775",
|
||||
[nct6776] = "nct6776",
|
||||
[nct6779] = "nct6779",
|
||||
|
|
@ -897,6 +900,31 @@ static const s8 NCT6116_BEEP_BITS[NUM_BEEP_BITS] = {
|
|||
|
||||
static const u16 NCT6116_REG_TSI_TEMP[] = { 0x59, 0x5b };
|
||||
|
||||
/* NCT6122D/NCT6126D specific data */
|
||||
|
||||
static const u16 NCT6126_REG_IN[] = {
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09 };
|
||||
static const u16 NCT6126_REG_IN_MAX[] = {
|
||||
0x90, 0x92, 0x94, 0x96, 0x98, 0x9a, 0x9c, 0x9e, 0xa0, 0xa2 };
|
||||
static const u16 NCT6126_REG_IN_MIN[] = {
|
||||
0x91, 0x93, 0x95, 0x97, 0x99, 0x9b, 0x9d, 0x9f, 0xa1, 0xa3 };
|
||||
|
||||
static const s8 NCT6126_ALARM_BITS[NUM_ALARM_BITS] = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, /* in0-in11 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* in12-in23 */
|
||||
32, 33, 34, 35, 36, -1, -1, -1, -1, -1, -1, -1, /* fan1-fan12 */
|
||||
16, 17, 18, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* temp1-temp12 */
|
||||
48, -1, /* intr0-intr1 */
|
||||
};
|
||||
|
||||
static const s8 NCT6126_BEEP_BITS[NUM_BEEP_BITS] = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, /* in0-in11 */
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* in12-in23 */
|
||||
24, 25, 26, 27, 28, -1, -1, -1, -1, -1, -1, -1, /* fan1-fan12 */
|
||||
16, 17, 18, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* temp1-temp12 */
|
||||
34, -1, 32 /* intr0-intr1, beep_en */
|
||||
};
|
||||
|
||||
static enum pwm_enable reg_to_pwm_enable(int pwm, int mode)
|
||||
{
|
||||
if (mode == 0 && pwm == 255)
|
||||
|
|
@ -998,6 +1026,14 @@ static const u16 scale_in_6798[NUM_IN] = {
|
|||
800, 800, 800, 1600, 800
|
||||
};
|
||||
|
||||
/*
|
||||
* NCT6126 scaling:
|
||||
* CPUVCORE, VIN0, AVSB, 3VCC, VIN1, VIN2, VHIF, 3VSB, VBAT, VTT
|
||||
*/
|
||||
static const u16 scale_in_6126[10] = {
|
||||
800, 800, 1600, 1600, 800, 800, 1600, 1600, 1600, 800
|
||||
};
|
||||
|
||||
static inline long in_from_reg(u8 reg, u8 nr, const u16 *scales)
|
||||
{
|
||||
return DIV_ROUND_CLOSEST(reg * scales[nr], 100);
|
||||
|
|
@ -1156,6 +1192,7 @@ bool nct6775_reg_is_word_sized(struct nct6775_data *data, u16 reg)
|
|||
reg == 0xe0 || reg == 0xe2 || reg == 0xe4 ||
|
||||
reg == 0x111 || reg == 0x121 || reg == 0x131;
|
||||
case nct6116:
|
||||
case nct6126:
|
||||
return reg == 0x20 || reg == 0x22 || reg == 0x24 ||
|
||||
reg == 0x26 || reg == 0x28 || reg == 0x59 || reg == 0x5b ||
|
||||
reg == 0xe0 || reg == 0xe2 || reg == 0xe4 || reg == 0xe6 ||
|
||||
|
|
@ -1538,6 +1575,7 @@ static int nct6775_update_pwm_limits(struct device *dev)
|
|||
break;
|
||||
case nct6106:
|
||||
case nct6116:
|
||||
case nct6126:
|
||||
case nct6779:
|
||||
case nct6791:
|
||||
case nct6792:
|
||||
|
|
@ -3203,6 +3241,7 @@ store_auto_pwm(struct device *dev, struct device_attribute *attr,
|
|||
break; /* always enabled, nothing to do */
|
||||
case nct6106:
|
||||
case nct6116:
|
||||
case nct6126:
|
||||
case nct6779:
|
||||
case nct6791:
|
||||
case nct6792:
|
||||
|
|
@ -3677,6 +3716,83 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,
|
|||
reg_temp_crit_l = NCT6106_REG_TEMP_CRIT_L;
|
||||
reg_temp_crit_h = NCT6106_REG_TEMP_CRIT_H;
|
||||
|
||||
break;
|
||||
case nct6126:
|
||||
data->in_num = 10;
|
||||
data->scale_in = scale_in_6126;
|
||||
data->pwm_num = 5;
|
||||
data->auto_pwm_num = 4;
|
||||
data->temp_fixed_num = 3;
|
||||
data->num_temp_alarms = 3;
|
||||
data->num_temp_beeps = 3;
|
||||
|
||||
data->fan_from_reg = fan_from_reg13;
|
||||
data->fan_from_reg_min = fan_from_reg13;
|
||||
|
||||
data->temp_label = nct6776_temp_label;
|
||||
data->temp_mask = NCT6776_TEMP_MASK;
|
||||
data->virt_temp_mask = NCT6776_VIRT_TEMP_MASK;
|
||||
|
||||
data->REG_VBAT = NCT6106_REG_VBAT;
|
||||
data->REG_DIODE = NCT6106_REG_DIODE;
|
||||
data->DIODE_MASK = NCT6106_DIODE_MASK;
|
||||
data->REG_VIN = NCT6126_REG_IN;
|
||||
data->REG_IN_MINMAX[0] = NCT6126_REG_IN_MIN;
|
||||
data->REG_IN_MINMAX[1] = NCT6126_REG_IN_MAX;
|
||||
data->REG_TARGET = NCT6116_REG_TARGET;
|
||||
data->REG_FAN = NCT6116_REG_FAN;
|
||||
data->REG_FAN_MODE = NCT6116_REG_FAN_MODE;
|
||||
data->REG_FAN_MIN = NCT6116_REG_FAN_MIN;
|
||||
data->REG_FAN_PULSES = NCT6116_REG_FAN_PULSES;
|
||||
data->FAN_PULSE_SHIFT = NCT6116_FAN_PULSE_SHIFT;
|
||||
data->REG_FAN_TIME[0] = NCT6116_REG_FAN_STOP_TIME;
|
||||
data->REG_FAN_TIME[1] = NCT6116_REG_FAN_STEP_UP_TIME;
|
||||
data->REG_FAN_TIME[2] = NCT6116_REG_FAN_STEP_DOWN_TIME;
|
||||
data->REG_TOLERANCE_H = NCT6116_REG_TOLERANCE_H;
|
||||
data->REG_PWM[0] = NCT6116_REG_PWM;
|
||||
data->REG_PWM[1] = NCT6116_REG_FAN_START_OUTPUT;
|
||||
data->REG_PWM[2] = NCT6116_REG_FAN_STOP_OUTPUT;
|
||||
data->REG_PWM[5] = NCT6106_REG_WEIGHT_DUTY_STEP;
|
||||
data->REG_PWM[6] = NCT6106_REG_WEIGHT_DUTY_BASE;
|
||||
data->REG_PWM_READ = NCT6106_REG_PWM_READ;
|
||||
data->REG_PWM_MODE = NCT6106_REG_PWM_MODE;
|
||||
data->PWM_MODE_MASK = NCT6106_PWM_MODE_MASK;
|
||||
data->REG_AUTO_TEMP = NCT6116_REG_AUTO_TEMP;
|
||||
data->REG_AUTO_PWM = NCT6116_REG_AUTO_PWM;
|
||||
data->REG_CRITICAL_TEMP = NCT6116_REG_CRITICAL_TEMP;
|
||||
data->REG_CRITICAL_TEMP_TOLERANCE =
|
||||
NCT6116_REG_CRITICAL_TEMP_TOLERANCE;
|
||||
data->REG_CRITICAL_PWM_ENABLE = NCT6116_REG_CRITICAL_PWM_ENABLE;
|
||||
data->CRITICAL_PWM_ENABLE_MASK =
|
||||
NCT6106_CRITICAL_PWM_ENABLE_MASK;
|
||||
data->REG_CRITICAL_PWM = NCT6116_REG_CRITICAL_PWM;
|
||||
data->REG_TEMP_OFFSET = NCT6106_REG_TEMP_OFFSET;
|
||||
data->REG_TEMP_SOURCE = NCT6106_REG_TEMP_SOURCE;
|
||||
data->REG_TEMP_SEL = NCT6116_REG_TEMP_SEL;
|
||||
data->REG_WEIGHT_TEMP_SEL = NCT6106_REG_WEIGHT_TEMP_SEL;
|
||||
data->REG_WEIGHT_TEMP[0] = NCT6106_REG_WEIGHT_TEMP_STEP;
|
||||
data->REG_WEIGHT_TEMP[1] = NCT6106_REG_WEIGHT_TEMP_STEP_TOL;
|
||||
data->REG_WEIGHT_TEMP[2] = NCT6106_REG_WEIGHT_TEMP_BASE;
|
||||
data->REG_ALARM = NCT6106_REG_ALARM;
|
||||
data->ALARM_BITS = NCT6126_ALARM_BITS;
|
||||
data->REG_BEEP = NCT6106_REG_BEEP;
|
||||
data->BEEP_BITS = NCT6126_BEEP_BITS;
|
||||
data->REG_TSI_TEMP = NCT6116_REG_TSI_TEMP;
|
||||
|
||||
reg_temp = NCT6106_REG_TEMP;
|
||||
reg_temp_mon = NCT6106_REG_TEMP_MON;
|
||||
num_reg_temp = 3;
|
||||
num_reg_temp_mon = ARRAY_SIZE(NCT6106_REG_TEMP_MON);
|
||||
num_reg_tsi_temp = ARRAY_SIZE(NCT6116_REG_TSI_TEMP);
|
||||
reg_temp_over = NCT6106_REG_TEMP_OVER;
|
||||
reg_temp_hyst = NCT6106_REG_TEMP_HYST;
|
||||
reg_temp_config = NCT6106_REG_TEMP_CONFIG;
|
||||
num_reg_temp_config = 3;
|
||||
reg_temp_alternate = NCT6106_REG_TEMP_ALTERNATE;
|
||||
reg_temp_crit = NCT6106_REG_TEMP_CRIT;
|
||||
reg_temp_crit_l = NCT6106_REG_TEMP_CRIT_L;
|
||||
reg_temp_crit_h = NCT6106_REG_TEMP_CRIT_H;
|
||||
|
||||
break;
|
||||
case nct6775:
|
||||
data->in_num = 9;
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ enum sensor_access { access_direct, access_asuswmi };
|
|||
static const char * const nct6775_sio_names[] __initconst = {
|
||||
[nct6106] = "NCT6106D",
|
||||
[nct6116] = "NCT6116D",
|
||||
[nct6126] = "NCT6122D/NCT6126D",
|
||||
[nct6775] = "NCT6775F",
|
||||
[nct6776] = "NCT6776D/F",
|
||||
[nct6779] = "NCT6779D",
|
||||
|
|
@ -76,6 +77,9 @@ MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal");
|
|||
|
||||
#define SIO_NCT6106_ID 0xc450
|
||||
#define SIO_NCT6116_ID 0xd280
|
||||
#define SIO_NCT6122_ID 0xd2a0
|
||||
#define SIO_NCT6126_A_ID 0xd283
|
||||
#define SIO_NCT6126_B_ID 0xd284
|
||||
#define SIO_NCT6775_ID 0xb470
|
||||
#define SIO_NCT6776_ID 0xc330
|
||||
#define SIO_NCT6779_ID 0xc560
|
||||
|
|
@ -565,6 +569,25 @@ nct6775_check_fan_inputs(struct nct6775_data *data, struct nct6775_sio_data *sio
|
|||
pwm3pin = fan3pin && (cr24 & 0x08);
|
||||
pwm4pin = fan4pin;
|
||||
pwm5pin = fan5pin;
|
||||
} else if (data->kind == nct6126) {
|
||||
int cr1a = sio_data->sio_inb(sio_data, 0x1a);
|
||||
int cr1b = sio_data->sio_inb(sio_data, 0x1b);
|
||||
int cr24 = sio_data->sio_inb(sio_data, 0x24);
|
||||
int cr2a = sio_data->sio_inb(sio_data, 0x2a);
|
||||
int cr2b = sio_data->sio_inb(sio_data, 0x2b);
|
||||
int cr2f = sio_data->sio_inb(sio_data, 0x2f);
|
||||
|
||||
fan3pin = !(cr2b & 0x10);
|
||||
fan4pin = (cr2b & 0x80) || /* pin 1(2) */
|
||||
(!(cr2f & 0x10) && (cr1a & 0x04)); /* pin 65(66) */
|
||||
fan5pin = (cr2b & 0x80) || /* pin 126(127) */
|
||||
(!(cr1b & 0x03) && (cr2a & 0x02)); /* pin 94(96) */
|
||||
|
||||
pwm3pin = fan3pin && (cr24 & 0x08);
|
||||
pwm4pin = fan4pin;
|
||||
pwm5pin = fan5pin;
|
||||
|
||||
fan4min = fan4pin;
|
||||
} else {
|
||||
/*
|
||||
* NCT6779D, NCT6791D, NCT6792D, NCT6793D, NCT6795D, NCT6796D,
|
||||
|
|
@ -861,6 +884,7 @@ static int nct6775_platform_probe_init(struct nct6775_data *data)
|
|||
break;
|
||||
case nct6106:
|
||||
case nct6116:
|
||||
case nct6126:
|
||||
case nct6779:
|
||||
case nct6791:
|
||||
case nct6792:
|
||||
|
|
@ -892,6 +916,7 @@ static int nct6775_platform_probe_init(struct nct6775_data *data)
|
|||
switch (data->kind) {
|
||||
case nct6106:
|
||||
case nct6116:
|
||||
case nct6126:
|
||||
tmp |= 0xe0;
|
||||
break;
|
||||
case nct6775:
|
||||
|
|
@ -1006,7 +1031,13 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data)
|
|||
sio_data->kind = nct6106;
|
||||
break;
|
||||
case SIO_NCT6116_ID:
|
||||
sio_data->kind = nct6116;
|
||||
if (val == SIO_NCT6126_A_ID || val == SIO_NCT6126_B_ID)
|
||||
sio_data->kind = nct6126;
|
||||
else
|
||||
sio_data->kind = nct6116;
|
||||
break;
|
||||
case SIO_NCT6122_ID:
|
||||
sio_data->kind = nct6126;
|
||||
break;
|
||||
case SIO_NCT6775_ID:
|
||||
sio_data->kind = nct6775;
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@
|
|||
|
||||
#include <linux/types.h>
|
||||
|
||||
enum kinds { nct6106, nct6116, nct6775, nct6776, nct6779, nct6791, nct6792,
|
||||
nct6793, nct6795, nct6796, nct6797, nct6798, nct6799 };
|
||||
enum kinds { nct6106, nct6116, nct6126, nct6775, nct6776, nct6779, nct6791,
|
||||
nct6792, nct6793, nct6795, nct6796, nct6797, nct6798, nct6799 };
|
||||
enum pwm_enable { off, manual, thermal_cruise, speed_cruise, sf3, sf4 };
|
||||
|
||||
#define NUM_TEMP 12 /* Max number of temp attribute sets w/ limits*/
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
#include <linux/i2c.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/watchdog.h>
|
||||
#include <linux/stringify.h>
|
||||
|
||||
#define VENDOR_ID_REG 0x7A /* Any bank */
|
||||
#define NUVOTON_ID 0x50
|
||||
|
|
@ -113,12 +114,12 @@
|
|||
static int timeout;
|
||||
module_param(timeout, int, 0);
|
||||
MODULE_PARM_DESC(timeout, "Watchdog timeout in minutes. 1 <= timeout <= 255, default="
|
||||
__MODULE_STRING(WATCHDOG_TIMEOUT) ".");
|
||||
__stringify(WATCHDOG_TIMEOUT) ".");
|
||||
|
||||
static bool nowayout = WATCHDOG_NOWAYOUT;
|
||||
module_param(nowayout, bool, 0);
|
||||
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
|
||||
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
|
||||
__stringify(WATCHDOG_NOWAYOUT) ")");
|
||||
|
||||
static const unsigned short normal_i2c[] = {
|
||||
0x2d, 0x2e, I2C_CLIENT_END
|
||||
|
|
@ -126,6 +127,7 @@ static const unsigned short normal_i2c[] = {
|
|||
|
||||
struct nct7904_data {
|
||||
struct i2c_client *client;
|
||||
struct device *hwmon_dev;
|
||||
struct watchdog_device wdt;
|
||||
int bank_sel;
|
||||
u32 fanin_mask;
|
||||
|
|
@ -913,6 +915,8 @@ static int nct7904_wdt_start(struct watchdog_device *wdt)
|
|||
{
|
||||
struct nct7904_data *data = watchdog_get_drvdata(wdt);
|
||||
|
||||
guard(hwmon_lock)(data->hwmon_dev);
|
||||
|
||||
/* Enable soft watchdog timer */
|
||||
return nct7904_write_reg(data, BANK_0, WDT_LOCK_REG, WDT_SOFT_EN);
|
||||
}
|
||||
|
|
@ -921,6 +925,8 @@ static int nct7904_wdt_stop(struct watchdog_device *wdt)
|
|||
{
|
||||
struct nct7904_data *data = watchdog_get_drvdata(wdt);
|
||||
|
||||
guard(hwmon_lock)(data->hwmon_dev);
|
||||
|
||||
return nct7904_write_reg(data, BANK_0, WDT_LOCK_REG, WDT_SOFT_DIS);
|
||||
}
|
||||
|
||||
|
|
@ -939,6 +945,8 @@ static int nct7904_wdt_set_timeout(struct watchdog_device *wdt,
|
|||
*/
|
||||
wdt->timeout = timeout / 60 * 60;
|
||||
|
||||
guard(hwmon_lock)(data->hwmon_dev);
|
||||
|
||||
return nct7904_write_reg(data, BANK_0, WDT_TIMER_REG,
|
||||
wdt->timeout / 60);
|
||||
}
|
||||
|
|
@ -954,6 +962,8 @@ static int nct7904_wdt_ping(struct watchdog_device *wdt)
|
|||
struct nct7904_data *data = watchdog_get_drvdata(wdt);
|
||||
int ret;
|
||||
|
||||
guard(hwmon_lock)(data->hwmon_dev);
|
||||
|
||||
/* Disable soft watchdog timer */
|
||||
ret = nct7904_write_reg(data, BANK_0, WDT_LOCK_REG, WDT_SOFT_DIS);
|
||||
if (ret < 0)
|
||||
|
|
@ -973,6 +983,8 @@ static unsigned int nct7904_wdt_get_timeleft(struct watchdog_device *wdt)
|
|||
struct nct7904_data *data = watchdog_get_drvdata(wdt);
|
||||
int ret;
|
||||
|
||||
guard(hwmon_lock)(data->hwmon_dev);
|
||||
|
||||
ret = nct7904_read_reg(data, BANK_0, WDT_TIMER_REG);
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
|
|
@ -1127,6 +1139,9 @@ static int nct7904_probe(struct i2c_client *client)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* The watchdog callbacks take its lock */
|
||||
data->hwmon_dev = hwmon_dev;
|
||||
|
||||
/* Watchdog initialization */
|
||||
data->wdt.ops = &nct7904_wdt_ops;
|
||||
data->wdt.info = &nct7904_wdt_info;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
|
@ -197,6 +198,8 @@ struct npcm7xx_pwm_fan_data {
|
|||
int pwm_modules;
|
||||
struct clk *pwm_clk;
|
||||
struct clk *fan_clk;
|
||||
/* serializes CMR/CR updates from hwmon and the cooling devices */
|
||||
struct mutex pwm_lock[NPCM7XX_PWM_MAX_MODULES];
|
||||
spinlock_t fan_lock[NPCM7XX_FAN_MAX_MODULE];
|
||||
int fan_irq[NPCM7XX_FAN_MAX_MODULE];
|
||||
bool pwm_present[NPCM7XX_PWM_MAX_CHN_NUM];
|
||||
|
|
@ -216,6 +219,8 @@ static int npcm7xx_pwm_config_set(struct npcm7xx_pwm_fan_data *data,
|
|||
u32 module = (channel / NPCM7XX_PWM_MAX_CHN_NUM_IN_A_MODULE);
|
||||
u32 tmp_buf, ctrl_en_bit, env_bit;
|
||||
|
||||
guard(mutex)(&data->pwm_lock[module]);
|
||||
|
||||
/*
|
||||
* Config PWM Comparator register for setting duty cycle
|
||||
*/
|
||||
|
|
@ -986,6 +991,12 @@ static int npcm7xx_pwm_fan_probe(struct platform_device *pdev)
|
|||
output_freq = npcm7xx_pwm_init(data);
|
||||
npcm7xx_fan_init(data);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(data->pwm_lock); i++) {
|
||||
ret = devm_mutex_init(dev, &data->pwm_lock[i]);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < NPCM7XX_FAN_MAX_MODULE; i++) {
|
||||
spin_lock_init(&data->fan_lock[i]);
|
||||
|
||||
|
|
|
|||
|
|
@ -393,6 +393,25 @@ config SENSORS_MAX20751
|
|||
This driver can also be built as a module. If so, the module will
|
||||
be called max20751.
|
||||
|
||||
config SENSORS_MAX20826
|
||||
tristate "Analog Devices MAX20826 and similar devices"
|
||||
help
|
||||
If you say yes here you get hardware monitoring support for Analog
|
||||
Devices MAX20826 and similar devices.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called max20826.
|
||||
|
||||
config SENSORS_MAX20826_REGULATOR
|
||||
bool "Regulator support for Analog Devices MAX20826 and similar devices"
|
||||
depends on SENSORS_MAX20826 && REGULATOR
|
||||
help
|
||||
If you say yes here you get regulator support for Analog Devices
|
||||
MAX20826 and similar sensors.
|
||||
|
||||
This enables the MAX20826 to be used as a regulator device,
|
||||
providing voltage control through the regulator framework.
|
||||
|
||||
config SENSORS_MAX20830
|
||||
tristate "Analog Devices MAX20830 and compatibles"
|
||||
help
|
||||
|
|
@ -435,8 +454,8 @@ config SENSORS_MAX34440
|
|||
help
|
||||
If you say yes here you get hardware monitoring support for Maxim
|
||||
MAX34440, MAX34441, MAX34446, MAX34451, MAX34452, MAX34460, and
|
||||
MAX34461. Other compatible devices are ADPM12160, ADPM12200, and
|
||||
ADPM12250.
|
||||
MAX34461. Other compatible devices are ADPM12160, ADPM12200,
|
||||
ADPM12250, and ADPM12300.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called max34440.
|
||||
|
|
@ -763,6 +782,15 @@ config SENSORS_TDA38640_REGULATOR
|
|||
If you say yes here you get regulator support for Infineon
|
||||
TDA38640 as regulator.
|
||||
|
||||
config SENSORS_TDA38740
|
||||
tristate "Infineon TDA38725/TDA38740"
|
||||
help
|
||||
If you say yes here you get hardware monitoring support for Infineon
|
||||
TDA38725 and TDA38740.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called tda38740.
|
||||
|
||||
config SENSORS_TPS25990
|
||||
tristate "TI TPS25990"
|
||||
help
|
||||
|
|
@ -790,10 +818,11 @@ config SENSORS_TPS40422
|
|||
be called tps40422.
|
||||
|
||||
config SENSORS_TPS53679
|
||||
tristate "TI TPS53647, TPS53667, TPS53676, TPS53679, TPS53681, TPS53688"
|
||||
tristate "TI TPS536xx family"
|
||||
help
|
||||
If you say yes here you get hardware monitoring support for TI
|
||||
TPS53647, TPS53667, TPS53676, TPS53679, TPS53681, and TPS53688.
|
||||
TPS53622, TPS53647, TPS53659, TPS53667, TPS53676, TPS53679, TPS53681,
|
||||
TPS53685, TPS53688, and TPS536C7.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called tps53679.
|
||||
|
|
@ -867,7 +896,7 @@ config SENSORS_XDPE1A2G7B
|
|||
tristate "Infineon XDPE1A2G7B"
|
||||
help
|
||||
If you say yes here you get hardware monitoring support for Infineon
|
||||
XDPE1A2G5B and XDPE1A2G7B.
|
||||
XDPE1A2G5B,XDPE1A2G7B and XDPE1A2G7C.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called xdpe1a2g7b.
|
||||
|
|
@ -877,7 +906,7 @@ config SENSORS_XDPE1A2G7B_REGULATOR
|
|||
depends on SENSORS_XDPE1A2G7B && REGULATOR
|
||||
help
|
||||
If you say yes here you get regulator support for Infineon
|
||||
XDPE1A2G5B and XDPE1A2G7B multi-phase digital controllers.
|
||||
XDPE1A2G5B,XDPE1A2G7B and XDPE1A2G7C multi-phase digital controllers.
|
||||
|
||||
This enables the controllers to be used as regulator devices,
|
||||
providing voltage control through the regulator framework.
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ obj-$(CONFIG_SENSORS_MAX16601) += max16601.o
|
|||
obj-$(CONFIG_SENSORS_MAX17616) += max17616.o
|
||||
obj-$(CONFIG_SENSORS_MAX20730) += max20730.o
|
||||
obj-$(CONFIG_SENSORS_MAX20751) += max20751.o
|
||||
obj-$(CONFIG_SENSORS_MAX20826) += max20826.o
|
||||
obj-$(CONFIG_SENSORS_MAX20830) += max20830.o
|
||||
obj-$(CONFIG_SENSORS_MAX20860A) += max20860a.o
|
||||
obj-$(CONFIG_SENSORS_MAX31785) += max31785.o
|
||||
|
|
@ -70,6 +71,7 @@ obj-$(CONFIG_SENSORS_STEF48H28) += stef48h28.o
|
|||
obj-$(CONFIG_SENSORS_SQ24860) += sq24860.o
|
||||
obj-$(CONFIG_SENSORS_STPDDC60) += stpddc60.o
|
||||
obj-$(CONFIG_SENSORS_TDA38640) += tda38640.o
|
||||
obj-$(CONFIG_SENSORS_TDA38740) += tda38740.o
|
||||
obj-$(CONFIG_SENSORS_TPS25990) += tps25990.o
|
||||
obj-$(CONFIG_SENSORS_TPS40422) += tps40422.o
|
||||
obj-$(CONFIG_SENSORS_TPS53679) += tps53679.o
|
||||
|
|
|
|||
|
|
@ -166,7 +166,7 @@ static ssize_t ibm_cffps_debugfs_read(struct file *file, char __user *buf,
|
|||
if (rc < 0)
|
||||
goto unlock;
|
||||
|
||||
snprintf(&data[i * 2], 3, "%02X", rc);
|
||||
snprintf(&data[i * 2], 3, "%02X", (u8)rc);
|
||||
}
|
||||
|
||||
rc = i * 2;
|
||||
|
|
@ -177,7 +177,7 @@ static ssize_t ibm_cffps_debugfs_read(struct file *file, char __user *buf,
|
|||
if (rc < 0)
|
||||
goto unlock;
|
||||
|
||||
snprintf(&data[i * 4], 5, "%04X", rc);
|
||||
snprintf(&data[i * 4], 5, "%04X", (u16)rc);
|
||||
}
|
||||
|
||||
rc = i * 4;
|
||||
|
|
|
|||
|
|
@ -456,6 +456,8 @@ static const struct i2c_device_id raa_dmpvr_id[] = {
|
|||
{ .name = "raa229004", .driver_data = raa_dmpvr2_2rail },
|
||||
{ .name = "raa229141", .driver_data = raa_dmpvr2_2rail_pmbus },
|
||||
{ .name = "raa229621", .driver_data = raa_dmpvr2_2rail },
|
||||
{ .name = "raa229639", .driver_data = raa_dmpvr2_2rail_nontc },
|
||||
{ .name = "raa229640", .driver_data = raa_dmpvr2_2rail_nontc },
|
||||
{ }
|
||||
};
|
||||
|
||||
|
|
@ -506,6 +508,8 @@ static const struct of_device_id isl68137_of_match[] = {
|
|||
{ .compatible = "renesas,raa229001", .data = (void *)raa_dmpvr2_2rail },
|
||||
{ .compatible = "renesas,raa229004", .data = (void *)raa_dmpvr2_2rail },
|
||||
{ .compatible = "renesas,raa229621", .data = (void *)raa_dmpvr2_2rail },
|
||||
{ .compatible = "renesas,raa229639", .data = (void *)raa_dmpvr2_2rail_nontc },
|
||||
{ .compatible = "renesas,raa229640", .data = (void *)raa_dmpvr2_2rail_nontc },
|
||||
{ },
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
|
|
@ -16,6 +17,85 @@
|
|||
|
||||
#define LTC4286_MFR_ID_SIZE 3
|
||||
|
||||
struct ltc4286_data {
|
||||
struct pmbus_driver_info info;
|
||||
u32 rsense;
|
||||
bool vrange_low_enable;
|
||||
};
|
||||
|
||||
#define to_ltc4286_data(x) container_of((x), struct ltc4286_data, info)
|
||||
|
||||
static ssize_t ltc4286_rsense_show(struct device *dev,
|
||||
struct device_attribute *devattr,
|
||||
char *buf)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev->parent);
|
||||
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
||||
struct ltc4286_data *data = to_ltc4286_data(info);
|
||||
u32 rsense;
|
||||
int ret;
|
||||
|
||||
ret = pmbus_lock_interruptible(client);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
rsense = data->rsense;
|
||||
|
||||
pmbus_unlock(client);
|
||||
|
||||
return sysfs_emit(buf, "%u\n", rsense);
|
||||
}
|
||||
|
||||
static ssize_t ltc4286_rsense_store(struct device *dev,
|
||||
struct device_attribute *devattr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev->parent);
|
||||
const struct pmbus_driver_info *info_ro = pmbus_get_driver_info(client);
|
||||
struct ltc4286_data *data = to_ltc4286_data(info_ro);
|
||||
struct pmbus_driver_info *info = &data->info;
|
||||
u32 rsense;
|
||||
int ret;
|
||||
|
||||
ret = kstrtou32(buf, 10, &rsense);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (rsense == 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (rsense > (INT_MAX / 1024))
|
||||
return -EINVAL;
|
||||
|
||||
ret = pmbus_lock_interruptible(client);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
data->rsense = rsense;
|
||||
info->m[PSC_CURRENT_OUT] = 1024 * rsense;
|
||||
info->m[PSC_POWER] = data->vrange_low_enable ? 4 * rsense : rsense;
|
||||
|
||||
pmbus_unlock(client);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static SENSOR_DEVICE_ATTR_RW(shunt_resistor, ltc4286_rsense, 0);
|
||||
|
||||
static struct attribute *ltc4286_attrs[] = {
|
||||
&sensor_dev_attr_shunt_resistor.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group ltc4286_group = {
|
||||
.attrs = ltc4286_attrs,
|
||||
};
|
||||
|
||||
static const struct attribute_group *ltc4286_attribute_groups[] = {
|
||||
<c4286_group,
|
||||
NULL,
|
||||
};
|
||||
|
||||
/*
|
||||
* Initialize the MBR as default settings which is referred to LTC4286 datasheet
|
||||
* (March 22, 2022 version) table 3 page 16
|
||||
|
|
@ -55,6 +135,7 @@ static struct pmbus_driver_info ltc4286_info = {
|
|||
.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
|
||||
PMBUS_HAVE_PIN | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_VOUT |
|
||||
PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_TEMP,
|
||||
.groups = ltc4286_attribute_groups,
|
||||
};
|
||||
|
||||
static const struct i2c_device_id ltc4286_id[] = {
|
||||
|
|
@ -69,6 +150,7 @@ static int ltc4286_probe(struct i2c_client *client)
|
|||
int ret;
|
||||
const struct i2c_device_id *mid;
|
||||
u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1];
|
||||
struct ltc4286_data *data;
|
||||
struct pmbus_driver_info *info;
|
||||
u32 rsense;
|
||||
int vrange_nval, vrange_oval;
|
||||
|
|
@ -114,11 +196,14 @@ static int ltc4286_probe(struct i2c_client *client)
|
|||
if (rsense > (INT_MAX / 1024))
|
||||
return -EINVAL;
|
||||
|
||||
info = devm_kmemdup(&client->dev, <c4286_info, sizeof(*info),
|
||||
GFP_KERNEL);
|
||||
if (!info)
|
||||
data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->info = ltc4286_info;
|
||||
data->rsense = rsense;
|
||||
info = &data->info;
|
||||
|
||||
/* Check MFR1 CONFIG register bit 1 VRANGE_SELECT before driver loading */
|
||||
vrange_oval = i2c_smbus_read_word_data(client, LTC4286_MFR_CONFIG1);
|
||||
if (vrange_oval < 0)
|
||||
|
|
@ -126,7 +211,10 @@ static int ltc4286_probe(struct i2c_client *client)
|
|||
"Failed to read manufacturer configuration one\n");
|
||||
vrange_nval = vrange_oval;
|
||||
|
||||
if (device_property_read_bool(&client->dev, "adi,vrange-low-enable")) {
|
||||
data->vrange_low_enable =
|
||||
device_property_read_bool(&client->dev, "adi,vrange-low-enable");
|
||||
|
||||
if (data->vrange_low_enable) {
|
||||
vrange_nval &=
|
||||
~VRANGE_SELECT_BIT; /* VRANGE_SELECT = 0, 25.6 volts */
|
||||
|
||||
|
|
|
|||
1044
drivers/hwmon/pmbus/max20826.c
Normal file
1044
drivers/hwmon/pmbus/max20826.c
Normal file
File diff suppressed because it is too large
Load Diff
|
|
@ -812,10 +812,12 @@ static int max34440_probe(struct i2c_client *client)
|
|||
return pmbus_do_probe(client, &data->info);
|
||||
}
|
||||
|
||||
/* ADPM12300 is just ADPM12250 with higher power capacity. Reuses driver data */
|
||||
static const struct i2c_device_id max34440_id[] = {
|
||||
{ .name = "adpm12160", .driver_data = adpm12160 },
|
||||
{ .name = "adpm12200", .driver_data = adpm12200 },
|
||||
{ .name = "adpm12250", .driver_data = adpm12250 },
|
||||
{ .name = "adpm12300", .driver_data = adpm12250 },
|
||||
{ .name = "max34440", .driver_data = max34440 },
|
||||
{ .name = "max34441", .driver_data = max34441 },
|
||||
{ .name = "max34446", .driver_data = max34446 },
|
||||
|
|
|
|||
|
|
@ -272,14 +272,14 @@ static int mp2888_write_word_data(struct i2c_client *client, int page, int reg,
|
|||
word = clamp_val(word, 0, GENMASK(7, 0));
|
||||
break;
|
||||
case PMBUS_IOUT_OC_WARN_LIMIT:
|
||||
/* Fix limit according to total curent resolution. */
|
||||
/* Fix limit according to total current resolution. */
|
||||
word = data->total_curr_resolution ? DIV_ROUND_CLOSEST(word, 8) :
|
||||
DIV_ROUND_CLOSEST(word, 4);
|
||||
/* Drop unused bits 15:10. */
|
||||
word = clamp_val(word, 0, GENMASK(9, 0));
|
||||
break;
|
||||
case PMBUS_POUT_OP_WARN_LIMIT:
|
||||
/* Fix limit according to total curent resolution. */
|
||||
/* Fix limit according to total current resolution. */
|
||||
word = data->total_curr_resolution ? DIV_ROUND_CLOSEST(word, 4) :
|
||||
DIV_ROUND_CLOSEST(word, 2);
|
||||
/* Drop unused bits 15:10. */
|
||||
|
|
|
|||
|
|
@ -461,6 +461,13 @@ struct pmbus_driver_info {
|
|||
int (*read_byte_data)(struct i2c_client *client, int page, int reg);
|
||||
int (*read_word_data)(struct i2c_client *client, int page, int phase,
|
||||
int reg);
|
||||
/*
|
||||
* data_buf is at least I2C_SMBUS_BLOCK_MAX bytes long. The callback
|
||||
* must never return more than I2C_SMBUS_BLOCK_MAX bytes of data and
|
||||
* returns the number of bytes written into data_buf.
|
||||
*/
|
||||
int (*read_block_data)(struct i2c_client *client, int page, u8 reg,
|
||||
char *data_buf);
|
||||
int (*write_byte_data)(struct i2c_client *client, int page, int reg,
|
||||
u8 byte);
|
||||
int (*write_word_data)(struct i2c_client *client, int page, int reg,
|
||||
|
|
|
|||
|
|
@ -616,6 +616,26 @@ static int pmbus_read_block_data(struct i2c_client *client, int page, u8 reg,
|
|||
return pmbus_read_smbus_i2c_block_data(client, reg, data_buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* _pmbus_read_block_data() is similar to pmbus_read_block_data(), but checks if
|
||||
* a device specific mapping function exists and calls it if necessary.
|
||||
*/
|
||||
static int _pmbus_read_block_data(struct i2c_client *client, int page, u8 reg,
|
||||
char *data_buf)
|
||||
{
|
||||
struct pmbus_data *data = i2c_get_clientdata(client);
|
||||
const struct pmbus_driver_info *info = data->info;
|
||||
int status;
|
||||
|
||||
if (info->read_block_data) {
|
||||
status = info->read_block_data(client, page, reg, data_buf);
|
||||
if (status != -ENODATA)
|
||||
return status;
|
||||
}
|
||||
|
||||
return pmbus_read_block_data(client, page, reg, data_buf);
|
||||
}
|
||||
|
||||
static struct pmbus_sensor *pmbus_find_sensor(struct pmbus_data *data, int page,
|
||||
int reg)
|
||||
{
|
||||
|
|
@ -761,7 +781,7 @@ static bool __maybe_unused pmbus_check_block_register(struct i2c_client *client,
|
|||
struct pmbus_data *data = i2c_get_clientdata(client);
|
||||
char data_buf[I2C_SMBUS_BLOCK_MAX + 2];
|
||||
|
||||
rv = pmbus_read_block_data(client, page, reg, data_buf);
|
||||
rv = _pmbus_read_block_data(client, page, reg, data_buf);
|
||||
if (rv >= 0 && !(data->flags & PMBUS_SKIP_STATUS_CHECK))
|
||||
rv = pmbus_check_status_cml(client);
|
||||
if (rv < 0 && (data->flags & PMBUS_READ_STATUS_AFTER_FAILED_CHECK))
|
||||
|
|
@ -2962,6 +2982,11 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data,
|
|||
}
|
||||
|
||||
for (page = 0; page < info->pages; page++) {
|
||||
if (info->phases[page] > PMBUS_PHASES) {
|
||||
dev_err(dev, "Bad number of PMBus phases for page %d: %d\n",
|
||||
page, info->phases[page]);
|
||||
return -ENODEV;
|
||||
}
|
||||
ret = pmbus_identify_common(client, data, page);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to identify chip capabilities\n");
|
||||
|
|
@ -3679,7 +3704,7 @@ static ssize_t pmbus_debugfs_block_read(struct file *file, char __user *buf,
|
|||
char data[I2C_SMBUS_BLOCK_MAX + 2] = { 0 };
|
||||
|
||||
scoped_guard(pmbus_lock, client) {
|
||||
rc = pmbus_read_block_data(client, entry->page, entry->reg, data);
|
||||
rc = _pmbus_read_block_data(client, entry->page, entry->reg, data);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
}
|
||||
|
|
|
|||
64
drivers/hwmon/pmbus/tda38740.c
Normal file
64
drivers/hwmon/pmbus/tda38740.c
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Hardware monitoring driver for Infineon TDA38725/TDA38740
|
||||
*
|
||||
* Copyright (c) 2023 9elements GmbH
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include "pmbus.h"
|
||||
|
||||
static struct pmbus_driver_info tda38740_info = {
|
||||
.pages = 1,
|
||||
.format[PSC_VOLTAGE_IN] = linear,
|
||||
.format[PSC_VOLTAGE_OUT] = linear,
|
||||
.format[PSC_CURRENT_OUT] = linear,
|
||||
.format[PSC_CURRENT_IN] = linear,
|
||||
.format[PSC_POWER] = linear,
|
||||
.format[PSC_TEMPERATURE] = linear,
|
||||
.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT
|
||||
| PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP
|
||||
| PMBUS_HAVE_IIN
|
||||
| PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
|
||||
| PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
|
||||
| PMBUS_HAVE_POUT | PMBUS_HAVE_PIN,
|
||||
};
|
||||
|
||||
static int tda38740_probe(struct i2c_client *client)
|
||||
{
|
||||
return pmbus_do_probe(client, &tda38740_info);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id tda38740_id[] = {
|
||||
{ .name = "tda38725"},
|
||||
{ .name = "tda38740"},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, tda38740_id);
|
||||
|
||||
static const struct of_device_id tda38740_of_match[] = {
|
||||
{ .compatible = "infineon,tda38725"},
|
||||
{ .compatible = "infineon,tda38740"},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tda38740_of_match);
|
||||
|
||||
static struct i2c_driver tda38740_driver = {
|
||||
.driver = {
|
||||
.name = "tda38740",
|
||||
.of_match_table = tda38740_of_match,
|
||||
},
|
||||
.probe = tda38740_probe,
|
||||
.id_table = tda38740_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(tda38740_driver);
|
||||
|
||||
MODULE_DESCRIPTION("PMBus driver for Infineon TDA38725/TDA38740");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS("PMBUS");
|
||||
|
|
@ -47,6 +47,22 @@
|
|||
PK_MIN_AVG_RST_AVG | \
|
||||
PK_MIN_AVG_RST_MIN)
|
||||
|
||||
#define TPS1689_VIN_OV_RANGE_SEL_MASK GENMASK(7, 6)
|
||||
#define TPS1689_VIN_VOV_MASK GENMASK(5, 0)
|
||||
#define TPS1689_VIN_SCALING 251
|
||||
#define TPS1689_VIN_VOV_STEP_MV 250
|
||||
#define TPS1689_VIN_RANGE_SPAN_MV 16000
|
||||
|
||||
enum chips {
|
||||
tps1689,
|
||||
tps25990,
|
||||
};
|
||||
|
||||
struct tps25990_data {
|
||||
struct pmbus_driver_info info;
|
||||
enum chips chip_id;
|
||||
};
|
||||
|
||||
/*
|
||||
* Arbitrary default Rimon value: 1kOhm
|
||||
* This correspond to an overcurrent limit of 55A, close to the specified limit
|
||||
|
|
@ -96,6 +112,8 @@ static int tps25990_mfr_write_protect_get(struct i2c_client *client)
|
|||
static int tps25990_read_word_data(struct i2c_client *client,
|
||||
int page, int phase, int reg)
|
||||
{
|
||||
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
||||
struct tps25990_data *data = container_of(info, struct tps25990_data, info);
|
||||
int ret;
|
||||
|
||||
switch (reg) {
|
||||
|
|
@ -184,9 +202,18 @@ static int tps25990_read_word_data(struct i2c_client *client,
|
|||
ret = pmbus_read_word_data(client, page, phase, reg);
|
||||
if (ret < 0)
|
||||
break;
|
||||
ret = DIV_ROUND_CLOSEST(ret * TPS25990_VIN_OVF_NUM,
|
||||
TPS25990_VIN_OVF_DIV);
|
||||
ret += TPS25990_VIN_OVF_OFF;
|
||||
if (data->chip_id == tps25990) {
|
||||
ret = DIV_ROUND_CLOSEST(ret * TPS25990_VIN_OVF_NUM,
|
||||
TPS25990_VIN_OVF_DIV);
|
||||
ret += TPS25990_VIN_OVF_OFF;
|
||||
} else if (data->chip_id == tps1689) {
|
||||
int rng = (FIELD_GET(TPS1689_VIN_OV_RANGE_SEL_MASK, ret) + 1) *
|
||||
TPS1689_VIN_RANGE_SPAN_MV;
|
||||
int vov = FIELD_GET(TPS1689_VIN_VOV_MASK, ret) * TPS1689_VIN_VOV_STEP_MV;
|
||||
|
||||
ret = DIV_ROUND_CLOSEST(rng + vov - TPS1689_VIN_RANGE_SPAN_MV,
|
||||
TPS1689_VIN_SCALING);
|
||||
}
|
||||
break;
|
||||
|
||||
case PMBUS_IIN_OC_FAULT_LIMIT:
|
||||
|
|
@ -229,6 +256,8 @@ static int tps25990_read_word_data(struct i2c_client *client,
|
|||
static int tps25990_write_word_data(struct i2c_client *client,
|
||||
int page, int reg, u16 value)
|
||||
{
|
||||
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
||||
struct tps25990_data *data = container_of(info, struct tps25990_data, info);
|
||||
int ret;
|
||||
|
||||
switch (reg) {
|
||||
|
|
@ -240,26 +269,52 @@ static int tps25990_write_word_data(struct i2c_client *client,
|
|||
case PMBUS_OT_WARN_LIMIT:
|
||||
case PMBUS_OT_FAULT_LIMIT:
|
||||
case PMBUS_PIN_OP_WARN_LIMIT:
|
||||
value >>= TPS25990_8B_SHIFT;
|
||||
value = clamp_val((s16)value, 0, S16_MAX) >> TPS25990_8B_SHIFT;
|
||||
value = clamp_val(value, 0, 0xff);
|
||||
ret = pmbus_write_word_data(client, page, reg, value);
|
||||
break;
|
||||
|
||||
case PMBUS_VIN_OV_FAULT_LIMIT:
|
||||
value -= TPS25990_VIN_OVF_OFF;
|
||||
value = DIV_ROUND_CLOSEST(((unsigned int)value) * TPS25990_VIN_OVF_DIV,
|
||||
TPS25990_VIN_OVF_NUM);
|
||||
value = clamp_val(value, 0, 0xf);
|
||||
if ((s16)value < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (data->chip_id == tps25990) {
|
||||
int tmp = (int)value - TPS25990_VIN_OVF_OFF;
|
||||
|
||||
tmp = clamp_val(tmp, 0, INT_MAX);
|
||||
value = DIV_ROUND_CLOSEST((unsigned int)tmp * TPS25990_VIN_OVF_DIV,
|
||||
TPS25990_VIN_OVF_NUM);
|
||||
value = clamp_val(value, 0, 0xf);
|
||||
} else if (data->chip_id == tps1689) {
|
||||
u32 scaled_value = value * TPS1689_VIN_SCALING + TPS1689_VIN_RANGE_SPAN_MV;
|
||||
u32 rng_idx = scaled_value / TPS1689_VIN_RANGE_SPAN_MV;
|
||||
u32 ov_set;
|
||||
|
||||
rng_idx = clamp_val(rng_idx, 1,
|
||||
FIELD_MAX(TPS1689_VIN_OV_RANGE_SEL_MASK) + 1);
|
||||
ov_set = scaled_value - (TPS1689_VIN_RANGE_SPAN_MV * rng_idx);
|
||||
ov_set = min_t(u32, ov_set / TPS1689_VIN_VOV_STEP_MV,
|
||||
FIELD_MAX(TPS1689_VIN_VOV_MASK));
|
||||
value = FIELD_PREP(TPS1689_VIN_OV_RANGE_SEL_MASK, rng_idx - 1) |
|
||||
FIELD_PREP(TPS1689_VIN_VOV_MASK, ov_set);
|
||||
}
|
||||
ret = pmbus_write_word_data(client, page, reg, value);
|
||||
break;
|
||||
|
||||
case PMBUS_IIN_OC_FAULT_LIMIT:
|
||||
value -= TPS25990_IIN_OCF_OFF;
|
||||
value = DIV_ROUND_CLOSEST(((unsigned int)value) * TPS25990_IIN_OCF_DIV,
|
||||
case PMBUS_IIN_OC_FAULT_LIMIT: {
|
||||
int tmp;
|
||||
|
||||
if ((s16)value < 0)
|
||||
return -EINVAL;
|
||||
|
||||
tmp = (int)value - TPS25990_IIN_OCF_OFF;
|
||||
tmp = clamp_val(tmp, 0, INT_MAX);
|
||||
value = DIV_ROUND_CLOSEST((unsigned int)tmp * TPS25990_IIN_OCF_DIV,
|
||||
TPS25990_IIN_OCF_NUM);
|
||||
value = clamp_val(value, 0, 0x3f);
|
||||
ret = pmbus_write_byte_data(client, page, TPS25990_VIREF, value);
|
||||
break;
|
||||
}
|
||||
|
||||
case PMBUS_VIRT_SAMPLES:
|
||||
value = clamp_val(value, 1, 1 << PK_MIN_AVG_AVG_CNT);
|
||||
|
|
@ -337,63 +392,120 @@ static const struct regulator_desc tps25990_reg_desc[] = {
|
|||
};
|
||||
#endif
|
||||
|
||||
static const struct pmbus_driver_info tps25990_base_info = {
|
||||
.pages = 1,
|
||||
.format[PSC_VOLTAGE_IN] = direct,
|
||||
.m[PSC_VOLTAGE_IN] = 5251,
|
||||
.b[PSC_VOLTAGE_IN] = 0,
|
||||
.R[PSC_VOLTAGE_IN] = -2,
|
||||
.format[PSC_VOLTAGE_OUT] = direct,
|
||||
.m[PSC_VOLTAGE_OUT] = 5251,
|
||||
.b[PSC_VOLTAGE_OUT] = 0,
|
||||
.R[PSC_VOLTAGE_OUT] = -2,
|
||||
.format[PSC_TEMPERATURE] = direct,
|
||||
.m[PSC_TEMPERATURE] = 140,
|
||||
.b[PSC_TEMPERATURE] = 32100,
|
||||
.R[PSC_TEMPERATURE] = -2,
|
||||
/*
|
||||
* Current and Power measurement depends on the ohm value
|
||||
* of Rimon. m is multiplied by 1000 below to have an integer
|
||||
* and -3 is added to R to compensate.
|
||||
*/
|
||||
.format[PSC_CURRENT_IN] = direct,
|
||||
.m[PSC_CURRENT_IN] = 9538,
|
||||
.b[PSC_CURRENT_IN] = 0,
|
||||
.R[PSC_CURRENT_IN] = -6,
|
||||
.format[PSC_POWER] = direct,
|
||||
.m[PSC_POWER] = 4901,
|
||||
.b[PSC_POWER] = 0,
|
||||
.R[PSC_POWER] = -7,
|
||||
.func[0] = (PMBUS_HAVE_VIN |
|
||||
PMBUS_HAVE_VOUT |
|
||||
PMBUS_HAVE_VMON |
|
||||
PMBUS_HAVE_IIN |
|
||||
PMBUS_HAVE_PIN |
|
||||
PMBUS_HAVE_TEMP |
|
||||
PMBUS_HAVE_STATUS_VOUT |
|
||||
PMBUS_HAVE_STATUS_IOUT |
|
||||
PMBUS_HAVE_STATUS_INPUT |
|
||||
PMBUS_HAVE_STATUS_TEMP |
|
||||
PMBUS_HAVE_SAMPLES),
|
||||
.read_word_data = tps25990_read_word_data,
|
||||
.write_word_data = tps25990_write_word_data,
|
||||
.read_byte_data = tps25990_read_byte_data,
|
||||
.write_byte_data = tps25990_write_byte_data,
|
||||
static const struct pmbus_driver_info tps25990_base_info[] = {
|
||||
[tps1689] = {
|
||||
.pages = 1,
|
||||
.format[PSC_VOLTAGE_IN] = direct,
|
||||
.m[PSC_VOLTAGE_IN] = 3984,
|
||||
.b[PSC_VOLTAGE_IN] = -63750,
|
||||
.R[PSC_VOLTAGE_IN] = -3,
|
||||
.format[PSC_VOLTAGE_OUT] = direct,
|
||||
.m[PSC_VOLTAGE_OUT] = 1166,
|
||||
.b[PSC_VOLTAGE_OUT] = 0,
|
||||
.R[PSC_VOLTAGE_OUT] = -2,
|
||||
.format[PSC_TEMPERATURE] = direct,
|
||||
.m[PSC_TEMPERATURE] = 140,
|
||||
.b[PSC_TEMPERATURE] = 32103,
|
||||
.R[PSC_TEMPERATURE] = -2,
|
||||
/*
|
||||
* Current and Power measurement depends on the ohm value
|
||||
* of Rimon. m is multiplied by 1000 below to have an integer
|
||||
* and -3 is added to R to compensate.
|
||||
*/
|
||||
.format[PSC_CURRENT_IN] = direct,
|
||||
.m[PSC_CURRENT_IN] = 9548,
|
||||
.b[PSC_CURRENT_IN] = 0,
|
||||
.R[PSC_CURRENT_IN] = -6,
|
||||
.format[PSC_CURRENT_OUT] = direct,
|
||||
.m[PSC_CURRENT_OUT] = 24347,
|
||||
.b[PSC_CURRENT_OUT] = 0,
|
||||
.R[PSC_CURRENT_OUT] = -3,
|
||||
.format[PSC_POWER] = direct,
|
||||
.m[PSC_POWER] = 2775,
|
||||
.b[PSC_POWER] = 0,
|
||||
.R[PSC_POWER] = -4,
|
||||
.func[0] = (PMBUS_HAVE_VIN |
|
||||
PMBUS_HAVE_VOUT |
|
||||
PMBUS_HAVE_VMON |
|
||||
PMBUS_HAVE_IIN |
|
||||
PMBUS_HAVE_IOUT |
|
||||
PMBUS_HAVE_PIN |
|
||||
PMBUS_HAVE_TEMP |
|
||||
PMBUS_HAVE_STATUS_VOUT |
|
||||
PMBUS_HAVE_STATUS_IOUT |
|
||||
PMBUS_HAVE_STATUS_INPUT |
|
||||
PMBUS_HAVE_STATUS_TEMP |
|
||||
PMBUS_HAVE_SAMPLES),
|
||||
|
||||
.read_word_data = tps25990_read_word_data,
|
||||
.write_word_data = tps25990_write_word_data,
|
||||
.read_byte_data = tps25990_read_byte_data,
|
||||
.write_byte_data = tps25990_write_byte_data,
|
||||
|
||||
#if IS_ENABLED(CONFIG_SENSORS_TPS25990_REGULATOR)
|
||||
.reg_desc = tps25990_reg_desc,
|
||||
.num_regulators = ARRAY_SIZE(tps25990_reg_desc),
|
||||
.reg_desc = tps25990_reg_desc,
|
||||
.num_regulators = ARRAY_SIZE(tps25990_reg_desc),
|
||||
#endif
|
||||
},
|
||||
[tps25990] = {
|
||||
.pages = 1,
|
||||
.format[PSC_VOLTAGE_IN] = direct,
|
||||
.m[PSC_VOLTAGE_IN] = 5251,
|
||||
.b[PSC_VOLTAGE_IN] = 0,
|
||||
.R[PSC_VOLTAGE_IN] = -2,
|
||||
.format[PSC_VOLTAGE_OUT] = direct,
|
||||
.m[PSC_VOLTAGE_OUT] = 5251,
|
||||
.b[PSC_VOLTAGE_OUT] = 0,
|
||||
.R[PSC_VOLTAGE_OUT] = -2,
|
||||
.format[PSC_TEMPERATURE] = direct,
|
||||
.m[PSC_TEMPERATURE] = 140,
|
||||
.b[PSC_TEMPERATURE] = 32100,
|
||||
.R[PSC_TEMPERATURE] = -2,
|
||||
/*
|
||||
* Current and Power measurement depends on the ohm value
|
||||
* of Rimon. m is multiplied by 1000 below to have an integer
|
||||
* and -3 is added to R to compensate.
|
||||
*/
|
||||
.format[PSC_CURRENT_IN] = direct,
|
||||
.m[PSC_CURRENT_IN] = 9538,
|
||||
.b[PSC_CURRENT_IN] = 0,
|
||||
.R[PSC_CURRENT_IN] = -6,
|
||||
.format[PSC_POWER] = direct,
|
||||
.m[PSC_POWER] = 4901,
|
||||
.b[PSC_POWER] = 0,
|
||||
.R[PSC_POWER] = -7,
|
||||
.func[0] = (PMBUS_HAVE_VIN |
|
||||
PMBUS_HAVE_VOUT |
|
||||
PMBUS_HAVE_VMON |
|
||||
PMBUS_HAVE_IIN |
|
||||
PMBUS_HAVE_PIN |
|
||||
PMBUS_HAVE_TEMP |
|
||||
PMBUS_HAVE_STATUS_VOUT |
|
||||
PMBUS_HAVE_STATUS_IOUT |
|
||||
PMBUS_HAVE_STATUS_INPUT |
|
||||
PMBUS_HAVE_STATUS_TEMP |
|
||||
PMBUS_HAVE_SAMPLES),
|
||||
.read_word_data = tps25990_read_word_data,
|
||||
.write_word_data = tps25990_write_word_data,
|
||||
.read_byte_data = tps25990_read_byte_data,
|
||||
.write_byte_data = tps25990_write_byte_data,
|
||||
#if IS_ENABLED(CONFIG_SENSORS_TPS25990_REGULATOR)
|
||||
.reg_desc = tps25990_reg_desc,
|
||||
.num_regulators = ARRAY_SIZE(tps25990_reg_desc),
|
||||
#endif
|
||||
},
|
||||
};
|
||||
|
||||
static const struct i2c_device_id tps25990_i2c_id[] = {
|
||||
{ .name = "tps25990" },
|
||||
{ }
|
||||
{ .name = "tps1689", .driver_data = tps1689 },
|
||||
{ .name = "tps25990", .driver_data = tps25990 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, tps25990_i2c_id);
|
||||
|
||||
static const struct of_device_id tps25990_of_match[] = {
|
||||
{ .compatible = "ti,tps25990" },
|
||||
{ .compatible = "ti,tps1689", .data = (void *)tps1689 },
|
||||
{ .compatible = "ti,tps25990", .data = (void *)tps25990 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tps25990_of_match);
|
||||
|
|
@ -401,8 +513,9 @@ MODULE_DEVICE_TABLE(of, tps25990_of_match);
|
|||
static int tps25990_probe(struct i2c_client *client)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct pmbus_driver_info *info;
|
||||
struct tps25990_data *data;
|
||||
const char *propname;
|
||||
enum chips chip_id;
|
||||
u32 rimon;
|
||||
int ret;
|
||||
|
||||
|
|
@ -415,15 +528,21 @@ static int tps25990_probe(struct i2c_client *client)
|
|||
rimon = TPS25990_DEFAULT_RIMON;
|
||||
}
|
||||
|
||||
info = devm_kmemdup(dev, &tps25990_base_info, sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
chip_id = (enum chips)(unsigned long)i2c_get_match_data(client);
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(struct tps25990_data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Adapt the current and power scale for each instance */
|
||||
tps25990_set_m(&info->m[PSC_CURRENT_IN], rimon);
|
||||
tps25990_set_m(&info->m[PSC_POWER], rimon);
|
||||
data->info = tps25990_base_info[chip_id];
|
||||
data->chip_id = chip_id;
|
||||
|
||||
return pmbus_do_probe(client, info);
|
||||
/* Adapt the current and power scale for each instance */
|
||||
tps25990_set_m(&data->info.m[PSC_CURRENT_IN], rimon);
|
||||
tps25990_set_m(&data->info.m[PSC_CURRENT_OUT], rimon);
|
||||
tps25990_set_m(&data->info.m[PSC_POWER], rimon);
|
||||
|
||||
return pmbus_do_probe(client, &data->info);
|
||||
}
|
||||
|
||||
static struct i2c_driver tps25990_driver = {
|
||||
|
|
|
|||
|
|
@ -16,13 +16,21 @@
|
|||
#include "pmbus.h"
|
||||
|
||||
enum chips {
|
||||
tps53647, tps53667, tps53676, tps53679, tps53681, tps53685, tps53688
|
||||
tps53622, tps53647, tps53659, tps53667, tps53676, tps53679, tps53681,
|
||||
tps53685, tps53688, tps536c7
|
||||
};
|
||||
|
||||
#define TPS53647_PAGE_NUM 1
|
||||
|
||||
#define TPS53676_USER_DATA_03 0xb3
|
||||
#define TPS536XX_PHASE_CONFIG 0xb3
|
||||
#define TPS53676_MAX_PHASES 7
|
||||
#define TPS536C7_MAX_PHASES 12
|
||||
|
||||
#define TPS536XX_PHASE_ENABLE BIT(7)
|
||||
#define TPS536XX_PHASE_PAGE BIT(4)
|
||||
|
||||
#define TPS53676_DEVICE_ID "TI\x53\x67\x60\x00"
|
||||
#define TPS536C7_DEVICE_ID "TI\x53\x6c\x70\x00"
|
||||
|
||||
#define TPS53679_PROT_VR12_5MV 0x01 /* VR12.0 mode, 5-mV DAC */
|
||||
#define TPS53679_PROT_VR12_5_10MV 0x02 /* VR12.5 mode, 10-mV DAC */
|
||||
|
|
@ -165,34 +173,72 @@ static int tps53681_identify(struct i2c_client *client,
|
|||
TPS53681_DEVICE_ID);
|
||||
}
|
||||
|
||||
static int tps53676_identify(struct i2c_client *client,
|
||||
struct pmbus_driver_info *info)
|
||||
static int tps536xx_read_block(struct i2c_client *client, u8 reg, u8 *buf,
|
||||
bool retry_without_pec)
|
||||
{
|
||||
int ret = i2c_smbus_read_block_data(client, reg, buf);
|
||||
|
||||
/*
|
||||
* Some TPS536C7 samples return an invalid PEC on the device-ID and
|
||||
* phase-configuration block reads. Retry once without PEC; ordinary
|
||||
* telemetry keeps using PEC.
|
||||
*/
|
||||
if (ret == -EBADMSG && retry_without_pec &&
|
||||
(client->flags & I2C_CLIENT_PEC)) {
|
||||
client->flags &= ~I2C_CLIENT_PEC;
|
||||
ret = i2c_smbus_read_block_data(client, reg, buf);
|
||||
client->flags |= I2C_CLIENT_PEC;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tps536xx_read_phases(struct i2c_client *client,
|
||||
const char *device_id, int max_phases,
|
||||
bool retry_without_pec, int *phases_a,
|
||||
int *phases_b)
|
||||
{
|
||||
u8 buf[I2C_SMBUS_BLOCK_MAX];
|
||||
int phases_a = 0, phases_b = 0;
|
||||
int i, ret;
|
||||
|
||||
ret = i2c_smbus_read_block_data(client, PMBUS_IC_DEVICE_ID, buf);
|
||||
ret = tps536xx_read_block(client, PMBUS_IC_DEVICE_ID, buf,
|
||||
retry_without_pec);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret != 6 || memcmp(buf, "TI\x53\x67\x60\x00", 6)) {
|
||||
if (ret != 6 || memcmp(buf, device_id, 6)) {
|
||||
dev_err(&client->dev, "Unexpected device ID: %*ph\n", ret, buf);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_read_block_data(client, TPS53676_USER_DATA_03, buf);
|
||||
ret = tps536xx_read_block(client, TPS536XX_PHASE_CONFIG, buf,
|
||||
retry_without_pec);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret != 24)
|
||||
return -EIO;
|
||||
for (i = 0; i < 2 * TPS53676_MAX_PHASES; i += 2) {
|
||||
if (buf[i + 1] & 0x80) {
|
||||
if (buf[i] & BIT(4))
|
||||
phases_b++;
|
||||
|
||||
*phases_a = 0;
|
||||
*phases_b = 0;
|
||||
for (i = 0; i < 2 * max_phases; i += 2) {
|
||||
if (buf[i + 1] & TPS536XX_PHASE_ENABLE) {
|
||||
if (buf[i] & TPS536XX_PHASE_PAGE)
|
||||
(*phases_b)++;
|
||||
else
|
||||
phases_a++;
|
||||
(*phases_a)++;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tps53676_identify(struct i2c_client *client,
|
||||
struct pmbus_driver_info *info)
|
||||
{
|
||||
int phases_a, phases_b, ret;
|
||||
|
||||
ret = tps536xx_read_phases(client, TPS53676_DEVICE_ID,
|
||||
TPS53676_MAX_PHASES, false,
|
||||
&phases_a, &phases_b);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
info->format[PSC_VOLTAGE_OUT] = linear;
|
||||
info->pages = 1;
|
||||
|
|
@ -213,6 +259,69 @@ static int tps53676_identify(struct i2c_client *client,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int tps536c7_identify(struct i2c_client *client,
|
||||
struct pmbus_driver_info *info)
|
||||
{
|
||||
int phases_a, phases_b, page, ret;
|
||||
|
||||
ret = tps536xx_read_phases(client, TPS536C7_DEVICE_ID,
|
||||
TPS536C7_MAX_PHASES, true,
|
||||
&phases_a, &phases_b);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (!phases_a) {
|
||||
dev_err(&client->dev,
|
||||
"TPS536C7 without channel A is not supported\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
info->format[PSC_VOLTAGE_OUT] = linear;
|
||||
/*
|
||||
* TPS536C7 can place up to 12 phases on channel A, which exceeds
|
||||
* PMBUS_PHASES. Report aggregate per-channel telemetry only and do
|
||||
* not populate info->phases[].
|
||||
*/
|
||||
info->pages = phases_b ? 2 : 1;
|
||||
|
||||
/*
|
||||
* pmbus_set_page() does not update the PAGE register on single-page
|
||||
* devices, so select page 0 explicitly in case the boot firmware
|
||||
* left the device on another page.
|
||||
*/
|
||||
if (info->pages == 1) {
|
||||
ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* With info->phases[] left unset the PMBus core never programs the
|
||||
* PHASE selector, so make sure each page reports the aggregate
|
||||
* current (PHASE = 0xff) rather than whatever a previous boot left.
|
||||
*/
|
||||
for (page = 0; page < info->pages; page++) {
|
||||
ret = pmbus_read_byte_data(client, page, PMBUS_PHASE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret == 0xff)
|
||||
continue;
|
||||
ret = pmbus_write_byte_data(client, page, PMBUS_PHASE, 0xff);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/* PHASE may be write-protected; confirm it actually changed. */
|
||||
ret = pmbus_read_byte_data(client, page, PMBUS_PHASE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret != 0xff) {
|
||||
dev_err(&client->dev,
|
||||
"failed to select aggregate PHASE on page %d\n",
|
||||
page);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tps53681_read_word_data(struct i2c_client *client, int page,
|
||||
int phase, int reg)
|
||||
{
|
||||
|
|
@ -277,6 +386,8 @@ static int tps53679_probe(struct i2c_client *client)
|
|||
case tps53676:
|
||||
info->identify = tps53676_identify;
|
||||
break;
|
||||
case tps53622:
|
||||
case tps53659:
|
||||
case tps53679:
|
||||
case tps53688:
|
||||
info->pages = TPS53679_PAGE_NUM;
|
||||
|
|
@ -292,6 +403,9 @@ static int tps53679_probe(struct i2c_client *client)
|
|||
info->pages = TPS53679_PAGE_NUM;
|
||||
info->identify = tps53685_identify;
|
||||
break;
|
||||
case tps536c7:
|
||||
info->identify = tps536c7_identify;
|
||||
break;
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
|
@ -301,26 +415,32 @@ static int tps53679_probe(struct i2c_client *client)
|
|||
|
||||
static const struct i2c_device_id tps53679_id[] = {
|
||||
{ .name = "bmr474", .driver_data = tps53676 },
|
||||
{ .name = "tps53622", .driver_data = tps53622 },
|
||||
{ .name = "tps53647", .driver_data = tps53647 },
|
||||
{ .name = "tps53659", .driver_data = tps53659 },
|
||||
{ .name = "tps53667", .driver_data = tps53667 },
|
||||
{ .name = "tps53676", .driver_data = tps53676 },
|
||||
{ .name = "tps53679", .driver_data = tps53679 },
|
||||
{ .name = "tps53681", .driver_data = tps53681 },
|
||||
{ .name = "tps53685", .driver_data = tps53685 },
|
||||
{ .name = "tps53688", .driver_data = tps53688 },
|
||||
{ .name = "tps536c7", .driver_data = tps536c7 },
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, tps53679_id);
|
||||
|
||||
static const struct of_device_id __maybe_unused tps53679_of_match[] = {
|
||||
{.compatible = "ti,tps53622", .data = (void *)tps53622},
|
||||
{.compatible = "ti,tps53647", .data = (void *)tps53647},
|
||||
{.compatible = "ti,tps53659", .data = (void *)tps53659},
|
||||
{.compatible = "ti,tps53667", .data = (void *)tps53667},
|
||||
{.compatible = "ti,tps53676", .data = (void *)tps53676},
|
||||
{.compatible = "ti,tps53679", .data = (void *)tps53679},
|
||||
{.compatible = "ti,tps53681", .data = (void *)tps53681},
|
||||
{.compatible = "ti,tps53685", .data = (void *)tps53685},
|
||||
{.compatible = "ti,tps53688", .data = (void *)tps53688},
|
||||
{.compatible = "ti,tps536c7", .data = (void *)tps536c7},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tps53679_of_match);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Hardware monitoring driver for Infineon Multi-phase Digital XDPE1A2G5B
|
||||
* and XDPE1A2G7B Controllers
|
||||
* Hardware monitoring driver for Infineon Multi-phase Digital XDPE1A2G5B,
|
||||
* XDPE1A2G7B and XDPE1A2G7C Controllers
|
||||
*
|
||||
* Copyright (c) 2026 Infineon Technologies. All rights reserved.
|
||||
*/
|
||||
|
|
@ -23,7 +23,7 @@ static int xdpe1a2g7b_identify(struct i2c_client *client,
|
|||
int vout_mode;
|
||||
|
||||
/*
|
||||
* XDPE1A2G5B and XDPE1A2G7B support both Linear and NVIDIA PWM VID data
|
||||
* XDPE1A2G5B and XDPE1A2G7B/7C support both Linear and NVIDIA PWM VID data
|
||||
* formats via VOUT_MODE. Note that the device pages/loops are not fully
|
||||
* independent: configuration is shared, so programming each page/loop
|
||||
* separately is not supported.
|
||||
|
|
@ -100,6 +100,7 @@ static int xdpe1a2g7b_probe(struct i2c_client *client)
|
|||
static const struct i2c_device_id xdpe1a2g7b_id[] = {
|
||||
{ .name = "xdpe1a2g5b" },
|
||||
{ .name = "xdpe1a2g7b" },
|
||||
{ .name = "xdpe1a2g7c" },
|
||||
{ }
|
||||
};
|
||||
|
||||
|
|
@ -108,6 +109,7 @@ MODULE_DEVICE_TABLE(i2c, xdpe1a2g7b_id);
|
|||
static const struct of_device_id __maybe_unused xdpe1a2g7b_of_match[] = {
|
||||
{ .compatible = "infineon,xdpe1a2g5b" },
|
||||
{ .compatible = "infineon,xdpe1a2g7b" },
|
||||
{ .compatible = "infineon,xdpe1a2g7c" },
|
||||
{}
|
||||
};
|
||||
|
||||
|
|
@ -125,6 +127,6 @@ static struct i2c_driver xdpe1a2g7b_driver = {
|
|||
module_i2c_driver(xdpe1a2g7b_driver);
|
||||
|
||||
MODULE_AUTHOR("Ashish Yadav <ashish.yadav@infineon.com>");
|
||||
MODULE_DESCRIPTION("PMBus driver for Infineon XDPE1A2G5B/7B");
|
||||
MODULE_DESCRIPTION("PMBus driver for Infineon XDPE1A2G5B/7B/7C");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS("PMBUS");
|
||||
|
|
|
|||
|
|
@ -611,8 +611,8 @@ static const struct of_device_id __maybe_unused pt5161l_of_match[] = {
|
|||
MODULE_DEVICE_TABLE(of, pt5161l_of_match);
|
||||
|
||||
static const struct acpi_device_id __maybe_unused pt5161l_acpi_match[] = {
|
||||
{ "PT5161L", 0 },
|
||||
{},
|
||||
{ .id = "PT5161L" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, pt5161l_acpi_match);
|
||||
|
||||
|
|
|
|||
|
|
@ -619,7 +619,7 @@ static int pwm_fan_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
channels = devm_kcalloc(dev, channel_count + 1,
|
||||
sizeof(struct hwmon_channel_info *), GFP_KERNEL);
|
||||
sizeof(*channels), GFP_KERNEL);
|
||||
if (!channels)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
|
|||
|
|
@ -18,13 +18,14 @@
|
|||
#include <linux/watchdog.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/stringify.h>
|
||||
#include "sch56xx-common.h"
|
||||
|
||||
/* Insmod parameters */
|
||||
static bool nowayout = WATCHDOG_NOWAYOUT;
|
||||
module_param(nowayout, bool, 0);
|
||||
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
|
||||
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
|
||||
__stringify(WATCHDOG_NOWAYOUT) ")");
|
||||
|
||||
#define SIO_SCH56XX_LD_EM 0x0C /* Embedded uController Logical Dev */
|
||||
#define SIO_UNLOCK_KEY 0x55 /* Key to enable Super-I/O */
|
||||
|
|
|
|||
|
|
@ -305,7 +305,8 @@ static int scmi_hwmon_probe(struct scmi_device *sdev)
|
|||
|
||||
scmi_sensors->info[type] =
|
||||
devm_kcalloc(dev, nr_count[type],
|
||||
sizeof(*scmi_sensors->info), GFP_KERNEL);
|
||||
sizeof(*scmi_sensors->info[type]),
|
||||
GFP_KERNEL);
|
||||
if (!scmi_sensors->info[type])
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -543,7 +543,10 @@ static ssize_t heater_enable_store(struct device *dev,
|
|||
|
||||
mutex_unlock(&data->i2c_lock);
|
||||
|
||||
return ret;
|
||||
if (ret != SHT3X_CMD_LENGTH)
|
||||
return ret < 0 ? ret : -EIO;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int update_interval_read(struct device *dev)
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@
|
|||
#define SHT4X_CRC8_LEN 1
|
||||
#define SHT4X_WORD_LEN 2
|
||||
#define SHT4X_RESPONSE_LENGTH 6
|
||||
#define STS4X_RESPONSE_LENGTH 3
|
||||
#define SHT4X_CRC8_POLYNOMIAL 0x31
|
||||
#define SHT4X_CRC8_INIT 0xff
|
||||
#define SHT4X_MIN_TEMPERATURE -45000
|
||||
|
|
@ -52,9 +53,15 @@
|
|||
|
||||
DECLARE_CRC8_TABLE(sht4x_crc8_table);
|
||||
|
||||
enum sht4x_chips {
|
||||
sht4x,
|
||||
sts4x,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sht4x_data - All the data required to operate an SHT4X chip
|
||||
* @client: the i2c client associated with the SHT4X
|
||||
* @chip_id: the chip type (sht4x or sts4x)
|
||||
* @heating_complete: the time that the last heating finished
|
||||
* @data_pending: true if and only if there are measurements to retrieve after heating
|
||||
* @heater_power: the power at which the heater will be started
|
||||
|
|
@ -67,7 +74,8 @@ DECLARE_CRC8_TABLE(sht4x_crc8_table);
|
|||
*/
|
||||
struct sht4x_data {
|
||||
struct i2c_client *client;
|
||||
unsigned long heating_complete; /* in jiffies */
|
||||
enum sht4x_chips chip_id;
|
||||
u64 heating_complete; /* in jiffies */
|
||||
bool data_pending;
|
||||
u32 heater_power; /* in milli-watts */
|
||||
u32 heater_time; /* in milli-seconds */
|
||||
|
|
@ -92,14 +100,18 @@ static int sht4x_read_values(struct sht4x_data *data)
|
|||
u8 crc;
|
||||
u8 cmd[SHT4X_CMD_LEN] = {SHT4X_CMD_MEASURE_HPM};
|
||||
u8 raw_data[SHT4X_RESPONSE_LENGTH];
|
||||
unsigned long curr_jiffies;
|
||||
size_t response_length = data->chip_id == sts4x ?
|
||||
STS4X_RESPONSE_LENGTH : SHT4X_RESPONSE_LENGTH;
|
||||
|
||||
curr_jiffies = jiffies;
|
||||
if (time_before(curr_jiffies, data->heating_complete))
|
||||
msleep(jiffies_to_msecs(data->heating_complete - curr_jiffies));
|
||||
if (data->chip_id != sts4x) {
|
||||
u64 curr_jiffies = get_jiffies_64();
|
||||
|
||||
if (time_before64(curr_jiffies, data->heating_complete))
|
||||
msleep(jiffies_to_msecs(data->heating_complete - curr_jiffies));
|
||||
}
|
||||
|
||||
if (data->data_pending &&
|
||||
time_before(jiffies, data->heating_complete + data->update_interval)) {
|
||||
time_before64(get_jiffies_64(), data->heating_complete + data->update_interval)) {
|
||||
data->data_pending = false;
|
||||
} else {
|
||||
next_update = data->last_updated +
|
||||
|
|
@ -115,15 +127,14 @@ static int sht4x_read_values(struct sht4x_data *data)
|
|||
usleep_range(SHT4X_MEAS_DELAY_HPM, SHT4X_MEAS_DELAY_HPM + SHT4X_DELAY_EXTRA);
|
||||
}
|
||||
|
||||
ret = i2c_master_recv(client, raw_data, SHT4X_RESPONSE_LENGTH);
|
||||
if (ret != SHT4X_RESPONSE_LENGTH) {
|
||||
ret = i2c_master_recv(client, raw_data, response_length);
|
||||
if (ret != response_length) {
|
||||
if (ret >= 0)
|
||||
ret = -ENODATA;
|
||||
return ret;
|
||||
}
|
||||
|
||||
t_ticks = raw_data[0] << 8 | raw_data[1];
|
||||
rh_ticks = raw_data[3] << 8 | raw_data[4];
|
||||
|
||||
crc = crc8(sht4x_crc8_table, &raw_data[0], SHT4X_WORD_LEN, CRC8_INIT_VALUE);
|
||||
if (crc != raw_data[2]) {
|
||||
|
|
@ -131,14 +142,19 @@ static int sht4x_read_values(struct sht4x_data *data)
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
crc = crc8(sht4x_crc8_table, &raw_data[3], SHT4X_WORD_LEN, CRC8_INIT_VALUE);
|
||||
if (crc != raw_data[5]) {
|
||||
dev_err(&client->dev, "data integrity check failed\n");
|
||||
return -EIO;
|
||||
data->temperature = ((21875 * (int32_t)t_ticks) >> 13) - 45000;
|
||||
|
||||
if (data->chip_id != sts4x) {
|
||||
rh_ticks = raw_data[3] << 8 | raw_data[4];
|
||||
crc = crc8(sht4x_crc8_table, &raw_data[3], SHT4X_WORD_LEN, CRC8_INIT_VALUE);
|
||||
if (crc != raw_data[5]) {
|
||||
dev_err(&client->dev, "data integrity check failed\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
data->humidity = ((15625 * (int32_t)rh_ticks) >> 13) - 6000;
|
||||
}
|
||||
|
||||
data->temperature = ((21875 * (int32_t)t_ticks) >> 13) - 45000;
|
||||
data->humidity = ((15625 * (int32_t)rh_ticks) >> 13) - 6000;
|
||||
data->last_updated = jiffies;
|
||||
data->valid = true;
|
||||
return 0;
|
||||
|
|
@ -190,9 +206,14 @@ static umode_t sht4x_hwmon_visible(const void *data,
|
|||
enum hwmon_sensor_types type,
|
||||
u32 attr, int channel)
|
||||
{
|
||||
const struct sht4x_data *chip_data = data;
|
||||
|
||||
switch (type) {
|
||||
case hwmon_temp:
|
||||
return 0444;
|
||||
case hwmon_humidity:
|
||||
if (chip_data->chip_id == sts4x)
|
||||
return 0;
|
||||
return 0444;
|
||||
case hwmon_chip:
|
||||
return 0644;
|
||||
|
|
@ -237,7 +258,9 @@ static ssize_t heater_enable_show(struct device *dev,
|
|||
{
|
||||
struct sht4x_data *data = dev_get_drvdata(dev);
|
||||
|
||||
return sysfs_emit(buf, "%u\n", time_before(jiffies, data->heating_complete));
|
||||
guard(hwmon_lock)(dev);
|
||||
|
||||
return sysfs_emit(buf, "%u\n", time_before64(get_jiffies_64(), data->heating_complete));
|
||||
}
|
||||
|
||||
static ssize_t heater_enable_store(struct device *dev,
|
||||
|
|
@ -279,14 +302,16 @@ static ssize_t heater_enable_store(struct device *dev,
|
|||
|
||||
guard(hwmon_lock)(dev);
|
||||
|
||||
if (time_before(jiffies, data->heating_complete))
|
||||
if (time_before64(get_jiffies_64(), data->heating_complete))
|
||||
return -EBUSY;
|
||||
|
||||
ret = i2c_master_send(data->client, &cmd, SHT4X_CMD_LEN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret != SHT4X_CMD_LEN)
|
||||
return -EIO;
|
||||
|
||||
data->heating_complete = jiffies + msecs_to_jiffies(heating_time_bound);
|
||||
data->heating_complete = get_jiffies_64() + msecs_to_jiffies(heating_time_bound);
|
||||
data->data_pending = true;
|
||||
return count;
|
||||
}
|
||||
|
|
@ -388,6 +413,7 @@ static const struct hwmon_chip_info sht4x_chip_info = {
|
|||
|
||||
static int sht4x_probe(struct i2c_client *client)
|
||||
{
|
||||
const struct attribute_group **groups = NULL;
|
||||
struct device *device = &client->dev;
|
||||
struct device *hwmon_dev;
|
||||
struct sht4x_data *data;
|
||||
|
|
@ -406,11 +432,15 @@ static int sht4x_probe(struct i2c_client *client)
|
|||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->chip_id = (uintptr_t)i2c_get_match_data(client);
|
||||
data->update_interval = SHT4X_MIN_POLL_INTERVAL;
|
||||
data->client = client;
|
||||
data->heater_power = 200;
|
||||
data->heater_time = 1000;
|
||||
data->heating_complete = jiffies;
|
||||
if (data->chip_id != sts4x) {
|
||||
data->heater_power = 200;
|
||||
data->heater_time = 1000;
|
||||
data->heating_complete = get_jiffies_64();
|
||||
groups = sht4x_groups;
|
||||
}
|
||||
|
||||
crc8_populate_msb(sht4x_crc8_table, SHT4X_CRC8_POLYNOMIAL);
|
||||
|
||||
|
|
@ -424,19 +454,21 @@ static int sht4x_probe(struct i2c_client *client)
|
|||
client->name,
|
||||
data,
|
||||
&sht4x_chip_info,
|
||||
sht4x_groups);
|
||||
groups);
|
||||
|
||||
return PTR_ERR_OR_ZERO(hwmon_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id sht4x_id[] = {
|
||||
{ .name = "sht4x" },
|
||||
{ .name = "sht4x", .driver_data = sht4x },
|
||||
{ .name = "sts4x", .driver_data = sts4x },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, sht4x_id);
|
||||
|
||||
static const struct of_device_id sht4x_of_match[] = {
|
||||
{ .compatible = "sensirion,sht4x" },
|
||||
{ .compatible = "sensirion,sht4x", .data = (void *)sht4x },
|
||||
{ .compatible = "sensirion,sts40", .data = (void *)sts4x },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sht4x_of_match);
|
||||
|
|
|
|||
|
|
@ -433,6 +433,26 @@ static const struct socfpga_hwmon_board_data agilex_hwmon_board = {
|
|||
.num_volt = ARRAY_SIZE(agilex_hwmon_volt_channels),
|
||||
};
|
||||
|
||||
/*
|
||||
* Agilex 5 exposes the SDM and three corner temperature sensors. Channel 2
|
||||
* (top-left corner on Agilex) is not present in hardware, so the SDM channel
|
||||
* numbering keeps the gap (0, 1, 3, 4) rather than renumbering.
|
||||
*/
|
||||
static const struct socfpga_hwmon_channel agilex5_hwmon_temp_channels[] = {
|
||||
{ SOCFPGA_HWMON_CHAN(0, 0), "Main Die SDM" },
|
||||
{ SOCFPGA_HWMON_CHAN(1, 0), "Main Die corner bottom left max" },
|
||||
{ SOCFPGA_HWMON_CHAN(3, 0), "Main Die corner bottom right max" },
|
||||
{ SOCFPGA_HWMON_CHAN(4, 0), "Main Die corner top right max" },
|
||||
};
|
||||
|
||||
/* Agilex 5 reuses the Agilex voltage SDM page/channel encoding and labels. */
|
||||
static const struct socfpga_hwmon_board_data agilex5_hwmon_board = {
|
||||
.temp = agilex5_hwmon_temp_channels,
|
||||
.num_temp = ARRAY_SIZE(agilex5_hwmon_temp_channels),
|
||||
.volt = agilex_hwmon_volt_channels,
|
||||
.num_volt = ARRAY_SIZE(agilex_hwmon_volt_channels),
|
||||
};
|
||||
|
||||
static const struct socfpga_hwmon_board_data *
|
||||
socfpga_hwmon_get_board(struct device *dev)
|
||||
{
|
||||
|
|
@ -443,6 +463,8 @@ socfpga_hwmon_get_board(struct device *dev)
|
|||
|
||||
if (of_device_is_compatible(np, "intel,stratix10-svc"))
|
||||
return &s10_hwmon_board;
|
||||
if (of_device_is_compatible(np, "intel,agilex5-svc"))
|
||||
return &agilex5_hwmon_board;
|
||||
if (of_device_is_compatible(np, "intel,agilex-svc"))
|
||||
return &agilex_hwmon_board;
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/dev_printk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i3c/device.h>
|
||||
|
|
@ -637,44 +638,54 @@ static int spd5118_i2c_init(struct i2c_client *client)
|
|||
I2C_FUNC_SMBUS_WORD_DATA))
|
||||
return -ENODEV;
|
||||
|
||||
/* Early check to avoid obviously unsupported I2C devices */
|
||||
regval = i2c_smbus_read_word_swapped(client, SPD5118_REG_TYPE);
|
||||
if (regval < 0 || (regval && regval != 0x5118))
|
||||
if (regval < 0)
|
||||
return regval;
|
||||
|
||||
/*
|
||||
* Some SPD5118 devices report 0x0 when page 0 is not selected,
|
||||
* so we only fail here if the register value is not 0x0 or 0x5118.
|
||||
*/
|
||||
if (regval && regval != 0x5118)
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* If the device type registers return 0, it is possible that the chip
|
||||
* has a non-zero page selected and takes the specification literally,
|
||||
* We must select page 0 to ensure that we can reliably read the volatile
|
||||
* registers on chips that take the specification literally,
|
||||
* i.e. disables access to volatile registers besides the page register
|
||||
* if the page is not 0. The Renesas/ITD SPD5118 Hub Controller is known
|
||||
* to show this behavior. Try to identify such chips.
|
||||
* to show this behavior.
|
||||
*
|
||||
* We must also perform an unconditional register write to detect if
|
||||
* the i2c controller blocks write accesses to the SPD device. Some Intel
|
||||
* controllers might be configured by the BIOS to do this.
|
||||
*/
|
||||
if (!regval) {
|
||||
/* Vendor ID registers must also be 0 */
|
||||
regval = i2c_smbus_read_word_data(client, SPD5118_REG_VENDOR);
|
||||
if (regval)
|
||||
return -ENODEV;
|
||||
mode = i2c_smbus_read_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE);
|
||||
if (mode < 0)
|
||||
return mode;
|
||||
|
||||
/* The selected page in MR11 must not be 0 */
|
||||
mode = i2c_smbus_read_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE);
|
||||
if (mode < 0 || (mode & ~SPD5118_LEGACY_MODE_MASK) ||
|
||||
!(mode & SPD5118_LEGACY_PAGE_MASK))
|
||||
return -ENODEV;
|
||||
/* 16-bit addressing is not supported */
|
||||
if (mode & SPD5118_LEGACY_MODE_ADDR) {
|
||||
dev_notice(&client->dev,
|
||||
"Unable to access device due to 16-bit addressing being enabled\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
err = i2c_smbus_write_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE,
|
||||
mode & SPD5118_LEGACY_MODE_ADDR);
|
||||
if (err)
|
||||
return -ENODEV;
|
||||
err = i2c_smbus_write_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE,
|
||||
mode & ~SPD5118_LEGACY_PAGE_MASK);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* If the device type registers are still bad after selecting
|
||||
* page 0, this is not a SPD5118 device. Restore original
|
||||
* legacy mode register value and abort.
|
||||
*/
|
||||
regval = i2c_smbus_read_word_swapped(client, SPD5118_REG_TYPE);
|
||||
if (regval != 0x5118) {
|
||||
i2c_smbus_write_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE, mode);
|
||||
return -ENODEV;
|
||||
}
|
||||
/* We only need to access SPD5118_REG_TYPE again if regval was 0x0 */
|
||||
if (regval == 0x5118)
|
||||
return 0;
|
||||
|
||||
regval = i2c_smbus_read_word_swapped(client, SPD5118_REG_TYPE);
|
||||
if (regval != 0x5118) {
|
||||
/* Restore original register content */
|
||||
i2c_smbus_write_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE, mode);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* We are reasonably sure that this is really a SPD5118 hub controller */
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ struct tmp102 {
|
|||
const char *label;
|
||||
struct regmap *regmap;
|
||||
u16 config_orig;
|
||||
unsigned long ready_time;
|
||||
u64 ready_time;
|
||||
u16 sample_time;
|
||||
};
|
||||
|
||||
|
|
@ -106,7 +106,7 @@ static int tmp102_read_temp(struct device *dev, u32 attr, long *val)
|
|||
switch (attr) {
|
||||
case hwmon_temp_input:
|
||||
/* Is it too early to return a conversion ? */
|
||||
if (time_before(jiffies, tmp102->ready_time)) {
|
||||
if (time_before64(get_jiffies_64(), tmp102->ready_time)) {
|
||||
dev_dbg(dev, "%s: Conversion not ready yet..\n", __func__);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
|
@ -354,7 +354,7 @@ static int tmp102_probe(struct i2c_client *client)
|
|||
* Mark that we are not ready with data until the first
|
||||
* conversion is complete
|
||||
*/
|
||||
tmp102->ready_time = jiffies + msecs_to_jiffies(CONVERSION_TIME_MS);
|
||||
tmp102->ready_time = get_jiffies_64() + msecs_to_jiffies(CONVERSION_TIME_MS);
|
||||
|
||||
hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
|
||||
tmp102,
|
||||
|
|
@ -387,7 +387,7 @@ static int tmp102_resume(struct device *dev)
|
|||
err = regmap_update_bits(tmp102->regmap, TMP102_CONF_REG,
|
||||
TMP102_CONF_SD, 0);
|
||||
|
||||
tmp102->ready_time = jiffies + msecs_to_jiffies(CONVERSION_TIME_MS);
|
||||
tmp102->ready_time = get_jiffies_64() + msecs_to_jiffies(CONVERSION_TIME_MS);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user