mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-23 20:24:12 +08:00
hwmon updates for v6.9
* New drivers for - Amphenol ChipCap 2 - ASPEED g6 PWM/Fan tach - Astera Labs PT5161L retimer - ASUS ROG RYUJIN II 360 AIO cooler - LTC4282 - Microsoft Surface devices - MPS MPQ8785 Synchronous Step-Down Converter - NZXT Kraken X and Z series AIO CPU coolers * Additional chip support in existing drivers - Ayaneo Air Plus 7320u (oxp-sensors) - INA260 (ina2xx) - XPS 9315 (dell-smm) - MSI customer ID (nct6683) * Devicetree bindings updates - Common schema for hardware monitoring devices - Common schema for fans - Update chip descriptions to use common schema - Document regulator properties in several drivers - Explicit bindings for infineon buck converters * Other improvements - Replaced rbtree with maple tree register cache in several drivers - Added support for humidity min/max alarm and volatage fault attributes to hwmon core - Dropped non-functional I2C_CLASS_HWMON support for drivers w/o detect() - Dropped obsolete and redundant entried from MAINTAINERS - Cleaned up axi-fan-control and coretemp drivers - Minor fixes and improvements in several other drivers -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEiHPvMQj9QTOCiqgVyx8mb86fmYEFAmXvI8QACgkQyx8mb86f mYFWTQ/9Hz4QfgIueAEhWGy0XORt7nSCeexkWIR341iyRk4LQA0UjDQg3+Ub5Hi1 7IBGDNi124S1I/W8fJl1KFgXjbirhuzpHq4DF60Ty+egTZ9IZpu3uySR4mubixYm J3el7SIJBvs3SgMFdS/HCtJqeU5HLk+1NjfWDmnq0Z27GHzEy/Nglj2TTO1CjMz/ tZ/LOWkdG5tMbwI8SZ/mBNMXMpYp/jnUZbrMxgZ/y+07R3jP7i1GWRjq5ZGuWaP6 SQEs4vfss/y6WUSZZenIIigRIAiAnsNIrjUjrMKPdf0EkjB+0ljn/jLXpAsUU6fL 07Uy+AwQb89PPWIKHdldn7/MYaR3zU+LwKwPbjULuvpo6Cj87WcIP/x7QqL//Ise Ix2Buy/oWoVHKG7Gtf+mF+Ott5MeFgj6pVsCN4IAYYdyai0GPM3RpFAcrIXFCjsE i3M5aRC46Yy8Ba6ov3Jmlh83kc9LauJrlCxIxIXTlUJIZiW7a5w083QDSaw3qQdB hukwfC8wOzpEsQngkBQyRSpF468lASzc4lp++tPLS/W0zxBrgrnHvgXTHnN8IxvQ ocuD5tVMg9gE2xT88t8BHTcw2uv03U5RoXY+nucbxA+Y/aT2t+jZhX9cPbq4+Rhe v7XDGMxcBYgtfwx6JT97DKqW9qLc01k8wxonCOrUop6B/+MdRbw= =BTB3 -----END PGP SIGNATURE----- Merge tag 'hwmon-for-v6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging Pull hwmon updates from Guenter Roeck: "New drivers: - Amphenol ChipCap 2 - ASPEED g6 PWM/Fan tach - Astera Labs PT5161L retimer - ASUS ROG RYUJIN II 360 AIO cooler - LTC4282 - Microsoft Surface devices - MPS MPQ8785 Synchronous Step-Down Converter - NZXT Kraken X and Z series AIO CPU coolers Additional chip support in existing drivers: - Ayaneo Air Plus 7320u (oxp-sensors) - INA260 (ina2xx) - XPS 9315 (dell-smm) - MSI customer ID (nct6683) Devicetree bindings updates: - Common schema for hardware monitoring devices - Common schema for fans - Update chip descriptions to use common schema - Document regulator properties in several drivers - Explicit bindings for infineon buck converters Other improvements: - Replaced rbtree with maple tree register cache in several drivers - Added support for humidity min/max alarm and volatage fault attributes to hwmon core - Dropped non-functional I2C_CLASS_HWMON support for drivers w/o detect() - Dropped obsolete and redundant entried from MAINTAINERS - Cleaned up axi-fan-control and coretemp drivers - Minor fixes and improvements in several other drivers" * tag 'hwmon-for-v6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (70 commits) hwmon: (dell-smm) Add XPS 9315 to fan control whitelist hwmon: (aspeed-g6-pwm-tacho): Support for ASPEED g6 PWM/Fan tach dt-bindings: hwmon: Support Aspeed g6 PWM TACH Control dt-bindings: hwmon: fan: Add fan binding to schema dt-bindings: hwmon: tda38640: Add interrupt & regulator properties hwmon: (amc6821) add of_match table dt-bindings: hwmon: lm75: use common hwmon schema hwmon: (sis5595) drop unused DIV_TO_REG function dt-bindings: hwmon: reference common hwmon schema dt-bindings: hwmon: lltc,ltc4286: use common hwmon schema dt-bindings: hwmon: adi,adm1275: use common hwmon schema dt-bindings: hwmon: ti,ina2xx: use common hwmon schema dt-bindings: hwmon: add common properties hwmon: (pmbus/ir38064) Use PMBUS_REGULATOR_ONE to declare regulator hwmon: (pmbus/lm25066) Use PMBUS_REGULATOR_ONE to declare regulator hwmon: (pmbus/tda38640) Use PMBUS_REGULATOR_ONE to declare regulator regulator: dt-bindings: promote infineon buck converters to their own binding dt-bindings: hwmon/pmbus: ti,lm25066: document regulators dt-bindings: hwmon: nuvoton,nct6775: Add compatible value for NCT6799 MAINTAINERS: Drop redundant hwmon entries ...
This commit is contained in:
commit
15223fdbdf
@ -149,6 +149,15 @@ Description:
|
||||
|
||||
RW
|
||||
|
||||
What: /sys/class/hwmon/hwmonX/inY_fault
|
||||
Description:
|
||||
Reports a voltage hard failure (eg: shorted component)
|
||||
|
||||
- 1: Failed
|
||||
- 0: Ok
|
||||
|
||||
RO
|
||||
|
||||
What: /sys/class/hwmon/hwmonX/cpuY_vid
|
||||
Description:
|
||||
CPU core reference voltage.
|
||||
@ -968,6 +977,15 @@ Description:
|
||||
|
||||
RW
|
||||
|
||||
What: /sys/class/hwmon/hwmonX/humidityY_max_alarm
|
||||
Description:
|
||||
Maximum humidity detection
|
||||
|
||||
- 0: OK
|
||||
- 1: Maximum humidity detected
|
||||
|
||||
RO
|
||||
|
||||
What: /sys/class/hwmon/hwmonX/humidityY_max_hyst
|
||||
Description:
|
||||
Humidity hysteresis value for max limit.
|
||||
@ -987,6 +1005,15 @@ Description:
|
||||
|
||||
RW
|
||||
|
||||
What: /sys/class/hwmon/hwmonX/humidityY_min_alarm
|
||||
Description:
|
||||
Minimum humidity detection
|
||||
|
||||
- 0: OK
|
||||
- 1: Minimum humidity detected
|
||||
|
||||
RO
|
||||
|
||||
What: /sys/class/hwmon/hwmonX/humidityY_min_hyst
|
||||
Description:
|
||||
Humidity hysteresis value for min limit.
|
||||
|
@ -46,7 +46,10 @@ required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
allOf:
|
||||
- $ref: hwmon-common.yaml#
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
|
@ -33,10 +33,6 @@ properties:
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
shunt-resistor-micro-ohms:
|
||||
description:
|
||||
Shunt resistor value in micro-Ohm.
|
||||
|
||||
adi,volt-curr-sample-average:
|
||||
description: |
|
||||
Number of samples to be used to report voltage and current values.
|
||||
@ -50,6 +46,7 @@ properties:
|
||||
enum: [1, 2, 4, 8, 16, 32, 64, 128]
|
||||
|
||||
allOf:
|
||||
- $ref: hwmon-common.yaml#
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
@ -107,7 +104,7 @@ required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
|
@ -31,7 +31,10 @@ required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
allOf:
|
||||
- $ref: hwmon-common.yaml#
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
|
159
Documentation/devicetree/bindings/hwmon/adi,ltc4282.yaml
Normal file
159
Documentation/devicetree/bindings/hwmon/adi,ltc4282.yaml
Normal file
@ -0,0 +1,159 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/hwmon/adi,ltc4282.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices LTC4282 I2C High Current Hot Swap Controller over I2C
|
||||
|
||||
maintainers:
|
||||
- Nuno Sa <nuno.sa@analog.com>
|
||||
|
||||
description: |
|
||||
Analog Devices LTC4282 I2C High Current Hot Swap Controller over I2C.
|
||||
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/ltc4282.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,ltc4282
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
vdd-supply: true
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
'#clock-cells':
|
||||
const: 0
|
||||
|
||||
adi,rsense-nano-ohms:
|
||||
description: Value of the sense resistor.
|
||||
|
||||
adi,vin-mode-microvolt:
|
||||
description:
|
||||
Selects operating range for the Undervoltage, Overvoltage and Foldback
|
||||
pins. Also for the ADC. Should be set to the nominal input voltage.
|
||||
enum: [3300000, 5000000, 12000000, 24000000]
|
||||
default: 12000000
|
||||
|
||||
adi,fet-bad-timeout-ms:
|
||||
description:
|
||||
From the moment a FET bad conditions is present, this property selects the
|
||||
wait time/timeout for a FET-bad fault to be signaled. Setting this to 0,
|
||||
disables FET bad faults to be reported.
|
||||
default: 255
|
||||
maximum: 255
|
||||
|
||||
adi,overvoltage-dividers:
|
||||
description: |
|
||||
Select which dividers to use for VDD Overvoltage detection. Note that
|
||||
when the internal dividers are used the threshold is referenced to VDD.
|
||||
The percentages in the datasheet are misleading since the actual values
|
||||
to look for are in the "Absolute Maximum Ratings" table in the
|
||||
"Comparator Inputs" section. In there there's a line for each of the 5%,
|
||||
10% and 15% settings with the actual min, typical and max tolerances.
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
enum: [external, vdd_5_percent, vdd_10_percent, vdd_15_percent]
|
||||
default: external
|
||||
|
||||
adi,undervoltage-dividers:
|
||||
description: |
|
||||
Select which dividers to use for VDD Overvoltage detection. Note that
|
||||
when the internal dividers are used the threshold is referenced to VDD.
|
||||
The percentages in the datasheet are misleading since the actual values
|
||||
to look for are in the "Absolute Maximum Ratings" table in the
|
||||
"Comparator Inputs" section. In there there's a line for each of the 5%,
|
||||
10% and 15% settings with the actual min, typical and max tolerances.
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
enum: [external, vdd_5_percent, vdd_10_percent, vdd_15_percent]
|
||||
default: external
|
||||
|
||||
adi,current-limit-sense-microvolt:
|
||||
description:
|
||||
The current limit sense voltage of the chip is adjustable between
|
||||
12.5mV and 34.4mV in 3.1mV steps. This effectively limits the current
|
||||
on the load.
|
||||
enum: [12500, 15625, 18750, 21875, 25000, 28125, 31250, 34375]
|
||||
default: 25000
|
||||
|
||||
adi,overcurrent-retry:
|
||||
description:
|
||||
If set, enables the chip to auto-retry 256 timer cycles after an
|
||||
Overcurrent fault.
|
||||
type: boolean
|
||||
|
||||
adi,overvoltage-retry-disable:
|
||||
description:
|
||||
If set, disables the chip to auto-retry 50ms after an Overvoltage fault.
|
||||
It's enabled by default.
|
||||
type: boolean
|
||||
|
||||
adi,undervoltage-retry-disable:
|
||||
description:
|
||||
If set, disables the chip to auto-retry 50ms after an Undervoltage fault.
|
||||
It's enabled by default.
|
||||
type: boolean
|
||||
|
||||
adi,fault-log-enable:
|
||||
description:
|
||||
If set, enables the FAULT_LOG and ADC_ALERT_LOG registers to be written
|
||||
to the EEPROM when a fault bit transitions high and hence, will be
|
||||
available after a power cycle (the chip loads the contents of
|
||||
the EE_FAULT_LOG register - the one in EEPROM - into FAULT_LOG at boot).
|
||||
type: boolean
|
||||
|
||||
adi,gpio1-mode:
|
||||
description: Defines the function of the Pin. It can indicate that power is
|
||||
good (PULL the pin low when power is not good) or that power is bad (Go
|
||||
into high-z when power is not good).
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
enum: [power_bad, power_good]
|
||||
default: power_good
|
||||
|
||||
adi,gpio2-mode:
|
||||
description: Defines the function of the Pin. It can be set as the input for
|
||||
the ADC or indicating that the MOSFET is in stress (dissipating power).
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
enum: [adc_input, stress_fet]
|
||||
default: adc_input
|
||||
|
||||
adi,gpio3-monitor-enable:
|
||||
description: If set, gpio3 is set as input for the ADC instead of gpio2.
|
||||
type: boolean
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
required:
|
||||
- adi,gpio3-monitor-enable
|
||||
then:
|
||||
properties:
|
||||
adi,gpio2-mode:
|
||||
const: stress_fet
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- adi,rsense-nano-ohms
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
hwmon@50 {
|
||||
compatible = "adi,ltc4282";
|
||||
reg = <0x50>;
|
||||
adi,rsense-nano-ohms = <500>;
|
||||
|
||||
adi,gpio1-mode = "power_good";
|
||||
adi,gpio2-mode = "adc_input";
|
||||
};
|
||||
};
|
||||
...
|
@ -0,0 +1,77 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/hwmon/amphenol,chipcap2.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: ChipCap 2 humidity and temperature iio sensor
|
||||
|
||||
maintainers:
|
||||
- Javier Carrasco <javier.carrasco.cruz@gmail.com>
|
||||
|
||||
description: |
|
||||
Relative humidity and temperature sensor on I2C bus.
|
||||
|
||||
Datasheets:
|
||||
https://www.amphenol-sensors.com/en/telaire/humidity/527-humidity-sensors/3095-chipcap-2
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
oneOf:
|
||||
- const: amphenol,cc2d23
|
||||
- items:
|
||||
- enum:
|
||||
- amphenol,cc2d23s
|
||||
- amphenol,cc2d25
|
||||
- amphenol,cc2d25s
|
||||
- amphenol,cc2d33
|
||||
- amphenol,cc2d33s
|
||||
- amphenol,cc2d35
|
||||
- amphenol,cc2d35s
|
||||
- const: amphenol,cc2d23
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
items:
|
||||
- description: measurement ready indicator
|
||||
- description: low humidity alarm
|
||||
- description: high humidity alarm
|
||||
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: ready
|
||||
- const: low
|
||||
- const: high
|
||||
|
||||
vdd-supply:
|
||||
description:
|
||||
Dedicated, controllable supply-regulator to reset the device and
|
||||
enter in command mode.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- vdd-supply
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
humidity@28 {
|
||||
compatible = "amphenol,cc2d23s", "amphenol,cc2d23";
|
||||
reg = <0x28>;
|
||||
interrupt-parent = <&gpio>;
|
||||
interrupts = <4 IRQ_TYPE_EDGE_RISING>,
|
||||
<5 IRQ_TYPE_EDGE_RISING>,
|
||||
<6 IRQ_TYPE_EDGE_RISING>;
|
||||
interrupt-names = "ready", "low", "high";
|
||||
vdd-supply = <®_vdd>;
|
||||
};
|
||||
};
|
@ -0,0 +1,71 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
# Copyright (C) 2023 Aspeed, Inc.
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/hwmon/aspeed,g6-pwm-tach.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: ASPEED G6 PWM and Fan Tach controller
|
||||
|
||||
maintainers:
|
||||
- Billy Tsai <billy_tsai@aspeedtech.com>
|
||||
|
||||
description: |
|
||||
The ASPEED PWM controller can support up to 16 PWM outputs.
|
||||
The ASPEED Fan Tacho controller can support up to 16 fan tach input.
|
||||
They are independent hardware blocks, which are different from the
|
||||
previous version of the ASPEED chip.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- aspeed,ast2600-pwm-tach
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
clocks:
|
||||
maxItems: 1
|
||||
|
||||
resets:
|
||||
maxItems: 1
|
||||
|
||||
"#pwm-cells":
|
||||
const: 3
|
||||
|
||||
patternProperties:
|
||||
"^fan-[0-9]+$":
|
||||
$ref: fan-common.yaml#
|
||||
unevaluatedProperties: false
|
||||
required:
|
||||
- tach-ch
|
||||
|
||||
required:
|
||||
- reg
|
||||
- clocks
|
||||
- resets
|
||||
- "#pwm-cells"
|
||||
- compatible
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/clock/aspeed-clock.h>
|
||||
pwm_tach: pwm-tach-controller@1e610000 {
|
||||
compatible = "aspeed,ast2600-pwm-tach";
|
||||
reg = <0x1e610000 0x100>;
|
||||
clocks = <&syscon ASPEED_CLK_AHB>;
|
||||
resets = <&syscon ASPEED_RESET_PWM>;
|
||||
#pwm-cells = <3>;
|
||||
|
||||
fan-0 {
|
||||
tach-ch = /bits/ 8 <0x0>;
|
||||
pwms = <&pwm_tach 0 40000 0>;
|
||||
};
|
||||
|
||||
fan-1 {
|
||||
tach-ch = /bits/ 8 <0x1 0x2>;
|
||||
pwms = <&pwm_tach 1 40000 0>;
|
||||
};
|
||||
};
|
79
Documentation/devicetree/bindings/hwmon/fan-common.yaml
Normal file
79
Documentation/devicetree/bindings/hwmon/fan-common.yaml
Normal file
@ -0,0 +1,79 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/hwmon/fan-common.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Common Fan Properties
|
||||
|
||||
maintainers:
|
||||
- Naresh Solanki <naresh.solanki@9elements.com>
|
||||
- Billy Tsai <billy_tsai@aspeedtech.com>
|
||||
|
||||
properties:
|
||||
max-rpm:
|
||||
description:
|
||||
Max RPM supported by fan.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
maximum: 100000
|
||||
|
||||
min-rpm:
|
||||
description:
|
||||
Min RPM supported by fan.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
maximum: 1000
|
||||
|
||||
pulses-per-revolution:
|
||||
description:
|
||||
The number of pulse from fan sensor per revolution.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
maximum: 4
|
||||
|
||||
tach-div:
|
||||
description:
|
||||
Divisor for the tach sampling clock, which determines the sensitivity of the tach pin.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
target-rpm:
|
||||
description:
|
||||
The default desired fan speed in RPM.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
|
||||
fan-driving-mode:
|
||||
description:
|
||||
Select the driving mode of the fan.(DC, PWM and so on)
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
enum: [ dc, pwm ]
|
||||
|
||||
pwms:
|
||||
description:
|
||||
PWM provider.
|
||||
maxItems: 1
|
||||
|
||||
"#cooling-cells":
|
||||
const: 2
|
||||
|
||||
cooling-levels:
|
||||
description:
|
||||
The control value which correspond to thermal cooling states.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
|
||||
tach-ch:
|
||||
description:
|
||||
The tach channel used for the fan.
|
||||
$ref: /schemas/types.yaml#/definitions/uint8-array
|
||||
|
||||
label:
|
||||
description:
|
||||
Optional fan label
|
||||
|
||||
fan-supply:
|
||||
description:
|
||||
Power supply for fan.
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
additionalProperties: true
|
||||
|
||||
...
|
19
Documentation/devicetree/bindings/hwmon/hwmon-common.yaml
Normal file
19
Documentation/devicetree/bindings/hwmon/hwmon-common.yaml
Normal file
@ -0,0 +1,19 @@
|
||||
# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/hwmon/hwmon-common.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Hardware Monitoring Devices Common Properties
|
||||
|
||||
maintainers:
|
||||
- Guenter Roeck <linux@roeck-us.net>
|
||||
|
||||
properties:
|
||||
label:
|
||||
description: A descriptive name for this device.
|
||||
|
||||
shunt-resistor-micro-ohms:
|
||||
description: The value of current sense resistor.
|
||||
|
||||
additionalProperties: true
|
@ -25,7 +25,10 @@ required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
allOf:
|
||||
- $ref: hwmon-common.yaml#
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
|
@ -25,15 +25,14 @@ properties:
|
||||
The default is 102.4 volts.
|
||||
type: boolean
|
||||
|
||||
shunt-resistor-micro-ohms:
|
||||
description:
|
||||
Resistor value micro-ohms.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
allOf:
|
||||
- $ref: hwmon-common.yaml#
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
|
@ -57,6 +57,7 @@ required:
|
||||
- reg
|
||||
|
||||
allOf:
|
||||
- $ref: hwmon-common.yaml#
|
||||
- if:
|
||||
not:
|
||||
properties:
|
||||
@ -71,7 +72,7 @@ allOf:
|
||||
properties:
|
||||
interrupts: false
|
||||
|
||||
additionalProperties: false
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
|
@ -25,6 +25,7 @@ properties:
|
||||
- nuvoton,nct6796
|
||||
- nuvoton,nct6797
|
||||
- nuvoton,nct6798
|
||||
- nuvoton,nct6799
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
@ -30,6 +30,23 @@ properties:
|
||||
unconnected(has internal pull-down).
|
||||
type: boolean
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
regulators:
|
||||
type: object
|
||||
description:
|
||||
list of regulators provided by this controller.
|
||||
|
||||
properties:
|
||||
vout:
|
||||
$ref: /schemas/regulator/regulator.yaml#
|
||||
type: object
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
@ -38,6 +55,7 @@ additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
@ -45,5 +63,15 @@ examples:
|
||||
tda38640@40 {
|
||||
compatible = "infineon,tda38640";
|
||||
reg = <0x40>;
|
||||
|
||||
interrupt-parent = <&smb_pex_cpu0_event>;
|
||||
interrupts = <10 IRQ_TYPE_LEVEL_LOW>;
|
||||
|
||||
regulators {
|
||||
pvnn_main_cpu0: vout {
|
||||
regulator-name = "pvnn_main_cpu0";
|
||||
regulator-enable-ramp-delay = <200>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -34,11 +34,26 @@ properties:
|
||||
Shunt (sense) resistor value in micro-Ohms
|
||||
default: 1000
|
||||
|
||||
regulators:
|
||||
type: object
|
||||
|
||||
properties:
|
||||
vout:
|
||||
$ref: /schemas/regulator/regulator.yaml#
|
||||
type: object
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
allOf:
|
||||
- $ref: /schemas/hwmon/hwmon-common.yaml#
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
|
@ -28,10 +28,14 @@ properties:
|
||||
- ti,ina231
|
||||
- ti,ina237
|
||||
- ti,ina238
|
||||
- ti,ina260
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
"#io-channel-cells":
|
||||
const: 1
|
||||
|
||||
shunt-resistor:
|
||||
description:
|
||||
Shunt resistor value in micro-Ohm.
|
||||
@ -66,7 +70,10 @@ required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
allOf:
|
||||
- $ref: hwmon-common.yaml#
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
@ -77,6 +84,8 @@ examples:
|
||||
power-sensor@44 {
|
||||
compatible = "ti,ina220";
|
||||
reg = <0x44>;
|
||||
#io-channel-cells = <1>;
|
||||
label = "vdd_3v0";
|
||||
shunt-resistor = <1000>;
|
||||
vs-supply = <&vdd_3v0>;
|
||||
};
|
||||
|
@ -72,7 +72,10 @@ required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
allOf:
|
||||
- $ref: hwmon-common.yaml#
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
|
@ -35,7 +35,10 @@ required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
allOf:
|
||||
- $ref: hwmon-common.yaml#
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
|
@ -0,0 +1,45 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/regulator/infineon,ir38060.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Infineon Buck Regulators with PMBUS interfaces
|
||||
|
||||
maintainers:
|
||||
- Not Me.
|
||||
|
||||
allOf:
|
||||
- $ref: regulator.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- infineon,ir38060
|
||||
- infineon,ir38064
|
||||
- infineon,ir38164
|
||||
- infineon,ir38263
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
regulator@34 {
|
||||
compatible = "infineon,ir38060";
|
||||
reg = <0x34>;
|
||||
|
||||
regulator-min-microvolt = <437500>;
|
||||
regulator-max-microvolt = <1387500>;
|
||||
};
|
||||
};
|
@ -47,6 +47,8 @@ properties:
|
||||
- adi,lt7182s
|
||||
# AMS iAQ-Core VOC Sensor
|
||||
- ams,iaq-core
|
||||
# Temperature monitoring of Astera Labs PT5161L PCIe retimer
|
||||
- asteralabs,pt5161l
|
||||
# i2c serial eeprom (24cxx)
|
||||
- at,24c08
|
||||
# ATSHA204 - i2c h/w symmetric crypto module
|
||||
@ -129,6 +131,8 @@ properties:
|
||||
- mps,mp2975
|
||||
# Monolithic Power Systems Inc. multi-phase hot-swap controller mp5990
|
||||
- mps,mp5990
|
||||
# Monolithic Power Systems Inc. synchronous step-down converter mpq8785
|
||||
- mps,mpq8785
|
||||
# Honeywell Humidicon HIH-6130 humidity/temperature sensor
|
||||
- honeywell,hi6130
|
||||
# IBM Common Form Factor Power Supply Versions (all versions)
|
||||
@ -139,14 +143,6 @@ properties:
|
||||
- ibm,cffps2
|
||||
# Infineon IR36021 digital POL buck controller
|
||||
- infineon,ir36021
|
||||
# Infineon IR38060 Voltage Regulator
|
||||
- infineon,ir38060
|
||||
# Infineon IR38064 Voltage Regulator
|
||||
- infineon,ir38064
|
||||
# Infineon IR38164 Voltage Regulator
|
||||
- infineon,ir38164
|
||||
# Infineon IR38263 Voltage Regulator
|
||||
- infineon,ir38263
|
||||
# Infineon IRPS5401 Voltage Regulator (PMIC)
|
||||
- infineon,irps5401
|
||||
# Infineon TLV493D-A1B6 I2C 3D Magnetic Sensor
|
||||
|
@ -109,6 +109,8 @@ patternProperties:
|
||||
description: Amlogic, Inc.
|
||||
"^ampere,.*":
|
||||
description: Ampere Computing LLC
|
||||
"^amphenol,.*":
|
||||
description: Amphenol Advanced Sensors
|
||||
"^ampire,.*":
|
||||
description: Ampire Co., Ltd.
|
||||
"^ams,.*":
|
||||
@ -161,6 +163,8 @@ patternProperties:
|
||||
description: ASPEED Technology Inc.
|
||||
"^asrock,.*":
|
||||
description: ASRock Inc.
|
||||
"^asteralabs,.*":
|
||||
description: Astera Labs, Inc.
|
||||
"^asus,.*":
|
||||
description: AsusTek Computer Inc.
|
||||
"^atheros,.*":
|
||||
|
26
Documentation/hwmon/aspeed-g6-pwm-tach.rst
Normal file
26
Documentation/hwmon/aspeed-g6-pwm-tach.rst
Normal file
@ -0,0 +1,26 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
Kernel driver aspeed-g6-pwm-tach
|
||||
=================================
|
||||
|
||||
Supported chips:
|
||||
ASPEED AST2600
|
||||
|
||||
Authors:
|
||||
<billy_tsai@aspeedtech.com>
|
||||
|
||||
Description:
|
||||
------------
|
||||
This driver implements support for ASPEED AST2600 Fan Tacho controller.
|
||||
The controller supports up to 16 tachometer inputs.
|
||||
|
||||
The driver provides the following sensor accesses in sysfs:
|
||||
|
||||
=============== ======= ======================================================
|
||||
fanX_input ro provide current fan rotation value in RPM as reported
|
||||
by the fan to the device.
|
||||
fanX_div rw Fan divisor: Supported value are power of 4 (1, 4, 16
|
||||
64, ... 4194304)
|
||||
The larger divisor, the less rpm accuracy and the less
|
||||
affected by fan signal glitch.
|
||||
=============== ======= ======================================================
|
47
Documentation/hwmon/asus_rog_ryujin.rst
Normal file
47
Documentation/hwmon/asus_rog_ryujin.rst
Normal file
@ -0,0 +1,47 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
Kernel driver asus_rog_ryujin
|
||||
=============================
|
||||
|
||||
Supported devices:
|
||||
|
||||
* ASUS ROG RYUJIN II 360
|
||||
|
||||
Author: Aleksa Savic
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
This driver enables hardware monitoring support for the listed ASUS ROG RYUJIN
|
||||
all-in-one CPU liquid coolers. Available sensors are pump, internal and external
|
||||
(controller) fan speed in RPM, their duties in PWM, as well as coolant temperature.
|
||||
|
||||
Attaching external fans to the controller is optional and allows them to be
|
||||
controlled from the device. If not connected, the fan-related sensors will
|
||||
report zeroes. The controller is a separate hardware unit that comes bundled
|
||||
with the AIO and connects to it to allow fan control.
|
||||
|
||||
The addressable LCD screen is not supported in this driver and should
|
||||
be controlled through userspace tools.
|
||||
|
||||
Usage notes
|
||||
-----------
|
||||
|
||||
As these are USB HIDs, the driver can be loaded automatically by the kernel and
|
||||
supports hot swapping.
|
||||
|
||||
Sysfs entries
|
||||
-------------
|
||||
|
||||
=========== =============================================
|
||||
fan1_input Pump speed (in rpm)
|
||||
fan2_input Internal fan speed (in rpm)
|
||||
fan3_input External (controller) fan 1 speed (in rpm)
|
||||
fan4_input External (controller) fan 2 speed (in rpm)
|
||||
fan5_input External (controller) fan 3 speed (in rpm)
|
||||
fan6_input External (controller) fan 4 speed (in rpm)
|
||||
temp1_input Coolant temperature (in millidegrees Celsius)
|
||||
pwm1 Pump duty
|
||||
pwm2 Internal fan duty
|
||||
pwm3 External (controller) fan duty
|
||||
=========== =============================================
|
73
Documentation/hwmon/chipcap2.rst
Normal file
73
Documentation/hwmon/chipcap2.rst
Normal file
@ -0,0 +1,73 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
Kernel driver ChipCap2
|
||||
======================
|
||||
|
||||
Supported chips:
|
||||
|
||||
* Amphenol CC2D23, CC2D23S, CC2D25, CC2D25S, CC2D33, CC2D33S, CC2D35, CC2D35S
|
||||
|
||||
Prefix: 'chipcap2'
|
||||
|
||||
Addresses scanned: -
|
||||
|
||||
Datasheet: https://www.amphenol-sensors.com/en/telaire/humidity/527-humidity-sensors/3095-chipcap-2
|
||||
|
||||
Author:
|
||||
|
||||
- Javier Carrasco <javier.carrasco.cruz@gmail.com>
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
This driver implements support for the Amphenol ChipCap 2, a humidity and
|
||||
temperature chip family. Temperature is measured in milli degrees celsius,
|
||||
relative humidity is expressed as a per cent mille. The measurement ranges
|
||||
are the following:
|
||||
|
||||
- Relative humidity: 0 to 100000 pcm (14-bit resolution)
|
||||
- Temperature: -40000 to +125000 m°C (14-bit resolution)
|
||||
|
||||
The device communicates with the I2C protocol and uses the I2C address 0x28
|
||||
by default.
|
||||
|
||||
Depending on the hardware configuration, up to two humidity alarms to control
|
||||
minimum and maximum values are provided. Their thresholds and hystersis can be
|
||||
configured via sysfs.
|
||||
|
||||
Thresholds and hysteris must be provided as a per cent mille. These values
|
||||
might be truncated to match the 14-bit device resolution (6.1 pcm/LSB)
|
||||
|
||||
Known Issues
|
||||
------------
|
||||
|
||||
The driver does not support I2C address and command window length modification.
|
||||
|
||||
sysfs-Interface
|
||||
---------------
|
||||
|
||||
The following list includes the sysfs attributes that the driver always provides,
|
||||
their permissions and a short description:
|
||||
|
||||
=============================== ======= ========================================
|
||||
Name Perm Description
|
||||
=============================== ======= ========================================
|
||||
temp1_input: RO temperature input
|
||||
humidity1_input: RO humidity input
|
||||
=============================== ======= ========================================
|
||||
|
||||
The following list includes the sysfs attributes that the driver may provide
|
||||
depending on the hardware configuration:
|
||||
|
||||
=============================== ======= ========================================
|
||||
Name Perm Description
|
||||
=============================== ======= ========================================
|
||||
humidity1_min: RW humidity low limit. Measurements under
|
||||
this limit trigger a humidity low alarm
|
||||
humidity1_max: RW humidity high limit. Measurements above
|
||||
this limit trigger a humidity high alarm
|
||||
humidity1_min_hyst: RW humidity low hystersis
|
||||
humidity1_max_hyst: RW humidity high hystersis
|
||||
humidity1_min_alarm: RO humidity low alarm indicator
|
||||
humidity1_max_alarm: RO humidity high alarm indicator
|
||||
=============================== ======= ========================================
|
@ -6,7 +6,6 @@ Kernel driver emc2305
|
||||
Supported chips:
|
||||
Microchip EMC2305, EMC2303, EMC2302, EMC2301
|
||||
|
||||
Addresses scanned: I2C 0x27, 0x2c, 0x2d, 0x2e, 0x2f, 0x4c, 0x4d
|
||||
Prefixes: 'emc2305'
|
||||
|
||||
Datasheet: Publicly available at the Microchip website :
|
||||
|
@ -44,13 +44,16 @@ Hardware Monitoring Kernel Drivers
|
||||
aquacomputer_d5next
|
||||
asb100
|
||||
asc7621
|
||||
aspeed-g6-pwm-tach
|
||||
aspeed-pwm-tacho
|
||||
asus_ec_sensors
|
||||
asus_rog_ryujin
|
||||
asus_wmi_sensors
|
||||
bcm54140
|
||||
bel-pfe
|
||||
bpa-rs600
|
||||
bt1-pvt
|
||||
chipcap2
|
||||
coretemp
|
||||
corsair-cpro
|
||||
corsair-psu
|
||||
@ -129,6 +132,7 @@ Hardware Monitoring Kernel Drivers
|
||||
ltc4245
|
||||
ltc4260
|
||||
ltc4261
|
||||
ltc4282
|
||||
ltc4286
|
||||
max127
|
||||
max15301
|
||||
@ -163,6 +167,7 @@ Hardware Monitoring Kernel Drivers
|
||||
mp2975
|
||||
mp5023
|
||||
mp5990
|
||||
mpq8785
|
||||
nct6683
|
||||
nct6775
|
||||
nct7802
|
||||
@ -171,6 +176,7 @@ Hardware Monitoring Kernel Drivers
|
||||
nsa320
|
||||
ntc_thermistor
|
||||
nzxt-kraken2
|
||||
nzxt-kraken3
|
||||
nzxt-smart2
|
||||
occ
|
||||
oxp-sensors
|
||||
@ -185,6 +191,7 @@ Hardware Monitoring Kernel Drivers
|
||||
pmbus
|
||||
powerz
|
||||
powr1220
|
||||
pt5161l
|
||||
pxe1610
|
||||
pwm-fan
|
||||
q54sj108a2
|
||||
@ -208,6 +215,7 @@ Hardware Monitoring Kernel Drivers
|
||||
smsc47m1
|
||||
sparx5-temp
|
||||
stpddc60
|
||||
surface_fan
|
||||
sy7636a-hwmon
|
||||
tc654
|
||||
tc74
|
||||
|
133
Documentation/hwmon/ltc4282.rst
Normal file
133
Documentation/hwmon/ltc4282.rst
Normal file
@ -0,0 +1,133 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
Kernel drivers ltc4282
|
||||
==========================================
|
||||
|
||||
Supported chips:
|
||||
|
||||
* Analog Devices LTC4282
|
||||
|
||||
Prefix: 'ltc4282'
|
||||
|
||||
Addresses scanned: - I2C 0x40 - 0x5A (7-bit)
|
||||
Addresses scanned: - I2C 0x80 - 0xB4 with a step of 2 (8-bit)
|
||||
|
||||
Datasheet:
|
||||
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/ltc4282.pdf
|
||||
|
||||
Author: Nuno Sá <nuno.sa@analog.com>
|
||||
|
||||
Description
|
||||
___________
|
||||
|
||||
The LTC4282 hot swap controller allows a board to be safely inserted and removed
|
||||
from a live backplane. Using one or more external N-channel pass transistors,
|
||||
board supply voltage and inrush current are ramped up at an adjustable rate. An
|
||||
I2C interface and onboard ADC allows for monitoring of board current, voltage,
|
||||
power, energy and fault status. The device features analog foldback current
|
||||
limiting and supply monitoring for applications from 2.9V to 33V. Dual 12V gate
|
||||
drive allows high power applications to either share safe operating area across
|
||||
parallel MOSFETs or support a 2-stage start-up that first charges the load
|
||||
capacitance followed by enabling a low on-resistance path to the load. The
|
||||
LTC4282 is well suited to high power applications because the precise monitoring
|
||||
capability and accurate current limiting reduce the extremes in which both loads
|
||||
and power supplies must safely operate. Non-volatile configuration allows for
|
||||
flexibility in the autonomous generation of alerts and response to faults.
|
||||
|
||||
Sysfs entries
|
||||
_____________
|
||||
|
||||
The following attributes are supported. Limits are read-write and all the other
|
||||
attributes are read-only. Note that in0 and in1 are mutually exclusive. Enabling
|
||||
one disables the other and disabling one enables the other.
|
||||
|
||||
======================= ==========================================
|
||||
in0_input Output voltage (mV).
|
||||
in0_min Undervoltage threshold
|
||||
in0_max Overvoltage threshold
|
||||
in0_lowest Lowest measured voltage
|
||||
in0_highest Highest measured voltage
|
||||
in0_reset_history Write 1 to reset in0 history.
|
||||
Also clears fet bad and short fault logs.
|
||||
in0_min_alarm Undervoltage alarm
|
||||
in0_max_alarm Overvoltage alarm
|
||||
in0_enable Enable/Disable VSOURCE monitoring
|
||||
in0_fault Failure in the MOSFETs. Either bad or shorted FET.
|
||||
in0_label Channel label (VSOURCE)
|
||||
|
||||
in1_input Input voltage (mV).
|
||||
in1_min Undervoltage threshold
|
||||
in1_max Overvoltage threshold
|
||||
in1_lowest Lowest measured voltage
|
||||
in1_highest Highest measured voltage
|
||||
in1_reset_history Write 1 to reset in1 history.
|
||||
Also clears over/undervoltage fault logs.
|
||||
in1_min_alarm Undervoltage alarm
|
||||
in1_max_alarm Overvoltage alarm
|
||||
in1_lcrit_alarm Critical Undervoltage alarm
|
||||
in1_crit_alarm Critical Overvoltage alarm
|
||||
in1_enable Enable/Disable VDD monitoring
|
||||
in1_label Channel label (VDD)
|
||||
|
||||
in2_input GPIO voltage (mV)
|
||||
in2_min Undervoltage threshold
|
||||
in2_max Overvoltage threshold
|
||||
in2_lowest Lowest measured voltage
|
||||
in2_highest Highest measured voltage
|
||||
in2_reset_history Write 1 to reset in2 history
|
||||
in2_min_alarm Undervoltage alarm
|
||||
in2_max_alarm Overvoltage alarm
|
||||
in2_label Channel label (VGPIO)
|
||||
|
||||
curr1_input Sense current (mA)
|
||||
curr1_min Undercurrent threshold
|
||||
curr1_max Overcurrent threshold
|
||||
curr1_lowest Lowest measured current
|
||||
curr1_highest Highest measured current
|
||||
curr1_reset_history Write 1 to reset curr1 history.
|
||||
Also clears overcurrent fault logs.
|
||||
curr1_min_alarm Undercurrent alarm
|
||||
curr1_max_alarm Overcurrent alarm
|
||||
curr1_crit_alarm Critical Overcurrent alarm
|
||||
curr1_label Channel label (ISENSE)
|
||||
|
||||
power1_input Power (in uW)
|
||||
power1_min Low power threshold
|
||||
power1_max High power threshold
|
||||
power1_input_lowest Historical minimum power use
|
||||
power1_input_highest Historical maximum power use
|
||||
power1_reset_history Write 1 to reset power1 history.
|
||||
Also clears power bad fault logs.
|
||||
power1_min_alarm Low power alarm
|
||||
power1_max_alarm High power alarm
|
||||
power1_label Channel label (Power)
|
||||
|
||||
energy1_input Measured energy over time (in microJoule)
|
||||
energy1_enable Enable/Disable Energy accumulation
|
||||
======================= ==========================================
|
||||
|
||||
DebugFs entries
|
||||
_______________
|
||||
|
||||
The chip also has a fault log register where failures can be logged. Hence,
|
||||
as these are logging events, we give access to them in debugfs. Note that
|
||||
even if some failure is detected in these logs, it does necessarily mean
|
||||
that the failure is still present. As mentioned in the proper Sysfs entries,
|
||||
these logs can be cleared by writing in the proper reset_history attribute.
|
||||
|
||||
.. warning:: The debugfs interface is subject to change without notice
|
||||
and is only available when the kernel is compiled with
|
||||
``CONFIG_DEBUG_FS`` defined.
|
||||
|
||||
``/sys/kernel/debug/ltc4282-hwmon[X]/``
|
||||
contains the following attributes:
|
||||
|
||||
======================= ==========================================
|
||||
power1_bad_fault_log Set to 1 by a power1 bad fault occurring.
|
||||
in0_fet_short_fault_log Set to 1 when the ADC detects a FET-short fault.
|
||||
in0_fet_bad_fault_log Set to 1 when a FET-BAD fault occurs.
|
||||
in1_crit_fault_log Set to 1 by a VDD overvoltage fault occurring.
|
||||
in1_lcrit_fault_log Set to 1 by a VDD undervoltage fault occurring.
|
||||
curr1_crit_fault_log Set to 1 by an overcurrent fault occurring.
|
||||
======================= ==========================================
|
@ -11,7 +11,7 @@ Supported chips:
|
||||
|
||||
Addresses scanned: none
|
||||
|
||||
Datasheet: http://pdfserv.maxim-ic.com/en/ds/MAX6620.pdf
|
||||
Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max6620.pdf
|
||||
|
||||
Authors:
|
||||
- L\. Grunenberg <contact@lgrunenberg.de>
|
||||
|
94
Documentation/hwmon/mpq8785.rst
Normal file
94
Documentation/hwmon/mpq8785.rst
Normal file
@ -0,0 +1,94 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
Kernel driver mpq8785
|
||||
=======================
|
||||
|
||||
Supported chips:
|
||||
|
||||
* MPS MPQ8785
|
||||
|
||||
Prefix: 'mpq8785'
|
||||
|
||||
Author: Charles Hsu <ythsu0511@gmail.com>
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The MPQ8785 is a fully integrated, PMBus-compatible, high-frequency, synchronous
|
||||
buck converter. The MPQ8785 offers a very compact solution that achieves up to
|
||||
40A output current per phase, with excellent load and line regulation over a
|
||||
wide input supply range. The MPQ8785 operates at high efficiency over a wide
|
||||
output current load range.
|
||||
|
||||
The PMBus interface provides converter configurations and key parameters
|
||||
monitoring.
|
||||
|
||||
The MPQ8785 adopts MPS's proprietary multi-phase digital constant-on-time (MCOT)
|
||||
control, which provides fast transient response and eases loop stabilization.
|
||||
The MCOT scheme also allows multiple MPQ8785 devices to be connected in parallel
|
||||
with excellent current sharing and phase interleaving for high-current
|
||||
applications.
|
||||
|
||||
Fully integrated protection features include over-current protection (OCP),
|
||||
over-voltage protection (OVP), under-voltage protection (UVP), and
|
||||
over-temperature protection (OTP).
|
||||
|
||||
The MPQ8785 requires a minimal number of readily available, standard external
|
||||
components, and is available in a TLGA (5mmx6mm) package.
|
||||
|
||||
Device compliant with:
|
||||
|
||||
- PMBus rev 1.3 interface.
|
||||
|
||||
The driver exports the following attributes via the 'sysfs' files
|
||||
for input voltage:
|
||||
|
||||
**in1_input**
|
||||
|
||||
**in1_label**
|
||||
|
||||
**in1_max**
|
||||
|
||||
**in1_max_alarm**
|
||||
|
||||
**in1_min**
|
||||
|
||||
**in1_min_alarm**
|
||||
|
||||
**in1_crit**
|
||||
|
||||
**in1_crit_alarm**
|
||||
|
||||
The driver provides the following attributes for output voltage:
|
||||
|
||||
**in2_input**
|
||||
|
||||
**in2_label**
|
||||
|
||||
**in2_alarm**
|
||||
|
||||
The driver provides the following attributes for output current:
|
||||
|
||||
**curr1_input**
|
||||
|
||||
**curr1_label**
|
||||
|
||||
**curr1_max**
|
||||
|
||||
**curr1_max_alarm**
|
||||
|
||||
**curr1_crit**
|
||||
|
||||
**curr1_crit_alarm**
|
||||
|
||||
The driver provides the following attributes for temperature:
|
||||
|
||||
**temp1_input**
|
||||
|
||||
**temp1_max**
|
||||
|
||||
**temp1_max_alarm**
|
||||
|
||||
**temp1_crit**
|
||||
|
||||
**temp1_crit_alarm**
|
@ -64,4 +64,5 @@ Intel DB85FL NCT6683D EC firmware version 1.0 build 04/03/13
|
||||
ASRock X570 NCT6683D EC firmware version 1.0 build 06/28/19
|
||||
ASRock X670E NCT6686D EC firmware version 1.0 build 05/19/22
|
||||
MSI B550 NCT6687D EC firmware version 1.0 build 05/07/20
|
||||
MSI X670-P NCT6687D EC firmware version 0.0 build 09/27/22
|
||||
=============== ===============================================
|
||||
|
74
Documentation/hwmon/nzxt-kraken3.rst
Normal file
74
Documentation/hwmon/nzxt-kraken3.rst
Normal file
@ -0,0 +1,74 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
Kernel driver nzxt-kraken3
|
||||
==========================
|
||||
|
||||
Supported devices:
|
||||
|
||||
* NZXT Kraken X53
|
||||
* NZXT Kraken X63
|
||||
* NZXT Kraken X73
|
||||
* NZXT Kraken Z53
|
||||
* NZXT Kraken Z63
|
||||
* NZXT Kraken Z73
|
||||
|
||||
Author: Jonas Malaco, Aleksa Savic
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
This driver enables hardware monitoring support for NZXT Kraken X53/X63/X73 and
|
||||
Z53/Z63/Z73 all-in-one CPU liquid coolers. All models expose liquid temperature
|
||||
and pump speed (in RPM), as well as PWM control (either as a fixed value
|
||||
or through a temp-PWM curve). The Z-series models additionally expose the speed
|
||||
and duty of an optionally connected fan, with the same PWM control capabilities.
|
||||
|
||||
Pump and fan duty control mode can be set through pwm[1-2]_enable, where 1 is
|
||||
for the manual control mode and 2 is for the liquid temp to PWM curve mode.
|
||||
Writing a 0 disables control of the channel through the driver after setting its
|
||||
duty to 100%.
|
||||
|
||||
The temperature of the curves relates to the fixed [20-59] range, correlating to
|
||||
the detected liquid temperature. Only PWM values (ranging from 0-255) can be set.
|
||||
If in curve mode, setting point values should be done in moderation - the devices
|
||||
require complete curves to be sent for each change; they can lock up or discard
|
||||
the changes if they are too numerous at once. Suggestion is to set them while
|
||||
in an another mode, and then apply them by switching to curve.
|
||||
|
||||
The devices can report if they are faulty. The driver supports that situation
|
||||
and will issue a warning. This can also happen when the USB cable is connected,
|
||||
but SATA power is not.
|
||||
|
||||
The addressable RGB LEDs and LCD screen (only on Z-series models) are not
|
||||
supported in this driver, but can be controlled through existing userspace tools,
|
||||
such as `liquidctl`_.
|
||||
|
||||
.. _liquidctl: https://github.com/liquidctl/liquidctl
|
||||
|
||||
Usage Notes
|
||||
-----------
|
||||
|
||||
As these are USB HIDs, the driver can be loaded automatically by the kernel and
|
||||
supports hot swapping.
|
||||
|
||||
Possible pwm_enable values are:
|
||||
|
||||
====== ==========================================================================
|
||||
0 Set fan to 100%
|
||||
1 Direct PWM mode (applies value in corresponding PWM entry)
|
||||
2 Curve control mode (applies the temp-PWM duty curve based on coolant temp)
|
||||
====== ==========================================================================
|
||||
|
||||
Sysfs entries
|
||||
-------------
|
||||
|
||||
============================== ================================================================
|
||||
fan1_input Pump speed (in rpm)
|
||||
fan2_input Fan speed (in rpm)
|
||||
temp1_input Coolant temperature (in millidegrees Celsius)
|
||||
pwm1 Pump duty (value between 0-255)
|
||||
pwm1_enable Pump duty control mode (0: disabled, 1: manual, 2: curve)
|
||||
pwm2 Fan duty (value between 0-255)
|
||||
pwm2_enable Fan duty control mode (0: disabled, 1: manual, 2: curve)
|
||||
temp[1-2]_auto_point[1-40]_pwm Temp-PWM duty curves (for pump and fan), related to coolant temp
|
||||
============================== ================================================================
|
@ -33,6 +33,7 @@ Currently the driver supports the following handhelds:
|
||||
- AOK ZOE A1 PRO
|
||||
- Aya Neo 2
|
||||
- Aya Neo AIR
|
||||
- Aya Neo AIR Plus (Mendocino)
|
||||
- Aya Neo AIR Pro
|
||||
- Aya Neo Geek
|
||||
- OneXPlayer AMD
|
||||
|
42
Documentation/hwmon/pt5161l.rst
Normal file
42
Documentation/hwmon/pt5161l.rst
Normal file
@ -0,0 +1,42 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
Kernel driver pt5161l
|
||||
=====================
|
||||
|
||||
Supported chips:
|
||||
|
||||
* Astera Labs PT5161L
|
||||
|
||||
Prefix: 'pt5161l'
|
||||
|
||||
Addresses scanned: I2C 0x20 - 0x27
|
||||
|
||||
Datasheet: Not publicly available.
|
||||
|
||||
Authors: Cosmo Chou <cosmo.chou@quantatw.com>
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
This driver implements support for temperature monitoring of Astera Labs
|
||||
PT5161L series PCIe retimer chips.
|
||||
|
||||
This driver implementation originates from the CSDK available at
|
||||
https://github.com/facebook/openbmc/tree/helium/common/recipes-lib/retimer-v2.14
|
||||
The communication protocol utilized is based on the I2C/SMBus standard.
|
||||
|
||||
Sysfs entries
|
||||
----------------
|
||||
|
||||
================ ==============================================
|
||||
temp1_input Measured temperature (in millidegrees Celsius)
|
||||
================ ==============================================
|
||||
|
||||
Debugfs entries
|
||||
----------------
|
||||
|
||||
================ ===============================
|
||||
fw_load_status Firmware load status
|
||||
fw_ver Firmware version of the retimer
|
||||
heartbeat_status Heartbeat status
|
||||
================ ===============================
|
@ -65,6 +65,10 @@ When the temperature and humidity readings move back between the hysteresis
|
||||
values, the alert bit is set to 0 and the alert pin on the sensor is set to
|
||||
low.
|
||||
|
||||
The serial number exposed to debugfs allows for unique identification of the
|
||||
sensors. For sts32, sts33 and sht33, the manufacturer provides calibration
|
||||
certificates through an API.
|
||||
|
||||
sysfs-Interface
|
||||
---------------
|
||||
|
||||
@ -99,3 +103,10 @@ repeatability: write or read repeatability, higher repeatability means
|
||||
- 1: medium repeatability
|
||||
- 2: high repeatability
|
||||
=================== ============================================================
|
||||
|
||||
debugfs-Interface
|
||||
-----------------
|
||||
|
||||
=================== ============================================================
|
||||
serial_number: unique serial number of the sensor in decimal
|
||||
=================== ============================================================
|
||||
|
25
Documentation/hwmon/surface_fan.rst
Normal file
25
Documentation/hwmon/surface_fan.rst
Normal file
@ -0,0 +1,25 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
Kernel driver surface_fan
|
||||
=========================
|
||||
|
||||
Supported Devices:
|
||||
|
||||
* Microsoft Surface Pro 9
|
||||
|
||||
Author: Ivor Wanders <ivor@iwanders.net>
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
This provides monitoring of the fan found in some Microsoft Surface Pro devices,
|
||||
like the Surface Pro 9. The fan is always controlled by the onboard controller.
|
||||
|
||||
Sysfs interface
|
||||
---------------
|
||||
|
||||
======================= ======= =========================================
|
||||
Name Perm Description
|
||||
======================= ======= =========================================
|
||||
``fan1_input`` RO Current fan speed in RPM.
|
||||
======================= ======= =========================================
|
167
MAINTAINERS
167
MAINTAINERS
@ -1104,6 +1104,14 @@ F: Documentation/devicetree/bindings/perf/amlogic,g12-ddr-pmu.yaml
|
||||
F: drivers/perf/amlogic/
|
||||
F: include/soc/amlogic/
|
||||
|
||||
AMPHENOL CHIPCAP 2 HUMIDITY-TEMPERATURE IIO DRIVER
|
||||
M: Javier Carrasco <javier.carrasco.cruz@gmail.com>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/hwmon/amphenol,chipcap2.yaml
|
||||
F: Documentation/hwmon/chipcap2.rst
|
||||
F: drivers/hwmon/chipcap2.c
|
||||
|
||||
AMPHION VPU CODEC V4L2 DRIVER
|
||||
M: Ming Qian <ming.qian@nxp.com>
|
||||
M: Zhou Peng <eagle.zhou@nxp.com>
|
||||
@ -1390,15 +1398,6 @@ F: drivers/iio/amplifiers/hmc425a.c
|
||||
F: drivers/staging/iio/*/ad*
|
||||
X: drivers/iio/*/adjd*
|
||||
|
||||
ANALOG DEVICES INC MAX31760 DRIVER
|
||||
M: Ibrahim Tilki <Ibrahim.Tilki@analog.com>
|
||||
S: Maintained
|
||||
W: http://wiki.analog.com/
|
||||
W: https://ez.analog.com/linux-software-drivers
|
||||
F: Documentation/devicetree/bindings/hwmon/adi,max31760.yaml
|
||||
F: Documentation/hwmon/max31760.rst
|
||||
F: drivers/hwmon/max31760.c
|
||||
|
||||
ANALOGBITS PLL LIBRARIES
|
||||
M: Paul Walmsley <paul.walmsley@sifive.com>
|
||||
M: Samuel Holland <samuel.holland@sifive.com>
|
||||
@ -3190,6 +3189,12 @@ S: Maintained
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86.git
|
||||
F: drivers/platform/x86/asus-tf103c-dock.c
|
||||
|
||||
ASUS ROG RYUJIN AIO HARDWARE MONITOR DRIVER
|
||||
M: Aleksa Savic <savicaleksa83@gmail.com>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/hwmon/asus_rog_ryujin.c
|
||||
|
||||
ASUS WIRELESS RADIO CONTROL DRIVER
|
||||
M: João Paulo Rechi Vita <jprvita@gmail.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
@ -10531,22 +10536,6 @@ L: linux-fbdev@vger.kernel.org
|
||||
S: Orphan
|
||||
F: drivers/video/fbdev/imsttfb.c
|
||||
|
||||
INA209 HARDWARE MONITOR DRIVER
|
||||
M: Guenter Roeck <linux@roeck-us.net>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml
|
||||
F: Documentation/hwmon/ina209.rst
|
||||
F: drivers/hwmon/ina209.c
|
||||
|
||||
INA2XX HARDWARE MONITOR DRIVER
|
||||
M: Guenter Roeck <linux@roeck-us.net>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/hwmon/ina2xx.rst
|
||||
F: drivers/hwmon/ina2xx.c
|
||||
F: include/linux/platform_data/ina2xx.h
|
||||
|
||||
INDEX OF FURTHER KERNEL DOCUMENTATION
|
||||
M: Carlos Bilbao <carlos.bilbao@amd.com>
|
||||
S: Maintained
|
||||
@ -11531,14 +11520,6 @@ S: Maintained
|
||||
F: arch/x86/include/asm/jailhouse_para.h
|
||||
F: arch/x86/kernel/jailhouse.c
|
||||
|
||||
JC42.4 TEMPERATURE SENSOR DRIVER
|
||||
M: Guenter Roeck <linux@roeck-us.net>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/hwmon/jedec,jc42.yaml
|
||||
F: Documentation/hwmon/jc42.rst
|
||||
F: drivers/hwmon/jc42.c
|
||||
|
||||
JFS FILESYSTEM
|
||||
M: Dave Kleikamp <shaggy@kernel.org>
|
||||
L: jfs-discussion@lists.sourceforge.net
|
||||
@ -12603,13 +12584,6 @@ F: Documentation/hwmon/lm90.rst
|
||||
F: drivers/hwmon/lm90.c
|
||||
F: include/dt-bindings/thermal/lm90.h
|
||||
|
||||
LM95234 HARDWARE MONITOR DRIVER
|
||||
M: Guenter Roeck <linux@roeck-us.net>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/hwmon/lm95234.rst
|
||||
F: drivers/hwmon/lm95234.c
|
||||
|
||||
LME2510 MEDIA DRIVER
|
||||
M: Malcolm Priestley <tvboxspy@gmail.com>
|
||||
L: linux-media@vger.kernel.org
|
||||
@ -12813,12 +12787,13 @@ W: https://ez.analog.com/linux-software-drivers
|
||||
F: Documentation/devicetree/bindings/iio/temperature/adi,ltc2983.yaml
|
||||
F: drivers/iio/temperature/ltc2983.c
|
||||
|
||||
LTC4261 HARDWARE MONITOR DRIVER
|
||||
M: Guenter Roeck <linux@roeck-us.net>
|
||||
LTC4282 HARDWARE MONITOR DRIVER
|
||||
M: Nuno Sa <nuno.sa@analog.com>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/hwmon/ltc4261.rst
|
||||
F: drivers/hwmon/ltc4261.c
|
||||
S: Supported
|
||||
F: Documentation/devicetree/bindings/hwmon/adi,ltc4282.yaml
|
||||
F: Documentation/hwmon/ltc4282.rst
|
||||
F: drivers/hwmon/ltc4282.c
|
||||
|
||||
LTC4286 HARDWARE MONITOR DRIVER
|
||||
M: Delphine CC Chiu <Delphine_CC_Chiu@Wiwynn.com>
|
||||
@ -13176,13 +13151,6 @@ S: Maintained
|
||||
F: Documentation/hwmon/max15301.rst
|
||||
F: drivers/hwmon/pmbus/max15301.c
|
||||
|
||||
MAX16065 HARDWARE MONITOR DRIVER
|
||||
M: Guenter Roeck <linux@roeck-us.net>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/hwmon/max16065.rst
|
||||
F: drivers/hwmon/max16065.c
|
||||
|
||||
MAX2175 SDR TUNER DRIVER
|
||||
M: Ramesh Shanmugasundaram <rashanmu@gmail.com>
|
||||
L: linux-media@vger.kernel.org
|
||||
@ -13193,15 +13161,6 @@ F: Documentation/userspace-api/media/drivers/max2175.rst
|
||||
F: drivers/media/i2c/max2175*
|
||||
F: include/uapi/linux/max2175.h
|
||||
|
||||
MAX31827 TEMPERATURE SWITCH DRIVER
|
||||
M: Daniel Matyas <daniel.matyas@analog.com>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
S: Supported
|
||||
W: https://ez.analog.com/linux-software-drivers
|
||||
F: Documentation/devicetree/bindings/hwmon/adi,max31827.yaml
|
||||
F: Documentation/hwmon/max31827.rst
|
||||
F: drivers/hwmon/max31827.c
|
||||
|
||||
MAX31335 RTC DRIVER
|
||||
M: Antoniu Miclaus <antoniu.miclaus@analog.com>
|
||||
L: linux-rtc@vger.kernel.org
|
||||
@ -13216,15 +13175,6 @@ S: Orphan
|
||||
F: Documentation/hwmon/max6650.rst
|
||||
F: drivers/hwmon/max6650.c
|
||||
|
||||
MAX6697 HARDWARE MONITOR DRIVER
|
||||
M: Guenter Roeck <linux@roeck-us.net>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/hwmon/max6697.txt
|
||||
F: Documentation/hwmon/max6697.rst
|
||||
F: drivers/hwmon/max6697.c
|
||||
F: include/linux/platform_data/max6697.h
|
||||
|
||||
MAX9286 QUAD GMSL DESERIALIZER DRIVER
|
||||
M: Jacopo Mondi <jacopo+renesas@jmondi.org>
|
||||
M: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
|
||||
@ -14630,6 +14580,14 @@ F: Documentation/driver-api/surface_aggregator/clients/dtx.rst
|
||||
F: drivers/platform/surface/surface_dtx.c
|
||||
F: include/uapi/linux/surface_aggregator/dtx.h
|
||||
|
||||
MICROSOFT SURFACE SENSOR FAN DRIVER
|
||||
M: Maximilian Luz <luzmaximilian@gmail.com>
|
||||
M: Ivor Wanders <ivor@iwanders.net>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/hwmon/surface_fan.rst
|
||||
F: drivers/hwmon/surface_fan.c
|
||||
|
||||
MICROSOFT SURFACE GPE LID SUPPORT DRIVER
|
||||
M: Maximilian Luz <luzmaximilian@gmail.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
@ -15134,15 +15092,6 @@ M: Samuel Mendoza-Jonas <sam@mendozajonas.com>
|
||||
S: Maintained
|
||||
F: net/ncsi/
|
||||
|
||||
NCT6775 HARDWARE MONITOR DRIVER - CORE & PLATFORM DRIVER
|
||||
M: Guenter Roeck <linux@roeck-us.net>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/hwmon/nct6775.rst
|
||||
F: drivers/hwmon/nct6775-core.c
|
||||
F: drivers/hwmon/nct6775-platform.c
|
||||
F: drivers/hwmon/nct6775.h
|
||||
|
||||
NCT6775 HARDWARE MONITOR DRIVER - I2C DRIVER
|
||||
M: Zev Weiss <zev@bewilderbeest.net>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
@ -15898,6 +15847,14 @@ S: Maintained
|
||||
F: Documentation/hwmon/nzxt-kraken2.rst
|
||||
F: drivers/hwmon/nzxt-kraken2.c
|
||||
|
||||
NZXT-KRAKEN3 HARDWARE MONITORING DRIVER
|
||||
M: Jonas Malaco <jonas@protocubo.io>
|
||||
M: Aleksa Savic <savicaleksa83@gmail.com>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/hwmon/nzxt-kraken3.rst
|
||||
F: drivers/hwmon/nzxt-kraken3.c
|
||||
|
||||
NZXT-SMART2 HARDWARE MONITORING DRIVER
|
||||
M: Aleksandr Mezin <mezin.alexander@gmail.com>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
@ -17490,35 +17447,6 @@ S: Maintained
|
||||
F: Documentation/hwmon/pm6764tr.rst
|
||||
F: drivers/hwmon/pmbus/pm6764tr.c
|
||||
|
||||
PMBUS HARDWARE MONITORING DRIVERS
|
||||
M: Guenter Roeck <linux@roeck-us.net>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
S: Maintained
|
||||
W: http://hwmon.wiki.kernel.org/
|
||||
W: http://www.roeck-us.net/linux/drivers/
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git
|
||||
F: Documentation/devicetree/bindings/hwmon/ltc2978.txt
|
||||
F: Documentation/devicetree/bindings/hwmon/max31785.txt
|
||||
F: Documentation/hwmon/adm1275.rst
|
||||
F: Documentation/hwmon/ibm-cffps.rst
|
||||
F: Documentation/hwmon/ir35221.rst
|
||||
F: Documentation/hwmon/lm25066.rst
|
||||
F: Documentation/hwmon/ltc2978.rst
|
||||
F: Documentation/hwmon/ltc3815.rst
|
||||
F: Documentation/hwmon/max16064.rst
|
||||
F: Documentation/hwmon/max20751.rst
|
||||
F: Documentation/hwmon/max31785.rst
|
||||
F: Documentation/hwmon/max34440.rst
|
||||
F: Documentation/hwmon/max8688.rst
|
||||
F: Documentation/hwmon/pmbus-core.rst
|
||||
F: Documentation/hwmon/pmbus.rst
|
||||
F: Documentation/hwmon/tps40422.rst
|
||||
F: Documentation/hwmon/ucd9000.rst
|
||||
F: Documentation/hwmon/ucd9200.rst
|
||||
F: Documentation/hwmon/zl6100.rst
|
||||
F: drivers/hwmon/pmbus/
|
||||
F: include/linux/pmbus.h
|
||||
|
||||
PMC SIERRA MaxRAID DRIVER
|
||||
L: linux-scsi@vger.kernel.org
|
||||
S: Orphan
|
||||
@ -17740,6 +17668,13 @@ F: fs/pstore/
|
||||
F: include/linux/pstore*
|
||||
K: \b(pstore|ramoops)
|
||||
|
||||
PT5161L HARDWARE MONITOR DRIVER
|
||||
M: Cosmo Chou <cosmo.chou@quantatw.com>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/hwmon/pt5161l.rst
|
||||
F: drivers/hwmon/pt5161l.c
|
||||
|
||||
PTP HARDWARE CLOCK SUPPORT
|
||||
M: Richard Cochran <richardcochran@gmail.com>
|
||||
L: netdev@vger.kernel.org
|
||||
@ -22227,22 +22162,6 @@ F: drivers/mmc/host/renesas_sdhi*
|
||||
F: drivers/mmc/host/tmio_mmc*
|
||||
F: include/linux/mfd/tmio.h
|
||||
|
||||
TMP401 HARDWARE MONITOR DRIVER
|
||||
M: Guenter Roeck <linux@roeck-us.net>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/hwmon/ti,tmp401.yaml
|
||||
F: Documentation/hwmon/tmp401.rst
|
||||
F: drivers/hwmon/tmp401.c
|
||||
|
||||
TMP464 HARDWARE MONITOR DRIVER
|
||||
M: Guenter Roeck <linux@roeck-us.net>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/hwmon/ti,tmp464.yaml
|
||||
F: Documentation/hwmon/tmp464.rst
|
||||
F: drivers/hwmon/tmp464.c
|
||||
|
||||
TMP513 HARDWARE MONITOR DRIVER
|
||||
M: Eric Tremblay <etremblay@distech-controls.com>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
|
@ -301,6 +301,16 @@ config SENSORS_ASC7621
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called asc7621.
|
||||
|
||||
config SENSORS_ASUS_ROG_RYUJIN
|
||||
tristate "ASUS ROG RYUJIN II 360 hardware monitoring driver"
|
||||
depends on HID
|
||||
help
|
||||
If you say yes here you get support for the fans and sensors of
|
||||
the ASUS ROG RYUJIN II 360 AIO CPU liquid cooler.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called asus_rog_ryujin.
|
||||
|
||||
config SENSORS_AXI_FAN_CONTROL
|
||||
tristate "Analog Devices FAN Control HDL Core driver"
|
||||
help
|
||||
@ -412,6 +422,17 @@ config SENSORS_ASPEED
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called aspeed_pwm_tacho.
|
||||
|
||||
config SENSORS_ASPEED_G6
|
||||
tristate "ASPEED g6 PWM and Fan tach driver"
|
||||
depends on ARCH_ASPEED || COMPILE_TEST
|
||||
depends on PWM
|
||||
help
|
||||
This driver provides support for ASPEED G6 PWM and Fan Tach
|
||||
controllers.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called aspeed_pwm_tacho.
|
||||
|
||||
config SENSORS_ATXP1
|
||||
tristate "Attansic ATXP1 VID controller"
|
||||
depends on I2C
|
||||
@ -452,6 +473,16 @@ config SENSORS_BT1_PVT_ALARMS
|
||||
the data conversion will be periodically performed and the data will be
|
||||
saved in the internal driver cache.
|
||||
|
||||
config SENSORS_CHIPCAP2
|
||||
tristate "Amphenol ChipCap 2 relative humidity and temperature sensor"
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here to build support for the Amphenol ChipCap 2
|
||||
relative humidity and temperature sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called chipcap2.
|
||||
|
||||
config SENSORS_CORSAIR_CPRO
|
||||
tristate "Corsair Commander Pro controller"
|
||||
depends on HID
|
||||
@ -1038,6 +1069,17 @@ config SENSORS_LTC4261
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called ltc4261.
|
||||
|
||||
config SENSORS_LTC4282
|
||||
tristate "Analog Devices LTC4282"
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
help
|
||||
If you say yes here you get support for Analog Devices LTC4282
|
||||
High Current Hot Swap Controller I2C interface.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called ltc4282.
|
||||
|
||||
config SENSORS_LTQ_CPUTEMP
|
||||
bool "Lantiq cpu temperature sensor driver"
|
||||
depends on SOC_XWAY
|
||||
@ -1674,6 +1716,16 @@ config SENSORS_NZXT_KRAKEN2
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called nzxt-kraken2.
|
||||
|
||||
config SENSORS_NZXT_KRAKEN3
|
||||
tristate "NZXT Kraken X53/X63/X73, Z53/Z63/Z73 coolers"
|
||||
depends on USB_HID
|
||||
help
|
||||
If you say yes here you get support for hardware monitoring for the
|
||||
NZXT Kraken X53/X63/X73, Z53/Z63/Z73 all-in-one CPU liquid coolers.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called nzxt-kraken3.
|
||||
|
||||
config SENSORS_NZXT_SMART2
|
||||
tristate "NZXT RGB & Fan Controller/Smart Device v2"
|
||||
depends on USB_HID
|
||||
@ -1714,6 +1766,16 @@ source "drivers/hwmon/peci/Kconfig"
|
||||
|
||||
source "drivers/hwmon/pmbus/Kconfig"
|
||||
|
||||
config SENSORS_PT5161L
|
||||
tristate "Astera Labs PT5161L PCIe retimer hardware monitoring"
|
||||
depends on I2C
|
||||
help
|
||||
If you say yes here you get support for temperature monitoring
|
||||
on the Astera Labs PT5161L PCIe retimer.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called pt5161l.
|
||||
|
||||
config SENSORS_PWM_FAN
|
||||
tristate "PWM fan"
|
||||
depends on (PWM && OF) || COMPILE_TEST
|
||||
@ -1994,6 +2056,20 @@ config SENSORS_SFCTEMP
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called sfctemp.
|
||||
|
||||
config SENSORS_SURFACE_FAN
|
||||
tristate "Surface Fan Driver"
|
||||
depends on SURFACE_AGGREGATOR
|
||||
depends on SURFACE_AGGREGATOR_BUS
|
||||
help
|
||||
Driver that provides monitoring of the fan on Surface Pro devices that
|
||||
have a fan, like the Surface Pro 9.
|
||||
|
||||
This makes the fan's current speed accessible through the hwmon
|
||||
system. It does not provide control over the fan, the firmware is
|
||||
responsible for that, this driver merely provides monitoring.
|
||||
|
||||
Select M or Y here, if you want to be able to read the fan's speed.
|
||||
|
||||
config SENSORS_ADC128D818
|
||||
tristate "Texas Instruments ADC128D818"
|
||||
depends on I2C
|
||||
|
@ -55,9 +55,12 @@ obj-$(CONFIG_SENSORS_ARM_SCPI) += scpi-hwmon.o
|
||||
obj-$(CONFIG_SENSORS_AS370) += as370-hwmon.o
|
||||
obj-$(CONFIG_SENSORS_ASC7621) += asc7621.o
|
||||
obj-$(CONFIG_SENSORS_ASPEED) += aspeed-pwm-tacho.o
|
||||
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_BT1_PVT) += bt1-pvt.o
|
||||
obj-$(CONFIG_SENSORS_CHIPCAP2) += chipcap2.o
|
||||
obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o
|
||||
obj-$(CONFIG_SENSORS_CORSAIR_CPRO) += corsair-cpro.o
|
||||
obj-$(CONFIG_SENSORS_CORSAIR_PSU) += corsair-psu.o
|
||||
@ -136,6 +139,7 @@ obj-$(CONFIG_SENSORS_LTC4222) += ltc4222.o
|
||||
obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o
|
||||
obj-$(CONFIG_SENSORS_LTC4260) += ltc4260.o
|
||||
obj-$(CONFIG_SENSORS_LTC4261) += ltc4261.o
|
||||
obj-$(CONFIG_SENSORS_LTC4282) += ltc4282.o
|
||||
obj-$(CONFIG_SENSORS_LTQ_CPUTEMP) += ltq-cputemp.o
|
||||
obj-$(CONFIG_SENSORS_MAX1111) += max1111.o
|
||||
obj-$(CONFIG_SENSORS_MAX127) += max127.o
|
||||
@ -173,6 +177,7 @@ obj-$(CONFIG_SENSORS_NPCM7XX) += npcm750-pwm-fan.o
|
||||
obj-$(CONFIG_SENSORS_NSA320) += nsa320-hwmon.o
|
||||
obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o
|
||||
obj-$(CONFIG_SENSORS_NZXT_KRAKEN2) += nzxt-kraken2.o
|
||||
obj-$(CONFIG_SENSORS_NZXT_KRAKEN3) += nzxt-kraken3.o
|
||||
obj-$(CONFIG_SENSORS_NZXT_SMART2) += nzxt-smart2.o
|
||||
obj-$(CONFIG_SENSORS_OXP) += oxp-sensors.o
|
||||
obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
|
||||
@ -180,6 +185,7 @@ obj-$(CONFIG_SENSORS_PC87427) += pc87427.o
|
||||
obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
|
||||
obj-$(CONFIG_SENSORS_POWERZ) += powerz.o
|
||||
obj-$(CONFIG_SENSORS_POWR1220) += powr1220.o
|
||||
obj-$(CONFIG_SENSORS_PT5161L) += pt5161l.o
|
||||
obj-$(CONFIG_SENSORS_PWM_FAN) += pwm-fan.o
|
||||
obj-$(CONFIG_SENSORS_RASPBERRYPI_HWMON) += raspberrypi-hwmon.o
|
||||
obj-$(CONFIG_SENSORS_SBTSI) += sbtsi_temp.o
|
||||
@ -201,6 +207,7 @@ obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o
|
||||
obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o
|
||||
obj-$(CONFIG_SENSORS_SPARX5) += sparx5-temp.o
|
||||
obj-$(CONFIG_SENSORS_STTS751) += stts751.o
|
||||
obj-$(CONFIG_SENSORS_SURFACE_FAN)+= surface_fan.o
|
||||
obj-$(CONFIG_SENSORS_SY7636A) += sy7636a-hwmon.o
|
||||
obj-$(CONFIG_SENSORS_AMC6821) += amc6821.o
|
||||
obj-$(CONFIG_SENSORS_TC74) += tc74.o
|
||||
|
@ -250,7 +250,6 @@ static const struct of_device_id adm1177_dt_ids[] = {
|
||||
MODULE_DEVICE_TABLE(of, adm1177_dt_ids);
|
||||
|
||||
static struct i2c_driver adm1177_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "adm1177",
|
||||
.of_match_table = adm1177_dt_ids,
|
||||
|
@ -124,7 +124,7 @@ static int adt7310_reg_write(void *context, unsigned int reg, unsigned int val)
|
||||
static const struct regmap_config adt7310_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 16,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.cache_type = REGCACHE_MAPLE,
|
||||
.volatile_reg = adt7310_regmap_is_volatile,
|
||||
.reg_read = adt7310_reg_read,
|
||||
.reg_write = adt7310_reg_write,
|
||||
|
@ -69,7 +69,7 @@ static const struct regmap_config adt7410_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 16,
|
||||
.max_register = ADT7X10_ID,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.cache_type = REGCACHE_MAPLE,
|
||||
.volatile_reg = adt7410_regmap_is_volatile,
|
||||
.reg_read = adt7410_reg_read,
|
||||
.reg_write = adt7410_reg_write,
|
||||
@ -95,14 +95,12 @@ static const struct i2c_device_id adt7410_ids[] = {
|
||||
MODULE_DEVICE_TABLE(i2c, adt7410_ids);
|
||||
|
||||
static struct i2c_driver adt7410_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "adt7410",
|
||||
.pm = pm_sleep_ptr(&adt7x10_dev_pm_ops),
|
||||
},
|
||||
.probe = adt7410_i2c_probe,
|
||||
.id_table = adt7410_ids,
|
||||
.address_list = I2C_ADDRS(0x48, 0x49, 0x4a, 0x4b),
|
||||
};
|
||||
module_i2c_driver(adt7410_driver);
|
||||
|
||||
|
@ -934,10 +934,21 @@ static const struct i2c_device_id amc6821_id[] = {
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, amc6821_id);
|
||||
|
||||
static const struct of_device_id __maybe_unused amc6821_of_match[] = {
|
||||
{
|
||||
.compatible = "ti,amc6821",
|
||||
.data = (void *)amc6821,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(of, amc6821_of_match);
|
||||
|
||||
static struct i2c_driver amc6821_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "amc6821",
|
||||
.of_match_table = of_match_ptr(amc6821_of_match),
|
||||
},
|
||||
.probe = amc6821_probe,
|
||||
.id_table = amc6821_id,
|
||||
|
549
drivers/hwmon/aspeed-g6-pwm-tach.c
Normal file
549
drivers/hwmon/aspeed-g6-pwm-tach.c
Normal file
@ -0,0 +1,549 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (C) 2021 Aspeed Technology Inc.
|
||||
*
|
||||
* PWM/TACH controller driver for Aspeed ast2600 SoCs.
|
||||
* This drivers doesn't support earlier version of the IP.
|
||||
*
|
||||
* The hardware operates in time quantities of length
|
||||
* Q := (DIV_L + 1) << DIV_H / input-clk
|
||||
* The length of a PWM period is (DUTY_CYCLE_PERIOD + 1) * Q.
|
||||
* The maximal value for DUTY_CYCLE_PERIOD is used here to provide
|
||||
* a fine grained selection for the duty cycle.
|
||||
*
|
||||
* This driver uses DUTY_CYCLE_RISING_POINT = 0, so from the start of a
|
||||
* period the output is active until DUTY_CYCLE_FALLING_POINT * Q. Note
|
||||
* that if DUTY_CYCLE_RISING_POINT = DUTY_CYCLE_FALLING_POINT the output is
|
||||
* always active.
|
||||
*
|
||||
* Register usage:
|
||||
* PIN_ENABLE: When it is unset the pwm controller will emit inactive level to the external.
|
||||
* Use to determine whether the PWM channel is enabled or disabled
|
||||
* CLK_ENABLE: When it is unset the pwm controller will assert the duty counter reset and
|
||||
* emit inactive level to the PIN_ENABLE mux after that the driver can still change the pwm period
|
||||
* and duty and the value will apply when CLK_ENABLE be set again.
|
||||
* Use to determine whether duty_cycle bigger than 0.
|
||||
* PWM_ASPEED_CTRL_INVERSE: When it is toggled the output value will inverse immediately.
|
||||
* PWM_ASPEED_DUTY_CYCLE_FALLING_POINT/PWM_ASPEED_DUTY_CYCLE_RISING_POINT: When these two
|
||||
* values are equal it means the duty cycle = 100%.
|
||||
*
|
||||
* The glitch may generate at:
|
||||
* - Enabled changing when the duty_cycle bigger than 0% and less than 100%.
|
||||
* - Polarity changing when the duty_cycle bigger than 0% and less than 100%.
|
||||
*
|
||||
* Limitations:
|
||||
* - When changing both duty cycle and period, we cannot prevent in
|
||||
* software that the output might produce a period with mixed
|
||||
* settings.
|
||||
* - Disabling the PWM doesn't complete the current period.
|
||||
*
|
||||
* Improvements:
|
||||
* - When only changing one of duty cycle or period, our pwm controller will not
|
||||
* generate the glitch, the configure will change at next cycle of pwm.
|
||||
* This improvement can disable/enable through PWM_ASPEED_CTRL_DUTY_SYNC_DISABLE.
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pwm.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
/* The channel number of Aspeed pwm controller */
|
||||
#define PWM_ASPEED_NR_PWMS 16
|
||||
/* PWM Control Register */
|
||||
#define PWM_ASPEED_CTRL(ch) ((ch) * 0x10 + 0x00)
|
||||
#define PWM_ASPEED_CTRL_LOAD_SEL_RISING_AS_WDT BIT(19)
|
||||
#define PWM_ASPEED_CTRL_DUTY_LOAD_AS_WDT_ENABLE BIT(18)
|
||||
#define PWM_ASPEED_CTRL_DUTY_SYNC_DISABLE BIT(17)
|
||||
#define PWM_ASPEED_CTRL_CLK_ENABLE BIT(16)
|
||||
#define PWM_ASPEED_CTRL_LEVEL_OUTPUT BIT(15)
|
||||
#define PWM_ASPEED_CTRL_INVERSE BIT(14)
|
||||
#define PWM_ASPEED_CTRL_OPEN_DRAIN_ENABLE BIT(13)
|
||||
#define PWM_ASPEED_CTRL_PIN_ENABLE BIT(12)
|
||||
#define PWM_ASPEED_CTRL_CLK_DIV_H GENMASK(11, 8)
|
||||
#define PWM_ASPEED_CTRL_CLK_DIV_L GENMASK(7, 0)
|
||||
|
||||
/* PWM Duty Cycle Register */
|
||||
#define PWM_ASPEED_DUTY_CYCLE(ch) ((ch) * 0x10 + 0x04)
|
||||
#define PWM_ASPEED_DUTY_CYCLE_PERIOD GENMASK(31, 24)
|
||||
#define PWM_ASPEED_DUTY_CYCLE_POINT_AS_WDT GENMASK(23, 16)
|
||||
#define PWM_ASPEED_DUTY_CYCLE_FALLING_POINT GENMASK(15, 8)
|
||||
#define PWM_ASPEED_DUTY_CYCLE_RISING_POINT GENMASK(7, 0)
|
||||
|
||||
/* PWM fixed value */
|
||||
#define PWM_ASPEED_FIXED_PERIOD FIELD_MAX(PWM_ASPEED_DUTY_CYCLE_PERIOD)
|
||||
|
||||
/* The channel number of Aspeed tach controller */
|
||||
#define TACH_ASPEED_NR_TACHS 16
|
||||
/* TACH Control Register */
|
||||
#define TACH_ASPEED_CTRL(ch) (((ch) * 0x10) + 0x08)
|
||||
#define TACH_ASPEED_IER BIT(31)
|
||||
#define TACH_ASPEED_INVERS_LIMIT BIT(30)
|
||||
#define TACH_ASPEED_LOOPBACK BIT(29)
|
||||
#define TACH_ASPEED_ENABLE BIT(28)
|
||||
#define TACH_ASPEED_DEBOUNCE_MASK GENMASK(27, 26)
|
||||
#define TACH_ASPEED_DEBOUNCE_BIT 26
|
||||
#define TACH_ASPEED_IO_EDGE_MASK GENMASK(25, 24)
|
||||
#define TACH_ASPEED_IO_EDGE_BIT 24
|
||||
#define TACH_ASPEED_CLK_DIV_T_MASK GENMASK(23, 20)
|
||||
#define TACH_ASPEED_CLK_DIV_BIT 20
|
||||
#define TACH_ASPEED_THRESHOLD_MASK GENMASK(19, 0)
|
||||
/* [27:26] */
|
||||
#define DEBOUNCE_3_CLK 0x00
|
||||
#define DEBOUNCE_2_CLK 0x01
|
||||
#define DEBOUNCE_1_CLK 0x02
|
||||
#define DEBOUNCE_0_CLK 0x03
|
||||
/* [25:24] */
|
||||
#define F2F_EDGES 0x00
|
||||
#define R2R_EDGES 0x01
|
||||
#define BOTH_EDGES 0x02
|
||||
/* [23:20] */
|
||||
/* divisor = 4 to the nth power, n = register value */
|
||||
#define DEFAULT_TACH_DIV 1024
|
||||
#define DIV_TO_REG(divisor) (ilog2(divisor) >> 1)
|
||||
|
||||
/* TACH Status Register */
|
||||
#define TACH_ASPEED_STS(ch) (((ch) * 0x10) + 0x0C)
|
||||
|
||||
/*PWM_TACH_STS */
|
||||
#define TACH_ASPEED_ISR BIT(31)
|
||||
#define TACH_ASPEED_PWM_OUT BIT(25)
|
||||
#define TACH_ASPEED_PWM_OEN BIT(24)
|
||||
#define TACH_ASPEED_DEB_INPUT BIT(23)
|
||||
#define TACH_ASPEED_RAW_INPUT BIT(22)
|
||||
#define TACH_ASPEED_VALUE_UPDATE BIT(21)
|
||||
#define TACH_ASPEED_FULL_MEASUREMENT BIT(20)
|
||||
#define TACH_ASPEED_VALUE_MASK GENMASK(19, 0)
|
||||
/**********************************************************
|
||||
* Software setting
|
||||
*********************************************************/
|
||||
#define DEFAULT_FAN_PULSE_PR 2
|
||||
|
||||
struct aspeed_pwm_tach_data {
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
struct clk *clk;
|
||||
struct reset_control *reset;
|
||||
unsigned long clk_rate;
|
||||
struct pwm_chip chip;
|
||||
bool tach_present[TACH_ASPEED_NR_TACHS];
|
||||
u32 tach_divisor;
|
||||
};
|
||||
|
||||
static inline struct aspeed_pwm_tach_data *
|
||||
aspeed_pwm_chip_to_data(struct pwm_chip *chip)
|
||||
{
|
||||
return container_of(chip, struct aspeed_pwm_tach_data, chip);
|
||||
}
|
||||
|
||||
static int aspeed_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
struct pwm_state *state)
|
||||
{
|
||||
struct aspeed_pwm_tach_data *priv = aspeed_pwm_chip_to_data(chip);
|
||||
u32 hwpwm = pwm->hwpwm;
|
||||
bool polarity, pin_en, clk_en;
|
||||
u32 duty_pt, val;
|
||||
u64 div_h, div_l, duty_cycle_period, dividend;
|
||||
|
||||
val = readl(priv->base + PWM_ASPEED_CTRL(hwpwm));
|
||||
polarity = FIELD_GET(PWM_ASPEED_CTRL_INVERSE, val);
|
||||
pin_en = FIELD_GET(PWM_ASPEED_CTRL_PIN_ENABLE, val);
|
||||
clk_en = FIELD_GET(PWM_ASPEED_CTRL_CLK_ENABLE, val);
|
||||
div_h = FIELD_GET(PWM_ASPEED_CTRL_CLK_DIV_H, val);
|
||||
div_l = FIELD_GET(PWM_ASPEED_CTRL_CLK_DIV_L, val);
|
||||
val = readl(priv->base + PWM_ASPEED_DUTY_CYCLE(hwpwm));
|
||||
duty_pt = FIELD_GET(PWM_ASPEED_DUTY_CYCLE_FALLING_POINT, val);
|
||||
duty_cycle_period = FIELD_GET(PWM_ASPEED_DUTY_CYCLE_PERIOD, val);
|
||||
/*
|
||||
* This multiplication doesn't overflow, the upper bound is
|
||||
* 1000000000 * 256 * 256 << 15 = 0x1dcd650000000000
|
||||
*/
|
||||
dividend = (u64)NSEC_PER_SEC * (div_l + 1) * (duty_cycle_period + 1)
|
||||
<< div_h;
|
||||
state->period = DIV_ROUND_UP_ULL(dividend, priv->clk_rate);
|
||||
|
||||
if (clk_en && duty_pt) {
|
||||
dividend = (u64)NSEC_PER_SEC * (div_l + 1) * duty_pt
|
||||
<< div_h;
|
||||
state->duty_cycle = DIV_ROUND_UP_ULL(dividend, priv->clk_rate);
|
||||
} else {
|
||||
state->duty_cycle = clk_en ? state->period : 0;
|
||||
}
|
||||
state->polarity = polarity ? PWM_POLARITY_INVERSED : PWM_POLARITY_NORMAL;
|
||||
state->enabled = pin_en;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aspeed_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
const struct pwm_state *state)
|
||||
{
|
||||
struct aspeed_pwm_tach_data *priv = aspeed_pwm_chip_to_data(chip);
|
||||
u32 hwpwm = pwm->hwpwm, duty_pt, val;
|
||||
u64 div_h, div_l, divisor, expect_period;
|
||||
bool clk_en;
|
||||
|
||||
expect_period = div64_u64(ULLONG_MAX, (u64)priv->clk_rate);
|
||||
expect_period = min(expect_period, state->period);
|
||||
dev_dbg(chip->dev, "expect period: %lldns, duty_cycle: %lldns",
|
||||
expect_period, state->duty_cycle);
|
||||
/*
|
||||
* Pick the smallest value for div_h so that div_l can be the biggest
|
||||
* which results in a finer resolution near the target period value.
|
||||
*/
|
||||
divisor = (u64)NSEC_PER_SEC * (PWM_ASPEED_FIXED_PERIOD + 1) *
|
||||
(FIELD_MAX(PWM_ASPEED_CTRL_CLK_DIV_L) + 1);
|
||||
div_h = order_base_2(DIV64_U64_ROUND_UP(priv->clk_rate * expect_period, divisor));
|
||||
if (div_h > 0xf)
|
||||
div_h = 0xf;
|
||||
|
||||
divisor = ((u64)NSEC_PER_SEC * (PWM_ASPEED_FIXED_PERIOD + 1)) << div_h;
|
||||
div_l = div64_u64(priv->clk_rate * expect_period, divisor);
|
||||
|
||||
if (div_l == 0)
|
||||
return -ERANGE;
|
||||
|
||||
div_l -= 1;
|
||||
|
||||
if (div_l > 255)
|
||||
div_l = 255;
|
||||
|
||||
dev_dbg(chip->dev, "clk source: %ld div_h %lld, div_l : %lld\n",
|
||||
priv->clk_rate, div_h, div_l);
|
||||
/* duty_pt = duty_cycle * (PERIOD + 1) / period */
|
||||
duty_pt = div64_u64(state->duty_cycle * priv->clk_rate,
|
||||
(u64)NSEC_PER_SEC * (div_l + 1) << div_h);
|
||||
dev_dbg(chip->dev, "duty_cycle = %lld, duty_pt = %d\n",
|
||||
state->duty_cycle, duty_pt);
|
||||
|
||||
/*
|
||||
* Fixed DUTY_CYCLE_PERIOD to its max value to get a
|
||||
* fine-grained resolution for duty_cycle at the expense of a
|
||||
* coarser period resolution.
|
||||
*/
|
||||
val = readl(priv->base + PWM_ASPEED_DUTY_CYCLE(hwpwm));
|
||||
val &= ~PWM_ASPEED_DUTY_CYCLE_PERIOD;
|
||||
val |= FIELD_PREP(PWM_ASPEED_DUTY_CYCLE_PERIOD,
|
||||
PWM_ASPEED_FIXED_PERIOD);
|
||||
writel(val, priv->base + PWM_ASPEED_DUTY_CYCLE(hwpwm));
|
||||
|
||||
if (duty_pt == 0) {
|
||||
/* emit inactive level and assert the duty counter reset */
|
||||
clk_en = 0;
|
||||
} else {
|
||||
clk_en = 1;
|
||||
if (duty_pt >= (PWM_ASPEED_FIXED_PERIOD + 1))
|
||||
duty_pt = 0;
|
||||
val = readl(priv->base + PWM_ASPEED_DUTY_CYCLE(hwpwm));
|
||||
val &= ~(PWM_ASPEED_DUTY_CYCLE_RISING_POINT |
|
||||
PWM_ASPEED_DUTY_CYCLE_FALLING_POINT);
|
||||
val |= FIELD_PREP(PWM_ASPEED_DUTY_CYCLE_FALLING_POINT, duty_pt);
|
||||
writel(val, priv->base + PWM_ASPEED_DUTY_CYCLE(hwpwm));
|
||||
}
|
||||
|
||||
val = readl(priv->base + PWM_ASPEED_CTRL(hwpwm));
|
||||
val &= ~(PWM_ASPEED_CTRL_CLK_DIV_H | PWM_ASPEED_CTRL_CLK_DIV_L |
|
||||
PWM_ASPEED_CTRL_PIN_ENABLE | PWM_ASPEED_CTRL_CLK_ENABLE |
|
||||
PWM_ASPEED_CTRL_INVERSE);
|
||||
val |= FIELD_PREP(PWM_ASPEED_CTRL_CLK_DIV_H, div_h) |
|
||||
FIELD_PREP(PWM_ASPEED_CTRL_CLK_DIV_L, div_l) |
|
||||
FIELD_PREP(PWM_ASPEED_CTRL_PIN_ENABLE, state->enabled) |
|
||||
FIELD_PREP(PWM_ASPEED_CTRL_CLK_ENABLE, clk_en) |
|
||||
FIELD_PREP(PWM_ASPEED_CTRL_INVERSE, state->polarity);
|
||||
writel(val, priv->base + PWM_ASPEED_CTRL(hwpwm));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct pwm_ops aspeed_pwm_ops = {
|
||||
.apply = aspeed_pwm_apply,
|
||||
.get_state = aspeed_pwm_get_state,
|
||||
};
|
||||
|
||||
static void aspeed_tach_ch_enable(struct aspeed_pwm_tach_data *priv, u8 tach_ch,
|
||||
bool enable)
|
||||
{
|
||||
if (enable)
|
||||
writel(readl(priv->base + TACH_ASPEED_CTRL(tach_ch)) |
|
||||
TACH_ASPEED_ENABLE,
|
||||
priv->base + TACH_ASPEED_CTRL(tach_ch));
|
||||
else
|
||||
writel(readl(priv->base + TACH_ASPEED_CTRL(tach_ch)) &
|
||||
~TACH_ASPEED_ENABLE,
|
||||
priv->base + TACH_ASPEED_CTRL(tach_ch));
|
||||
}
|
||||
|
||||
static int aspeed_tach_val_to_rpm(struct aspeed_pwm_tach_data *priv, u32 tach_val)
|
||||
{
|
||||
u64 rpm;
|
||||
u32 tach_div;
|
||||
|
||||
tach_div = tach_val * priv->tach_divisor * DEFAULT_FAN_PULSE_PR;
|
||||
|
||||
dev_dbg(priv->dev, "clk %ld, tach_val %d , tach_div %d\n",
|
||||
priv->clk_rate, tach_val, tach_div);
|
||||
|
||||
rpm = (u64)priv->clk_rate * 60;
|
||||
do_div(rpm, tach_div);
|
||||
|
||||
return (int)rpm;
|
||||
}
|
||||
|
||||
static int aspeed_get_fan_tach_ch_rpm(struct aspeed_pwm_tach_data *priv,
|
||||
u8 fan_tach_ch)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = readl(priv->base + TACH_ASPEED_STS(fan_tach_ch));
|
||||
|
||||
if (!(val & TACH_ASPEED_FULL_MEASUREMENT))
|
||||
return 0;
|
||||
val = FIELD_GET(TACH_ASPEED_VALUE_MASK, val);
|
||||
return aspeed_tach_val_to_rpm(priv, val);
|
||||
}
|
||||
|
||||
static int aspeed_tach_hwmon_read(struct device *dev,
|
||||
enum hwmon_sensor_types type, u32 attr,
|
||||
int channel, long *val)
|
||||
{
|
||||
struct aspeed_pwm_tach_data *priv = dev_get_drvdata(dev);
|
||||
u32 reg_val;
|
||||
|
||||
switch (attr) {
|
||||
case hwmon_fan_input:
|
||||
*val = aspeed_get_fan_tach_ch_rpm(priv, channel);
|
||||
break;
|
||||
case hwmon_fan_div:
|
||||
reg_val = readl(priv->base + TACH_ASPEED_CTRL(channel));
|
||||
reg_val = FIELD_GET(TACH_ASPEED_CLK_DIV_T_MASK, reg_val);
|
||||
*val = BIT(reg_val << 1);
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aspeed_tach_hwmon_write(struct device *dev,
|
||||
enum hwmon_sensor_types type, u32 attr,
|
||||
int channel, long val)
|
||||
{
|
||||
struct aspeed_pwm_tach_data *priv = dev_get_drvdata(dev);
|
||||
u32 reg_val;
|
||||
|
||||
switch (attr) {
|
||||
case hwmon_fan_div:
|
||||
if (!is_power_of_2(val) || (ilog2(val) % 2) ||
|
||||
DIV_TO_REG(val) > 0xb)
|
||||
return -EINVAL;
|
||||
priv->tach_divisor = val;
|
||||
reg_val = readl(priv->base + TACH_ASPEED_CTRL(channel));
|
||||
reg_val &= ~TACH_ASPEED_CLK_DIV_T_MASK;
|
||||
reg_val |= FIELD_PREP(TACH_ASPEED_CLK_DIV_T_MASK,
|
||||
DIV_TO_REG(priv->tach_divisor));
|
||||
writel(reg_val, priv->base + TACH_ASPEED_CTRL(channel));
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static umode_t aspeed_tach_dev_is_visible(const void *drvdata,
|
||||
enum hwmon_sensor_types type,
|
||||
u32 attr, int channel)
|
||||
{
|
||||
const struct aspeed_pwm_tach_data *priv = drvdata;
|
||||
|
||||
if (!priv->tach_present[channel])
|
||||
return 0;
|
||||
switch (attr) {
|
||||
case hwmon_fan_input:
|
||||
return 0444;
|
||||
case hwmon_fan_div:
|
||||
return 0644;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct hwmon_ops aspeed_tach_ops = {
|
||||
.is_visible = aspeed_tach_dev_is_visible,
|
||||
.read = aspeed_tach_hwmon_read,
|
||||
.write = aspeed_tach_hwmon_write,
|
||||
};
|
||||
|
||||
static const struct hwmon_channel_info *aspeed_tach_info[] = {
|
||||
HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_DIV, HWMON_F_INPUT | HWMON_F_DIV,
|
||||
HWMON_F_INPUT | HWMON_F_DIV, HWMON_F_INPUT | HWMON_F_DIV,
|
||||
HWMON_F_INPUT | HWMON_F_DIV, HWMON_F_INPUT | HWMON_F_DIV,
|
||||
HWMON_F_INPUT | HWMON_F_DIV, HWMON_F_INPUT | HWMON_F_DIV,
|
||||
HWMON_F_INPUT | HWMON_F_DIV, HWMON_F_INPUT | HWMON_F_DIV,
|
||||
HWMON_F_INPUT | HWMON_F_DIV, HWMON_F_INPUT | HWMON_F_DIV,
|
||||
HWMON_F_INPUT | HWMON_F_DIV, HWMON_F_INPUT | HWMON_F_DIV,
|
||||
HWMON_F_INPUT | HWMON_F_DIV, HWMON_F_INPUT | HWMON_F_DIV),
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct hwmon_chip_info aspeed_tach_chip_info = {
|
||||
.ops = &aspeed_tach_ops,
|
||||
.info = aspeed_tach_info,
|
||||
};
|
||||
|
||||
static void aspeed_present_fan_tach(struct aspeed_pwm_tach_data *priv, u8 *tach_ch, int count)
|
||||
{
|
||||
u8 ch, index;
|
||||
u32 val;
|
||||
|
||||
for (index = 0; index < count; index++) {
|
||||
ch = tach_ch[index];
|
||||
priv->tach_present[ch] = true;
|
||||
priv->tach_divisor = DEFAULT_TACH_DIV;
|
||||
|
||||
val = readl(priv->base + TACH_ASPEED_CTRL(ch));
|
||||
val &= ~(TACH_ASPEED_INVERS_LIMIT | TACH_ASPEED_DEBOUNCE_MASK |
|
||||
TACH_ASPEED_IO_EDGE_MASK | TACH_ASPEED_CLK_DIV_T_MASK |
|
||||
TACH_ASPEED_THRESHOLD_MASK);
|
||||
val |= (DEBOUNCE_3_CLK << TACH_ASPEED_DEBOUNCE_BIT) |
|
||||
F2F_EDGES |
|
||||
FIELD_PREP(TACH_ASPEED_CLK_DIV_T_MASK,
|
||||
DIV_TO_REG(priv->tach_divisor));
|
||||
writel(val, priv->base + TACH_ASPEED_CTRL(ch));
|
||||
|
||||
aspeed_tach_ch_enable(priv, ch, true);
|
||||
}
|
||||
}
|
||||
|
||||
static int aspeed_create_fan_monitor(struct device *dev,
|
||||
struct device_node *child,
|
||||
struct aspeed_pwm_tach_data *priv)
|
||||
{
|
||||
int ret, count;
|
||||
u8 *tach_ch;
|
||||
|
||||
count = of_property_count_u8_elems(child, "tach-ch");
|
||||
if (count < 1)
|
||||
return -EINVAL;
|
||||
tach_ch = devm_kcalloc(dev, count, sizeof(*tach_ch), GFP_KERNEL);
|
||||
if (!tach_ch)
|
||||
return -ENOMEM;
|
||||
ret = of_property_read_u8_array(child, "tach-ch", tach_ch, count);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
aspeed_present_fan_tach(priv, tach_ch, count);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void aspeed_pwm_tach_reset_assert(void *data)
|
||||
{
|
||||
struct reset_control *rst = data;
|
||||
|
||||
reset_control_assert(rst);
|
||||
}
|
||||
|
||||
static int aspeed_pwm_tach_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev, *hwmon;
|
||||
int ret;
|
||||
struct device_node *child;
|
||||
struct aspeed_pwm_tach_data *priv;
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
priv->dev = dev;
|
||||
priv->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(priv->base))
|
||||
return PTR_ERR(priv->base);
|
||||
|
||||
priv->clk = devm_clk_get_enabled(dev, NULL);
|
||||
if (IS_ERR(priv->clk))
|
||||
return dev_err_probe(dev, PTR_ERR(priv->clk),
|
||||
"Couldn't get clock\n");
|
||||
priv->clk_rate = clk_get_rate(priv->clk);
|
||||
priv->reset = devm_reset_control_get_exclusive(dev, NULL);
|
||||
if (IS_ERR(priv->reset))
|
||||
return dev_err_probe(dev, PTR_ERR(priv->reset),
|
||||
"Couldn't get reset control\n");
|
||||
|
||||
ret = reset_control_deassert(priv->reset);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"Couldn't deassert reset control\n");
|
||||
ret = devm_add_action_or_reset(dev, aspeed_pwm_tach_reset_assert,
|
||||
priv->reset);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
priv->chip.dev = dev;
|
||||
priv->chip.ops = &aspeed_pwm_ops;
|
||||
priv->chip.npwm = PWM_ASPEED_NR_PWMS;
|
||||
|
||||
ret = devm_pwmchip_add(dev, &priv->chip);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to add PWM chip\n");
|
||||
|
||||
for_each_child_of_node(dev->of_node, child) {
|
||||
ret = aspeed_create_fan_monitor(dev, child, priv);
|
||||
if (ret) {
|
||||
of_node_put(child);
|
||||
dev_warn(dev, "Failed to create fan %d", ret);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
hwmon = devm_hwmon_device_register_with_info(dev, "aspeed_tach", priv,
|
||||
&aspeed_tach_chip_info, NULL);
|
||||
ret = PTR_ERR_OR_ZERO(hwmon);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"Failed to register hwmon device\n");
|
||||
|
||||
of_platform_populate(dev->of_node, NULL, NULL, dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aspeed_pwm_tach_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct aspeed_pwm_tach_data *priv = platform_get_drvdata(pdev);
|
||||
|
||||
reset_control_assert(priv->reset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id aspeed_pwm_tach_match[] = {
|
||||
{
|
||||
.compatible = "aspeed,ast2600-pwm-tach",
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, aspeed_pwm_tach_match);
|
||||
|
||||
static struct platform_driver aspeed_pwm_tach_driver = {
|
||||
.probe = aspeed_pwm_tach_probe,
|
||||
.remove = aspeed_pwm_tach_remove,
|
||||
.driver = {
|
||||
.name = "aspeed-g6-pwm-tach",
|
||||
.of_match_table = aspeed_pwm_tach_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(aspeed_pwm_tach_driver);
|
||||
|
||||
MODULE_AUTHOR("Billy Tsai <billy_tsai@aspeedtech.com>");
|
||||
MODULE_DESCRIPTION("Aspeed ast2600 PWM and Fan Tach device driver");
|
||||
MODULE_LICENSE("GPL");
|
609
drivers/hwmon/asus_rog_ryujin.c
Normal file
609
drivers/hwmon/asus_rog_ryujin.c
Normal file
@ -0,0 +1,609 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* hwmon driver for Asus ROG Ryujin II 360 AIO cooler.
|
||||
*
|
||||
* Copyright 2024 Aleksa Savic <savicaleksa83@gmail.com>
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#define DRIVER_NAME "asus_rog_ryujin"
|
||||
|
||||
#define USB_VENDOR_ID_ASUS_ROG 0x0b05
|
||||
#define USB_PRODUCT_ID_RYUJIN_AIO 0x1988 /* ASUS ROG RYUJIN II 360 */
|
||||
|
||||
#define STATUS_VALIDITY 1500 /* ms */
|
||||
#define MAX_REPORT_LENGTH 65
|
||||
|
||||
/* Cooler status report offsets */
|
||||
#define RYUJIN_TEMP_SENSOR_1 3
|
||||
#define RYUJIN_TEMP_SENSOR_2 4
|
||||
#define RYUJIN_PUMP_SPEED 5
|
||||
#define RYUJIN_INTERNAL_FAN_SPEED 7
|
||||
|
||||
/* Cooler duty report offsets */
|
||||
#define RYUJIN_PUMP_DUTY 4
|
||||
#define RYUJIN_INTERNAL_FAN_DUTY 5
|
||||
|
||||
/* Controller status (speeds) report offsets */
|
||||
#define RYUJIN_CONTROLLER_SPEED_1 5
|
||||
#define RYUJIN_CONTROLLER_SPEED_2 7
|
||||
#define RYUJIN_CONTROLLER_SPEED_3 9
|
||||
#define RYUJIN_CONTROLLER_SPEED_4 3
|
||||
|
||||
/* Controller duty report offsets */
|
||||
#define RYUJIN_CONTROLLER_DUTY 4
|
||||
|
||||
/* Control commands and their inner offsets */
|
||||
#define RYUJIN_CMD_PREFIX 0xEC
|
||||
|
||||
static const u8 get_cooler_status_cmd[] = { RYUJIN_CMD_PREFIX, 0x99 };
|
||||
static const u8 get_cooler_duty_cmd[] = { RYUJIN_CMD_PREFIX, 0x9A };
|
||||
static const u8 get_controller_speed_cmd[] = { RYUJIN_CMD_PREFIX, 0xA0 };
|
||||
static const u8 get_controller_duty_cmd[] = { RYUJIN_CMD_PREFIX, 0xA1 };
|
||||
|
||||
#define RYUJIN_SET_COOLER_PUMP_DUTY_OFFSET 3
|
||||
#define RYUJIN_SET_COOLER_FAN_DUTY_OFFSET 4
|
||||
static const u8 set_cooler_duty_cmd[] = { RYUJIN_CMD_PREFIX, 0x1A, 0x00, 0x00, 0x00 };
|
||||
|
||||
#define RYUJIN_SET_CONTROLLER_FAN_DUTY_OFFSET 4
|
||||
static const u8 set_controller_duty_cmd[] = { RYUJIN_CMD_PREFIX, 0x21, 0x00, 0x00, 0x00 };
|
||||
|
||||
/* Command lengths */
|
||||
#define GET_CMD_LENGTH 2 /* Same length for all get commands */
|
||||
#define SET_CMD_LENGTH 5 /* Same length for all set commands */
|
||||
|
||||
/* Command response headers */
|
||||
#define RYUJIN_GET_COOLER_STATUS_CMD_RESPONSE 0x19
|
||||
#define RYUJIN_GET_COOLER_DUTY_CMD_RESPONSE 0x1A
|
||||
#define RYUJIN_GET_CONTROLLER_SPEED_CMD_RESPONSE 0x20
|
||||
#define RYUJIN_GET_CONTROLLER_DUTY_CMD_RESPONSE 0x21
|
||||
|
||||
static const char *const rog_ryujin_temp_label[] = {
|
||||
"Coolant temp"
|
||||
};
|
||||
|
||||
static const char *const rog_ryujin_speed_label[] = {
|
||||
"Pump speed",
|
||||
"Internal fan speed",
|
||||
"Controller fan 1 speed",
|
||||
"Controller fan 2 speed",
|
||||
"Controller fan 3 speed",
|
||||
"Controller fan 4 speed",
|
||||
};
|
||||
|
||||
struct rog_ryujin_data {
|
||||
struct hid_device *hdev;
|
||||
struct device *hwmon_dev;
|
||||
/* For locking access to buffer */
|
||||
struct mutex buffer_lock;
|
||||
/* For queueing multiple readers */
|
||||
struct mutex status_report_request_mutex;
|
||||
/* For reinitializing the completions below */
|
||||
spinlock_t status_report_request_lock;
|
||||
struct completion cooler_status_received;
|
||||
struct completion controller_status_received;
|
||||
struct completion cooler_duty_received;
|
||||
struct completion controller_duty_received;
|
||||
struct completion cooler_duty_set;
|
||||
struct completion controller_duty_set;
|
||||
|
||||
/* Sensor data */
|
||||
s32 temp_input[1];
|
||||
u16 speed_input[6]; /* Pump, internal fan and four controller fan speeds in RPM */
|
||||
u8 duty_input[3]; /* Pump, internal fan and controller fan duty in PWM */
|
||||
|
||||
u8 *buffer;
|
||||
unsigned long updated; /* jiffies */
|
||||
};
|
||||
|
||||
static int rog_ryujin_percent_to_pwm(u16 val)
|
||||
{
|
||||
return DIV_ROUND_CLOSEST(val * 255, 100);
|
||||
}
|
||||
|
||||
static int rog_ryujin_pwm_to_percent(long val)
|
||||
{
|
||||
return DIV_ROUND_CLOSEST(val * 100, 255);
|
||||
}
|
||||
|
||||
static umode_t rog_ryujin_is_visible(const void *data,
|
||||
enum hwmon_sensor_types type, u32 attr, int channel)
|
||||
{
|
||||
switch (type) {
|
||||
case hwmon_temp:
|
||||
switch (attr) {
|
||||
case hwmon_temp_label:
|
||||
case hwmon_temp_input:
|
||||
return 0444;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case hwmon_fan:
|
||||
switch (attr) {
|
||||
case hwmon_fan_label:
|
||||
case hwmon_fan_input:
|
||||
return 0444;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case hwmon_pwm:
|
||||
switch (attr) {
|
||||
case hwmon_pwm_input:
|
||||
return 0644;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Writes the command to the device with the rest of the report filled with zeroes */
|
||||
static int rog_ryujin_write_expanded(struct rog_ryujin_data *priv, const u8 *cmd, int cmd_length)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&priv->buffer_lock);
|
||||
|
||||
memcpy_and_pad(priv->buffer, MAX_REPORT_LENGTH, cmd, cmd_length, 0x00);
|
||||
ret = hid_hw_output_report(priv->hdev, priv->buffer, MAX_REPORT_LENGTH);
|
||||
|
||||
mutex_unlock(&priv->buffer_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Assumes priv->status_report_request_mutex is locked */
|
||||
static int rog_ryujin_execute_cmd(struct rog_ryujin_data *priv, const u8 *cmd, int cmd_length,
|
||||
struct completion *status_completion)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Disable raw event parsing for a moment to safely reinitialize the
|
||||
* completion. Reinit is done because hidraw could have triggered
|
||||
* the raw event parsing and marked the passed in completion as done.
|
||||
*/
|
||||
spin_lock_bh(&priv->status_report_request_lock);
|
||||
reinit_completion(status_completion);
|
||||
spin_unlock_bh(&priv->status_report_request_lock);
|
||||
|
||||
/* Send command for getting data */
|
||||
ret = rog_ryujin_write_expanded(priv, cmd, cmd_length);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = wait_for_completion_interruptible_timeout(status_completion,
|
||||
msecs_to_jiffies(STATUS_VALIDITY));
|
||||
if (ret == 0)
|
||||
return -ETIMEDOUT;
|
||||
else if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rog_ryujin_get_status(struct rog_ryujin_data *priv)
|
||||
{
|
||||
int ret = mutex_lock_interruptible(&priv->status_report_request_mutex);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!time_after(jiffies, priv->updated + msecs_to_jiffies(STATUS_VALIDITY))) {
|
||||
/* Data is up to date */
|
||||
goto unlock_and_return;
|
||||
}
|
||||
|
||||
/* Retrieve cooler status */
|
||||
ret =
|
||||
rog_ryujin_execute_cmd(priv, get_cooler_status_cmd, GET_CMD_LENGTH,
|
||||
&priv->cooler_status_received);
|
||||
if (ret < 0)
|
||||
goto unlock_and_return;
|
||||
|
||||
/* Retrieve controller status (speeds) */
|
||||
ret =
|
||||
rog_ryujin_execute_cmd(priv, get_controller_speed_cmd, GET_CMD_LENGTH,
|
||||
&priv->controller_status_received);
|
||||
if (ret < 0)
|
||||
goto unlock_and_return;
|
||||
|
||||
/* Retrieve cooler duty */
|
||||
ret =
|
||||
rog_ryujin_execute_cmd(priv, get_cooler_duty_cmd, GET_CMD_LENGTH,
|
||||
&priv->cooler_duty_received);
|
||||
if (ret < 0)
|
||||
goto unlock_and_return;
|
||||
|
||||
/* Retrieve controller duty */
|
||||
ret =
|
||||
rog_ryujin_execute_cmd(priv, get_controller_duty_cmd, GET_CMD_LENGTH,
|
||||
&priv->controller_duty_received);
|
||||
if (ret < 0)
|
||||
goto unlock_and_return;
|
||||
|
||||
priv->updated = jiffies;
|
||||
|
||||
unlock_and_return:
|
||||
mutex_unlock(&priv->status_report_request_mutex);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rog_ryujin_read(struct device *dev, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel, long *val)
|
||||
{
|
||||
struct rog_ryujin_data *priv = dev_get_drvdata(dev);
|
||||
int ret = rog_ryujin_get_status(priv);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
switch (type) {
|
||||
case hwmon_temp:
|
||||
*val = priv->temp_input[channel];
|
||||
break;
|
||||
case hwmon_fan:
|
||||
*val = priv->speed_input[channel];
|
||||
break;
|
||||
case hwmon_pwm:
|
||||
switch (attr) {
|
||||
case hwmon_pwm_input:
|
||||
*val = priv->duty_input[channel];
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP; /* unreachable */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rog_ryujin_read_string(struct device *dev, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel, const char **str)
|
||||
{
|
||||
switch (type) {
|
||||
case hwmon_temp:
|
||||
*str = rog_ryujin_temp_label[channel];
|
||||
break;
|
||||
case hwmon_fan:
|
||||
*str = rog_ryujin_speed_label[channel];
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP; /* unreachable */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rog_ryujin_write_fixed_duty(struct rog_ryujin_data *priv, int channel, int val)
|
||||
{
|
||||
u8 set_cmd[SET_CMD_LENGTH];
|
||||
int ret;
|
||||
|
||||
if (channel < 2) {
|
||||
/*
|
||||
* Retrieve cooler duty since both pump and internal fan are set
|
||||
* together, then write back with one of them modified.
|
||||
*/
|
||||
ret = mutex_lock_interruptible(&priv->status_report_request_mutex);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret =
|
||||
rog_ryujin_execute_cmd(priv, get_cooler_duty_cmd, GET_CMD_LENGTH,
|
||||
&priv->cooler_duty_received);
|
||||
if (ret < 0)
|
||||
goto unlock_and_return;
|
||||
|
||||
memcpy(set_cmd, set_cooler_duty_cmd, SET_CMD_LENGTH);
|
||||
|
||||
/* Cooler duties are set as 0-100% */
|
||||
val = rog_ryujin_pwm_to_percent(val);
|
||||
|
||||
if (channel == 0) {
|
||||
/* Cooler pump duty */
|
||||
set_cmd[RYUJIN_SET_COOLER_PUMP_DUTY_OFFSET] = val;
|
||||
set_cmd[RYUJIN_SET_COOLER_FAN_DUTY_OFFSET] =
|
||||
rog_ryujin_pwm_to_percent(priv->duty_input[1]);
|
||||
} else if (channel == 1) {
|
||||
/* Cooler internal fan duty */
|
||||
set_cmd[RYUJIN_SET_COOLER_PUMP_DUTY_OFFSET] =
|
||||
rog_ryujin_pwm_to_percent(priv->duty_input[0]);
|
||||
set_cmd[RYUJIN_SET_COOLER_FAN_DUTY_OFFSET] = val;
|
||||
}
|
||||
|
||||
ret = rog_ryujin_execute_cmd(priv, set_cmd, SET_CMD_LENGTH, &priv->cooler_duty_set);
|
||||
unlock_and_return:
|
||||
mutex_unlock(&priv->status_report_request_mutex);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
/*
|
||||
* Controller fan duty (channel == 2). No need to retrieve current
|
||||
* duty, so just send the command.
|
||||
*/
|
||||
memcpy(set_cmd, set_controller_duty_cmd, SET_CMD_LENGTH);
|
||||
set_cmd[RYUJIN_SET_CONTROLLER_FAN_DUTY_OFFSET] = val;
|
||||
|
||||
ret =
|
||||
rog_ryujin_execute_cmd(priv, set_cmd, SET_CMD_LENGTH,
|
||||
&priv->controller_duty_set);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Lock onto this value until next refresh cycle */
|
||||
priv->duty_input[channel] = val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rog_ryujin_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
|
||||
long val)
|
||||
{
|
||||
struct rog_ryujin_data *priv = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
switch (type) {
|
||||
case hwmon_pwm:
|
||||
switch (attr) {
|
||||
case hwmon_pwm_input:
|
||||
if (val < 0 || val > 255)
|
||||
return -EINVAL;
|
||||
|
||||
ret = rog_ryujin_write_fixed_duty(priv, channel, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct hwmon_ops rog_ryujin_hwmon_ops = {
|
||||
.is_visible = rog_ryujin_is_visible,
|
||||
.read = rog_ryujin_read,
|
||||
.read_string = rog_ryujin_read_string,
|
||||
.write = rog_ryujin_write
|
||||
};
|
||||
|
||||
static const struct hwmon_channel_info *rog_ryujin_info[] = {
|
||||
HWMON_CHANNEL_INFO(temp,
|
||||
HWMON_T_INPUT | HWMON_T_LABEL),
|
||||
HWMON_CHANNEL_INFO(fan,
|
||||
HWMON_F_INPUT | HWMON_F_LABEL,
|
||||
HWMON_F_INPUT | HWMON_F_LABEL,
|
||||
HWMON_F_INPUT | HWMON_F_LABEL,
|
||||
HWMON_F_INPUT | HWMON_F_LABEL,
|
||||
HWMON_F_INPUT | HWMON_F_LABEL,
|
||||
HWMON_F_INPUT | HWMON_F_LABEL),
|
||||
HWMON_CHANNEL_INFO(pwm,
|
||||
HWMON_PWM_INPUT,
|
||||
HWMON_PWM_INPUT,
|
||||
HWMON_PWM_INPUT),
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct hwmon_chip_info rog_ryujin_chip_info = {
|
||||
.ops = &rog_ryujin_hwmon_ops,
|
||||
.info = rog_ryujin_info,
|
||||
};
|
||||
|
||||
static int rog_ryujin_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data,
|
||||
int size)
|
||||
{
|
||||
struct rog_ryujin_data *priv = hid_get_drvdata(hdev);
|
||||
|
||||
if (data[0] != RYUJIN_CMD_PREFIX)
|
||||
return 0;
|
||||
|
||||
if (data[1] == RYUJIN_GET_COOLER_STATUS_CMD_RESPONSE) {
|
||||
/* Received coolant temp and speeds of pump and internal fan */
|
||||
priv->temp_input[0] =
|
||||
data[RYUJIN_TEMP_SENSOR_1] * 1000 + data[RYUJIN_TEMP_SENSOR_2] * 100;
|
||||
priv->speed_input[0] = get_unaligned_le16(data + RYUJIN_PUMP_SPEED);
|
||||
priv->speed_input[1] = get_unaligned_le16(data + RYUJIN_INTERNAL_FAN_SPEED);
|
||||
|
||||
if (!completion_done(&priv->cooler_status_received))
|
||||
complete_all(&priv->cooler_status_received);
|
||||
} else if (data[1] == RYUJIN_GET_CONTROLLER_SPEED_CMD_RESPONSE) {
|
||||
/* Received speeds of four fans attached to the controller */
|
||||
priv->speed_input[2] = get_unaligned_le16(data + RYUJIN_CONTROLLER_SPEED_1);
|
||||
priv->speed_input[3] = get_unaligned_le16(data + RYUJIN_CONTROLLER_SPEED_2);
|
||||
priv->speed_input[4] = get_unaligned_le16(data + RYUJIN_CONTROLLER_SPEED_3);
|
||||
priv->speed_input[5] = get_unaligned_le16(data + RYUJIN_CONTROLLER_SPEED_4);
|
||||
|
||||
if (!completion_done(&priv->controller_status_received))
|
||||
complete_all(&priv->controller_status_received);
|
||||
} else if (data[1] == RYUJIN_GET_COOLER_DUTY_CMD_RESPONSE) {
|
||||
/* Received report for pump and internal fan duties (in %) */
|
||||
if (data[RYUJIN_PUMP_DUTY] == 0 && data[RYUJIN_INTERNAL_FAN_DUTY] == 0) {
|
||||
/*
|
||||
* We received a report with zeroes for duty in both places.
|
||||
* The device returns this as a confirmation that setting values
|
||||
* is successful. If we initiated a write, mark it as complete.
|
||||
*/
|
||||
if (!completion_done(&priv->cooler_duty_set))
|
||||
complete_all(&priv->cooler_duty_set);
|
||||
else if (!completion_done(&priv->cooler_duty_received))
|
||||
/*
|
||||
* We didn't initiate a write, but received both zeroes.
|
||||
* This means that either both duties are actually zero,
|
||||
* or that we received a success report caused by userspace.
|
||||
* We're expecting a report, so parse it.
|
||||
*/
|
||||
goto read_cooler_duty;
|
||||
return 0;
|
||||
}
|
||||
read_cooler_duty:
|
||||
priv->duty_input[0] = rog_ryujin_percent_to_pwm(data[RYUJIN_PUMP_DUTY]);
|
||||
priv->duty_input[1] = rog_ryujin_percent_to_pwm(data[RYUJIN_INTERNAL_FAN_DUTY]);
|
||||
|
||||
if (!completion_done(&priv->cooler_duty_received))
|
||||
complete_all(&priv->cooler_duty_received);
|
||||
} else if (data[1] == RYUJIN_GET_CONTROLLER_DUTY_CMD_RESPONSE) {
|
||||
/* Received report for controller duty for fans (in PWM) */
|
||||
if (data[RYUJIN_CONTROLLER_DUTY] == 0) {
|
||||
/*
|
||||
* We received a report with a zero for duty. The device returns this as
|
||||
* a confirmation that setting the controller duty value was successful.
|
||||
* If we initiated a write, mark it as complete.
|
||||
*/
|
||||
if (!completion_done(&priv->controller_duty_set))
|
||||
complete_all(&priv->controller_duty_set);
|
||||
else if (!completion_done(&priv->controller_duty_received))
|
||||
/*
|
||||
* We didn't initiate a write, but received a zero for duty.
|
||||
* This means that either the duty is actually zero, or that
|
||||
* we received a success report caused by userspace.
|
||||
* We're expecting a report, so parse it.
|
||||
*/
|
||||
goto read_controller_duty;
|
||||
return 0;
|
||||
}
|
||||
read_controller_duty:
|
||||
priv->duty_input[2] = data[RYUJIN_CONTROLLER_DUTY];
|
||||
|
||||
if (!completion_done(&priv->controller_duty_received))
|
||||
complete_all(&priv->controller_duty_received);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rog_ryujin_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
{
|
||||
struct rog_ryujin_data *priv;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(&hdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->hdev = hdev;
|
||||
hid_set_drvdata(hdev, priv);
|
||||
|
||||
/*
|
||||
* Initialize priv->updated to STATUS_VALIDITY seconds in the past, making
|
||||
* the initial empty data invalid for rog_ryujin_read() without the need for
|
||||
* a special case there.
|
||||
*/
|
||||
priv->updated = jiffies - msecs_to_jiffies(STATUS_VALIDITY);
|
||||
|
||||
ret = hid_parse(hdev);
|
||||
if (ret) {
|
||||
hid_err(hdev, "hid parse failed with %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Enable hidraw so existing user-space tools can continue to work */
|
||||
ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
|
||||
if (ret) {
|
||||
hid_err(hdev, "hid hw start failed with %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = hid_hw_open(hdev);
|
||||
if (ret) {
|
||||
hid_err(hdev, "hid hw open failed with %d\n", ret);
|
||||
goto fail_and_stop;
|
||||
}
|
||||
|
||||
priv->buffer = devm_kzalloc(&hdev->dev, MAX_REPORT_LENGTH, GFP_KERNEL);
|
||||
if (!priv->buffer) {
|
||||
ret = -ENOMEM;
|
||||
goto fail_and_close;
|
||||
}
|
||||
|
||||
mutex_init(&priv->status_report_request_mutex);
|
||||
mutex_init(&priv->buffer_lock);
|
||||
spin_lock_init(&priv->status_report_request_lock);
|
||||
init_completion(&priv->cooler_status_received);
|
||||
init_completion(&priv->controller_status_received);
|
||||
init_completion(&priv->cooler_duty_received);
|
||||
init_completion(&priv->controller_duty_received);
|
||||
init_completion(&priv->cooler_duty_set);
|
||||
init_completion(&priv->controller_duty_set);
|
||||
|
||||
priv->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "rog_ryujin",
|
||||
priv, &rog_ryujin_chip_info, NULL);
|
||||
if (IS_ERR(priv->hwmon_dev)) {
|
||||
ret = PTR_ERR(priv->hwmon_dev);
|
||||
hid_err(hdev, "hwmon registration failed with %d\n", ret);
|
||||
goto fail_and_close;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail_and_close:
|
||||
hid_hw_close(hdev);
|
||||
fail_and_stop:
|
||||
hid_hw_stop(hdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void rog_ryujin_remove(struct hid_device *hdev)
|
||||
{
|
||||
struct rog_ryujin_data *priv = hid_get_drvdata(hdev);
|
||||
|
||||
hwmon_device_unregister(priv->hwmon_dev);
|
||||
|
||||
hid_hw_close(hdev);
|
||||
hid_hw_stop(hdev);
|
||||
}
|
||||
|
||||
static const struct hid_device_id rog_ryujin_table[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ASUS_ROG, USB_PRODUCT_ID_RYUJIN_AIO) },
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(hid, rog_ryujin_table);
|
||||
|
||||
static struct hid_driver rog_ryujin_driver = {
|
||||
.name = "rog_ryujin",
|
||||
.id_table = rog_ryujin_table,
|
||||
.probe = rog_ryujin_probe,
|
||||
.remove = rog_ryujin_remove,
|
||||
.raw_event = rog_ryujin_raw_event,
|
||||
};
|
||||
|
||||
static int __init rog_ryujin_init(void)
|
||||
{
|
||||
return hid_register_driver(&rog_ryujin_driver);
|
||||
}
|
||||
|
||||
static void __exit rog_ryujin_exit(void)
|
||||
{
|
||||
hid_unregister_driver(&rog_ryujin_driver);
|
||||
}
|
||||
|
||||
/* When compiled into the kernel, initialize after the HID bus */
|
||||
late_initcall(rog_ryujin_init);
|
||||
module_exit(rog_ryujin_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Aleksa Savic <savicaleksa83@gmail.com>");
|
||||
MODULE_DESCRIPTION("Hwmon driver for Asus ROG Ryujin II 360 AIO cooler");
|
@ -13,8 +13,9 @@
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/property.h>
|
||||
|
||||
/* register map */
|
||||
#define ADI_REG_RSTN 0x0080
|
||||
@ -83,7 +84,7 @@ static ssize_t axi_fan_control_show(struct device *dev, struct device_attribute
|
||||
|
||||
temp = DIV_ROUND_CLOSEST_ULL(temp * 509314ULL, 65535) - 280230;
|
||||
|
||||
return sprintf(buf, "%u\n", temp);
|
||||
return sysfs_emit(buf, "%u\n", temp);
|
||||
}
|
||||
|
||||
static ssize_t axi_fan_control_store(struct device *dev, struct device_attribute *da,
|
||||
@ -368,12 +369,12 @@ static irqreturn_t axi_fan_control_irq_handler(int irq, void *data)
|
||||
}
|
||||
|
||||
static int axi_fan_control_init(struct axi_fan_control_data *ctl,
|
||||
const struct device_node *np)
|
||||
const struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* get fan pulses per revolution */
|
||||
ret = of_property_read_u32(np, "pulses-per-revolution", &ctl->ppr);
|
||||
ret = device_property_read_u32(dev, "pulses-per-revolution", &ctl->ppr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -443,25 +444,16 @@ static struct attribute *axi_fan_control_attrs[] = {
|
||||
};
|
||||
ATTRIBUTE_GROUPS(axi_fan_control);
|
||||
|
||||
static const u32 version_1_0_0 = ADI_AXI_PCORE_VER(1, 0, 'a');
|
||||
|
||||
static const struct of_device_id axi_fan_control_of_match[] = {
|
||||
{ .compatible = "adi,axi-fan-control-1.00.a",
|
||||
.data = (void *)&version_1_0_0},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, axi_fan_control_of_match);
|
||||
|
||||
static int axi_fan_control_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct axi_fan_control_data *ctl;
|
||||
struct clk *clk;
|
||||
const struct of_device_id *id;
|
||||
const unsigned int *id;
|
||||
const char *name = "axi_fan_control";
|
||||
u32 version;
|
||||
int ret;
|
||||
|
||||
id = of_match_node(axi_fan_control_of_match, pdev->dev.of_node);
|
||||
id = device_get_match_data(&pdev->dev);
|
||||
if (!id)
|
||||
return -EINVAL;
|
||||
|
||||
@ -474,10 +466,9 @@ static int axi_fan_control_probe(struct platform_device *pdev)
|
||||
return PTR_ERR(ctl->base);
|
||||
|
||||
clk = devm_clk_get_enabled(&pdev->dev, NULL);
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(&pdev->dev, "clk_get failed with %ld\n", PTR_ERR(clk));
|
||||
return PTR_ERR(clk);
|
||||
}
|
||||
if (IS_ERR(clk))
|
||||
return dev_err_probe(&pdev->dev, PTR_ERR(clk),
|
||||
"clk_get failed\n");
|
||||
|
||||
ctl->clk_rate = clk_get_rate(clk);
|
||||
if (!ctl->clk_rate)
|
||||
@ -485,22 +476,20 @@ static int axi_fan_control_probe(struct platform_device *pdev)
|
||||
|
||||
version = axi_ioread(ADI_AXI_REG_VERSION, ctl);
|
||||
if (ADI_AXI_PCORE_VER_MAJOR(version) !=
|
||||
ADI_AXI_PCORE_VER_MAJOR((*(u32 *)id->data))) {
|
||||
dev_err(&pdev->dev, "Major version mismatch. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n",
|
||||
ADI_AXI_PCORE_VER_MAJOR((*(u32 *)id->data)),
|
||||
ADI_AXI_PCORE_VER_MINOR((*(u32 *)id->data)),
|
||||
ADI_AXI_PCORE_VER_PATCH((*(u32 *)id->data)),
|
||||
ADI_AXI_PCORE_VER_MAJOR(version),
|
||||
ADI_AXI_PCORE_VER_MINOR(version),
|
||||
ADI_AXI_PCORE_VER_PATCH(version));
|
||||
return -ENODEV;
|
||||
}
|
||||
ADI_AXI_PCORE_VER_MAJOR((*id)))
|
||||
return dev_err_probe(&pdev->dev, -ENODEV,
|
||||
"Major version mismatch. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n",
|
||||
ADI_AXI_PCORE_VER_MAJOR(*id),
|
||||
ADI_AXI_PCORE_VER_MINOR(*id),
|
||||
ADI_AXI_PCORE_VER_PATCH(*id),
|
||||
ADI_AXI_PCORE_VER_MAJOR(version),
|
||||
ADI_AXI_PCORE_VER_MINOR(version),
|
||||
ADI_AXI_PCORE_VER_PATCH(version));
|
||||
|
||||
ret = axi_fan_control_init(ctl, pdev->dev.of_node);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to initialize device\n");
|
||||
return ret;
|
||||
}
|
||||
ret = axi_fan_control_init(ctl, &pdev->dev);
|
||||
if (ret)
|
||||
return dev_err_probe(&pdev->dev, ret,
|
||||
"Failed to initialize device\n");
|
||||
|
||||
ctl->hdev = devm_hwmon_device_register_with_info(&pdev->dev,
|
||||
name,
|
||||
@ -519,14 +508,22 @@ static int axi_fan_control_probe(struct platform_device *pdev)
|
||||
axi_fan_control_irq_handler,
|
||||
IRQF_ONESHOT | IRQF_TRIGGER_HIGH,
|
||||
pdev->driver_override, ctl);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to request an irq, %d", ret);
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
return dev_err_probe(&pdev->dev, ret,
|
||||
"failed to request an irq\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const u32 version_1_0_0 = ADI_AXI_PCORE_VER(1, 0, 'a');
|
||||
|
||||
static const struct of_device_id axi_fan_control_of_match[] = {
|
||||
{ .compatible = "adi,axi-fan-control-1.00.a",
|
||||
.data = (void *)&version_1_0_0},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, axi_fan_control_of_match);
|
||||
|
||||
static struct platform_driver axi_fan_control_driver = {
|
||||
.driver = {
|
||||
.name = "axi_fan_control_driver",
|
||||
|
822
drivers/hwmon/chipcap2.c
Normal file
822
drivers/hwmon/chipcap2.c
Normal file
@ -0,0 +1,822 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* cc2.c - Support for the Amphenol ChipCap 2 relative humidity, temperature sensor
|
||||
*
|
||||
* Part numbers supported:
|
||||
* CC2D23, CC2D23S, CC2D25, CC2D25S, CC2D33, CC2D33S, CC2D35, CC2D35S
|
||||
*
|
||||
* Author: Javier Carrasco <javier.carrasco.cruz@gmail.com>
|
||||
*
|
||||
* Datasheet and application notes:
|
||||
* https://www.amphenol-sensors.com/en/telaire/humidity/527-humidity-sensors/3095-chipcap-2
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#define CC2_START_CM 0xA0
|
||||
#define CC2_START_NOM 0x80
|
||||
#define CC2_R_ALARM_H_ON 0x18
|
||||
#define CC2_R_ALARM_H_OFF 0x19
|
||||
#define CC2_R_ALARM_L_ON 0x1A
|
||||
#define CC2_R_ALARM_L_OFF 0x1B
|
||||
#define CC2_RW_OFFSET 0x40
|
||||
#define CC2_W_ALARM_H_ON (CC2_R_ALARM_H_ON + CC2_RW_OFFSET)
|
||||
#define CC2_W_ALARM_H_OFF (CC2_R_ALARM_H_OFF + CC2_RW_OFFSET)
|
||||
#define CC2_W_ALARM_L_ON (CC2_R_ALARM_L_ON + CC2_RW_OFFSET)
|
||||
#define CC2_W_ALARM_L_OFF (CC2_R_ALARM_L_OFF + CC2_RW_OFFSET)
|
||||
|
||||
#define CC2_STATUS_FIELD GENMASK(7, 6)
|
||||
#define CC2_STATUS_VALID_DATA 0x00
|
||||
#define CC2_STATUS_STALE_DATA 0x01
|
||||
#define CC2_STATUS_CMD_MODE 0x02
|
||||
|
||||
#define CC2_RESPONSE_FIELD GENMASK(1, 0)
|
||||
#define CC2_RESPONSE_BUSY 0x00
|
||||
#define CC2_RESPONSE_ACK 0x01
|
||||
#define CC2_RESPONSE_NACK 0x02
|
||||
|
||||
#define CC2_ERR_CORR_EEPROM BIT(2)
|
||||
#define CC2_ERR_UNCORR_EEPROM BIT(3)
|
||||
#define CC2_ERR_RAM_PARITY BIT(4)
|
||||
#define CC2_ERR_CONFIG_LOAD BIT(5)
|
||||
|
||||
#define CC2_EEPROM_SIZE 10
|
||||
#define CC2_EEPROM_DATA_LEN 3
|
||||
#define CC2_MEASUREMENT_DATA_LEN 4
|
||||
|
||||
#define CC2_RH_DATA_FIELD GENMASK(13, 0)
|
||||
|
||||
/* ensure clean off -> on transitions */
|
||||
#define CC2_POWER_CYCLE_MS 80
|
||||
|
||||
#define CC2_STARTUP_TO_DATA_MS 55
|
||||
#define CC2_RESP_START_CM_US 100
|
||||
#define CC2_RESP_EEPROM_R_US 100
|
||||
#define CC2_RESP_EEPROM_W_MS 12
|
||||
#define CC2_STARTUP_TIME_US 1250
|
||||
|
||||
#define CC2_RH_MAX (100 * 1000U)
|
||||
|
||||
#define CC2_CM_RETRIES 5
|
||||
|
||||
struct cc2_rh_alarm_info {
|
||||
bool low_alarm;
|
||||
bool high_alarm;
|
||||
bool low_alarm_visible;
|
||||
bool high_alarm_visible;
|
||||
};
|
||||
|
||||
struct cc2_data {
|
||||
struct cc2_rh_alarm_info rh_alarm;
|
||||
struct completion complete;
|
||||
struct device *hwmon;
|
||||
struct i2c_client *client;
|
||||
struct mutex dev_access_lock; /* device access lock */
|
||||
struct regulator *regulator;
|
||||
const char *name;
|
||||
int irq_ready;
|
||||
int irq_low;
|
||||
int irq_high;
|
||||
bool process_irqs;
|
||||
};
|
||||
|
||||
enum cc2_chan_addr {
|
||||
CC2_CHAN_TEMP = 0,
|
||||
CC2_CHAN_HUMIDITY,
|
||||
};
|
||||
|
||||
/* %RH as a per cent mille from a register value */
|
||||
static long cc2_rh_convert(u16 data)
|
||||
{
|
||||
unsigned long tmp = (data & CC2_RH_DATA_FIELD) * CC2_RH_MAX;
|
||||
|
||||
return tmp / ((1 << 14) - 1);
|
||||
}
|
||||
|
||||
/* convert %RH to a register value */
|
||||
static u16 cc2_rh_to_reg(long data)
|
||||
{
|
||||
return data * ((1 << 14) - 1) / CC2_RH_MAX;
|
||||
}
|
||||
|
||||
/* temperature in milli degrees celsius from a register value */
|
||||
static long cc2_temp_convert(u16 data)
|
||||
{
|
||||
unsigned long tmp = ((data >> 2) * 165 * 1000U) / ((1 << 14) - 1);
|
||||
|
||||
return tmp - 40 * 1000U;
|
||||
}
|
||||
|
||||
static int cc2_enable(struct cc2_data *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* exclusive regulator, check in case a disable failed */
|
||||
if (regulator_is_enabled(data->regulator))
|
||||
return 0;
|
||||
|
||||
/* clear any pending completion */
|
||||
try_wait_for_completion(&data->complete);
|
||||
|
||||
ret = regulator_enable(data->regulator);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
usleep_range(CC2_STARTUP_TIME_US, CC2_STARTUP_TIME_US + 125);
|
||||
|
||||
data->process_irqs = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cc2_disable(struct cc2_data *data)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* ignore alarms triggered by voltage toggling when powering up */
|
||||
data->process_irqs = false;
|
||||
|
||||
/* exclusive regulator, check in case an enable failed */
|
||||
if (regulator_is_enabled(data->regulator)) {
|
||||
err = regulator_disable(data->regulator);
|
||||
if (err)
|
||||
dev_dbg(&data->client->dev, "Failed to disable device");
|
||||
}
|
||||
}
|
||||
|
||||
static int cc2_cmd_response_diagnostic(struct device *dev, u8 status)
|
||||
{
|
||||
int resp;
|
||||
|
||||
if (FIELD_GET(CC2_STATUS_FIELD, status) != CC2_STATUS_CMD_MODE) {
|
||||
dev_dbg(dev, "Command sent out of command window\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
resp = FIELD_GET(CC2_RESPONSE_FIELD, status);
|
||||
switch (resp) {
|
||||
case CC2_RESPONSE_ACK:
|
||||
return 0;
|
||||
case CC2_RESPONSE_BUSY:
|
||||
return -EBUSY;
|
||||
case CC2_RESPONSE_NACK:
|
||||
if (resp & CC2_ERR_CORR_EEPROM)
|
||||
dev_dbg(dev, "Command failed: corrected EEPROM\n");
|
||||
if (resp & CC2_ERR_UNCORR_EEPROM)
|
||||
dev_dbg(dev, "Command failed: uncorrected EEPROM\n");
|
||||
if (resp & CC2_ERR_RAM_PARITY)
|
||||
dev_dbg(dev, "Command failed: RAM parity\n");
|
||||
if (resp & CC2_ERR_RAM_PARITY)
|
||||
dev_dbg(dev, "Command failed: configuration error\n");
|
||||
return -ENODATA;
|
||||
default:
|
||||
dev_dbg(dev, "Unknown command reply\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int cc2_read_command_status(struct i2c_client *client)
|
||||
{
|
||||
u8 status;
|
||||
int ret;
|
||||
|
||||
ret = i2c_master_recv(client, &status, 1);
|
||||
if (ret != 1) {
|
||||
ret = ret < 0 ? ret : -EIO;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return cc2_cmd_response_diagnostic(&client->dev, status);
|
||||
}
|
||||
|
||||
/*
|
||||
* The command mode is only accessible after sending the START_CM command in the
|
||||
* first 10 ms after power-up. Only in case the command window is missed,
|
||||
* CC2_CM_RETRIES retries are attempted before giving up and returning an error.
|
||||
*/
|
||||
static int cc2_command_mode_start(struct cc2_data *data)
|
||||
{
|
||||
unsigned long timeout;
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < CC2_CM_RETRIES; i++) {
|
||||
ret = cc2_enable(data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = i2c_smbus_write_word_data(data->client, CC2_START_CM, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (data->irq_ready > 0) {
|
||||
timeout = usecs_to_jiffies(2 * CC2_RESP_START_CM_US);
|
||||
ret = wait_for_completion_timeout(&data->complete,
|
||||
timeout);
|
||||
if (!ret)
|
||||
return -ETIMEDOUT;
|
||||
} else {
|
||||
usleep_range(CC2_RESP_START_CM_US,
|
||||
2 * CC2_RESP_START_CM_US);
|
||||
}
|
||||
ret = cc2_read_command_status(data->client);
|
||||
if (ret != -ETIMEDOUT || i == CC2_CM_RETRIES)
|
||||
break;
|
||||
|
||||
/* command window missed, prepare for a retry */
|
||||
cc2_disable(data);
|
||||
msleep(CC2_POWER_CYCLE_MS);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Sending a Start_NOM command finishes the command mode immediately with no
|
||||
* reply and the device enters normal operation mode
|
||||
*/
|
||||
static int cc2_command_mode_finish(struct cc2_data *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_write_word_data(data->client, CC2_START_NOM, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cc2_write_reg(struct cc2_data *data, u8 reg, u16 val)
|
||||
{
|
||||
unsigned long timeout;
|
||||
int ret;
|
||||
|
||||
ret = cc2_command_mode_start(data);
|
||||
if (ret < 0)
|
||||
goto disable;
|
||||
|
||||
cpu_to_be16s(&val);
|
||||
ret = i2c_smbus_write_word_data(data->client, reg, val);
|
||||
if (ret < 0)
|
||||
goto disable;
|
||||
|
||||
if (data->irq_ready > 0) {
|
||||
timeout = msecs_to_jiffies(2 * CC2_RESP_EEPROM_W_MS);
|
||||
ret = wait_for_completion_timeout(&data->complete, timeout);
|
||||
if (!ret) {
|
||||
ret = -ETIMEDOUT;
|
||||
goto disable;
|
||||
}
|
||||
} else {
|
||||
msleep(CC2_RESP_EEPROM_W_MS);
|
||||
}
|
||||
|
||||
ret = cc2_read_command_status(data->client);
|
||||
|
||||
disable:
|
||||
cc2_disable(data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cc2_read_reg(struct cc2_data *data, u8 reg, u16 *val)
|
||||
{
|
||||
u8 buf[CC2_EEPROM_DATA_LEN];
|
||||
unsigned long timeout;
|
||||
int ret;
|
||||
|
||||
ret = cc2_command_mode_start(data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = i2c_smbus_write_word_data(data->client, reg, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (data->irq_ready > 0) {
|
||||
timeout = usecs_to_jiffies(2 * CC2_RESP_EEPROM_R_US);
|
||||
ret = wait_for_completion_timeout(&data->complete, timeout);
|
||||
if (!ret)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
} else {
|
||||
usleep_range(CC2_RESP_EEPROM_R_US, CC2_RESP_EEPROM_R_US + 10);
|
||||
}
|
||||
ret = i2c_master_recv(data->client, buf, CC2_EEPROM_DATA_LEN);
|
||||
if (ret != CC2_EEPROM_DATA_LEN)
|
||||
return ret < 0 ? ret : -EIO;
|
||||
|
||||
*val = be16_to_cpup((__be16 *)&buf[1]);
|
||||
|
||||
return cc2_read_command_status(data->client);
|
||||
}
|
||||
|
||||
static int cc2_get_reg_val(struct cc2_data *data, u8 reg, long *val)
|
||||
{
|
||||
u16 reg_val;
|
||||
int ret;
|
||||
|
||||
ret = cc2_read_reg(data, reg, ®_val);
|
||||
if (!ret)
|
||||
*val = cc2_rh_convert(reg_val);
|
||||
|
||||
cc2_disable(data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cc2_data_fetch(struct i2c_client *client,
|
||||
enum hwmon_sensor_types type, long *val)
|
||||
{
|
||||
u8 data[CC2_MEASUREMENT_DATA_LEN];
|
||||
u8 status;
|
||||
int ret;
|
||||
|
||||
ret = i2c_master_recv(client, data, CC2_MEASUREMENT_DATA_LEN);
|
||||
if (ret != CC2_MEASUREMENT_DATA_LEN) {
|
||||
ret = ret < 0 ? ret : -EIO;
|
||||
return ret;
|
||||
}
|
||||
status = FIELD_GET(CC2_STATUS_FIELD, data[0]);
|
||||
if (status == CC2_STATUS_STALE_DATA)
|
||||
return -EBUSY;
|
||||
|
||||
if (status != CC2_STATUS_VALID_DATA)
|
||||
return -EIO;
|
||||
|
||||
switch (type) {
|
||||
case hwmon_humidity:
|
||||
*val = cc2_rh_convert(be16_to_cpup((__be16 *)&data[0]));
|
||||
break;
|
||||
case hwmon_temp:
|
||||
*val = cc2_temp_convert(be16_to_cpup((__be16 *)&data[2]));
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cc2_read_measurement(struct cc2_data *data,
|
||||
enum hwmon_sensor_types type, long *val)
|
||||
{
|
||||
unsigned long timeout;
|
||||
int ret;
|
||||
|
||||
if (data->irq_ready > 0) {
|
||||
timeout = msecs_to_jiffies(CC2_STARTUP_TO_DATA_MS * 2);
|
||||
ret = wait_for_completion_timeout(&data->complete, timeout);
|
||||
if (!ret)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
} else {
|
||||
msleep(CC2_STARTUP_TO_DATA_MS);
|
||||
}
|
||||
|
||||
ret = cc2_data_fetch(data->client, type, val);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* A measurement requires enabling the device, waiting for the automatic
|
||||
* measurement to finish, reading the measurement data and disabling the device
|
||||
* again.
|
||||
*/
|
||||
static int cc2_measurement(struct cc2_data *data, enum hwmon_sensor_types type,
|
||||
long *val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = cc2_enable(data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = cc2_read_measurement(data, type, val);
|
||||
|
||||
cc2_disable(data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* In order to check alarm status, the corresponding ALARM_OFF (hysteresis)
|
||||
* register must be read and a new measurement must be carried out to trigger
|
||||
* the alarm signals. Given that the device carries out a measurement after
|
||||
* exiting the command mode, there is no need to force two power-up sequences.
|
||||
* Instead, a NOM command is sent and the device is disabled after the
|
||||
* measurement is read.
|
||||
*/
|
||||
static int cc2_read_hyst_and_measure(struct cc2_data *data, u8 reg,
|
||||
long *hyst, long *measurement)
|
||||
{
|
||||
u16 reg_val;
|
||||
int ret;
|
||||
|
||||
ret = cc2_read_reg(data, reg, ®_val);
|
||||
if (ret)
|
||||
goto disable;
|
||||
|
||||
*hyst = cc2_rh_convert(reg_val);
|
||||
|
||||
ret = cc2_command_mode_finish(data);
|
||||
if (ret)
|
||||
goto disable;
|
||||
|
||||
ret = cc2_read_measurement(data, hwmon_humidity, measurement);
|
||||
|
||||
disable:
|
||||
cc2_disable(data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static umode_t cc2_is_visible(const void *data, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel)
|
||||
{
|
||||
const struct cc2_data *cc2 = data;
|
||||
|
||||
switch (type) {
|
||||
case hwmon_humidity:
|
||||
switch (attr) {
|
||||
case hwmon_humidity_input:
|
||||
return 0444;
|
||||
case hwmon_humidity_min_alarm:
|
||||
return cc2->rh_alarm.low_alarm_visible ? 0444 : 0;
|
||||
case hwmon_humidity_max_alarm:
|
||||
return cc2->rh_alarm.high_alarm_visible ? 0444 : 0;
|
||||
case hwmon_humidity_min:
|
||||
case hwmon_humidity_min_hyst:
|
||||
return cc2->rh_alarm.low_alarm_visible ? 0644 : 0;
|
||||
case hwmon_humidity_max:
|
||||
case hwmon_humidity_max_hyst:
|
||||
return cc2->rh_alarm.high_alarm_visible ? 0644 : 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
case hwmon_temp:
|
||||
switch (attr) {
|
||||
case hwmon_temp_input:
|
||||
return 0444;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t cc2_ready_interrupt(int irq, void *data)
|
||||
{
|
||||
struct cc2_data *cc2 = data;
|
||||
|
||||
if (cc2->process_irqs)
|
||||
complete(&cc2->complete);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t cc2_low_interrupt(int irq, void *data)
|
||||
{
|
||||
struct cc2_data *cc2 = data;
|
||||
|
||||
if (cc2->process_irqs) {
|
||||
hwmon_notify_event(cc2->hwmon, hwmon_humidity,
|
||||
hwmon_humidity_min_alarm, CC2_CHAN_HUMIDITY);
|
||||
cc2->rh_alarm.low_alarm = true;
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t cc2_high_interrupt(int irq, void *data)
|
||||
{
|
||||
struct cc2_data *cc2 = data;
|
||||
|
||||
if (cc2->process_irqs) {
|
||||
hwmon_notify_event(cc2->hwmon, hwmon_humidity,
|
||||
hwmon_humidity_max_alarm, CC2_CHAN_HUMIDITY);
|
||||
cc2->rh_alarm.high_alarm = true;
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int cc2_humidity_min_alarm_status(struct cc2_data *data, long *val)
|
||||
{
|
||||
long measurement, min_hyst;
|
||||
int ret;
|
||||
|
||||
ret = cc2_read_hyst_and_measure(data, CC2_R_ALARM_L_OFF, &min_hyst,
|
||||
&measurement);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (data->rh_alarm.low_alarm) {
|
||||
*val = (measurement < min_hyst) ? 1 : 0;
|
||||
data->rh_alarm.low_alarm = *val;
|
||||
} else {
|
||||
*val = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cc2_humidity_max_alarm_status(struct cc2_data *data, long *val)
|
||||
{
|
||||
long measurement, max_hyst;
|
||||
int ret;
|
||||
|
||||
ret = cc2_read_hyst_and_measure(data, CC2_R_ALARM_H_OFF, &max_hyst,
|
||||
&measurement);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (data->rh_alarm.high_alarm) {
|
||||
*val = (measurement > max_hyst) ? 1 : 0;
|
||||
data->rh_alarm.high_alarm = *val;
|
||||
} else {
|
||||
*val = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cc2_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
|
||||
int channel, long *val)
|
||||
{
|
||||
struct cc2_data *data = dev_get_drvdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&data->dev_access_lock);
|
||||
|
||||
switch (type) {
|
||||
case hwmon_temp:
|
||||
ret = cc2_measurement(data, type, val);
|
||||
break;
|
||||
case hwmon_humidity:
|
||||
switch (attr) {
|
||||
case hwmon_humidity_input:
|
||||
ret = cc2_measurement(data, type, val);
|
||||
break;
|
||||
case hwmon_humidity_min:
|
||||
ret = cc2_get_reg_val(data, CC2_R_ALARM_L_ON, val);
|
||||
break;
|
||||
case hwmon_humidity_min_hyst:
|
||||
ret = cc2_get_reg_val(data, CC2_R_ALARM_L_OFF, val);
|
||||
break;
|
||||
case hwmon_humidity_max:
|
||||
ret = cc2_get_reg_val(data, CC2_R_ALARM_H_ON, val);
|
||||
break;
|
||||
case hwmon_humidity_max_hyst:
|
||||
ret = cc2_get_reg_val(data, CC2_R_ALARM_H_OFF, val);
|
||||
break;
|
||||
case hwmon_humidity_min_alarm:
|
||||
ret = cc2_humidity_min_alarm_status(data, val);
|
||||
break;
|
||||
case hwmon_humidity_max_alarm:
|
||||
ret = cc2_humidity_max_alarm_status(data, val);
|
||||
break;
|
||||
default:
|
||||
ret = -EOPNOTSUPP;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret = -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
mutex_unlock(&data->dev_access_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cc2_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
|
||||
int channel, long val)
|
||||
{
|
||||
struct cc2_data *data = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
u16 arg;
|
||||
u8 cmd;
|
||||
|
||||
if (type != hwmon_humidity)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (val < 0 || val > CC2_RH_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&data->dev_access_lock);
|
||||
|
||||
switch (attr) {
|
||||
case hwmon_humidity_min:
|
||||
cmd = CC2_W_ALARM_L_ON;
|
||||
arg = cc2_rh_to_reg(val);
|
||||
ret = cc2_write_reg(data, cmd, arg);
|
||||
break;
|
||||
|
||||
case hwmon_humidity_min_hyst:
|
||||
cmd = CC2_W_ALARM_L_OFF;
|
||||
arg = cc2_rh_to_reg(val);
|
||||
ret = cc2_write_reg(data, cmd, arg);
|
||||
break;
|
||||
|
||||
case hwmon_humidity_max:
|
||||
cmd = CC2_W_ALARM_H_ON;
|
||||
arg = cc2_rh_to_reg(val);
|
||||
ret = cc2_write_reg(data, cmd, arg);
|
||||
break;
|
||||
|
||||
case hwmon_humidity_max_hyst:
|
||||
cmd = CC2_W_ALARM_H_OFF;
|
||||
arg = cc2_rh_to_reg(val);
|
||||
ret = cc2_write_reg(data, cmd, arg);
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = -EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&data->dev_access_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cc2_request_ready_irq(struct cc2_data *data, struct device *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
data->irq_ready = fwnode_irq_get_byname(dev_fwnode(dev), "ready");
|
||||
if (data->irq_ready > 0) {
|
||||
init_completion(&data->complete);
|
||||
ret = devm_request_threaded_irq(dev, data->irq_ready, NULL,
|
||||
cc2_ready_interrupt,
|
||||
IRQF_ONESHOT |
|
||||
IRQF_TRIGGER_RISING,
|
||||
dev_name(dev), data);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cc2_request_alarm_irqs(struct cc2_data *data, struct device *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
data->irq_low = fwnode_irq_get_byname(dev_fwnode(dev), "low");
|
||||
if (data->irq_low > 0) {
|
||||
ret = devm_request_threaded_irq(dev, data->irq_low, NULL,
|
||||
cc2_low_interrupt,
|
||||
IRQF_ONESHOT |
|
||||
IRQF_TRIGGER_RISING,
|
||||
dev_name(dev), data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
data->rh_alarm.low_alarm_visible = true;
|
||||
}
|
||||
|
||||
data->irq_high = fwnode_irq_get_byname(dev_fwnode(dev), "high");
|
||||
if (data->irq_high > 0) {
|
||||
ret = devm_request_threaded_irq(dev, data->irq_high, NULL,
|
||||
cc2_high_interrupt,
|
||||
IRQF_ONESHOT |
|
||||
IRQF_TRIGGER_RISING,
|
||||
dev_name(dev), data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
data->rh_alarm.high_alarm_visible = true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct hwmon_channel_info *cc2_info[] = {
|
||||
HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
|
||||
HWMON_CHANNEL_INFO(humidity, HWMON_H_INPUT | HWMON_H_MIN | HWMON_H_MAX |
|
||||
HWMON_H_MIN_HYST | HWMON_H_MAX_HYST |
|
||||
HWMON_H_MIN_ALARM | HWMON_H_MAX_ALARM),
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct hwmon_ops cc2_hwmon_ops = {
|
||||
.is_visible = cc2_is_visible,
|
||||
.read = cc2_read,
|
||||
.write = cc2_write,
|
||||
};
|
||||
|
||||
static const struct hwmon_chip_info cc2_chip_info = {
|
||||
.ops = &cc2_hwmon_ops,
|
||||
.info = cc2_info,
|
||||
};
|
||||
|
||||
static int cc2_probe(struct i2c_client *client)
|
||||
{
|
||||
struct cc2_data *data;
|
||||
struct device *dev = &client->dev;
|
||||
int ret;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(client, data);
|
||||
|
||||
mutex_init(&data->dev_access_lock);
|
||||
|
||||
data->client = client;
|
||||
|
||||
data->regulator = devm_regulator_get_exclusive(dev, "vdd");
|
||||
if (IS_ERR(data->regulator)) {
|
||||
dev_err_probe(dev, PTR_ERR(data->regulator),
|
||||
"Failed to get regulator\n");
|
||||
return PTR_ERR(data->regulator);
|
||||
}
|
||||
|
||||
ret = cc2_request_ready_irq(data, dev);
|
||||
if (ret) {
|
||||
dev_err_probe(dev, ret, "Failed to request ready irq\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = cc2_request_alarm_irqs(data, dev);
|
||||
if (ret) {
|
||||
dev_err_probe(dev, ret, "Failed to request alarm irqs\n");
|
||||
goto disable;
|
||||
}
|
||||
|
||||
data->hwmon = devm_hwmon_device_register_with_info(dev, client->name,
|
||||
data, &cc2_chip_info,
|
||||
NULL);
|
||||
if (IS_ERR(data->hwmon)) {
|
||||
dev_err_probe(dev, PTR_ERR(data->hwmon),
|
||||
"Failed to register hwmon device\n");
|
||||
ret = PTR_ERR(data->hwmon);
|
||||
}
|
||||
|
||||
disable:
|
||||
cc2_disable(data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void cc2_remove(struct i2c_client *client)
|
||||
{
|
||||
struct cc2_data *data = i2c_get_clientdata(client);
|
||||
|
||||
cc2_disable(data);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id cc2_id[] = {
|
||||
{ "cc2d23" },
|
||||
{ "cc2d23s" },
|
||||
{ "cc2d25" },
|
||||
{ "cc2d25s" },
|
||||
{ "cc2d33" },
|
||||
{ "cc2d33s" },
|
||||
{ "cc2d35" },
|
||||
{ "cc2d35s" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, cc2_id);
|
||||
|
||||
static const struct of_device_id cc2_of_match[] = {
|
||||
{ .compatible = "amphenol,cc2d23" },
|
||||
{ .compatible = "amphenol,cc2d23s" },
|
||||
{ .compatible = "amphenol,cc2d25" },
|
||||
{ .compatible = "amphenol,cc2d25s" },
|
||||
{ .compatible = "amphenol,cc2d33" },
|
||||
{ .compatible = "amphenol,cc2d33s" },
|
||||
{ .compatible = "amphenol,cc2d35" },
|
||||
{ .compatible = "amphenol,cc2d35s" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, cc2_of_match);
|
||||
|
||||
static struct i2c_driver cc2_driver = {
|
||||
.driver = {
|
||||
.name = "cc2d23",
|
||||
.of_match_table = cc2_of_match,
|
||||
},
|
||||
.probe = cc2_probe,
|
||||
.remove = cc2_remove,
|
||||
.id_table = cc2_id,
|
||||
};
|
||||
module_i2c_driver(cc2_driver);
|
||||
|
||||
MODULE_AUTHOR("Javier Carrasco <javier.carrasco.cruz@gamil.com>");
|
||||
MODULE_DESCRIPTION("Amphenol ChipCap 2 humidity and temperature sensor driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -39,13 +39,18 @@ static int force_tjmax;
|
||||
module_param_named(tjmax, force_tjmax, int, 0444);
|
||||
MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius");
|
||||
|
||||
#define PKG_SYSFS_ATTR_NO 1 /* Sysfs attribute for package temp */
|
||||
#define BASE_SYSFS_ATTR_NO 2 /* Sysfs Base attr no for coretemp */
|
||||
#define NUM_REAL_CORES 512 /* Number of Real cores per cpu */
|
||||
#define CORETEMP_NAME_LENGTH 28 /* String Length of attrs */
|
||||
#define MAX_CORE_ATTRS 4 /* Maximum no of basic attrs */
|
||||
#define TOTAL_ATTRS (MAX_CORE_ATTRS + 1)
|
||||
#define MAX_CORE_DATA (NUM_REAL_CORES + BASE_SYSFS_ATTR_NO)
|
||||
|
||||
enum coretemp_attr_index {
|
||||
ATTR_LABEL,
|
||||
ATTR_CRIT_ALARM,
|
||||
ATTR_TEMP,
|
||||
ATTR_TJMAX,
|
||||
ATTR_TTARGET,
|
||||
MAX_CORE_ATTRS = ATTR_TJMAX + 1, /* Maximum no of basic attrs */
|
||||
TOTAL_ATTRS = ATTR_TTARGET + 1 /* Maximum no of possible attrs */
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
#define for_each_sibling(i, cpu) \
|
||||
@ -65,19 +70,17 @@ MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius");
|
||||
* @status_reg: One of IA32_THERM_STATUS or IA32_PACKAGE_THERM_STATUS,
|
||||
* from where the temperature values should be read.
|
||||
* @attr_size: Total number of pre-core attrs displayed in the sysfs.
|
||||
* @is_pkg_data: If this is 1, the temp_data holds pkgtemp data.
|
||||
* Otherwise, temp_data holds coretemp data.
|
||||
*/
|
||||
struct temp_data {
|
||||
int temp;
|
||||
int tjmax;
|
||||
unsigned long last_updated;
|
||||
unsigned int cpu;
|
||||
int index;
|
||||
u32 cpu_core_id;
|
||||
u32 status_reg;
|
||||
int attr_size;
|
||||
bool is_pkg_data;
|
||||
struct sensor_device_attribute sd_attrs[TOTAL_ATTRS];
|
||||
struct device_attribute sd_attrs[TOTAL_ATTRS];
|
||||
char attr_name[TOTAL_ATTRS][CORETEMP_NAME_LENGTH];
|
||||
struct attribute *attrs[TOTAL_ATTRS + 1];
|
||||
struct attribute_group attr_group;
|
||||
@ -88,10 +91,11 @@ struct temp_data {
|
||||
struct platform_data {
|
||||
struct device *hwmon_dev;
|
||||
u16 pkg_id;
|
||||
u16 cpu_map[NUM_REAL_CORES];
|
||||
int nr_cores;
|
||||
struct ida ida;
|
||||
struct cpumask cpumask;
|
||||
struct temp_data *core_data[MAX_CORE_DATA];
|
||||
struct temp_data *pkg_data;
|
||||
struct temp_data **core_data;
|
||||
struct device_attribute name_attr;
|
||||
};
|
||||
|
||||
@ -143,6 +147,11 @@ static const struct tjmax_model tjmax_model_table[] = {
|
||||
*/
|
||||
};
|
||||
|
||||
static bool is_pkg_temp_data(struct temp_data *tdata)
|
||||
{
|
||||
return tdata->index < 0;
|
||||
}
|
||||
|
||||
static int adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev)
|
||||
{
|
||||
/* The 100C is default for both mobile and non mobile CPUs */
|
||||
@ -332,11 +341,10 @@ static struct platform_device **zone_devices;
|
||||
static ssize_t show_label(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct platform_data *pdata = dev_get_drvdata(dev);
|
||||
struct temp_data *tdata = pdata->core_data[attr->index];
|
||||
struct temp_data *tdata = container_of(devattr, struct temp_data, sd_attrs[ATTR_LABEL]);
|
||||
|
||||
if (tdata->is_pkg_data)
|
||||
if (is_pkg_temp_data(tdata))
|
||||
return sprintf(buf, "Package id %u\n", pdata->pkg_id);
|
||||
|
||||
return sprintf(buf, "Core %u\n", tdata->cpu_core_id);
|
||||
@ -346,9 +354,8 @@ static ssize_t show_crit_alarm(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
u32 eax, edx;
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct platform_data *pdata = dev_get_drvdata(dev);
|
||||
struct temp_data *tdata = pdata->core_data[attr->index];
|
||||
struct temp_data *tdata = container_of(devattr, struct temp_data,
|
||||
sd_attrs[ATTR_CRIT_ALARM]);
|
||||
|
||||
mutex_lock(&tdata->update_lock);
|
||||
rdmsr_on_cpu(tdata->cpu, tdata->status_reg, &eax, &edx);
|
||||
@ -360,9 +367,7 @@ static ssize_t show_crit_alarm(struct device *dev,
|
||||
static ssize_t show_tjmax(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct platform_data *pdata = dev_get_drvdata(dev);
|
||||
struct temp_data *tdata = pdata->core_data[attr->index];
|
||||
struct temp_data *tdata = container_of(devattr, struct temp_data, sd_attrs[ATTR_TJMAX]);
|
||||
int tjmax;
|
||||
|
||||
mutex_lock(&tdata->update_lock);
|
||||
@ -375,9 +380,7 @@ static ssize_t show_tjmax(struct device *dev,
|
||||
static ssize_t show_ttarget(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct platform_data *pdata = dev_get_drvdata(dev);
|
||||
struct temp_data *tdata = pdata->core_data[attr->index];
|
||||
struct temp_data *tdata = container_of(devattr, struct temp_data, sd_attrs[ATTR_TTARGET]);
|
||||
int ttarget;
|
||||
|
||||
mutex_lock(&tdata->update_lock);
|
||||
@ -393,9 +396,7 @@ static ssize_t show_temp(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
u32 eax, edx;
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct platform_data *pdata = dev_get_drvdata(dev);
|
||||
struct temp_data *tdata = pdata->core_data[attr->index];
|
||||
struct temp_data *tdata = container_of(devattr, struct temp_data, sd_attrs[ATTR_TEMP]);
|
||||
int tjmax;
|
||||
|
||||
mutex_lock(&tdata->update_lock);
|
||||
@ -418,8 +419,7 @@ static ssize_t show_temp(struct device *dev,
|
||||
return sprintf(buf, "%d\n", tdata->temp);
|
||||
}
|
||||
|
||||
static int create_core_attrs(struct temp_data *tdata, struct device *dev,
|
||||
int index)
|
||||
static int create_core_attrs(struct temp_data *tdata, struct device *dev)
|
||||
{
|
||||
int i;
|
||||
static ssize_t (*const rd_ptr[TOTAL_ATTRS]) (struct device *dev,
|
||||
@ -436,16 +436,15 @@ static int create_core_attrs(struct temp_data *tdata, struct device *dev,
|
||||
* The attr number is always core id + 2
|
||||
* The Pkgtemp will always show up as temp1_*, if available
|
||||
*/
|
||||
int attr_no = tdata->is_pkg_data ? 1 : tdata->cpu_core_id + 2;
|
||||
int attr_no = is_pkg_temp_data(tdata) ? 1 : tdata->cpu_core_id + 2;
|
||||
|
||||
snprintf(tdata->attr_name[i], CORETEMP_NAME_LENGTH,
|
||||
"temp%d_%s", attr_no, suffixes[i]);
|
||||
sysfs_attr_init(&tdata->sd_attrs[i].dev_attr.attr);
|
||||
tdata->sd_attrs[i].dev_attr.attr.name = tdata->attr_name[i];
|
||||
tdata->sd_attrs[i].dev_attr.attr.mode = 0444;
|
||||
tdata->sd_attrs[i].dev_attr.show = rd_ptr[i];
|
||||
tdata->sd_attrs[i].index = index;
|
||||
tdata->attrs[i] = &tdata->sd_attrs[i].dev_attr.attr;
|
||||
sysfs_attr_init(&tdata->sd_attrs[i].attr);
|
||||
tdata->sd_attrs[i].attr.name = tdata->attr_name[i];
|
||||
tdata->sd_attrs[i].attr.mode = 0444;
|
||||
tdata->sd_attrs[i].show = rd_ptr[i];
|
||||
tdata->attrs[i] = &tdata->sd_attrs[i].attr;
|
||||
}
|
||||
tdata->attr_group.attrs = tdata->attrs;
|
||||
return sysfs_create_group(&dev->kobj, &tdata->attr_group);
|
||||
@ -477,17 +476,44 @@ static struct platform_device *coretemp_get_pdev(unsigned int cpu)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct temp_data *init_temp_data(unsigned int cpu, int pkg_flag)
|
||||
static struct temp_data *
|
||||
init_temp_data(struct platform_data *pdata, unsigned int cpu, int pkg_flag)
|
||||
{
|
||||
struct temp_data *tdata;
|
||||
|
||||
if (!pdata->core_data) {
|
||||
/*
|
||||
* TODO:
|
||||
* The information of actual possible cores in a package is broken for now.
|
||||
* Will replace hardcoded NUM_REAL_CORES with actual per package core count
|
||||
* when this information becomes available.
|
||||
*/
|
||||
pdata->nr_cores = NUM_REAL_CORES;
|
||||
pdata->core_data = kcalloc(pdata->nr_cores, sizeof(struct temp_data *),
|
||||
GFP_KERNEL);
|
||||
if (!pdata->core_data)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tdata = kzalloc(sizeof(struct temp_data), GFP_KERNEL);
|
||||
if (!tdata)
|
||||
return NULL;
|
||||
|
||||
if (pkg_flag) {
|
||||
pdata->pkg_data = tdata;
|
||||
/* Use tdata->index as indicator of package temp data */
|
||||
tdata->index = -1;
|
||||
} else {
|
||||
tdata->index = ida_alloc_max(&pdata->ida, pdata->nr_cores - 1, GFP_KERNEL);
|
||||
if (tdata->index < 0) {
|
||||
kfree(tdata);
|
||||
return NULL;
|
||||
}
|
||||
pdata->core_data[tdata->index] = tdata;
|
||||
}
|
||||
|
||||
tdata->status_reg = pkg_flag ? MSR_IA32_PACKAGE_THERM_STATUS :
|
||||
MSR_IA32_THERM_STATUS;
|
||||
tdata->is_pkg_data = pkg_flag;
|
||||
tdata->cpu = cpu;
|
||||
tdata->cpu_core_id = topology_core_id(cpu);
|
||||
tdata->attr_size = MAX_CORE_ATTRS;
|
||||
@ -495,6 +521,36 @@ static struct temp_data *init_temp_data(unsigned int cpu, int pkg_flag)
|
||||
return tdata;
|
||||
}
|
||||
|
||||
static void destroy_temp_data(struct platform_data *pdata, struct temp_data *tdata)
|
||||
{
|
||||
if (is_pkg_temp_data(tdata)) {
|
||||
pdata->pkg_data = NULL;
|
||||
kfree(pdata->core_data);
|
||||
pdata->core_data = NULL;
|
||||
pdata->nr_cores = 0;
|
||||
} else {
|
||||
pdata->core_data[tdata->index] = NULL;
|
||||
ida_free(&pdata->ida, tdata->index);
|
||||
}
|
||||
kfree(tdata);
|
||||
}
|
||||
|
||||
static struct temp_data *get_temp_data(struct platform_data *pdata, int cpu)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* cpu < 0 means get pkg temp_data */
|
||||
if (cpu < 0)
|
||||
return pdata->pkg_data;
|
||||
|
||||
for (i = 0; i < pdata->nr_cores; i++) {
|
||||
if (pdata->core_data[i] &&
|
||||
pdata->core_data[i]->cpu_core_id == topology_core_id(cpu))
|
||||
return pdata->core_data[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int create_core_data(struct platform_device *pdev, unsigned int cpu,
|
||||
int pkg_flag)
|
||||
{
|
||||
@ -502,37 +558,19 @@ static int create_core_data(struct platform_device *pdev, unsigned int cpu,
|
||||
struct platform_data *pdata = platform_get_drvdata(pdev);
|
||||
struct cpuinfo_x86 *c = &cpu_data(cpu);
|
||||
u32 eax, edx;
|
||||
int err, index;
|
||||
int err;
|
||||
|
||||
if (!housekeeping_cpu(cpu, HK_TYPE_MISC))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Get the index of tdata in pdata->core_data[]
|
||||
* tdata for package: pdata->core_data[1]
|
||||
* tdata for core: pdata->core_data[2] .. pdata->core_data[NUM_REAL_CORES + 1]
|
||||
*/
|
||||
if (pkg_flag) {
|
||||
index = PKG_SYSFS_ATTR_NO;
|
||||
} else {
|
||||
index = ida_alloc_max(&pdata->ida, NUM_REAL_CORES - 1, GFP_KERNEL);
|
||||
if (index < 0)
|
||||
return index;
|
||||
|
||||
pdata->cpu_map[index] = topology_core_id(cpu);
|
||||
index += BASE_SYSFS_ATTR_NO;
|
||||
}
|
||||
|
||||
tdata = init_temp_data(cpu, pkg_flag);
|
||||
if (!tdata) {
|
||||
err = -ENOMEM;
|
||||
goto ida_free;
|
||||
}
|
||||
tdata = init_temp_data(pdata, cpu, pkg_flag);
|
||||
if (!tdata)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Test if we can access the status register */
|
||||
err = rdmsr_safe_on_cpu(cpu, tdata->status_reg, &eax, &edx);
|
||||
if (err)
|
||||
goto exit_free;
|
||||
goto err;
|
||||
|
||||
/* Make sure tdata->tjmax is a valid indicator for dynamic/static tjmax */
|
||||
get_tjmax(tdata, &pdev->dev);
|
||||
@ -546,20 +584,15 @@ static int create_core_data(struct platform_device *pdev, unsigned int cpu,
|
||||
if (get_ttarget(tdata, &pdev->dev) >= 0)
|
||||
tdata->attr_size++;
|
||||
|
||||
pdata->core_data[index] = tdata;
|
||||
|
||||
/* Create sysfs interfaces */
|
||||
err = create_core_attrs(tdata, pdata->hwmon_dev, index);
|
||||
err = create_core_attrs(tdata, pdata->hwmon_dev);
|
||||
if (err)
|
||||
goto exit_free;
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
exit_free:
|
||||
pdata->core_data[index] = NULL;
|
||||
kfree(tdata);
|
||||
ida_free:
|
||||
if (!pkg_flag)
|
||||
ida_free(&pdata->ida, index - BASE_SYSFS_ATTR_NO);
|
||||
|
||||
err:
|
||||
destroy_temp_data(pdata, tdata);
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -570,10 +603,8 @@ coretemp_add_core(struct platform_device *pdev, unsigned int cpu, int pkg_flag)
|
||||
dev_err(&pdev->dev, "Adding Core %u failed\n", cpu);
|
||||
}
|
||||
|
||||
static void coretemp_remove_core(struct platform_data *pdata, int indx)
|
||||
static void coretemp_remove_core(struct platform_data *pdata, struct temp_data *tdata)
|
||||
{
|
||||
struct temp_data *tdata = pdata->core_data[indx];
|
||||
|
||||
/* if we errored on add then this is already gone */
|
||||
if (!tdata)
|
||||
return;
|
||||
@ -581,11 +612,7 @@ static void coretemp_remove_core(struct platform_data *pdata, int indx)
|
||||
/* Remove the sysfs attributes */
|
||||
sysfs_remove_group(&pdata->hwmon_dev->kobj, &tdata->attr_group);
|
||||
|
||||
kfree(pdata->core_data[indx]);
|
||||
pdata->core_data[indx] = NULL;
|
||||
|
||||
if (indx >= BASE_SYSFS_ATTR_NO)
|
||||
ida_free(&pdata->ida, indx - BASE_SYSFS_ATTR_NO);
|
||||
destroy_temp_data(pdata, tdata);
|
||||
}
|
||||
|
||||
static int coretemp_device_add(int zoneid)
|
||||
@ -698,7 +725,7 @@ static int coretemp_cpu_offline(unsigned int cpu)
|
||||
struct platform_device *pdev = coretemp_get_pdev(cpu);
|
||||
struct platform_data *pd;
|
||||
struct temp_data *tdata;
|
||||
int i, indx = -1, target;
|
||||
int target;
|
||||
|
||||
/* No need to tear down any interfaces for suspend */
|
||||
if (cpuhp_tasks_frozen)
|
||||
@ -709,18 +736,7 @@ static int coretemp_cpu_offline(unsigned int cpu)
|
||||
if (!pd->hwmon_dev)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < NUM_REAL_CORES; i++) {
|
||||
if (pd->cpu_map[i] == topology_core_id(cpu)) {
|
||||
indx = i + BASE_SYSFS_ATTR_NO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Too many cores and this core is not populated, just return */
|
||||
if (indx < 0)
|
||||
return 0;
|
||||
|
||||
tdata = pd->core_data[indx];
|
||||
tdata = get_temp_data(pd, cpu);
|
||||
|
||||
cpumask_clear_cpu(cpu, &pd->cpumask);
|
||||
|
||||
@ -731,7 +747,7 @@ static int coretemp_cpu_offline(unsigned int cpu)
|
||||
*/
|
||||
target = cpumask_any_and(&pd->cpumask, topology_sibling_cpumask(cpu));
|
||||
if (target >= nr_cpu_ids) {
|
||||
coretemp_remove_core(pd, indx);
|
||||
coretemp_remove_core(pd, tdata);
|
||||
} else if (tdata && tdata->cpu == cpu) {
|
||||
mutex_lock(&tdata->update_lock);
|
||||
tdata->cpu = target;
|
||||
@ -741,10 +757,10 @@ static int coretemp_cpu_offline(unsigned int cpu)
|
||||
/*
|
||||
* If all cores in this pkg are offline, remove the interface.
|
||||
*/
|
||||
tdata = pd->core_data[PKG_SYSFS_ATTR_NO];
|
||||
tdata = get_temp_data(pd, -1);
|
||||
if (cpumask_empty(&pd->cpumask)) {
|
||||
if (tdata)
|
||||
coretemp_remove_core(pd, PKG_SYSFS_ATTR_NO);
|
||||
coretemp_remove_core(pd, tdata);
|
||||
hwmon_device_unregister(pd->hwmon_dev);
|
||||
pd->hwmon_dev = NULL;
|
||||
return 0;
|
||||
|
@ -1450,10 +1450,15 @@ struct i8k_fan_control_data {
|
||||
};
|
||||
|
||||
enum i8k_fan_controls {
|
||||
I8K_FAN_30A3_31A3,
|
||||
I8K_FAN_34A3_35A3,
|
||||
};
|
||||
|
||||
static const struct i8k_fan_control_data i8k_fan_control_data[] __initconst = {
|
||||
[I8K_FAN_30A3_31A3] = {
|
||||
.manual_fan = 0x30a3,
|
||||
.auto_fan = 0x31a3,
|
||||
},
|
||||
[I8K_FAN_34A3_35A3] = {
|
||||
.manual_fan = 0x34a3,
|
||||
.auto_fan = 0x35a3,
|
||||
@ -1517,6 +1522,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 XPS 9315",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "XPS 9315"),
|
||||
},
|
||||
.driver_data = (void *)&i8k_fan_control_data[I8K_FAN_30A3_31A3],
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
|
@ -380,7 +380,6 @@ MODULE_DEVICE_TABLE(i2c, ds1621_id);
|
||||
|
||||
/* This is the driver that will be inserted */
|
||||
static struct i2c_driver ds1621_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "ds1621",
|
||||
},
|
||||
|
@ -241,7 +241,6 @@ MODULE_DEVICE_TABLE(i2c, ds620_id);
|
||||
|
||||
/* This is the driver that will be inserted */
|
||||
static struct i2c_driver ds620_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "ds620",
|
||||
},
|
||||
|
@ -385,7 +385,7 @@ static bool emc1403_regmap_is_volatile(struct device *dev, unsigned int reg)
|
||||
static const struct regmap_config emc1403_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.cache_type = REGCACHE_MAPLE,
|
||||
.volatile_reg = emc1403_regmap_is_volatile,
|
||||
};
|
||||
|
||||
|
@ -12,9 +12,6 @@
|
||||
#include <linux/platform_data/emc2305.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
static const unsigned short
|
||||
emc2305_normal_i2c[] = { 0x27, 0x2c, 0x2d, 0x2e, 0x2f, 0x4c, 0x4d, I2C_CLIENT_END };
|
||||
|
||||
#define EMC2305_REG_DRIVE_FAIL_STATUS 0x27
|
||||
#define EMC2305_REG_VENDOR 0xfe
|
||||
#define EMC2305_FAN_MAX 0xff
|
||||
@ -611,14 +608,12 @@ static void emc2305_remove(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static struct i2c_driver emc2305_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "emc2305",
|
||||
},
|
||||
.probe = emc2305_probe,
|
||||
.remove = emc2305_remove,
|
||||
.id_table = emc2305_ids,
|
||||
.address_list = emc2305_normal_i2c,
|
||||
};
|
||||
|
||||
module_i2c_driver(emc2305_driver);
|
||||
|
@ -510,6 +510,7 @@ static const char * const hwmon_in_attr_templates[] = {
|
||||
[hwmon_in_rated_min] = "in%d_rated_min",
|
||||
[hwmon_in_rated_max] = "in%d_rated_max",
|
||||
[hwmon_in_beep] = "in%d_beep",
|
||||
[hwmon_in_fault] = "in%d_fault",
|
||||
};
|
||||
|
||||
static const char * const hwmon_curr_attr_templates[] = {
|
||||
@ -586,6 +587,8 @@ static const char * const hwmon_humidity_attr_templates[] = {
|
||||
[hwmon_humidity_fault] = "humidity%d_fault",
|
||||
[hwmon_humidity_rated_min] = "humidity%d_rated_min",
|
||||
[hwmon_humidity_rated_max] = "humidity%d_rated_max",
|
||||
[hwmon_humidity_min_alarm] = "humidity%d_min_alarm",
|
||||
[hwmon_humidity_max_alarm] = "humidity%d_max_alarm",
|
||||
};
|
||||
|
||||
static const char * const hwmon_fan_attr_templates[] = {
|
||||
|
@ -589,7 +589,6 @@ MODULE_DEVICE_TABLE(of, ina209_of_match);
|
||||
|
||||
/* This is the driver that will be inserted */
|
||||
static struct i2c_driver ina209_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "ina209",
|
||||
.of_match_table = of_match_ptr(ina209_of_match),
|
||||
|
@ -629,7 +629,6 @@ static const struct of_device_id __maybe_unused ina238_of_match[] = {
|
||||
MODULE_DEVICE_TABLE(of, ina238_of_match);
|
||||
|
||||
static struct i2c_driver ina238_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "ina238",
|
||||
.of_match_table = of_match_ptr(ina238_of_match),
|
||||
|
@ -762,7 +762,7 @@ static const struct regmap_config ina3221_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 16,
|
||||
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.cache_type = REGCACHE_MAPLE,
|
||||
.volatile_table = &ina3221_volatile_table,
|
||||
};
|
||||
|
||||
|
@ -497,7 +497,7 @@ static const struct regmap_config jc42_regmap_config = {
|
||||
.writeable_reg = jc42_writable_reg,
|
||||
.readable_reg = jc42_readable_reg,
|
||||
.volatile_reg = jc42_volatile_reg,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.cache_type = REGCACHE_MAPLE,
|
||||
};
|
||||
|
||||
static int jc42_probe(struct i2c_client *client)
|
||||
|
@ -165,7 +165,7 @@ static bool lm83_regmap_is_volatile(struct device *dev, unsigned int reg)
|
||||
static const struct regmap_config lm83_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.cache_type = REGCACHE_MAPLE,
|
||||
.volatile_reg = lm83_regmap_is_volatile,
|
||||
.reg_read = lm83_regmap_reg_read,
|
||||
.reg_write = lm83_regmap_reg_write,
|
||||
|
1782
drivers/hwmon/ltc4282.c
Normal file
1782
drivers/hwmon/ltc4282.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -335,7 +335,6 @@ static const struct i2c_device_id max127_id[] = {
|
||||
MODULE_DEVICE_TABLE(i2c, max127_id);
|
||||
|
||||
static struct i2c_driver max127_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "max127",
|
||||
},
|
||||
|
@ -60,7 +60,7 @@ static const struct regmap_config regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.max_register = 0x5B,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.cache_type = REGCACHE_MAPLE,
|
||||
.volatile_reg = max31760_volatile_reg,
|
||||
};
|
||||
|
||||
@ -578,7 +578,6 @@ static DEFINE_SIMPLE_DEV_PM_OPS(max31760_pm_ops, max31760_suspend,
|
||||
max31760_resume);
|
||||
|
||||
static struct i2c_driver max31760_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "max31760",
|
||||
.of_match_table = max31760_of_match,
|
||||
|
@ -543,7 +543,6 @@ static const struct i2c_device_id max31790_id[] = {
|
||||
MODULE_DEVICE_TABLE(i2c, max31790_id);
|
||||
|
||||
static struct i2c_driver max31790_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.probe = max31790_probe,
|
||||
.driver = {
|
||||
.name = "max31790",
|
||||
|
@ -652,7 +652,6 @@ static const struct of_device_id max31827_of_match[] = {
|
||||
MODULE_DEVICE_TABLE(of, max31827_of_match);
|
||||
|
||||
static struct i2c_driver max31827_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "max31827",
|
||||
.of_match_table = max31827_of_match,
|
||||
|
@ -549,7 +549,6 @@ static const struct of_device_id __maybe_unused max6621_of_match[] = {
|
||||
MODULE_DEVICE_TABLE(of, max6621_of_match);
|
||||
|
||||
static struct i2c_driver max6621_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = MAX6621_DRV_NAME,
|
||||
.of_match_table = of_match_ptr(max6621_of_match),
|
||||
|
@ -780,7 +780,6 @@ static const struct of_device_id __maybe_unused max6697_of_match[] = {
|
||||
MODULE_DEVICE_TABLE(of, max6697_of_match);
|
||||
|
||||
static struct i2c_driver max6697_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "max6697",
|
||||
.of_match_table = of_match_ptr(max6697_of_match),
|
||||
|
@ -174,6 +174,7 @@ superio_exit(int ioreg)
|
||||
#define NCT6683_CUSTOMER_ID_MITAC 0xa0e
|
||||
#define NCT6683_CUSTOMER_ID_MSI 0x201
|
||||
#define NCT6683_CUSTOMER_ID_MSI2 0x200
|
||||
#define NCT6683_CUSTOMER_ID_MSI3 0x207
|
||||
#define NCT6683_CUSTOMER_ID_ASROCK 0xe2c
|
||||
#define NCT6683_CUSTOMER_ID_ASROCK2 0xe1b
|
||||
#define NCT6683_CUSTOMER_ID_ASROCK3 0x1631
|
||||
@ -1224,6 +1225,8 @@ static int nct6683_probe(struct platform_device *pdev)
|
||||
break;
|
||||
case NCT6683_CUSTOMER_ID_MSI2:
|
||||
break;
|
||||
case NCT6683_CUSTOMER_ID_MSI3:
|
||||
break;
|
||||
case NCT6683_CUSTOMER_ID_ASROCK:
|
||||
break;
|
||||
case NCT6683_CUSTOMER_ID_ASROCK2:
|
||||
|
@ -1051,7 +1051,7 @@ static bool nct7802_regmap_is_volatile(struct device *dev, unsigned int reg)
|
||||
static const struct regmap_config nct7802_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.cache_type = REGCACHE_MAPLE,
|
||||
.volatile_reg = nct7802_regmap_is_volatile,
|
||||
};
|
||||
|
||||
|
1008
drivers/hwmon/nzxt-kraken3.c
Normal file
1008
drivers/hwmon/nzxt-kraken3.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -241,7 +241,6 @@ static const struct of_device_id p8_i2c_occ_of_match[] = {
|
||||
MODULE_DEVICE_TABLE(of, p8_i2c_occ_of_match);
|
||||
|
||||
static struct i2c_driver p8_i2c_occ_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "occ-hwmon",
|
||||
.of_match_table = p8_i2c_occ_of_match,
|
||||
|
@ -43,6 +43,7 @@ enum oxp_board {
|
||||
aok_zoe_a1 = 1,
|
||||
aya_neo_2,
|
||||
aya_neo_air,
|
||||
aya_neo_air_plus_mendo,
|
||||
aya_neo_air_pro,
|
||||
aya_neo_geek,
|
||||
oxp_mini_amd,
|
||||
@ -98,6 +99,13 @@ static const struct dmi_system_id dmi_table[] = {
|
||||
},
|
||||
.driver_data = (void *)aya_neo_air,
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
|
||||
DMI_EXACT_MATCH(DMI_BOARD_NAME, "AB05-Mendocino"),
|
||||
},
|
||||
.driver_data = (void *)aya_neo_air_plus_mendo,
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
|
||||
@ -332,6 +340,7 @@ static int oxp_platform_read(struct device *dev, enum hwmon_sensor_types type,
|
||||
switch (board) {
|
||||
case aya_neo_2:
|
||||
case aya_neo_air:
|
||||
case aya_neo_air_plus_mendo:
|
||||
case aya_neo_air_pro:
|
||||
case aya_neo_geek:
|
||||
case oxp_mini_amd:
|
||||
@ -374,6 +383,7 @@ static int oxp_platform_write(struct device *dev, enum hwmon_sensor_types type,
|
||||
switch (board) {
|
||||
case aya_neo_2:
|
||||
case aya_neo_air:
|
||||
case aya_neo_air_plus_mendo:
|
||||
case aya_neo_air_pro:
|
||||
case aya_neo_geek:
|
||||
case oxp_mini_amd:
|
||||
|
@ -377,6 +377,15 @@ config SENSORS_MPQ7932
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called mpq7932.
|
||||
|
||||
config SENSORS_MPQ8785
|
||||
tristate "MPS MPQ8785"
|
||||
help
|
||||
If you say yes here you get hardware monitoring functionality support
|
||||
for power management IC MPS MPQ8785.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called mpq8785.
|
||||
|
||||
config SENSORS_PIM4328
|
||||
tristate "Flex PIM4328 and compatibles"
|
||||
help
|
||||
|
@ -39,6 +39,7 @@ obj-$(CONFIG_SENSORS_MP2975) += mp2975.o
|
||||
obj-$(CONFIG_SENSORS_MP5023) += mp5023.o
|
||||
obj-$(CONFIG_SENSORS_MP5990) += mp5990.o
|
||||
obj-$(CONFIG_SENSORS_MPQ7932) += mpq7932.o
|
||||
obj-$(CONFIG_SENSORS_MPQ8785) += mpq8785.o
|
||||
obj-$(CONFIG_SENSORS_PLI1209BC) += pli1209bc.o
|
||||
obj-$(CONFIG_SENSORS_PM6764TR) += pm6764tr.o
|
||||
obj-$(CONFIG_SENSORS_PXE1610) += pxe1610.o
|
||||
|
@ -63,7 +63,6 @@ static const struct of_device_id __maybe_unused ir36021_of_id[] = {
|
||||
MODULE_DEVICE_TABLE(of, ir36021_of_id);
|
||||
|
||||
static struct i2c_driver ir36021_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "ir36021",
|
||||
.of_match_table = of_match_ptr(ir36021_of_id),
|
||||
|
@ -22,7 +22,7 @@
|
||||
|
||||
#if IS_ENABLED(CONFIG_SENSORS_IR38064_REGULATOR)
|
||||
static const struct regulator_desc ir38064_reg_desc[] = {
|
||||
PMBUS_REGULATOR("vout", 0),
|
||||
PMBUS_REGULATOR_ONE("vout"),
|
||||
};
|
||||
#endif /* CONFIG_SENSORS_IR38064_REGULATOR */
|
||||
|
||||
|
@ -437,7 +437,7 @@ static int lm25066_write_word_data(struct i2c_client *client, int page, int reg,
|
||||
|
||||
#if IS_ENABLED(CONFIG_SENSORS_LM25066_REGULATOR)
|
||||
static const struct regulator_desc lm25066_reg_desc[] = {
|
||||
PMBUS_REGULATOR("vout", 0),
|
||||
PMBUS_REGULATOR_ONE("vout"),
|
||||
};
|
||||
#endif
|
||||
|
||||
|
90
drivers/hwmon/pmbus/mpq8785.c
Normal file
90
drivers/hwmon/pmbus/mpq8785.c
Normal file
@ -0,0 +1,90 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Driver for MPS MPQ8785 Step-Down Converter
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include "pmbus.h"
|
||||
|
||||
static int mpq8785_identify(struct i2c_client *client,
|
||||
struct pmbus_driver_info *info)
|
||||
{
|
||||
int vout_mode;
|
||||
|
||||
vout_mode = pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE);
|
||||
if (vout_mode < 0 || vout_mode == 0xff)
|
||||
return vout_mode < 0 ? vout_mode : -ENODEV;
|
||||
switch (vout_mode >> 5) {
|
||||
case 0:
|
||||
info->format[PSC_VOLTAGE_OUT] = linear;
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
info->format[PSC_VOLTAGE_OUT] = direct,
|
||||
info->m[PSC_VOLTAGE_OUT] = 64;
|
||||
info->b[PSC_VOLTAGE_OUT] = 0;
|
||||
info->R[PSC_VOLTAGE_OUT] = 1;
|
||||
break;
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
static struct pmbus_driver_info mpq8785_info = {
|
||||
.pages = 1,
|
||||
.format[PSC_VOLTAGE_IN] = direct,
|
||||
.format[PSC_CURRENT_OUT] = direct,
|
||||
.format[PSC_TEMPERATURE] = direct,
|
||||
.m[PSC_VOLTAGE_IN] = 4,
|
||||
.b[PSC_VOLTAGE_IN] = 0,
|
||||
.R[PSC_VOLTAGE_IN] = 1,
|
||||
.m[PSC_CURRENT_OUT] = 16,
|
||||
.b[PSC_CURRENT_OUT] = 0,
|
||||
.R[PSC_CURRENT_OUT] = 0,
|
||||
.m[PSC_TEMPERATURE] = 1,
|
||||
.b[PSC_TEMPERATURE] = 0,
|
||||
.R[PSC_TEMPERATURE] = 0,
|
||||
.func[0] =
|
||||
PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT |
|
||||
PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
|
||||
PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
|
||||
PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
|
||||
.identify = mpq8785_identify,
|
||||
};
|
||||
|
||||
static int mpq8785_probe(struct i2c_client *client)
|
||||
{
|
||||
return pmbus_do_probe(client, &mpq8785_info);
|
||||
};
|
||||
|
||||
static const struct i2c_device_id mpq8785_id[] = {
|
||||
{ "mpq8785", 0 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mpq8785_id);
|
||||
|
||||
static const struct of_device_id __maybe_unused mpq8785_of_match[] = {
|
||||
{ .compatible = "mps,mpq8785" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mpq8785_of_match);
|
||||
|
||||
static struct i2c_driver mpq8785_driver = {
|
||||
.driver = {
|
||||
.name = "mpq8785",
|
||||
.of_match_table = of_match_ptr(mpq8785_of_match),
|
||||
},
|
||||
.probe = mpq8785_probe,
|
||||
.id_table = mpq8785_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(mpq8785_driver);
|
||||
|
||||
MODULE_AUTHOR("Charles Hsu <ythsu0511@gmail.com>");
|
||||
MODULE_DESCRIPTION("PMBus driver for MPS MPQ8785");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(PMBUS);
|
@ -3188,7 +3188,7 @@ static int pmbus_regulator_notify(struct pmbus_data *data, int page, int event)
|
||||
|
||||
static int pmbus_write_smbalert_mask(struct i2c_client *client, u8 page, u8 reg, u8 val)
|
||||
{
|
||||
return pmbus_write_word_data(client, page, PMBUS_SMBALERT_MASK, reg | (val << 8));
|
||||
return _pmbus_write_word_data(client, page, PMBUS_SMBALERT_MASK, reg | (val << 8));
|
||||
}
|
||||
|
||||
static irqreturn_t pmbus_fault_handler(int irq, void *pdata)
|
||||
|
@ -15,7 +15,7 @@
|
||||
#include "pmbus.h"
|
||||
|
||||
static const struct regulator_desc __maybe_unused tda38640_reg_desc[] = {
|
||||
PMBUS_REGULATOR("vout", 0),
|
||||
PMBUS_REGULATOR_ONE("vout"),
|
||||
};
|
||||
|
||||
struct tda38640_data {
|
||||
|
@ -323,7 +323,6 @@ static const struct i2c_device_id powr1220_ids[] = {
|
||||
MODULE_DEVICE_TABLE(i2c, powr1220_ids);
|
||||
|
||||
static struct i2c_driver powr1220_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "powr1220",
|
||||
},
|
||||
|
667
drivers/hwmon/pt5161l.c
Normal file
667
drivers/hwmon/pt5161l.c
Normal file
@ -0,0 +1,667 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
/* Aries current average temp ADC code CSR */
|
||||
#define ARIES_CURRENT_AVG_TEMP_ADC_CSR 0x42c
|
||||
|
||||
/* Device Load check register */
|
||||
#define ARIES_CODE_LOAD_REG 0x605
|
||||
/* Value indicating FW was loaded properly, [3:1] = 3'b111 */
|
||||
#define ARIES_LOAD_CODE 0xe
|
||||
|
||||
/* Main Micro Heartbeat register */
|
||||
#define ARIES_MM_HEARTBEAT_ADDR 0x923
|
||||
|
||||
/* Reg offset to specify Address for MM assisted accesses */
|
||||
#define ARIES_MM_ASSIST_REG_ADDR_OFFSET 0xd99
|
||||
/* Reg offset to specify Command for MM assisted accesses */
|
||||
#define ARIES_MM_ASSIST_CMD_OFFSET 0xd9d
|
||||
/* Reg offset to MM SPARE 0 used specify Address[7:0] */
|
||||
#define ARIES_MM_ASSIST_SPARE_0_OFFSET 0xd9f
|
||||
/* Reg offset to MM SPARE 3 used specify Data Byte 0 */
|
||||
#define ARIES_MM_ASSIST_SPARE_3_OFFSET 0xda2
|
||||
/* Wide register reads */
|
||||
#define ARIES_MM_RD_WIDE_REG_2B 0x1d
|
||||
#define ARIES_MM_RD_WIDE_REG_3B 0x1e
|
||||
#define ARIES_MM_RD_WIDE_REG_4B 0x1f
|
||||
#define ARIES_MM_RD_WIDE_REG_5B 0x20
|
||||
|
||||
/* Time delay between checking MM status of EEPROM write (microseconds) */
|
||||
#define ARIES_MM_STATUS_TIME 5000
|
||||
|
||||
/* AL Main SRAM DMEM offset (A0) */
|
||||
#define AL_MAIN_SRAM_DMEM_OFFSET (64 * 1024)
|
||||
/* SRAM read command */
|
||||
#define AL_TG_RD_LOC_IND_SRAM 0x16
|
||||
|
||||
/* Offset for main micro FW info */
|
||||
#define ARIES_MAIN_MICRO_FW_INFO (96 * 1024 - 128)
|
||||
/* FW Info (Major) offset location in struct */
|
||||
#define ARIES_MM_FW_VERSION_MAJOR 0
|
||||
/* FW Info (Minor) offset location in struct */
|
||||
#define ARIES_MM_FW_VERSION_MINOR 1
|
||||
/* FW Info (Build no.) offset location in struct */
|
||||
#define ARIES_MM_FW_VERSION_BUILD 2
|
||||
|
||||
#define ARIES_TEMP_CAL_CODE_DEFAULT 84
|
||||
|
||||
/* Struct defining FW version loaded on an Aries device */
|
||||
struct pt5161l_fw_ver {
|
||||
u8 major;
|
||||
u8 minor;
|
||||
u16 build;
|
||||
};
|
||||
|
||||
/* Each client has this additional data */
|
||||
struct pt5161l_data {
|
||||
struct i2c_client *client;
|
||||
struct dentry *debugfs;
|
||||
struct pt5161l_fw_ver fw_ver;
|
||||
struct mutex lock; /* for atomic I2C transactions */
|
||||
bool init_done;
|
||||
bool code_load_okay; /* indicate if code load reg value is expected */
|
||||
bool mm_heartbeat_okay; /* indicate if Main Micro heartbeat is good */
|
||||
bool mm_wide_reg_access; /* MM assisted wide register access */
|
||||
};
|
||||
|
||||
static struct dentry *pt5161l_debugfs_dir;
|
||||
|
||||
/*
|
||||
* Write multiple data bytes to Aries over I2C
|
||||
*/
|
||||
static int pt5161l_write_block_data(struct pt5161l_data *data, u32 address,
|
||||
u8 len, u8 *val)
|
||||
{
|
||||
struct i2c_client *client = data->client;
|
||||
int ret;
|
||||
u8 remain_len = len;
|
||||
u8 xfer_len, curr_len;
|
||||
u8 buf[16];
|
||||
u8 cmd = 0x0F; /* [7]:pec_en, [4:2]:func, [1]:start, [0]:end */
|
||||
u8 config = 0x40; /* [6]:cfg_type, [4:1]:burst_len, [0]:address bit16 */
|
||||
|
||||
while (remain_len > 0) {
|
||||
if (remain_len > 4) {
|
||||
curr_len = 4;
|
||||
remain_len -= 4;
|
||||
} else {
|
||||
curr_len = remain_len;
|
||||
remain_len = 0;
|
||||
}
|
||||
|
||||
buf[0] = config | (curr_len - 1) << 1 | ((address >> 16) & 0x1);
|
||||
buf[1] = (address >> 8) & 0xff;
|
||||
buf[2] = address & 0xff;
|
||||
memcpy(&buf[3], val, curr_len);
|
||||
|
||||
xfer_len = 3 + curr_len;
|
||||
ret = i2c_smbus_write_block_data(client, cmd, xfer_len, buf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val += curr_len;
|
||||
address += curr_len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read multiple data bytes from Aries over I2C
|
||||
*/
|
||||
static int pt5161l_read_block_data(struct pt5161l_data *data, u32 address,
|
||||
u8 len, u8 *val)
|
||||
{
|
||||
struct i2c_client *client = data->client;
|
||||
int ret, tries;
|
||||
u8 remain_len = len;
|
||||
u8 curr_len;
|
||||
u8 wbuf[16], rbuf[24];
|
||||
u8 cmd = 0x08; /* [7]:pec_en, [4:2]:func, [1]:start, [0]:end */
|
||||
u8 config = 0x00; /* [6]:cfg_type, [4:1]:burst_len, [0]:address bit16 */
|
||||
|
||||
while (remain_len > 0) {
|
||||
if (remain_len > 16) {
|
||||
curr_len = 16;
|
||||
remain_len -= 16;
|
||||
} else {
|
||||
curr_len = remain_len;
|
||||
remain_len = 0;
|
||||
}
|
||||
|
||||
wbuf[0] = config | (curr_len - 1) << 1 |
|
||||
((address >> 16) & 0x1);
|
||||
wbuf[1] = (address >> 8) & 0xff;
|
||||
wbuf[2] = address & 0xff;
|
||||
|
||||
for (tries = 0; tries < 3; tries++) {
|
||||
ret = i2c_smbus_write_block_data(client, (cmd | 0x2), 3,
|
||||
wbuf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = i2c_smbus_read_block_data(client, (cmd | 0x1),
|
||||
rbuf);
|
||||
if (ret == curr_len)
|
||||
break;
|
||||
}
|
||||
if (tries >= 3)
|
||||
return ret;
|
||||
|
||||
memcpy(val, rbuf, curr_len);
|
||||
val += curr_len;
|
||||
address += curr_len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pt5161l_read_wide_reg(struct pt5161l_data *data, u32 address,
|
||||
u8 width, u8 *val)
|
||||
{
|
||||
int ret, tries;
|
||||
u8 buf[8];
|
||||
u8 status;
|
||||
|
||||
/*
|
||||
* Safely access wide registers using mailbox method to prevent
|
||||
* risking conflict with Aries firmware; otherwise fallback to
|
||||
* legacy, less secure method.
|
||||
*/
|
||||
if (data->mm_wide_reg_access) {
|
||||
buf[0] = address & 0xff;
|
||||
buf[1] = (address >> 8) & 0xff;
|
||||
buf[2] = (address >> 16) & 0x1;
|
||||
ret = pt5161l_write_block_data(data,
|
||||
ARIES_MM_ASSIST_SPARE_0_OFFSET,
|
||||
3, buf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Set command based on width */
|
||||
switch (width) {
|
||||
case 2:
|
||||
buf[0] = ARIES_MM_RD_WIDE_REG_2B;
|
||||
break;
|
||||
case 3:
|
||||
buf[0] = ARIES_MM_RD_WIDE_REG_3B;
|
||||
break;
|
||||
case 4:
|
||||
buf[0] = ARIES_MM_RD_WIDE_REG_4B;
|
||||
break;
|
||||
case 5:
|
||||
buf[0] = ARIES_MM_RD_WIDE_REG_5B;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = pt5161l_write_block_data(data, ARIES_MM_ASSIST_CMD_OFFSET,
|
||||
1, buf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
status = 0xff;
|
||||
for (tries = 0; tries < 100; tries++) {
|
||||
ret = pt5161l_read_block_data(data,
|
||||
ARIES_MM_ASSIST_CMD_OFFSET,
|
||||
1, &status);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (status == 0)
|
||||
break;
|
||||
|
||||
usleep_range(ARIES_MM_STATUS_TIME,
|
||||
ARIES_MM_STATUS_TIME + 1000);
|
||||
}
|
||||
if (status != 0)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
ret = pt5161l_read_block_data(data,
|
||||
ARIES_MM_ASSIST_SPARE_3_OFFSET,
|
||||
width, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
return pt5161l_read_block_data(data, address, width, val);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read multiple (up to eight) data bytes from micro SRAM over I2C
|
||||
*/
|
||||
static int
|
||||
pt5161l_read_block_data_main_micro_indirect(struct pt5161l_data *data,
|
||||
u32 address, u8 len, u8 *val)
|
||||
{
|
||||
int ret, tries;
|
||||
u8 buf[8];
|
||||
u8 i, status;
|
||||
u32 uind_offs = ARIES_MM_ASSIST_REG_ADDR_OFFSET;
|
||||
u32 eeprom_base, eeprom_addr;
|
||||
|
||||
/* No multi-byte indirect support here. Hence read a byte at a time */
|
||||
eeprom_base = address - AL_MAIN_SRAM_DMEM_OFFSET;
|
||||
for (i = 0; i < len; i++) {
|
||||
eeprom_addr = eeprom_base + i;
|
||||
buf[0] = eeprom_addr & 0xff;
|
||||
buf[1] = (eeprom_addr >> 8) & 0xff;
|
||||
buf[2] = (eeprom_addr >> 16) & 0xff;
|
||||
ret = pt5161l_write_block_data(data, uind_offs, 3, buf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
buf[0] = AL_TG_RD_LOC_IND_SRAM;
|
||||
ret = pt5161l_write_block_data(data, uind_offs + 4, 1, buf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
status = 0xff;
|
||||
for (tries = 0; tries < 255; tries++) {
|
||||
ret = pt5161l_read_block_data(data, uind_offs + 4, 1,
|
||||
&status);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (status == 0)
|
||||
break;
|
||||
}
|
||||
if (status != 0)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
ret = pt5161l_read_block_data(data, uind_offs + 3, 1, buf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val[i] = buf[0];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check firmware load status
|
||||
*/
|
||||
static int pt5161l_fw_load_check(struct pt5161l_data *data)
|
||||
{
|
||||
int ret;
|
||||
u8 buf[8];
|
||||
|
||||
ret = pt5161l_read_block_data(data, ARIES_CODE_LOAD_REG, 1, buf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (buf[0] < ARIES_LOAD_CODE) {
|
||||
dev_dbg(&data->client->dev,
|
||||
"Code Load reg unexpected. Not all modules are loaded %x\n",
|
||||
buf[0]);
|
||||
data->code_load_okay = false;
|
||||
} else {
|
||||
data->code_load_okay = true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check main micro heartbeat
|
||||
*/
|
||||
static int pt5161l_heartbeat_check(struct pt5161l_data *data)
|
||||
{
|
||||
int ret, tries;
|
||||
u8 buf[8];
|
||||
u8 heartbeat;
|
||||
bool hb_changed = false;
|
||||
|
||||
ret = pt5161l_read_block_data(data, ARIES_MM_HEARTBEAT_ADDR, 1, buf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
heartbeat = buf[0];
|
||||
for (tries = 0; tries < 100; tries++) {
|
||||
ret = pt5161l_read_block_data(data, ARIES_MM_HEARTBEAT_ADDR, 1,
|
||||
buf);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (buf[0] != heartbeat) {
|
||||
hb_changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
data->mm_heartbeat_okay = hb_changed;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check the status of firmware
|
||||
*/
|
||||
static int pt5161l_fwsts_check(struct pt5161l_data *data)
|
||||
{
|
||||
int ret;
|
||||
u8 buf[8];
|
||||
u8 major = 0, minor = 0;
|
||||
u16 build = 0;
|
||||
|
||||
ret = pt5161l_fw_load_check(data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = pt5161l_heartbeat_check(data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (data->code_load_okay && data->mm_heartbeat_okay) {
|
||||
ret = pt5161l_read_block_data_main_micro_indirect(data, ARIES_MAIN_MICRO_FW_INFO +
|
||||
ARIES_MM_FW_VERSION_MAJOR,
|
||||
1, &major);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = pt5161l_read_block_data_main_micro_indirect(data, ARIES_MAIN_MICRO_FW_INFO +
|
||||
ARIES_MM_FW_VERSION_MINOR,
|
||||
1, &minor);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = pt5161l_read_block_data_main_micro_indirect(data, ARIES_MAIN_MICRO_FW_INFO +
|
||||
ARIES_MM_FW_VERSION_BUILD,
|
||||
2, buf);
|
||||
if (ret)
|
||||
return ret;
|
||||
build = buf[1] << 8 | buf[0];
|
||||
}
|
||||
data->fw_ver.major = major;
|
||||
data->fw_ver.minor = minor;
|
||||
data->fw_ver.build = build;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pt5161l_fw_is_at_least(struct pt5161l_data *data, u8 major, u8 minor,
|
||||
u16 build)
|
||||
{
|
||||
u32 ver = major << 24 | minor << 16 | build;
|
||||
u32 curr_ver = data->fw_ver.major << 24 | data->fw_ver.minor << 16 |
|
||||
data->fw_ver.build;
|
||||
|
||||
if (curr_ver >= ver)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int pt5161l_init_dev(struct pt5161l_data *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
ret = pt5161l_fwsts_check(data);
|
||||
mutex_unlock(&data->lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Firmware 2.2.0 enables safe access to wide registers */
|
||||
if (pt5161l_fw_is_at_least(data, 2, 2, 0))
|
||||
data->mm_wide_reg_access = true;
|
||||
|
||||
data->init_done = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pt5161l_read(struct device *dev, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel, long *val)
|
||||
{
|
||||
struct pt5161l_data *data = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
u8 buf[8];
|
||||
long adc_code;
|
||||
|
||||
switch (attr) {
|
||||
case hwmon_temp_input:
|
||||
if (!data->init_done) {
|
||||
ret = pt5161l_init_dev(data);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
ret = pt5161l_read_wide_reg(data,
|
||||
ARIES_CURRENT_AVG_TEMP_ADC_CSR, 4,
|
||||
buf);
|
||||
mutex_unlock(&data->lock);
|
||||
if (ret) {
|
||||
dev_dbg(dev, "Read adc_code failed %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
adc_code = buf[3] << 24 | buf[2] << 16 | buf[1] << 8 | buf[0];
|
||||
if (adc_code == 0 || adc_code >= 0x3ff) {
|
||||
dev_dbg(dev, "Invalid adc_code %lx\n", adc_code);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
*val = 110000 +
|
||||
((adc_code - (ARIES_TEMP_CAL_CODE_DEFAULT + 250)) *
|
||||
-320);
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static umode_t pt5161l_is_visible(const void *data,
|
||||
enum hwmon_sensor_types type, u32 attr,
|
||||
int channel)
|
||||
{
|
||||
switch (attr) {
|
||||
case hwmon_temp_input:
|
||||
return 0444;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct hwmon_channel_info *pt5161l_info[] = {
|
||||
HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct hwmon_ops pt5161l_hwmon_ops = {
|
||||
.is_visible = pt5161l_is_visible,
|
||||
.read = pt5161l_read,
|
||||
};
|
||||
|
||||
static const struct hwmon_chip_info pt5161l_chip_info = {
|
||||
.ops = &pt5161l_hwmon_ops,
|
||||
.info = pt5161l_info,
|
||||
};
|
||||
|
||||
static ssize_t pt5161l_debugfs_read_fw_ver(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct pt5161l_data *data = file->private_data;
|
||||
int ret;
|
||||
char ver[32];
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
ret = pt5161l_fwsts_check(data);
|
||||
mutex_unlock(&data->lock);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = snprintf(ver, sizeof(ver), "%u.%u.%u\n", data->fw_ver.major,
|
||||
data->fw_ver.minor, data->fw_ver.build);
|
||||
|
||||
return simple_read_from_buffer(buf, count, ppos, ver, ret);
|
||||
}
|
||||
|
||||
static const struct file_operations pt5161l_debugfs_ops_fw_ver = {
|
||||
.read = pt5161l_debugfs_read_fw_ver,
|
||||
.open = simple_open,
|
||||
};
|
||||
|
||||
static ssize_t pt5161l_debugfs_read_fw_load_sts(struct file *file,
|
||||
char __user *buf, size_t count,
|
||||
loff_t *ppos)
|
||||
{
|
||||
struct pt5161l_data *data = file->private_data;
|
||||
int ret;
|
||||
bool status = false;
|
||||
char health[16];
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
ret = pt5161l_fw_load_check(data);
|
||||
mutex_unlock(&data->lock);
|
||||
if (ret == 0)
|
||||
status = data->code_load_okay;
|
||||
|
||||
ret = snprintf(health, sizeof(health), "%s\n",
|
||||
status ? "normal" : "abnormal");
|
||||
|
||||
return simple_read_from_buffer(buf, count, ppos, health, ret);
|
||||
}
|
||||
|
||||
static const struct file_operations pt5161l_debugfs_ops_fw_load_sts = {
|
||||
.read = pt5161l_debugfs_read_fw_load_sts,
|
||||
.open = simple_open,
|
||||
};
|
||||
|
||||
static ssize_t pt5161l_debugfs_read_hb_sts(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct pt5161l_data *data = file->private_data;
|
||||
int ret;
|
||||
bool status = false;
|
||||
char health[16];
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
ret = pt5161l_heartbeat_check(data);
|
||||
mutex_unlock(&data->lock);
|
||||
if (ret == 0)
|
||||
status = data->mm_heartbeat_okay;
|
||||
|
||||
ret = snprintf(health, sizeof(health), "%s\n",
|
||||
status ? "normal" : "abnormal");
|
||||
|
||||
return simple_read_from_buffer(buf, count, ppos, health, ret);
|
||||
}
|
||||
|
||||
static const struct file_operations pt5161l_debugfs_ops_hb_sts = {
|
||||
.read = pt5161l_debugfs_read_hb_sts,
|
||||
.open = simple_open,
|
||||
};
|
||||
|
||||
static int pt5161l_init_debugfs(struct pt5161l_data *data)
|
||||
{
|
||||
data->debugfs = debugfs_create_dir(dev_name(&data->client->dev),
|
||||
pt5161l_debugfs_dir);
|
||||
|
||||
debugfs_create_file("fw_ver", 0444, data->debugfs, data,
|
||||
&pt5161l_debugfs_ops_fw_ver);
|
||||
|
||||
debugfs_create_file("fw_load_status", 0444, data->debugfs, data,
|
||||
&pt5161l_debugfs_ops_fw_load_sts);
|
||||
|
||||
debugfs_create_file("heartbeat_status", 0444, data->debugfs, data,
|
||||
&pt5161l_debugfs_ops_hb_sts);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pt5161l_probe(struct i2c_client *client)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct device *hwmon_dev;
|
||||
struct pt5161l_data *data;
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->client = client;
|
||||
mutex_init(&data->lock);
|
||||
pt5161l_init_dev(data);
|
||||
dev_set_drvdata(dev, data);
|
||||
|
||||
hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
|
||||
data,
|
||||
&pt5161l_chip_info,
|
||||
NULL);
|
||||
|
||||
pt5161l_init_debugfs(data);
|
||||
|
||||
return PTR_ERR_OR_ZERO(hwmon_dev);
|
||||
}
|
||||
|
||||
static void pt5161l_remove(struct i2c_client *client)
|
||||
{
|
||||
struct pt5161l_data *data = i2c_get_clientdata(client);
|
||||
|
||||
debugfs_remove_recursive(data->debugfs);
|
||||
}
|
||||
|
||||
static const struct of_device_id __maybe_unused pt5161l_of_match[] = {
|
||||
{ .compatible = "asteralabs,pt5161l" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pt5161l_of_match);
|
||||
|
||||
static const struct acpi_device_id __maybe_unused pt5161l_acpi_match[] = {
|
||||
{ "PT5161L", 0 },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, pt5161l_acpi_match);
|
||||
|
||||
static const struct i2c_device_id pt5161l_id[] = {
|
||||
{ "pt5161l", 0 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, pt5161l_id);
|
||||
|
||||
static struct i2c_driver pt5161l_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "pt5161l",
|
||||
.of_match_table = of_match_ptr(pt5161l_of_match),
|
||||
.acpi_match_table = ACPI_PTR(pt5161l_acpi_match),
|
||||
},
|
||||
.probe = pt5161l_probe,
|
||||
.remove = pt5161l_remove,
|
||||
.id_table = pt5161l_id,
|
||||
};
|
||||
|
||||
static int __init pt5161l_init(void)
|
||||
{
|
||||
pt5161l_debugfs_dir = debugfs_create_dir("pt5161l", NULL);
|
||||
return i2c_add_driver(&pt5161l_driver);
|
||||
}
|
||||
|
||||
static void __exit pt5161l_exit(void)
|
||||
{
|
||||
i2c_del_driver(&pt5161l_driver);
|
||||
debugfs_remove_recursive(pt5161l_debugfs_dir);
|
||||
}
|
||||
|
||||
module_init(pt5161l_init);
|
||||
module_exit(pt5161l_exit);
|
||||
|
||||
MODULE_AUTHOR("Cosmo Chou <cosmo.chou@quantatw.com>");
|
||||
MODULE_DESCRIPTION("Hwmon driver for Astera Labs Aries PCIe retimer");
|
||||
MODULE_LICENSE("GPL");
|
@ -342,7 +342,6 @@ static const struct of_device_id __maybe_unused sbrmi_of_match[] = {
|
||||
MODULE_DEVICE_TABLE(of, sbrmi_of_match);
|
||||
|
||||
static struct i2c_driver sbrmi_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "sbrmi",
|
||||
.of_match_table = of_match_ptr(sbrmi_of_match),
|
||||
|
@ -232,7 +232,6 @@ static const struct of_device_id __maybe_unused sbtsi_of_match[] = {
|
||||
MODULE_DEVICE_TABLE(of, sbtsi_of_match);
|
||||
|
||||
static struct i2c_driver sbtsi_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "sbtsi",
|
||||
.of_match_table = of_match_ptr(sbtsi_of_match),
|
||||
|
@ -116,7 +116,7 @@ static const struct regmap_config sch5627_regmap_config = {
|
||||
.val_bits = 8,
|
||||
.wr_table = &sch5627_tunables_table,
|
||||
.rd_table = &sch5627_tunables_table,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.cache_type = REGCACHE_MAPLE,
|
||||
.use_single_read = true,
|
||||
.use_single_write = true,
|
||||
.can_sleep = true,
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
#include <asm/page.h>
|
||||
#include <linux/crc8.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/hwmon.h>
|
||||
@ -41,6 +42,9 @@ static const unsigned char sht3x_cmd_heater_off[] = { 0x30, 0x66 };
|
||||
/* other commands */
|
||||
static const unsigned char sht3x_cmd_read_status_reg[] = { 0xf3, 0x2d };
|
||||
static const unsigned char sht3x_cmd_clear_status_reg[] = { 0x30, 0x41 };
|
||||
static const unsigned char sht3x_cmd_read_serial_number[] = { 0x37, 0x80 };
|
||||
|
||||
static struct dentry *debugfs;
|
||||
|
||||
/* delays for single-shot mode i2c commands, both in us */
|
||||
#define SHT3X_SINGLE_WAIT_TIME_HPM 15000
|
||||
@ -163,12 +167,14 @@ struct sht3x_data {
|
||||
enum sht3x_chips chip_id;
|
||||
struct mutex i2c_lock; /* lock for sending i2c commands */
|
||||
struct mutex data_lock; /* lock for updating driver data */
|
||||
struct dentry *sensor_dir;
|
||||
|
||||
u8 mode;
|
||||
const unsigned char *command;
|
||||
u32 wait_time; /* in us*/
|
||||
unsigned long last_update; /* last update in periodic mode*/
|
||||
enum sht3x_repeatability repeatability;
|
||||
u32 serial_number;
|
||||
|
||||
/*
|
||||
* cached values for temperature and humidity and limits
|
||||
@ -831,6 +837,40 @@ static int sht3x_write(struct device *dev, enum hwmon_sensor_types type,
|
||||
}
|
||||
}
|
||||
|
||||
static void sht3x_debugfs_init(struct sht3x_data *data)
|
||||
{
|
||||
char name[32];
|
||||
|
||||
snprintf(name, sizeof(name), "i2c%u-%02x",
|
||||
data->client->adapter->nr, data->client->addr);
|
||||
data->sensor_dir = debugfs_create_dir(name, debugfs);
|
||||
debugfs_create_u32("serial_number", 0444,
|
||||
data->sensor_dir, &data->serial_number);
|
||||
}
|
||||
|
||||
static void sht3x_debugfs_remove(void *sensor_dir)
|
||||
{
|
||||
debugfs_remove_recursive(sensor_dir);
|
||||
}
|
||||
|
||||
static int sht3x_serial_number_read(struct sht3x_data *data)
|
||||
{
|
||||
int ret;
|
||||
char buffer[SHT3X_RESPONSE_LENGTH];
|
||||
struct i2c_client *client = data->client;
|
||||
|
||||
ret = sht3x_read_from_command(client, data,
|
||||
sht3x_cmd_read_serial_number,
|
||||
buffer,
|
||||
SHT3X_RESPONSE_LENGTH, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
data->serial_number = (buffer[0] << 24) | (buffer[1] << 16) |
|
||||
(buffer[3] << 8) | buffer[4];
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct hwmon_ops sht3x_ops = {
|
||||
.is_visible = sht3x_is_visible,
|
||||
.read = sht3x_read,
|
||||
@ -899,6 +939,18 @@ static int sht3x_probe(struct i2c_client *client)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = sht3x_serial_number_read(data);
|
||||
if (ret) {
|
||||
dev_dbg(dev, "unable to read serial number\n");
|
||||
} else {
|
||||
sht3x_debugfs_init(data);
|
||||
ret = devm_add_action_or_reset(dev,
|
||||
sht3x_debugfs_remove,
|
||||
data->sensor_dir);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
hwmon_dev = devm_hwmon_device_register_with_info(dev,
|
||||
client->name,
|
||||
data,
|
||||
@ -917,7 +969,19 @@ static struct i2c_driver sht3x_i2c_driver = {
|
||||
.id_table = sht3x_ids,
|
||||
};
|
||||
|
||||
module_i2c_driver(sht3x_i2c_driver);
|
||||
static int __init sht3x_init(void)
|
||||
{
|
||||
debugfs = debugfs_create_dir("sht3x", NULL);
|
||||
return i2c_add_driver(&sht3x_i2c_driver);
|
||||
}
|
||||
module_init(sht3x_init);
|
||||
|
||||
static void __exit sht3x_cleanup(void)
|
||||
{
|
||||
debugfs_remove_recursive(debugfs);
|
||||
i2c_del_driver(&sht3x_i2c_driver);
|
||||
}
|
||||
module_exit(sht3x_cleanup);
|
||||
|
||||
MODULE_AUTHOR("David Frey <david.frey@sensirion.com>");
|
||||
MODULE_AUTHOR("Pascal Sachs <pascal.sachs@sensirion.com>");
|
||||
|
@ -153,13 +153,9 @@ static inline s8 TEMP_TO_REG(long val)
|
||||
}
|
||||
|
||||
/*
|
||||
* FAN DIV: 1, 2, 4, or 8 (defaults to 2)
|
||||
* REG: 0, 1, 2, or 3 (respectively) (defaults to 1)
|
||||
* FAN DIV: 1, 2, 4, or 8
|
||||
* REG: 0, 1, 2, or 3 (respectively)
|
||||
*/
|
||||
static inline u8 DIV_TO_REG(int val)
|
||||
{
|
||||
return val == 8 ? 3 : val == 4 ? 2 : val == 1 ? 0 : 1;
|
||||
}
|
||||
#define DIV_FROM_REG(val) (1 << (val))
|
||||
|
||||
/*
|
||||
|
91
drivers/hwmon/surface_fan.c
Normal file
91
drivers/hwmon/surface_fan.c
Normal file
@ -0,0 +1,91 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Surface Fan driver for Surface System Aggregator Module. It provides access
|
||||
* to the fan's rpm through the hwmon system.
|
||||
*
|
||||
* Copyright (C) 2023 Ivor Wanders <ivor@iwanders.net>
|
||||
*/
|
||||
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/surface_aggregator/device.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
// SSAM
|
||||
SSAM_DEFINE_SYNC_REQUEST_CL_R(__ssam_fan_rpm_get, __le16, {
|
||||
.target_category = SSAM_SSH_TC_FAN,
|
||||
.command_id = 0x01,
|
||||
});
|
||||
|
||||
// hwmon
|
||||
static umode_t surface_fan_hwmon_is_visible(const void *drvdata,
|
||||
enum hwmon_sensor_types type, u32 attr,
|
||||
int channel)
|
||||
{
|
||||
return 0444;
|
||||
}
|
||||
|
||||
static int surface_fan_hwmon_read(struct device *dev,
|
||||
enum hwmon_sensor_types type, u32 attr,
|
||||
int channel, long *val)
|
||||
{
|
||||
struct ssam_device *sdev = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
__le16 value;
|
||||
|
||||
ret = __ssam_fan_rpm_get(sdev, &value);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = le16_to_cpu(value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct hwmon_channel_info *const surface_fan_info[] = {
|
||||
HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT),
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct hwmon_ops surface_fan_hwmon_ops = {
|
||||
.is_visible = surface_fan_hwmon_is_visible,
|
||||
.read = surface_fan_hwmon_read,
|
||||
};
|
||||
|
||||
static const struct hwmon_chip_info surface_fan_chip_info = {
|
||||
.ops = &surface_fan_hwmon_ops,
|
||||
.info = surface_fan_info,
|
||||
};
|
||||
|
||||
static int surface_fan_probe(struct ssam_device *sdev)
|
||||
{
|
||||
struct device *hdev;
|
||||
|
||||
hdev = devm_hwmon_device_register_with_info(&sdev->dev,
|
||||
"surface_fan", sdev,
|
||||
&surface_fan_chip_info,
|
||||
NULL);
|
||||
|
||||
return PTR_ERR_OR_ZERO(hdev);
|
||||
}
|
||||
|
||||
static const struct ssam_device_id ssam_fan_match[] = {
|
||||
{ SSAM_SDEV(FAN, SAM, 0x01, 0x01) },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(ssam, ssam_fan_match);
|
||||
|
||||
static struct ssam_device_driver surface_fan = {
|
||||
.probe = surface_fan_probe,
|
||||
.match_table = ssam_fan_match,
|
||||
.driver = {
|
||||
.name = "surface_fan",
|
||||
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||
},
|
||||
};
|
||||
module_ssam_device_driver(surface_fan);
|
||||
|
||||
MODULE_AUTHOR("Ivor Wanders <ivor@iwanders.net>");
|
||||
MODULE_DESCRIPTION("Fan Driver for Surface System Aggregator Module");
|
||||
MODULE_LICENSE("GPL");
|
@ -256,7 +256,7 @@ static int tmp401_reg_write(void *context, unsigned int reg, unsigned int val)
|
||||
static const struct regmap_config tmp401_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 16,
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.cache_type = REGCACHE_MAPLE,
|
||||
.volatile_reg = tmp401_regmap_is_volatile,
|
||||
.reg_read = tmp401_reg_read,
|
||||
.reg_write = tmp401_reg_write,
|
||||
|
@ -290,7 +290,6 @@ static int w83773_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static struct i2c_driver w83773_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "w83773g",
|
||||
.of_match_table = of_match_ptr(w83773_of_match),
|
||||
|
@ -141,6 +141,7 @@ enum hwmon_in_attributes {
|
||||
hwmon_in_rated_min,
|
||||
hwmon_in_rated_max,
|
||||
hwmon_in_beep,
|
||||
hwmon_in_fault,
|
||||
};
|
||||
|
||||
#define HWMON_I_ENABLE BIT(hwmon_in_enable)
|
||||
@ -162,6 +163,7 @@ enum hwmon_in_attributes {
|
||||
#define HWMON_I_RATED_MIN BIT(hwmon_in_rated_min)
|
||||
#define HWMON_I_RATED_MAX BIT(hwmon_in_rated_max)
|
||||
#define HWMON_I_BEEP BIT(hwmon_in_beep)
|
||||
#define HWMON_I_FAULT BIT(hwmon_in_fault)
|
||||
|
||||
enum hwmon_curr_attributes {
|
||||
hwmon_curr_enable,
|
||||
@ -293,6 +295,8 @@ enum hwmon_humidity_attributes {
|
||||
hwmon_humidity_fault,
|
||||
hwmon_humidity_rated_min,
|
||||
hwmon_humidity_rated_max,
|
||||
hwmon_humidity_min_alarm,
|
||||
hwmon_humidity_max_alarm,
|
||||
};
|
||||
|
||||
#define HWMON_H_ENABLE BIT(hwmon_humidity_enable)
|
||||
@ -306,6 +310,8 @@ enum hwmon_humidity_attributes {
|
||||
#define HWMON_H_FAULT BIT(hwmon_humidity_fault)
|
||||
#define HWMON_H_RATED_MIN BIT(hwmon_humidity_rated_min)
|
||||
#define HWMON_H_RATED_MAX BIT(hwmon_humidity_rated_max)
|
||||
#define HWMON_H_MIN_ALARM BIT(hwmon_humidity_min_alarm)
|
||||
#define HWMON_H_MAX_ALARM BIT(hwmon_humidity_max_alarm)
|
||||
|
||||
enum hwmon_fan_attributes {
|
||||
hwmon_fan_enable,
|
||||
@ -425,12 +431,12 @@ struct hwmon_channel_info {
|
||||
const u32 *config;
|
||||
};
|
||||
|
||||
#define HWMON_CHANNEL_INFO(stype, ...) \
|
||||
(&(struct hwmon_channel_info) { \
|
||||
.type = hwmon_##stype, \
|
||||
.config = (u32 []) { \
|
||||
__VA_ARGS__, 0 \
|
||||
} \
|
||||
#define HWMON_CHANNEL_INFO(stype, ...) \
|
||||
(&(const struct hwmon_channel_info) { \
|
||||
.type = hwmon_##stype, \
|
||||
.config = (const u32 []) { \
|
||||
__VA_ARGS__, 0 \
|
||||
} \
|
||||
})
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user