mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-22 02:17:36 -04:00
Merge tag 'hwmon-for-v7.2' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging
Pull hwmon updates from Guenter Roeck:
"New drivers for the following chips:
- Analog Devices LTC4283 Swap Controller
- Analog Devices MAX20830
- Analog Devices MAX20860A
- ARCTIC Fan Controller
- Delta E50SN12051
- Luxshare LX1308
- Microchip EMC1812/13/14/15/33
- Monolithic MP2985
- Murata D1U74T PSU
New chip support added to existing drivers:
- asus-ec-sensors: Support for ROG MAXIMUS Z790 EXTREME, ROG STRIX
B850-E GAMING WIFI, and ROG STRIX B650E-E GAMING WIFI
- dell-smm: Add Dell Latitude 7530 to fan control whitelist
- nct6683: Support for ASRock Z890 Pro-A
- pmbus: Support for Flex BMR316, BMR321, BMR350 and BMR351
- pmbus/max34440: Support for ADPM12250
- pmbus/xdp720: Support for Infineon xdp730, and fix driver issues
reported by Sashiko
New functionality:
- Add support for update_interval_us chip attribute, and support it
in ina238 driver
- Add support for guard() and scoped_guard() for subsystem locks, and
use it in adt7411, ina2xx, and lm90 drivers
- emc2305: Support configurable fan PWM at shutdown
- lm63: Expose PWM frequency and LUT hysteresis as writable
- lm75: Support active-high alert polarity
- nct7802: Add time step attributes for tweaking responsiveness
- pmbus/adm1266: Add rtc debugfs entries for rtc, powerup_counter,
clear_blackbox, and firmware_revision
- raspberrypi: Fix delayed-work teardown race, add voltage input
support as well as voltage domain IDs
- mcp9982: Add support for reporting external diode faults
Miscellaneous bug fixes, changes and improvements:
- Use named initializers for platform_device_id arrays and
i2c_device_data, and remove unused driver data
- Various drivers: Move MODULE_DEVICE_TABLE next to the table itself
- ads7871: Convert to hwmon_device_register_with_info(), and use
DMA-safe buffer for SPI writes
- adt7411: document supported sysfs attributes
- adt7462: Add of_match_table to support devicetree
- adt7475: Add explicit header include
- coretemp; Fix outdated documentation, coding style issues, and
replace hardcoded core count with dynamic value
- cros_ec: Drop unused assignment of platform_device_id driver data
- emc2305: Fix fan channel index handling
- gpd-fan: Reject EC PWM value 0 as invalid, fix race condition
between device removal and sysfs access, upgrade log level from
warn to err for platform device creation failure, initialize EC
before registering hwmon device, drop global driver data and use
per-device allocation
- htu31: document debugfs serial_number
- ina238: Add support for samples and update_interval
- it87: Clamp negative values to zero in set_fan()
- lm75: Add explicit header include, Add explicit default cases in
lm75_is_visible(), and add section for sysfs interface to
documentation
- pmbus/lm25066: Fix PMBus coefficients for LM5064/5066/5066i
- tmp102: Use device_property_read_string API
- tmp401: Read "ti,n-factor" as signed
- Convert zyxel,nsa320-mcu to DT schema"
* tag 'hwmon-for-v7.2' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (81 commits)
hwmon: tmp401: Read "ti,n-factor" as signed
hwmon: (pmbus/lm25066) Fix PMBus coefficients for LM5064/5066/5066i
hwmon: (gpd-fan) Reject EC PWM value 0 as invalid
hwmon: (dell-smm) Add Dell Latitude 7530 to fan control whitelist
hwmon: temperature: add support for EMC1812
dt-bindings: hwmon: temperature: add support for EMC1812
hwmon: (gpd-fan): fix race condition between device removal and sysfs access
hwmon: (gpd-fan): upgrade log level from warn to err for platform device creation failure
hwmon: (gpd-fan): Initialize EC before registering hwmon device
hwmon: (gpd-fan): drop global driver data and use per-device allocation
hwmon: (pmbus/max34440): add support adpm12250
hwmon: (ina238) Add update_interval_us attribute
hwmon: Add update_interval_us chip attribute
hwmon: (ina238) Add support for samples and update_interval
gpio: gpio-ltc4283: Add support for the LTC4283 Swap Controller
hwmon: ltc4283: Add support for the LTC4283 Swap Controller
dt-bindings: hwmon: Document the LTC4283 Swap Controller
hwmon: (pmbus/xdp720) Fix driver issues xdp720/730
hwmon: (pmbus/xdp720) Add support for efuse xdp730
dt-bindings: hwmon/pmbus: Add Infineon xdp730
...
This commit is contained in:
@@ -27,6 +27,20 @@ Description:
|
||||
Some devices have a variable update rate or interval.
|
||||
This attribute can be used to change it to the desired value.
|
||||
|
||||
What: /sys/class/hwmon/hwmonX/update_interval_us
|
||||
Description:
|
||||
The interval at which the chip will update readings,
|
||||
expressed in microseconds.
|
||||
Unit: microsecond
|
||||
|
||||
RW
|
||||
|
||||
Some devices have a variable update rate or interval and
|
||||
require finer-than-millisecond control.
|
||||
This attribute can be used to change it to the desired value.
|
||||
Drivers implementing this attribute should also implement
|
||||
update_interval for millisecond-based userspace interfaces.
|
||||
|
||||
What: /sys/class/hwmon/hwmonX/inY_min
|
||||
Description:
|
||||
Voltage min value.
|
||||
|
||||
272
Documentation/devicetree/bindings/hwmon/adi,ltc4283.yaml
Normal file
272
Documentation/devicetree/bindings/hwmon/adi,ltc4283.yaml
Normal file
@@ -0,0 +1,272 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/hwmon/adi,ltc4283.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: LTC4283 Negative Voltage Hot Swap Controller
|
||||
|
||||
maintainers:
|
||||
- Nuno Sá <nuno.sa@analog.com>
|
||||
|
||||
description: |
|
||||
The LTC4283 negative voltage hot swap controller drives an external N-channel
|
||||
MOSFET to allow a board to be safely inserted and removed from a live
|
||||
backplane.
|
||||
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/ltc4283.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- adi,ltc4283
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
adi,rsense-nano-ohms:
|
||||
description: Value of the sense resistor.
|
||||
|
||||
adi,current-limit-sense-microvolt:
|
||||
description:
|
||||
The current limit sense voltage of the chip is adjustable between
|
||||
15mV and 30mV in 1mV steps. This effectively limits the current
|
||||
on the load.
|
||||
minimum: 15000
|
||||
maximum: 30000
|
||||
default: 15000
|
||||
|
||||
adi,current-limit-foldback-factor:
|
||||
description:
|
||||
Specifies the foldback factor for the current limit. The current limit
|
||||
can be reduced (folded back) to one of four preset levels. The value
|
||||
represents the percentage of the current limit sense voltage to use
|
||||
during foldback. A value of 100 means no foldback.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
enum: [10, 20, 50, 100]
|
||||
default: 100
|
||||
|
||||
adi,cooling-delay-ms:
|
||||
description:
|
||||
Cooling time to apply after an overcurrent fault, FET bad or
|
||||
external fault.
|
||||
enum: [512, 1002, 2005, 4100, 8190, 16400, 32800, 65600]
|
||||
default: 512
|
||||
|
||||
adi,fet-bad-timer-delay-ms:
|
||||
description:
|
||||
FET bad timer delay. After a FET bad status condition is detected,
|
||||
this timer is started. If the condition persists for the
|
||||
specified time, the FET is turned off and a fault is logged.
|
||||
enum: [256, 512, 1002, 2005]
|
||||
default: 256
|
||||
|
||||
adi,power-good-reset-on-fet:
|
||||
description:
|
||||
If set, resets the power good status when the MOSFET is turned off.
|
||||
Otherwise, it resets when a low output voltage is detected.
|
||||
type: boolean
|
||||
|
||||
adi,fet-turn-off-disable:
|
||||
description:
|
||||
If set, the MOSFET is not turned off when a FET fault is detected.
|
||||
type: boolean
|
||||
|
||||
adi,tmr-pull-down-disable:
|
||||
description: Disables 2uA pull-down current on the TMR pin.
|
||||
type: boolean
|
||||
|
||||
adi,dvdt-inrush-control-disable:
|
||||
description:
|
||||
Disables dV/dt inrush control during startup. In dV/dt mode, the inrush
|
||||
current is limited by controlling a constant output voltage ramp rate.
|
||||
When disabled, the inrush control mechanism is active current limiting.
|
||||
type: boolean
|
||||
|
||||
adi,fault-log-enable:
|
||||
description:
|
||||
If set, enables logging fault registers and ADC data into EEPROM upon a
|
||||
fault.
|
||||
type: boolean
|
||||
|
||||
adi,vpower-drns-enable:
|
||||
description:
|
||||
If set, enables the attenuated MOSFET drain voltage to be monitored. This
|
||||
effectively means that the MOSFET power is monitored. If not set, the
|
||||
attenuated input voltage (and hence input power) is monitored.
|
||||
type: boolean
|
||||
|
||||
adi,external-fault-fet-off-enable:
|
||||
description: Turns MOSFET off following an external fault.
|
||||
type: boolean
|
||||
|
||||
adi,undervoltage-retry-disable:
|
||||
description: Do not retry to turn on the MOSFET after an undervoltage fault.
|
||||
type: boolean
|
||||
|
||||
adi,overvoltage-retry-disable:
|
||||
description: Do not retry to turn on the MOSFET after an overvoltage fault.
|
||||
type: boolean
|
||||
|
||||
adi,external-fault-retry-enable:
|
||||
description: Retry to turn on the MOSFET after an external fault.
|
||||
type: boolean
|
||||
|
||||
adi,overcurrent-retries:
|
||||
description: Configures auto-retry following an Overcurrent fault.
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
enum: [latch-off, "1", "7", unlimited]
|
||||
default: latch-off
|
||||
|
||||
adi,fet-bad-retries:
|
||||
description:
|
||||
Configures auto-retry following a FET bad fault and a consequent MOSFET
|
||||
turn off.
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
enum: [latch-off, "1", "7", unlimited]
|
||||
default: latch-off
|
||||
|
||||
adi,pgio1-func:
|
||||
description: Configures the function of the PGIO1 pin.
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
enum: [inverted_power_good, power_good, gpio]
|
||||
default: inverted_power_good
|
||||
|
||||
adi,pgio2-func:
|
||||
description: Configures the function of the PGIO2 pin.
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
enum: [inverted_power_good, power_good, gpio, active_current_limiting]
|
||||
default: inverted_power_good
|
||||
|
||||
adi,pgio3-func:
|
||||
description: Configures the function of the PGIO3 pin.
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
enum: [inverted_power_good_input, power_good_input, gpio]
|
||||
default: inverted_power_good_input
|
||||
|
||||
adi,pgio4-func:
|
||||
description: Configures the function of the PGIO4 pin.
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
enum: [inverted_external_fault, external_fault, gpio]
|
||||
default: inverted_external_fault
|
||||
|
||||
adi,gpio-on-adio1:
|
||||
description: If set, the ADIO1 pin is used as a GPIO.
|
||||
type: boolean
|
||||
|
||||
adi,gpio-on-adio2:
|
||||
description: If set, the ADIO2 pin is used as a GPIO.
|
||||
type: boolean
|
||||
|
||||
adi,gpio-on-adio3:
|
||||
description: If set, the ADIO3 pin is used as a GPIO.
|
||||
type: boolean
|
||||
|
||||
adi,gpio-on-adio4:
|
||||
description: If set, the ADIO4 pin is used as a GPIO.
|
||||
type: boolean
|
||||
|
||||
gpio-controller: true
|
||||
|
||||
'#gpio-cells':
|
||||
const: 2
|
||||
|
||||
dependencies:
|
||||
adi,gpio-on-adio1:
|
||||
- gpio-controller
|
||||
- '#gpio-cells'
|
||||
adi,gpio-on-adio2:
|
||||
- gpio-controller
|
||||
- '#gpio-cells'
|
||||
adi,gpio-on-adio3:
|
||||
- gpio-controller
|
||||
- '#gpio-cells'
|
||||
adi,gpio-on-adio4:
|
||||
- gpio-controller
|
||||
- '#gpio-cells'
|
||||
adi,external-fault-retry-enable:
|
||||
- adi,pgio4-func
|
||||
adi,external-fault-fet-off-enable:
|
||||
- adi,pgio4-func
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- adi,rsense-nano-ohms
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
properties:
|
||||
adi,pgio1-func:
|
||||
const: gpio
|
||||
required:
|
||||
- adi,pgio1-func
|
||||
then:
|
||||
required:
|
||||
- gpio-controller
|
||||
- '#gpio-cells'
|
||||
|
||||
- if:
|
||||
properties:
|
||||
adi,pgio2-func:
|
||||
const: gpio
|
||||
required:
|
||||
- adi,pgio2-func
|
||||
then:
|
||||
required:
|
||||
- gpio-controller
|
||||
- '#gpio-cells'
|
||||
|
||||
- if:
|
||||
properties:
|
||||
adi,pgio3-func:
|
||||
const: gpio
|
||||
required:
|
||||
- adi,pgio3-func
|
||||
then:
|
||||
required:
|
||||
- gpio-controller
|
||||
- '#gpio-cells'
|
||||
|
||||
- if:
|
||||
properties:
|
||||
adi,pgio4-func:
|
||||
const: gpio
|
||||
required:
|
||||
- adi,pgio4-func
|
||||
then:
|
||||
properties:
|
||||
adi,external-fault-retry-enable: false
|
||||
adi,external-fault-fet-off-enable: false
|
||||
required:
|
||||
- gpio-controller
|
||||
- '#gpio-cells'
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
swap-controller@15 {
|
||||
compatible = "adi,ltc4283";
|
||||
reg = <0x15>;
|
||||
|
||||
adi,rsense-nano-ohms = <500>;
|
||||
adi,current-limit-sense-microvolt = <25000>;
|
||||
adi,current-limit-foldback-factor = <10>;
|
||||
adi,cooling-delay-ms = <8190>;
|
||||
adi,fet-bad-timer-delay-ms = <512>;
|
||||
|
||||
adi,external-fault-fet-off-enable;
|
||||
adi,pgio4-func = "external_fault";
|
||||
|
||||
adi,gpio-on-adio1;
|
||||
adi,pgio1-func = "gpio";
|
||||
gpio-controller;
|
||||
#gpio-cells = <2>;
|
||||
};
|
||||
};
|
||||
...
|
||||
@@ -54,6 +54,13 @@ properties:
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
ti,alert-polarity-active-high:
|
||||
description: Alert pin is asserted based on the value of alert polarity
|
||||
bit of configuration register. Default value is normal (0 which maps to
|
||||
active-low). The other value is inverted (1 which maps to active-high).
|
||||
Specify this property to set the alert polarity to active-high.
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
193
Documentation/devicetree/bindings/hwmon/microchip,emc1812.yaml
Normal file
193
Documentation/devicetree/bindings/hwmon/microchip,emc1812.yaml
Normal file
@@ -0,0 +1,193 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/hwmon/microchip,emc1812.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Microchip EMC1812/13/14/15/33 multichannel temperature sensor
|
||||
|
||||
maintainers:
|
||||
- Marius Cristea <marius.cristea@microchip.com>
|
||||
|
||||
description: |
|
||||
The Microchip EMC1812/13/14/15/33 is a high-accuracy 2-wire multichannel
|
||||
low-voltage remote diode temperature monitor.
|
||||
|
||||
The datasheet can be found here:
|
||||
https://ww1.microchip.com/downloads/aemDocuments/documents/MSLD/ProductDocuments/DataSheets/EMC1812-3-4-5-33-Data-Sheet-DS20005751.pdf
|
||||
|
||||
EMC1812 has one external remote temperature monitoring channel
|
||||
EMC1813 has two external remote temperature monitoring channels
|
||||
EMC1814 has three external remote temperature monitoring channels and
|
||||
channels 2 and 3 support anti parallel diode
|
||||
EMC1815 has four external remote temperature monitoring channels and
|
||||
channels 1/2 and 3/4 support anti parallel diode
|
||||
EMC1833 has two external remote temperature monitoring channels and
|
||||
channels 1 and 2 support anti parallel diode
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- microchip,emc1812
|
||||
- microchip,emc1813
|
||||
- microchip,emc1814
|
||||
- microchip,emc1815
|
||||
- microchip,emc1833
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
items:
|
||||
- description: alert-therm2 asserts when the ALERT limit is exceeded.
|
||||
- description: therm-addr asserts when the THERM limit is exceeded.
|
||||
minItems: 1
|
||||
|
||||
interrupt-names:
|
||||
items:
|
||||
- const: alert-therm2
|
||||
- const: therm-addr
|
||||
minItems: 1
|
||||
|
||||
"#address-cells":
|
||||
const: 1
|
||||
|
||||
"#size-cells":
|
||||
const: 0
|
||||
|
||||
microchip,enable-anti-parallel:
|
||||
description:
|
||||
Enable anti-parallel diode mode operation. EMC1814, EMC1815 and EMC1833
|
||||
support reading two external diodes in anti-parallel connection on the
|
||||
same set of pins. Disabling APD functionality to implement substrate
|
||||
diodes on devices that support APD eliminates the benefit of APD
|
||||
(two diodes on one channel).
|
||||
type: boolean
|
||||
|
||||
microchip,parasitic-res-on-channel1-2:
|
||||
description:
|
||||
Indicates that the chip and the diodes/transistors are sufficiently
|
||||
far apart that a parasitic resistance is added to the wires, which can
|
||||
affect the measurements. Due to the availability of only a single
|
||||
configuration bit in hardware, channels 1 and 2 are affected together.
|
||||
If channel 2 is not available in hardware, this setting affects only
|
||||
channel 1.
|
||||
type: boolean
|
||||
|
||||
microchip,parasitic-res-on-channel3-4:
|
||||
description:
|
||||
Indicates that the chip and the diodes/transistors are sufficiently
|
||||
far apart that a parasitic resistance is added to the wires, which can
|
||||
affect the measurements. Due to the availability of only a single
|
||||
configuration bit in hardware, channels 3 and 4 are affected together.
|
||||
If channel 4 is not available in hardware, this setting affects only
|
||||
channel 3.
|
||||
type: boolean
|
||||
|
||||
vdd-supply: true
|
||||
|
||||
patternProperties:
|
||||
"^channel@[0-4]$":
|
||||
description: |
|
||||
Represents the temperature channels.
|
||||
0: Internal sensor
|
||||
1-4: External remote diodes
|
||||
type: object
|
||||
|
||||
properties:
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
label:
|
||||
description: Unique name to identify which channel this is.
|
||||
|
||||
required:
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- vdd-supply
|
||||
|
||||
allOf:
|
||||
# EMC1812: 1 Internal, 1 External Channels, No APD,
|
||||
# parasitic-res-on-channel1-2: for channel 1
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
const: microchip,emc1812
|
||||
then:
|
||||
properties:
|
||||
microchip,enable-anti-parallel: false
|
||||
microchip,parasitic-res-on-channel3-4: false
|
||||
patternProperties:
|
||||
"^channel@[2-4]$": false
|
||||
|
||||
# EMC1813: 1 Internal, 2 External Channels, No APD,
|
||||
# parasitic-res-on-channel1-2: on both channel 1 & 2
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
const: microchip,emc1813
|
||||
then:
|
||||
properties:
|
||||
microchip,enable-anti-parallel: false
|
||||
microchip,parasitic-res-on-channel3-4: false
|
||||
patternProperties:
|
||||
"^channel@[3-4]$": false
|
||||
|
||||
# EMC1833: 1 Internal, 2 External Channels, Supports APD,
|
||||
# parasitic-res-on-channel1-2: on both channel 1 & 2
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
const: microchip,emc1833
|
||||
then:
|
||||
properties:
|
||||
microchip,parasitic-res-on-channel3-4: false
|
||||
patternProperties:
|
||||
"^channel@[3-4]$": false
|
||||
|
||||
# EMC1814: 1 Internal, 3 External Channels, Supports APD,
|
||||
# parasitic-res-on-channel1-2: on both channel 1 & 2
|
||||
# parasitic-res-on-channel3-4: for channel 3
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
const: microchip,emc1814
|
||||
then:
|
||||
properties:
|
||||
channel@4: false
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
temperature-sensor@4c {
|
||||
compatible = "microchip,emc1813";
|
||||
reg = <0x4c>;
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
microchip,parasitic-res-on-channel1-2;
|
||||
|
||||
vdd-supply = <&vdd>;
|
||||
|
||||
channel@1 {
|
||||
reg = <1>;
|
||||
label = "External CH1 Temperature";
|
||||
};
|
||||
|
||||
channel@2 {
|
||||
reg = <2>;
|
||||
label = "External CH2 Temperature";
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -54,6 +54,12 @@ patternProperties:
|
||||
The fan number used to determine the associated PWM channel.
|
||||
maxItems: 1
|
||||
|
||||
fan-shutdown-percent:
|
||||
description:
|
||||
PWM duty cycle in percent applied to the fan during shutdown.
|
||||
minimum: 0
|
||||
maximum: 100
|
||||
|
||||
required:
|
||||
- reg
|
||||
|
||||
@@ -80,12 +86,14 @@ examples:
|
||||
fan@0 {
|
||||
reg = <0x0>;
|
||||
pwms = <&fan_controller 26000 PWM_POLARITY_INVERTED 1>;
|
||||
fan-shutdown-percent = <100>;
|
||||
#cooling-cells = <2>;
|
||||
};
|
||||
|
||||
fan@1 {
|
||||
reg = <0x1>;
|
||||
pwms = <&fan_controller 26000 0 1>;
|
||||
fan-shutdown-percent = <50>;
|
||||
#cooling-cells = <2>;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
Bindings for the fan / temperature monitor microcontroller used on
|
||||
the Zyxel NSA 320 and several subsequent models.
|
||||
|
||||
Required properties:
|
||||
- compatible : "zyxel,nsa320-mcu"
|
||||
- data-gpios : The GPIO pin connected to the data line on the MCU
|
||||
- clk-gpios : The GPIO pin connected to the clock line on the MCU
|
||||
- act-gpios : The GPIO pin connected to the active line on the MCU
|
||||
|
||||
Example:
|
||||
|
||||
hwmon {
|
||||
compatible = "zyxel,nsa320-mcu";
|
||||
pinctrl-0 = <&pmx_mcu_data &pmx_mcu_clk &pmx_mcu_act>;
|
||||
pinctrl-names = "default";
|
||||
|
||||
data-gpios = <&gpio0 14 GPIO_ACTIVE_HIGH>;
|
||||
clk-gpios = <&gpio0 16 GPIO_ACTIVE_HIGH>;
|
||||
act-gpios = <&gpio0 17 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/hwmon/pmbus/adi,max20830.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices MAX20830 Step-Down Switching Regulator with PMBus
|
||||
|
||||
maintainers:
|
||||
- Alexis Czezar Torreno <alexisczezar.torreno@analog.com>
|
||||
|
||||
description: |
|
||||
The MAX20830 is a fully integrated step-down DC-DC switching regulator with
|
||||
PMBus interface. It provides 2.7V to 16V input, 0.4V to 5.8V adjustable
|
||||
output, and up to 30A output current. It allows monitoring of input/output
|
||||
voltage, output current and temperature through the PMBus serial interface.
|
||||
Datasheet:
|
||||
https://www.analog.com/en/products/max20830.html
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/regulator/regulator.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: adi,max20830
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
vddh-supply:
|
||||
description:
|
||||
Phandle to the regulator that provides the VDDH power supply.
|
||||
|
||||
avdd-supply:
|
||||
description:
|
||||
Phandle to the regulator that provides the AVDD power supply.
|
||||
|
||||
ldoin-supply:
|
||||
description:
|
||||
Optional 2.5V to 5.5V LDO input supply.
|
||||
|
||||
pwr-good-gpios:
|
||||
description:
|
||||
GPIO connected to the power-good status output pin.
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- vddh-supply
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
regulator@30 {
|
||||
compatible = "adi,max20830";
|
||||
reg = <0x30>;
|
||||
vddh-supply = <&vddh>;
|
||||
};
|
||||
};
|
||||
...
|
||||
@@ -0,0 +1,45 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/hwmon/pmbus/adi,max20860a.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Analog Devices MAX20860A Step-Down Converter
|
||||
|
||||
maintainers:
|
||||
- Sanman Pradhan <psanman@juniper.net>
|
||||
|
||||
description: |
|
||||
The MAX20860A is a fully integrated step-down DC-DC switching regulator
|
||||
with PMBus interface for monitoring input/output voltage, output current
|
||||
and temperature.
|
||||
|
||||
Datasheet: https://www.analog.com/en/products/max20860a.html
|
||||
|
||||
allOf:
|
||||
- $ref: /schemas/regulator/regulator.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: adi,max20860a
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
regulator@40 {
|
||||
compatible = "adi,max20860a";
|
||||
reg = <0x40>;
|
||||
};
|
||||
};
|
||||
@@ -5,23 +5,31 @@
|
||||
$id: http://devicetree.org/schemas/hwmon/pmbus/infineon,xdp720.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Infineon XDP720 Digital eFuse Controller
|
||||
title: Infineon XDP720 / XDP730 Digital eFuse Controllers
|
||||
|
||||
maintainers:
|
||||
- Ashish Yadav <ashish.yadav@infineon.com>
|
||||
|
||||
description: |
|
||||
The XDP720 is an eFuse with integrated current sensor and digital
|
||||
controller. It provides accurate system telemetry (V, I, P, T) and
|
||||
reports analog current at the IMON pin for post-processing.
|
||||
The XDP720 and XDP730 are PMBus-compliant digital eFuse controllers
|
||||
with an integrated current sensor. They provide accurate system
|
||||
telemetry (V, I, P, T) and report analog current at the IMON pin for
|
||||
post-processing.
|
||||
|
||||
Datasheet:
|
||||
https://www.infineon.com/assets/row/public/documents/24/49/infineon-xdp720-001-datasheet-en.pdf
|
||||
Both parts share the same PMBus register map and direct-format
|
||||
coefficients; they differ in the GIMON gain step exposed via the
|
||||
TELEMETRY_AVG register (bit 10) and in the VDD_VIN pin number
|
||||
(XDP720: pin 9, XDP730: pin 20).
|
||||
|
||||
Datasheets:
|
||||
- XDP720: https://www.infineon.com/assets/row/public/documents/24/49/infineon-xdp720-001-datasheet-en.pdf
|
||||
- XDP730: https://www.infineon.com/assets/row/public/documents/24/49/infineon-xdp730-001-datasheet-en.pdf
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- infineon,xdp720
|
||||
- infineon,xdp730
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
@@ -33,9 +41,9 @@ properties:
|
||||
|
||||
vdd-vin-supply:
|
||||
description:
|
||||
Supply for the VDD_VIN pin (pin 9), the IC controller power supply.
|
||||
Typically connected to the input bus (VIN) through a 100 ohm / 100 nF
|
||||
RC filter.
|
||||
Supply for the VDD_VIN pin (XDP720 pin 9, XDP730 pin 20), the IC
|
||||
controller power supply. Typically connected to the input bus
|
||||
(VIN) through a 100 ohm / 100 nF RC filter.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
@@ -50,7 +58,7 @@ examples:
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
hwmon@11 {
|
||||
efuse@11 {
|
||||
compatible = "infineon,xdp720";
|
||||
reg = <0x11>;
|
||||
vdd-vin-supply = <&vdd_vin>;
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/hwmon/zyxel,nsa320-mcu.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: ZyXEL NSA320 MCU
|
||||
|
||||
maintainers:
|
||||
- Adam Baker <linux@baker-net.org.uk>
|
||||
- Guenter Roeck <linux@roeck-us.net>
|
||||
|
||||
description:
|
||||
The ZyXEL NSA320 uses a dedicated microcontroller to manage system-critical
|
||||
functions like fan speed and power monitoring. It is connected to the SoC
|
||||
via a GPIO-based serial protocol.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: zyxel,nsa320-mcu
|
||||
|
||||
data-gpios:
|
||||
maxItems: 1
|
||||
description: GPIO pin connected to the data line on the MCU.
|
||||
|
||||
clk-gpios:
|
||||
maxItems: 1
|
||||
description: GPIO pin connected to the clock line on the MCU.
|
||||
|
||||
act-gpios:
|
||||
maxItems: 1
|
||||
description: GPIO pin connected to the active line on the MCU.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- data-gpios
|
||||
- clk-gpios
|
||||
- act-gpios
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
|
||||
hwmon {
|
||||
compatible = "zyxel,nsa320-mcu";
|
||||
pinctrl-0 = <&pmx_mcu_data &pmx_mcu_clk &pmx_mcu_act>;
|
||||
pinctrl-names = "default";
|
||||
|
||||
data-gpios = <&gpio0 14 GPIO_ACTIVE_HIGH>;
|
||||
clk-gpios = <&gpio0 16 GPIO_ACTIVE_HIGH>;
|
||||
act-gpios = <&gpio0 17 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
@@ -99,6 +99,8 @@ properties:
|
||||
- delta,dps800
|
||||
# Delta Electronics DPS920AB 920W 54V Power Supply
|
||||
- delta,dps920ab
|
||||
# 600W Non-isolated 1/8th Brick DC/DC Power Modules
|
||||
- delta,e50sn12051
|
||||
# 1/4 Brick DC/DC Regulated Power Module
|
||||
- delta,q54sj108a2
|
||||
# 1300W 1/4 Brick DC/DC Regulated Power Module
|
||||
@@ -192,6 +194,8 @@ properties:
|
||||
- jedec,spd5118
|
||||
# Linear Technology LTC2488
|
||||
- lineartechnology,ltc2488
|
||||
# Regulated 12V, 860W, Digital DC/DC Power Module
|
||||
- luxshare,lx1308
|
||||
# 5 Bit Programmable, Pulse-Width Modulator
|
||||
- maxim,ds1050
|
||||
# 10 kOhm digital potentiometer with I2C interface
|
||||
@@ -336,6 +340,8 @@ properties:
|
||||
- mps,mp29612
|
||||
# Monolithic Power Systems Inc. multi-phase controller mp29816
|
||||
- mps,mp29816
|
||||
# Monolithic Power Systems Inc. multi-phase controller mp2985
|
||||
- mps,mp2985
|
||||
# Monolithic Power Systems Inc. multi-phase controller mp2993
|
||||
- mps,mp2993
|
||||
# Monolithic Power Systems Inc. hot-swap protection device
|
||||
@@ -352,6 +358,8 @@ properties:
|
||||
- mps,mp9941
|
||||
# Monolithic Power Systems Inc. digital step-down converter mp9945
|
||||
- mps,mp9945
|
||||
# Murata D1U74T-W power supply unit
|
||||
- murata,d1u74t
|
||||
# Temperature sensor with integrated fan control
|
||||
- national,lm63
|
||||
# Temperature sensor with integrated fan control
|
||||
|
||||
@@ -981,6 +981,8 @@ patternProperties:
|
||||
description: Shenzhen Luckfox Technology Co., Ltd.
|
||||
"^lunzn,.*":
|
||||
description: Shenzhen Lunzn Technology Co., Ltd.
|
||||
"^luxshare,.*":
|
||||
description: Luxshare-ICT Co., Ltd.
|
||||
"^luxul,.*":
|
||||
description: Lagrand | AV
|
||||
"^lwn,.*":
|
||||
|
||||
@@ -30,11 +30,36 @@ Check the datasheet for details.
|
||||
sysfs-Interface
|
||||
---------------
|
||||
|
||||
================ =================
|
||||
in0_input vdd voltage input
|
||||
in[1-8]_input analog 1-8 input
|
||||
temp1_input temperature input
|
||||
================ =================
|
||||
================ =================================
|
||||
in0_input vdd voltage input
|
||||
in0_min vdd low limit
|
||||
in0_max vdd high limit
|
||||
in0_alarm vdd alarm
|
||||
in[1-8]_input analog 1-8 input
|
||||
in[1-8]_min analog input low limit
|
||||
in[1-8]_max analog input high limit
|
||||
in[1-8]_alarm analog input alarm
|
||||
temp1_input internal temperature input
|
||||
temp1_min internal temperature low limit
|
||||
temp1_max internal temperature high limit
|
||||
temp1_min_alarm internal temperature low alarm
|
||||
temp1_max_alarm internal temperature high alarm
|
||||
================ =================================
|
||||
|
||||
If the external temperature sensor is enabled, the following attributes are
|
||||
also available:
|
||||
|
||||
================ ================================================
|
||||
temp2_input external temperature input
|
||||
temp2_min external temperature low limit
|
||||
temp2_max external temperature high limit
|
||||
temp2_min_alarm external temperature low alarm
|
||||
temp2_max_alarm external temperature high alarm
|
||||
temp2_fault external temperature sensor fault
|
||||
================ ================================================
|
||||
|
||||
If the external temperature sensor is enabled, analog inputs in1 and in2 are
|
||||
not available.
|
||||
|
||||
Besides standard interfaces, this driver adds (0 = off, 1 = on):
|
||||
|
||||
@@ -47,4 +72,4 @@ Besides standard interfaces, this driver adds (0 = off, 1 = on):
|
||||
Notes
|
||||
-----
|
||||
|
||||
SPI, external temperature sensor and limit registers are not supported yet.
|
||||
SPI is not supported yet.
|
||||
|
||||
56
Documentation/hwmon/arctic_fan_controller.rst
Normal file
56
Documentation/hwmon/arctic_fan_controller.rst
Normal file
@@ -0,0 +1,56 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
Kernel driver arctic_fan_controller
|
||||
=====================================
|
||||
|
||||
Supported devices:
|
||||
|
||||
* ARCTIC Fan Controller (USB HID, VID 0x3904, PID 0xF001)
|
||||
|
||||
Author: Aureo Serrano de Souza <aureo.serrano@arctic.de>
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
This driver provides hwmon support for the ARCTIC Fan Controller, a USB
|
||||
Custom HID device with 10 fan channels. The device sends IN reports about
|
||||
once per second containing current RPM values (bytes 11-30, 10 x uint16 LE).
|
||||
Fan speed control is manual-only: the device does not change PWM
|
||||
autonomously; it only applies a new duty cycle when it receives an OUT
|
||||
report from the host.
|
||||
|
||||
After the device applies an OUT report, it sends back a 2-byte ACK IN
|
||||
report (Report ID 0x02, byte 1 = 0x00 on success) confirming the command
|
||||
was applied.
|
||||
|
||||
Usage notes
|
||||
-----------
|
||||
|
||||
Since it is a USB device, hotplug is supported. The device is autodetected.
|
||||
|
||||
The device does not support GET_REPORT, so the driver cannot read back the
|
||||
current hardware PWM state at probe time. The cached PWM values (readable
|
||||
via pwm[1-10]) start at 0 and reflect only values that have been
|
||||
successfully written. Because each OUT report carries all 10 channel values,
|
||||
writing a single channel also sends the cached values for all other channels.
|
||||
Users should set all channels to the desired values before relying on the
|
||||
cached state.
|
||||
|
||||
On system suspend, the device may lose power and reset its PWM channels to
|
||||
hardware defaults. The driver clears its cached duty values on resume so
|
||||
that reads reflect the unknown hardware state rather than stale pre-suspend
|
||||
values. Userspace is responsible for re-applying the desired duty cycles
|
||||
after resume.
|
||||
|
||||
Sysfs entries
|
||||
-------------
|
||||
|
||||
================ ==============================================================
|
||||
fan[1-10]_input Fan speed in RPM (read-only). Updated from IN reports at ~1 Hz.
|
||||
pwm[1-10] PWM duty cycle (0-255). Write: sends an OUT report setting the
|
||||
duty cycle (scaled from 0-255 to 0-100% for the device);
|
||||
the cached value is updated only after the device ACKs the
|
||||
command with a success status. Read: returns the last
|
||||
successfully written value; initialized to 0 at driver load
|
||||
and after resume (hardware state unknown).
|
||||
================ ==============================================================
|
||||
@@ -29,9 +29,12 @@ Supported boards:
|
||||
* ROG MAXIMUS XI HERO
|
||||
* ROG MAXIMUS XI HERO (WI-FI)
|
||||
* ROG MAXIMUS Z690 FORMULA
|
||||
* ROG MAXIMUS Z790 EXTREME
|
||||
* ROG STRIX B550-E GAMING
|
||||
* ROG STRIX B550-I GAMING
|
||||
* ROG STRIX B650E-E GAMING WIFI
|
||||
* ROG STRIX B650E-I GAMING WIFI
|
||||
* ROG STRIX B850-E GAMING WIFI
|
||||
* ROG STRIX B850-I GAMING WIFI
|
||||
* ROG STRIX X470-F GAMING
|
||||
* ROG STRIX X470-I GAMING
|
||||
|
||||
@@ -22,8 +22,7 @@ Supported chips:
|
||||
|
||||
Intel 64 and IA-32 Architectures Software Developer's Manual
|
||||
Volume 3A: System Programming Guide
|
||||
|
||||
http://softwarecommunity.intel.com/Wiki/Mobility/720.htm
|
||||
(https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html)
|
||||
|
||||
Author: Rudolf Marek
|
||||
|
||||
@@ -51,12 +50,13 @@ All Sysfs entries are named with their core_id (represented here by 'X').
|
||||
|
||||
================= ========================================================
|
||||
tempX_input Core temperature (in millidegrees Celsius).
|
||||
tempX_max All cooling devices should be turned on (on Core2).
|
||||
tempX_max Maximum recommended operating temperature (Tcontrol).
|
||||
All cooling devices should be turned on.
|
||||
tempX_crit Maximum junction temperature (in millidegrees Celsius).
|
||||
tempX_crit_alarm Set when Out-of-spec bit is set, never clears.
|
||||
Correct CPU operation is no longer guaranteed.
|
||||
tempX_label Contains string "Core X", where X is processor
|
||||
number. For Package temp, this will be "Physical id Y",
|
||||
number. For Package temp, this will be "Package id Y",
|
||||
where Y is the package number.
|
||||
================= ========================================================
|
||||
|
||||
|
||||
81
Documentation/hwmon/d1u74t.rst
Normal file
81
Documentation/hwmon/d1u74t.rst
Normal file
@@ -0,0 +1,81 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
Kernel driver d1u74t
|
||||
====================
|
||||
|
||||
Supported chips:
|
||||
|
||||
* Murata D1U74T
|
||||
|
||||
Prefix: 'd1u74t'
|
||||
|
||||
Addresses scanned: -
|
||||
|
||||
Datasheet: Publicly available at the Murata website
|
||||
|
||||
Authors:
|
||||
Abdurrahman Hussain <abdurrahman@nexthop.ai>
|
||||
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
This driver implements support for Murata D1U74T Power Supply with
|
||||
PMBus support.
|
||||
|
||||
The driver is a client driver to the core PMBus driver.
|
||||
Please see Documentation/hwmon/pmbus.rst for details on PMBus client drivers.
|
||||
|
||||
|
||||
Usage Notes
|
||||
-----------
|
||||
|
||||
This driver does not auto-detect devices. You will have to instantiate the
|
||||
devices explicitly. Please see Documentation/i2c/instantiating-devices.rst for
|
||||
details.
|
||||
|
||||
|
||||
Sysfs entries
|
||||
-------------
|
||||
|
||||
======================= ======================================================
|
||||
curr1_label "iin"
|
||||
curr1_input Measured input current
|
||||
curr1_alarm Input current alarm
|
||||
curr1_rated_max Maximum rated input current
|
||||
|
||||
curr2_label "iout1"
|
||||
curr2_input Measured output current
|
||||
curr2_max Maximum output current
|
||||
curr2_max_alarm Output current high alarm
|
||||
curr2_crit Critical high output current
|
||||
curr2_crit_alarm Output current critical high alarm
|
||||
curr2_rated_max Maximum rated output current
|
||||
|
||||
in1_label "vin"
|
||||
in1_input Measured input voltage
|
||||
in1_alarm Input voltage alarm
|
||||
in1_rated_min Minimum rated input voltage
|
||||
in1_rated_max Maximum rated input voltage
|
||||
|
||||
in2_label "vout1"
|
||||
in2_input Measured output voltage
|
||||
in2_alarm Output voltage alarm
|
||||
in2_rated_min Minimum rated output voltage
|
||||
in2_rated_max Maximum rated output voltage
|
||||
|
||||
power1_label "pin"
|
||||
power1_input Measured input power
|
||||
power1_alarm Input power alarm
|
||||
power1_rated_max Maximum rated input power
|
||||
|
||||
temp[1-3]_input Measured temperature
|
||||
temp[1-3]_max Maximum temperature
|
||||
temp[1-3]_max_alarm Maximum temperature alarm
|
||||
temp[1-3]_rated_max Maximum rated temperature
|
||||
|
||||
fan1_alarm Fan 1 warning
|
||||
fan1_fault Fan 1 fault
|
||||
fan1_input Fan 1 speed in RPM
|
||||
fan1_target Fan 1 target
|
||||
======================= ======================================================
|
||||
81
Documentation/hwmon/e50sn12051.rst
Normal file
81
Documentation/hwmon/e50sn12051.rst
Normal file
@@ -0,0 +1,81 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
Kernel driver e50sn12051
|
||||
========================
|
||||
|
||||
Supported chips:
|
||||
|
||||
* Delta E50SN12051
|
||||
|
||||
Prefix: 'e50sn12051'
|
||||
|
||||
Addresses scanned: -
|
||||
|
||||
Datasheet:
|
||||
|
||||
Provided by Delta upon request and NDA
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
E50SN12051 is a 600W non-isolated 1/8th brick DC-DC power module.
|
||||
|
||||
This driver supports the E50SN12051 PMBus compliant monitor device.
|
||||
|
||||
The device provides measurements for:
|
||||
|
||||
* Input voltage
|
||||
* Output current
|
||||
* Output voltage
|
||||
* Temperature
|
||||
|
||||
The driver is based on the PMBus core and exposes standard hwmon
|
||||
sysfs attributes.
|
||||
|
||||
Sysfs attributes
|
||||
----------------
|
||||
|
||||
======================= =======================================================
|
||||
curr1_label "iout1"
|
||||
curr1_crit Critical maximum current.
|
||||
From IOUT_OC_FAULT_LIMIT register.
|
||||
curr1_crit_alarm Current critical high alarm.
|
||||
From IOUT_OC_FAULT status.
|
||||
curr1_input Measured current.
|
||||
From READ_IOUT register.
|
||||
curr1_max Maximum current.
|
||||
From IOUT_OC_WARN_LIMIT register.
|
||||
curr1_max_alarm Current high alarm.
|
||||
From IOUT_OC_WARN_LIMIT status.
|
||||
|
||||
in1_label "vin"
|
||||
in1_alarm Input voltage alarm.
|
||||
in1_input Measured voltage.
|
||||
From READ_VIN register.
|
||||
|
||||
in2_label "vout1"
|
||||
in2_crit Critical maximum voltage.
|
||||
From VOUT_OV_FAULT_LIMIT register.
|
||||
in2_crit_alarm Voltage critical high alarm.
|
||||
From VOLTAGE_OV_FAULT status.
|
||||
in2_input Measured voltage.
|
||||
From READ_VOUT register.
|
||||
in2_max Maximum voltage.
|
||||
From VOUT_OV_WARN_LIMIT register.
|
||||
in2_max_alarm Voltage high alarm.
|
||||
From VOLTAGE_OV_WARNING status.
|
||||
|
||||
temp1_crit Critical high temperature.
|
||||
From OT_FAULT_LIMIT register.
|
||||
temp1_crit_alarm Module temperature critical high alarm.
|
||||
Set by comparing READ_TEMPERATURE_1 with OT_FAULT_LIMIT
|
||||
if TEMP_OT_FAULT status is set.
|
||||
temp1_input Measured module's hot spot temperature.
|
||||
From READ_TEMPERATURE_1 register.
|
||||
temp1_max Maximum temperature.
|
||||
From OT_WARN_LIMIT register.
|
||||
temp1_max_alarm Module temperature high alarm.
|
||||
Set by comparing READ_TEMPERATURE_1 with OT_WARN_LIMIT if
|
||||
TEMP_OT_WARNING status is set.
|
||||
======================= =======================================================
|
||||
|
||||
67
Documentation/hwmon/emc1812.rst
Normal file
67
Documentation/hwmon/emc1812.rst
Normal file
@@ -0,0 +1,67 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
Kernel driver emc1812
|
||||
=====================
|
||||
|
||||
Supported chips:
|
||||
|
||||
* Microchip EMC1812, EMC1813, EMC1814, EMC1815, EMC1833
|
||||
|
||||
Prefix: 'emc1812'
|
||||
|
||||
Datasheets:
|
||||
|
||||
- https://ww1.microchip.com/downloads/aemDocuments/documents/MSLD/ProductDocuments/DataSheets/EMC1812-3-4-5-33-Data-Sheet-DS20005751.pdf
|
||||
|
||||
Author:
|
||||
Marius Cristea <marius.cristea@microchip.com>
|
||||
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The Microchip EMC181x/33 chips contain up to 4 remote temperature sensors
|
||||
and one internal.
|
||||
- The EMC1812 is a single channel remote temperature sensor.
|
||||
- The EMC1813 and EMC1833 are dual channel remote temperature sensor. The
|
||||
remote channels for this selection of devices can support substrate diodes,
|
||||
discrete diode-connected transistors or CPU/GPU thermal diodes.
|
||||
- The EMC1814 is a three channel remote temperature sensor that supports
|
||||
Anti-Parallel Diode (APD) only on one channel. For the channel that does not
|
||||
support APD functionality, substrate diodes, discrete diode-connected
|
||||
transistors or CPU/GPU thermal diodes are supported. For the channel that
|
||||
supports APD, only discrete diode-connected transistors may be implemented.
|
||||
However, if APD is disabled on the EMC1814, then the channel that supports
|
||||
APD will be functional with substrate diodes, discrete diode-connected
|
||||
transistors and CPU/GPU thermal diodes.
|
||||
- The EMC1815 is a four channel remote temperature sensor.
|
||||
|
||||
The EMC1815 and EMC1833 support APD on all channels. When APD is enabled,
|
||||
the channels support only diode-connected transistors. If APD is disabled,
|
||||
then the channels will support substrate transistors, discrete diode-connected
|
||||
transistors and CPU/GPU thermal diodes.
|
||||
|
||||
Note: Disabling APD functionality to implement substrate diodes on devices
|
||||
that support APD eliminates the benefit of APD (two diodes on one channel).
|
||||
|
||||
The chips implement three limits for each sensor: low (tempX_min), high
|
||||
(tempX_max) and critical (tempX_crit). The chips also implement an
|
||||
hysteresis mechanism which applies to all limits. The relative difference
|
||||
is stored in a single register on the chip, which means that the relative
|
||||
difference between the limit and its hysteresis is always the same for
|
||||
all three limits.
|
||||
|
||||
This implementation detail implies the following:
|
||||
|
||||
* When setting a limit, its hysteresis will automatically follow, the
|
||||
difference staying unchanged. For example, if the old critical limit was
|
||||
80 degrees C, and the hysteresis was 75 degrees C, and you change the
|
||||
critical limit to 90 degrees C, then the hysteresis will automatically
|
||||
change to 85 degrees C.
|
||||
* The hysteresis values can't be set independently. We decided to make
|
||||
only tempX_crit_hyst writable, while all other hysteresis attributes
|
||||
are read-only. Setting tempX_crit_hyst writes the difference between
|
||||
tempX_crit_hyst and tempX_crit into the chip, and the same relative
|
||||
hysteresis applies automatically to all other limits.
|
||||
* The limits should be set before the hysteresis. At power up the device
|
||||
starts with 10 degree hysteresis.
|
||||
@@ -35,3 +35,10 @@ temp1_input: temperature input
|
||||
humidity1_input: humidity input
|
||||
heater_enable: heater control
|
||||
=================== =================
|
||||
|
||||
debugfs-Interface
|
||||
-----------------
|
||||
|
||||
=================== =========================================
|
||||
serial_number: unique serial number of the sensor
|
||||
=================== =========================================
|
||||
|
||||
@@ -85,9 +85,10 @@ removal.
|
||||
When using ``[devm_]hwmon_device_register_with_info()`` to register the
|
||||
hardware monitoring device, accesses using the associated access functions
|
||||
are serialised by the hardware monitoring core. If a driver needs locking
|
||||
for other functions such as interrupt handlers or for attributes which are
|
||||
fully implemented in the driver, hwmon_lock() and hwmon_unlock() can be used
|
||||
to ensure that calls to those functions are serialized.
|
||||
for other functions such as interrupt handlers, attributes which are fully
|
||||
implemented in the driver, or debugfs functions, hwmon_lock() and hwmon_unlock()
|
||||
can be used to ensure that calls to those functions are serialized. Those
|
||||
functions also support guard() and scoped_guard() variants.
|
||||
|
||||
Using devm_hwmon_device_register_with_info()
|
||||
--------------------------------------------
|
||||
|
||||
@@ -106,4 +106,8 @@ energy1_input Energy measurement (uJ)
|
||||
temp1_input Die temperature measurement (mC)
|
||||
temp1_max Maximum die temperature threshold (mC)
|
||||
temp1_max_alarm Maximum die temperature alarm
|
||||
|
||||
samples ADC averaging count (1, 4, 16, 64, 128, 256, 512, 1024)
|
||||
update_interval Total ADC conversion cycle time including averaging (ms)
|
||||
update_interval_us Total ADC conversion cycle time including averaging (us)
|
||||
======================= =======================================================
|
||||
|
||||
@@ -43,6 +43,7 @@ Hardware Monitoring Kernel Drivers
|
||||
amc6821
|
||||
aps-379
|
||||
aquacomputer_d5next
|
||||
arctic_fan_controller
|
||||
asb100
|
||||
asc7621
|
||||
aspeed-g6-pwm-tach
|
||||
@@ -60,6 +61,7 @@ Hardware Monitoring Kernel Drivers
|
||||
corsair-psu
|
||||
cros_ec_hwmon
|
||||
crps
|
||||
d1u74t
|
||||
da9052
|
||||
da9055
|
||||
dell-smm-hwmon
|
||||
@@ -68,7 +70,9 @@ Hardware Monitoring Kernel Drivers
|
||||
drivetemp
|
||||
ds1621
|
||||
ds620
|
||||
e50sn12051
|
||||
emc1403
|
||||
emc1812
|
||||
emc2103
|
||||
emc2305
|
||||
emc6w201
|
||||
@@ -145,7 +149,9 @@ Hardware Monitoring Kernel Drivers
|
||||
ltc4260
|
||||
ltc4261
|
||||
ltc4282
|
||||
ltc4283
|
||||
ltc4286
|
||||
lx1308
|
||||
macsmc-hwmon
|
||||
max127
|
||||
max15301
|
||||
@@ -158,6 +164,8 @@ Hardware Monitoring Kernel Drivers
|
||||
max197
|
||||
max20730
|
||||
max20751
|
||||
max20830
|
||||
max20860a
|
||||
max31722
|
||||
max31730
|
||||
max31760
|
||||
@@ -185,6 +193,7 @@ Hardware Monitoring Kernel Drivers
|
||||
mp2925
|
||||
mp29502
|
||||
mp2975
|
||||
mp2985
|
||||
mp2993
|
||||
mp5023
|
||||
mp5920
|
||||
|
||||
@@ -181,3 +181,19 @@ is supported by this driver, other specific enhancements are not.
|
||||
|
||||
The LM77 is not supported, contrary to what we pretended for a long time.
|
||||
Both chips are simply not compatible, value encoding differs.
|
||||
|
||||
sysfs-Interface
|
||||
---------------
|
||||
|
||||
The following list includes the sysfs attributes that the driver provides, their
|
||||
permissions and a short description:
|
||||
|
||||
=============================== ======= ===========================================
|
||||
Name Perm Description
|
||||
=============================== ======= ===========================================
|
||||
temp1_input RO Temperature input
|
||||
temp1_label RO Descriptive name for the sensor
|
||||
temp1_max RW Maximum temperature
|
||||
temp1_max_hyst RW Maximum hysteresis temperature
|
||||
update_interval RW Update conversions interval in milliseconds
|
||||
=============================== ======= ===========================================
|
||||
|
||||
267
Documentation/hwmon/ltc4283.rst
Normal file
267
Documentation/hwmon/ltc4283.rst
Normal file
@@ -0,0 +1,267 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
Kernel drivers ltc4283
|
||||
==========================================
|
||||
|
||||
Supported chips:
|
||||
|
||||
* Analog Devices LTC4283
|
||||
|
||||
Prefix: 'ltc4283'
|
||||
|
||||
Addresses scanned: -
|
||||
|
||||
Datasheet:
|
||||
|
||||
https://www.analog.com/media/en/technical-documentation/data-sheets/ltc4283.pdf
|
||||
|
||||
Author: Nuno Sá <nuno.sa@analog.com>
|
||||
|
||||
Description
|
||||
___________
|
||||
|
||||
The LTC4283 negative voltage hot swap controller drives an external N-channel
|
||||
MOSFET to allow a board to be safely inserted and removed from a live backplane.
|
||||
The device features programmable current limit with foldback and independently
|
||||
adjustable inrush current to optimize the MOSFET safe operating area (SOA). The
|
||||
SOA timer limits MOSFET temperature rise for reliable protection against
|
||||
overstresses. An I2C interface and onboard gear-shift ADC allow monitoring of
|
||||
board current, voltage, power, energy, and fault status. Additional features
|
||||
respond to input UV/OV, interrupt the host when a fault has occurred, notify
|
||||
when output power is good, detect insertion of a board, turn off the MOSFET
|
||||
if an external supply monitor fails to indicate power good within a timeout
|
||||
period, and auto-reboot after a programmable delay following a host commanded
|
||||
turn-off.
|
||||
|
||||
Sysfs entries
|
||||
_____________
|
||||
|
||||
The following attributes are supported. Limits are read-write and all the other
|
||||
attributes are read-only. Note that the VADIOx channels might not be available
|
||||
if the ADIO pins are used as GPIOs (naturally also affects the respective
|
||||
differential channels).
|
||||
|
||||
======================= ==========================================
|
||||
in0_lcrit_alarm Critical Undervoltage alarm
|
||||
in0_crit_alarm Critical Overvoltage alarm
|
||||
in0_reset_history Clears Under and Overvoltage fault logs.
|
||||
in0_label Channel label (VIN)
|
||||
|
||||
in1_input Output 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 history.
|
||||
in1_min_alarm Undervoltage alarm
|
||||
in1_max_alarm Overvoltage alarm
|
||||
in1_label Channel label (VPWR)
|
||||
|
||||
in2_input Output 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 history.
|
||||
in2_min_alarm Undervoltage alarm
|
||||
in2_max_alarm Overvoltage alarm
|
||||
in2_enable Enable/Disable monitoring.
|
||||
in2_label Channel label (VADI1)
|
||||
|
||||
in3_input Output voltage (mV).
|
||||
in3_min Undervoltage threshold
|
||||
in3_max Overvoltage threshold
|
||||
in3_lowest Lowest measured voltage
|
||||
in3_highest Highest measured voltage
|
||||
in3_reset_history Write 1 to reset history.
|
||||
in3_min_alarm Undervoltage alarm
|
||||
in3_max_alarm Overvoltage alarm
|
||||
in3_enable Enable/Disable monitoring.
|
||||
in3_label Channel label (VADI2)
|
||||
|
||||
in4_input Output voltage (mV).
|
||||
in4_min Undervoltage threshold
|
||||
in4_max Overvoltage threshold
|
||||
in4_lowest Lowest measured voltage
|
||||
in4_highest Highest measured voltage
|
||||
in4_reset_history Write 1 to reset history.
|
||||
in4_min_alarm Undervoltage alarm
|
||||
in4_max_alarm Overvoltage alarm
|
||||
in4_enable Enable/Disable monitoring.
|
||||
in4_label Channel label (VADI3)
|
||||
|
||||
in5_input Output voltage (mV).
|
||||
in5_min Undervoltage threshold
|
||||
in5_max Overvoltage threshold
|
||||
in5_lowest Lowest measured voltage
|
||||
in5_highest Highest measured voltage
|
||||
in5_reset_history Write 1 to reset history.
|
||||
in5_min_alarm Undervoltage alarm
|
||||
in5_max_alarm Overvoltage alarm
|
||||
in5_enable Enable/Disable monitoring.
|
||||
in5_label Channel label (VADI4)
|
||||
|
||||
in6_input Output voltage (mV).
|
||||
in6_min Undervoltage threshold
|
||||
in6_max Overvoltage threshold
|
||||
in6_lowest Lowest measured voltage
|
||||
in6_highest Highest measured voltage
|
||||
in6_reset_history Write 1 to reset history.
|
||||
in6_min_alarm Undervoltage alarm
|
||||
in6_max_alarm Overvoltage alarm
|
||||
in6_enable Enable/Disable monitoring.
|
||||
in6_label Channel label (VADIO1)
|
||||
|
||||
in7_input Output voltage (mV).
|
||||
in7_min Undervoltage threshold
|
||||
in7_max Overvoltage threshold
|
||||
in7_lowest Lowest measured voltage
|
||||
in7_highest Highest measured voltage
|
||||
in7_reset_history Write 1 to reset history.
|
||||
in7_min_alarm Undervoltage alarm
|
||||
in7_max_alarm Overvoltage alarm
|
||||
in7_enable Enable/Disable monitoring.
|
||||
in7_label Channel label (VADIO2)
|
||||
|
||||
in8_input Output voltage (mV).
|
||||
in8_min Undervoltage threshold
|
||||
in8_max Overvoltage threshold
|
||||
in8_lowest Lowest measured voltage
|
||||
in8_highest Highest measured voltage
|
||||
in8_reset_history Write 1 to reset history.
|
||||
in8_min_alarm Undervoltage alarm
|
||||
in8_max_alarm Overvoltage alarm
|
||||
in8_enable Enable/Disable monitoring.
|
||||
in8_label Channel label (VADIO3)
|
||||
|
||||
in9_input Output voltage (mV).
|
||||
in9_min Undervoltage threshold
|
||||
in9_max Overvoltage threshold
|
||||
in9_lowest Lowest measured voltage
|
||||
in9_highest Highest measured voltage
|
||||
in9_reset_history Write 1 to reset history.
|
||||
in9_min_alarm Undervoltage alarm
|
||||
in9_max_alarm Overvoltage alarm
|
||||
in9_enable Enable/Disable monitoring.
|
||||
in9_label Channel label (VADIO4)
|
||||
|
||||
in10_input Output voltage (mV).
|
||||
in10_min Undervoltage threshold
|
||||
in10_max Overvoltage threshold
|
||||
in10_lowest Lowest measured voltage
|
||||
in10_highest Highest measured voltage
|
||||
in10_reset_history Write 1 to reset history.
|
||||
in10_min_alarm Undervoltage alarm
|
||||
in10_max_alarm Overvoltage alarm
|
||||
in10_enable Enable/Disable monitoring.
|
||||
in10_label Channel label (DRNS)
|
||||
|
||||
in11_input Output voltage (mV).
|
||||
in11_min Undervoltage threshold
|
||||
in11_max Overvoltage threshold
|
||||
in11_lowest Lowest measured voltage
|
||||
in11_highest Highest measured voltage
|
||||
in11_reset_history Write 1 to reset history.
|
||||
Also clears fet bad and short fault logs.
|
||||
in11_min_alarm Undervoltage alarm
|
||||
in11_max_alarm Overvoltage alarm
|
||||
in11_enable Enable/Disable monitoring
|
||||
in11_fault Failure in the MOSFET. Either bad or shorted FET.
|
||||
in11_label Channel label (DRAIN)
|
||||
|
||||
in12_input Output voltage (mV).
|
||||
in12_min Undervoltage threshold
|
||||
in12_max Overvoltage threshold
|
||||
in12_lowest Lowest measured voltage
|
||||
in12_highest Highest measured voltage
|
||||
in12_reset_history Write 1 to reset history.
|
||||
in12_min_alarm Undervoltage alarm
|
||||
in12_max_alarm Overvoltage alarm
|
||||
in12_enable Enable/Disable monitoring.
|
||||
in12_label Channel label (ADIN2-ADIN1)
|
||||
|
||||
in13_input Output voltage (mV).
|
||||
in13_min Undervoltage threshold
|
||||
in13_max Overvoltage threshold
|
||||
in13_lowest Lowest measured voltage
|
||||
in13_highest Highest measured voltage
|
||||
in13_reset_history Write 1 to reset history.
|
||||
in13_min_alarm Undervoltage alarm
|
||||
in13_max_alarm Overvoltage alarm
|
||||
in13_enable Enable/Disable monitoring.
|
||||
in13_label Channel label (ADIN4-ADIN3)
|
||||
|
||||
in14_input Output voltage (mV).
|
||||
in14_min Undervoltage threshold
|
||||
in14_max Overvoltage threshold
|
||||
in14_lowest Lowest measured voltage
|
||||
in14_highest Highest measured voltage
|
||||
in14_reset_history Write 1 to reset history.
|
||||
in14_min_alarm Undervoltage alarm
|
||||
in14_max_alarm Overvoltage alarm
|
||||
in14_enable Enable/Disable monitoring.
|
||||
in14_label Channel label (ADIO2-ADIO1)
|
||||
|
||||
in15_input Output voltage (mV).
|
||||
in15_min Undervoltage threshold
|
||||
in15_max Overvoltage threshold
|
||||
in15_lowest Lowest measured voltage
|
||||
in15_highest Highest measured voltage
|
||||
in15_reset_history Write 1 to reset history.
|
||||
in15_min_alarm Undervoltage alarm
|
||||
in15_max_alarm Overvoltage alarm
|
||||
in15_enable Enable/Disable monitoring.
|
||||
in15_label Channel label (ADIO4-ADIO3)
|
||||
|
||||
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 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/i2c/i2c-[X]/[X]-addr/``
|
||||
contains the following attributes:
|
||||
|
||||
======================= ==========================================
|
||||
power1_failed_fault_log Set to 1 by a power1 fault occurring.
|
||||
power1_good_input_fault_log Set to 1 by a power1 good input fault occurring at PGIO3.
|
||||
in11_fet_short_fault_log Set to 1 when a FET-short fault occurs.
|
||||
in11_fet_bad_fault_log Set to 1 when a FET-BAD fault occurs.
|
||||
in0_lcrit_fault_log Set to 1 by a VIN undervoltage fault occurring.
|
||||
in0_crit_fault_log Set to 1 by a VIN overvoltage fault occurring.
|
||||
curr1_crit_fault_log Set to 1 by an overcurrent fault occurring.
|
||||
======================= ==========================================
|
||||
90
Documentation/hwmon/lx1308.rst
Normal file
90
Documentation/hwmon/lx1308.rst
Normal file
@@ -0,0 +1,90 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
Kernel driver lx1308
|
||||
====================
|
||||
|
||||
Supported chips:
|
||||
|
||||
* Luxshare LX1308
|
||||
|
||||
Prefixes: 'lx1308'
|
||||
|
||||
Addresses scanned: -
|
||||
|
||||
Datasheet: Datasheet is not publicly available.
|
||||
|
||||
Author: Brian Chiang <chiang.brian@inventec.com>
|
||||
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The LX1308 is a high-efficiency, non-isolated, regulated 12V, 860W,
|
||||
digital DC/DC power module. The module operates from a 40V to 60V DC
|
||||
primary bus and provides a 12V regulated output voltage. It can deliver
|
||||
up to 860W continuous and 1300W in transient.
|
||||
|
||||
The module has slow OCP and fast OCP. If the module output current is higher
|
||||
than slow OCP set point and the lasting time is also longer than the delay,
|
||||
the module will shut down and retry 3 time, if the fault still exists then
|
||||
module enter latch mode.
|
||||
|
||||
If the module output current is higher than fast OCP set point then it shut
|
||||
down and enter latch mode.
|
||||
|
||||
The driver is a client driver to the core PMBus driver.
|
||||
Please see Documentation/hwmon/pmbus.rst for details on PMBus client drivers.
|
||||
|
||||
|
||||
Usage Notes
|
||||
-----------
|
||||
|
||||
This driver does not auto-detect devices. You will have to instantiate the
|
||||
devices explicitly. Please see Documentation/i2c/instantiating-devices.rst for
|
||||
details.
|
||||
|
||||
|
||||
Sysfs entries
|
||||
-------------
|
||||
|
||||
======================= ======================================================
|
||||
curr1_alarm Input current alarm
|
||||
curr1_input Input current (IIN)
|
||||
curr1_label "iin"
|
||||
curr2_crit Output over current fault threshold (slow OCP, 60ms delay)
|
||||
curr2_crit_alarm Output over current fault alarm
|
||||
curr2_input Output current (IOUT)
|
||||
curr2_label "iout1"
|
||||
curr2_max Output over current warning threshold (slow OCP, 60ms delay)
|
||||
curr2_max_alarm Output over current warning alarm
|
||||
in1_crit Input over voltage fault threshold
|
||||
in1_crit_alarm Input over voltage fault alarm
|
||||
in1_input Input voltage (VIN)
|
||||
in1_label "vin"
|
||||
in1_lcrit Input under voltage fault threshold
|
||||
in1_lcrit_alarm Input under voltage fault alarm
|
||||
in1_max Input over voltage warning threshold
|
||||
in1_max_alarm Input over voltage warning alarm
|
||||
in1_min Input under voltage warning threshold
|
||||
in1_min_alarm Input under voltage warning alarm
|
||||
in2_crit Output over voltage fault threshold
|
||||
in2_crit_alarm Output over voltage fault alarm
|
||||
in2_input Output voltage (VOUT)
|
||||
in2_label "vout1"
|
||||
in2_lcrit Output under voltage fault threshold
|
||||
in2_lcrit_alarm Output under voltage fault alarm
|
||||
in2_max Output over voltage warning threshold
|
||||
in2_max_alarm Output over voltage warning alarm
|
||||
in2_min Output under voltage warning threshold
|
||||
in2_min_alarm Output under voltage warning alarm
|
||||
power1_alarm Input power alarm
|
||||
power1_input Input power (PIN)
|
||||
power1_label "pin"
|
||||
power2_input Output power (POUT)
|
||||
power2_label "pout1"
|
||||
temp1_crit Over temperature fault threshold
|
||||
temp1_crit_alarm Over temperature fault alarm
|
||||
temp1_input Module hot spot temperature
|
||||
temp1_max Over temperature warning threshold
|
||||
temp1_max_alarm Over temperature warning alarm
|
||||
======================= ======================================================
|
||||
49
Documentation/hwmon/max20830.rst
Normal file
49
Documentation/hwmon/max20830.rst
Normal file
@@ -0,0 +1,49 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
Kernel driver max20830
|
||||
======================
|
||||
|
||||
Supported chips:
|
||||
|
||||
* Analog Devices MAX20830
|
||||
|
||||
Prefix: 'max20830'
|
||||
|
||||
Addresses scanned: -
|
||||
|
||||
Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max20830.pdf
|
||||
|
||||
Author:
|
||||
|
||||
- Alexis Czezar Torreno <alexisczezar.torreno@analog.com>
|
||||
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
This driver supports hardware monitoring for Analog Devices MAX20830
|
||||
Step-Down Switching Regulator with PMBus Interface.
|
||||
|
||||
The MAX20830 is a 2.7V to 16V, 30A fully integrated step-down DC-DC switching
|
||||
regulator. Through the PMBus interface, the device can monitor input/output
|
||||
voltages, output current and temperature.
|
||||
|
||||
The driver is a client driver to the core PMBus driver. Please see
|
||||
Documentation/hwmon/pmbus.rst for details on PMBus client drivers.
|
||||
|
||||
Sysfs entries
|
||||
-------------
|
||||
|
||||
================= ========================================
|
||||
in1_label "vin"
|
||||
in1_input Measured input voltage
|
||||
in1_alarm Input voltage alarm
|
||||
in2_label "vout1"
|
||||
in2_input Measured output voltage
|
||||
in2_alarm Output voltage alarm
|
||||
curr1_label "iout1"
|
||||
curr1_input Measured output current
|
||||
curr1_alarm Output current alarm
|
||||
temp1_input Measured temperature
|
||||
temp1_alarm Chip temperature alarm
|
||||
================= ========================================
|
||||
57
Documentation/hwmon/max20860a.rst
Normal file
57
Documentation/hwmon/max20860a.rst
Normal file
@@ -0,0 +1,57 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
Kernel driver max20860a
|
||||
=======================
|
||||
|
||||
Supported chips:
|
||||
|
||||
* Analog Devices MAX20860A
|
||||
|
||||
Prefix: 'max20860a'
|
||||
|
||||
Addresses scanned: -
|
||||
|
||||
Datasheet: https://www.analog.com/en/products/max20860a.html
|
||||
|
||||
Author:
|
||||
|
||||
- Syed Arif <arif.syed@hpe.com>
|
||||
- Sanman Pradhan <psanman@juniper.net>
|
||||
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
This driver supports hardware monitoring for Analog Devices MAX20860A
|
||||
Step-Down Switching Regulator with PMBus Interface.
|
||||
|
||||
The MAX20860A is a fully integrated step-down DC-DC switching regulator.
|
||||
Through the PMBus interface, the device can monitor input/output voltages,
|
||||
output current and temperature.
|
||||
|
||||
The driver is a client driver to the core PMBus driver. Please see
|
||||
Documentation/hwmon/pmbus.rst for details on PMBus client drivers.
|
||||
|
||||
Usage Notes
|
||||
-----------
|
||||
|
||||
This driver does not auto-detect devices. You will have to instantiate
|
||||
the devices explicitly.
|
||||
|
||||
Sysfs entries
|
||||
-------------
|
||||
|
||||
================= ========================================
|
||||
in1_label "vin"
|
||||
in1_input Measured input voltage
|
||||
in1_alarm Input voltage alarm
|
||||
in2_label "vout1"
|
||||
in2_input Measured output voltage
|
||||
in2_alarm Output voltage alarm
|
||||
curr1_label "iout1"
|
||||
curr1_input Measured output current
|
||||
curr1_alarm Output current alarm
|
||||
temp1_input Measured temperature
|
||||
temp1_alarm Chip temperature alarm
|
||||
temp2_input Measured temperature (secondary)
|
||||
================= ========================================
|
||||
@@ -1,4 +1,4 @@
|
||||
Kernel driver max31790
|
||||
Kernel driver max31730
|
||||
======================
|
||||
|
||||
Supported chips:
|
||||
|
||||
@@ -19,6 +19,14 @@ Supported chips:
|
||||
|
||||
Datasheet: -
|
||||
|
||||
* ADI ADPM12250
|
||||
|
||||
Prefixes: 'adpm12250'
|
||||
|
||||
Addresses scanned: -
|
||||
|
||||
Datasheet: -
|
||||
|
||||
* Maxim MAX34440
|
||||
|
||||
Prefixes: 'max34440'
|
||||
@@ -87,11 +95,11 @@ This driver supports multiple devices: hardware monitoring for Maxim MAX34440
|
||||
PMBus 6-Channel Power-Supply Manager, MAX34441 PMBus 5-Channel Power-Supply
|
||||
Manager and Intelligent Fan Controller, and MAX34446 PMBus Power-Supply Data
|
||||
Logger; PMBus Voltage Monitor and Sequencers for MAX34451, MAX34460, and
|
||||
MAX34461; PMBus DC/DC Power Module ADPM12160, and ADPM12200. The MAX34451
|
||||
supports monitoring voltage or current of 12 channels based on GIN pins. The
|
||||
MAX34460 supports 12 voltage channels, and the MAX34461 supports 16 voltage
|
||||
channels. The ADPM12160, and ADPM12200 also monitors both input and output
|
||||
of voltage and current.
|
||||
MAX34461; PMBus DC/DC Power Module ADPM12160, ADPM12200, and ADPM12250. The
|
||||
MAX34451 supports monitoring voltage or current of 12 channels based on GIN
|
||||
pins. The MAX34460 supports 12 voltage channels, and the MAX34461 supports 16
|
||||
voltage channels. The ADPM12160, ADPM12200, and ADPM12250 also monitors both
|
||||
input and output of voltage and current.
|
||||
|
||||
The driver is a client driver to the core PMBus driver. Please see
|
||||
Documentation/hwmon/pmbus.rst for details on PMBus client drivers.
|
||||
@@ -149,7 +157,7 @@ in[1-6]_reset_history Write any value to reset history.
|
||||
.. note::
|
||||
|
||||
- MAX34446 only supports in[1-4].
|
||||
- ADPM12160, and ADPM12200 only supports in[1-2]. Label is "vin1"
|
||||
- ADPM12160, ADPM12200, and ADPM12250 only supports in[1-2]. Label is "vin1"
|
||||
and "vout1" respectively.
|
||||
|
||||
Curr
|
||||
@@ -172,8 +180,9 @@ curr[1-6]_reset_history Write any value to reset history.
|
||||
|
||||
- in6 and curr6 attributes only exist for MAX34440.
|
||||
- MAX34446 only supports curr[1-4].
|
||||
- For ADPM12160, and ADPM12200, curr[1] is "iin1" and curr[2-6]
|
||||
are "iout[1-5]".
|
||||
- For ADPM12160, ADPM12200, and ADPM12250, curr[1] is "iin1"
|
||||
- For ADPM12160, and ADPM12200 curr[2-6] are "iout[1-5]".
|
||||
- For ADPM12250, curr[2-4] are "iout[1-3]".
|
||||
|
||||
Power
|
||||
~~~~~
|
||||
@@ -209,7 +218,7 @@ temp[1-8]_reset_history Write any value to reset history.
|
||||
.. note::
|
||||
- temp7 and temp8 attributes only exist for MAX34440.
|
||||
- MAX34446 only supports temp[1-3].
|
||||
- ADPM12160, and ADPM12200 only supports temp[1].
|
||||
- ADPM12160, ADPM12200, and ADPM12250 only supports temp[1].
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
147
Documentation/hwmon/mp2985.rst
Normal file
147
Documentation/hwmon/mp2985.rst
Normal file
@@ -0,0 +1,147 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
Kernel driver mp2985
|
||||
====================
|
||||
|
||||
Supported chips:
|
||||
|
||||
* MPS mp2985
|
||||
|
||||
Prefix: 'mp2985'
|
||||
|
||||
Author:
|
||||
|
||||
Wensheng Wang <wenswang@yeah.net>
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
This driver implements support for Monolithic Power Systems, Inc. (MPS)
|
||||
MP2985 Dual Loop Digital Multi-phase Controller.
|
||||
|
||||
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_crit**
|
||||
|
||||
**in1_crit_alarm**
|
||||
|
||||
**in1_lcrit**
|
||||
|
||||
**in1_lcrit_alarm**
|
||||
|
||||
**in1_max**
|
||||
|
||||
**in1_max_alarm**
|
||||
|
||||
**in1_min**
|
||||
|
||||
**in1_min_alarm**
|
||||
|
||||
The driver provides the following attributes for output voltage:
|
||||
|
||||
**in2_input**
|
||||
|
||||
**in2_label**
|
||||
|
||||
**in2_crit**
|
||||
|
||||
**in2_crit_alarm**
|
||||
|
||||
**in2_lcrit**
|
||||
|
||||
**in2_lcrit_alarm**
|
||||
|
||||
**in3_input**
|
||||
|
||||
**in3_label**
|
||||
|
||||
**in3_crit**
|
||||
|
||||
**in3_crit_alarm**
|
||||
|
||||
**in3_lcrit**
|
||||
|
||||
**in3_lcrit_alarm**
|
||||
|
||||
The driver provides the following attributes for input current:
|
||||
|
||||
**curr1_input**
|
||||
|
||||
**curr1_label**
|
||||
|
||||
The driver provides the following attributes for output current:
|
||||
|
||||
**curr2_input**
|
||||
|
||||
**curr2_label**
|
||||
|
||||
**curr2_crit**
|
||||
|
||||
**curr2_crit_alarm**
|
||||
|
||||
**curr2_max**
|
||||
|
||||
**curr2_max_alarm**
|
||||
|
||||
**curr3_input**
|
||||
|
||||
**curr3_label**
|
||||
|
||||
**curr3_crit**
|
||||
|
||||
**curr3_crit_alarm**
|
||||
|
||||
**curr3_max**
|
||||
|
||||
**curr3_max_alarm**
|
||||
|
||||
The driver provides the following attributes for input power:
|
||||
|
||||
**power1_input**
|
||||
|
||||
**power1_label**
|
||||
|
||||
**power2_input**
|
||||
|
||||
**power2_label**
|
||||
|
||||
The driver provides the following attributes for output power:
|
||||
|
||||
**power3_input**
|
||||
|
||||
**power3_label**
|
||||
|
||||
**power4_input**
|
||||
|
||||
**power4_label**
|
||||
|
||||
The driver provides the following attributes for temperature:
|
||||
|
||||
**temp1_input**
|
||||
|
||||
**temp1_crit**
|
||||
|
||||
**temp1_crit_alarm**
|
||||
|
||||
**temp1_max**
|
||||
|
||||
**temp1_max_alarm**
|
||||
|
||||
**temp2_input**
|
||||
|
||||
**temp2_crit**
|
||||
|
||||
**temp2_crit_alarm**
|
||||
|
||||
**temp2_max**
|
||||
|
||||
**temp2_max_alarm**
|
||||
@@ -24,6 +24,22 @@ speed sensors.
|
||||
|
||||
Smart Fan™ speed control is available via pwmX_auto_point attributes.
|
||||
|
||||
Sysfs Attributes
|
||||
----------------
|
||||
|
||||
Sysfs attributes unique to this chip are documented below. For common
|
||||
attributes, see Documentation/hwmon/sysfs-interface.rst.
|
||||
|
||||
step_up_time
|
||||
Time interval between successive duty cycle increases
|
||||
when in Smart Fan mode. Specified in milliseconds and
|
||||
rounded to intervals of 100 in the range 100-25500.
|
||||
|
||||
step_down_time
|
||||
Time interval between successive duty cycle decreases
|
||||
when in Smart Fan mode. Specified in milliseconds and
|
||||
rounded to intervals of 100 in the range 100-25500.
|
||||
|
||||
Tested Boards and BIOS Versions
|
||||
-------------------------------
|
||||
|
||||
|
||||
@@ -3,17 +3,18 @@ Kernel driver pmbus
|
||||
|
||||
Supported chips:
|
||||
|
||||
* Flex BMR310, BMR453, BMR454, BMR456, BMR457, BMR458, BMR480,
|
||||
BMR490, BMR491, BMR492
|
||||
* Flex BMR310, BMR316, BMR321, BMR350, BMR351, BMR453, BMR454,
|
||||
BMR456, BMR457, BMR458, BMR480, BMR490, BMR491, BMR492
|
||||
|
||||
Prefixes: 'bmr310', 'bmr453', 'bmr454', 'bmr456', 'bmr457', 'bmr458', 'bmr480',
|
||||
Prefixes: 'bmr310', 'bmr316', 'bmr321', 'bmr350', 'bmr351',
|
||||
'bmr453', 'bmr454', 'bmr456', 'bmr457', 'bmr458', 'bmr480',
|
||||
'bmr490', 'bmr491', 'bmr492'
|
||||
|
||||
Addresses scanned: -
|
||||
|
||||
Datasheets:
|
||||
|
||||
https://flexpowermodules.com/products
|
||||
https://flex.com/products/power-modules/product-selector
|
||||
|
||||
|
||||
* ON Semiconductor ADP4000, NCP4200, NCP4208
|
||||
|
||||
@@ -20,6 +20,17 @@ undervoltage conditions.
|
||||
Sysfs entries
|
||||
-------------
|
||||
|
||||
======================= ==================
|
||||
======================= ======================================================
|
||||
in0_input Core voltage in millivolts
|
||||
in1_input SDRAM controller voltage in millivolts
|
||||
in2_input SDRAM I/O voltage in millivolts
|
||||
in3_input SDRAM PHY voltage in millivolts
|
||||
in0_label "core"
|
||||
in1_label "sdram_c"
|
||||
in2_label "sdram_i"
|
||||
in3_label "sdram_p"
|
||||
in0_lcrit_alarm Undervoltage alarm
|
||||
======================= ==================
|
||||
======================= ======================================================
|
||||
|
||||
The voltage inputs and labels are only exposed if the firmware reports support
|
||||
for the corresponding voltage ID.
|
||||
|
||||
@@ -106,6 +106,10 @@ Global attributes
|
||||
`update_interval`
|
||||
The interval at which the chip will update readings.
|
||||
|
||||
`update_interval_us`
|
||||
The interval at which the chip will update readings,
|
||||
expressed in microseconds for finer resolution.
|
||||
|
||||
|
||||
********
|
||||
Voltages
|
||||
|
||||
55
MAINTAINERS
55
MAINTAINERS
@@ -2077,6 +2077,13 @@ S: Maintained
|
||||
F: drivers/net/arcnet/
|
||||
F: include/uapi/linux/if_arcnet.h
|
||||
|
||||
ARCTIC FAN CONTROLLER DRIVER
|
||||
M: Aureo Serrano de Souza <aureo.serrano@arctic.de>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/hwmon/arctic_fan_controller.rst
|
||||
F: drivers/hwmon/arctic_fan_controller.c
|
||||
|
||||
ARM AND ARM64 SoC SUB-ARCHITECTURES (COMMON PARTS)
|
||||
M: Arnd Bergmann <arnd@arndb.de>
|
||||
M: Krzysztof Kozlowski <krzk@kernel.org>
|
||||
@@ -15251,6 +15258,15 @@ F: Documentation/devicetree/bindings/hwmon/adi,ltc4282.yaml
|
||||
F: Documentation/hwmon/ltc4282.rst
|
||||
F: drivers/hwmon/ltc4282.c
|
||||
|
||||
LTC4283 HARDWARE MONITOR AND GPIO DRIVER
|
||||
M: Nuno Sá <nuno.sa@analog.com>
|
||||
L: linux-gpio@vger.kernel.org
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
S: Supported
|
||||
F: Documentation/devicetree/bindings/hwmon/adi,ltc4283.yaml
|
||||
F: drivers/gpio/gpio-ltc4283.c
|
||||
F: drivers/hwmon/ltc4283.c
|
||||
|
||||
LTC4286 HARDWARE MONITOR DRIVER
|
||||
M: Delphine CC Chiu <Delphine_CC_Chiu@Wiwynn.com>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
@@ -15683,6 +15699,23 @@ F: Documentation/devicetree/bindings/hwmon/pmbus/adi,max17616.yaml
|
||||
F: Documentation/hwmon/max17616.rst
|
||||
F: drivers/hwmon/pmbus/max17616.c
|
||||
|
||||
MAX20830 HARDWARE MONITOR DRIVER
|
||||
M: Alexis Czezar Torreno <alexisczezar.torreno@analog.com>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
S: Supported
|
||||
W: https://ez.analog.com/linux-software-drivers
|
||||
F: Documentation/devicetree/bindings/hwmon/pmbus/adi,max20830.yaml
|
||||
F: Documentation/hwmon/max20830.rst
|
||||
F: drivers/hwmon/pmbus/max20830.c
|
||||
|
||||
MAX20860A HARDWARE MONITOR DRIVER
|
||||
M: Sanman Pradhan <psanman@juniper.net>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/hwmon/pmbus/adi,max20860a.yaml
|
||||
F: Documentation/hwmon/max20860a.rst
|
||||
F: drivers/hwmon/pmbus/max20860a.c
|
||||
|
||||
MAX2175 SDR TUNER DRIVER
|
||||
M: Ramesh Shanmugasundaram <rashanmu@gmail.com>
|
||||
L: linux-media@vger.kernel.org
|
||||
@@ -17370,6 +17403,14 @@ S: Supported
|
||||
F: Documentation/devicetree/bindings/interrupt-controller/microchip,sama7g5-eic.yaml
|
||||
F: drivers/irqchip/irq-mchp-eic.c
|
||||
|
||||
MICROCHIP EMC1812 DRIVER
|
||||
M: Marius Cristea <marius.cristea@microchip.com>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
S: Supported
|
||||
F: Documentation/devicetree/bindings/hwmon/microchip,emc1812.yaml
|
||||
F: Documentation/hwmon/emc1812.rst
|
||||
F: drivers/hwmon/emc1812.c
|
||||
|
||||
MICROCHIP I2C DRIVER
|
||||
M: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
|
||||
L: linux-i2c@vger.kernel.org
|
||||
@@ -18091,6 +18132,13 @@ S: Maintained
|
||||
F: Documentation/hwmon/mp29502.rst
|
||||
F: drivers/hwmon/pmbus/mp29502.c
|
||||
|
||||
MPS MP2985 DRIVER
|
||||
M: Wensheng Wang <wenswang@yeah.net>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/hwmon/mp2985.rst
|
||||
F: drivers/hwmon/pmbus/mp2985.c
|
||||
|
||||
MPS MP2993 DRIVER
|
||||
M: Noah Wang <noahwang.wang@outlook.com>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
@@ -18267,6 +18315,13 @@ F: drivers/mux/
|
||||
F: include/dt-bindings/mux/
|
||||
F: include/linux/mux/
|
||||
|
||||
MURATA D1U74T PSU DRIVER
|
||||
M: Abdurrahman Hussain <abdurrahman@nexthop.ai>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/hwmon/d1u74t.rst
|
||||
F: drivers/hwmon/pmbus/d1u74t.c
|
||||
|
||||
MUSB MULTIPOINT HIGH SPEED DUAL-ROLE CONTROLLER
|
||||
M: Bin Liu <b-liu@ti.com>
|
||||
L: linux-usb@vger.kernel.org
|
||||
|
||||
@@ -1793,6 +1793,21 @@ config GPIO_WM8994
|
||||
|
||||
endmenu
|
||||
|
||||
menu "Auxiliary Bus GPIO drivers"
|
||||
depends on AUXILIARY_BUS
|
||||
|
||||
config GPIO_LTC4283
|
||||
tristate "Analog Devices LTC4283 GPIO support"
|
||||
depends on SENSORS_LTC4283
|
||||
help
|
||||
If you say yes here you want the GPIO function available in Analog
|
||||
Devices LTC4283 Negative Voltage Hot Swap Controller.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called gpio-ltc4283.
|
||||
|
||||
endmenu
|
||||
|
||||
menu "PCI GPIO expanders"
|
||||
depends on PCI
|
||||
|
||||
|
||||
@@ -101,6 +101,7 @@ obj-$(CONFIG_GPIO_LP873X) += gpio-lp873x.o
|
||||
obj-$(CONFIG_GPIO_LP87565) += gpio-lp87565.o
|
||||
obj-$(CONFIG_GPIO_LPC18XX) += gpio-lpc18xx.o
|
||||
obj-$(CONFIG_GPIO_LPC32XX) += gpio-lpc32xx.o
|
||||
obj-$(CONFIG_GPIO_LTC4283) += gpio-ltc4283.o
|
||||
obj-$(CONFIG_GPIO_MACSMC) += gpio-macsmc.o
|
||||
obj-$(CONFIG_GPIO_MADERA) += gpio-madera.o
|
||||
obj-$(CONFIG_GPIO_MAX3191X) += gpio-max3191x.o
|
||||
|
||||
218
drivers/gpio/gpio-ltc4283.c
Normal file
218
drivers/gpio/gpio-ltc4283.c
Normal file
@@ -0,0 +1,218 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Analog Devices LTC4283 GPIO driver
|
||||
*
|
||||
* Copyright 2025 Analog Devices Inc.
|
||||
*/
|
||||
|
||||
#include <linux/auxiliary_bus.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#define LTC4283_PINS_MAX 8
|
||||
#define LTC4283_PGIOX_START_NR 4
|
||||
#define LTC4283_INPUT_STATUS 0x02
|
||||
#define LTC4283_PGIO_CONFIG 0x10
|
||||
#define LTC4283_PGIO_CFG_MASK(pin) \
|
||||
GENMASK(((pin) - LTC4283_PGIOX_START_NR) * 2 + 1, (((pin) - LTC4283_PGIOX_START_NR) * 2))
|
||||
#define LTC4283_PGIO_CONFIG_2 0x11
|
||||
|
||||
#define LTC4283_ADIO_CONFIG 0x12
|
||||
/* starts at bit 4 */
|
||||
#define LTC4283_ADIOX_CONFIG_MASK(pin) BIT((pin) + 4)
|
||||
#define LTC4283_PGIO_DIR_IN 3
|
||||
#define LTC4283_PGIO_DIR_OUT 2
|
||||
|
||||
struct ltc4283_gpio {
|
||||
struct gpio_chip gpio_chip;
|
||||
struct regmap *regmap;
|
||||
};
|
||||
|
||||
static int ltc4283_pgio_get_direction(const struct ltc4283_gpio *st, unsigned int off)
|
||||
{
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(st->regmap, LTC4283_PGIO_CONFIG, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val = field_get(LTC4283_PGIO_CFG_MASK(off), val);
|
||||
if (val == LTC4283_PGIO_DIR_IN)
|
||||
return GPIO_LINE_DIRECTION_IN;
|
||||
|
||||
return GPIO_LINE_DIRECTION_OUT;
|
||||
}
|
||||
|
||||
static int ltc4283_gpio_get_direction(struct gpio_chip *gc, unsigned int off)
|
||||
{
|
||||
struct ltc4283_gpio *st = gpiochip_get_data(gc);
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
if (off >= LTC4283_PGIOX_START_NR)
|
||||
return ltc4283_pgio_get_direction(st, off);
|
||||
|
||||
ret = regmap_read(st->regmap, LTC4283_ADIO_CONFIG, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (val & LTC4283_ADIOX_CONFIG_MASK(off))
|
||||
return GPIO_LINE_DIRECTION_IN;
|
||||
|
||||
return GPIO_LINE_DIRECTION_OUT;
|
||||
}
|
||||
|
||||
static int ltc4283_gpio_direction_set(const struct ltc4283_gpio *st,
|
||||
unsigned int off, bool input)
|
||||
{
|
||||
if (off >= LTC4283_PGIOX_START_NR) {
|
||||
unsigned int val = LTC4283_PGIO_DIR_OUT;
|
||||
|
||||
if (input)
|
||||
val = LTC4283_PGIO_DIR_IN;
|
||||
|
||||
val = field_prep(LTC4283_PGIO_CFG_MASK(off), val);
|
||||
return regmap_update_bits(st->regmap, LTC4283_PGIO_CONFIG,
|
||||
LTC4283_PGIO_CFG_MASK(off), val);
|
||||
}
|
||||
|
||||
return regmap_update_bits(st->regmap, LTC4283_ADIO_CONFIG,
|
||||
LTC4283_ADIOX_CONFIG_MASK(off),
|
||||
field_prep(LTC4283_ADIOX_CONFIG_MASK(off), input));
|
||||
}
|
||||
|
||||
static int __ltc4283_gpio_set_value(const struct ltc4283_gpio *st,
|
||||
unsigned int off, int val)
|
||||
{
|
||||
u32 reg = off < LTC4283_PGIOX_START_NR ? LTC4283_ADIO_CONFIG : LTC4283_PGIO_CONFIG_2;
|
||||
|
||||
return regmap_update_bits(st->regmap, reg, BIT(off),
|
||||
field_prep(BIT(off), !!val));
|
||||
}
|
||||
|
||||
static int ltc4283_gpio_direction_input(struct gpio_chip *gc, unsigned int off)
|
||||
{
|
||||
struct ltc4283_gpio *st = gpiochip_get_data(gc);
|
||||
|
||||
return ltc4283_gpio_direction_set(st, off, true);
|
||||
}
|
||||
|
||||
static int ltc4283_gpio_direction_output(struct gpio_chip *gc, unsigned int off, int val)
|
||||
{
|
||||
struct ltc4283_gpio *st = gpiochip_get_data(gc);
|
||||
int ret;
|
||||
|
||||
ret = ltc4283_gpio_direction_set(st, off, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return __ltc4283_gpio_set_value(st, off, val);
|
||||
}
|
||||
|
||||
static int ltc4283_gpio_get_value(struct gpio_chip *gc, unsigned int off)
|
||||
{
|
||||
struct ltc4283_gpio *st = gpiochip_get_data(gc);
|
||||
unsigned int val, reg;
|
||||
int ret, dir;
|
||||
|
||||
dir = ltc4283_gpio_get_direction(gc, off);
|
||||
if (dir < 0)
|
||||
return dir;
|
||||
|
||||
if (dir == GPIO_LINE_DIRECTION_IN) {
|
||||
ret = regmap_read(st->regmap, LTC4283_INPUT_STATUS, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* ADIO1 is at bit 3. */
|
||||
if (off < LTC4283_PGIOX_START_NR)
|
||||
return !!(val & BIT(3 - off));
|
||||
|
||||
/* PGIO1 is at bit 7. */
|
||||
return !!(val & BIT(7 - (off - LTC4283_PGIOX_START_NR)));
|
||||
}
|
||||
|
||||
if (off < LTC4283_PGIOX_START_NR)
|
||||
reg = LTC4283_ADIO_CONFIG;
|
||||
else
|
||||
reg = LTC4283_PGIO_CONFIG_2;
|
||||
|
||||
ret = regmap_read(st->regmap, reg, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return !!(val & BIT(off));
|
||||
}
|
||||
|
||||
static int ltc4283_gpio_set_value(struct gpio_chip *gc, unsigned int off, int val)
|
||||
{
|
||||
struct ltc4283_gpio *st = gpiochip_get_data(gc);
|
||||
|
||||
return __ltc4283_gpio_set_value(st, off, val);
|
||||
}
|
||||
|
||||
static int ltc4283_init_valid_mask(struct gpio_chip *gc, unsigned long *valid_mask,
|
||||
unsigned int ngpios)
|
||||
{
|
||||
unsigned long *mask = dev_get_platdata(gc->parent);
|
||||
|
||||
bitmap_copy(valid_mask, mask, ngpios);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ltc4283_gpio_probe(struct auxiliary_device *adev,
|
||||
const struct auxiliary_device_id *id)
|
||||
{
|
||||
struct device *dev = &adev->dev;
|
||||
struct ltc4283_gpio *st;
|
||||
struct gpio_chip *gc;
|
||||
|
||||
st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
|
||||
if (!st)
|
||||
return -ENOMEM;
|
||||
|
||||
st->regmap = dev_get_regmap(dev->parent, NULL);
|
||||
if (!st->regmap)
|
||||
return dev_err_probe(dev, -ENODEV,
|
||||
"Failed to get regmap\n");
|
||||
|
||||
gc = &st->gpio_chip;
|
||||
gc->parent = dev;
|
||||
gc->get_direction = ltc4283_gpio_get_direction;
|
||||
gc->direction_input = ltc4283_gpio_direction_input;
|
||||
gc->direction_output = ltc4283_gpio_direction_output;
|
||||
gc->get = ltc4283_gpio_get_value;
|
||||
gc->set = ltc4283_gpio_set_value;
|
||||
gc->init_valid_mask = ltc4283_init_valid_mask;
|
||||
gc->can_sleep = true;
|
||||
|
||||
gc->base = -1;
|
||||
gc->ngpio = LTC4283_PINS_MAX;
|
||||
gc->label = adev->name;
|
||||
gc->owner = THIS_MODULE;
|
||||
|
||||
return devm_gpiochip_add_data(dev, &st->gpio_chip, st);
|
||||
}
|
||||
|
||||
static const struct auxiliary_device_id ltc4283_aux_id_table[] = {
|
||||
{ "ltc4283.gpio" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(auxiliary, ltc4283_aux_id_table);
|
||||
|
||||
static struct auxiliary_driver ltc4283_gpio_driver = {
|
||||
.probe = ltc4283_gpio_probe,
|
||||
.id_table = ltc4283_aux_id_table,
|
||||
};
|
||||
module_auxiliary_driver(ltc4283_gpio_driver);
|
||||
|
||||
MODULE_AUTHOR("Nuno Sá <nuno.sa@analog.com>");
|
||||
MODULE_DESCRIPTION("GPIO LTC4283 Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -388,6 +388,18 @@ config SENSORS_APPLESMC
|
||||
Say Y here if you have an applicable laptop and want to experience
|
||||
the awesome power of applesmc.
|
||||
|
||||
config SENSORS_ARCTIC_FAN_CONTROLLER
|
||||
tristate "ARCTIC Fan Controller"
|
||||
depends on USB_HID
|
||||
help
|
||||
If you say yes here you get support for the ARCTIC Fan Controller,
|
||||
a USB HID device (VID 0x3904, PID 0xF001) with 10 fan channels.
|
||||
The driver exposes fan speed (RPM) and PWM control via the hwmon
|
||||
sysfs interface.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called arctic_fan_controller.
|
||||
|
||||
config SENSORS_ARM_SCMI
|
||||
tristate "ARM SCMI Sensors"
|
||||
depends on ARM_SCMI_PROTOCOL
|
||||
@@ -1157,6 +1169,18 @@ config SENSORS_LTC4282
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called ltc4282.
|
||||
|
||||
config SENSORS_LTC4283
|
||||
tristate "Analog Devices LTC4283"
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
select AUXILIARY_BUS
|
||||
help
|
||||
If you say yes here you get support for Analog Devices LTC4283
|
||||
Negative Voltage Hot Swap Controller I2C interface.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called ltc4283.
|
||||
|
||||
config SENSORS_LTQ_CPUTEMP
|
||||
bool "Lantiq cpu temperature sensor driver"
|
||||
depends on SOC_XWAY
|
||||
@@ -2044,6 +2068,17 @@ config SENSORS_EMC1403
|
||||
Threshold values can be configured using sysfs.
|
||||
Data from the different diodes are accessible via sysfs.
|
||||
|
||||
config SENSORS_EMC1812
|
||||
tristate "Microchip Technology EMC1812 driver"
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
help
|
||||
If you say yes here to build support for Microchip Technology's
|
||||
EMC181X/33 Multichannel Low-Voltage Remote Diode Sensor Family.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called emc1812.
|
||||
|
||||
config SENSORS_EMC2103
|
||||
tristate "SMSC EMC2103"
|
||||
depends on I2C
|
||||
|
||||
@@ -49,6 +49,7 @@ obj-$(CONFIG_SENSORS_ADT7475) += adt7475.o
|
||||
obj-$(CONFIG_SENSORS_AHT10) += aht10.o
|
||||
obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o
|
||||
obj-$(CONFIG_SENSORS_AQUACOMPUTER_D5NEXT) += aquacomputer_d5next.o
|
||||
obj-$(CONFIG_SENSORS_ARCTIC_FAN_CONTROLLER) += arctic_fan_controller.o
|
||||
obj-$(CONFIG_SENSORS_ARM_SCMI) += scmi-hwmon.o
|
||||
obj-$(CONFIG_SENSORS_ARM_SCPI) += scpi-hwmon.o
|
||||
obj-$(CONFIG_SENSORS_AS370) += as370-hwmon.o
|
||||
@@ -72,6 +73,7 @@ obj-$(CONFIG_SENSORS_DRIVETEMP) += drivetemp.o
|
||||
obj-$(CONFIG_SENSORS_DS620) += ds620.o
|
||||
obj-$(CONFIG_SENSORS_DS1621) += ds1621.o
|
||||
obj-$(CONFIG_SENSORS_EMC1403) += emc1403.o
|
||||
obj-$(CONFIG_SENSORS_EMC1812) += emc1812.o
|
||||
obj-$(CONFIG_SENSORS_EMC2103) += emc2103.o
|
||||
obj-$(CONFIG_SENSORS_EMC2305) += emc2305.o
|
||||
obj-$(CONFIG_SENSORS_EMC6W201) += emc6w201.o
|
||||
@@ -147,6 +149,7 @@ 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_LTC4283) += ltc4283.o
|
||||
obj-$(CONFIG_SENSORS_LTQ_CPUTEMP) += ltq-cputemp.o
|
||||
obj-$(CONFIG_SENSORS_MACSMC_HWMON) += macsmc-hwmon.o
|
||||
obj-$(CONFIG_SENSORS_MAX1111) += max1111.o
|
||||
|
||||
@@ -205,8 +205,8 @@ static int ad7414_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ad7414_id[] = {
|
||||
{ "ad7414" },
|
||||
{}
|
||||
{ .name = "ad7414" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ad7414_id);
|
||||
|
||||
|
||||
@@ -281,9 +281,9 @@ static int ad7418_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ad7418_id[] = {
|
||||
{ "ad7416", ad7416 },
|
||||
{ "ad7417", ad7417 },
|
||||
{ "ad7418", ad7418 },
|
||||
{ .name = "ad7416", .driver_data = ad7416 },
|
||||
{ .name = "ad7417", .driver_data = ad7417 },
|
||||
{ .name = "ad7418", .driver_data = ad7418 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ad7418_id);
|
||||
|
||||
@@ -480,7 +480,7 @@ static int adc128_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id adc128_id[] = {
|
||||
{ "adc128d818" },
|
||||
{ .name = "adc128d818" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, adc128_id);
|
||||
|
||||
@@ -548,8 +548,8 @@ static int adm1025_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id adm1025_id[] = {
|
||||
{ "adm1025", adm1025 },
|
||||
{ "ne1619", ne1619 },
|
||||
{ .name = "adm1025", .driver_data = adm1025 },
|
||||
{ .name = "ne1619", .driver_data = ne1619 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, adm1025_id);
|
||||
|
||||
@@ -1857,7 +1857,7 @@ static int adm1026_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id adm1026_id[] = {
|
||||
{ "adm1026" },
|
||||
{ .name = "adm1026" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, adm1026_id);
|
||||
|
||||
@@ -382,7 +382,7 @@ static int adm1029_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id adm1029_id[] = {
|
||||
{ "adm1029" },
|
||||
{ .name = "adm1029" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, adm1029_id);
|
||||
|
||||
@@ -1055,8 +1055,8 @@ static int adm1031_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id adm1031_id[] = {
|
||||
{ "adm1030", adm1030 },
|
||||
{ "adm1031", adm1031 },
|
||||
{ .name = "adm1030", .driver_data = adm1030 },
|
||||
{ .name = "adm1031", .driver_data = adm1031 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, adm1031_id);
|
||||
|
||||
@@ -246,8 +246,8 @@ static int adm1177_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id adm1177_id[] = {
|
||||
{"adm1177"},
|
||||
{}
|
||||
{ .name = "adm1177" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, adm1177_id);
|
||||
|
||||
|
||||
@@ -794,9 +794,9 @@ static int adm9240_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id adm9240_id[] = {
|
||||
{ "adm9240", adm9240 },
|
||||
{ "ds1780", ds1780 },
|
||||
{ "lm81", lm81 },
|
||||
{ .name = "adm9240", .driver_data = adm9240 },
|
||||
{ .name = "ds1780", .driver_data = ds1780 },
|
||||
{ .name = "lm81", .driver_data = lm81 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, adm9240_id);
|
||||
|
||||
@@ -176,8 +176,8 @@ static int ads7828_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ads7828_device_ids[] = {
|
||||
{ "ads7828", ads7828 },
|
||||
{ "ads7830", ads7830 },
|
||||
{ .name = "ads7828", .driver_data = ads7828 },
|
||||
{ .name = "ads7830", .driver_data = ads7830 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ads7828_device_ids);
|
||||
|
||||
@@ -56,7 +56,6 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
@@ -64,8 +63,19 @@
|
||||
|
||||
struct ads7871_data {
|
||||
struct spi_device *spi;
|
||||
u8 tx_buf[2] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static umode_t ads7871_is_visible(const void *data,
|
||||
enum hwmon_sensor_types type,
|
||||
u32 attr, int channel)
|
||||
{
|
||||
if (type == hwmon_in && attr == hwmon_in_input)
|
||||
return 0444;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ads7871_read_reg8(struct spi_device *spi, int reg)
|
||||
{
|
||||
int ret;
|
||||
@@ -86,29 +96,31 @@ static int ads7871_read_reg16(struct spi_device *spi, int reg)
|
||||
return le16_to_cpu((__force __le16)ret);
|
||||
}
|
||||
|
||||
static int ads7871_write_reg8(struct spi_device *spi, int reg, u8 val)
|
||||
static int ads7871_write_reg8(struct ads7871_data *pdata, int reg, u8 val)
|
||||
{
|
||||
u8 tmp[2] = {reg, val};
|
||||
return spi_write(spi, tmp, sizeof(tmp));
|
||||
pdata->tx_buf[0] = reg;
|
||||
pdata->tx_buf[1] = val;
|
||||
|
||||
return spi_write(pdata->spi, pdata->tx_buf, 2);
|
||||
}
|
||||
|
||||
static ssize_t voltage_show(struct device *dev, struct device_attribute *da,
|
||||
char *buf)
|
||||
static int ads7871_read(struct device *dev, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel, long *val)
|
||||
{
|
||||
struct ads7871_data *pdata = dev_get_drvdata(dev);
|
||||
struct spi_device *spi = pdata->spi;
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
int ret, val, i = 0;
|
||||
uint8_t channel, mux_cnv;
|
||||
int ret, raw_val, i = 0;
|
||||
u8 mux_cnv;
|
||||
|
||||
channel = attr->index;
|
||||
if (type != hwmon_in || attr != hwmon_in_input)
|
||||
return -EOPNOTSUPP;
|
||||
/*
|
||||
* TODO: add support for conversions
|
||||
* other than single ended with a gain of 1
|
||||
*/
|
||||
/*MUX_M3_BM forces single ended*/
|
||||
/*This is also where the gain of the PGA would be set*/
|
||||
ret = ads7871_write_reg8(spi, REG_GAIN_MUX,
|
||||
ret = ads7871_write_reg8(pdata, REG_GAIN_MUX,
|
||||
(MUX_CNV_BM | MUX_M3_BM | channel));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@@ -116,6 +128,7 @@ static ssize_t voltage_show(struct device *dev, struct device_attribute *da,
|
||||
ret = ads7871_read_reg8(spi, REG_GAIN_MUX);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
mux_cnv = ((ret & MUX_CNV_BM) >> MUX_CNV_BV);
|
||||
/*
|
||||
* on 400MHz arm9 platform the conversion
|
||||
@@ -131,39 +144,37 @@ static ssize_t voltage_show(struct device *dev, struct device_attribute *da,
|
||||
}
|
||||
|
||||
if (mux_cnv == 0) {
|
||||
val = ads7871_read_reg16(spi, REG_LS_BYTE);
|
||||
if (val < 0)
|
||||
return val;
|
||||
/*result in volts*10000 = (val/8192)*2.5*10000*/
|
||||
val = ((val >> 2) * 25000) / 8192;
|
||||
return sysfs_emit(buf, "%d\n", val);
|
||||
raw_val = ads7871_read_reg16(spi, REG_LS_BYTE);
|
||||
if (raw_val < 0)
|
||||
return raw_val;
|
||||
|
||||
/*
|
||||
* Use (s16) to ensure the sign bit is preserved during the shift.
|
||||
* Report millivolts (2.5V = 2500mV).
|
||||
*/
|
||||
*val = ((s16)raw_val >> 2) * 2500 / 8192;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static SENSOR_DEVICE_ATTR_RO(in0_input, voltage, 0);
|
||||
static SENSOR_DEVICE_ATTR_RO(in1_input, voltage, 1);
|
||||
static SENSOR_DEVICE_ATTR_RO(in2_input, voltage, 2);
|
||||
static SENSOR_DEVICE_ATTR_RO(in3_input, voltage, 3);
|
||||
static SENSOR_DEVICE_ATTR_RO(in4_input, voltage, 4);
|
||||
static SENSOR_DEVICE_ATTR_RO(in5_input, voltage, 5);
|
||||
static SENSOR_DEVICE_ATTR_RO(in6_input, voltage, 6);
|
||||
static SENSOR_DEVICE_ATTR_RO(in7_input, voltage, 7);
|
||||
|
||||
static struct attribute *ads7871_attrs[] = {
|
||||
&sensor_dev_attr_in0_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in3_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in4_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in5_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in6_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in7_input.dev_attr.attr,
|
||||
static const struct hwmon_channel_info * const ads7871_info[] = {
|
||||
HWMON_CHANNEL_INFO(in,
|
||||
HWMON_I_INPUT, HWMON_I_INPUT, HWMON_I_INPUT, HWMON_I_INPUT,
|
||||
HWMON_I_INPUT, HWMON_I_INPUT, HWMON_I_INPUT, HWMON_I_INPUT),
|
||||
NULL
|
||||
};
|
||||
|
||||
ATTRIBUTE_GROUPS(ads7871);
|
||||
static const struct hwmon_ops ads7871_hwmon_ops = {
|
||||
.is_visible = ads7871_is_visible,
|
||||
.read = ads7871_read,
|
||||
};
|
||||
|
||||
static const struct hwmon_chip_info ads7871_chip_info = {
|
||||
.ops = &ads7871_hwmon_ops,
|
||||
.info = ads7871_info,
|
||||
};
|
||||
|
||||
static int ads7871_probe(struct spi_device *spi)
|
||||
{
|
||||
@@ -178,11 +189,17 @@ static int ads7871_probe(struct spi_device *spi)
|
||||
spi->bits_per_word = 8;
|
||||
spi_setup(spi);
|
||||
|
||||
ads7871_write_reg8(spi, REG_SER_CONTROL, 0);
|
||||
ads7871_write_reg8(spi, REG_AD_CONTROL, 0);
|
||||
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return -ENOMEM;
|
||||
|
||||
pdata->spi = spi;
|
||||
|
||||
ads7871_write_reg8(pdata, REG_SER_CONTROL, 0);
|
||||
ads7871_write_reg8(pdata, REG_AD_CONTROL, 0);
|
||||
|
||||
val = (OSC_OSCR_BM | OSC_OSCE_BM | OSC_REFE_BM | OSC_BUFE_BM);
|
||||
ads7871_write_reg8(spi, REG_OSC_CONTROL, val);
|
||||
ads7871_write_reg8(pdata, REG_OSC_CONTROL, val);
|
||||
ret = ads7871_read_reg8(spi, REG_OSC_CONTROL);
|
||||
|
||||
dev_dbg(dev, "REG_OSC_CONTROL write:%x, read:%x\n", val, ret);
|
||||
@@ -193,15 +210,10 @@ static int ads7871_probe(struct spi_device *spi)
|
||||
if (val != ret)
|
||||
return -ENODEV;
|
||||
|
||||
pdata = devm_kzalloc(dev, sizeof(struct ads7871_data), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return -ENOMEM;
|
||||
|
||||
pdata->spi = spi;
|
||||
|
||||
hwmon_dev = devm_hwmon_device_register_with_groups(dev, spi->modalias,
|
||||
pdata,
|
||||
ads7871_groups);
|
||||
hwmon_dev = devm_hwmon_device_register_with_info(dev, spi->modalias,
|
||||
pdata,
|
||||
&ads7871_chip_info,
|
||||
NULL);
|
||||
return PTR_ERR_OR_ZERO(hwmon_dev);
|
||||
}
|
||||
|
||||
|
||||
@@ -89,10 +89,10 @@ static int adt7410_i2c_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id adt7410_ids[] = {
|
||||
{ "adt7410" },
|
||||
{ "adt7420" },
|
||||
{ "adt7422" },
|
||||
{}
|
||||
{ .name = "adt7410" },
|
||||
{ .name = "adt7420" },
|
||||
{ .name = "adt7422" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, adt7410_ids);
|
||||
|
||||
|
||||
@@ -169,11 +169,11 @@ static ssize_t adt7411_set_bit(struct device *dev,
|
||||
if (ret || flag > 1)
|
||||
return -EINVAL;
|
||||
|
||||
hwmon_lock(dev);
|
||||
ret = adt7411_modify_bit(client, s_attr2->index, s_attr2->nr, flag);
|
||||
/* force update */
|
||||
data->next_update = jiffies;
|
||||
hwmon_unlock(dev);
|
||||
scoped_guard(hwmon_lock, dev) {
|
||||
ret = adt7411_modify_bit(client, s_attr2->index, s_attr2->nr, flag);
|
||||
/* force update */
|
||||
data->next_update = jiffies;
|
||||
}
|
||||
|
||||
return ret < 0 ? ret : count;
|
||||
}
|
||||
@@ -670,7 +670,7 @@ static int adt7411_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id adt7411_id[] = {
|
||||
{ "adt7411" },
|
||||
{ .name = "adt7411" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, adt7411_id);
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/slab.h>
|
||||
@@ -1809,15 +1810,22 @@ static int adt7462_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id adt7462_id[] = {
|
||||
{ "adt7462" },
|
||||
{ .name = "adt7462" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, adt7462_id);
|
||||
|
||||
static const struct of_device_id adt7462_of_match[] = {
|
||||
{ .compatible = "onnn,adt7462" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, adt7462_of_match);
|
||||
|
||||
static struct i2c_driver adt7462_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "adt7462",
|
||||
.of_match_table = adt7462_of_match,
|
||||
},
|
||||
.probe = adt7462_probe,
|
||||
.id_table = adt7462_id,
|
||||
|
||||
@@ -1296,7 +1296,7 @@ static void adt7470_remove(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id adt7470_id[] = {
|
||||
{ "adt7470" },
|
||||
{ .name = "adt7470" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, adt7470_id);
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/util_macros.h>
|
||||
|
||||
#include <dt-bindings/pwm/pwm.h>
|
||||
@@ -165,15 +167,15 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END };
|
||||
enum chips { adt7473, adt7475, adt7476, adt7490 };
|
||||
|
||||
static const struct i2c_device_id adt7475_id[] = {
|
||||
{ "adt7473", adt7473 },
|
||||
{ "adt7475", adt7475 },
|
||||
{ "adt7476", adt7476 },
|
||||
{ "adt7490", adt7490 },
|
||||
{ .name = "adt7473", .driver_data = adt7473 },
|
||||
{ .name = "adt7475", .driver_data = adt7475 },
|
||||
{ .name = "adt7476", .driver_data = adt7476 },
|
||||
{ .name = "adt7490", .driver_data = adt7490 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, adt7475_id);
|
||||
|
||||
static const struct of_device_id __maybe_unused adt7475_of_match[] = {
|
||||
static const struct of_device_id adt7475_of_match[] = {
|
||||
{
|
||||
.compatible = "adi,adt7473",
|
||||
.data = (void *)adt7473
|
||||
@@ -1995,7 +1997,7 @@ static struct i2c_driver adt7475_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "adt7475",
|
||||
.of_match_table = of_match_ptr(adt7475_of_match),
|
||||
.of_match_table = adt7475_of_match,
|
||||
},
|
||||
.probe = adt7475_probe,
|
||||
.id_table = adt7475_id,
|
||||
|
||||
@@ -55,10 +55,10 @@
|
||||
enum aht10_variant { aht10, aht20, dht20};
|
||||
|
||||
static const struct i2c_device_id aht10_id[] = {
|
||||
{ "aht10", aht10 },
|
||||
{ "aht20", aht20 },
|
||||
{ "dht20", dht20 },
|
||||
{ },
|
||||
{ .name = "aht10", .driver_data = aht10 },
|
||||
{ .name = "aht20", .driver_data = aht20 },
|
||||
{ .name = "dht20", .driver_data = dht20 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, aht10_id);
|
||||
|
||||
|
||||
@@ -1083,7 +1083,7 @@ static int amc6821_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id amc6821_id[] = {
|
||||
{ "amc6821" },
|
||||
{ .name = "amc6821" },
|
||||
{ }
|
||||
};
|
||||
|
||||
|
||||
@@ -1305,6 +1305,7 @@ static const struct dmi_system_id applesmc_whitelist[] __initconst = {
|
||||
},
|
||||
{ .ident = NULL }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(dmi, applesmc_whitelist);
|
||||
|
||||
static int __init applesmc_init(void)
|
||||
{
|
||||
@@ -1416,4 +1417,3 @@ module_exit(applesmc_exit);
|
||||
MODULE_AUTHOR("Nicolas Boichat");
|
||||
MODULE_DESCRIPTION("Apple SMC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DEVICE_TABLE(dmi, applesmc_whitelist);
|
||||
|
||||
374
drivers/hwmon/arctic_fan_controller.c
Normal file
374
drivers/hwmon/arctic_fan_controller.c
Normal file
@@ -0,0 +1,374 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Linux hwmon driver for ARCTIC Fan Controller
|
||||
*
|
||||
* USB Custom HID device with 10 fan channels.
|
||||
* Exposes fan RPM (input) and PWM (0-255) via hwmon. Device pushes IN reports
|
||||
* at ~1 Hz; no GET_REPORT. OUT reports set PWM duty (bytes 1-10, 0-100%).
|
||||
* PWM is manual-only: the device does not change duty autonomously, only
|
||||
* when it receives an OUT report from the host.
|
||||
*/
|
||||
|
||||
#include <linux/completion.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/minmax.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/unaligned.h>
|
||||
|
||||
#define ARCTIC_VID 0x3904
|
||||
#define ARCTIC_PID 0xF001
|
||||
#define ARCTIC_NUM_FANS 10
|
||||
#define ARCTIC_OUTPUT_REPORT_ID 0x01
|
||||
#define ARCTIC_REPORT_LEN 32
|
||||
#define ARCTIC_RPM_OFFSET 11 /* bytes 11-30: 10 x uint16 LE */
|
||||
/* ACK report: device sends Report ID 0x02, 2 bytes (ID + status) after applying OUT report */
|
||||
#define ARCTIC_ACK_REPORT_ID 0x02
|
||||
#define ARCTIC_ACK_REPORT_LEN 2
|
||||
/*
|
||||
* Time to wait for ACK report after send.
|
||||
* Measured over 500 iterations: max ~563 ms. Keep 1 s as margin.
|
||||
*/
|
||||
#define ARCTIC_ACK_TIMEOUT_MS 1000
|
||||
|
||||
struct arctic_fan_data {
|
||||
struct hid_device *hdev;
|
||||
struct device *hwmon_dev; /* stored for explicit unregister in remove() */
|
||||
spinlock_t in_report_lock; /* protects fan_rpm, ack_status, write_pending, pwm_duty */
|
||||
struct completion in_report_received; /* ACK (ID 0x02) received in raw_event */
|
||||
int ack_status; /* 0 = OK, negative errno on device error */
|
||||
bool write_pending; /* true while an OUT report ACK is in flight */
|
||||
u32 fan_rpm[ARCTIC_NUM_FANS];
|
||||
u8 pwm_duty[ARCTIC_NUM_FANS]; /* 0-255 matching sysfs range; converted to 0-100 on send */
|
||||
/*
|
||||
* OUT report buffer passed to hid_hw_output_report(). Embedded in the
|
||||
* devm_kzalloc'd struct so it is heap-allocated and passes
|
||||
* usb_hcd_map_urb_for_dma(). Exclusively accessed by write(), which
|
||||
* the hwmon core serializes.
|
||||
*/
|
||||
__dma_from_device_group_begin();
|
||||
u8 buf[ARCTIC_REPORT_LEN];
|
||||
__dma_from_device_group_end();
|
||||
};
|
||||
|
||||
/*
|
||||
* Parse RPM values from the periodic status report (10 x uint16 LE at rpm_off).
|
||||
* pwm_duty is not updated from the report: the device is manual-only, so the
|
||||
* host cache is the authoritative source for PWM.
|
||||
* Called from raw_event which may run in IRQ context; must not sleep.
|
||||
*/
|
||||
static void arctic_fan_parse_report(struct arctic_fan_data *priv, u8 *buf,
|
||||
int len, int rpm_off)
|
||||
{
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
if (len < rpm_off + 20)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&priv->in_report_lock, flags);
|
||||
for (i = 0; i < ARCTIC_NUM_FANS; i++)
|
||||
priv->fan_rpm[i] = get_unaligned_le16(&buf[rpm_off + i * 2]);
|
||||
spin_unlock_irqrestore(&priv->in_report_lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* raw_event: IN reports.
|
||||
*
|
||||
* Status report: Report ID 0x01, 32 bytes:
|
||||
* byte 0 = report ID, bytes 1-10 = PWM 0-100%, bytes 11-30 = 10 x RPM uint16 LE.
|
||||
* Device pushes these at ~1 Hz; no GET_REPORT.
|
||||
*
|
||||
* ACK report: Report ID 0x02, 2 bytes:
|
||||
* byte 0 = 0x02, byte 1 = status (0x00 = OK, 0x01 = ERROR).
|
||||
* Sent once after accepting and applying an OUT report (ID 0x01).
|
||||
*/
|
||||
static int arctic_fan_raw_event(struct hid_device *hdev,
|
||||
struct hid_report *report, u8 *data, int size)
|
||||
{
|
||||
struct arctic_fan_data *priv = hid_get_drvdata(hdev);
|
||||
unsigned long flags;
|
||||
|
||||
hid_dbg(hdev, "arctic_fan: raw_event id=%u size=%d\n", report->id, size);
|
||||
|
||||
if (report->id == ARCTIC_ACK_REPORT_ID && size == ARCTIC_ACK_REPORT_LEN) {
|
||||
spin_lock_irqsave(&priv->in_report_lock, flags);
|
||||
/*
|
||||
* Only deliver if a write is in flight. This prevents a
|
||||
* late-arriving ACK from a timed-out write from erroneously
|
||||
* satisfying a subsequent write's completion wait.
|
||||
*/
|
||||
if (priv->write_pending) {
|
||||
priv->ack_status = data[1] == 0x00 ? 0 : -EIO;
|
||||
complete(&priv->in_report_received);
|
||||
}
|
||||
spin_unlock_irqrestore(&priv->in_report_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (report->id != ARCTIC_OUTPUT_REPORT_ID || size != ARCTIC_REPORT_LEN) {
|
||||
hid_dbg(hdev, "arctic_fan: raw_event id=%u size=%d ignored\n",
|
||||
report->id, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
arctic_fan_parse_report(priv, data, size, ARCTIC_RPM_OFFSET);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static umode_t arctic_fan_is_visible(const void *data,
|
||||
enum hwmon_sensor_types type,
|
||||
u32 attr, int channel)
|
||||
{
|
||||
if (type == hwmon_fan && attr == hwmon_fan_input)
|
||||
return 0444;
|
||||
if (type == hwmon_pwm && attr == hwmon_pwm_input)
|
||||
return 0644;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int arctic_fan_read(struct device *dev, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel, long *val)
|
||||
{
|
||||
struct arctic_fan_data *priv = dev_get_drvdata(dev);
|
||||
unsigned long flags;
|
||||
|
||||
if (type == hwmon_fan && attr == hwmon_fan_input) {
|
||||
spin_lock_irqsave(&priv->in_report_lock, flags);
|
||||
*val = priv->fan_rpm[channel];
|
||||
spin_unlock_irqrestore(&priv->in_report_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
if (type == hwmon_pwm && attr == hwmon_pwm_input) {
|
||||
spin_lock_irqsave(&priv->in_report_lock, flags);
|
||||
*val = priv->pwm_duty[channel];
|
||||
spin_unlock_irqrestore(&priv->in_report_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int arctic_fan_write(struct device *dev, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel, long val)
|
||||
{
|
||||
struct arctic_fan_data *priv = dev_get_drvdata(dev);
|
||||
u8 new_duty = (u8)clamp_val(val, 0, 255);
|
||||
unsigned long flags;
|
||||
unsigned long t;
|
||||
int i, ret;
|
||||
|
||||
/*
|
||||
* Build the buffer and arm write_pending under in_report_lock so that
|
||||
* reset_resume() cannot clear pwm_duty[] between the pwm_duty[] read
|
||||
* and the buffer write, and raw_event() cannot deliver a stale ACK
|
||||
* from a previous write into this write's completion.
|
||||
*
|
||||
* priv->buf is heap-allocated (embedded in the devm_kzalloc'd struct),
|
||||
* satisfying usb_hcd_map_urb_for_dma(). Exclusively accessed by
|
||||
* write() which the hwmon core serializes.
|
||||
*
|
||||
* pwm_duty[channel] is committed only after a positive device ACK so a
|
||||
* failed or timed-out write does not corrupt the cached state.
|
||||
*
|
||||
* Residual theoretical race: if write A times out (write_pending
|
||||
* cleared), write B sets write_pending = true, and a late ACK from
|
||||
* write A—delayed beyond ARCTIC_ACK_TIMEOUT_MS—arrives during write
|
||||
* B's pending window, it would falsely satisfy write B's completion.
|
||||
* This cannot be prevented in driver code without protocol support
|
||||
* (for example, a correlation ID echoed in the device ACK report).
|
||||
* In testing, observed ACK latency stayed below the 1 s timeout
|
||||
* (maximum ~563 ms over 500 iterations).
|
||||
*
|
||||
* The wait is non-interruptible so that a signal cannot cause write()
|
||||
* to return early while the OUT report is already in flight; an
|
||||
* interruptible early return would create the same late-ACK window
|
||||
* without even the timeout guard.
|
||||
* Serialized by the hwmon core: only one arctic_fan_write() at a time.
|
||||
* Use irqsave to match the IRQ context in which raw_event may run.
|
||||
*/
|
||||
spin_lock_irqsave(&priv->in_report_lock, flags);
|
||||
priv->buf[0] = ARCTIC_OUTPUT_REPORT_ID;
|
||||
for (i = 0; i < ARCTIC_NUM_FANS; i++) {
|
||||
u8 d = i == channel ? new_duty : priv->pwm_duty[i];
|
||||
|
||||
priv->buf[1 + i] = DIV_ROUND_CLOSEST((unsigned int)d * 100, 255);
|
||||
}
|
||||
priv->ack_status = -ETIMEDOUT;
|
||||
priv->write_pending = true;
|
||||
reinit_completion(&priv->in_report_received);
|
||||
spin_unlock_irqrestore(&priv->in_report_lock, flags);
|
||||
|
||||
ret = hid_hw_output_report(priv->hdev, priv->buf, ARCTIC_REPORT_LEN);
|
||||
if (ret < 0) {
|
||||
spin_lock_irqsave(&priv->in_report_lock, flags);
|
||||
priv->write_pending = false;
|
||||
spin_unlock_irqrestore(&priv->in_report_lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
t = wait_for_completion_timeout(&priv->in_report_received,
|
||||
msecs_to_jiffies(ARCTIC_ACK_TIMEOUT_MS));
|
||||
spin_lock_irqsave(&priv->in_report_lock, flags);
|
||||
priv->write_pending = false;
|
||||
/* Commit inside the lock so reset_resume() cannot race with this write */
|
||||
if (t && priv->ack_status == 0)
|
||||
priv->pwm_duty[channel] = new_duty;
|
||||
spin_unlock_irqrestore(&priv->in_report_lock, flags);
|
||||
|
||||
if (!t)
|
||||
return -ETIMEDOUT;
|
||||
return priv->ack_status; /* 0=OK, -EIO=device error */
|
||||
}
|
||||
|
||||
static const struct hwmon_ops arctic_fan_ops = {
|
||||
.is_visible = arctic_fan_is_visible,
|
||||
.read = arctic_fan_read,
|
||||
.write = arctic_fan_write,
|
||||
};
|
||||
|
||||
static const struct hwmon_channel_info *arctic_fan_info[] = {
|
||||
HWMON_CHANNEL_INFO(fan,
|
||||
HWMON_F_INPUT, HWMON_F_INPUT, HWMON_F_INPUT,
|
||||
HWMON_F_INPUT, HWMON_F_INPUT, HWMON_F_INPUT,
|
||||
HWMON_F_INPUT, HWMON_F_INPUT, HWMON_F_INPUT,
|
||||
HWMON_F_INPUT),
|
||||
HWMON_CHANNEL_INFO(pwm,
|
||||
HWMON_PWM_INPUT, HWMON_PWM_INPUT, HWMON_PWM_INPUT,
|
||||
HWMON_PWM_INPUT, HWMON_PWM_INPUT, HWMON_PWM_INPUT,
|
||||
HWMON_PWM_INPUT, HWMON_PWM_INPUT, HWMON_PWM_INPUT,
|
||||
HWMON_PWM_INPUT),
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct hwmon_chip_info arctic_fan_chip_info = {
|
||||
.ops = &arctic_fan_ops,
|
||||
.info = arctic_fan_info,
|
||||
};
|
||||
|
||||
static int arctic_fan_reset_resume(struct hid_device *hdev)
|
||||
{
|
||||
struct arctic_fan_data *priv = hid_get_drvdata(hdev);
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* The device resets its PWM channels to hardware defaults on power
|
||||
* loss during suspend. Clear the cached duty values so they reflect
|
||||
* the unknown hardware state, consistent with probe-time behaviour
|
||||
* (the device has no GET_REPORT support). Hold in_report_lock so
|
||||
* this does not race with a concurrent pwm read or write callback.
|
||||
*/
|
||||
spin_lock_irqsave(&priv->in_report_lock, flags);
|
||||
memset(priv->pwm_duty, 0, sizeof(priv->pwm_duty));
|
||||
spin_unlock_irqrestore(&priv->in_report_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int arctic_fan_probe(struct hid_device *hdev,
|
||||
const struct hid_device_id *id)
|
||||
{
|
||||
struct arctic_fan_data *priv;
|
||||
int ret;
|
||||
|
||||
if (!hid_is_usb(hdev))
|
||||
return -ENODEV;
|
||||
|
||||
ret = hid_parse(hdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
priv = devm_kzalloc(&hdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->hdev = hdev;
|
||||
spin_lock_init(&priv->in_report_lock);
|
||||
init_completion(&priv->in_report_received);
|
||||
hid_set_drvdata(hdev, priv);
|
||||
|
||||
ret = hid_hw_start(hdev, HID_CONNECT_DRIVER);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = hid_hw_open(hdev);
|
||||
if (ret)
|
||||
goto out_stop;
|
||||
|
||||
/*
|
||||
* Start IO before registering with hwmon. If IO were started after
|
||||
* hwmon registration, a sysfs write arriving in that narrow window
|
||||
* would send an OUT report but the ACK could not be delivered (the HID
|
||||
* core discards events until io_started), causing a spurious timeout.
|
||||
*/
|
||||
hid_device_io_start(hdev);
|
||||
|
||||
/*
|
||||
* Use the non-devm variant and store the pointer so remove() can
|
||||
* call hwmon_device_unregister() before tearing down the HID
|
||||
* transport. devm_hwmon_device_register_with_info() would defer
|
||||
* unregistration until after remove() returns, leaving a window
|
||||
* where a concurrent sysfs write could call hid_hw_output_report()
|
||||
* on an already-stopped device (use-after-free).
|
||||
*/
|
||||
priv->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "arctic_fan",
|
||||
priv, &arctic_fan_chip_info,
|
||||
NULL);
|
||||
if (IS_ERR(priv->hwmon_dev)) {
|
||||
ret = PTR_ERR(priv->hwmon_dev);
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_close:
|
||||
hid_device_io_stop(hdev);
|
||||
hid_hw_close(hdev);
|
||||
out_stop:
|
||||
hid_hw_stop(hdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void arctic_fan_remove(struct hid_device *hdev)
|
||||
{
|
||||
struct arctic_fan_data *priv = hid_get_drvdata(hdev);
|
||||
|
||||
/*
|
||||
* Unregister hwmon before stopping the HID transport. This removes
|
||||
* the sysfs files and waits for any in-progress write() callback to
|
||||
* return, so no hwmon op can call hid_hw_output_report() after
|
||||
* hid_hw_stop() frees the underlying USB resources.
|
||||
* Matches the pattern used by nzxt-smart2 and aquacomputer_d5next.
|
||||
*
|
||||
* The HID core clears hdev->io_started before invoking ->remove(),
|
||||
* so hid_device_io_stop() is not called here; doing so would emit
|
||||
* a spurious "io already stopped" warning.
|
||||
*/
|
||||
hwmon_device_unregister(priv->hwmon_dev);
|
||||
hid_hw_close(hdev);
|
||||
hid_hw_stop(hdev);
|
||||
}
|
||||
|
||||
static const struct hid_device_id arctic_fan_id_table[] = {
|
||||
{ HID_USB_DEVICE(ARCTIC_VID, ARCTIC_PID) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(hid, arctic_fan_id_table);
|
||||
|
||||
static struct hid_driver arctic_fan_driver = {
|
||||
.name = "arctic_fan",
|
||||
.id_table = arctic_fan_id_table,
|
||||
.probe = arctic_fan_probe,
|
||||
.remove = arctic_fan_remove,
|
||||
.raw_event = arctic_fan_raw_event,
|
||||
.reset_resume = arctic_fan_reset_resume,
|
||||
};
|
||||
|
||||
module_hid_driver(arctic_fan_driver);
|
||||
|
||||
MODULE_AUTHOR("Aureo Serrano de Souza <aureo.serrano@arctic.de>");
|
||||
MODULE_DESCRIPTION("HID hwmon driver for ARCTIC Fan Controller");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -213,7 +213,7 @@ static struct asb100_data *asb100_update_device(struct device *dev);
|
||||
static void asb100_init_client(struct i2c_client *client);
|
||||
|
||||
static const struct i2c_device_id asb100_id[] = {
|
||||
{ "asb100" },
|
||||
{ .name = "asb100" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, asb100_id);
|
||||
|
||||
@@ -1179,9 +1179,9 @@ static void asc7621_remove(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id asc7621_id[] = {
|
||||
{"asc7621", asc7621},
|
||||
{"asc7621a", asc7621a},
|
||||
{},
|
||||
{ .name = "asc7621", .driver_data = asc7621 },
|
||||
{ .name = "asc7621a", .driver_data = asc7621a },
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, asc7621_id);
|
||||
|
||||
@@ -277,7 +277,7 @@ static const struct ec_sensor_info sensors_family_amd_600[] = {
|
||||
[ec_sensor_temp_cpu_package] =
|
||||
EC_SENSOR("CPU Package", hwmon_temp, 1, 0x00, 0x31),
|
||||
[ec_sensor_temp_mb] =
|
||||
EC_SENSOR("Motherboard", hwmon_temp, 1, 0x00, 0x32),
|
||||
EC_SENSOR("Motherboard", hwmon_temp, 1, 0x00, 0x32),
|
||||
[ec_sensor_temp_vrm] =
|
||||
EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x33),
|
||||
[ec_sensor_temp_t_sensor] =
|
||||
@@ -404,6 +404,12 @@ static const struct ec_sensor_info sensors_family_intel_700[] = {
|
||||
[ec_sensor_temp_vrm] = EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x33),
|
||||
[ec_sensor_fan_cpu_opt] =
|
||||
EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xb0),
|
||||
[ec_sensor_fan_water_flow] =
|
||||
EC_SENSOR("Water_Flow", hwmon_fan, 2, 0x00, 0xbc),
|
||||
[ec_sensor_temp_water_in] =
|
||||
EC_SENSOR("Water_In", hwmon_temp, 1, 0x01, 0x00),
|
||||
[ec_sensor_temp_water_out] =
|
||||
EC_SENSOR("Water_Out", hwmon_temp, 1, 0x01, 0x01),
|
||||
};
|
||||
|
||||
/* Shortcuts for common combinations */
|
||||
@@ -514,6 +520,13 @@ static const struct ec_board_info board_info_maximus_z690_formula = {
|
||||
.family = family_intel_600_series,
|
||||
};
|
||||
|
||||
static const struct ec_board_info board_info_maximus_z790_extreme = {
|
||||
.sensors = SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM |
|
||||
SENSOR_SET_TEMP_WATER | SENSOR_FAN_WATER_FLOW,
|
||||
.mutex_path = ASUS_HW_ACCESS_MUTEX_RMTW_ASMX,
|
||||
.family = family_intel_700_series,
|
||||
};
|
||||
|
||||
static const struct ec_board_info board_info_prime_x470_pro = {
|
||||
.sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
|
||||
SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM |
|
||||
@@ -621,6 +634,14 @@ static const struct ec_board_info board_info_strix_b550_i_gaming = {
|
||||
.family = family_amd_500_series,
|
||||
};
|
||||
|
||||
static const struct ec_board_info board_info_strix_b650e_e_gaming = {
|
||||
.sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE |
|
||||
SENSOR_TEMP_MB | SENSOR_TEMP_VRM |
|
||||
SENSOR_FAN_CPU_OPT,
|
||||
.mutex_path = ASUS_HW_ACCESS_MUTEX_SB_PCI0_SBRG_SIO1_MUT0,
|
||||
.family = family_amd_600_series,
|
||||
};
|
||||
|
||||
static const struct ec_board_info board_info_strix_b650e_i_gaming = {
|
||||
.sensors = SENSOR_TEMP_VRM | SENSOR_TEMP_T_SENSOR |
|
||||
SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_IN_CPU_CORE,
|
||||
@@ -628,6 +649,14 @@ static const struct ec_board_info board_info_strix_b650e_i_gaming = {
|
||||
.family = family_amd_600_series,
|
||||
};
|
||||
|
||||
static const struct ec_board_info board_info_strix_b850_e_gaming_wifi = {
|
||||
.sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE |
|
||||
SENSOR_TEMP_MB | SENSOR_TEMP_VRM |
|
||||
SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CPU_OPT,
|
||||
.mutex_path = ASUS_HW_ACCESS_MUTEX_SB_PCI0_SBRG_SIO1_MUT0,
|
||||
.family = family_amd_800_series,
|
||||
};
|
||||
|
||||
static const struct ec_board_info board_info_strix_b850_i_gaming_wifi = {
|
||||
.sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE |
|
||||
SENSOR_TEMP_MB | SENSOR_TEMP_VRM,
|
||||
@@ -862,12 +891,18 @@ static const struct dmi_system_id dmi_table[] = {
|
||||
&board_info_maximus_x_hero),
|
||||
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG MAXIMUS Z690 FORMULA",
|
||||
&board_info_maximus_z690_formula),
|
||||
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG MAXIMUS Z790 EXTREME",
|
||||
&board_info_maximus_z790_extreme),
|
||||
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B550-E GAMING",
|
||||
&board_info_strix_b550_e_gaming),
|
||||
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B550-I GAMING",
|
||||
&board_info_strix_b550_i_gaming),
|
||||
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B650E-E GAMING WIFI",
|
||||
&board_info_strix_b650e_e_gaming),
|
||||
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B650E-I GAMING WIFI",
|
||||
&board_info_strix_b650e_i_gaming),
|
||||
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B850-E GAMING WIFI",
|
||||
&board_info_strix_b850_e_gaming_wifi),
|
||||
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B850-I GAMING WIFI",
|
||||
&board_info_strix_b850_i_gaming_wifi),
|
||||
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X470-F GAMING",
|
||||
|
||||
@@ -278,7 +278,7 @@ static int atxp1_probe(struct i2c_client *client)
|
||||
};
|
||||
|
||||
static const struct i2c_device_id atxp1_id[] = {
|
||||
{ "atxp1" },
|
||||
{ .name = "atxp1" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, atxp1_id);
|
||||
|
||||
@@ -736,14 +736,14 @@ static void cc2_remove(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id cc2_id[] = {
|
||||
{ "cc2d23" },
|
||||
{ "cc2d23s" },
|
||||
{ "cc2d25" },
|
||||
{ "cc2d25s" },
|
||||
{ "cc2d33" },
|
||||
{ "cc2d33s" },
|
||||
{ "cc2d35" },
|
||||
{ "cc2d35s" },
|
||||
{ .name = "cc2d23" },
|
||||
{ .name = "cc2d23s" },
|
||||
{ .name = "cc2d25" },
|
||||
{ .name = "cc2d25s" },
|
||||
{ .name = "cc2d33" },
|
||||
{ .name = "cc2d33s" },
|
||||
{ .name = "cc2d35" },
|
||||
{ .name = "cc2d35s" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, cc2_id);
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/pci.h>
|
||||
#include <asm/msr.h>
|
||||
#include <asm/processor.h>
|
||||
#include <linux/processor.h>
|
||||
#include <asm/cpu_device_id.h>
|
||||
#include <linux/sched/isolation.h>
|
||||
|
||||
@@ -39,7 +39,6 @@ static int force_tjmax;
|
||||
module_param_named(tjmax, force_tjmax, int, 0444);
|
||||
MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius");
|
||||
|
||||
#define NUM_REAL_CORES 512 /* Number of Real cores per cpu */
|
||||
#define CORETEMP_NAME_LENGTH 28 /* String Length of attrs */
|
||||
|
||||
enum coretemp_attr_index {
|
||||
@@ -136,14 +135,14 @@ static const struct tjmax_model tjmax_model_table[] = {
|
||||
* which are covered by tjmax_table
|
||||
*/
|
||||
{ INTEL_ATOM_BONNELL_MID, ANY, 90000 }, /* Atom Tunnel Creek (Exx), Lincroft (Z6xx)
|
||||
* Note: TjMax for E6xxT is 110C, but CPU type
|
||||
* is undetectable by software
|
||||
* Note: TjMax for E6xxT is 110C, but CPU
|
||||
* type is undetectable by software
|
||||
*/
|
||||
{ INTEL_ATOM_SALTWELL_MID, ANY, 90000 }, /* Atom Medfield (Z2460) */
|
||||
{ INTEL_ATOM_SALTWELL_TABLET, ANY, 90000 }, /* Atom Clover Trail/Cloverview (Z27x0) */
|
||||
{ INTEL_ATOM_SALTWELL, ANY, 100000 }, /* Atom Cedar Trail/Cedarview (N2xxx, D2xxx)
|
||||
* Also matches S12x0 (stepping 9), covered by
|
||||
* PCI table
|
||||
* Also matches S12x0 (stepping 9), covered
|
||||
* by PCI table
|
||||
*/
|
||||
{ INTEL_ATOM_SILVERMONT, 9, 110000 }, /* Atom Bay Trail E38xx (embedded) */
|
||||
{ INTEL_ATOM_SILVERMONT, ANY, 90000 }, /* Atom Bay Trail Z37xx (tablet) */
|
||||
@@ -201,6 +200,7 @@ static int adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev)
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tjmax_model_table); i++) {
|
||||
const struct tjmax_model *tm = &tjmax_model_table[i];
|
||||
|
||||
if (c->x86_vfm == tm->vfm &&
|
||||
(tm->stepping_mask == ANY ||
|
||||
tm->stepping_mask == c->x86_stepping))
|
||||
@@ -223,8 +223,7 @@ static int adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev)
|
||||
err = rdmsrq_safe_on_cpu(id, 0x17, &val);
|
||||
if (err) {
|
||||
dev_warn(dev,
|
||||
"Unable to access MSR 0x17, assuming desktop"
|
||||
" CPU\n");
|
||||
"Unable to access MSR 0x17, assuming desktop CPU\n");
|
||||
usemsr_ee = 0;
|
||||
} else if (c->x86_vfm < INTEL_CORE2_PENRYN &&
|
||||
!(val & 0x10000000)) {
|
||||
@@ -258,8 +257,7 @@ static int adjust_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev)
|
||||
err = rdmsrq_safe_on_cpu(id, 0xee, &val);
|
||||
if (err) {
|
||||
dev_warn(dev,
|
||||
"Unable to access MSR 0xEE, for Tjmax, left"
|
||||
" at default\n");
|
||||
"Unable to access MSR 0xEE, for Tjmax, left at default\n");
|
||||
} else if (val & 0x40000000) {
|
||||
tjmax = tjmax_ee;
|
||||
}
|
||||
@@ -342,7 +340,7 @@ static int max_zones __read_mostly;
|
||||
static struct platform_device **zone_devices;
|
||||
|
||||
static ssize_t show_label(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
struct platform_data *pdata = dev_get_drvdata(dev);
|
||||
struct temp_data *tdata = container_of(devattr, struct temp_data, sd_attrs[ATTR_LABEL]);
|
||||
@@ -354,7 +352,7 @@ static ssize_t show_label(struct device *dev,
|
||||
}
|
||||
|
||||
static ssize_t show_crit_alarm(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
struct msr val;
|
||||
struct temp_data *tdata = container_of(devattr, struct temp_data,
|
||||
@@ -368,7 +366,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 device_attribute *devattr, char *buf)
|
||||
{
|
||||
struct temp_data *tdata = container_of(devattr, struct temp_data, sd_attrs[ATTR_TJMAX]);
|
||||
int tjmax;
|
||||
@@ -381,7 +379,7 @@ static ssize_t show_tjmax(struct device *dev,
|
||||
}
|
||||
|
||||
static ssize_t show_ttarget(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
struct temp_data *tdata = container_of(devattr, struct temp_data, sd_attrs[ATTR_TTARGET]);
|
||||
int ttarget;
|
||||
@@ -396,7 +394,7 @@ static ssize_t show_ttarget(struct device *dev,
|
||||
}
|
||||
|
||||
static ssize_t show_temp(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
struct msr val;
|
||||
struct temp_data *tdata = container_of(devattr, struct temp_data, sd_attrs[ATTR_TEMP]);
|
||||
@@ -424,7 +422,6 @@ static ssize_t show_temp(struct device *dev,
|
||||
|
||||
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,
|
||||
struct device_attribute *devattr, char *buf) = {
|
||||
show_label, show_crit_alarm, show_temp, show_tjmax,
|
||||
@@ -432,6 +429,7 @@ static int create_core_attrs(struct temp_data *tdata, struct device *dev)
|
||||
static const char *const suffixes[TOTAL_ATTRS] = {
|
||||
"label", "crit_alarm", "input", "crit", "max"
|
||||
};
|
||||
int i;
|
||||
|
||||
for (i = 0; i < tdata->attr_size; i++) {
|
||||
/*
|
||||
@@ -453,7 +451,6 @@ static int create_core_attrs(struct temp_data *tdata, struct device *dev)
|
||||
return sysfs_create_group(&dev->kobj, &tdata->attr_group);
|
||||
}
|
||||
|
||||
|
||||
static int chk_ucode_version(unsigned int cpu)
|
||||
{
|
||||
struct cpuinfo_x86 *c = &cpu_data(cpu);
|
||||
@@ -485,13 +482,7 @@ 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->nr_cores = topology_num_cores_per_package();
|
||||
pdata->core_data = kzalloc_objs(struct temp_data *,
|
||||
pdata->nr_cores);
|
||||
if (!pdata->core_data)
|
||||
@@ -781,7 +772,8 @@ static int coretemp_cpu_offline(unsigned int cpu)
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
static const struct x86_cpu_id __initconst coretemp_ids[] = {
|
||||
|
||||
static const struct x86_cpu_id coretemp_ids[] __initconst = {
|
||||
X86_MATCH_VENDOR_FEATURE(INTEL, X86_FEATURE_DTHERM, NULL),
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -654,9 +654,10 @@ static int cros_ec_hwmon_resume(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
static const struct platform_device_id cros_ec_hwmon_id[] = {
|
||||
{ DRV_NAME, 0 },
|
||||
{}
|
||||
{ .name = DRV_NAME },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, cros_ec_hwmon_id);
|
||||
|
||||
static struct platform_driver cros_ec_hwmon_driver = {
|
||||
.driver.name = DRV_NAME,
|
||||
@@ -667,7 +668,6 @@ static struct platform_driver cros_ec_hwmon_driver = {
|
||||
};
|
||||
module_platform_driver(cros_ec_hwmon_driver);
|
||||
|
||||
MODULE_DEVICE_TABLE(platform, cros_ec_hwmon_id);
|
||||
MODULE_DESCRIPTION("ChromeOS EC Hardware Monitoring Driver");
|
||||
MODULE_AUTHOR("Thomas Weißschuh <linux@weissschuh.net");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -1574,6 +1574,14 @@ static const struct dmi_system_id i8k_whitelist_fan_control[] __initconst = {
|
||||
},
|
||||
.driver_data = (void *)&i8k_fan_control_data[I8K_FAN_30A3_31A3],
|
||||
},
|
||||
{
|
||||
.ident = "Dell Latitude 7530",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Latitude 7530"),
|
||||
},
|
||||
.driver_data = (void *)&i8k_fan_control_data[I8K_FAN_30A3_31A3],
|
||||
},
|
||||
{
|
||||
.ident = "Dell Latitude E6440",
|
||||
.matches = {
|
||||
|
||||
@@ -2515,8 +2515,8 @@ static void dme1737_i2c_remove(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id dme1737_id[] = {
|
||||
{ "dme1737", dme1737 },
|
||||
{ "sch5027", sch5027 },
|
||||
{ .name = "dme1737", .driver_data = dme1737 },
|
||||
{ .name = "sch5027", .driver_data = sch5027 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, dme1737_id);
|
||||
|
||||
@@ -367,11 +367,11 @@ static int ds1621_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ds1621_id[] = {
|
||||
{ "ds1621", ds1621 },
|
||||
{ "ds1625", ds1625 },
|
||||
{ "ds1631", ds1631 },
|
||||
{ "ds1721", ds1721 },
|
||||
{ "ds1731", ds1731 },
|
||||
{ .name = "ds1621", .driver_data = ds1621 },
|
||||
{ .name = "ds1625", .driver_data = ds1625 },
|
||||
{ .name = "ds1631", .driver_data = ds1631 },
|
||||
{ .name = "ds1721", .driver_data = ds1721 },
|
||||
{ .name = "ds1731", .driver_data = ds1731 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ds1621_id);
|
||||
|
||||
@@ -233,8 +233,8 @@ static int ds620_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ds620_id[] = {
|
||||
{"ds620"},
|
||||
{}
|
||||
{ .name = "ds620" },
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, ds620_id);
|
||||
|
||||
@@ -639,18 +639,18 @@ static const struct hwmon_chip_info emc1403_chip_info = {
|
||||
|
||||
/* Last digit of chip name indicates number of channels */
|
||||
static const struct i2c_device_id emc1403_idtable[] = {
|
||||
{ "emc1402", emc1402 },
|
||||
{ "emc1403", emc1403 },
|
||||
{ "emc1404", emc1404 },
|
||||
{ "emc1412", emc1402 },
|
||||
{ "emc1413", emc1403 },
|
||||
{ "emc1414", emc1404 },
|
||||
{ "emc1422", emc1402 },
|
||||
{ "emc1423", emc1403 },
|
||||
{ "emc1424", emc1404 },
|
||||
{ "emc1428", emc1428 },
|
||||
{ "emc1438", emc1428 },
|
||||
{ "emc1442", emc1402 },
|
||||
{ .name = "emc1402", .driver_data = emc1402 },
|
||||
{ .name = "emc1403", .driver_data = emc1403 },
|
||||
{ .name = "emc1404", .driver_data = emc1404 },
|
||||
{ .name = "emc1412", .driver_data = emc1402 },
|
||||
{ .name = "emc1413", .driver_data = emc1403 },
|
||||
{ .name = "emc1414", .driver_data = emc1404 },
|
||||
{ .name = "emc1422", .driver_data = emc1402 },
|
||||
{ .name = "emc1423", .driver_data = emc1403 },
|
||||
{ .name = "emc1424", .driver_data = emc1404 },
|
||||
{ .name = "emc1428", .driver_data = emc1428 },
|
||||
{ .name = "emc1438", .driver_data = emc1428 },
|
||||
{ .name = "emc1442", .driver_data = emc1402 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, emc1403_idtable);
|
||||
|
||||
965
drivers/hwmon/emc1812.c
Normal file
965
drivers/hwmon/emc1812.c
Normal file
@@ -0,0 +1,965 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* HWMON driver for Microchip EMC1812/13/14/15/33 Multichannel high-accuracy
|
||||
* 2-wire low-voltage remote diode temperature monitor family.
|
||||
*
|
||||
* Copyright (C) 2026 Microchip Technology Inc. and its subsidiaries
|
||||
*
|
||||
* Author: Marius Cristea <marius.cristea@microchip.com>
|
||||
*
|
||||
* Datasheet can be found here:
|
||||
* https://ww1.microchip.com/downloads/aemDocuments/documents/MSLD/ProductDocuments/DataSheets/EMC1812-3-4-5-33-Data-Sheet-DS20005751.pdf
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/units.h>
|
||||
#include <linux/util_macros.h>
|
||||
|
||||
/* EMC1812 Registers Addresses */
|
||||
#define EMC1812_STATUS_ADDR 0x02
|
||||
#define EMC1812_CONFIG_LO_ADDR 0x03
|
||||
|
||||
#define EMC1812_CFG_ADDR 0x09
|
||||
#define EMC1812_CONV_ADDR 0x0A
|
||||
#define EMC1812_INT_DIODE_HIGH_LIMIT_ADDR 0x0B
|
||||
#define EMC1812_INT_DIODE_LOW_LIMIT_ADDR 0x0C
|
||||
#define EMC1812_EXT1_HIGH_LIMIT_HIGH_BYTE_ADDR 0x0D
|
||||
#define EMC1812_EXT1_LOW_LIMIT_HIGH_BYTE_ADDR 0x0E
|
||||
#define EMC1812_ONE_SHOT_ADDR 0x0F
|
||||
|
||||
#define EMC1812_EXT1_HIGH_LIMIT_LOW_BYTE_ADDR 0x13
|
||||
#define EMC1812_EXT1_LOW_LIMIT_LOW_BYTE_ADDR 0x14
|
||||
#define EMC1812_EXT2_HIGH_LIMIT_HIGH_BYTE_ADDR 0x15
|
||||
#define EMC1812_EXT2_LOW_LIMIT_HIGH_BYTE_ADDR 0x16
|
||||
#define EMC1812_EXT2_HIGH_LIMIT_LOW_BYTE_ADDR 0x17
|
||||
#define EMC1812_EXT2_LOW_LIMIT_LOW_BYTE_ADDR 0x18
|
||||
#define EMC1812_EXT1_THERM_LIMIT_ADDR 0x19
|
||||
#define EMC1812_EXT2_THERM_LIMIT_ADDR 0x1A
|
||||
#define EMC1812_EXT_DIODE_FAULT_STATUS_ADDR 0x1B
|
||||
|
||||
#define EMC1812_DIODE_FAULT_MASK_ADDR 0x1F
|
||||
#define EMC1812_INT_DIODE_THERM_LIMIT_ADDR 0x20
|
||||
#define EMC1812_THRM_HYS_ADDR 0x21
|
||||
#define EMC1812_CONSEC_ALERT_ADDR 0x22
|
||||
|
||||
#define EMC1812_EXT1_BETA_CONFIG_ADDR 0x25
|
||||
#define EMC1812_EXT2_BETA_CONFIG_ADDR 0x26
|
||||
#define EMC1812_EXT1_IDEALITY_FACTOR_ADDR 0x27
|
||||
#define EMC1812_EXT2_IDEALITY_FACTOR_ADDR 0x28
|
||||
|
||||
#define EMC1812_EXT3_HIGH_LIMIT_HIGH_BYTE_ADDR 0x2C
|
||||
#define EMC1812_EXT3_LOW_LIMIT_HIGH_BYTE_ADDR 0x2D
|
||||
#define EMC1812_EXT3_HIGH_LIMIT_LOW_BYTE_ADDR 0x2E
|
||||
#define EMC1812_EXT3_LOW_LIMIT_LOW_BYTE_ADDR 0x2F
|
||||
#define EMC1812_EXT3_THERM_LIMIT_ADDR 0x30
|
||||
#define EMC1812_EXT3_IDEALITY_FACTOR_ADDR 0x31
|
||||
|
||||
#define EMC1812_EXT4_HIGH_LIMIT_HIGH_BYTE_ADDR 0x34
|
||||
#define EMC1812_EXT4_LOW_LIMIT_HIGH_BYTE_ADDR 0x35
|
||||
#define EMC1812_EXT4_HIGH_LIMIT_LOW_BYTE_ADDR 0x36
|
||||
#define EMC1812_EXT4_LOW_LIMIT_LOW_BYTE_ADDR 0x37
|
||||
#define EMC1812_EXT4_THERM_LIMIT_ADDR 0x38
|
||||
#define EMC1812_EXT4_IDEALITY_FACTOR_ADDR 0x39
|
||||
#define EMC1812_HIGH_LIMIT_STATUS_ADDR 0x3A
|
||||
#define EMC1812_LOW_LIMIT_STATUS_ADDR 0x3B
|
||||
#define EMC1812_THERM_LIMIT_STATUS_ADDR 0x3C
|
||||
#define EMC1812_ROC_GAIN_ADDR 0x3D
|
||||
#define EMC1812_ROC_CONFIG_ADDR 0x3E
|
||||
#define EMC1812_ROC_STATUS_ADDR 0x3F
|
||||
#define EMC1812_R1_RESH_ADDR 0x40
|
||||
#define EMC1812_R1_LIMH_ADDR 0x41
|
||||
#define EMC1812_R1_LIML_ADDR 0x42
|
||||
#define EMC1812_R1_SMPL_ADDR 0x43
|
||||
#define EMC1812_R2_RESH_ADDR 0x44
|
||||
#define EMC1812_R2_3_RESL_ADDR 0x45
|
||||
#define EMC1812_R2_LIMH_ADDR 0x46
|
||||
#define EMC1812_R2_LIML_ADDR 0x47
|
||||
#define EMC1812_R2_SMPL_ADDR 0x48
|
||||
#define EMC1812_PER_MAXTH_1_ADDR 0x49
|
||||
#define EMC1812_PER_MAXT1L_ADDR 0x4A
|
||||
#define EMC1812_PER_MAXTH_2_ADDR 0x4B
|
||||
#define EMC1812_PER_MAXT2_3L_ADDR 0x4C
|
||||
#define EMC1812_GBL_MAXT1H_ADDR 0x4D
|
||||
#define EMC1812_GBL_MAXT1L_ADDR 0x4E
|
||||
#define EMC1812_GBL_MAXT2H_ADDR 0x4F
|
||||
#define EMC1812_GBL_MAXT2L_ADDR 0x50
|
||||
#define EMC1812_FILTER_SEL_ADDR 0x51
|
||||
|
||||
#define EMC1812_INT_HIGH_BYTE_ADDR 0x60
|
||||
#define EMC1812_INT_LOW_BYTE_ADDR 0x61
|
||||
#define EMC1812_EXT1_HIGH_BYTE_ADDR 0x62
|
||||
#define EMC1812_EXT1_LOW_BYTE_ADDR 0x63
|
||||
#define EMC1812_EXT2_HIGH_BYTE_ADDR 0x64
|
||||
#define EMC1812_EXT2_LOW_BYTE_ADDR 0x65
|
||||
#define EMC1812_EXT3_HIGH_BYTE_ADDR 0x66
|
||||
#define EMC1812_EXT3_LOW_BYTE_ADDR 0x67
|
||||
#define EMC1812_EXT4_HIGH_BYTE_ADDR 0x68
|
||||
#define EMC1812_EXT4_LOW_BYTE_ADDR 0x69
|
||||
#define EMC1812_HOTTEST_DIODE_HIGH_BYTE_ADDR 0x6A
|
||||
#define EMC1812_HOTTEST_DIODE_LOW_BYTE_ADDR 0x6B
|
||||
#define EMC1812_HOTTEST_STATUS_ADDR 0x6C
|
||||
#define EMC1812_HOTTEST_CFG_ADDR 0x6D
|
||||
|
||||
#define EMC1812_PRODUCT_ID_ADDR 0xFD
|
||||
#define EMC1812_MANUFACTURER_ID_ADDR 0xFE
|
||||
#define EMC1812_REVISION_ADDR 0xFF
|
||||
|
||||
/* EMC1812 Config Bits */
|
||||
#define EMC1812_CFG_MSKAL BIT(7)
|
||||
#define EMC1812_CFG_RS BIT(6)
|
||||
#define EMC1812_CFG_ATTHM BIT(5)
|
||||
#define EMC1812_CFG_RECD12 BIT(4)
|
||||
#define EMC1812_CFG_RECD34 BIT(3)
|
||||
#define EMC1812_CFG_RANGE BIT(2)
|
||||
#define EMC1812_CFG_DA_ENA BIT(1)
|
||||
#define EMC1812_CFG_APDD BIT(0)
|
||||
|
||||
/* EMC1812 Status Bits */
|
||||
#define EMC1812_STATUS_ROCF BIT(7)
|
||||
#define EMC1812_STATUS_HOTCHG BIT(6)
|
||||
#define EMC1812_STATUS_BUSY BIT(5)
|
||||
#define EMC1812_STATUS_HIGH BIT(4)
|
||||
#define EMC1812_STATUS_LOW BIT(3)
|
||||
#define EMC1812_STATUS_FAULT BIT(2)
|
||||
#define EMC1812_STATUS_ETHRM BIT(1)
|
||||
#define EMC1812_STATUS_ITHRM BIT(0)
|
||||
|
||||
#define EMC1812_BETA_LOCK_VAL 0x0F
|
||||
|
||||
#define EMC1812_TEMP_CH_ADDR(index) (EMC1812_INT_HIGH_BYTE_ADDR + 2 * (index))
|
||||
|
||||
#define EMC1812_FILTER_MASK_LEN 2
|
||||
|
||||
#define EMC1812_PID 0x81
|
||||
#define EMC1813_PID 0x87
|
||||
#define EMC1814_PID 0x84
|
||||
#define EMC1815_PID 0x85
|
||||
#define EMC1833_PID 0x83
|
||||
|
||||
/* The maximum number of channels a member of the family can have */
|
||||
#define EMC1812_MAX_NUM_CHANNELS 5
|
||||
#define EMC1812_TEMP_OFFSET 64
|
||||
|
||||
#define EMC1812_DEFAULT_IDEALITY_FACTOR 0x12
|
||||
|
||||
/* Constants and default values */
|
||||
#define EMC1812_HIGH_LIMIT_DEFAULT (85 + EMC1812_TEMP_OFFSET)
|
||||
|
||||
#define EMC1812_TEMP_MASK (HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX | \
|
||||
HWMON_T_CRIT | HWMON_T_MAX_HYST | HWMON_T_CRIT_HYST | \
|
||||
HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM | \
|
||||
HWMON_T_CRIT_ALARM | HWMON_T_LABEL)
|
||||
|
||||
static const struct hwmon_channel_info * const emc1812_info[] = {
|
||||
HWMON_CHANNEL_INFO(chip, HWMON_C_UPDATE_INTERVAL),
|
||||
HWMON_CHANNEL_INFO(temp,
|
||||
EMC1812_TEMP_MASK,
|
||||
EMC1812_TEMP_MASK | HWMON_T_FAULT,
|
||||
EMC1812_TEMP_MASK | HWMON_T_FAULT,
|
||||
EMC1812_TEMP_MASK | HWMON_T_FAULT,
|
||||
EMC1812_TEMP_MASK | HWMON_T_FAULT),
|
||||
NULL
|
||||
};
|
||||
|
||||
/**
|
||||
* struct emc1812_features - features of a emc1812 instance
|
||||
* @name: chip's name
|
||||
* @phys_channels: number of physical channels supported by the chip
|
||||
* @has_ext2_beta_reg: the EXT2_BETA register is available on the chip
|
||||
*/
|
||||
struct emc1812_features {
|
||||
const char *name;
|
||||
u8 phys_channels;
|
||||
bool has_ext2_beta_reg;
|
||||
};
|
||||
|
||||
static const struct emc1812_features emc1833_chip_config = {
|
||||
.name = "emc1833",
|
||||
.phys_channels = 3,
|
||||
.has_ext2_beta_reg = true,
|
||||
};
|
||||
|
||||
static const struct emc1812_features emc1812_chip_config = {
|
||||
.name = "emc1812",
|
||||
.phys_channels = 2,
|
||||
.has_ext2_beta_reg = false,
|
||||
};
|
||||
|
||||
static const struct emc1812_features emc1813_chip_config = {
|
||||
.name = "emc1813",
|
||||
.phys_channels = 3,
|
||||
.has_ext2_beta_reg = true,
|
||||
};
|
||||
|
||||
static const struct emc1812_features emc1814_chip_config = {
|
||||
.name = "emc1814",
|
||||
.phys_channels = 4,
|
||||
.has_ext2_beta_reg = false,
|
||||
};
|
||||
|
||||
static const struct emc1812_features emc1815_chip_config = {
|
||||
.name = "emc1815",
|
||||
.phys_channels = 5,
|
||||
.has_ext2_beta_reg = false,
|
||||
};
|
||||
|
||||
enum emc1812_limit_type {temp_min, temp_max};
|
||||
|
||||
static const u8 emc1812_temp_map[] = {
|
||||
[hwmon_temp_min] = temp_min,
|
||||
[hwmon_temp_max] = temp_max,
|
||||
};
|
||||
|
||||
static const u8 emc1812_ideality_regs[] = {
|
||||
[0] = 0xff,
|
||||
[1] = EMC1812_EXT1_IDEALITY_FACTOR_ADDR,
|
||||
[2] = EMC1812_EXT2_IDEALITY_FACTOR_ADDR,
|
||||
[3] = EMC1812_EXT3_IDEALITY_FACTOR_ADDR,
|
||||
[4] = EMC1812_EXT4_IDEALITY_FACTOR_ADDR,
|
||||
};
|
||||
|
||||
static const u8 emc1812_temp_crit_regs[] = {
|
||||
[0] = EMC1812_INT_DIODE_THERM_LIMIT_ADDR,
|
||||
[1] = EMC1812_EXT1_THERM_LIMIT_ADDR,
|
||||
[2] = EMC1812_EXT2_THERM_LIMIT_ADDR,
|
||||
[3] = EMC1812_EXT3_THERM_LIMIT_ADDR,
|
||||
[4] = EMC1812_EXT4_THERM_LIMIT_ADDR,
|
||||
};
|
||||
|
||||
static const u8 emc1812_limit_regs[][2] = {
|
||||
[0] = {
|
||||
[temp_min] = EMC1812_INT_DIODE_LOW_LIMIT_ADDR,
|
||||
[temp_max] = EMC1812_INT_DIODE_HIGH_LIMIT_ADDR,
|
||||
},
|
||||
[1] = {
|
||||
[temp_min] = EMC1812_EXT1_LOW_LIMIT_HIGH_BYTE_ADDR,
|
||||
[temp_max] = EMC1812_EXT1_HIGH_LIMIT_HIGH_BYTE_ADDR,
|
||||
},
|
||||
[2] = {
|
||||
[temp_min] = EMC1812_EXT2_LOW_LIMIT_HIGH_BYTE_ADDR,
|
||||
[temp_max] = EMC1812_EXT2_HIGH_LIMIT_HIGH_BYTE_ADDR,
|
||||
},
|
||||
[3] = {
|
||||
[temp_min] = EMC1812_EXT3_LOW_LIMIT_HIGH_BYTE_ADDR,
|
||||
[temp_max] = EMC1812_EXT3_HIGH_LIMIT_HIGH_BYTE_ADDR,
|
||||
},
|
||||
[4] = {
|
||||
[temp_min] = EMC1812_EXT4_LOW_LIMIT_HIGH_BYTE_ADDR,
|
||||
[temp_max] = EMC1812_EXT4_HIGH_LIMIT_HIGH_BYTE_ADDR,
|
||||
},
|
||||
};
|
||||
|
||||
static const u8 emc1812_limit_regs_low[][2] = {
|
||||
[0] = {
|
||||
[temp_min] = 0xff,
|
||||
[temp_max] = 0xff,
|
||||
},
|
||||
[1] = {
|
||||
[temp_min] = EMC1812_EXT1_LOW_LIMIT_LOW_BYTE_ADDR,
|
||||
[temp_max] = EMC1812_EXT1_HIGH_LIMIT_LOW_BYTE_ADDR,
|
||||
},
|
||||
[2] = {
|
||||
[temp_min] = EMC1812_EXT2_LOW_LIMIT_LOW_BYTE_ADDR,
|
||||
[temp_max] = EMC1812_EXT2_HIGH_LIMIT_LOW_BYTE_ADDR,
|
||||
},
|
||||
[3] = {
|
||||
[temp_min] = EMC1812_EXT3_LOW_LIMIT_LOW_BYTE_ADDR,
|
||||
[temp_max] = EMC1812_EXT3_HIGH_LIMIT_LOW_BYTE_ADDR,
|
||||
},
|
||||
[4] = {
|
||||
[temp_min] = EMC1812_EXT4_LOW_LIMIT_LOW_BYTE_ADDR,
|
||||
[temp_max] = EMC1812_EXT4_HIGH_LIMIT_LOW_BYTE_ADDR,
|
||||
},
|
||||
};
|
||||
|
||||
/* Lookup table for temperature conversion times in msec */
|
||||
static const u16 emc1812_conv_time[] = {
|
||||
16000, 8000, 4000, 2000, 1000, 500, 250, 125, 62, 31, 16
|
||||
};
|
||||
|
||||
/**
|
||||
* struct emc1812_data - information about chip parameters
|
||||
* @labels: labels of the channels
|
||||
* @active_ch_mask: active channels
|
||||
* @chip: pointer to structure holding chip features
|
||||
* @regmap: device register map
|
||||
* @recd34_en: state of Resistance Error Correction (REC) on channels 3 and 4
|
||||
* @recd12_en: state of Resistance Error Correction (REC) on channels 1 and 2
|
||||
* @apdd_en: state of anti-parallel diode mode
|
||||
*/
|
||||
struct emc1812_data {
|
||||
const char *labels[EMC1812_MAX_NUM_CHANNELS];
|
||||
unsigned long active_ch_mask;
|
||||
const struct emc1812_features *chip;
|
||||
struct regmap *regmap;
|
||||
bool recd34_en;
|
||||
bool recd12_en;
|
||||
bool apdd_en;
|
||||
};
|
||||
|
||||
/* emc1812 regmap configuration */
|
||||
static const struct regmap_range emc1812_regmap_writable_ranges[] = {
|
||||
regmap_reg_range(EMC1812_CFG_ADDR, EMC1812_ONE_SHOT_ADDR),
|
||||
regmap_reg_range(EMC1812_EXT1_HIGH_LIMIT_LOW_BYTE_ADDR, EMC1812_EXT2_THERM_LIMIT_ADDR),
|
||||
regmap_reg_range(EMC1812_DIODE_FAULT_MASK_ADDR, EMC1812_CONSEC_ALERT_ADDR),
|
||||
regmap_reg_range(EMC1812_EXT1_BETA_CONFIG_ADDR, EMC1812_EXT4_IDEALITY_FACTOR_ADDR),
|
||||
regmap_reg_range(EMC1812_ROC_GAIN_ADDR, EMC1812_ROC_CONFIG_ADDR),
|
||||
regmap_reg_range(EMC1812_R1_LIMH_ADDR, EMC1812_R1_SMPL_ADDR),
|
||||
regmap_reg_range(EMC1812_R2_LIMH_ADDR, EMC1812_R2_SMPL_ADDR),
|
||||
regmap_reg_range(EMC1812_FILTER_SEL_ADDR, EMC1812_FILTER_SEL_ADDR),
|
||||
regmap_reg_range(EMC1812_HOTTEST_CFG_ADDR, EMC1812_HOTTEST_CFG_ADDR),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table emc1812_regmap_wr_table = {
|
||||
.yes_ranges = emc1812_regmap_writable_ranges,
|
||||
.n_yes_ranges = ARRAY_SIZE(emc1812_regmap_writable_ranges),
|
||||
};
|
||||
|
||||
static const struct regmap_range emc1812_regmap_rd_ranges[] = {
|
||||
regmap_reg_range(EMC1812_STATUS_ADDR, EMC1812_CONFIG_LO_ADDR),
|
||||
regmap_reg_range(EMC1812_CFG_ADDR, EMC1812_ONE_SHOT_ADDR),
|
||||
regmap_reg_range(EMC1812_EXT1_HIGH_LIMIT_LOW_BYTE_ADDR,
|
||||
EMC1812_EXT_DIODE_FAULT_STATUS_ADDR),
|
||||
regmap_reg_range(EMC1812_DIODE_FAULT_MASK_ADDR, EMC1812_CONSEC_ALERT_ADDR),
|
||||
regmap_reg_range(EMC1812_EXT1_BETA_CONFIG_ADDR, EMC1812_FILTER_SEL_ADDR),
|
||||
regmap_reg_range(EMC1812_INT_HIGH_BYTE_ADDR, EMC1812_HOTTEST_CFG_ADDR),
|
||||
regmap_reg_range(EMC1812_PRODUCT_ID_ADDR, EMC1812_REVISION_ADDR),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table emc1812_regmap_rd_table = {
|
||||
.yes_ranges = emc1812_regmap_rd_ranges,
|
||||
.n_yes_ranges = ARRAY_SIZE(emc1812_regmap_rd_ranges),
|
||||
};
|
||||
|
||||
static bool emc1812_is_volatile_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case EMC1812_STATUS_ADDR:
|
||||
case EMC1812_EXT_DIODE_FAULT_STATUS_ADDR:
|
||||
case EMC1812_DIODE_FAULT_MASK_ADDR:
|
||||
case EMC1812_EXT1_BETA_CONFIG_ADDR:
|
||||
case EMC1812_EXT2_BETA_CONFIG_ADDR:
|
||||
case EMC1812_HIGH_LIMIT_STATUS_ADDR:
|
||||
case EMC1812_LOW_LIMIT_STATUS_ADDR:
|
||||
case EMC1812_THERM_LIMIT_STATUS_ADDR:
|
||||
case EMC1812_ROC_STATUS_ADDR:
|
||||
case EMC1812_PER_MAXTH_1_ADDR:
|
||||
case EMC1812_PER_MAXT1L_ADDR:
|
||||
case EMC1812_PER_MAXTH_2_ADDR:
|
||||
case EMC1812_PER_MAXT2_3L_ADDR:
|
||||
case EMC1812_GBL_MAXT1H_ADDR:
|
||||
case EMC1812_GBL_MAXT1L_ADDR:
|
||||
case EMC1812_GBL_MAXT2H_ADDR:
|
||||
case EMC1812_GBL_MAXT2L_ADDR:
|
||||
case EMC1812_INT_HIGH_BYTE_ADDR:
|
||||
case EMC1812_INT_LOW_BYTE_ADDR:
|
||||
case EMC1812_EXT1_HIGH_BYTE_ADDR:
|
||||
case EMC1812_EXT1_LOW_BYTE_ADDR:
|
||||
case EMC1812_EXT2_HIGH_BYTE_ADDR:
|
||||
case EMC1812_EXT2_LOW_BYTE_ADDR:
|
||||
case EMC1812_EXT3_HIGH_BYTE_ADDR:
|
||||
case EMC1812_EXT3_LOW_BYTE_ADDR:
|
||||
case EMC1812_EXT4_HIGH_BYTE_ADDR:
|
||||
case EMC1812_EXT4_LOW_BYTE_ADDR:
|
||||
case EMC1812_HOTTEST_DIODE_HIGH_BYTE_ADDR:
|
||||
case EMC1812_HOTTEST_DIODE_LOW_BYTE_ADDR:
|
||||
case EMC1812_HOTTEST_STATUS_ADDR:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct regmap_config emc1812_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
.rd_table = &emc1812_regmap_rd_table,
|
||||
.wr_table = &emc1812_regmap_wr_table,
|
||||
.volatile_reg = emc1812_is_volatile_reg,
|
||||
.max_register = EMC1812_REVISION_ADDR,
|
||||
.cache_type = REGCACHE_MAPLE,
|
||||
};
|
||||
|
||||
static umode_t emc1812_is_visible(const void *_data, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel)
|
||||
{
|
||||
const struct emc1812_data *data = _data;
|
||||
|
||||
switch (type) {
|
||||
case hwmon_temp:
|
||||
/* Don't show channels which are not enabled */
|
||||
if (!(data->active_ch_mask & BIT(channel)))
|
||||
return 0;
|
||||
|
||||
switch (attr) {
|
||||
case hwmon_temp_min:
|
||||
case hwmon_temp_max:
|
||||
case hwmon_temp_crit:
|
||||
case hwmon_temp_crit_hyst:
|
||||
return 0644;
|
||||
case hwmon_temp_crit_alarm:
|
||||
case hwmon_temp_input:
|
||||
case hwmon_temp_fault:
|
||||
case hwmon_temp_max_alarm:
|
||||
case hwmon_temp_max_hyst:
|
||||
case hwmon_temp_min_alarm:
|
||||
return 0444;
|
||||
case hwmon_temp_label:
|
||||
if (data->labels[channel])
|
||||
return 0444;
|
||||
return 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
case hwmon_chip:
|
||||
switch (attr) {
|
||||
case hwmon_chip_update_interval:
|
||||
return 0644;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
static int emc1812_get_temp(struct emc1812_data *data, int channel, long *val)
|
||||
{
|
||||
__be16 tmp_be16;
|
||||
int ret;
|
||||
|
||||
ret = regmap_bulk_read(data->regmap, EMC1812_TEMP_CH_ADDR(channel),
|
||||
&tmp_be16, sizeof(tmp_be16));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Range is always -64 to 191.875°C */
|
||||
*val = ((be16_to_cpu(tmp_be16) >> 5) - (EMC1812_TEMP_OFFSET << 3)) * 125;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int emc1812_get_crit_limit_temp(struct emc1812_data *data, int channel, long *val)
|
||||
{
|
||||
unsigned int tmp;
|
||||
int ret;
|
||||
|
||||
/* Critical register is 8bits long and keeps only integer part of temperature */
|
||||
ret = regmap_read(data->regmap, emc1812_temp_crit_regs[channel], &tmp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = tmp;
|
||||
/* Range is always -64 to 191°C */
|
||||
*val = (*val - EMC1812_TEMP_OFFSET) * 1000;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int emc1812_get_limit_temp(struct emc1812_data *data, int ch,
|
||||
enum emc1812_limit_type type, long *val)
|
||||
{
|
||||
unsigned int regvalh;
|
||||
unsigned int regvall = 0;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(data->regmap, emc1812_limit_regs[ch][type], ®valh);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ch) {
|
||||
ret = regmap_read(data->regmap, emc1812_limit_regs_low[ch][type], ®vall);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Range is always -64 to 191.875°C */
|
||||
*val = ((regvalh << 3) | (regvall >> 5));
|
||||
*val = (*val - (EMC1812_TEMP_OFFSET << 3)) * 125;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int emc1812_read_reg(struct device *dev, struct emc1812_data *data, u32 attr,
|
||||
int channel, long *val)
|
||||
{
|
||||
unsigned int hyst;
|
||||
int ret;
|
||||
|
||||
switch (attr) {
|
||||
case hwmon_temp_min:
|
||||
case hwmon_temp_max:
|
||||
return emc1812_get_limit_temp(data, channel, emc1812_temp_map[attr], val);
|
||||
case hwmon_temp_crit:
|
||||
return emc1812_get_crit_limit_temp(data, channel, val);
|
||||
case hwmon_temp_input:
|
||||
return emc1812_get_temp(data, channel, val);
|
||||
case hwmon_temp_max_hyst:
|
||||
ret = emc1812_get_limit_temp(data, channel, temp_max, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_read(data->regmap, EMC1812_THRM_HYS_ADDR, &hyst);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val -= (long)hyst * 1000;
|
||||
|
||||
return 0;
|
||||
case hwmon_temp_crit_hyst:
|
||||
ret = emc1812_get_crit_limit_temp(data, channel, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_read(data->regmap, EMC1812_THRM_HYS_ADDR, &hyst);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val -= (long)hyst * 1000;
|
||||
|
||||
return 0;
|
||||
case hwmon_temp_min_alarm:
|
||||
*val = regmap_test_bits(data->regmap, EMC1812_LOW_LIMIT_STATUS_ADDR,
|
||||
BIT(channel));
|
||||
if (*val < 0)
|
||||
return *val;
|
||||
|
||||
return 0;
|
||||
case hwmon_temp_max_alarm:
|
||||
*val = regmap_test_bits(data->regmap, EMC1812_HIGH_LIMIT_STATUS_ADDR,
|
||||
BIT(channel));
|
||||
if (*val < 0)
|
||||
return *val;
|
||||
|
||||
return 0;
|
||||
case hwmon_temp_crit_alarm:
|
||||
*val = regmap_test_bits(data->regmap, EMC1812_THERM_LIMIT_STATUS_ADDR,
|
||||
BIT(channel));
|
||||
if (*val < 0)
|
||||
return *val;
|
||||
|
||||
return 0;
|
||||
case hwmon_temp_fault:
|
||||
*val = regmap_test_bits(data->regmap, EMC1812_EXT_DIODE_FAULT_STATUS_ADDR,
|
||||
BIT(channel));
|
||||
if (*val < 0)
|
||||
return *val;
|
||||
|
||||
return 0;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
static int emc1812_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
|
||||
int channel, long *val)
|
||||
{
|
||||
struct emc1812_data *data = dev_get_drvdata(dev);
|
||||
unsigned int convrate;
|
||||
int ret;
|
||||
|
||||
switch (type) {
|
||||
case hwmon_temp:
|
||||
return emc1812_read_reg(dev, data, attr, channel, val);
|
||||
case hwmon_chip:
|
||||
switch (attr) {
|
||||
case hwmon_chip_update_interval:
|
||||
ret = regmap_read(data->regmap, EMC1812_CONV_ADDR, &convrate);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (convrate > 10)
|
||||
convrate = 4;
|
||||
|
||||
*val = DIV_ROUND_CLOSEST(16000, 1 << convrate);
|
||||
return 0;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
static int emc1812_read_string(struct device *dev, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel, const char **str)
|
||||
{
|
||||
struct emc1812_data *data = dev_get_drvdata(dev);
|
||||
|
||||
if (channel >= data->chip->phys_channels)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
switch (type) {
|
||||
case hwmon_temp:
|
||||
switch (attr) {
|
||||
case hwmon_temp_label:
|
||||
*str = data->labels[channel];
|
||||
return 0;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
static int emc1812_set_hyst(struct emc1812_data *data, int channel, int val)
|
||||
{
|
||||
unsigned int limit;
|
||||
int hyst, ret;
|
||||
|
||||
/* Critical register is 8bits long and keeps only integer part of temperature */
|
||||
ret = regmap_read(data->regmap, emc1812_temp_crit_regs[channel], &limit);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
hyst = clamp_val((int)limit - val, 0, 255);
|
||||
|
||||
ret = regmap_write(data->regmap, EMC1812_THRM_HYS_ADDR, hyst);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int emc1812_set_temp(struct emc1812_data *data, int channel,
|
||||
enum emc1812_limit_type map, int val)
|
||||
{
|
||||
unsigned int valh, vall;
|
||||
u8 regh, regl;
|
||||
int ret;
|
||||
|
||||
regh = emc1812_limit_regs[channel][map];
|
||||
regl = emc1812_limit_regs_low[channel][map];
|
||||
|
||||
if (channel) {
|
||||
val = DIV_ROUND_CLOSEST(val, 125);
|
||||
valh = (val >> 3) & 0xff;
|
||||
vall = (val & 0x07) << 5;
|
||||
} else {
|
||||
/* Temperature limit for internal channel is stored on 8bits */
|
||||
valh = DIV_ROUND_CLOSEST(val, 1000);
|
||||
valh = clamp_val(valh, 0, 255);
|
||||
}
|
||||
|
||||
ret = regmap_write(data->regmap, regh, valh);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (channel)
|
||||
ret = regmap_write(data->regmap, regl, vall);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int emc1812_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
|
||||
int channel, long val)
|
||||
{
|
||||
struct emc1812_data *data = dev_get_drvdata(dev);
|
||||
unsigned int interval, tmp;
|
||||
|
||||
switch (type) {
|
||||
case hwmon_temp:
|
||||
/* Range should be -64000 to 191875°C + (EMC1812_TEMP_OFFSET * 1000) */
|
||||
val = clamp_val(val, -64000, 191875);
|
||||
val = val + (EMC1812_TEMP_OFFSET * 1000);
|
||||
|
||||
switch (attr) {
|
||||
case hwmon_temp_min:
|
||||
case hwmon_temp_max:
|
||||
return emc1812_set_temp(data, channel, emc1812_temp_map[attr], val);
|
||||
case hwmon_temp_crit:
|
||||
/* Critical temperature limit is stored on 8bits */
|
||||
val = DIV_ROUND_CLOSEST(val, 1000);
|
||||
tmp = clamp_val(val, 0, 255);
|
||||
return regmap_write(data->regmap, emc1812_temp_crit_regs[channel], tmp);
|
||||
case hwmon_temp_crit_hyst:
|
||||
/* Critical temperature hysteresis is stored on 8bits */
|
||||
val = DIV_ROUND_CLOSEST(val, 1000);
|
||||
tmp = clamp_val(val, 0, 255);
|
||||
return emc1812_set_hyst(data, channel, tmp);
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
case hwmon_chip:
|
||||
switch (attr) {
|
||||
case hwmon_chip_update_interval:
|
||||
interval = clamp_val(val, 0, 16000);
|
||||
tmp = find_closest_descending(interval, emc1812_conv_time,
|
||||
ARRAY_SIZE(emc1812_conv_time));
|
||||
return regmap_write(data->regmap, EMC1812_CONV_ADDR, tmp);
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
static int emc1812_init(struct emc1812_data *priv)
|
||||
{
|
||||
int i, ret;
|
||||
u8 val;
|
||||
|
||||
ret = regmap_write(priv->regmap, EMC1812_THRM_HYS_ADDR, 0x0A);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(priv->regmap, EMC1812_CONSEC_ALERT_ADDR, 0x70);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(priv->regmap, EMC1812_FILTER_SEL_ADDR, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(priv->regmap, EMC1812_HOTTEST_CFG_ADDR, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Enables the beta compensation factor auto-detection function for beta1 and beta2 */
|
||||
ret = regmap_write(priv->regmap, EMC1812_EXT1_BETA_CONFIG_ADDR,
|
||||
EMC1812_BETA_LOCK_VAL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (priv->chip->has_ext2_beta_reg) {
|
||||
ret = regmap_write(priv->regmap, EMC1812_EXT2_BETA_CONFIG_ADDR,
|
||||
EMC1812_BETA_LOCK_VAL);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < priv->chip->phys_channels; i++) {
|
||||
if (!test_bit(i, &priv->active_ch_mask))
|
||||
continue;
|
||||
|
||||
/* Update the max temperature limit for extended temperature range. */
|
||||
ret = emc1812_set_temp(priv, i, emc1812_temp_map[hwmon_temp_max],
|
||||
EMC1812_HIGH_LIMIT_DEFAULT * 1000);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Update the critical temperature limit for extended temperature range. */
|
||||
ret = regmap_write(priv->regmap, emc1812_temp_crit_regs[i],
|
||||
EMC1812_HIGH_LIMIT_DEFAULT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Set the ideality factor */
|
||||
if (i > 0) {
|
||||
ret = regmap_write(priv->regmap, emc1812_ideality_regs[i],
|
||||
EMC1812_DEFAULT_IDEALITY_FACTOR);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Set default values in registers. APDD, RECD12 and RECD34 are active on 0.
|
||||
* Set the device to be in Run (Active) state and converting on all
|
||||
* channels.
|
||||
* Don't change conversion rate. After reset, default is 4 conversions/seconds.
|
||||
* The temperature measurement range is -64°C to +191.875°C.
|
||||
* Set ALERT/THERM2 pin to be in comparator mode (When the ALERT/THERM2 pin is
|
||||
* asserted in comparator mode, the corresponding High Limit Status bits are set.
|
||||
* Reading these bits does not clear them until the ALERT/THERM2 pin is deasserted.
|
||||
* Once the ALERT/THERM2 pin is deasserted, the status bits are automatically
|
||||
* cleared.).
|
||||
*/
|
||||
val = FIELD_PREP(EMC1812_CFG_MSKAL, 0) |
|
||||
FIELD_PREP(EMC1812_CFG_RS, 0) |
|
||||
FIELD_PREP(EMC1812_CFG_ATTHM, 1) |
|
||||
FIELD_PREP(EMC1812_CFG_RECD12, !priv->recd12_en) |
|
||||
FIELD_PREP(EMC1812_CFG_RECD34, !priv->recd34_en) |
|
||||
FIELD_PREP(EMC1812_CFG_RANGE, 1) |
|
||||
FIELD_PREP(EMC1812_CFG_DA_ENA, 0) |
|
||||
FIELD_PREP(EMC1812_CFG_APDD, !priv->apdd_en);
|
||||
|
||||
return regmap_write(priv->regmap, EMC1812_CFG_ADDR, val);
|
||||
}
|
||||
|
||||
static int emc1812_parse_fw_config(struct emc1812_data *data, struct device *dev)
|
||||
{
|
||||
unsigned int reg_nr = 0;
|
||||
int ret;
|
||||
|
||||
/* To be able to load the driver in case we don't have device tree */
|
||||
if (!dev_fwnode(dev)) {
|
||||
data->active_ch_mask = BIT(data->chip->phys_channels) - 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
data->apdd_en = device_property_read_bool(dev, "microchip,enable-anti-parallel");
|
||||
data->recd12_en = device_property_read_bool(dev, "microchip,parasitic-res-on-channel1-2");
|
||||
data->recd34_en = device_property_read_bool(dev, "microchip,parasitic-res-on-channel3-4");
|
||||
|
||||
/* Internal temperature channel is always active */
|
||||
data->labels[reg_nr] = "internal_diode";
|
||||
set_bit(reg_nr, &data->active_ch_mask);
|
||||
|
||||
device_for_each_child_node_scoped(dev, child) {
|
||||
ret = fwnode_property_read_u32(child, "reg", ®_nr);
|
||||
if (ret || reg_nr >= data->chip->phys_channels)
|
||||
return dev_err_probe(dev, -EINVAL,
|
||||
"The index is higher then the chip supports\n");
|
||||
/* Mark channel as active */
|
||||
set_bit(reg_nr, &data->active_ch_mask);
|
||||
|
||||
fwnode_property_read_string(child, "label", &data->labels[reg_nr]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int emc1812_chip_identify(struct emc1812_data *data, struct i2c_client *client)
|
||||
{
|
||||
const struct emc1812_features *chip;
|
||||
struct device *dev = &client->dev;
|
||||
unsigned int tmp;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(data->regmap, EMC1812_PRODUCT_ID_ADDR, &tmp);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
switch (tmp) {
|
||||
case EMC1812_PID:
|
||||
data->chip = &emc1812_chip_config;
|
||||
break;
|
||||
case EMC1813_PID:
|
||||
data->chip = &emc1813_chip_config;
|
||||
break;
|
||||
case EMC1814_PID:
|
||||
data->chip = &emc1814_chip_config;
|
||||
break;
|
||||
case EMC1815_PID:
|
||||
data->chip = &emc1815_chip_config;
|
||||
break;
|
||||
case EMC1833_PID:
|
||||
data->chip = &emc1833_chip_config;
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
* If failed to identify the hardware based on internal registers,
|
||||
* try using fallback compatible in device tree to deal with some
|
||||
* newer part number.
|
||||
*/
|
||||
chip = i2c_get_match_data(client);
|
||||
if (!chip)
|
||||
return -ENODEV;
|
||||
|
||||
dev_warn(dev, "Unrecognized hardware ID 0x%x, using %s from devicetree data\n",
|
||||
tmp, chip->name);
|
||||
|
||||
data->chip = chip;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct hwmon_ops emc1812_ops = {
|
||||
.is_visible = emc1812_is_visible,
|
||||
.read = emc1812_read,
|
||||
.read_string = emc1812_read_string,
|
||||
.write = emc1812_write,
|
||||
};
|
||||
|
||||
static const struct hwmon_chip_info emc1812_chip_info = {
|
||||
.ops = &emc1812_ops,
|
||||
.info = emc1812_info,
|
||||
};
|
||||
|
||||
static int emc1812_probe(struct i2c_client *client)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct emc1812_data *data;
|
||||
struct device *hwmon_dev;
|
||||
int ret;
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->regmap = devm_regmap_init_i2c(client, &emc1812_regmap_config);
|
||||
if (IS_ERR(data->regmap))
|
||||
return dev_err_probe(dev, PTR_ERR(data->regmap),
|
||||
"Cannot initialize register map\n");
|
||||
|
||||
ret = emc1812_chip_identify(data, client);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Chip identification fails\n");
|
||||
|
||||
ret = emc1812_parse_fw_config(data, dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = emc1812_init(data);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Cannot initialize device\n");
|
||||
|
||||
hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data,
|
||||
&emc1812_chip_info, NULL);
|
||||
|
||||
return PTR_ERR_OR_ZERO(hwmon_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id emc1812_id[] = {
|
||||
{ .name = "emc1812", .driver_data = (kernel_ulong_t)&emc1812_chip_config },
|
||||
{ .name = "emc1813", .driver_data = (kernel_ulong_t)&emc1813_chip_config },
|
||||
{ .name = "emc1814", .driver_data = (kernel_ulong_t)&emc1814_chip_config },
|
||||
{ .name = "emc1815", .driver_data = (kernel_ulong_t)&emc1815_chip_config },
|
||||
{ .name = "emc1833", .driver_data = (kernel_ulong_t)&emc1833_chip_config },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, emc1812_id);
|
||||
|
||||
static const struct of_device_id emc1812_of_match[] = {
|
||||
{
|
||||
.compatible = "microchip,emc1812",
|
||||
.data = &emc1812_chip_config
|
||||
},
|
||||
{
|
||||
.compatible = "microchip,emc1813",
|
||||
.data = &emc1813_chip_config
|
||||
},
|
||||
{
|
||||
.compatible = "microchip,emc1814",
|
||||
.data = &emc1814_chip_config
|
||||
},
|
||||
{
|
||||
.compatible = "microchip,emc1815",
|
||||
.data = &emc1815_chip_config
|
||||
},
|
||||
{
|
||||
.compatible = "microchip,emc1833",
|
||||
.data = &emc1833_chip_config
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, emc1812_of_match);
|
||||
|
||||
static struct i2c_driver emc1812_driver = {
|
||||
.driver = {
|
||||
.name = "emc1812",
|
||||
.of_match_table = emc1812_of_match,
|
||||
},
|
||||
.probe = emc1812_probe,
|
||||
.id_table = emc1812_id,
|
||||
};
|
||||
module_i2c_driver(emc1812_driver);
|
||||
|
||||
MODULE_AUTHOR("Marius Cristea <marius.cristea@microchip.com>");
|
||||
MODULE_DESCRIPTION("EMC1812/13/14/15/33 high-accuracy remote diode temperature monitor Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -624,7 +624,7 @@ emc2103_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id emc2103_ids[] = {
|
||||
{ "emc2103" },
|
||||
{ .name = "emc2103" },
|
||||
{ /* LIST END */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, emc2103_ids);
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#define EMC2305_REG_DRIVE_PWM_OUT 0x2b
|
||||
#define EMC2305_OPEN_DRAIN 0x0
|
||||
#define EMC2305_PUSH_PULL 0x1
|
||||
#define EMC2305_PWM_SHUTDOWN_UNSET -1
|
||||
|
||||
#define EMC2305_PWM_DUTY2STATE(duty, max_state, pwm_max) \
|
||||
DIV_ROUND_CLOSEST((duty) * (max_state), (pwm_max))
|
||||
@@ -59,10 +60,10 @@ enum emc230x_product_id {
|
||||
};
|
||||
|
||||
static const struct i2c_device_id emc2305_ids[] = {
|
||||
{ "emc2305" },
|
||||
{ "emc2303" },
|
||||
{ "emc2302" },
|
||||
{ "emc2301" },
|
||||
{ .name = "emc2305" },
|
||||
{ .name = "emc2303" },
|
||||
{ .name = "emc2302" },
|
||||
{ .name = "emc2301" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, emc2305_ids);
|
||||
@@ -104,6 +105,7 @@ struct emc2305_cdev_data {
|
||||
* @pwm_output_mask: PWM output mask
|
||||
* @pwm_polarity_mask: PWM polarity mask
|
||||
* @pwm_separate: separate PWM settings for every channel
|
||||
* @pwm_shutdown: Set shutdown PWM.
|
||||
* @pwm_min: array of minimum PWM per channel
|
||||
* @pwm_freq: array of PWM frequency per channel
|
||||
* @cdev_data: array of cooling devices data
|
||||
@@ -116,6 +118,7 @@ struct emc2305_data {
|
||||
u8 pwm_output_mask;
|
||||
u8 pwm_polarity_mask;
|
||||
bool pwm_separate;
|
||||
s16 pwm_shutdown[EMC2305_PWM_MAX];
|
||||
u8 pwm_min[EMC2305_PWM_MAX];
|
||||
u16 pwm_freq[EMC2305_PWM_MAX];
|
||||
struct emc2305_cdev_data cdev_data[EMC2305_PWM_MAX];
|
||||
@@ -539,6 +542,7 @@ static int emc2305_of_parse_pwm_child(struct device *dev,
|
||||
struct device_node *child,
|
||||
struct emc2305_data *data)
|
||||
{ u32 ch;
|
||||
u32 pwm_shutdown_percent;
|
||||
int ret;
|
||||
struct of_phandle_args args;
|
||||
|
||||
@@ -548,6 +552,12 @@ static int emc2305_of_parse_pwm_child(struct device *dev,
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (ch >= data->pwm_num) {
|
||||
dev_err(dev, "invalid reg %u for node %pOF (valid range 0-%u)\n", ch, child,
|
||||
data->pwm_num - 1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = of_parse_phandle_with_args(child, "pwms", "#pwm-cells", 0, &args);
|
||||
|
||||
if (ret)
|
||||
@@ -579,6 +589,16 @@ static int emc2305_of_parse_pwm_child(struct device *dev,
|
||||
}
|
||||
|
||||
of_node_put(args.np);
|
||||
|
||||
ret = of_property_read_u32(child, "fan-shutdown-percent",
|
||||
&pwm_shutdown_percent);
|
||||
|
||||
if (!ret) {
|
||||
pwm_shutdown_percent = clamp(pwm_shutdown_percent, 0, 100);
|
||||
data->pwm_shutdown[ch] =
|
||||
DIV_ROUND_CLOSEST(pwm_shutdown_percent * EMC2305_FAN_MAX, 100);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -612,6 +632,7 @@ static int emc2305_probe(struct i2c_client *client)
|
||||
int ret;
|
||||
int i;
|
||||
int pwm_childs;
|
||||
u32 ch;
|
||||
|
||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA))
|
||||
return -ENODEV;
|
||||
@@ -631,6 +652,9 @@ static int emc2305_probe(struct i2c_client *client)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < EMC2305_PWM_MAX; i++)
|
||||
data->pwm_shutdown[i] = EMC2305_PWM_SHUTDOWN_UNSET;
|
||||
|
||||
pwm_childs = emc2305_probe_childs_from_dt(dev);
|
||||
|
||||
pdata = dev_get_platdata(&client->dev);
|
||||
@@ -680,12 +704,18 @@ static int emc2305_probe(struct i2c_client *client)
|
||||
if (IS_REACHABLE(CONFIG_THERMAL)) {
|
||||
/* Parse and check for the available PWM child nodes */
|
||||
if (pwm_childs > 0) {
|
||||
i = 0;
|
||||
for_each_child_of_node_scoped(dev->of_node, child) {
|
||||
ret = emc2305_set_single_tz(dev, child, i);
|
||||
ret = of_property_read_u32(child, "reg", &ch);
|
||||
if (ret || ch >= data->pwm_num)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* emc2305_set_single_tz() uses 0 for the common cooling
|
||||
* device and 1..pwm_num for individual fan channels.
|
||||
*/
|
||||
ret = emc2305_set_single_tz(dev, child, ch + 1);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
ret = emc2305_set_tz(dev);
|
||||
@@ -714,6 +744,23 @@ static int emc2305_probe(struct i2c_client *client)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void emc2305_shutdown(struct i2c_client *client)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
struct emc2305_data *data = i2c_get_clientdata(client);
|
||||
|
||||
for (i = 0; i < data->pwm_num; i++) {
|
||||
if (data->pwm_shutdown[i] != EMC2305_PWM_SHUTDOWN_UNSET) {
|
||||
ret = i2c_smbus_write_byte_data(client, EMC2305_REG_FAN_DRIVE(i),
|
||||
data->pwm_shutdown[i]);
|
||||
if (ret < 0)
|
||||
dev_warn(&client->dev,
|
||||
"Failed to set shutdown PWM for ch %d\n", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const struct of_device_id of_emc2305_match_table[] = {
|
||||
{ .compatible = "microchip,emc2305", },
|
||||
{},
|
||||
@@ -726,6 +773,7 @@ static struct i2c_driver emc2305_driver = {
|
||||
.of_match_table = of_emc2305_match_table,
|
||||
},
|
||||
.probe = emc2305_probe,
|
||||
.shutdown = emc2305_shutdown,
|
||||
.id_table = emc2305_ids,
|
||||
};
|
||||
|
||||
|
||||
@@ -464,7 +464,7 @@ static int emc6w201_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id emc6w201_id[] = {
|
||||
{ "emc6w201" },
|
||||
{ .name = "emc6w201" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, emc6w201_id);
|
||||
|
||||
@@ -877,9 +877,9 @@ static int f75375_detect(struct i2c_client *client,
|
||||
}
|
||||
|
||||
static const struct i2c_device_id f75375_id[] = {
|
||||
{ "f75373", f75373 },
|
||||
{ "f75375", f75375 },
|
||||
{ "f75387", f75387 },
|
||||
{ .name = "f75373", .driver_data = f75373 },
|
||||
{ .name = "f75375", .driver_data = f75375 },
|
||||
{ .name = "f75387", .driver_data = f75387 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, f75375_id);
|
||||
|
||||
@@ -225,13 +225,13 @@ static struct fschmd_data *fschmd_update_device(struct device *dev);
|
||||
*/
|
||||
|
||||
static const struct i2c_device_id fschmd_id[] = {
|
||||
{ "fscpos", fscpos },
|
||||
{ "fscher", fscher },
|
||||
{ "fscscy", fscscy },
|
||||
{ "fschrc", fschrc },
|
||||
{ "fschmd", fschmd },
|
||||
{ "fschds", fschds },
|
||||
{ "fscsyl", fscsyl },
|
||||
{ .name = "fscpos", .driver_data = fscpos },
|
||||
{ .name = "fscher", .driver_data = fscher },
|
||||
{ .name = "fscscy", .driver_data = fscscy },
|
||||
{ .name = "fschrc", .driver_data = fschrc },
|
||||
{ .name = "fschmd", .driver_data = fschmd },
|
||||
{ .name = "fschds", .driver_data = fschds },
|
||||
{ .name = "fscsyl", .driver_data = fscsyl },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, fschmd_id);
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
static const unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END };
|
||||
|
||||
static const struct i2c_device_id fts_id[] = {
|
||||
{ "ftsteutates" },
|
||||
{ .name = "ftsteutates" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, fts_id);
|
||||
|
||||
@@ -197,7 +197,7 @@ static int g760a_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id g760a_id[] = {
|
||||
{ "g760a" },
|
||||
{ .name = "g760a" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, g760a_id);
|
||||
|
||||
@@ -44,9 +44,9 @@
|
||||
#define DRVNAME "g762"
|
||||
|
||||
static const struct i2c_device_id g762_id[] = {
|
||||
{ "g761" },
|
||||
{ "g762" },
|
||||
{ "g763" },
|
||||
{ .name = "g761" },
|
||||
{ .name = "g762" },
|
||||
{ .name = "g763" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, g762_id);
|
||||
|
||||
@@ -642,7 +642,7 @@ static int gl518_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id gl518_id[] = {
|
||||
{ "gl518sm" },
|
||||
{ .name = "gl518sm" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, gl518_id);
|
||||
|
||||
@@ -885,7 +885,7 @@ static int gl520_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id gl520_id[] = {
|
||||
{ "gl520sm" },
|
||||
{ .name = "gl520sm" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, gl520_id);
|
||||
|
||||
@@ -40,12 +40,11 @@ enum FAN_PWM_ENABLE {
|
||||
AUTOMATIC = 2,
|
||||
};
|
||||
|
||||
static struct {
|
||||
struct gpd_fan_data {
|
||||
enum FAN_PWM_ENABLE pwm_enable;
|
||||
u8 pwm_value;
|
||||
|
||||
const struct gpd_fan_drvdata *drvdata;
|
||||
} gpd_driver_priv;
|
||||
};
|
||||
|
||||
struct gpd_fan_drvdata {
|
||||
const char *board_name; // Board name for module param comparison
|
||||
@@ -249,10 +248,10 @@ static const struct gpd_fan_drvdata *gpd_module_drvdata[] = {
|
||||
};
|
||||
|
||||
// Helper functions to handle EC read/write
|
||||
static void gpd_ecram_read(u16 offset, u8 *val)
|
||||
static void gpd_ecram_read(const struct gpd_fan_drvdata *drvdata, u16 offset, u8 *val)
|
||||
{
|
||||
u16 addr_port = gpd_driver_priv.drvdata->addr_port;
|
||||
u16 data_port = gpd_driver_priv.drvdata->data_port;
|
||||
u16 addr_port = drvdata->addr_port;
|
||||
u16 data_port = drvdata->data_port;
|
||||
|
||||
outb(0x2E, addr_port);
|
||||
outb(0x11, data_port);
|
||||
@@ -270,10 +269,10 @@ static void gpd_ecram_read(u16 offset, u8 *val)
|
||||
*val = inb(data_port);
|
||||
}
|
||||
|
||||
static void gpd_ecram_write(u16 offset, u8 value)
|
||||
static void gpd_ecram_write(const struct gpd_fan_drvdata *drvdata, u16 offset, u8 value)
|
||||
{
|
||||
u16 addr_port = gpd_driver_priv.drvdata->addr_port;
|
||||
u16 data_port = gpd_driver_priv.drvdata->data_port;
|
||||
u16 addr_port = drvdata->addr_port;
|
||||
u16 data_port = drvdata->data_port;
|
||||
|
||||
outb(0x2E, addr_port);
|
||||
outb(0x11, data_port);
|
||||
@@ -291,198 +290,202 @@ static void gpd_ecram_write(u16 offset, u8 value)
|
||||
outb(value, data_port);
|
||||
}
|
||||
|
||||
static int gpd_generic_read_rpm(void)
|
||||
static int gpd_generic_read_rpm(struct gpd_fan_data *data)
|
||||
{
|
||||
const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata;
|
||||
const struct gpd_fan_drvdata *drvdata = data->drvdata;
|
||||
u8 high, low;
|
||||
|
||||
gpd_ecram_read(drvdata->rpm_read, &high);
|
||||
gpd_ecram_read(drvdata->rpm_read + 1, &low);
|
||||
gpd_ecram_read(drvdata, drvdata->rpm_read, &high);
|
||||
gpd_ecram_read(drvdata, drvdata->rpm_read + 1, &low);
|
||||
|
||||
return (u16)high << 8 | low;
|
||||
}
|
||||
|
||||
static int gpd_wm2_read_rpm(void)
|
||||
static int gpd_wm2_read_rpm(struct gpd_fan_data *data)
|
||||
{
|
||||
const struct gpd_fan_drvdata *drvdata = data->drvdata;
|
||||
|
||||
for (u16 pwm_ctr_offset = GPD_PWM_CTR_OFFSET;
|
||||
pwm_ctr_offset <= GPD_PWM_CTR_OFFSET + 2; pwm_ctr_offset++) {
|
||||
u8 PWMCTR;
|
||||
|
||||
gpd_ecram_read(pwm_ctr_offset, &PWMCTR);
|
||||
gpd_ecram_read(drvdata, pwm_ctr_offset, &PWMCTR);
|
||||
|
||||
if (PWMCTR != 0xB8)
|
||||
gpd_ecram_write(pwm_ctr_offset, 0xB8);
|
||||
gpd_ecram_write(drvdata, pwm_ctr_offset, 0xB8);
|
||||
}
|
||||
|
||||
return gpd_generic_read_rpm();
|
||||
return gpd_generic_read_rpm(data);
|
||||
}
|
||||
|
||||
// Read value for fan1_input
|
||||
static int gpd_read_rpm(void)
|
||||
static int gpd_read_rpm(struct gpd_fan_data *data)
|
||||
{
|
||||
switch (gpd_driver_priv.drvdata->board) {
|
||||
switch (data->drvdata->board) {
|
||||
case win4_6800u:
|
||||
case win_mini:
|
||||
case duo:
|
||||
case mpc2:
|
||||
return gpd_generic_read_rpm();
|
||||
return gpd_generic_read_rpm(data);
|
||||
case win_max_2:
|
||||
return gpd_wm2_read_rpm();
|
||||
return gpd_wm2_read_rpm(data);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpd_wm2_read_pwm(void)
|
||||
static int gpd_wm2_read_pwm(struct gpd_fan_data *data)
|
||||
{
|
||||
const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata;
|
||||
const struct gpd_fan_drvdata *drvdata = data->drvdata;
|
||||
u8 var;
|
||||
|
||||
gpd_ecram_read(drvdata->pwm_write, &var);
|
||||
gpd_ecram_read(drvdata, drvdata->pwm_write, &var);
|
||||
|
||||
// EC PWM register valid range is 1 ~ pwm_max; 0 is an invalid state
|
||||
if (unlikely(!var))
|
||||
return -EIO;
|
||||
|
||||
// Match gpd_generic_write_pwm(u8) below
|
||||
return DIV_ROUND_CLOSEST((var - 1) * 255, (drvdata->pwm_max - 1));
|
||||
}
|
||||
|
||||
// Read value for pwm1
|
||||
static int gpd_read_pwm(void)
|
||||
static int gpd_read_pwm(struct gpd_fan_data *data)
|
||||
{
|
||||
switch (gpd_driver_priv.drvdata->board) {
|
||||
switch (data->drvdata->board) {
|
||||
case win_mini:
|
||||
case duo:
|
||||
case win4_6800u:
|
||||
case mpc2:
|
||||
switch (gpd_driver_priv.pwm_enable) {
|
||||
switch (data->pwm_enable) {
|
||||
case DISABLE:
|
||||
return 255;
|
||||
case MANUAL:
|
||||
return gpd_driver_priv.pwm_value;
|
||||
return data->pwm_value;
|
||||
case AUTOMATIC:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
break;
|
||||
case win_max_2:
|
||||
return gpd_wm2_read_pwm();
|
||||
return gpd_wm2_read_pwm(data);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// PWM value's range in EC is 1 - pwm_max, cast 0 - 255 to it.
|
||||
static inline u8 gpd_cast_pwm_range(u8 val)
|
||||
static inline u8 gpd_cast_pwm_range(const struct gpd_fan_drvdata *drvdata, u8 val)
|
||||
{
|
||||
const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata;
|
||||
|
||||
return DIV_ROUND_CLOSEST(val * (drvdata->pwm_max - 1), 255) + 1;
|
||||
}
|
||||
|
||||
static void gpd_generic_write_pwm(u8 val)
|
||||
static void gpd_generic_write_pwm(struct gpd_fan_data *data, u8 val)
|
||||
{
|
||||
const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata;
|
||||
const struct gpd_fan_drvdata *drvdata = data->drvdata;
|
||||
u8 pwm_reg;
|
||||
|
||||
pwm_reg = gpd_cast_pwm_range(val);
|
||||
gpd_ecram_write(drvdata->pwm_write, pwm_reg);
|
||||
pwm_reg = gpd_cast_pwm_range(drvdata, val);
|
||||
gpd_ecram_write(drvdata, drvdata->pwm_write, pwm_reg);
|
||||
}
|
||||
|
||||
static void gpd_duo_write_pwm(u8 val)
|
||||
static void gpd_duo_write_pwm(struct gpd_fan_data *data, u8 val)
|
||||
{
|
||||
const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata;
|
||||
const struct gpd_fan_drvdata *drvdata = data->drvdata;
|
||||
u8 pwm_reg;
|
||||
|
||||
pwm_reg = gpd_cast_pwm_range(val);
|
||||
gpd_ecram_write(drvdata->pwm_write, pwm_reg);
|
||||
gpd_ecram_write(drvdata->pwm_write + 1, pwm_reg);
|
||||
pwm_reg = gpd_cast_pwm_range(drvdata, val);
|
||||
gpd_ecram_write(drvdata, drvdata->pwm_write, pwm_reg);
|
||||
gpd_ecram_write(drvdata, drvdata->pwm_write + 1, pwm_reg);
|
||||
}
|
||||
|
||||
// Write value for pwm1
|
||||
static int gpd_write_pwm(u8 val)
|
||||
static int gpd_write_pwm(struct gpd_fan_data *data, u8 val)
|
||||
{
|
||||
if (gpd_driver_priv.pwm_enable != MANUAL)
|
||||
if (data->pwm_enable != MANUAL)
|
||||
return -EPERM;
|
||||
|
||||
switch (gpd_driver_priv.drvdata->board) {
|
||||
switch (data->drvdata->board) {
|
||||
case duo:
|
||||
gpd_duo_write_pwm(val);
|
||||
gpd_duo_write_pwm(data, val);
|
||||
break;
|
||||
case win_mini:
|
||||
case win4_6800u:
|
||||
case win_max_2:
|
||||
case mpc2:
|
||||
gpd_generic_write_pwm(val);
|
||||
gpd_generic_write_pwm(data, val);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gpd_win_mini_set_pwm_enable(enum FAN_PWM_ENABLE pwm_enable)
|
||||
static void gpd_win_mini_set_pwm_enable(struct gpd_fan_data *data, enum FAN_PWM_ENABLE pwm_enable)
|
||||
{
|
||||
switch (pwm_enable) {
|
||||
case DISABLE:
|
||||
gpd_generic_write_pwm(255);
|
||||
gpd_generic_write_pwm(data, 255);
|
||||
break;
|
||||
case MANUAL:
|
||||
gpd_generic_write_pwm(gpd_driver_priv.pwm_value);
|
||||
gpd_generic_write_pwm(data, data->pwm_value);
|
||||
break;
|
||||
case AUTOMATIC:
|
||||
gpd_ecram_write(gpd_driver_priv.drvdata->pwm_write, 0);
|
||||
gpd_ecram_write(data->drvdata, data->drvdata->pwm_write, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void gpd_duo_set_pwm_enable(enum FAN_PWM_ENABLE pwm_enable)
|
||||
static void gpd_duo_set_pwm_enable(struct gpd_fan_data *data, enum FAN_PWM_ENABLE pwm_enable)
|
||||
{
|
||||
switch (pwm_enable) {
|
||||
case DISABLE:
|
||||
gpd_duo_write_pwm(255);
|
||||
gpd_duo_write_pwm(data, 255);
|
||||
break;
|
||||
case MANUAL:
|
||||
gpd_duo_write_pwm(gpd_driver_priv.pwm_value);
|
||||
gpd_duo_write_pwm(data, data->pwm_value);
|
||||
break;
|
||||
case AUTOMATIC:
|
||||
gpd_ecram_write(gpd_driver_priv.drvdata->pwm_write, 0);
|
||||
gpd_ecram_write(data->drvdata, data->drvdata->pwm_write, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void gpd_wm2_set_pwm_enable(enum FAN_PWM_ENABLE enable)
|
||||
static void gpd_wm2_set_pwm_enable(struct gpd_fan_data *data, enum FAN_PWM_ENABLE enable)
|
||||
{
|
||||
const struct gpd_fan_drvdata *const drvdata = gpd_driver_priv.drvdata;
|
||||
const struct gpd_fan_drvdata *drvdata = data->drvdata;
|
||||
|
||||
switch (enable) {
|
||||
case DISABLE:
|
||||
gpd_generic_write_pwm(255);
|
||||
gpd_ecram_write(drvdata->manual_control_enable, 1);
|
||||
gpd_generic_write_pwm(data, 255);
|
||||
gpd_ecram_write(drvdata, drvdata->manual_control_enable, 1);
|
||||
break;
|
||||
case MANUAL:
|
||||
gpd_generic_write_pwm(gpd_driver_priv.pwm_value);
|
||||
gpd_ecram_write(drvdata->manual_control_enable, 1);
|
||||
gpd_generic_write_pwm(data, data->pwm_value);
|
||||
gpd_ecram_write(drvdata, drvdata->manual_control_enable, 1);
|
||||
break;
|
||||
case AUTOMATIC:
|
||||
gpd_ecram_write(drvdata->manual_control_enable, 0);
|
||||
gpd_ecram_write(drvdata, drvdata->manual_control_enable, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Write value for pwm1_enable
|
||||
static void gpd_set_pwm_enable(enum FAN_PWM_ENABLE enable)
|
||||
static void gpd_set_pwm_enable(struct gpd_fan_data *data, enum FAN_PWM_ENABLE enable)
|
||||
{
|
||||
if (enable == MANUAL)
|
||||
// Set pwm_value to max firstly when switching to manual mode, in
|
||||
// consideration of device safety.
|
||||
gpd_driver_priv.pwm_value = 255;
|
||||
data->pwm_value = 255;
|
||||
|
||||
switch (gpd_driver_priv.drvdata->board) {
|
||||
switch (data->drvdata->board) {
|
||||
case win_mini:
|
||||
case win4_6800u:
|
||||
case mpc2:
|
||||
gpd_win_mini_set_pwm_enable(enable);
|
||||
gpd_win_mini_set_pwm_enable(data, enable);
|
||||
break;
|
||||
case duo:
|
||||
gpd_duo_set_pwm_enable(enable);
|
||||
gpd_duo_set_pwm_enable(data, enable);
|
||||
break;
|
||||
case win_max_2:
|
||||
gpd_wm2_set_pwm_enable(enable);
|
||||
gpd_wm2_set_pwm_enable(data, enable);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -505,15 +508,16 @@ static umode_t gpd_fan_hwmon_is_visible(__always_unused const void *drvdata,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpd_fan_hwmon_read(__always_unused struct device *dev,
|
||||
static int gpd_fan_hwmon_read(struct device *dev,
|
||||
enum hwmon_sensor_types type, u32 attr,
|
||||
__always_unused int channel, long *val)
|
||||
{
|
||||
struct gpd_fan_data *data = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
if (type == hwmon_fan) {
|
||||
if (attr == hwmon_fan_input) {
|
||||
ret = gpd_read_rpm();
|
||||
ret = gpd_read_rpm(data);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@@ -524,10 +528,10 @@ static int gpd_fan_hwmon_read(__always_unused struct device *dev,
|
||||
} else if (type == hwmon_pwm) {
|
||||
switch (attr) {
|
||||
case hwmon_pwm_enable:
|
||||
*val = gpd_driver_priv.pwm_enable;
|
||||
*val = data->pwm_enable;
|
||||
return 0;
|
||||
case hwmon_pwm_input:
|
||||
ret = gpd_read_pwm();
|
||||
ret = gpd_read_pwm(data);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@@ -540,27 +544,29 @@ static int gpd_fan_hwmon_read(__always_unused struct device *dev,
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static int gpd_fan_hwmon_write(__always_unused struct device *dev,
|
||||
static int gpd_fan_hwmon_write(struct device *dev,
|
||||
enum hwmon_sensor_types type, u32 attr,
|
||||
__always_unused int channel, long val)
|
||||
{
|
||||
struct gpd_fan_data *data = dev_get_drvdata(dev);
|
||||
|
||||
if (type == hwmon_pwm) {
|
||||
switch (attr) {
|
||||
case hwmon_pwm_enable:
|
||||
if (!in_range(val, 0, 3))
|
||||
return -EINVAL;
|
||||
|
||||
gpd_driver_priv.pwm_enable = val;
|
||||
data->pwm_enable = val;
|
||||
|
||||
gpd_set_pwm_enable(gpd_driver_priv.pwm_enable);
|
||||
gpd_set_pwm_enable(data, data->pwm_enable);
|
||||
return 0;
|
||||
case hwmon_pwm_input:
|
||||
if (!in_range(val, 0, 256))
|
||||
return -EINVAL;
|
||||
|
||||
gpd_driver_priv.pwm_value = val;
|
||||
data->pwm_value = val;
|
||||
|
||||
return gpd_write_pwm(val);
|
||||
return gpd_write_pwm(data, val);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -584,26 +590,37 @@ static struct hwmon_chip_info gpd_fan_chip_info = {
|
||||
.info = gpd_fan_hwmon_channel_info
|
||||
};
|
||||
|
||||
static void gpd_win4_init_ec(void)
|
||||
static void gpd_win4_init_ec(struct gpd_fan_data *data)
|
||||
{
|
||||
const struct gpd_fan_drvdata *drvdata = data->drvdata;
|
||||
u8 chip_id, chip_ver;
|
||||
|
||||
gpd_ecram_read(0x2000, &chip_id);
|
||||
gpd_ecram_read(drvdata, 0x2000, &chip_id);
|
||||
|
||||
if (chip_id == 0x55) {
|
||||
gpd_ecram_read(0x1060, &chip_ver);
|
||||
gpd_ecram_write(0x1060, chip_ver | 0x80);
|
||||
gpd_ecram_read(drvdata, 0x1060, &chip_ver);
|
||||
gpd_ecram_write(drvdata, 0x1060, chip_ver | 0x80);
|
||||
}
|
||||
}
|
||||
|
||||
static void gpd_init_ec(void)
|
||||
static void gpd_init_ec(struct gpd_fan_data *data)
|
||||
{
|
||||
// The buggy firmware won't initialize EC properly on boot.
|
||||
// Before its initialization, reading RPM will always return 0,
|
||||
// and writing PWM will have no effect.
|
||||
// Initialize it manually on driver load.
|
||||
if (gpd_driver_priv.drvdata->board == win4_6800u)
|
||||
gpd_win4_init_ec();
|
||||
if (data->drvdata->board == win4_6800u)
|
||||
gpd_win4_init_ec(data);
|
||||
}
|
||||
|
||||
static void gpd_fan_reset_hardware(void *pdata)
|
||||
{
|
||||
struct gpd_fan_data *data = pdata;
|
||||
|
||||
if (data) {
|
||||
data->pwm_enable = AUTOMATIC;
|
||||
gpd_set_pwm_enable(data, AUTOMATIC);
|
||||
}
|
||||
}
|
||||
|
||||
static int gpd_fan_probe(struct platform_device *pdev)
|
||||
@@ -611,7 +628,10 @@ static int gpd_fan_probe(struct platform_device *pdev)
|
||||
struct device *dev = &pdev->dev;
|
||||
const struct resource *region;
|
||||
const struct resource *res;
|
||||
const struct device *hwdev;
|
||||
struct device *hwdev;
|
||||
struct gpd_fan_data *data;
|
||||
const struct gpd_fan_drvdata *match;
|
||||
int ret;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
|
||||
if (!res)
|
||||
@@ -624,29 +644,39 @@ static int gpd_fan_probe(struct platform_device *pdev)
|
||||
return dev_err_probe(dev, -EBUSY,
|
||||
"Failed to request region\n");
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
match = dev_get_platdata(dev);
|
||||
if (!match)
|
||||
return -EINVAL;
|
||||
|
||||
data->drvdata = match;
|
||||
data->pwm_enable = AUTOMATIC;
|
||||
data->pwm_value = 255;
|
||||
|
||||
dev_set_drvdata(dev, data);
|
||||
|
||||
gpd_init_ec(data);
|
||||
|
||||
ret = devm_add_action_or_reset(dev, gpd_fan_reset_hardware, data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
hwdev = devm_hwmon_device_register_with_info(dev,
|
||||
DRIVER_NAME,
|
||||
NULL,
|
||||
data,
|
||||
&gpd_fan_chip_info,
|
||||
NULL);
|
||||
if (IS_ERR(hwdev))
|
||||
return dev_err_probe(dev, PTR_ERR(hwdev),
|
||||
"Failed to register hwmon device\n");
|
||||
|
||||
gpd_init_ec();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gpd_fan_remove(__always_unused struct platform_device *pdev)
|
||||
{
|
||||
gpd_driver_priv.pwm_enable = AUTOMATIC;
|
||||
gpd_set_pwm_enable(AUTOMATIC);
|
||||
}
|
||||
|
||||
static struct platform_driver gpd_fan_driver = {
|
||||
.probe = gpd_fan_probe,
|
||||
.remove = gpd_fan_remove,
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
},
|
||||
@@ -668,6 +698,7 @@ static int __init gpd_fan_init(void)
|
||||
if (!match) {
|
||||
const struct dmi_system_id *dmi_match =
|
||||
dmi_first_match(dmi_table);
|
||||
|
||||
if (dmi_match)
|
||||
match = dmi_match->driver_data;
|
||||
}
|
||||
@@ -675,10 +706,6 @@ static int __init gpd_fan_init(void)
|
||||
if (!match)
|
||||
return -ENODEV;
|
||||
|
||||
gpd_driver_priv.pwm_enable = AUTOMATIC;
|
||||
gpd_driver_priv.pwm_value = 255;
|
||||
gpd_driver_priv.drvdata = match;
|
||||
|
||||
struct resource gpd_fan_resources[] = {
|
||||
{
|
||||
.start = match->addr_port,
|
||||
@@ -690,10 +717,11 @@ static int __init gpd_fan_init(void)
|
||||
gpd_fan_platform_device = platform_create_bundle(&gpd_fan_driver,
|
||||
gpd_fan_probe,
|
||||
gpd_fan_resources,
|
||||
1, NULL, 0);
|
||||
1,
|
||||
match, sizeof(*match));
|
||||
|
||||
if (IS_ERR(gpd_fan_platform_device)) {
|
||||
pr_warn("Failed to create platform device\n");
|
||||
pr_err("Failed to create platform device\n");
|
||||
return PTR_ERR(gpd_fan_platform_device);
|
||||
}
|
||||
|
||||
|
||||
@@ -233,7 +233,7 @@ static int hih6130_probe(struct i2c_client *client)
|
||||
|
||||
/* Device ID table */
|
||||
static const struct i2c_device_id hih6130_id[] = {
|
||||
{ "hih6130" },
|
||||
{ .name = "hih6130" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, hih6130_id);
|
||||
|
||||
@@ -169,8 +169,8 @@ static const struct hwmon_chip_info hs3001_chip_info = {
|
||||
|
||||
/* device ID table */
|
||||
static const struct i2c_device_id hs3001_ids[] = {
|
||||
{ "hs3001" },
|
||||
{ },
|
||||
{ .name = "hs3001" },
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, hs3001_ids);
|
||||
|
||||
@@ -322,7 +322,7 @@ static int htu31_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id htu31_id[] = {
|
||||
{ "htu31" },
|
||||
{ .name = "htu31" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, htu31_id);
|
||||
|
||||
@@ -573,6 +573,7 @@ static const char * const hwmon_chip_attrs[] = {
|
||||
[hwmon_chip_curr_reset_history] = "curr_reset_history",
|
||||
[hwmon_chip_power_reset_history] = "power_reset_history",
|
||||
[hwmon_chip_update_interval] = "update_interval",
|
||||
[hwmon_chip_update_interval_us] = "update_interval_us",
|
||||
[hwmon_chip_alarms] = "alarms",
|
||||
[hwmon_chip_samples] = "samples",
|
||||
[hwmon_chip_curr_samples] = "curr_samples",
|
||||
|
||||
@@ -569,7 +569,7 @@ static void ina209_remove(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ina209_id[] = {
|
||||
{ "ina209" },
|
||||
{ .name = "ina209" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ina209_id);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/util_macros.h>
|
||||
|
||||
/* INA238 register definitions */
|
||||
#define INA238_CONFIG 0x0
|
||||
@@ -49,6 +50,17 @@
|
||||
#define INA238_DIAG_ALERT_BUSUL BIT(3)
|
||||
#define INA238_DIAG_ALERT_POL BIT(2)
|
||||
|
||||
/* INA238_ADC_CONFIG register field masks and shifts */
|
||||
#define INA238_ADC_CONFIG_MODE_MASK GENMASK(15, 12)
|
||||
#define INA238_ADC_CONFIG_VBUSCT_SHIFT 9
|
||||
#define INA238_ADC_CONFIG_VBUSCT_MASK GENMASK(11, 9)
|
||||
#define INA238_ADC_CONFIG_VSHCT_SHIFT 6
|
||||
#define INA238_ADC_CONFIG_VSHCT_MASK GENMASK(8, 6)
|
||||
#define INA238_ADC_CONFIG_VTCT_SHIFT 3
|
||||
#define INA238_ADC_CONFIG_VTCT_MASK GENMASK(5, 3)
|
||||
#define INA238_ADC_CONFIG_AVG_SHIFT 0
|
||||
#define INA238_ADC_CONFIG_AVG_MASK GENMASK(2, 0)
|
||||
|
||||
#define INA238_REGISTERS 0x20
|
||||
|
||||
#define INA238_RSHUNT_DEFAULT 2500 /* uOhm */
|
||||
@@ -101,6 +113,21 @@ static const struct regmap_config ina238_regmap_config = {
|
||||
.val_bits = 16,
|
||||
};
|
||||
|
||||
/* Lookup table for conversion times in usec for INA238 family */
|
||||
static const u16 ina238_conv_time[] = {
|
||||
50, 84, 150, 280, 540, 1052, 2074, 4120,
|
||||
};
|
||||
|
||||
/* Lookup table for conversion times in usec for SQ52206 */
|
||||
static const u16 sq52206_conv_time[] = {
|
||||
66, 118, 310, 566, 1070, 2090, 4140, 8230,
|
||||
};
|
||||
|
||||
/* Lookup table for number of samples used in averaging mode */
|
||||
static const int ina238_avg_samples[] = {
|
||||
1, 4, 16, 64, 128, 256, 512, 1024,
|
||||
};
|
||||
|
||||
enum ina238_ids { ina228, ina237, ina238, ina700, ina780, sq52206 };
|
||||
|
||||
struct ina238_config {
|
||||
@@ -112,6 +139,7 @@ struct ina238_config {
|
||||
u32 power_calculate_factor; /* fixed parameter for power calculation, from datasheet */
|
||||
u32 bus_voltage_lsb; /* bus voltage LSB, in nV */
|
||||
int current_lsb; /* current LSB, in uA */
|
||||
const u16 *conv_time; /* conversion time lookup table */
|
||||
};
|
||||
|
||||
struct ina238_data {
|
||||
@@ -124,6 +152,7 @@ struct ina238_data {
|
||||
int current_lsb; /* current LSB, in uA */
|
||||
int power_lsb; /* power LSB, in uW */
|
||||
int energy_lsb; /* energy LSB, in uJ */
|
||||
u16 adc_config; /* cached ADC_CONFIG register value */
|
||||
};
|
||||
|
||||
static const struct ina238_config ina238_config[] = {
|
||||
@@ -135,6 +164,7 @@ static const struct ina238_config ina238_config[] = {
|
||||
.config_default = INA238_CONFIG_DEFAULT,
|
||||
.bus_voltage_lsb = INA238_BUS_VOLTAGE_LSB,
|
||||
.temp_resolution = 16,
|
||||
.conv_time = ina238_conv_time,
|
||||
},
|
||||
[ina237] = {
|
||||
.has_20bit_voltage_current = false,
|
||||
@@ -144,6 +174,7 @@ static const struct ina238_config ina238_config[] = {
|
||||
.config_default = INA238_CONFIG_DEFAULT,
|
||||
.bus_voltage_lsb = INA238_BUS_VOLTAGE_LSB,
|
||||
.temp_resolution = 12,
|
||||
.conv_time = ina238_conv_time,
|
||||
},
|
||||
[ina238] = {
|
||||
.has_20bit_voltage_current = false,
|
||||
@@ -153,6 +184,7 @@ static const struct ina238_config ina238_config[] = {
|
||||
.config_default = INA238_CONFIG_DEFAULT,
|
||||
.bus_voltage_lsb = INA238_BUS_VOLTAGE_LSB,
|
||||
.temp_resolution = 12,
|
||||
.conv_time = ina238_conv_time,
|
||||
},
|
||||
[ina700] = {
|
||||
.has_20bit_voltage_current = false,
|
||||
@@ -163,6 +195,7 @@ static const struct ina238_config ina238_config[] = {
|
||||
.bus_voltage_lsb = INA238_BUS_VOLTAGE_LSB,
|
||||
.temp_resolution = 12,
|
||||
.current_lsb = 480,
|
||||
.conv_time = ina238_conv_time,
|
||||
},
|
||||
[ina780] = {
|
||||
.has_20bit_voltage_current = false,
|
||||
@@ -173,6 +206,7 @@ static const struct ina238_config ina238_config[] = {
|
||||
.bus_voltage_lsb = INA238_BUS_VOLTAGE_LSB,
|
||||
.temp_resolution = 12,
|
||||
.current_lsb = 2400,
|
||||
.conv_time = ina238_conv_time,
|
||||
},
|
||||
[sq52206] = {
|
||||
.has_20bit_voltage_current = false,
|
||||
@@ -182,6 +216,7 @@ static const struct ina238_config ina238_config[] = {
|
||||
.config_default = SQ52206_CONFIG_DEFAULT,
|
||||
.bus_voltage_lsb = SQ52206_BUS_VOLTAGE_LSB,
|
||||
.temp_resolution = 16,
|
||||
.conv_time = sq52206_conv_time,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -261,6 +296,111 @@ static int ina228_read_voltage(struct ina238_data *data, int channel, long *val)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Converting ADC_CONFIG register value to update_interval in usec */
|
||||
static inline u32 ina238_reg_to_interval_us(struct ina238_data *data)
|
||||
{
|
||||
const u16 *ct = data->config->conv_time;
|
||||
u32 vbusct = ct[(data->adc_config & INA238_ADC_CONFIG_VBUSCT_MASK) >>
|
||||
INA238_ADC_CONFIG_VBUSCT_SHIFT];
|
||||
u32 vshct = ct[(data->adc_config & INA238_ADC_CONFIG_VSHCT_MASK) >>
|
||||
INA238_ADC_CONFIG_VSHCT_SHIFT];
|
||||
u32 vtct = ct[(data->adc_config & INA238_ADC_CONFIG_VTCT_MASK) >>
|
||||
INA238_ADC_CONFIG_VTCT_SHIFT];
|
||||
|
||||
return vbusct + vshct + vtct;
|
||||
}
|
||||
|
||||
static inline u32 ina238_samples(struct ina238_data *data)
|
||||
{
|
||||
return ina238_avg_samples[(data->adc_config & INA238_ADC_CONFIG_AVG_MASK) >>
|
||||
INA238_ADC_CONFIG_AVG_SHIFT];
|
||||
}
|
||||
|
||||
/* Converting update_interval(_us) to a per-field conversion time in usec.
|
||||
* interval_us is the total ADC cycle time including averaging in microseconds.
|
||||
* All three conversion fields (VBUSCT, VSHCT, VTCT) are set equal, so the
|
||||
* per-field time is interval_us / (samples * 3).
|
||||
*/
|
||||
static inline u32 ina238_interval_us_to_conv_time(u32 interval_us, u32 samples)
|
||||
{
|
||||
return DIV_ROUND_CLOSEST_ULL(interval_us, samples * 3);
|
||||
}
|
||||
|
||||
/* Write a per-field conversion time (in usec) to the ADC_CONFIG register */
|
||||
static int ina238_write_conv_time(struct ina238_data *data, u32 conv_time_us)
|
||||
{
|
||||
u16 adc_config;
|
||||
int idx, ret;
|
||||
|
||||
idx = find_closest(conv_time_us, data->config->conv_time,
|
||||
ARRAY_SIZE(ina238_conv_time));
|
||||
adc_config = (data->adc_config &
|
||||
~(INA238_ADC_CONFIG_VBUSCT_MASK |
|
||||
INA238_ADC_CONFIG_VSHCT_MASK |
|
||||
INA238_ADC_CONFIG_VTCT_MASK)) |
|
||||
((u16)idx << INA238_ADC_CONFIG_VBUSCT_SHIFT) |
|
||||
((u16)idx << INA238_ADC_CONFIG_VSHCT_SHIFT) |
|
||||
((u16)idx << INA238_ADC_CONFIG_VTCT_SHIFT);
|
||||
ret = regmap_write(data->regmap, INA238_ADC_CONFIG, adc_config);
|
||||
if (ret)
|
||||
return ret;
|
||||
data->adc_config = adc_config;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ina238_read_chip(struct device *dev, u32 attr, long *val)
|
||||
{
|
||||
struct ina238_data *data = dev_get_drvdata(dev);
|
||||
|
||||
switch (attr) {
|
||||
case hwmon_chip_samples:
|
||||
*val = ina238_samples(data);
|
||||
return 0;
|
||||
case hwmon_chip_update_interval:
|
||||
/* Return in msec */
|
||||
*val = DIV_ROUND_CLOSEST(ina238_reg_to_interval_us(data) *
|
||||
ina238_samples(data), 1000);
|
||||
return 0;
|
||||
case hwmon_chip_update_interval_us:
|
||||
/* Return in usec */
|
||||
*val = ina238_reg_to_interval_us(data) * ina238_samples(data);
|
||||
return 0;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
static int ina238_write_chip(struct device *dev, u32 attr, long val)
|
||||
{
|
||||
struct ina238_data *data = dev_get_drvdata(dev);
|
||||
u16 adc_config;
|
||||
int idx, ret;
|
||||
|
||||
switch (attr) {
|
||||
case hwmon_chip_samples:
|
||||
idx = find_closest(val, ina238_avg_samples,
|
||||
ARRAY_SIZE(ina238_avg_samples));
|
||||
adc_config = (data->adc_config & ~INA238_ADC_CONFIG_AVG_MASK) |
|
||||
(idx << INA238_ADC_CONFIG_AVG_SHIFT);
|
||||
ret = regmap_write(data->regmap, INA238_ADC_CONFIG, adc_config);
|
||||
if (ret)
|
||||
return ret;
|
||||
data->adc_config = adc_config;
|
||||
return 0;
|
||||
case hwmon_chip_update_interval:
|
||||
/* Convert ms to us before passing to the shared helper */
|
||||
val = clamp_val(val, 0, INT_MAX / 1000) * 1000;
|
||||
return ina238_write_conv_time(data,
|
||||
ina238_interval_us_to_conv_time((u32)val, ina238_samples(data)));
|
||||
case hwmon_chip_update_interval_us:
|
||||
val = clamp_val(val, 0, INT_MAX);
|
||||
return ina238_write_conv_time(data,
|
||||
ina238_interval_us_to_conv_time((u32)val, ina238_samples(data)));
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
static int ina238_read_in(struct device *dev, u32 attr, int channel,
|
||||
long *val)
|
||||
{
|
||||
@@ -587,6 +727,8 @@ static int ina238_read(struct device *dev, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel, long *val)
|
||||
{
|
||||
switch (type) {
|
||||
case hwmon_chip:
|
||||
return ina238_read_chip(dev, attr, val);
|
||||
case hwmon_in:
|
||||
return ina238_read_in(dev, attr, channel, val);
|
||||
case hwmon_curr:
|
||||
@@ -607,6 +749,8 @@ static int ina238_write(struct device *dev, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel, long val)
|
||||
{
|
||||
switch (type) {
|
||||
case hwmon_chip:
|
||||
return ina238_write_chip(dev, attr, val);
|
||||
case hwmon_in:
|
||||
return ina238_write_in(dev, attr, channel, val);
|
||||
case hwmon_curr:
|
||||
@@ -629,6 +773,15 @@ static umode_t ina238_is_visible(const void *drvdata,
|
||||
bool has_energy = data->config->has_energy;
|
||||
|
||||
switch (type) {
|
||||
case hwmon_chip:
|
||||
switch (attr) {
|
||||
case hwmon_chip_samples:
|
||||
case hwmon_chip_update_interval:
|
||||
case hwmon_chip_update_interval_us:
|
||||
return 0644;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
case hwmon_in:
|
||||
switch (attr) {
|
||||
case hwmon_in_input:
|
||||
@@ -692,6 +845,9 @@ static umode_t ina238_is_visible(const void *drvdata,
|
||||
HWMON_I_MIN | HWMON_I_MIN_ALARM)
|
||||
|
||||
static const struct hwmon_channel_info * const ina238_info[] = {
|
||||
HWMON_CHANNEL_INFO(chip,
|
||||
HWMON_C_SAMPLES | HWMON_C_UPDATE_INTERVAL |
|
||||
HWMON_C_UPDATE_INTERVAL_US),
|
||||
HWMON_CHANNEL_INFO(in,
|
||||
/* 0: shunt voltage */
|
||||
INA238_HWMON_IN_CONFIG,
|
||||
@@ -798,8 +954,8 @@ static int ina238_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
/* Setup ADC_CONFIG register */
|
||||
ret = regmap_write(data->regmap, INA238_ADC_CONFIG,
|
||||
INA238_ADC_CONFIG_DEFAULT);
|
||||
data->adc_config = INA238_ADC_CONFIG_DEFAULT;
|
||||
ret = regmap_write(data->regmap, INA238_ADC_CONFIG, data->adc_config);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "error configuring the device: %d\n", ret);
|
||||
return -ENODEV;
|
||||
@@ -837,12 +993,12 @@ static int ina238_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ina238_id[] = {
|
||||
{ "ina228", ina228 },
|
||||
{ "ina237", ina237 },
|
||||
{ "ina238", ina238 },
|
||||
{ "ina700", ina700 },
|
||||
{ "ina780", ina780 },
|
||||
{ "sq52206", sq52206 },
|
||||
{ .name = "ina228", .driver_data = ina228 },
|
||||
{ .name = "ina237", .driver_data = ina237 },
|
||||
{ .name = "ina238", .driver_data = ina238 },
|
||||
{ .name = "ina700", .driver_data = ina700 },
|
||||
{ .name = "ina780", .driver_data = ina780 },
|
||||
{ .name = "sq52206", .driver_data = sq52206 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ina238_id);
|
||||
|
||||
@@ -875,11 +875,11 @@ static ssize_t shunt_resistor_store(struct device *dev,
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
hwmon_lock(dev);
|
||||
status = ina2xx_set_shunt(data, val);
|
||||
hwmon_unlock(dev);
|
||||
if (status < 0)
|
||||
return status;
|
||||
scoped_guard(hwmon_lock, dev) {
|
||||
status = ina2xx_set_shunt(data, val);
|
||||
if (status < 0)
|
||||
return status;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -1000,14 +1000,14 @@ static int ina2xx_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ina2xx_id[] = {
|
||||
{ "ina219", ina219 },
|
||||
{ "ina220", ina219 },
|
||||
{ "ina226", ina226 },
|
||||
{ "ina230", ina226 },
|
||||
{ "ina231", ina226 },
|
||||
{ "ina234", ina234 },
|
||||
{ "ina260", ina260 },
|
||||
{ "sy24655", sy24655 },
|
||||
{ .name = "ina219", .driver_data = ina219 },
|
||||
{ .name = "ina220", .driver_data = ina219 },
|
||||
{ .name = "ina226", .driver_data = ina226 },
|
||||
{ .name = "ina230", .driver_data = ina226 },
|
||||
{ .name = "ina231", .driver_data = ina226 },
|
||||
{ .name = "ina234", .driver_data = ina234 },
|
||||
{ .name = "ina260", .driver_data = ina260 },
|
||||
{ .name = "sy24655", .driver_data = sy24655 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ina2xx_id);
|
||||
|
||||
@@ -1002,7 +1002,7 @@ static const struct of_device_id ina3221_of_match_table[] = {
|
||||
MODULE_DEVICE_TABLE(of, ina3221_of_match_table);
|
||||
|
||||
static const struct i2c_device_id ina3221_ids[] = {
|
||||
{ "ina3221" },
|
||||
{ .name = "ina3221" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ina3221_ids);
|
||||
|
||||
@@ -773,6 +773,7 @@ static const struct platform_device_id intel_m10bmc_hwmon_ids[] = {
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, intel_m10bmc_hwmon_ids);
|
||||
|
||||
static struct platform_driver intel_m10bmc_hwmon_driver = {
|
||||
.probe = m10bmc_hwmon_probe,
|
||||
@@ -783,7 +784,6 @@ static struct platform_driver intel_m10bmc_hwmon_driver = {
|
||||
};
|
||||
module_platform_driver(intel_m10bmc_hwmon_driver);
|
||||
|
||||
MODULE_DEVICE_TABLE(platform, intel_m10bmc_hwmon_ids);
|
||||
MODULE_AUTHOR("Intel Corporation");
|
||||
MODULE_DESCRIPTION("Intel MAX 10 BMC hardware monitor");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
@@ -475,7 +475,7 @@ static int isl28022_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id isl28022_ids[] = {
|
||||
{ "isl28022" },
|
||||
{ .name = "isl28022" },
|
||||
{ /* LIST END */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, isl28022_ids);
|
||||
|
||||
@@ -1412,6 +1412,9 @@ static ssize_t set_fan(struct device *dev, struct device_attribute *attr,
|
||||
if (kstrtol(buf, 10, &val) < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (val < 0)
|
||||
val = 0;
|
||||
|
||||
err = it87_lock(data);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -579,7 +579,7 @@ static const struct dev_pm_ops jc42_dev_pm_ops = {
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static const struct i2c_device_id jc42_id[] = {
|
||||
{ "jc42" },
|
||||
{ .name = "jc42" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, jc42_id);
|
||||
|
||||
@@ -502,8 +502,8 @@ static int pem_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id pem_id[] = {
|
||||
{"lineage_pem"},
|
||||
{}
|
||||
{ .name = "lineage_pem" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, pem_id);
|
||||
|
||||
|
||||
@@ -92,6 +92,9 @@ static const unsigned short normal_i2c[] = { 0x18, 0x4c, 0x4e, I2C_CLIENT_END };
|
||||
#define LM96163_REG_REMOTE_TEMP_U_LSB 0x32
|
||||
#define LM96163_REG_CONFIG_ENHANCED 0x45
|
||||
|
||||
#define LM63_PWM_BASE_FAST_HZ 180000
|
||||
#define LM63_PWM_BASE_SLOW_HZ 700
|
||||
|
||||
#define LM63_MAX_CONVRATE 9
|
||||
|
||||
#define LM63_MAX_CONVRATE_HZ 32
|
||||
@@ -455,6 +458,91 @@ static ssize_t pwm1_enable_store(struct device *dev,
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t pwm1_freq_show(struct device *dev,
|
||||
struct device_attribute *dummy, char *buf)
|
||||
{
|
||||
struct lm63_data *data = lm63_update_device(dev);
|
||||
unsigned int base, freq;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
base = (data->config_fan & 0x08) ?
|
||||
LM63_PWM_BASE_SLOW_HZ : LM63_PWM_BASE_FAST_HZ;
|
||||
freq = data->pwm1_freq;
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return sprintf(buf, "%u\n", base / freq);
|
||||
}
|
||||
|
||||
/*
|
||||
* Pick the closest CONFIG_FAN.SCS + PFR for the requested frequency.
|
||||
* PWM_FREQ writes need the LUT unlocked, same as set_pwm1(). LUT PWM
|
||||
* bytes are register-relative; rewrite them after a frequency change
|
||||
* if duty cycles must be preserved.
|
||||
*/
|
||||
static ssize_t pwm1_freq_store(struct device *dev,
|
||||
struct device_attribute *dummy,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct lm63_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
unsigned long val, pfr_fast, pfr_slow, err_fast, err_slow, pfr;
|
||||
bool slow_clock;
|
||||
int ret;
|
||||
|
||||
ret = kstrtoul(buf, 10, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (val == 0)
|
||||
return -EINVAL;
|
||||
|
||||
pfr_fast = clamp_val(DIV_ROUND_CLOSEST((unsigned long)LM63_PWM_BASE_FAST_HZ, val),
|
||||
1UL, 31UL);
|
||||
pfr_slow = clamp_val(DIV_ROUND_CLOSEST((unsigned long)LM63_PWM_BASE_SLOW_HZ, val),
|
||||
1UL, 31UL);
|
||||
err_fast = abs_diff(LM63_PWM_BASE_FAST_HZ / pfr_fast, val);
|
||||
err_slow = abs_diff(LM63_PWM_BASE_SLOW_HZ / pfr_slow, val);
|
||||
|
||||
if (err_slow < err_fast) {
|
||||
slow_clock = true;
|
||||
pfr = pfr_slow;
|
||||
} else {
|
||||
slow_clock = false;
|
||||
pfr = pfr_fast;
|
||||
}
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
ret = i2c_smbus_read_byte_data(client, LM63_REG_CONFIG_FAN);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&data->update_lock);
|
||||
return ret;
|
||||
}
|
||||
data->config_fan = ret;
|
||||
|
||||
if (!(data->config_fan & 0x20)) { /* register is read-only */
|
||||
mutex_unlock(&data->update_lock);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
if (data->kind == lm96163) {
|
||||
ret = i2c_smbus_read_byte_data(client, LM96163_REG_CONFIG_ENHANCED);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&data->update_lock);
|
||||
return ret;
|
||||
}
|
||||
data->pwm_highres = !slow_clock && pfr == 8 && (ret & 0x10);
|
||||
}
|
||||
|
||||
if (slow_clock)
|
||||
data->config_fan |= 0x08;
|
||||
else
|
||||
data->config_fan &= ~0x08;
|
||||
i2c_smbus_write_byte_data(client, LM63_REG_CONFIG_FAN, data->config_fan);
|
||||
i2c_smbus_write_byte_data(client, LM63_REG_PWM_FREQ, pfr);
|
||||
data->pwm1_freq = pfr;
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* There are 8bit registers for both local(temp1) and remote(temp2) sensor.
|
||||
* For remote sensor registers temp2_offset has to be considered,
|
||||
@@ -629,6 +717,47 @@ static ssize_t show_lut_temp_hyst(struct device *dev,
|
||||
return sprintf(buf, "%d\n", temp);
|
||||
}
|
||||
|
||||
/*
|
||||
* The LM63 has a single hysteresis register shared by all LUT entries.
|
||||
* Expose it as a chip-wide hysteresis amount in millidegrees; the
|
||||
* per-point pwm1_auto_pointN_temp_hyst attributes remain read-only and
|
||||
* show the resulting absolute trip-down temperature for each entry.
|
||||
*/
|
||||
static ssize_t pwm1_auto_point_temp_hyst_show(struct device *dev,
|
||||
struct device_attribute *dummy,
|
||||
char *buf)
|
||||
{
|
||||
struct lm63_data *data = lm63_update_device(dev);
|
||||
u8 hyst;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
hyst = data->lut_temp_hyst;
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return sprintf(buf, "%d\n", TEMP8_FROM_REG(hyst));
|
||||
}
|
||||
|
||||
static ssize_t pwm1_auto_point_temp_hyst_store(struct device *dev,
|
||||
struct device_attribute *dummy,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct lm63_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
unsigned long val;
|
||||
int err;
|
||||
|
||||
err = kstrtoul(buf, 10, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->lut_temp_hyst = HYST_TO_REG(val);
|
||||
i2c_smbus_write_byte_data(client, LM63_REG_LUT_TEMP_HYST,
|
||||
data->lut_temp_hyst);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* And now the other way around, user-space provides an absolute
|
||||
* hysteresis value and we have to store a relative one
|
||||
@@ -764,6 +893,8 @@ static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan,
|
||||
|
||||
static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm1, set_pwm1, 0);
|
||||
static DEVICE_ATTR_RW(pwm1_enable);
|
||||
static DEVICE_ATTR_RW(pwm1_freq);
|
||||
static DEVICE_ATTR_RW(pwm1_auto_point_temp_hyst);
|
||||
static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IWUSR | S_IRUGO,
|
||||
show_pwm1, set_pwm1, 1);
|
||||
static SENSOR_DEVICE_ATTR(pwm1_auto_point1_temp, S_IWUSR | S_IRUGO,
|
||||
@@ -869,6 +1000,8 @@ static DEVICE_ATTR_RW(update_interval);
|
||||
static struct attribute *lm63_attributes[] = {
|
||||
&sensor_dev_attr_pwm1.dev_attr.attr,
|
||||
&dev_attr_pwm1_enable.attr,
|
||||
&dev_attr_pwm1_freq.attr,
|
||||
&dev_attr_pwm1_auto_point_temp_hyst.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point1_temp.dev_attr.attr,
|
||||
&sensor_dev_attr_pwm1_auto_point1_temp_hyst.dev_attr.attr,
|
||||
@@ -1152,9 +1285,9 @@ static int lm63_probe(struct i2c_client *client)
|
||||
*/
|
||||
|
||||
static const struct i2c_device_id lm63_id[] = {
|
||||
{ "lm63", lm63 },
|
||||
{ "lm64", lm64 },
|
||||
{ "lm96163", lm96163 },
|
||||
{ .name = "lm63", .driver_data = lm63 },
|
||||
{ .name = "lm64", .driver_data = lm64 },
|
||||
{ .name = "lm96163", .driver_data = lm96163 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, lm63_id);
|
||||
|
||||
@@ -220,7 +220,7 @@ lm73_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id lm73_ids[] = {
|
||||
{ "lm73" },
|
||||
{ .name = "lm73" },
|
||||
{ /* LIST END */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, lm73_ids);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user