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:
Linus Torvalds
2026-06-16 08:10:21 +05:30
260 changed files with 8132 additions and 1093 deletions

View File

@@ -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.

View 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>;
};
};
...

View File

@@ -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

View 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";
};
};
};

View File

@@ -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>;
};

View File

@@ -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>;
};

View File

@@ -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>;
};
};
...

View File

@@ -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>;
};
};

View File

@@ -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>;

View File

@@ -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>;
};

View File

@@ -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

View File

@@ -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,.*":

View File

@@ -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.

View 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).
================ ==============================================================

View File

@@ -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

View File

@@ -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.
================= ========================================================

View 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
======================= ======================================================

View 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.
======================= =======================================================

View 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.

View File

@@ -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
=================== =========================================

View File

@@ -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()
--------------------------------------------

View File

@@ -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)
======================= =======================================================

View File

@@ -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

View File

@@ -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
=============================== ======= ===========================================

View 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.
======================= ==========================================

View 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
======================= ======================================================

View 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
================= ========================================

View 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)
================= ========================================

View File

@@ -1,4 +1,4 @@
Kernel driver max31790
Kernel driver max31730
======================
Supported chips:

View File

@@ -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::

View 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**

View File

@@ -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
-------------------------------

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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
View 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");

View File

@@ -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

View File

@@ -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

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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,

View File

@@ -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);

View File

@@ -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,

View File

@@ -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);

View File

@@ -1083,7 +1083,7 @@ static int amc6821_probe(struct i2c_client *client)
}
static const struct i2c_device_id amc6821_id[] = {
{ "amc6821" },
{ .name = "amc6821" },
{ }
};

View File

@@ -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);

View 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 Adelayed beyond ARCTIC_ACK_TIMEOUT_MSarrives 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");

View File

@@ -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);

View File

@@ -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);

View File

@@ -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",

View File

@@ -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);

View File

@@ -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);

View File

@@ -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),
{}
};

View File

@@ -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");

View File

@@ -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 = {

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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
View 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], &regvalh);
if (ret < 0)
return ret;
if (ch) {
ret = regmap_read(data->regmap, emc1812_limit_regs_low[ch][type], &regvall);
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", &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");

View File

@@ -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);

View File

@@ -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,
};

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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",

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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");

View File

@@ -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);

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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