hwmon updates for v6.15

- New drivers
 
   * Driver for HTU31
 
   * Congatec Board Controller monitoring driver
 
   * Driver for TI INA233 Current and Power Monitor
 
 - Support for additional chips or boards in existing drivers
 
   * pmbus/ltc2978: Add support for LT717x and LTM4673
 
   * asus-ec-sensors: Add PRIME X670E-PRO WIFI
 
   * k10temp: Add support for cyan skillfish
 
   * nct6683: Add customer ID for AMD BC-250
 
   * lm90: Add support for NCT7716, NCT7717 and NCT7718
 
 - Other notable improvements in existing drivers
 
   * emc2305: Add devicetree support, and use
     devm_thermal_of_cooling_device_register
 
   * acpi_power_meter: Convert to with_info API
 
   * dell-smm: Increase the number of fans
 
   * pmbus/core: Optimize debugfs support and use i2c_client debugfs
     directory
 
    * hwmon core: Fix the missing of 'average' word in
      hwmon_power_attr_templates
 
    * Various drivers: Use per-client debugfs entry provided by I2C
      subsystem
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEiHPvMQj9QTOCiqgVyx8mb86fmYEFAmfh5YsACgkQyx8mb86f
 mYG/AA//Z8b43kMxImlQjw4SXFyp3lry2OoLBxJADDZk/gHAbGBtrzYv5FFrHs/c
 0YUNP2yGApQnl7iwnUTgmayCafsq+BO1TXLHrn9VwWsefDHNBUZO8PbdZOlmlJgG
 LIxeIBPgHhuNoLthrZIEzGzDqd2xfNURXnox1J2/uHE6ggor5ceXXcAjxeZtxh45
 WP9rwaypC189qmMyLnHGIqGKsiTjFUkri6miAM0UkaRnrR8ELeTCtGeXWA+54b9+
 uOq7VyLYfIV7CffxwFuPfIMXRs/0kye0Y5GMzRPLPXZuY//A1QcVW6OcN6/+8Rgm
 qxsp0uWfwuUYi8Tek+DaX9+vKPo9IoUOvYjKlQt6J55FdBnkvnL6uW430lXp/9O3
 JQnRGLXV0GEdf8aSEya9lfAF7kPl6K9HRlqYIG/DpSoLDqkvn1ArGiRzkUEGRFHc
 pyTwUalhyqxsL9mELuVscN4g4t+MqXld43o/YEqfU8bDsVGztStjJFtrd+XSroKH
 FcvZldoVz6H99n22U45YHzA5BqDn53vP67uqM180mx4yCKDJIEAAduD48TFNJExV
 l9QgPZPZuxch8saFn7IeFmRNNeF7Y0W/TjQ4rjXHaaZN4+elyAXl4wwKYM61xP9Y
 u2E8qXl8Nk2oyXFKNcc4BBIbCB4aaQ66RaK0pWxzFGQDCiR+IKs=
 =6HFa
 -----END PGP SIGNATURE-----

Merge tag 'hwmon-for-v6.15' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging

Pull hwmon updates from Guenter Roeck:
 "New drivers:

   - Driver for HTU31

   - Congatec Board Controller monitoring driver

   - Driver for TI INA233 Current and Power Monitor

  Support for additional chips or boards in existing drivers:

   - pmbus/ltc2978: Add support for LT717x and LTM4673

   - asus-ec-sensors: Add PRIME X670E-PRO WIFI

   - k10temp: Add support for cyan skillfish

   - nct6683: Add customer ID for AMD BC-250

   - lm90: Add support for NCT7716, NCT7717 and NCT7718

  Other notable improvements in existing drivers:

   - emc2305: Add devicetree support, and use
     devm_thermal_of_cooling_device_register

   - acpi_power_meter: Convert to with_info API

   - dell-smm: Increase the number of fans

   - pmbus/core: Optimize debugfs support and use i2c_client
     debugfs directory

   - hwmon core: Fix the missing of 'average' word in
     hwmon_power_attr_templates

   - Various drivers: Use per-client debugfs entry provided by
     I2C subsystem"

* tag 'hwmon-for-v6.15' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (49 commits)
  hwmon: emc2305: Use devm_thermal_of_cooling_device_register
  hwmon: emc2305: Add OF support
  dt-bindings: hwmon: Add Microchip emc2305 support
  dt-bindings: hwmon: Drop stray blank line in the header
  hwmon: (acpi_power_meter) Replace the deprecated hwmon_device_register
  hwmon: add driver for HTU31
  dt-bindings: hwmon: Add description for sensor HTU31
  hwmon: Add driver for TI INA233 Current and Power Monitor
  dt-bindings: hwmon: ti,ina2xx: Add INA233 device
  hwmon: Add Congatec Board Controller monitoring driver
  hwmon: (pmbus/ltc2978) add support for lt717x
  dt-bindings: hwmon: ltc2978: add support for LT717x
  hwmon: (pmbus/ltc2978) Add support for LT717x - docs
  hwmon: (dell-smm) Increment the number of fans
  hwmon: (ntc_thermistor) return error instead of clipping on OOB
  hwmon: (pt5161l) Use per-client debugfs entry
  hwmon: Fix the missing of 'average' word in hwmon_power_attr_templates
  hwmon: (acpi_power_meter) Fix the fake power alarm reporting
  hwmon: (gpio-fan) Add missing mutex locks
  dt-bindings: hwmon: gpio-fan: Add optional regulator support
  ...
This commit is contained in:
Linus Torvalds 2025-03-25 19:55:29 -07:00
commit c07666e29b
64 changed files with 2151 additions and 992 deletions

View File

@ -1,7 +1,6 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/adi,ad741x.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

View File

@ -1,7 +1,6 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/adi,adm1275.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

View File

@ -1,7 +1,6 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/adi,ltc2991.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

View File

@ -23,6 +23,9 @@ properties:
alarm-gpios:
maxItems: 1
fan-supply:
description: Power supply for fan
gpio-fan,speed-map:
$ref: /schemas/types.yaml#/definitions/uint32-matrix
minItems: 2

View File

@ -12,6 +12,8 @@ maintainers:
properties:
compatible:
enum:
- lltc,lt7170
- lltc,lt7171
- lltc,ltc2972
- lltc,ltc2974
- lltc,ltc2975
@ -30,6 +32,7 @@ properties:
- lltc,ltc7880
- lltc,ltm2987
- lltc,ltm4664
- lltc,ltm4673
- lltc,ltm4675
- lltc,ltm4676
- lltc,ltm4677
@ -46,6 +49,7 @@ properties:
description: |
list of regulators provided by this controller.
Valid names of regulators depend on number of supplies supported per device:
* lt7170, lt7171 : vout0
* ltc2972 vout0 - vout1
* ltc2974, ltc2975 : vout0 - vout3
* ltc2977, ltc2979, ltc2980, ltm2987 : vout0 - vout7
@ -55,6 +59,7 @@ properties:
* ltc7880 : vout0 - vout1
* ltc3883 : vout0
* ltm4664 : vout0 - vout1
* ltm4673 : vout0 - vout3
* ltm4675, ltm4676, ltm4677, ltm4678 : vout0 - vout1
* ltm4680, ltm4686 : vout0 - vout1
* ltm4700 : vout0 - vout1

View File

@ -1,7 +1,6 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/maxim,max20730.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

View File

@ -1,7 +1,6 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/maxim,max6639.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

View File

@ -1,7 +1,6 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/maxim,max6650.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

View File

@ -0,0 +1,111 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/microchip,emc2305.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Microchip EMC2305 SMBus compliant PWM fan controller
maintainers:
- Michael Shych <michaelsh@nvidia.com>
description:
Microchip EMC2301/2/3/5 pwm controller which supports up to five programmable
fan control circuits.
properties:
compatible:
oneOf:
- enum:
- microchip,emc2305
- items:
- enum:
- microchip,emc2303
- microchip,emc2302
- microchip,emc2301
- const: microchip,emc2305
reg:
maxItems: 1
'#address-cells':
const: 1
'#size-cells':
const: 0
'#pwm-cells':
const: 3
description: |
Number of cells in a PWM specifier.
- cell 0: The PWM frequency
- cell 1: The PWM polarity: 0 or PWM_POLARITY_INVERTED
- cell 2: The PWM output config:
- 0 (Open-Drain)
- 1 (Push-Pull)
patternProperties:
'^fan@[0-4]$':
$ref: fan-common.yaml#
unevaluatedProperties: false
properties:
reg:
description:
The fan number used to determine the associated PWM channel.
maxItems: 1
required:
- reg
required:
- compatible
- reg
additionalProperties: false
examples:
- |
#include <dt-bindings/pwm/pwm.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
fan_controller: fan-controller@2f {
compatible = "microchip,emc2305";
reg = <0x2f>;
#address-cells = <1>;
#size-cells = <0>;
#pwm-cells = <3>;
fan@0 {
reg = <0x0>;
pwms = <&fan_controller 26000 PWM_POLARITY_INVERTED 1>;
#cooling-cells = <2>;
};
fan@1 {
reg = <0x1>;
pwms = <&fan_controller 26000 0 1>;
#cooling-cells = <2>;
};
fan@2 {
reg = <0x2>;
pwms = <&fan_controller 26000 0 1>;
#cooling-cells = <2>;
};
fan@3 {
reg = <0x3>;
pwms = <&fan_controller 26000 0 1>;
#cooling-cells = <2>;
};
fan@4 {
reg = <0x4>;
pwms = <&fan_controller 26000 0 1>;
#cooling-cells = <2>;
};
};
};
...

View File

@ -32,6 +32,9 @@ properties:
- national,lm89
- national,lm90
- national,lm99
- nuvoton,nct7716
- nuvoton,nct7717
- nuvoton,nct7718
- nxp,sa56004
- onnn,nct1008
- ti,tmp451
@ -120,6 +123,8 @@ allOf:
- dallas,max6659
- dallas,max6695
- dallas,max6696
- nuvoton,nct7716
- nuvoton,nct7717
then:
patternProperties:
"^channel@([0-2])$":
@ -155,6 +160,7 @@ allOf:
- national,lm89
- national,lm90
- national,lm99
- nuvoton,nct7718
- nxp,sa56004
- winbond,w83l771
then:

View File

@ -76,7 +76,7 @@ properties:
- const: murata,ncp03wf104
- const: murata,ncp15xh103
- const: samsung,1404-001221
# Deprecated "ntp," compatible strings
# Deprecated "ntc," compatible strings
- const: ntc,ncp15wb473
deprecated: true
- const: ntc,ncp18wb473

View File

@ -1,7 +1,6 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/nuvoton,nct6775.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

View File

@ -1,7 +1,6 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/nuvoton,nct7363.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

View File

@ -1,7 +1,6 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/nuvoton,nct7802.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

View File

@ -28,6 +28,15 @@ properties:
reg:
maxItems: 1
gpio-controller: true
gpio-line-names:
minItems: 84
maxItems: 84
'#gpio-cells':
const: 1
required:
- compatible
- reg

View File

@ -1,7 +1,6 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/ti,adc128d818.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

View File

@ -1,7 +1,6 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/ti,ads7828.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

View File

@ -1,7 +1,6 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/ti,ina2xx.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
@ -27,6 +26,7 @@ properties:
- ti,ina226
- ti,ina230
- ti,ina231
- ti,ina233
- ti,ina237
- ti,ina238
- ti,ina260
@ -75,12 +75,41 @@ properties:
the alert polarity to active-high.
$ref: /schemas/types.yaml#/definitions/flag
ti,maximum-expected-current-microamp:
description: |
This value indicates the maximum current in microamps that you can
expect to measure with ina233 in your circuit.
This value will be used to calculate the Current_LSB and current/power
coefficient for the pmbus and to calibrate the IC.
minimum: 32768
maximum: 4294967295
default: 32768000
required:
- compatible
- reg
allOf:
- $ref: hwmon-common.yaml#
- if:
properties:
compatible:
contains:
enum:
- silergy,sy24655
- ti,ina209
- ti,ina219
- ti,ina220
- ti,ina226
- ti,ina230
- ti,ina231
- ti,ina237
- ti,ina238
- ti,ina260
then:
properties:
ti,maximum-expected-current-microamp: false
unevaluatedProperties: false

View File

@ -1,7 +1,6 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/ti,lm87.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

View File

@ -1,7 +1,6 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/ti,tmp513.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

View File

@ -1,7 +1,6 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/ti,tps23861.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

View File

@ -1,7 +1,6 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/winbond,w83781d.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

View File

@ -189,6 +189,8 @@ properties:
- mcube,mc3230
# Measurement Specialities I2C temperature and humidity sensor
- meas,htu21
# Measurement Specialities I2C temperature and humidity sensor
- meas,htu31
# Measurement Specialities I2C pressure and temperature sensor
- meas,ms5637
# Measurement Specialities I2C pressure and temperature sensor

View File

@ -6,9 +6,9 @@ First of all, what I know about uGuru is no fact based on any help, hints or
datasheet from Abit. The data I have got on uGuru have I assembled through
my weak knowledge in "backwards engineering".
And just for the record, you may have noticed uGuru isn't a chip developed by
Abit, as they claim it to be. It's really just an microprocessor (uC) created by
Abit, as they claim it to be. It's really just a microprocessor (uC) created by
Winbond (W83L950D). And no, reading the manual for this specific uC or
mailing Windbond for help won't give any useful data about uGuru, as it is
mailing Winbond for help won't give any useful data about uGuru, as it is
the program inside the uC that is responding to calls.
Olle Sandberg <ollebull@gmail.com>, 2005-05-25
@ -35,7 +35,7 @@ As far as known the uGuru is always placed at and using the (ISA) I/O-ports
ports are holding for detection. We will refer to 0xE0 as CMD (command-port)
and 0xE4 as DATA because Abit refers to them with these names.
If DATA holds 0x00 or 0x08 and CMD holds 0x00 or 0xAC an uGuru could be
If DATA holds 0x00 or 0x08 and CMD holds 0x00 or 0xAC a uGuru could be
present. We have to check for two different values at data-port, because
after a reboot uGuru will hold 0x00 here, but if the driver is removed and
later on attached again data-port will hold 0x08, more about this later.
@ -46,7 +46,7 @@ have to test CMD for two different values. On these uGuru's DATA will initially
hold 0x09 and will only hold 0x08 after reading CMD first, so CMD must be read
first!
To be really sure an uGuru is present a test read of one or more register
To be really sure a uGuru is present a test read of one or more register
sets should be done.

View File

@ -40,7 +40,7 @@ Supported chips:
.. [2] There is a separate abituguru3 driver for these motherboards,
the abituguru (without the 3 !) driver will not work on these
motherboards (and visa versa)!
motherboards (and vice versa)!
Authors:
- Hans de Goede <j.w.r.degoede@hhs.nl>,

View File

@ -6,6 +6,7 @@ Kernel driver asus_ec_sensors
Supported boards:
* PRIME X470-PRO
* PRIME X570-PRO
* PRIME X670E-PRO WIFI
* Pro WS X570-ACE
* ProArt X570-CREATOR WIFI
* ProArt X670E-CREATOR WIFI

View File

@ -0,0 +1,63 @@
.. SPDX-License-Identifier: GPL-2.0-or-later
Kernel driver cgbc-hwmon
========================
Supported chips:
* Congatec Board Controller.
Prefix: 'cgbc-hwmon'
Author: Thomas Richard <thomas.richard@bootlin.com>
Description
-----------
This driver enables monitoring support for the Congatec Board Controller.
This controller is embedded on the x86 SoMs of Congatec.
Sysfs entries
-------------
The following sysfs entries list contains all sensors defined in the Board
Controller. The available sensors in sysfs depend on the SoM and the
system.
============= ======================
Name Description
============= ======================
temp1_input CPU temperature
temp2_input Box temperature
temp3_input Ambient temperature
temp4_input Board temperature
temp5_input Carrier temperature
temp6_input Chipset temperature
temp7_input Video temperature
temp8_input Other temperature
temp9_input TOPDIM temperature
temp10_input BOTTOMDIM temperature
in0_input CPU voltage
in1_input DC Runtime voltage
in2_input DC Standby voltage
in3_input CMOS Battery voltage
in4_input Battery voltage
in5_input AC voltage
in6_input Other voltage
in7_input 5V voltage
in8_input 5V Standby voltage
in9_input 3V3 voltage
in10_input 3V3 Standby voltage
in11_input VCore A voltage
in12_input VCore B voltage
in13_input 12V voltage
curr1_input DC current
curr2_input 5V current
curr3_input 12V current
fan1_input CPU fan
fan2_input Box fan
fan3_input Ambient fan
fan4_input Chiptset fan
fan5_input Video fan
fan6_input Other fan
============= ======================

View File

@ -32,12 +32,12 @@ Temperature sensors and fans can be queried and set via the standard
=============================== ======= =======================================
Name Perm Description
=============================== ======= =======================================
fan[1-3]_input RO Fan speed in RPM.
fan[1-3]_label RO Fan label.
fan[1-3]_min RO Minimal Fan speed in RPM
fan[1-3]_max RO Maximal Fan speed in RPM
fan[1-3]_target RO Expected Fan speed in RPM
pwm[1-3] RW Control the fan PWM duty-cycle.
fan[1-4]_input RO Fan speed in RPM.
fan[1-4]_label RO Fan label.
fan[1-4]_min RO Minimal Fan speed in RPM
fan[1-4]_max RO Maximal Fan speed in RPM
fan[1-4]_target RO Expected Fan speed in RPM
pwm[1-4] RW Control the fan PWM duty-cycle.
pwm1_enable WO Enable or disable automatic BIOS fan
control (not supported on all laptops,
see below for details).
@ -93,7 +93,7 @@ Again, when you find new codes, we'd be happy to have your patches!
---------------------------
The driver also exports the fans as thermal cooling devices with
``type`` set to ``dell-smm-fan[1-3]``. This allows for easy fan control
``type`` set to ``dell-smm-fan[1-4]``. This allows for easy fan control
using one of the thermal governors.
Module parameters

View File

@ -0,0 +1,37 @@
.. SPDX-License-Identifier: GPL-2.0-or-later
Kernel driver HTU31
====================
Supported chips:
* Measurement Specialties HTU31
Prefix: 'htu31'
Addresses scanned: -
Datasheet: Publicly available from https://www.te.com/en/product-CAT-HSC0007.html
Author:
- Andrei Lalaev <andrey.lalaev@gmail.com>
Description
-----------
HTU31 is a humidity and temperature sensor.
Supported temperature range is from -40 to 125 degrees Celsius.
Communication with the device is performed via I2C protocol. Sensor's default address
is 0x40.
sysfs-Interface
---------------
=================== =================
temp1_input: temperature input
humidity1_input: humidity input
heater_enable: heater control
=================== =================

View File

@ -0,0 +1,75 @@
.. SPDX-License-Identifier: GPL-2.0
Kernel driver ina233
====================
Supported chips:
* TI INA233
Prefix: 'ina233'
* Datasheet
Publicly available at the TI website : https://www.ti.com/lit/ds/symlink/ina233.pdf
Author: Leo Yang <leo.yang.sy0@gmail.com>
Usage Notes
-----------
The shunt resistor value can be configured by a device tree property;
see Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml for details.
Description
-----------
This driver supports hardware monitoring for TI INA233.
The driver is a client driver to the core PMBus driver. Please see
Documentation/hwmon/pmbus.rst for details on PMBus client drivers.
The driver provides the following attributes for input voltage:
**in1_input**
**in1_label**
**in1_max**
**in1_max_alarm**
**in1_min**
**in1_min_alarm**
The driver provides the following attributes for shunt voltage:
**in2_input**
**in2_label**
The driver provides the following attributes for output voltage:
**in3_input**
**in3_label**
**in3_alarm**
The driver provides the following attributes for output current:
**curr1_input**
**curr1_label**
**curr1_max**
**curr1_max_alarm**
The driver provides the following attributes for input power:
**power1_input**
**power1_label**

View File

@ -53,6 +53,7 @@ Hardware Monitoring Kernel Drivers
bel-pfe
bpa-rs600
bt1-pvt
cgbc-hwmon
chipcap2
coretemp
corsair-cpro
@ -85,11 +86,13 @@ Hardware Monitoring Kernel Drivers
hih6130
hp-wmi-sensors
hs3001
htu31
ibmaem
ibm-cffps
ibmpowernv
ina209
ina2xx
ina233
ina238
ina3221
inspur-ipsps1

View File

@ -365,6 +365,34 @@ Supported chips:
Datasheet: Not publicly available, can be requested from Nuvoton
* Nuvoton NCT7716
Prefix: 'nct7716'
Addresses scanned: I2C 0x48, 0x49
Datasheet: Not publicly available, can be requested from Nuvoton
* Nuvoton NCT7717
Prefix: 'nct7717'
Addresses scanned: I2C 0x48
Datasheet: Publicly available at Nuvoton website
https://www.nuvoton.com/resource-files/Nuvoton_NCT7717U_Datasheet_V111.pdf
* Nuvoton NCT7718
Prefix: 'nct7718'
Addresses scanned: I2C 0x4c
Datasheet: Publicly available at Nuvoton website
https://www.nuvoton.com/resource-files/Nuvoton_NCT7718W_Datasheet_V11.pdf
* Philips/NXP SA56004X
Prefix: 'sa56004'
@ -573,6 +601,21 @@ W83L771AWG/ASG
* The AWG and ASG variants only differ in package format.
* Diode ideality factor configuration (remote sensor) at 0xE3
NCT7716:
* 8 bit sensor resolution
* Selectable address
* Configurable conversion rate
NCT7717:
* 8 bit sensor resolution
* Configurable conversion rate
NCT7718:
* Temperature offset register for remote temperature sensor
* 11 bit resolution for remote temperature sensor
* Low temperature limits
* Configurable conversion rate
SA56004X:
* Better local resolution

View File

@ -5,6 +5,22 @@ Kernel driver ltc2978
Supported chips:
* Analog Devices LT7170
Prefix: 'lt7170'
Addresses scanned: -
Datasheet: https://www.analog.com/en/products/lt7170.html
* Analog Devices LT7171
Prefix: 'lt7171'
Addresses scanned: -
Datasheet: https://www.analog.com/en/products/lt7171.html
* Linear Technology LTC2972
Prefix: 'ltc2972'
@ -151,6 +167,14 @@ Supported chips:
Datasheet: https://www.analog.com/en/products/ltm4644
* Linear Technology LTM4673
Prefix: 'ltm4673'
Addresses scanned: -
Datasheet: https://www.analog.com/en/products/ltm4673
* Linear Technology LTM4675
Prefix: 'ltm4675'
@ -215,6 +239,8 @@ Author: Guenter Roeck <linux@roeck-us.net>
Description
-----------
- LT7170 and LT7171 are 20 A, 16 V, single- or dual-phase Silent Switcher
- step-down regulators with Digital Power System Management.
- LTC2974 and LTC2975 are quad digital power supply managers.
- LTC2978 is an octal power supply monitor.
- LTC2977 is a pin compatible replacement for LTC2978.
@ -292,6 +318,7 @@ in1_reset_history Reset input voltage history.
in[N]_label "vout[1-8]".
- LT7170, LT7171: N=2
- LTC2972: N=2-3
- LTC2974, LTC2975: N=2-5
- LTC2977, LTC2979, LTC2980, LTM2987: N=2-9
@ -330,6 +357,8 @@ in[N]_reset_history Reset output voltage history.
temp[N]_input Measured temperature.
- On LT7170 and LT7171, temp1 reports the chip
temperature.
- On LTC2972, temp[1-2] report external temperatures,
and temp 3 reports the chip temperature.
- On LTC2974 and LTC2975, temp[1-4] report external
@ -403,9 +432,9 @@ power[N]_input Measured output power.
curr1_label "iin".
LTC3880, LTC3883, LTC3884, LTC3886, LTC3887, LTC3889,
LTM4644, LTM4675, LTM4676, LTM4677, LTM4678, LTM4680,
and LTM4700 only.
LT7170, LT7171, LTC3880, LTC3883, LTC3884, LTC3886,
LTC3887, LTC3889, LTM4644, LTM4675, LTM4676, LTM4677,
LTM4678, LTM4680, and LTM4700 only.
curr1_input Measured input current.
@ -423,6 +452,7 @@ curr1_reset_history Reset input current history.
curr[N]_label "iout[1-4]".
- LT7170, LT7171: N=1
- LTC2972: N-1-2
- LTC2974, LTC2975: N=1-4
- LTC2977, LTC2979, LTC2980, LTM2987: not supported

View File

@ -3,7 +3,7 @@ Kernel driver nct6683
Supported chips:
* Nuvoton NCT6683D/NCT6687D
* Nuvoton NCT6683D/NCT6686D/NCT6687D
Prefix: 'nct6683'
@ -61,6 +61,7 @@ Board Firmware version
Intel DH87RL NCT6683D EC firmware version 1.0 build 04/03/13
Intel DH87MC NCT6683D EC firmware version 1.0 build 04/03/13
Intel DB85FL NCT6683D EC firmware version 1.0 build 04/03/13
AMD BC-250 NCT6686D EC firmware version 1.0 build 07/28/21
ASRock X570 NCT6683D EC firmware version 1.0 build 06/28/19
ASRock X670E NCT6686D EC firmware version 1.0 build 05/19/22
ASRock B650 Steel Legend WiFi NCT6686D EC firmware version 1.0 build 11/09/23

View File

@ -5878,6 +5878,7 @@ CONGATEC BOARD CONTROLLER MFD DRIVER
M: Thomas Richard <thomas.richard@bootlin.com>
S: Maintained
F: drivers/gpio/gpio-cgbc.c
F: drivers/hwmon/cgbc-hwmon.c
F: drivers/i2c/busses/i2c-cgbc.c
F: drivers/mfd/cgbc-core.c
F: drivers/watchdog/cgbc_wdt.c
@ -10729,6 +10730,12 @@ W: http://www.st.com/
F: Documentation/devicetree/bindings/iio/humidity/st,hts221.yaml
F: drivers/iio/humidity/hts221*
HTU31 Hardware Temperature and Humidity Sensor
M: Andrei Lalaev <andrey.lalaev@gmail.com>
L: linux-hwmon@vger.kernel.org
S: Maintained
F: drivers/hwmon/htu31.c
HUAWEI ETHERNET DRIVER
M: Cai Huoqing <cai.huoqing@linux.dev>
L: netdev@vger.kernel.org
@ -11370,6 +11377,13 @@ L: linux-fbdev@vger.kernel.org
S: Orphan
F: drivers/video/fbdev/imsttfb.c
INA233 HARDWARE MONITOR DRIVERS
M: Leo Yang <leo.yang.sy0@gmail.com>
L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/ina233.rst
F: drivers/hwmon/pmbus/ina233.c
INDEX OF FURTHER KERNEL DOCUMENTATION
M: Carlos Bilbao <carlos.bilbao@kernel.org>
S: Maintained

View File

@ -463,6 +463,16 @@ config SENSORS_BT1_PVT_ALARMS
the data conversion will be periodically performed and the data will be
saved in the internal driver cache.
config SENSORS_CGBC
tristate "Congatec Board Controller Sensors"
depends on MFD_CGBC
help
Enable sensors support for the Congatec Board Controller. It has
temperature, voltage, current and fan sensors.
This driver can also be built as a module. If so, the module will be
called cgbc-hwmon.
config SENSORS_CHIPCAP2
tristate "Amphenol ChipCap 2 relative humidity and temperature sensor"
depends on I2C
@ -789,6 +799,17 @@ config SENSORS_HS3001
This driver can also be built as a module. If so, the module
will be called hs3001.
config SENSORS_HTU31
tristate "Measurement Specialties HTU31 humidity and temperature sensor"
depends on I2C
select CRC8
help
If you say yes here you get support for the HTU31 humidity
and temperature sensors.
This driver can also be built as a module. If so, the module
will be called htu31.
config SENSORS_IBMAEM
tristate "IBM Active Energy Manager temperature/power sensors and control"
select IPMI_SI
@ -1519,7 +1540,7 @@ config SENSORS_LM90
MAX6657, MAX6658, MAX6659, MAX6680, MAX6681, MAX6692, MAX6695,
MAX6696,
ON Semiconductor NCT1008, NCT210, NCT72, NCT214, NCT218,
Winbond/Nuvoton W83L771W/G/AWG/ASG,
Winbond/Nuvoton W83L771W/G/AWG/ASG, NCT7716, NCT7717 and NCT7718,
Philips NE1618, SA56004, GMT G781, Texas Instruments TMP451 and TMP461
sensor chips.
@ -1625,7 +1646,7 @@ config SENSORS_NTC_THERMISTOR
B57891S0103 from EPCOS.
This driver can also be built as a module. If so, the module
will be called ntc-thermistor.
will be called ntc_thermistor.
config SENSORS_NCT6683
tristate "Nuvoton NCT6683D"

View File

@ -59,6 +59,7 @@ obj-$(CONFIG_SENSORS_ASUS_ROG_RYUJIN) += asus_rog_ryujin.o
obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o
obj-$(CONFIG_SENSORS_AXI_FAN_CONTROL) += axi-fan-control.o
obj-$(CONFIG_SENSORS_BT1_PVT) += bt1-pvt.o
obj-$(CONFIG_SENSORS_CGBC) += cgbc-hwmon.o
obj-$(CONFIG_SENSORS_CHIPCAP2) += chipcap2.o
obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o
obj-$(CONFIG_SENSORS_CORSAIR_CPRO) += corsair-cpro.o
@ -91,6 +92,7 @@ obj-$(CONFIG_SENSORS_GPIO_FAN) += gpio-fan.o
obj-$(CONFIG_SENSORS_GXP_FAN_CTRL) += gxp-fan-ctrl.o
obj-$(CONFIG_SENSORS_HIH6130) += hih6130.o
obj-$(CONFIG_SENSORS_HS3001) += hs3001.o
obj-$(CONFIG_SENSORS_HTU31) += htu31.o
obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o
obj-$(CONFIG_SENSORS_I5500) += i5500_temp.o
obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o

View File

@ -87,25 +87,14 @@ struct acpi_power_meter_resource {
bool power_alarm;
int sensors_valid;
unsigned long sensors_last_updated;
struct sensor_device_attribute sensors[NUM_SENSORS];
int num_sensors;
#define POWER_METER_TRIP_AVERAGE_MIN_IDX 0
#define POWER_METER_TRIP_AVERAGE_MAX_IDX 1
s64 trip[2];
int num_domain_devices;
struct acpi_device **domain_devices;
struct kobject *holders_dir;
};
struct sensor_template {
char *label;
ssize_t (*show)(struct device *dev,
struct device_attribute *devattr,
char *buf);
ssize_t (*set)(struct device *dev,
struct device_attribute *devattr,
const char *buf, size_t count);
int index;
};
/* Averaging interval */
static int update_avg_interval(struct acpi_power_meter_resource *resource)
{
@ -124,62 +113,6 @@ static int update_avg_interval(struct acpi_power_meter_resource *resource)
return 0;
}
static ssize_t show_avg_interval(struct device *dev,
struct device_attribute *devattr,
char *buf)
{
struct acpi_device *acpi_dev = to_acpi_device(dev);
struct acpi_power_meter_resource *resource = acpi_dev->driver_data;
mutex_lock(&resource->lock);
update_avg_interval(resource);
mutex_unlock(&resource->lock);
return sprintf(buf, "%llu\n", resource->avg_interval);
}
static ssize_t set_avg_interval(struct device *dev,
struct device_attribute *devattr,
const char *buf, size_t count)
{
struct acpi_device *acpi_dev = to_acpi_device(dev);
struct acpi_power_meter_resource *resource = acpi_dev->driver_data;
union acpi_object arg0 = { ACPI_TYPE_INTEGER };
struct acpi_object_list args = { 1, &arg0 };
int res;
unsigned long temp;
unsigned long long data;
acpi_status status;
res = kstrtoul(buf, 10, &temp);
if (res)
return res;
if (temp > resource->caps.max_avg_interval ||
temp < resource->caps.min_avg_interval)
return -EINVAL;
arg0.integer.value = temp;
mutex_lock(&resource->lock);
status = acpi_evaluate_integer(resource->acpi_dev->handle, "_PAI",
&args, &data);
if (ACPI_SUCCESS(status))
resource->avg_interval = temp;
mutex_unlock(&resource->lock);
if (ACPI_FAILURE(status)) {
acpi_evaluation_failure_warn(resource->acpi_dev->handle, "_PAI",
status);
return -EINVAL;
}
/* _PAI returns 0 on success, nonzero otherwise */
if (data)
return -EINVAL;
return count;
}
/* Cap functions */
static int update_cap(struct acpi_power_meter_resource *resource)
{
@ -198,61 +131,6 @@ static int update_cap(struct acpi_power_meter_resource *resource)
return 0;
}
static ssize_t show_cap(struct device *dev,
struct device_attribute *devattr,
char *buf)
{
struct acpi_device *acpi_dev = to_acpi_device(dev);
struct acpi_power_meter_resource *resource = acpi_dev->driver_data;
mutex_lock(&resource->lock);
update_cap(resource);
mutex_unlock(&resource->lock);
return sprintf(buf, "%llu\n", resource->cap * 1000);
}
static ssize_t set_cap(struct device *dev, struct device_attribute *devattr,
const char *buf, size_t count)
{
struct acpi_device *acpi_dev = to_acpi_device(dev);
struct acpi_power_meter_resource *resource = acpi_dev->driver_data;
union acpi_object arg0 = { ACPI_TYPE_INTEGER };
struct acpi_object_list args = { 1, &arg0 };
int res;
unsigned long temp;
unsigned long long data;
acpi_status status;
res = kstrtoul(buf, 10, &temp);
if (res)
return res;
temp = DIV_ROUND_CLOSEST(temp, 1000);
if (temp > resource->caps.max_cap || temp < resource->caps.min_cap)
return -EINVAL;
arg0.integer.value = temp;
mutex_lock(&resource->lock);
status = acpi_evaluate_integer(resource->acpi_dev->handle, "_SHL",
&args, &data);
if (ACPI_SUCCESS(status))
resource->cap = temp;
mutex_unlock(&resource->lock);
if (ACPI_FAILURE(status)) {
acpi_evaluation_failure_warn(resource->acpi_dev->handle, "_SHL",
status);
return -EINVAL;
}
/* _SHL returns 0 on success, nonzero otherwise */
if (data)
return -EINVAL;
return count;
}
/* Power meter trip points */
static int set_acpi_trip(struct acpi_power_meter_resource *resource)
{
@ -287,34 +165,6 @@ static int set_acpi_trip(struct acpi_power_meter_resource *resource)
return 0;
}
static ssize_t set_trip(struct device *dev, struct device_attribute *devattr,
const char *buf, size_t count)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct acpi_device *acpi_dev = to_acpi_device(dev);
struct acpi_power_meter_resource *resource = acpi_dev->driver_data;
unsigned long temp, trip_bk;
int res;
res = kstrtoul(buf, 10, &temp);
if (res)
return res;
temp = DIV_ROUND_CLOSEST(temp, 1000);
guard(mutex)(&resource->lock);
trip_bk = resource->trip[attr->index - 7];
resource->trip[attr->index - 7] = temp;
res = set_acpi_trip(resource);
if (res) {
resource->trip[attr->index - 7] = trip_bk;
return res;
}
return count;
}
/* Power meter */
static int update_meter(struct acpi_power_meter_resource *resource)
{
@ -341,202 +191,6 @@ static int update_meter(struct acpi_power_meter_resource *resource)
return 0;
}
static ssize_t show_power(struct device *dev,
struct device_attribute *devattr,
char *buf)
{
struct acpi_device *acpi_dev = to_acpi_device(dev);
struct acpi_power_meter_resource *resource = acpi_dev->driver_data;
mutex_lock(&resource->lock);
update_meter(resource);
mutex_unlock(&resource->lock);
if (resource->power == UNKNOWN_POWER)
return -ENODATA;
return sprintf(buf, "%llu\n", resource->power * 1000);
}
/* Miscellaneous */
static ssize_t show_str(struct device *dev,
struct device_attribute *devattr,
char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct acpi_device *acpi_dev = to_acpi_device(dev);
struct acpi_power_meter_resource *resource = acpi_dev->driver_data;
acpi_string val;
int ret;
mutex_lock(&resource->lock);
switch (attr->index) {
case 0:
val = resource->model_number;
break;
case 1:
val = resource->serial_number;
break;
case 2:
val = resource->oem_info;
break;
default:
WARN(1, "Implementation error: unexpected attribute index %d\n",
attr->index);
val = "";
break;
}
ret = sprintf(buf, "%s\n", val);
mutex_unlock(&resource->lock);
return ret;
}
static ssize_t show_val(struct device *dev,
struct device_attribute *devattr,
char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct acpi_device *acpi_dev = to_acpi_device(dev);
struct acpi_power_meter_resource *resource = acpi_dev->driver_data;
u64 val = 0;
int ret;
guard(mutex)(&resource->lock);
switch (attr->index) {
case 0:
val = resource->caps.min_avg_interval;
break;
case 1:
val = resource->caps.max_avg_interval;
break;
case 2:
val = resource->caps.min_cap * 1000;
break;
case 3:
val = resource->caps.max_cap * 1000;
break;
case 4:
if (resource->caps.hysteresis == UNKNOWN_HYSTERESIS)
return sprintf(buf, "unknown\n");
val = resource->caps.hysteresis * 1000;
break;
case 5:
if (resource->caps.flags & POWER_METER_IS_BATTERY)
val = 1;
else
val = 0;
break;
case 6:
ret = update_meter(resource);
if (ret)
return ret;
/* need to update cap if not to support the notification. */
if (!(resource->caps.flags & POWER_METER_CAN_NOTIFY)) {
ret = update_cap(resource);
if (ret)
return ret;
}
val = resource->power_alarm || resource->power > resource->cap;
resource->power_alarm = resource->power > resource->cap;
break;
case 7:
case 8:
if (resource->trip[attr->index - 7] < 0)
return sprintf(buf, "unknown\n");
val = resource->trip[attr->index - 7] * 1000;
break;
default:
WARN(1, "Implementation error: unexpected attribute index %d\n",
attr->index);
break;
}
return sprintf(buf, "%llu\n", val);
}
static ssize_t show_accuracy(struct device *dev,
struct device_attribute *devattr,
char *buf)
{
struct acpi_device *acpi_dev = to_acpi_device(dev);
struct acpi_power_meter_resource *resource = acpi_dev->driver_data;
unsigned int acc = resource->caps.accuracy;
return sprintf(buf, "%u.%u%%\n", acc / 1000, acc % 1000);
}
static ssize_t show_name(struct device *dev,
struct device_attribute *devattr,
char *buf)
{
return sprintf(buf, "%s\n", ACPI_POWER_METER_NAME);
}
#define RO_SENSOR_TEMPLATE(_label, _show, _index) \
{ \
.label = _label, \
.show = _show, \
.index = _index, \
}
#define RW_SENSOR_TEMPLATE(_label, _show, _set, _index) \
{ \
.label = _label, \
.show = _show, \
.set = _set, \
.index = _index, \
}
/* Sensor descriptions. If you add a sensor, update NUM_SENSORS above! */
static struct sensor_template meter_attrs[] = {
RO_SENSOR_TEMPLATE(POWER_AVERAGE_NAME, show_power, 0),
RO_SENSOR_TEMPLATE("power1_accuracy", show_accuracy, 0),
RO_SENSOR_TEMPLATE("power1_average_interval_min", show_val, 0),
RO_SENSOR_TEMPLATE("power1_average_interval_max", show_val, 1),
RO_SENSOR_TEMPLATE("power1_is_battery", show_val, 5),
RW_SENSOR_TEMPLATE(POWER_AVG_INTERVAL_NAME, show_avg_interval,
set_avg_interval, 0),
{},
};
static struct sensor_template misc_cap_attrs[] = {
RO_SENSOR_TEMPLATE("power1_cap_min", show_val, 2),
RO_SENSOR_TEMPLATE("power1_cap_max", show_val, 3),
RO_SENSOR_TEMPLATE("power1_cap_hyst", show_val, 4),
RO_SENSOR_TEMPLATE(POWER_ALARM_NAME, show_val, 6),
{},
};
static struct sensor_template ro_cap_attrs[] = {
RO_SENSOR_TEMPLATE(POWER_CAP_NAME, show_cap, 0),
{},
};
static struct sensor_template rw_cap_attrs[] = {
RW_SENSOR_TEMPLATE(POWER_CAP_NAME, show_cap, set_cap, 0),
{},
};
static struct sensor_template trip_attrs[] = {
RW_SENSOR_TEMPLATE("power1_average_min", show_val, set_trip, 7),
RW_SENSOR_TEMPLATE("power1_average_max", show_val, set_trip, 8),
{},
};
static struct sensor_template misc_attrs[] = {
RO_SENSOR_TEMPLATE("name", show_name, 0),
RO_SENSOR_TEMPLATE("power1_model_number", show_str, 0),
RO_SENSOR_TEMPLATE("power1_oem_info", show_str, 2),
RO_SENSOR_TEMPLATE("power1_serial_number", show_str, 1),
{},
};
#undef RO_SENSOR_TEMPLATE
#undef RW_SENSOR_TEMPLATE
/* Read power domain data */
static void remove_domain_devices(struct acpi_power_meter_resource *resource)
{
@ -638,109 +292,434 @@ static int read_domain_devices(struct acpi_power_meter_resource *resource)
return res;
}
/* Registration and deregistration */
static int register_attrs(struct acpi_power_meter_resource *resource,
struct sensor_template *attrs)
static int set_trip(struct acpi_power_meter_resource *resource, u16 trip_idx,
unsigned long trip)
{
struct device *dev = &resource->acpi_dev->dev;
struct sensor_device_attribute *sensors =
&resource->sensors[resource->num_sensors];
int res = 0;
unsigned long trip_bk;
int ret;
while (attrs->label) {
sensors->dev_attr.attr.name = attrs->label;
sensors->dev_attr.attr.mode = 0444;
sensors->dev_attr.show = attrs->show;
sensors->index = attrs->index;
trip = DIV_ROUND_CLOSEST(trip, 1000);
trip_bk = resource->trip[trip_idx];
if (attrs->set) {
sensors->dev_attr.attr.mode |= 0200;
sensors->dev_attr.store = attrs->set;
}
sysfs_attr_init(&sensors->dev_attr.attr);
res = device_create_file(dev, &sensors->dev_attr);
if (res) {
sensors->dev_attr.attr.name = NULL;
goto error;
}
sensors++;
resource->num_sensors++;
attrs++;
resource->trip[trip_idx] = trip;
ret = set_acpi_trip(resource);
if (ret) {
dev_err(&resource->acpi_dev->dev, "set %s failed.\n",
(trip_idx == POWER_METER_TRIP_AVERAGE_MIN_IDX) ?
"power1_average_min" : "power1_average_max");
resource->trip[trip_idx] = trip_bk;
}
error:
return res;
return ret;
}
static void remove_attrs(struct acpi_power_meter_resource *resource)
static int set_cap(struct acpi_power_meter_resource *resource,
unsigned long cap)
{
int i;
union acpi_object arg0 = { ACPI_TYPE_INTEGER };
struct acpi_object_list args = { 1, &arg0 };
unsigned long long data;
acpi_status status;
for (i = 0; i < resource->num_sensors; i++) {
if (!resource->sensors[i].dev_attr.attr.name)
continue;
device_remove_file(&resource->acpi_dev->dev,
&resource->sensors[i].dev_attr);
cap = DIV_ROUND_CLOSEST(cap, 1000);
if (cap > resource->caps.max_cap || cap < resource->caps.min_cap)
return -EINVAL;
arg0.integer.value = cap;
status = acpi_evaluate_integer(resource->acpi_dev->handle, "_SHL",
&args, &data);
if (ACPI_FAILURE(status)) {
acpi_evaluation_failure_warn(resource->acpi_dev->handle, "_SHL",
status);
return -EINVAL;
}
resource->cap = cap;
remove_domain_devices(resource);
/* _SHL returns 0 on success, nonzero otherwise */
if (data)
return -EINVAL;
resource->num_sensors = 0;
return 0;
}
static int setup_attrs(struct acpi_power_meter_resource *resource)
static int set_avg_interval(struct acpi_power_meter_resource *resource,
unsigned long val)
{
int res = 0;
union acpi_object arg0 = { ACPI_TYPE_INTEGER };
struct acpi_object_list args = { 1, &arg0 };
unsigned long long data;
acpi_status status;
/* _PMD method is optional. */
res = read_domain_devices(resource);
if (res && res != -ENODEV)
return res;
if (val > resource->caps.max_avg_interval ||
val < resource->caps.min_avg_interval)
return -EINVAL;
if (resource->caps.flags & POWER_METER_CAN_MEASURE) {
res = register_attrs(resource, meter_attrs);
if (res)
goto error;
arg0.integer.value = val;
status = acpi_evaluate_integer(resource->acpi_dev->handle, "_PAI",
&args, &data);
if (ACPI_FAILURE(status)) {
acpi_evaluation_failure_warn(resource->acpi_dev->handle, "_PAI",
status);
return -EINVAL;
}
resource->avg_interval = val;
/* _PAI returns 0 on success, nonzero otherwise */
if (data)
return -EINVAL;
return 0;
}
static int get_power_alarm_state(struct acpi_power_meter_resource *resource,
long *val)
{
int ret;
ret = update_meter(resource);
if (ret)
return ret;
/* need to update cap if not to support the notification. */
if (!(resource->caps.flags & POWER_METER_CAN_NOTIFY)) {
ret = update_cap(resource);
if (ret)
return ret;
resource->power_alarm = resource->power > resource->cap;
*val = resource->power_alarm;
} else {
*val = resource->power_alarm || resource->power > resource->cap;
resource->power_alarm = resource->power > resource->cap;
}
if (resource->caps.flags & POWER_METER_CAN_CAP) {
if (!can_cap_in_hardware()) {
dev_warn(&resource->acpi_dev->dev,
return 0;
}
static umode_t power_meter_is_visible(const void *data,
enum hwmon_sensor_types type,
u32 attr, int channel)
{
const struct acpi_power_meter_resource *res = data;
if (type != hwmon_power)
return 0;
switch (attr) {
case hwmon_power_average:
case hwmon_power_average_interval_min:
case hwmon_power_average_interval_max:
if (res->caps.flags & POWER_METER_CAN_MEASURE)
return 0444;
break;
case hwmon_power_average_interval:
if (res->caps.flags & POWER_METER_CAN_MEASURE)
return 0644;
break;
case hwmon_power_cap_min:
case hwmon_power_cap_max:
case hwmon_power_alarm:
if (res->caps.flags & POWER_METER_CAN_CAP && can_cap_in_hardware())
return 0444;
break;
case hwmon_power_cap:
if (res->caps.flags & POWER_METER_CAN_CAP && can_cap_in_hardware()) {
if (res->caps.configurable_cap)
return 0644;
else
return 0444;
}
break;
default:
break;
}
return 0;
}
static int power_meter_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
struct acpi_power_meter_resource *res = dev_get_drvdata(dev);
int ret = 0;
if (type != hwmon_power)
return -EINVAL;
guard(mutex)(&res->lock);
switch (attr) {
case hwmon_power_average:
ret = update_meter(res);
if (ret)
return ret;
if (res->power == UNKNOWN_POWER)
return -ENODATA;
*val = res->power * 1000;
break;
case hwmon_power_average_interval_min:
*val = res->caps.min_avg_interval;
break;
case hwmon_power_average_interval_max:
*val = res->caps.max_avg_interval;
break;
case hwmon_power_average_interval:
ret = update_avg_interval(res);
if (ret)
return ret;
*val = (res)->avg_interval;
break;
case hwmon_power_cap_min:
*val = res->caps.min_cap * 1000;
break;
case hwmon_power_cap_max:
*val = res->caps.max_cap * 1000;
break;
case hwmon_power_alarm:
ret = get_power_alarm_state(res, val);
if (ret)
return ret;
break;
case hwmon_power_cap:
ret = update_cap(res);
if (ret)
return ret;
*val = res->cap * 1000;
break;
default:
break;
}
return 0;
}
static int power_meter_write(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long val)
{
struct acpi_power_meter_resource *res = dev_get_drvdata(dev);
int ret;
if (type != hwmon_power)
return -EINVAL;
guard(mutex)(&res->lock);
switch (attr) {
case hwmon_power_cap:
ret = set_cap(res, val);
break;
case hwmon_power_average_interval:
ret = set_avg_interval(res, val);
break;
default:
ret = -EOPNOTSUPP;
}
return ret;
}
static const struct hwmon_channel_info * const power_meter_info[] = {
HWMON_CHANNEL_INFO(power, HWMON_P_AVERAGE |
HWMON_P_AVERAGE_INTERVAL | HWMON_P_AVERAGE_INTERVAL_MIN |
HWMON_P_AVERAGE_INTERVAL_MAX | HWMON_P_CAP | HWMON_P_CAP_MIN |
HWMON_P_CAP_MAX | HWMON_P_ALARM),
NULL
};
static const struct hwmon_ops power_meter_ops = {
.is_visible = power_meter_is_visible,
.read = power_meter_read,
.write = power_meter_write,
};
static const struct hwmon_chip_info power_meter_chip_info = {
.ops = &power_meter_ops,
.info = power_meter_info,
};
static ssize_t power1_average_max_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct acpi_power_meter_resource *res = dev_get_drvdata(dev);
unsigned long trip;
int ret;
ret = kstrtoul(buf, 10, &trip);
if (ret)
return ret;
mutex_lock(&res->lock);
ret = set_trip(res, POWER_METER_TRIP_AVERAGE_MAX_IDX, trip);
mutex_unlock(&res->lock);
return ret == 0 ? count : ret;
}
static ssize_t power1_average_min_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct acpi_power_meter_resource *res = dev_get_drvdata(dev);
unsigned long trip;
int ret;
ret = kstrtoul(buf, 10, &trip);
if (ret)
return ret;
mutex_lock(&res->lock);
ret = set_trip(res, POWER_METER_TRIP_AVERAGE_MIN_IDX, trip);
mutex_unlock(&res->lock);
return ret == 0 ? count : ret;
}
static ssize_t power1_average_min_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct acpi_power_meter_resource *res = dev_get_drvdata(dev);
if (res->trip[POWER_METER_TRIP_AVERAGE_MIN_IDX] < 0)
return sysfs_emit(buf, "unknown\n");
return sysfs_emit(buf, "%lld\n",
res->trip[POWER_METER_TRIP_AVERAGE_MIN_IDX] * 1000);
}
static ssize_t power1_average_max_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct acpi_power_meter_resource *res = dev_get_drvdata(dev);
if (res->trip[POWER_METER_TRIP_AVERAGE_MAX_IDX] < 0)
return sysfs_emit(buf, "unknown\n");
return sysfs_emit(buf, "%lld\n",
res->trip[POWER_METER_TRIP_AVERAGE_MAX_IDX] * 1000);
}
static ssize_t power1_cap_hyst_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct acpi_power_meter_resource *res = dev_get_drvdata(dev);
if (res->caps.hysteresis == UNKNOWN_HYSTERESIS)
return sysfs_emit(buf, "unknown\n");
return sysfs_emit(buf, "%llu\n", res->caps.hysteresis * 1000);
}
static ssize_t power1_accuracy_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct acpi_power_meter_resource *res = dev_get_drvdata(dev);
unsigned int acc = res->caps.accuracy;
return sysfs_emit(buf, "%u.%u%%\n", acc / 1000, acc % 1000);
}
static ssize_t power1_is_battery_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct acpi_power_meter_resource *res = dev_get_drvdata(dev);
return sysfs_emit(buf, "%u\n",
res->caps.flags & POWER_METER_IS_BATTERY ? 1 : 0);
}
static ssize_t power1_model_number_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct acpi_power_meter_resource *res = dev_get_drvdata(dev);
return sysfs_emit(buf, "%s\n", res->model_number);
}
static ssize_t power1_oem_info_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct acpi_power_meter_resource *res = dev_get_drvdata(dev);
return sysfs_emit(buf, "%s\n", res->oem_info);
}
static ssize_t power1_serial_number_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct acpi_power_meter_resource *res = dev_get_drvdata(dev);
return sysfs_emit(buf, "%s\n", res->serial_number);
}
/* depend on POWER_METER_CAN_TRIP */
static DEVICE_ATTR_RW(power1_average_max);
static DEVICE_ATTR_RW(power1_average_min);
/* depend on POWER_METER_CAN_CAP */
static DEVICE_ATTR_RO(power1_cap_hyst);
/* depend on POWER_METER_CAN_MEASURE */
static DEVICE_ATTR_RO(power1_accuracy);
static DEVICE_ATTR_RO(power1_is_battery);
static DEVICE_ATTR_RO(power1_model_number);
static DEVICE_ATTR_RO(power1_oem_info);
static DEVICE_ATTR_RO(power1_serial_number);
static umode_t power_extra_is_visible(struct kobject *kobj,
struct attribute *attr, int idx)
{
struct device *dev = kobj_to_dev(kobj);
struct acpi_power_meter_resource *res = dev_get_drvdata(dev);
if (attr == &dev_attr_power1_is_battery.attr ||
attr == &dev_attr_power1_accuracy.attr) {
if ((res->caps.flags & POWER_METER_CAN_MEASURE) == 0)
return 0;
}
if (attr == &dev_attr_power1_cap_hyst.attr) {
if ((res->caps.flags & POWER_METER_CAN_CAP) == 0) {
return 0;
} else if (!can_cap_in_hardware()) {
dev_warn(&res->acpi_dev->dev,
"Ignoring unsafe software power cap!\n");
goto skip_unsafe_cap;
return 0;
}
if (resource->caps.configurable_cap)
res = register_attrs(resource, rw_cap_attrs);
else
res = register_attrs(resource, ro_cap_attrs);
if (res)
goto error;
res = register_attrs(resource, misc_cap_attrs);
if (res)
goto error;
}
skip_unsafe_cap:
if (resource->caps.flags & POWER_METER_CAN_TRIP) {
res = register_attrs(resource, trip_attrs);
if (res)
goto error;
if (attr == &dev_attr_power1_average_max.attr ||
attr == &dev_attr_power1_average_min.attr) {
if ((res->caps.flags & POWER_METER_CAN_TRIP) == 0)
return 0;
}
res = register_attrs(resource, misc_attrs);
if (res)
goto error;
return res;
error:
remove_attrs(resource);
return res;
return attr->mode;
}
static struct attribute *power_extra_attrs[] = {
&dev_attr_power1_average_max.attr,
&dev_attr_power1_average_min.attr,
&dev_attr_power1_cap_hyst.attr,
&dev_attr_power1_accuracy.attr,
&dev_attr_power1_is_battery.attr,
&dev_attr_power1_model_number.attr,
&dev_attr_power1_oem_info.attr,
&dev_attr_power1_serial_number.attr,
NULL
};
static const struct attribute_group power_extra_group = {
.attrs = power_extra_attrs,
.is_visible = power_extra_is_visible,
};
__ATTRIBUTE_GROUPS(power_extra);
static void free_capabilities(struct acpi_power_meter_resource *resource)
{
acpi_string *str;
@ -848,13 +827,23 @@ static void acpi_power_meter_notify(struct acpi_device *device, u32 event)
case METER_NOTIFY_CONFIG:
mutex_lock(&resource->lock);
free_capabilities(resource);
remove_domain_devices(resource);
hwmon_device_unregister(resource->hwmon_dev);
res = read_capabilities(resource);
mutex_unlock(&resource->lock);
if (res)
break;
remove_attrs(resource);
setup_attrs(resource);
dev_err_once(&device->dev, "read capabilities failed.\n");
res = read_domain_devices(resource);
if (res && res != -ENODEV)
dev_err_once(&device->dev, "read domain devices failed.\n");
resource->hwmon_dev =
hwmon_device_register_with_info(&device->dev,
ACPI_POWER_METER_NAME,
resource,
&power_meter_chip_info,
power_extra_groups);
if (IS_ERR(resource->hwmon_dev))
dev_err_once(&device->dev, "register hwmon device failed.\n");
mutex_unlock(&resource->lock);
break;
case METER_NOTIFY_TRIP:
sysfs_notify(&device->dev.kobj, NULL, POWER_AVERAGE_NAME);
@ -916,7 +905,7 @@ static int acpi_power_meter_add(struct acpi_device *device)
struct acpi_device *ipi_device = acpi_dev_get_first_match_dev("IPI0001", NULL, -1);
if (ipi_device && acpi_wait_for_acpi_ipmi())
dev_warn(&device->dev, "Waiting for ACPI IPMI timeout");
dev_warn(&device->dev, "Waiting for ACPI IPMI timeout");
acpi_dev_put(ipi_device);
}
#endif
@ -928,11 +917,16 @@ static int acpi_power_meter_add(struct acpi_device *device)
resource->trip[0] = -1;
resource->trip[1] = -1;
res = setup_attrs(resource);
if (res)
/* _PMD method is optional. */
res = read_domain_devices(resource);
if (res && res != -ENODEV)
goto exit_free_capability;
resource->hwmon_dev = hwmon_device_register(&device->dev);
resource->hwmon_dev =
hwmon_device_register_with_info(&device->dev,
ACPI_POWER_METER_NAME, resource,
&power_meter_chip_info,
power_extra_groups);
if (IS_ERR(resource->hwmon_dev)) {
res = PTR_ERR(resource->hwmon_dev);
goto exit_remove;
@ -942,7 +936,7 @@ static int acpi_power_meter_add(struct acpi_device *device)
goto exit;
exit_remove:
remove_attrs(resource);
remove_domain_devices(resource);
exit_free_capability:
free_capabilities(resource);
exit_free:
@ -961,7 +955,7 @@ static void acpi_power_meter_remove(struct acpi_device *device)
resource = acpi_driver_data(device);
hwmon_device_unregister(resource->hwmon_dev);
remove_attrs(resource);
remove_domain_devices(resource);
free_capabilities(resource);
kfree(resource);

View File

@ -316,6 +316,14 @@ static const struct ec_board_info board_info_prime_x570_pro = {
.family = family_amd_500_series,
};
static const struct ec_board_info board_info_prime_x670e_pro_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 = ACPI_GLOBAL_LOCK_PSEUDO_PATH,
.family = family_amd_600_series,
};
static const struct ec_board_info board_info_pro_art_x570_creator_wifi = {
.sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_VRM |
SENSOR_TEMP_T_SENSOR | SENSOR_FAN_CPU_OPT |
@ -503,6 +511,8 @@ static const struct dmi_system_id dmi_table[] = {
&board_info_prime_x470_pro),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("PRIME X570-PRO",
&board_info_prime_x570_pro),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("PRIME X670E-PRO WIFI",
&board_info_prime_x670e_pro_wifi),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ProArt X570-CREATOR WIFI",
&board_info_pro_art_x570_creator_wifi),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ProArt X670E-CREATOR WIFI",

304
drivers/hwmon/cgbc-hwmon.c Normal file
View File

@ -0,0 +1,304 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* cgbc-hwmon - Congatec Board Controller hardware monitoring driver
*
* Copyright (C) 2024 Thomas Richard <thomas.richard@bootlin.com>
*/
#include <linux/bitfield.h>
#include <linux/device.h>
#include <linux/hwmon.h>
#include <linux/mfd/cgbc.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#define CGBC_HWMON_CMD_SENSOR 0x77
#define CGBC_HWMON_CMD_SENSOR_DATA_SIZE 0x05
#define CGBC_HWMON_TYPE_MASK GENMASK(6, 5)
#define CGBC_HWMON_ID_MASK GENMASK(4, 0)
#define CGBC_HWMON_ACTIVE_BIT BIT(7)
struct cgbc_hwmon_sensor {
enum hwmon_sensor_types type;
bool active;
unsigned int index;
unsigned int channel;
const char *label;
};
struct cgbc_hwmon_data {
struct cgbc_device_data *cgbc;
unsigned int nb_sensors;
struct cgbc_hwmon_sensor *sensors;
};
enum cgbc_sensor_types {
CGBC_HWMON_TYPE_TEMP = 1,
CGBC_HWMON_TYPE_IN,
CGBC_HWMON_TYPE_FAN
};
static const char * const cgbc_hwmon_labels_temp[] = {
"CPU Temperature",
"Box Temperature",
"Ambient Temperature",
"Board Temperature",
"Carrier Temperature",
"Chipset Temperature",
"Video Temperature",
"Other Temperature",
"TOPDIM Temperature",
"BOTTOMDIM Temperature",
};
static const struct {
enum hwmon_sensor_types type;
const char *label;
} cgbc_hwmon_labels_in[] = {
{ hwmon_in, "CPU Voltage" },
{ hwmon_in, "DC Runtime Voltage" },
{ hwmon_in, "DC Standby Voltage" },
{ hwmon_in, "CMOS Battery Voltage" },
{ hwmon_in, "Battery Voltage" },
{ hwmon_in, "AC Voltage" },
{ hwmon_in, "Other Voltage" },
{ hwmon_in, "5V Voltage" },
{ hwmon_in, "5V Standby Voltage" },
{ hwmon_in, "3V3 Voltage" },
{ hwmon_in, "3V3 Standby Voltage" },
{ hwmon_in, "VCore A Voltage" },
{ hwmon_in, "VCore B Voltage" },
{ hwmon_in, "12V Voltage" },
{ hwmon_curr, "DC Current" },
{ hwmon_curr, "5V Current" },
{ hwmon_curr, "12V Current" },
};
#define CGBC_HWMON_NB_IN_SENSORS 14
static const char * const cgbc_hwmon_labels_fan[] = {
"CPU Fan",
"Box Fan",
"Ambient Fan",
"Chipset Fan",
"Video Fan",
"Other Fan",
};
static int cgbc_hwmon_cmd(struct cgbc_device_data *cgbc, u8 index, u8 *data)
{
u8 cmd[2] = {CGBC_HWMON_CMD_SENSOR, index};
return cgbc_command(cgbc, cmd, sizeof(cmd), data, CGBC_HWMON_CMD_SENSOR_DATA_SIZE, NULL);
}
static int cgbc_hwmon_probe_sensors(struct device *dev, struct cgbc_hwmon_data *hwmon)
{
struct cgbc_device_data *cgbc = hwmon->cgbc;
struct cgbc_hwmon_sensor *sensor = hwmon->sensors;
u8 data[CGBC_HWMON_CMD_SENSOR_DATA_SIZE], nb_sensors, i;
int ret;
ret = cgbc_hwmon_cmd(cgbc, 0, &data[0]);
if (ret)
return ret;
nb_sensors = data[0];
hwmon->sensors = devm_kzalloc(dev, sizeof(*hwmon->sensors) * nb_sensors, GFP_KERNEL);
sensor = hwmon->sensors;
for (i = 0; i < nb_sensors; i++) {
enum cgbc_sensor_types type;
unsigned int channel;
/*
* No need to request data for the first sensor.
* We got data for the first sensor when we ask the number of sensors to the Board
* Controller.
*/
if (i) {
ret = cgbc_hwmon_cmd(cgbc, i, &data[0]);
if (ret)
return ret;
}
type = FIELD_GET(CGBC_HWMON_TYPE_MASK, data[1]);
channel = FIELD_GET(CGBC_HWMON_ID_MASK, data[1]) - 1;
if (type == CGBC_HWMON_TYPE_TEMP && channel < ARRAY_SIZE(cgbc_hwmon_labels_temp)) {
sensor->type = hwmon_temp;
sensor->label = cgbc_hwmon_labels_temp[channel];
} else if (type == CGBC_HWMON_TYPE_IN &&
channel < ARRAY_SIZE(cgbc_hwmon_labels_in)) {
/*
* The Board Controller doesn't differentiate current and voltage sensors.
* Get the sensor type from cgbc_hwmon_labels_in[channel].type instead.
*/
sensor->type = cgbc_hwmon_labels_in[channel].type;
sensor->label = cgbc_hwmon_labels_in[channel].label;
} else if (type == CGBC_HWMON_TYPE_FAN &&
channel < ARRAY_SIZE(cgbc_hwmon_labels_fan)) {
sensor->type = hwmon_fan;
sensor->label = cgbc_hwmon_labels_fan[channel];
} else {
dev_warn(dev, "Board Controller returned an unknown sensor (type=%d, channel=%d), ignore it",
type, channel);
continue;
}
sensor->active = FIELD_GET(CGBC_HWMON_ACTIVE_BIT, data[1]);
sensor->channel = channel;
sensor->index = i;
sensor++;
hwmon->nb_sensors++;
}
return 0;
}
static struct cgbc_hwmon_sensor *cgbc_hwmon_find_sensor(struct cgbc_hwmon_data *hwmon,
enum hwmon_sensor_types type, int channel)
{
struct cgbc_hwmon_sensor *sensor = NULL;
int i;
/*
* The Board Controller doesn't differentiate current and voltage sensors.
* The channel value (from the Board Controller point of view) shall be computed for current
* sensors.
*/
if (type == hwmon_curr)
channel += CGBC_HWMON_NB_IN_SENSORS;
for (i = 0; i < hwmon->nb_sensors; i++) {
if (hwmon->sensors[i].type == type && hwmon->sensors[i].channel == channel) {
sensor = &hwmon->sensors[i];
break;
}
}
return sensor;
}
static int cgbc_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
long *val)
{
struct cgbc_hwmon_data *hwmon = dev_get_drvdata(dev);
struct cgbc_hwmon_sensor *sensor = cgbc_hwmon_find_sensor(hwmon, type, channel);
struct cgbc_device_data *cgbc = hwmon->cgbc;
u8 data[CGBC_HWMON_CMD_SENSOR_DATA_SIZE];
int ret;
ret = cgbc_hwmon_cmd(cgbc, sensor->index, &data[0]);
if (ret)
return ret;
*val = (data[3] << 8) | data[2];
/*
* For the Board Controller 1lsb = 0.1 degree centigrade.
* Other units are as expected.
*/
if (sensor->type == hwmon_temp)
*val *= 100;
return 0;
}
static umode_t cgbc_hwmon_is_visible(const void *_data, enum hwmon_sensor_types type, u32 attr,
int channel)
{
struct cgbc_hwmon_data *data = (struct cgbc_hwmon_data *)_data;
struct cgbc_hwmon_sensor *sensor;
sensor = cgbc_hwmon_find_sensor(data, type, channel);
if (!sensor)
return 0;
return sensor->active ? 0444 : 0;
}
static int cgbc_hwmon_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr,
int channel, const char **str)
{
struct cgbc_hwmon_data *hwmon = dev_get_drvdata(dev);
struct cgbc_hwmon_sensor *sensor = cgbc_hwmon_find_sensor(hwmon, type, channel);
*str = sensor->label;
return 0;
}
static const struct hwmon_channel_info * const cgbc_hwmon_info[] = {
HWMON_CHANNEL_INFO(temp,
HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL, HWMON_T_INPUT | HWMON_T_LABEL),
HWMON_CHANNEL_INFO(in,
HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL, HWMON_I_INPUT | HWMON_I_LABEL),
HWMON_CHANNEL_INFO(curr,
HWMON_C_INPUT | HWMON_C_LABEL, HWMON_C_INPUT | HWMON_C_LABEL,
HWMON_C_INPUT | HWMON_C_LABEL),
HWMON_CHANNEL_INFO(fan,
HWMON_F_INPUT | HWMON_F_LABEL, HWMON_F_INPUT | HWMON_F_LABEL,
HWMON_F_INPUT | HWMON_F_LABEL, HWMON_F_INPUT | HWMON_F_LABEL,
HWMON_F_INPUT | HWMON_F_LABEL, HWMON_F_INPUT | HWMON_F_LABEL),
NULL
};
static const struct hwmon_ops cgbc_hwmon_ops = {
.is_visible = cgbc_hwmon_is_visible,
.read = cgbc_hwmon_read,
.read_string = cgbc_hwmon_read_string,
};
static const struct hwmon_chip_info cgbc_chip_info = {
.ops = &cgbc_hwmon_ops,
.info = cgbc_hwmon_info,
};
static int cgbc_hwmon_probe(struct platform_device *pdev)
{
struct cgbc_device_data *cgbc = dev_get_drvdata(pdev->dev.parent);
struct device *dev = &pdev->dev;
struct cgbc_hwmon_data *data;
struct device *hwmon_dev;
int ret;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->cgbc = cgbc;
ret = cgbc_hwmon_probe_sensors(dev, data);
if (ret)
return dev_err_probe(dev, ret, "Failed to probe sensors");
hwmon_dev = devm_hwmon_device_register_with_info(dev, "cgbc_hwmon", data, &cgbc_chip_info,
NULL);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
static struct platform_driver cgbc_hwmon_driver = {
.driver = {
.name = "cgbc-hwmon",
},
.probe = cgbc_hwmon_probe,
};
module_platform_driver(cgbc_hwmon_driver);
MODULE_AUTHOR("Thomas Richard <thomas.richard@bootlin.com>");
MODULE_DESCRIPTION("Congatec Board Controller Hardware Monitoring Driver");
MODULE_LICENSE("GPL");

View File

@ -73,7 +73,7 @@
#define DELL_SMM_LEGACY_EXECUTE 0x1
#define DELL_SMM_NO_TEMP 10
#define DELL_SMM_NO_FANS 3
#define DELL_SMM_NO_FANS 4
struct smm_regs {
unsigned int eax;
@ -1074,11 +1074,14 @@ static const struct hwmon_channel_info * const dell_smm_info[] = {
HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MIN | HWMON_F_MAX |
HWMON_F_TARGET,
HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MIN | HWMON_F_MAX |
HWMON_F_TARGET,
HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_MIN | HWMON_F_MAX |
HWMON_F_TARGET
),
HWMON_CHANNEL_INFO(pwm,
HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
HWMON_PWM_INPUT,
HWMON_PWM_INPUT,
HWMON_PWM_INPUT
),
NULL

View File

@ -112,8 +112,6 @@ static char *emc2305_fan_name[] = {
"emc2305_fan5",
};
static void emc2305_unset_tz(struct device *dev);
static int emc2305_get_max_channel(const struct emc2305_data *data)
{
return data->pwm_num;
@ -293,8 +291,9 @@ static int emc2305_set_single_tz(struct device *dev, int idx)
pwm = data->pwm_min[cdev_idx];
data->cdev_data[cdev_idx].cdev =
thermal_cooling_device_register(emc2305_fan_name[idx], data,
&emc2305_cooling_ops);
devm_thermal_of_cooling_device_register(dev, dev->of_node,
emc2305_fan_name[idx], data,
&emc2305_cooling_ops);
if (IS_ERR(data->cdev_data[cdev_idx].cdev)) {
dev_err(dev, "Failed to register cooling device %s\n", emc2305_fan_name[idx]);
@ -332,24 +331,9 @@ static int emc2305_set_tz(struct device *dev)
for (i = 0; i < data->pwm_num; i++) {
ret = emc2305_set_single_tz(dev, i + 1);
if (ret)
goto thermal_cooling_device_register_fail;
return ret;
}
return 0;
thermal_cooling_device_register_fail:
emc2305_unset_tz(dev);
return ret;
}
static void emc2305_unset_tz(struct device *dev)
{
struct emc2305_data *data = dev_get_drvdata(dev);
int i;
/* Unregister cooling device. */
for (i = 0; i < EMC2305_PWM_MAX; i++)
if (data->cdev_data[i].cdev)
thermal_cooling_device_unregister(data->cdev_data[i].cdev);
}
static umode_t
@ -599,20 +583,18 @@ static int emc2305_probe(struct i2c_client *client)
return 0;
}
static void emc2305_remove(struct i2c_client *client)
{
struct device *dev = &client->dev;
if (IS_REACHABLE(CONFIG_THERMAL))
emc2305_unset_tz(dev);
}
static const struct of_device_id of_emc2305_match_table[] = {
{ .compatible = "microchip,emc2305", },
{},
};
MODULE_DEVICE_TABLE(of, of_emc2305_match_table);
static struct i2c_driver emc2305_driver = {
.driver = {
.name = "emc2305",
.of_match_table = of_emc2305_match_table,
},
.probe = emc2305_probe,
.remove = emc2305_remove,
.id_table = emc2305_ids,
};

View File

@ -393,7 +393,12 @@ static int gpio_fan_set_cur_state(struct thermal_cooling_device *cdev,
if (state >= fan_data->num_speed)
return -EINVAL;
mutex_lock(&fan_data->lock);
set_fan_speed(fan_data, state);
mutex_unlock(&fan_data->lock);
return 0;
}
@ -489,7 +494,11 @@ MODULE_DEVICE_TABLE(of, of_gpio_fan_match);
static void gpio_fan_stop(void *data)
{
struct gpio_fan_data *fan_data = data;
mutex_lock(&fan_data->lock);
set_fan_speed(data, 0);
mutex_unlock(&fan_data->lock);
}
static int gpio_fan_probe(struct platform_device *pdev)
@ -562,7 +571,9 @@ static int gpio_fan_suspend(struct device *dev)
if (fan_data->gpios) {
fan_data->resume_speed = fan_data->speed_index;
mutex_lock(&fan_data->lock);
set_fan_speed(fan_data, 0);
mutex_unlock(&fan_data->lock);
}
return 0;
@ -572,8 +583,11 @@ static int gpio_fan_resume(struct device *dev)
{
struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
if (fan_data->gpios)
if (fan_data->gpios) {
mutex_lock(&fan_data->lock);
set_fan_speed(fan_data, fan_data->resume_speed);
mutex_unlock(&fan_data->lock);
}
return 0;
}

View File

@ -47,7 +47,6 @@ static const struct regmap_bus gsc_hwmon_regmap_bus = {
static const struct regmap_config gsc_hwmon_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.cache_type = REGCACHE_NONE,
};
static ssize_t pwm_auto_point_temp_show(struct device *dev,

350
drivers/hwmon/htu31.c Normal file
View File

@ -0,0 +1,350 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* The driver for Measurement Specialties HTU31 Temperature and Humidity sensor.
*
* Copyright (C) 2025
* Author: Andrei Lalaev <andrey.lalaev@gmail.com>
*/
#include <linux/array_size.h>
#include <linux/cleanup.h>
#include <linux/crc8.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/module.h>
#define HTU31_READ_TEMP_HUM_CMD 0x00
#define HTU31_READ_SERIAL_CMD 0x0a
#define HTU31_CONVERSION_CMD 0x5e
#define HTU31_HEATER_OFF_CMD 0x02
#define HTU31_HEATER_ON_CMD 0x04
#define HTU31_TEMP_HUM_LEN 6
/* Conversion time for the highest resolution */
#define HTU31_HUMIDITY_CONV_TIME 10000 /* us */
#define HTU31_TEMPERATURE_CONV_TIME 15000 /* us */
#define HTU31_SERIAL_NUMBER_LEN 3
#define HTU31_SERIAL_NUMBER_CRC_LEN 1
#define HTU31_SERIAL_NUMBER_CRC_OFFSET 3
#define HTU31_CRC8_INIT_VAL 0
#define HTU31_CRC8_POLYNOMIAL 0x31
DECLARE_CRC8_TABLE(htu31_crc8_table);
/**
* struct htu31_data - all the data required to operate a HTU31 chip
* @client: the i2c client associated with the HTU31
* @lock: a mutex to prevent parallel access to the data
* @wait_time: the time needed by sensor to convert values
* @temperature: the latest temperature value in millidegrees
* @humidity: the latest relative humidity value in millipercent
* @serial_number: the serial number of the sensor
* @heater_enable: the internal state of the heater
*/
struct htu31_data {
struct i2c_client *client;
struct mutex lock; /* Used to protect against parallel data updates */
long wait_time;
long temperature;
long humidity;
u8 serial_number[HTU31_SERIAL_NUMBER_LEN];
bool heater_enable;
};
static long htu31_temp_to_millicelsius(u16 val)
{
return -40000 + DIV_ROUND_CLOSEST_ULL(165000ULL * val, 65535);
}
static long htu31_relative_humidity(u16 val)
{
return DIV_ROUND_CLOSEST_ULL(100000ULL * val, 65535);
}
static int htu31_data_fetch_command(struct htu31_data *data)
{
struct i2c_client *client = data->client;
u8 conversion_on = HTU31_CONVERSION_CMD;
u8 read_data_cmd = HTU31_READ_TEMP_HUM_CMD;
u8 t_h_buf[HTU31_TEMP_HUM_LEN] = {};
struct i2c_msg msgs[] = {
{
.addr = client->addr,
.flags = 0,
.len = 1,
.buf = &read_data_cmd,
},
{
.addr = client->addr,
.flags = I2C_M_RD,
.len = sizeof(t_h_buf),
.buf = t_h_buf,
},
};
int ret;
u8 crc;
guard(mutex)(&data->lock);
ret = i2c_master_send(client, &conversion_on, 1);
if (ret != 1) {
ret = ret < 0 ? ret : -EIO;
dev_err(&client->dev,
"Conversion command is failed. Error code: %d\n", ret);
return ret;
}
fsleep(data->wait_time);
ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
if (ret != ARRAY_SIZE(msgs)) {
ret = ret < 0 ? ret : -EIO;
dev_err(&client->dev,
"T&H command is failed. Error code: %d\n", ret);
return ret;
}
crc = crc8(htu31_crc8_table, &t_h_buf[0], 2, HTU31_CRC8_INIT_VAL);
if (crc != t_h_buf[2]) {
dev_err(&client->dev, "Temperature CRC mismatch\n");
return -EIO;
}
crc = crc8(htu31_crc8_table, &t_h_buf[3], 2, HTU31_CRC8_INIT_VAL);
if (crc != t_h_buf[5]) {
dev_err(&client->dev, "Humidity CRC mismatch\n");
return -EIO;
}
data->temperature = htu31_temp_to_millicelsius(be16_to_cpup((__be16 *)&t_h_buf[0]));
data->humidity = htu31_relative_humidity(be16_to_cpup((__be16 *)&t_h_buf[3]));
return 0;
}
static umode_t htu31_is_visible(const void *data, enum hwmon_sensor_types type,
u32 attr, int channel)
{
switch (type) {
case hwmon_temp:
case hwmon_humidity:
return 0444;
default:
return 0;
}
}
static int htu31_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
struct htu31_data *data = dev_get_drvdata(dev);
int ret;
ret = htu31_data_fetch_command(data);
if (ret < 0)
return ret;
switch (type) {
case hwmon_temp:
if (attr != hwmon_temp_input)
return -EINVAL;
*val = data->temperature;
break;
case hwmon_humidity:
if (attr != hwmon_humidity_input)
return -EINVAL;
*val = data->humidity;
break;
default:
return -EOPNOTSUPP;
}
return 0;
}
static int htu31_read_serial_number(struct htu31_data *data)
{
struct i2c_client *client = data->client;
u8 read_sn_cmd = HTU31_READ_SERIAL_CMD;
u8 sn_buf[HTU31_SERIAL_NUMBER_LEN + HTU31_SERIAL_NUMBER_CRC_LEN];
struct i2c_msg msgs[] = {
{
.addr = client->addr,
.flags = 0,
.len = 1,
.buf = &read_sn_cmd,
},
{
.addr = client->addr,
.flags = I2C_M_RD,
.len = sizeof(sn_buf),
.buf = sn_buf,
},
};
int ret;
u8 crc;
ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
if (ret < 0)
return ret;
crc = crc8(htu31_crc8_table, sn_buf, HTU31_SERIAL_NUMBER_LEN, HTU31_CRC8_INIT_VAL);
if (crc != sn_buf[HTU31_SERIAL_NUMBER_CRC_OFFSET]) {
dev_err(&client->dev, "Serial number CRC mismatch\n");
return -EIO;
}
memcpy(data->serial_number, sn_buf, HTU31_SERIAL_NUMBER_LEN);
return 0;
}
static ssize_t heater_enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct htu31_data *data = dev_get_drvdata(dev);
return sysfs_emit(buf, "%d\n", data->heater_enable);
}
static ssize_t heater_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
struct htu31_data *data = dev_get_drvdata(dev);
u8 heater_cmd;
bool status;
int ret;
ret = kstrtobool(buf, &status);
if (ret)
return ret;
heater_cmd = status ? HTU31_HEATER_ON_CMD : HTU31_HEATER_OFF_CMD;
guard(mutex)(&data->lock);
ret = i2c_master_send(data->client, &heater_cmd, 1);
if (ret < 0)
return ret;
data->heater_enable = status;
return count;
}
static DEVICE_ATTR_RW(heater_enable);
static int serial_number_show(struct seq_file *seq_file,
void *unused)
{
struct htu31_data *data = seq_file->private;
seq_printf(seq_file, "%X%X%X\n", data->serial_number[0],
data->serial_number[1], data->serial_number[2]);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(serial_number);
static struct attribute *htu31_attrs[] = {
&dev_attr_heater_enable.attr,
NULL
};
ATTRIBUTE_GROUPS(htu31);
static const struct hwmon_channel_info * const htu31_info[] = {
HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
HWMON_CHANNEL_INFO(humidity, HWMON_H_INPUT),
NULL
};
static const struct hwmon_ops htu31_hwmon_ops = {
.is_visible = htu31_is_visible,
.read = htu31_read,
};
static const struct hwmon_chip_info htu31_chip_info = {
.info = htu31_info,
.ops = &htu31_hwmon_ops,
};
static int htu31_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct device *hwmon_dev;
struct htu31_data *data;
int ret;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->client = client;
data->wait_time = HTU31_TEMPERATURE_CONV_TIME + HTU31_HUMIDITY_CONV_TIME;
ret = devm_mutex_init(dev, &data->lock);
if (ret)
return ret;
crc8_populate_msb(htu31_crc8_table, HTU31_CRC8_POLYNOMIAL);
ret = htu31_read_serial_number(data);
if (ret) {
dev_err(dev, "Failed to read serial number\n");
return ret;
}
debugfs_create_file("serial_number",
0444,
client->debugfs,
data,
&serial_number_fops);
hwmon_dev = devm_hwmon_device_register_with_info(dev,
client->name,
data,
&htu31_chip_info,
htu31_groups);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
static const struct i2c_device_id htu31_id[] = {
{ "htu31" },
{ }
};
MODULE_DEVICE_TABLE(i2c, htu31_id);
#if IS_ENABLED(CONFIG_OF)
static const struct of_device_id htu31_of_match[] = {
{ .compatible = "meas,htu31" },
{ }
};
MODULE_DEVICE_TABLE(of, htu31_of_match);
#endif
static struct i2c_driver htu31_driver = {
.driver = {
.name = "htu31",
.of_match_table = of_match_ptr(htu31_of_match),
},
.probe = htu31_probe,
.id_table = htu31_id,
};
module_i2c_driver(htu31_driver);
MODULE_AUTHOR("Andrei Lalaev <andrey.lalaev@gmail.com>");
MODULE_DESCRIPTION("HTU31 Temperature and Humidity sensor driver");
MODULE_LICENSE("GPL");

View File

@ -646,8 +646,8 @@ static const char * const hwmon_power_attr_templates[] = {
[hwmon_power_enable] = "power%d_enable",
[hwmon_power_average] = "power%d_average",
[hwmon_power_average_interval] = "power%d_average_interval",
[hwmon_power_average_interval_max] = "power%d_interval_max",
[hwmon_power_average_interval_min] = "power%d_interval_min",
[hwmon_power_average_interval_max] = "power%d_average_interval_max",
[hwmon_power_average_interval_min] = "power%d_average_interval_min",
[hwmon_power_average_highest] = "power%d_average_highest",
[hwmon_power_average_lowest] = "power%d_average_lowest",
[hwmon_power_average_max] = "power%d_average_max",

View File

@ -116,7 +116,6 @@ struct ina3221_input {
* @fields: Register fields of the device
* @inputs: Array of channel input source specific structures
* @lock: mutex lock to serialize sysfs attribute accesses
* @debugfs: Pointer to debugfs entry for device
* @reg_config: Register value of INA3221_CONFIG
* @summation_shunt_resistor: equivalent shunt resistor value for summation
* @summation_channel_control: Value written to SCC field in INA3221_MASK_ENABLE
@ -128,7 +127,6 @@ struct ina3221_data {
struct regmap_field *fields[F_MAX_FIELDS];
struct ina3221_input inputs[INA3221_NUM_CHANNELS];
struct mutex lock;
struct dentry *debugfs;
u32 reg_config;
int summation_shunt_resistor;
u32 summation_channel_control;
@ -913,12 +911,9 @@ static int ina3221_probe(struct i2c_client *client)
goto fail;
}
scnprintf(name, sizeof(name), "%s-%s", INA3221_DRIVER_NAME, dev_name(dev));
ina->debugfs = debugfs_create_dir(name, NULL);
for (i = 0; i < INA3221_NUM_CHANNELS; i++) {
scnprintf(name, sizeof(name), "in%d_summation_disable", i);
debugfs_create_bool(name, 0400, ina->debugfs,
debugfs_create_bool(name, 0400, client->debugfs,
&ina->inputs[i].summation_disable);
}
@ -940,8 +935,6 @@ static void ina3221_remove(struct i2c_client *client)
struct ina3221_data *ina = dev_get_drvdata(&client->dev);
int i;
debugfs_remove_recursive(ina->debugfs);
pm_runtime_disable(ina->pm_dev);
pm_runtime_set_suspended(ina->pm_dev);

View File

@ -324,26 +324,6 @@ static int shunt_voltage_show(struct seq_file *seqf, void *unused)
}
DEFINE_SHOW_ATTRIBUTE(shunt_voltage);
static struct dentry *isl28022_debugfs_root;
static void isl28022_debugfs_remove(void *res)
{
debugfs_remove_recursive(res);
}
static void isl28022_debugfs_init(struct i2c_client *client, struct isl28022_data *data)
{
char name[16];
struct dentry *debugfs;
scnprintf(name, sizeof(name), "%d-%04hx", client->adapter->nr, client->addr);
debugfs = debugfs_create_dir(name, isl28022_debugfs_root);
debugfs_create_file("shunt_voltage", 0444, debugfs, data, &shunt_voltage_fops);
devm_add_action_or_reset(&client->dev, isl28022_debugfs_remove, debugfs);
}
/*
* read property values and make consistency checks.
*
@ -475,7 +455,7 @@ static int isl28022_probe(struct i2c_client *client)
if (err)
return err;
isl28022_debugfs_init(client, data);
debugfs_create_file("shunt_voltage", 0444, client->debugfs, data, &shunt_voltage_fops);
hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
data, &isl28022_chip_info, NULL);
@ -505,27 +485,7 @@ static struct i2c_driver isl28022_driver = {
.probe = isl28022_probe,
.id_table = isl28022_ids,
};
static int __init isl28022_init(void)
{
int err;
isl28022_debugfs_root = debugfs_create_dir("isl28022", NULL);
err = i2c_add_driver(&isl28022_driver);
if (!err)
return 0;
debugfs_remove_recursive(isl28022_debugfs_root);
return err;
}
module_init(isl28022_init);
static void __exit isl28022_exit(void)
{
i2c_del_driver(&isl28022_driver);
debugfs_remove_recursive(isl28022_debugfs_root);
}
module_exit(isl28022_exit);
module_i2c_driver(isl28022_driver);
MODULE_AUTHOR("Carsten Spieß <mail@carsten-spiess.de>");
MODULE_DESCRIPTION("ISL28022 driver");

View File

@ -467,6 +467,7 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
k10temp_get_ccd_support(data, 4);
break;
case 0x31: /* Zen2 Threadripper */
case 0x47: /* Cyan Skillfish */
case 0x60: /* Renoir */
case 0x68: /* Lucienne */
case 0x71: /* Zen2 */
@ -535,6 +536,7 @@ static const struct pci_device_id k10temp_id_table[] = {
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_DF_F3) },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M10H_DF_F3) },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M30H_DF_F3) },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M40H_DF_F3) },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M60H_DF_F3) },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M70H_DF_F3) },
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_MA0H_DF_F3) },

View File

@ -90,6 +90,9 @@
* This driver also supports NE1618 from Philips. It is similar to NE1617
* but supports 11 bit external temperature values.
*
* This driver also supports NCT7716, NCT7717 and NCT7718 from Nuvoton.
* The NCT7716 is similar to NCT7717 but has one more address support.
*
* Since the LM90 was the first chipset supported by this driver, most
* comments will refer to this chipset, but are actually general and
* concern all supported chipsets, unless mentioned otherwise.
@ -119,13 +122,15 @@
* Address is fully defined internally and cannot be changed except for
* MAX6659, MAX6680 and MAX6681.
* LM86, LM89, LM90, LM99, ADM1032, ADM1032-1, ADT7461, ADT7461A, MAX6649,
* MAX6657, MAX6658, NCT1008 and W83L771 have address 0x4c.
* MAX6657, MAX6658, NCT1008, NCT7718 and W83L771 have address 0x4c.
* ADM1032-2, ADT7461-2, ADT7461A-2, LM89-1, LM99-1, MAX6646, and NCT1008D
* have address 0x4d.
* MAX6647 has address 0x4e.
* MAX6659 can have address 0x4c, 0x4d or 0x4e.
* MAX6654, MAX6680, and MAX6681 can have address 0x18, 0x19, 0x1a, 0x29,
* 0x2a, 0x2b, 0x4c, 0x4d or 0x4e.
* NCT7716 can have address 0x48 or 0x49.
* NCT7717 has address 0x48.
* SA56004 can have address 0x48 through 0x4F.
*/
@ -136,7 +141,7 @@ static const unsigned short normal_i2c[] = {
enum chips { adm1023, adm1032, adt7461, adt7461a, adt7481,
g781, lm84, lm90, lm99,
max1617, max6642, max6646, max6648, max6654, max6657, max6659, max6680, max6696,
nct210, nct72, ne1618, sa56004, tmp451, tmp461, w83l771,
nct210, nct72, nct7716, nct7717, nct7718, ne1618, sa56004, tmp451, tmp461, w83l771,
};
/*
@ -191,6 +196,9 @@ enum chips { adm1023, adm1032, adt7461, adt7461a, adt7481,
#define ADT7481_REG_MAN_ID 0x3e
#define ADT7481_REG_CHIP_ID 0x3d
/* NCT7716/7717/7718 registers */
#define NCT7716_REG_CHIP_ID 0xFD
/* Device features */
#define LM90_HAVE_EXTENDED_TEMP BIT(0) /* extended temperature support */
#define LM90_HAVE_OFFSET BIT(1) /* temperature offset register */
@ -275,6 +283,9 @@ static const struct i2c_device_id lm90_id[] = {
{ "nct214", nct72 },
{ "nct218", nct72 },
{ "nct72", nct72 },
{ "nct7716", nct7716 },
{ "nct7717", nct7717 },
{ "nct7718", nct7718 },
{ "ne1618", ne1618 },
{ "w83l771", w83l771 },
{ "sa56004", sa56004 },
@ -382,6 +393,18 @@ static const struct of_device_id __maybe_unused lm90_of_match[] = {
.compatible = "onnn,nct72",
.data = (void *)nct72
},
{
.compatible = "nuvoton,nct7716",
.data = (void *)nct7716
},
{
.compatible = "nuvoton,nct7717",
.data = (void *)nct7717
},
{
.compatible = "nuvoton,nct7718",
.data = (void *)nct7718
},
{
.compatible = "winbond,w83l771",
.data = (void *)w83l771
@ -601,6 +624,26 @@ static const struct lm90_params lm90_params[] = {
.resolution = 11,
.max_convrate = 7,
},
[nct7716] = {
.flags = LM90_HAVE_ALARMS | LM90_HAVE_CONVRATE,
.alert_alarms = 0x40,
.resolution = 8,
.max_convrate = 8,
},
[nct7717] = {
.flags = LM90_HAVE_ALARMS | LM90_HAVE_CONVRATE,
.alert_alarms = 0x40,
.resolution = 8,
.max_convrate = 8,
},
[nct7718] = {
.flags = LM90_HAVE_OFFSET | LM90_HAVE_REM_LIMIT_EXT | LM90_HAVE_CRIT
| LM90_HAVE_ALARMS | LM90_HAVE_LOW | LM90_HAVE_CONVRATE
| LM90_HAVE_REMOTE_EXT,
.alert_alarms = 0x7c,
.resolution = 11,
.max_convrate = 8,
},
[ne1618] = {
.flags = LM90_PAUSE_FOR_CONFIG | LM90_HAVE_BROKEN_ALERT
| LM90_HAVE_LOW | LM90_HAVE_CONVRATE | LM90_HAVE_REMOTE_EXT,
@ -2300,6 +2343,38 @@ static const char *lm90_detect_nuvoton(struct i2c_client *client, int chip_id,
return name;
}
static const char *lm90_detect_nuvoton_50(struct i2c_client *client, int chip_id,
int config1, int convrate)
{
int chip_id2 = i2c_smbus_read_byte_data(client, NCT7716_REG_CHIP_ID);
int config2 = i2c_smbus_read_byte_data(client, LM90_REG_CONFIG2);
int address = client->addr;
const char *name = NULL;
if (chip_id2 < 0 || config2 < 0)
return NULL;
if (chip_id2 != 0x50 || convrate > 0x08)
return NULL;
switch (chip_id) {
case 0x90:
if (address == 0x48 && !(config1 & 0x3e) && !(config2 & 0xfe))
name = "nct7717";
break;
case 0x91:
if ((address == 0x48 || address == 0x49) && !(config1 & 0x3e) &&
!(config2 & 0xfe))
name = "nct7716";
else if (address == 0x4c && !(config1 & 0x38) && !(config2 & 0xf8))
name = "nct7718";
break;
default:
break;
}
return name;
}
static const char *lm90_detect_nxp(struct i2c_client *client, bool common_address,
int chip_id, int config1, int convrate)
{
@ -2484,6 +2559,9 @@ static int lm90_detect(struct i2c_client *client, struct i2c_board_info *info)
name = lm90_detect_maxim(client, common_address, chip_id,
config1, convrate);
break;
case 0x50:
name = lm90_detect_nuvoton_50(client, chip_id, config1, convrate);
break;
case 0x54: /* ON MC1066, Microchip TC1068, TCM1617 (originally TelCom) */
if (common_address && !(config1 & 0x3f) && !(convrate & 0xf8))
name = "mc1066";

View File

@ -1674,47 +1674,19 @@ static int ltc4282_show_power1_bad_fault_log(void *arg, u64 *val)
DEFINE_DEBUGFS_ATTRIBUTE(ltc4282_power1_bad_fault_log,
ltc4282_show_power1_bad_fault_log, NULL, "%llu\n");
static void ltc4282_debugfs_remove(void *dir)
static void ltc4282_debugfs_init(struct ltc4282_state *st, struct i2c_client *i2c)
{
debugfs_remove_recursive(dir);
}
static void ltc4282_debugfs_init(struct ltc4282_state *st,
struct i2c_client *i2c,
const struct device *hwmon)
{
const char *debugfs_name;
struct dentry *dentry;
int ret;
if (!IS_ENABLED(CONFIG_DEBUG_FS))
return;
debugfs_name = devm_kasprintf(&i2c->dev, GFP_KERNEL, "ltc4282-%s",
dev_name(hwmon));
if (!debugfs_name)
return;
dentry = debugfs_create_dir(debugfs_name, NULL);
if (IS_ERR(dentry))
return;
ret = devm_add_action_or_reset(&i2c->dev, ltc4282_debugfs_remove,
dentry);
if (ret)
return;
debugfs_create_file_unsafe("power1_bad_fault_log", 0400, dentry, st,
debugfs_create_file_unsafe("power1_bad_fault_log", 0400, i2c->debugfs, st,
&ltc4282_power1_bad_fault_log);
debugfs_create_file_unsafe("in0_fet_short_fault_log", 0400, dentry, st,
debugfs_create_file_unsafe("in0_fet_short_fault_log", 0400, i2c->debugfs, st,
&ltc4282_fet_short_fault_log);
debugfs_create_file_unsafe("in0_fet_bad_fault_log", 0400, dentry, st,
debugfs_create_file_unsafe("in0_fet_bad_fault_log", 0400, i2c->debugfs, st,
&ltc4282_fet_bad_fault_log);
debugfs_create_file_unsafe("in1_crit_fault_log", 0400, dentry, st,
debugfs_create_file_unsafe("in1_crit_fault_log", 0400, i2c->debugfs, st,
&ltc4282_in1_crit_fault_log);
debugfs_create_file_unsafe("in1_lcrit_fault_log", 0400, dentry, st,
debugfs_create_file_unsafe("in1_lcrit_fault_log", 0400, i2c->debugfs, st,
&ltc4282_in1_lcrit_fault_log);
debugfs_create_file_unsafe("curr1_crit_fault_log", 0400, dentry, st,
debugfs_create_file_unsafe("curr1_crit_fault_log", 0400, i2c->debugfs, st,
&ltc4282_curr1_crit_fault_log);
}
@ -1757,7 +1729,7 @@ static int ltc4282_probe(struct i2c_client *i2c)
if (IS_ERR(hwmon))
return PTR_ERR(hwmon);
ltc4282_debugfs_init(st, i2c, hwmon);
ltc4282_debugfs_init(st, i2c);
return 0;
}

View File

@ -176,6 +176,7 @@ superio_exit(int ioreg)
#define NCT6683_CUSTOMER_ID_MSI2 0x200
#define NCT6683_CUSTOMER_ID_MSI3 0x207
#define NCT6683_CUSTOMER_ID_MSI4 0x20d
#define NCT6683_CUSTOMER_ID_AMD 0x162b
#define NCT6683_CUSTOMER_ID_ASROCK 0xe2c
#define NCT6683_CUSTOMER_ID_ASROCK2 0xe1b
#define NCT6683_CUSTOMER_ID_ASROCK3 0x1631
@ -1231,6 +1232,8 @@ static int nct6683_probe(struct platform_device *pdev)
break;
case NCT6683_CUSTOMER_ID_MSI4:
break;
case NCT6683_CUSTOMER_ID_AMD:
break;
case NCT6683_CUSTOMER_ID_ASROCK:
break;
case NCT6683_CUSTOMER_ID_ASROCK2:

View File

@ -387,12 +387,9 @@ static int get_ohm_of_thermistor(struct ntc_data *data, unsigned int uv)
puo = data->pullup_ohm;
pdo = data->pulldown_ohm;
if (uv == 0)
return (data->connect == NTC_CONNECTED_POSITIVE) ?
INT_MAX : 0;
if (uv >= puv)
return (data->connect == NTC_CONNECTED_POSITIVE) ?
0 : INT_MAX;
/* faulty adc value */
if (uv == 0 || uv >= puv)
return -ENODATA;
if (data->connect == NTC_CONNECTED_POSITIVE && puo == 0)
n = div_u64(pdo * (puv - uv), uv);
@ -404,8 +401,10 @@ static int get_ohm_of_thermistor(struct ntc_data *data, unsigned int uv)
else
n = div64_u64_safe(pdo * puo * uv, pdo * (puv - uv) - puo * uv);
if (n > INT_MAX)
n = INT_MAX;
/* sensor out of bounds */
if (n > data->comp[0].ohm || n < data->comp[data->n_comp - 1].ohm)
return -ENODATA;
return n;
}

View File

@ -133,6 +133,15 @@ config SENSORS_DPS920AB
This driver can also be built as a module. If so, the module will
be called dps920ab.
config SENSORS_INA233
tristate "Texas Instruments INA233 and compatibles"
help
If you say yes here you get hardware monitoring support for Texas
Instruments INA233.
This driver can also be built as a module. If so, the module will
be called ina233.
config SENSORS_INSPUR_IPSPS
tristate "INSPUR Power System Power Supply"
help
@ -233,9 +242,9 @@ config SENSORS_LTC2978_REGULATOR
depends on SENSORS_LTC2978 && REGULATOR
help
If you say yes here you get regulator support for Linear Technology
LTC3880, LTC3883, LTC3884, LTC3886, LTC3887, LTC3889, LTC7841,
LTC7880, LTM4644, LTM4675, LTM4676, LTM4677, LTM4678, LTM4680,
LTM4686, and LTM4700.
LT7170, LT7171, LTC3880, LTC3883, LTC3884, LTC3886, LTC3887, LTC3889,
LTC7841, LTC7880, LTM4644, LTM4673, LTM4675, LTM4676, LTM4677,
LTM4678, LTM4680, LTM4686, and LTM4700.
config SENSORS_LTC3815
tristate "Linear Technologies LTC3815"

View File

@ -15,6 +15,7 @@ obj-$(CONFIG_SENSORS_DELTA_AHE50DC_FAN) += delta-ahe50dc-fan.o
obj-$(CONFIG_SENSORS_FSP_3Y) += fsp-3y.o
obj-$(CONFIG_SENSORS_IBM_CFFPS) += ibm-cffps.o
obj-$(CONFIG_SENSORS_DPS920AB) += dps920ab.o
obj-$(CONFIG_SENSORS_INA233) += ina233.o
obj-$(CONFIG_SENSORS_INSPUR_IPSPS) += inspur-ipsps.o
obj-$(CONFIG_SENSORS_IR35221) += ir35221.o
obj-$(CONFIG_SENSORS_IR36021) += ir36021.o

View File

@ -0,0 +1,191 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Hardware monitoring driver for ina233
*
* Copyright (c) 2025 Leo Yang
*/
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include "pmbus.h"
#define MFR_READ_VSHUNT 0xd1
#define MFR_CALIBRATION 0xd4
#define INA233_MAX_CURRENT_DEFAULT 32768000 /* uA */
#define INA233_RSHUNT_DEFAULT 2000 /* uOhm */
#define MAX_M_VAL 32767
static void calculate_coef(int *m, int *R, u32 current_lsb, int power_coef)
{
u64 scaled_m;
int scale_factor = 0;
int scale_coef = 1;
/*
* 1000000 from Current_LSB A->uA .
* scale_coef is for scaling up to minimize rounding errors,
* If there is no decimal information, no need to scale.
*/
if (1000000 % current_lsb) {
/* Scaling to keep integer precision */
scale_factor = -3;
scale_coef = 1000;
}
/*
* Unit Conversion (Current_LSB A->uA) and use scaling(scale_factor)
* to keep integer precision.
* Formulae referenced from spec.
*/
scaled_m = div64_u64(1000000 * scale_coef, (u64)current_lsb * power_coef);
/* Maximize while keeping it bounded.*/
while (scaled_m > MAX_M_VAL) {
scaled_m = div_u64(scaled_m, 10);
scale_factor++;
}
/* Scale up only if fractional part exists. */
while (scaled_m * 10 < MAX_M_VAL && scale_coef != 1) {
scaled_m *= 10;
scale_factor--;
}
*m = scaled_m;
*R = scale_factor;
}
static int ina233_read_word_data(struct i2c_client *client, int page,
int phase, int reg)
{
int ret;
switch (reg) {
case PMBUS_VIRT_READ_VMON:
ret = pmbus_read_word_data(client, 0, 0xff, MFR_READ_VSHUNT);
/* Adjust returned value to match VIN coefficients */
/* VIN: 1.25 mV VSHUNT: 2.5 uV LSB */
ret = DIV_ROUND_CLOSEST(ret * 25, 12500);
break;
default:
ret = -ENODATA;
break;
}
return ret;
}
static int ina233_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
int ret, m, R;
u32 rshunt;
u32 max_current;
u32 current_lsb;
u16 calibration;
struct pmbus_driver_info *info;
info = devm_kzalloc(dev, sizeof(struct pmbus_driver_info),
GFP_KERNEL);
if (!info)
return -ENOMEM;
info->pages = 1;
info->format[PSC_VOLTAGE_IN] = direct;
info->format[PSC_VOLTAGE_OUT] = direct;
info->format[PSC_CURRENT_OUT] = direct;
info->format[PSC_POWER] = direct;
info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_INPUT
| PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
| PMBUS_HAVE_POUT
| PMBUS_HAVE_VMON | PMBUS_HAVE_STATUS_VMON;
info->m[PSC_VOLTAGE_IN] = 8;
info->R[PSC_VOLTAGE_IN] = 2;
info->m[PSC_VOLTAGE_OUT] = 8;
info->R[PSC_VOLTAGE_OUT] = 2;
info->read_word_data = ina233_read_word_data;
/* If INA233 skips current/power, shunt-resistor and current-lsb aren't needed. */
/* read rshunt value (uOhm) */
ret = device_property_read_u32(dev, "shunt-resistor", &rshunt);
if (ret) {
if (ret != -EINVAL)
return dev_err_probe(dev, ret, "Shunt resistor property read fail.\n");
rshunt = INA233_RSHUNT_DEFAULT;
}
if (!rshunt)
return dev_err_probe(dev, -EINVAL,
"Shunt resistor cannot be zero.\n");
/* read Maximum expected current value (uA) */
ret = device_property_read_u32(dev, "ti,maximum-expected-current-microamp", &max_current);
if (ret) {
if (ret != -EINVAL)
return dev_err_probe(dev, ret,
"Maximum expected current property read fail.\n");
max_current = INA233_MAX_CURRENT_DEFAULT;
}
if (max_current < 32768)
return dev_err_probe(dev, -EINVAL,
"Maximum expected current cannot less then 32768.\n");
/* Calculate Current_LSB according to the spec formula */
current_lsb = max_current / 32768;
/* calculate current coefficient */
calculate_coef(&m, &R, current_lsb, 1);
info->m[PSC_CURRENT_OUT] = m;
info->R[PSC_CURRENT_OUT] = R;
/* calculate power coefficient */
calculate_coef(&m, &R, current_lsb, 25);
info->m[PSC_POWER] = m;
info->R[PSC_POWER] = R;
/* write MFR_CALIBRATION register, Apply formula from spec with unit scaling. */
calibration = div64_u64(5120000000ULL, (u64)rshunt * current_lsb);
if (calibration > 0x7FFF)
return dev_err_probe(dev, -EINVAL,
"Product of Current_LSB %u and shunt resistor %u too small, MFR_CALIBRATION reg exceeds 0x7FFF.\n",
current_lsb, rshunt);
ret = i2c_smbus_write_word_data(client, MFR_CALIBRATION, calibration);
if (ret < 0)
return dev_err_probe(dev, ret, "Unable to write calibration.\n");
dev_dbg(dev, "power monitor %s (Rshunt = %u uOhm, Current_LSB = %u uA/bit)\n",
client->name, rshunt, current_lsb);
return pmbus_do_probe(client, info);
}
static const struct i2c_device_id ina233_id[] = {
{"ina233", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, ina233_id);
static const struct of_device_id __maybe_unused ina233_of_match[] = {
{ .compatible = "ti,ina233" },
{}
};
MODULE_DEVICE_TABLE(of, ina233_of_match);
static struct i2c_driver ina233_driver = {
.driver = {
.name = "ina233",
.of_match_table = of_match_ptr(ina233_of_match),
},
.probe = ina233_probe,
.id_table = ina233_id,
};
module_i2c_driver(ina233_driver);
MODULE_AUTHOR("Leo Yang <leo.yang.sy0@gmail.com>");
MODULE_DESCRIPTION("PMBus driver for INA233 and compatible chips");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS("PMBUS");

View File

@ -23,11 +23,11 @@ enum chips {
/* Managers */
ltc2972, ltc2974, ltc2975, ltc2977, ltc2978, ltc2979, ltc2980,
/* Controllers */
ltc3880, ltc3882, ltc3883, ltc3884, ltc3886, ltc3887, ltc3889, ltc7132,
ltc7841, ltc7880,
lt7170, lt7171, ltc3880, ltc3882, ltc3883, ltc3884, ltc3886, ltc3887,
ltc3889, ltc7132, ltc7841, ltc7880,
/* Modules */
ltm2987, ltm4664, ltm4675, ltm4676, ltm4677, ltm4678, ltm4680, ltm4686,
ltm4700,
ltm2987, ltm4664, ltm4673, ltm4675, ltm4676, ltm4677, ltm4678, ltm4680,
ltm4686, ltm4700,
};
/* Common for all chips */
@ -62,6 +62,7 @@ enum chips {
#define LTC2978_ID_MASK 0xfff0
#define LT7170_ID 0x1C10
#define LTC2972_ID 0x0310
#define LTC2974_ID 0x0210
#define LTC2975_ID 0x0220
@ -86,6 +87,8 @@ enum chips {
#define LTM2987_ID_A 0x8010 /* A/B for two die IDs */
#define LTM2987_ID_B 0x8020
#define LTM4664_ID 0x4120
#define LTM4673_ID_REV1 0x0230
#define LTM4673_ID 0x4480
#define LTM4675_ID 0x47a0
#define LTM4676_ID_REV1 0x4400
#define LTM4676_ID_REV2 0x4480
@ -535,6 +538,8 @@ static int ltc2978_write_word_data(struct i2c_client *client, int page,
}
static const struct i2c_device_id ltc2978_id[] = {
{"lt7170", lt7170},
{"lt7171", lt7171},
{"ltc2972", ltc2972},
{"ltc2974", ltc2974},
{"ltc2975", ltc2975},
@ -554,6 +559,7 @@ static const struct i2c_device_id ltc2978_id[] = {
{"ltc7880", ltc7880},
{"ltm2987", ltm2987},
{"ltm4664", ltm4664},
{"ltm4673", ltm4673},
{"ltm4675", ltm4675},
{"ltm4676", ltm4676},
{"ltm4677", ltm4677},
@ -612,7 +618,7 @@ static int ltc2978_get_id(struct i2c_client *client)
ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf);
if (ret < 0)
return ret;
if (ret < 3 || strncmp(buf, "LTC", 3))
if (ret < 3 || (strncmp(buf, "LTC", 3) && strncmp(buf, "ADI", 3)))
return -ENODEV;
ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf);
@ -627,6 +633,25 @@ static int ltc2978_get_id(struct i2c_client *client)
chip_id &= LTC2978_ID_MASK;
if (chip_id == LT7170_ID) {
u8 buf[I2C_SMBUS_BLOCK_MAX];
int ret;
ret = i2c_smbus_read_i2c_block_data(client, PMBUS_IC_DEVICE_ID,
sizeof(buf), buf);
if (ret < 0)
return ret;
if (!strncmp(buf + 1, "LT7170", 6) ||
!strncmp(buf + 1, "LT7170-1", 8))
return lt7170;
if (!strncmp(buf + 1, "LT7171", 6) ||
!strncmp(buf + 1, "LT7171-1", 8))
return lt7171;
return -ENODEV;
}
if (chip_id == LTC2972_ID)
return ltc2972;
else if (chip_id == LTC2974_ID)
@ -665,6 +690,8 @@ static int ltc2978_get_id(struct i2c_client *client)
return ltm2987;
else if (chip_id == LTM4664_ID)
return ltm4664;
else if (chip_id == LTM4673_ID || chip_id == LTM4673_ID_REV1)
return ltm4673;
else if (chip_id == LTM4675_ID)
return ltm4675;
else if (chip_id == LTM4676_ID_REV1 || chip_id == LTM4676_ID_REV2 ||
@ -736,6 +763,20 @@ static int ltc2978_probe(struct i2c_client *client)
data->temp2_max = 0x7c00;
switch (data->id) {
case lt7170:
case lt7171:
data->features |= FEAT_CLEAR_PEAKS | FEAT_NEEDS_POLLING;
info->read_word_data = ltc3883_read_word_data;
info->pages = LTC3883_NUM_PAGES;
info->format[PSC_VOLTAGE_IN] = ieee754;
info->format[PSC_VOLTAGE_OUT] = ieee754;
info->format[PSC_CURRENT_OUT] = ieee754;
info->format[PSC_TEMPERATURE] = ieee754;
info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT
| PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
| PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
| PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
break;
case ltc2972:
info->read_word_data = ltc2975_read_word_data;
info->pages = LTC2972_NUM_PAGES;
@ -869,6 +910,21 @@ static int ltc2978_probe(struct i2c_client *client)
| PMBUS_HAVE_IOUT
| PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
break;
case ltm4673:
data->features |= FEAT_NEEDS_POLLING;
info->read_word_data = ltc2975_read_word_data;
info->pages = LTC2974_NUM_PAGES;
info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT
| PMBUS_HAVE_TEMP2;
for (i = 0; i < info->pages; i++) {
info->func[i] |= PMBUS_HAVE_IIN
| PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
| PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
| PMBUS_HAVE_PIN
| PMBUS_HAVE_POUT
| PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
}
break;
default:
return -ENODEV;
}
@ -907,6 +963,8 @@ static int ltc2978_probe(struct i2c_client *client)
#ifdef CONFIG_OF
static const struct of_device_id ltc2978_of_match[] = {
{ .compatible = "lltc,lt7170" },
{ .compatible = "lltc,lt7171" },
{ .compatible = "lltc,ltc2972" },
{ .compatible = "lltc,ltc2974" },
{ .compatible = "lltc,ltc2975" },
@ -926,6 +984,7 @@ static const struct of_device_id ltc2978_of_match[] = {
{ .compatible = "lltc,ltc7880" },
{ .compatible = "lltc,ltm2987" },
{ .compatible = "lltc,ltm4664" },
{ .compatible = "lltc,ltm4673" },
{ .compatible = "lltc,ltm4675" },
{ .compatible = "lltc,ltm4676" },
{ .compatible = "lltc,ltm4677" },

View File

@ -8,6 +8,7 @@
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/dcache.h>
#include <linux/kernel.h>
#include <linux/math64.h>
#include <linux/module.h>
@ -44,8 +45,7 @@ struct pmbus_sensor {
enum pmbus_sensor_classes class; /* sensor class */
bool update; /* runtime sensor update needed */
bool convert; /* Whether or not to apply linear/vid/direct */
int data; /* Sensor data.
Negative if there was a read error */
int data; /* Sensor data; negative if there was a read error */
};
#define to_pmbus_sensor(_attr) \
container_of(_attr, struct pmbus_sensor, attribute)
@ -100,7 +100,6 @@ struct pmbus_data {
int num_attributes;
struct attribute_group group;
const struct attribute_group **groups;
struct dentry *debugfs; /* debugfs device directory */
struct pmbus_sensor *sensors;
@ -192,11 +191,10 @@ static void pmbus_update_ts(struct i2c_client *client, bool write_op)
struct pmbus_data *data = i2c_get_clientdata(client);
const struct pmbus_driver_info *info = data->info;
if (info->access_delay) {
if (info->access_delay)
data->access_time = ktime_get();
} else if (info->write_delay && write_op) {
else if (info->write_delay && write_op)
data->write_time = ktime_get();
}
}
int pmbus_set_page(struct i2c_client *client, int page, int phase)
@ -292,7 +290,6 @@ int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg,
}
EXPORT_SYMBOL_NS_GPL(pmbus_write_word_data, "PMBUS");
static int pmbus_write_virt_reg(struct i2c_client *client, int page, int reg,
u16 word)
{
@ -381,14 +378,14 @@ int pmbus_update_fan(struct i2c_client *client, int page, int id,
u8 to;
from = _pmbus_read_byte_data(client, page,
pmbus_fan_config_registers[id]);
pmbus_fan_config_registers[id]);
if (from < 0)
return from;
to = (from & ~mask) | (config & mask);
if (to != from) {
rv = _pmbus_write_byte_data(client, page,
pmbus_fan_config_registers[id], to);
pmbus_fan_config_registers[id], to);
if (rv < 0)
return rv;
}
@ -563,7 +560,7 @@ static int pmbus_get_fan_rate(struct i2c_client *client, int page, int id,
}
config = _pmbus_read_byte_data(client, page,
pmbus_fan_config_registers[id]);
pmbus_fan_config_registers[id]);
if (config < 0)
return config;
@ -788,7 +785,7 @@ static s64 pmbus_reg2data_linear(struct pmbus_data *data,
if (sensor->class == PSC_VOLTAGE_OUT) { /* LINEAR16 */
exponent = data->exponent[sensor->page];
mantissa = (u16) sensor->data;
mantissa = (u16)sensor->data;
} else { /* LINEAR11 */
exponent = ((s16)sensor->data) >> 11;
mantissa = ((s16)((sensor->data & 0x7ff) << 5)) >> 5;
@ -1173,7 +1170,6 @@ static int pmbus_get_boolean(struct i2c_client *client, struct pmbus_boolean *b,
} else {
pmbus_clear_fault_page(client, page);
}
}
if (s1 && s2) {
s64 v1, v2;
@ -1470,8 +1466,7 @@ static int pmbus_add_label(struct pmbus_data *data,
snprintf(label->name, sizeof(label->name), "%s%d_label", name, seq);
if (!index) {
if (phase == 0xff)
strncpy(label->label, lstring,
sizeof(label->label) - 1);
strscpy(label->label, lstring);
else
snprintf(label->label, sizeof(label->label), "%s.%d",
lstring, phase);
@ -1500,8 +1495,7 @@ struct pmbus_limit_attr {
u16 reg; /* Limit register */
u16 sbit; /* Alarm attribute status bit */
bool update; /* True if register needs updates */
bool low; /* True if low limit; for limits with compare
functions only */
bool low; /* True if low limit; for limits with compare functions only */
const char *attr; /* Attribute name */
const char *alarm; /* Alarm attribute name */
};
@ -2212,8 +2206,8 @@ static const u32 pmbus_fan_status_flags[] = {
/* Precondition: FAN_CONFIG_x_y and FAN_COMMAND_x must exist for the fan ID */
static int pmbus_add_fan_ctrl(struct i2c_client *client,
struct pmbus_data *data, int index, int page, int id,
u8 config)
struct pmbus_data *data, int index, int page,
int id, u8 config)
{
struct pmbus_sensor *sensor;
@ -2225,7 +2219,7 @@ static int pmbus_add_fan_ctrl(struct i2c_client *client,
return -ENOMEM;
if (!((data->info->func[page] & PMBUS_HAVE_PWM12) ||
(data->info->func[page] & PMBUS_HAVE_PWM34)))
(data->info->func[page] & PMBUS_HAVE_PWM34)))
return 0;
sensor = pmbus_add_sensor(data, "pwm", NULL, index, page,
@ -2935,7 +2929,7 @@ static void pmbus_notify(struct pmbus_data *data, int page, int reg, int flags)
}
static int _pmbus_get_flags(struct pmbus_data *data, u8 page, unsigned int *flags,
unsigned int *event, bool notify)
unsigned int *event, bool notify)
{
int i, status;
const struct pmbus_status_category *cat;
@ -2964,7 +2958,6 @@ static int _pmbus_get_flags(struct pmbus_data *data, u8 page, unsigned int *flag
if (notify && status)
pmbus_notify(data, page, cat->reg, status);
}
/*
@ -3015,7 +3008,6 @@ static int _pmbus_get_flags(struct pmbus_data *data, u8 page, unsigned int *flag
*event |= REGULATOR_EVENT_OVER_TEMP_WARN;
}
return 0;
}
@ -3228,7 +3220,7 @@ static int pmbus_regulator_set_voltage(struct regulator_dev *rdev, int min_uv,
}
static int pmbus_regulator_list_voltage(struct regulator_dev *rdev,
unsigned int selector)
unsigned int selector)
{
struct device *dev = rdev_get_dev(rdev);
struct i2c_client *client = to_i2c_client(dev->parent);
@ -3320,17 +3312,16 @@ static int pmbus_regulator_register(struct pmbus_data *data)
return 0;
}
static int pmbus_regulator_notify(struct pmbus_data *data, int page, int event)
static void pmbus_regulator_notify(struct pmbus_data *data, int page, int event)
{
int j;
int j;
for (j = 0; j < data->info->num_regulators; j++) {
if (page == rdev_get_id(data->rdevs[j])) {
regulator_notifier_call_chain(data->rdevs[j], event, NULL);
break;
}
for (j = 0; j < data->info->num_regulators; j++) {
if (page == rdev_get_id(data->rdevs[j])) {
regulator_notifier_call_chain(data->rdevs[j], event, NULL);
break;
}
return 0;
}
}
#else
static int pmbus_regulator_register(struct pmbus_data *data)
@ -3338,9 +3329,8 @@ static int pmbus_regulator_register(struct pmbus_data *data)
return 0;
}
static int pmbus_regulator_notify(struct pmbus_data *data, int page, int event)
static void pmbus_regulator_notify(struct pmbus_data *data, int page, int event)
{
return 0;
}
#endif
@ -3363,8 +3353,8 @@ static irqreturn_t pmbus_fault_handler(int irq, void *pdata)
{
struct pmbus_data *data = pdata;
struct i2c_client *client = to_i2c_client(data->dev);
int i, status, event;
mutex_lock(&data->update_lock);
for (i = 0; i < data->info->pages; i++) {
_pmbus_get_flags(data, i, &status, &event, true);
@ -3428,7 +3418,6 @@ static int pmbus_irq_setup(struct i2c_client *client, struct pmbus_data *data)
static struct dentry *pmbus_debugfs_dir; /* pmbus debugfs directory */
#if IS_ENABLED(CONFIG_DEBUG_FS)
static int pmbus_debugfs_get(void *data, u64 *val)
{
int rc;
@ -3471,8 +3460,8 @@ static int pmbus_debugfs_get_status(void *data, u64 *val)
DEFINE_DEBUGFS_ATTRIBUTE(pmbus_debugfs_ops_status, pmbus_debugfs_get_status,
NULL, "0x%04llx\n");
static ssize_t pmbus_debugfs_mfr_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
static ssize_t pmbus_debugfs_block_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
int rc;
struct pmbus_debugfs_entry *entry = file->private_data;
@ -3497,51 +3486,98 @@ static ssize_t pmbus_debugfs_mfr_read(struct file *file, char __user *buf,
return simple_read_from_buffer(buf, count, ppos, data, rc);
}
static const struct file_operations pmbus_debugfs_ops_mfr = {
static const struct file_operations pmbus_debugfs_block_ops = {
.llseek = noop_llseek,
.read = pmbus_debugfs_mfr_read,
.read = pmbus_debugfs_block_read,
.write = NULL,
.open = simple_open,
};
static void pmbus_remove_debugfs(void *data)
static void pmbus_remove_symlink(void *symlink)
{
struct dentry *entry = data;
debugfs_remove_recursive(entry);
debugfs_remove(symlink);
}
static int pmbus_init_debugfs(struct i2c_client *client,
struct pmbus_data *data)
{
int i, idx = 0;
char name[PMBUS_NAME_SIZE];
struct pmbus_debugfs_entry *entries;
struct pmbus_debugfs_data {
u8 reg;
u32 flag;
const char *name;
};
if (!pmbus_debugfs_dir)
return -ENODEV;
static const struct pmbus_debugfs_data pmbus_debugfs_block_data[] = {
{ .reg = PMBUS_MFR_ID, .name = "mfr_id" },
{ .reg = PMBUS_MFR_MODEL, .name = "mfr_model" },
{ .reg = PMBUS_MFR_REVISION, .name = "mfr_revision" },
{ .reg = PMBUS_MFR_LOCATION, .name = "mfr_location" },
{ .reg = PMBUS_MFR_DATE, .name = "mfr_date" },
{ .reg = PMBUS_MFR_SERIAL, .name = "mfr_serial" },
};
static const struct pmbus_debugfs_data pmbus_debugfs_status_data[] = {
{ .reg = PMBUS_STATUS_VOUT, .flag = PMBUS_HAVE_STATUS_VOUT, .name = "status%d_vout" },
{ .reg = PMBUS_STATUS_IOUT, .flag = PMBUS_HAVE_STATUS_IOUT, .name = "status%d_iout" },
{ .reg = PMBUS_STATUS_INPUT, .flag = PMBUS_HAVE_STATUS_INPUT, .name = "status%d_input" },
{ .reg = PMBUS_STATUS_TEMPERATURE, .flag = PMBUS_HAVE_STATUS_TEMP,
.name = "status%d_temp" },
{ .reg = PMBUS_STATUS_FAN_12, .flag = PMBUS_HAVE_STATUS_FAN12, .name = "status%d_fan12" },
{ .reg = PMBUS_STATUS_FAN_34, .flag = PMBUS_HAVE_STATUS_FAN34, .name = "status%d_fan34" },
{ .reg = PMBUS_STATUS_CML, .name = "status%d_cml" },
{ .reg = PMBUS_STATUS_OTHER, .name = "status%d_other" },
{ .reg = PMBUS_STATUS_MFR_SPECIFIC, .name = "status%d_mfr" },
};
static void pmbus_init_debugfs(struct i2c_client *client,
struct pmbus_data *data)
{
struct dentry *symlink_d, *debugfs = client->debugfs;
struct pmbus_debugfs_entry *entries;
const char *pathname, *symlink;
char name[PMBUS_NAME_SIZE];
int page, i, idx = 0;
/*
* Create the debugfs directory for this device. Use the hwmon device
* name to avoid conflicts (hwmon numbers are globally unique).
* client->debugfs may be NULL or an ERR_PTR(). dentry_path_raw()
* does not check if its parameters are valid, so validate
* client->debugfs before using it.
*/
data->debugfs = debugfs_create_dir(dev_name(data->hwmon_dev),
pmbus_debugfs_dir);
if (IS_ERR_OR_NULL(data->debugfs)) {
data->debugfs = NULL;
return -ENODEV;
}
if (!pmbus_debugfs_dir || IS_ERR_OR_NULL(debugfs))
return;
/*
* Backwards compatibility: Create symlink from /pmbus/<hwmon_device>
* to i2c debugfs directory.
*/
pathname = dentry_path_raw(debugfs, name, sizeof(name));
if (IS_ERR(pathname))
return;
/*
* The path returned by dentry_path_raw() starts with '/'. Prepend it
* with ".." to get the symlink relative to the pmbus root directory.
*/
symlink = kasprintf(GFP_KERNEL, "..%s", pathname);
if (!symlink)
return;
symlink_d = debugfs_create_symlink(dev_name(data->hwmon_dev),
pmbus_debugfs_dir, symlink);
kfree(symlink);
devm_add_action_or_reset(data->dev, pmbus_remove_symlink, symlink_d);
/*
* Allocate the max possible entries we need.
* 7 entries device-specific
* 10 entries page-specific
* device specific:
* ARRAY_SIZE(pmbus_debugfs_block_data) + 2
* page specific:
* ARRAY_SIZE(pmbus_debugfs_status_data) + 1
*/
entries = devm_kcalloc(data->dev,
7 + data->info->pages * 10, sizeof(*entries),
GFP_KERNEL);
ARRAY_SIZE(pmbus_debugfs_block_data) + 2 +
data->info->pages * (ARRAY_SIZE(pmbus_debugfs_status_data) + 1),
sizeof(*entries), GFP_KERNEL);
if (!entries)
return -ENOMEM;
return;
/*
* Add device-specific entries.
@ -3551,184 +3587,67 @@ static int pmbus_init_debugfs(struct i2c_client *client,
* assume that values of the following registers are the same for all
* pages and report values only for page 0.
*/
if (!(data->flags & PMBUS_NO_CAPABILITY) &&
pmbus_check_byte_register(client, 0, PMBUS_CAPABILITY)) {
entries[idx].client = client;
entries[idx].page = 0;
entries[idx].reg = PMBUS_CAPABILITY;
debugfs_create_file("capability", 0444, debugfs,
&entries[idx++],
&pmbus_debugfs_ops);
}
if (pmbus_check_byte_register(client, 0, PMBUS_REVISION)) {
entries[idx].client = client;
entries[idx].page = 0;
entries[idx].reg = PMBUS_REVISION;
debugfs_create_file("revision", 0444, data->debugfs,
debugfs_create_file("pmbus_revision", 0444, debugfs,
&entries[idx++],
&pmbus_debugfs_ops);
}
if (pmbus_check_block_register(client, 0, PMBUS_MFR_ID)) {
entries[idx].client = client;
entries[idx].page = 0;
entries[idx].reg = PMBUS_MFR_ID;
debugfs_create_file("mfr_id", 0444, data->debugfs,
&entries[idx++],
&pmbus_debugfs_ops_mfr);
}
for (i = 0; i < ARRAY_SIZE(pmbus_debugfs_block_data); i++) {
const struct pmbus_debugfs_data *d = &pmbus_debugfs_block_data[i];
if (pmbus_check_block_register(client, 0, PMBUS_MFR_MODEL)) {
entries[idx].client = client;
entries[idx].page = 0;
entries[idx].reg = PMBUS_MFR_MODEL;
debugfs_create_file("mfr_model", 0444, data->debugfs,
&entries[idx++],
&pmbus_debugfs_ops_mfr);
}
if (pmbus_check_block_register(client, 0, PMBUS_MFR_REVISION)) {
entries[idx].client = client;
entries[idx].page = 0;
entries[idx].reg = PMBUS_MFR_REVISION;
debugfs_create_file("mfr_revision", 0444, data->debugfs,
&entries[idx++],
&pmbus_debugfs_ops_mfr);
}
if (pmbus_check_block_register(client, 0, PMBUS_MFR_LOCATION)) {
entries[idx].client = client;
entries[idx].page = 0;
entries[idx].reg = PMBUS_MFR_LOCATION;
debugfs_create_file("mfr_location", 0444, data->debugfs,
&entries[idx++],
&pmbus_debugfs_ops_mfr);
}
if (pmbus_check_block_register(client, 0, PMBUS_MFR_DATE)) {
entries[idx].client = client;
entries[idx].page = 0;
entries[idx].reg = PMBUS_MFR_DATE;
debugfs_create_file("mfr_date", 0444, data->debugfs,
&entries[idx++],
&pmbus_debugfs_ops_mfr);
}
if (pmbus_check_block_register(client, 0, PMBUS_MFR_SERIAL)) {
entries[idx].client = client;
entries[idx].page = 0;
entries[idx].reg = PMBUS_MFR_SERIAL;
debugfs_create_file("mfr_serial", 0444, data->debugfs,
&entries[idx++],
&pmbus_debugfs_ops_mfr);
if (pmbus_check_block_register(client, 0, d->reg)) {
entries[idx].client = client;
entries[idx].page = 0;
entries[idx].reg = d->reg;
debugfs_create_file(d->name, 0444, debugfs,
&entries[idx++],
&pmbus_debugfs_block_ops);
}
}
/* Add page specific entries */
for (i = 0; i < data->info->pages; ++i) {
for (page = 0; page < data->info->pages; ++page) {
/* Check accessibility of status register if it's not page 0 */
if (!i || pmbus_check_status_register(client, i)) {
if (!page || pmbus_check_status_register(client, page)) {
/* No need to set reg as we have special read op. */
entries[idx].client = client;
entries[idx].page = i;
scnprintf(name, PMBUS_NAME_SIZE, "status%d", i);
debugfs_create_file(name, 0444, data->debugfs,
entries[idx].page = page;
scnprintf(name, PMBUS_NAME_SIZE, "status%d", page);
debugfs_create_file(name, 0444, debugfs,
&entries[idx++],
&pmbus_debugfs_ops_status);
}
if (data->info->func[i] & PMBUS_HAVE_STATUS_VOUT) {
entries[idx].client = client;
entries[idx].page = i;
entries[idx].reg = PMBUS_STATUS_VOUT;
scnprintf(name, PMBUS_NAME_SIZE, "status%d_vout", i);
debugfs_create_file(name, 0444, data->debugfs,
&entries[idx++],
&pmbus_debugfs_ops);
}
for (i = 0; i < ARRAY_SIZE(pmbus_debugfs_status_data); i++) {
const struct pmbus_debugfs_data *d =
&pmbus_debugfs_status_data[i];
if (data->info->func[i] & PMBUS_HAVE_STATUS_IOUT) {
entries[idx].client = client;
entries[idx].page = i;
entries[idx].reg = PMBUS_STATUS_IOUT;
scnprintf(name, PMBUS_NAME_SIZE, "status%d_iout", i);
debugfs_create_file(name, 0444, data->debugfs,
&entries[idx++],
&pmbus_debugfs_ops);
}
if (data->info->func[i] & PMBUS_HAVE_STATUS_INPUT) {
entries[idx].client = client;
entries[idx].page = i;
entries[idx].reg = PMBUS_STATUS_INPUT;
scnprintf(name, PMBUS_NAME_SIZE, "status%d_input", i);
debugfs_create_file(name, 0444, data->debugfs,
&entries[idx++],
&pmbus_debugfs_ops);
}
if (data->info->func[i] & PMBUS_HAVE_STATUS_TEMP) {
entries[idx].client = client;
entries[idx].page = i;
entries[idx].reg = PMBUS_STATUS_TEMPERATURE;
scnprintf(name, PMBUS_NAME_SIZE, "status%d_temp", i);
debugfs_create_file(name, 0444, data->debugfs,
&entries[idx++],
&pmbus_debugfs_ops);
}
if (pmbus_check_byte_register(client, i, PMBUS_STATUS_CML)) {
entries[idx].client = client;
entries[idx].page = i;
entries[idx].reg = PMBUS_STATUS_CML;
scnprintf(name, PMBUS_NAME_SIZE, "status%d_cml", i);
debugfs_create_file(name, 0444, data->debugfs,
&entries[idx++],
&pmbus_debugfs_ops);
}
if (pmbus_check_byte_register(client, i, PMBUS_STATUS_OTHER)) {
entries[idx].client = client;
entries[idx].page = i;
entries[idx].reg = PMBUS_STATUS_OTHER;
scnprintf(name, PMBUS_NAME_SIZE, "status%d_other", i);
debugfs_create_file(name, 0444, data->debugfs,
&entries[idx++],
&pmbus_debugfs_ops);
}
if (pmbus_check_byte_register(client, i,
PMBUS_STATUS_MFR_SPECIFIC)) {
entries[idx].client = client;
entries[idx].page = i;
entries[idx].reg = PMBUS_STATUS_MFR_SPECIFIC;
scnprintf(name, PMBUS_NAME_SIZE, "status%d_mfr", i);
debugfs_create_file(name, 0444, data->debugfs,
&entries[idx++],
&pmbus_debugfs_ops);
}
if (data->info->func[i] & PMBUS_HAVE_STATUS_FAN12) {
entries[idx].client = client;
entries[idx].page = i;
entries[idx].reg = PMBUS_STATUS_FAN_12;
scnprintf(name, PMBUS_NAME_SIZE, "status%d_fan12", i);
debugfs_create_file(name, 0444, data->debugfs,
&entries[idx++],
&pmbus_debugfs_ops);
}
if (data->info->func[i] & PMBUS_HAVE_STATUS_FAN34) {
entries[idx].client = client;
entries[idx].page = i;
entries[idx].reg = PMBUS_STATUS_FAN_34;
scnprintf(name, PMBUS_NAME_SIZE, "status%d_fan34", i);
debugfs_create_file(name, 0444, data->debugfs,
&entries[idx++],
&pmbus_debugfs_ops);
if ((data->info->func[page] & d->flag) ||
(!d->flag && pmbus_check_byte_register(client, page, d->reg))) {
entries[idx].client = client;
entries[idx].page = page;
entries[idx].reg = d->reg;
scnprintf(name, PMBUS_NAME_SIZE, d->name, page);
debugfs_create_file(name, 0444, debugfs,
&entries[idx++],
&pmbus_debugfs_ops);
}
}
}
return devm_add_action_or_reset(data->dev,
pmbus_remove_debugfs, data->debugfs);
}
#else
static int pmbus_init_debugfs(struct i2c_client *client,
struct pmbus_data *data)
{
return 0;
}
#endif /* IS_ENABLED(CONFIG_DEBUG_FS) */
int pmbus_do_probe(struct i2c_client *client, struct pmbus_driver_info *info)
{
@ -3800,8 +3719,8 @@ int pmbus_do_probe(struct i2c_client *client, struct pmbus_driver_info *info)
data->groups[0] = &data->group;
memcpy(data->groups + 1, info->groups, sizeof(void *) * groups_num);
data->hwmon_dev = devm_hwmon_device_register_with_groups(dev,
name, data, data->groups);
data->hwmon_dev = devm_hwmon_device_register_with_groups(dev, name,
data, data->groups);
if (IS_ERR(data->hwmon_dev)) {
dev_err(dev, "Failed to register hwmon device\n");
return PTR_ERR(data->hwmon_dev);
@ -3815,9 +3734,7 @@ int pmbus_do_probe(struct i2c_client *client, struct pmbus_driver_info *info)
if (ret)
return ret;
ret = pmbus_init_debugfs(client, data);
if (ret)
dev_warn(dev, "Failed to register debugfs\n");
pmbus_init_debugfs(client, data);
return 0;
}
@ -3825,9 +3742,15 @@ EXPORT_SYMBOL_NS_GPL(pmbus_do_probe, "PMBUS");
struct dentry *pmbus_get_debugfs_dir(struct i2c_client *client)
{
struct pmbus_data *data = i2c_get_clientdata(client);
return data->debugfs;
/*
* client->debugfs may be an ERR_PTR(). Returning that to
* the calling code would potentially require additional
* complexity in the calling code and otherwise add no
* value. Return NULL in that case.
*/
if (IS_ERR_OR_NULL(client->debugfs))
return NULL;
return client->debugfs;
}
EXPORT_SYMBOL_NS_GPL(pmbus_get_debugfs_dir, "PMBUS");

View File

@ -63,7 +63,6 @@ struct pt5161l_fw_ver {
/* Each client has this additional data */
struct pt5161l_data {
struct i2c_client *client;
struct dentry *debugfs;
struct pt5161l_fw_ver fw_ver;
struct mutex lock; /* for atomic I2C transactions */
bool init_done;
@ -72,8 +71,6 @@ struct pt5161l_data {
bool mm_wide_reg_access; /* MM assisted wide register access */
};
static struct dentry *pt5161l_debugfs_dir;
/*
* Write multiple data bytes to Aries over I2C
*/
@ -568,21 +565,16 @@ static const struct file_operations pt5161l_debugfs_ops_hb_sts = {
.open = simple_open,
};
static int pt5161l_init_debugfs(struct pt5161l_data *data)
static void pt5161l_init_debugfs(struct i2c_client *client, struct pt5161l_data *data)
{
data->debugfs = debugfs_create_dir(dev_name(&data->client->dev),
pt5161l_debugfs_dir);
debugfs_create_file("fw_ver", 0444, data->debugfs, data,
debugfs_create_file("fw_ver", 0444, client->debugfs, data,
&pt5161l_debugfs_ops_fw_ver);
debugfs_create_file("fw_load_status", 0444, data->debugfs, data,
debugfs_create_file("fw_load_status", 0444, client->debugfs, data,
&pt5161l_debugfs_ops_fw_load_sts);
debugfs_create_file("heartbeat_status", 0444, data->debugfs, data,
debugfs_create_file("heartbeat_status", 0444, client->debugfs, data,
&pt5161l_debugfs_ops_hb_sts);
return 0;
}
static int pt5161l_probe(struct i2c_client *client)
@ -604,17 +596,12 @@ static int pt5161l_probe(struct i2c_client *client)
data,
&pt5161l_chip_info,
NULL);
if (IS_ERR(hwmon_dev))
return PTR_ERR(hwmon_dev);
pt5161l_init_debugfs(data);
pt5161l_init_debugfs(client, data);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
static void pt5161l_remove(struct i2c_client *client)
{
struct pt5161l_data *data = i2c_get_clientdata(client);
debugfs_remove_recursive(data->debugfs);
return 0;
}
static const struct of_device_id __maybe_unused pt5161l_of_match[] = {
@ -643,24 +630,9 @@ static struct i2c_driver pt5161l_driver = {
.acpi_match_table = ACPI_PTR(pt5161l_acpi_match),
},
.probe = pt5161l_probe,
.remove = pt5161l_remove,
.id_table = pt5161l_id,
};
static int __init pt5161l_init(void)
{
pt5161l_debugfs_dir = debugfs_create_dir("pt5161l", NULL);
return i2c_add_driver(&pt5161l_driver);
}
static void __exit pt5161l_exit(void)
{
i2c_del_driver(&pt5161l_driver);
debugfs_remove_recursive(pt5161l_debugfs_dir);
}
module_init(pt5161l_init);
module_exit(pt5161l_exit);
module_i2c_driver(pt5161l_driver);
MODULE_AUTHOR("Cosmo Chou <cosmo.chou@quantatw.com>");
MODULE_DESCRIPTION("Hwmon driver for Astera Labs Aries PCIe retimer");

View File

@ -50,12 +50,9 @@
struct sg2042_mcu_data {
struct i2c_client *client;
struct dentry *debugfs;
struct mutex mutex;
};
static struct dentry *sgmcu_debugfs;
static ssize_t reset_count_show(struct device *dev,
struct device_attribute *attr,
char *buf)
@ -292,18 +289,15 @@ static const struct hwmon_chip_info sg2042_mcu_chip_info = {
.info = sg2042_mcu_info,
};
static void sg2042_mcu_debugfs_init(struct sg2042_mcu_data *mcu,
struct device *dev)
static void sg2042_mcu_debugfs_init(struct sg2042_mcu_data *mcu)
{
mcu->debugfs = debugfs_create_dir(dev_name(dev), sgmcu_debugfs);
debugfs_create_file("firmware_version", 0444, mcu->debugfs,
debugfs_create_file("firmware_version", 0444, mcu->client->debugfs,
mcu, &firmware_version_fops);
debugfs_create_file("pcb_version", 0444, mcu->debugfs, mcu,
debugfs_create_file("pcb_version", 0444, mcu->client->debugfs, mcu,
&pcb_version_fops);
debugfs_create_file("mcu_type", 0444, mcu->debugfs, mcu,
debugfs_create_file("mcu_type", 0444, mcu->client->debugfs, mcu,
&mcu_type_fops);
debugfs_create_file("board_type", 0444, mcu->debugfs, mcu,
debugfs_create_file("board_type", 0444, mcu->client->debugfs, mcu,
&board_type_fops);
}
@ -333,18 +327,11 @@ static int sg2042_mcu_i2c_probe(struct i2c_client *client)
if (IS_ERR(hwmon_dev))
return PTR_ERR(hwmon_dev);
sg2042_mcu_debugfs_init(mcu, dev);
sg2042_mcu_debugfs_init(mcu);
return 0;
}
static void sg2042_mcu_i2c_remove(struct i2c_client *client)
{
struct sg2042_mcu_data *mcu = i2c_get_clientdata(client);
debugfs_remove_recursive(mcu->debugfs);
}
static const struct i2c_device_id sg2042_mcu_id[] = {
{ "sg2042-hwmon-mcu" },
{ }
@ -364,24 +351,9 @@ static struct i2c_driver sg2042_mcu_driver = {
.dev_groups = sg2042_mcu_groups,
},
.probe = sg2042_mcu_i2c_probe,
.remove = sg2042_mcu_i2c_remove,
.id_table = sg2042_mcu_id,
};
static int __init sg2042_mcu_init(void)
{
sgmcu_debugfs = debugfs_create_dir("sg2042-mcu", NULL);
return i2c_add_driver(&sg2042_mcu_driver);
}
static void __exit sg2042_mcu_exit(void)
{
debugfs_remove_recursive(sgmcu_debugfs);
i2c_del_driver(&sg2042_mcu_driver);
}
module_init(sg2042_mcu_init);
module_exit(sg2042_mcu_exit);
module_i2c_driver(sg2042_mcu_driver);
MODULE_AUTHOR("Inochi Amaoto <inochiama@outlook.com>");
MODULE_DESCRIPTION("MCU I2C driver for SG2042 soc platform");

View File

@ -44,8 +44,6 @@ static const unsigned char sht3x_cmd_read_status_reg[] = { 0xf3, 0x2d };
static const unsigned char sht3x_cmd_clear_status_reg[] = { 0x30, 0x41 };
static const unsigned char sht3x_cmd_read_serial_number[] = { 0x37, 0x80 };
static struct dentry *debugfs;
/* delays for single-shot mode i2c commands, both in us */
#define SHT3X_SINGLE_WAIT_TIME_HPM 15000
#define SHT3X_SINGLE_WAIT_TIME_MPM 6000
@ -167,7 +165,6 @@ struct sht3x_data {
enum sht3x_chips chip_id;
struct mutex i2c_lock; /* lock for sending i2c commands */
struct mutex data_lock; /* lock for updating driver data */
struct dentry *sensor_dir;
u8 mode;
const unsigned char *command;
@ -837,23 +834,7 @@ static int sht3x_write(struct device *dev, enum hwmon_sensor_types type,
}
}
static void sht3x_debugfs_init(struct sht3x_data *data)
{
char name[32];
snprintf(name, sizeof(name), "i2c%u-%02x",
data->client->adapter->nr, data->client->addr);
data->sensor_dir = debugfs_create_dir(name, debugfs);
debugfs_create_u32("serial_number", 0444,
data->sensor_dir, &data->serial_number);
}
static void sht3x_debugfs_remove(void *sensor_dir)
{
debugfs_remove_recursive(sensor_dir);
}
static int sht3x_serial_number_read(struct sht3x_data *data)
static void sht3x_serial_number_read(struct sht3x_data *data)
{
int ret;
char buffer[SHT3X_RESPONSE_LENGTH];
@ -864,11 +845,12 @@ static int sht3x_serial_number_read(struct sht3x_data *data)
buffer,
SHT3X_RESPONSE_LENGTH, 0);
if (ret)
return ret;
return;
data->serial_number = (buffer[0] << 24) | (buffer[1] << 16) |
(buffer[3] << 8) | buffer[4];
return ret;
debugfs_create_u32("serial_number", 0444, client->debugfs, &data->serial_number);
}
static const struct hwmon_ops sht3x_ops = {
@ -930,28 +912,14 @@ static int sht3x_probe(struct i2c_client *client)
if (ret)
return ret;
ret = sht3x_serial_number_read(data);
if (ret) {
dev_dbg(dev, "unable to read serial number\n");
} else {
sht3x_debugfs_init(data);
ret = devm_add_action_or_reset(dev,
sht3x_debugfs_remove,
data->sensor_dir);
if (ret)
return ret;
}
hwmon_dev = devm_hwmon_device_register_with_info(dev,
client->name,
data,
&sht3x_chip_info,
sht3x_groups);
hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data,
&sht3x_chip_info, sht3x_groups);
if (IS_ERR(hwmon_dev))
dev_dbg(dev, "unable to register hwmon device\n");
return PTR_ERR(hwmon_dev);
return PTR_ERR_OR_ZERO(hwmon_dev);
sht3x_serial_number_read(data);
return 0;
}
/* device ID table */
@ -968,20 +936,7 @@ static struct i2c_driver sht3x_i2c_driver = {
.probe = sht3x_probe,
.id_table = sht3x_ids,
};
static int __init sht3x_init(void)
{
debugfs = debugfs_create_dir("sht3x", NULL);
return i2c_add_driver(&sht3x_i2c_driver);
}
module_init(sht3x_init);
static void __exit sht3x_cleanup(void)
{
debugfs_remove_recursive(debugfs);
i2c_del_driver(&sht3x_i2c_driver);
}
module_exit(sht3x_cleanup);
module_i2c_driver(sht3x_i2c_driver);
MODULE_AUTHOR("David Frey <david.frey@sensirion.com>");
MODULE_AUTHOR("Pascal Sachs <pascal.sachs@sensirion.com>");

View File

@ -114,7 +114,6 @@ struct tps23861_data {
struct regmap *regmap;
u32 shunt_resistor;
struct i2c_client *client;
struct dentry *debugfs_dir;
};
static const struct regmap_config tps23861_regmap_config = {
@ -503,25 +502,6 @@ static int tps23861_port_status_show(struct seq_file *s, void *data)
DEFINE_SHOW_ATTRIBUTE(tps23861_port_status);
static void tps23861_init_debugfs(struct tps23861_data *data,
struct device *hwmon_dev)
{
const char *debugfs_name;
debugfs_name = devm_kasprintf(&data->client->dev, GFP_KERNEL, "%s-%s",
data->client->name, dev_name(hwmon_dev));
if (!debugfs_name)
return;
data->debugfs_dir = debugfs_create_dir(debugfs_name, NULL);
debugfs_create_file("port_status",
0400,
data->debugfs_dir,
data,
&tps23861_port_status_fops);
}
static int tps23861_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
@ -562,18 +542,12 @@ static int tps23861_probe(struct i2c_client *client)
if (IS_ERR(hwmon_dev))
return PTR_ERR(hwmon_dev);
tps23861_init_debugfs(data, hwmon_dev);
debugfs_create_file("port_status", 0400, client->debugfs, data,
&tps23861_port_status_fops);
return 0;
}
static void tps23861_remove(struct i2c_client *client)
{
struct tps23861_data *data = i2c_get_clientdata(client);
debugfs_remove_recursive(data->debugfs_dir);
}
static const struct of_device_id __maybe_unused tps23861_of_match[] = {
{ .compatible = "ti,tps23861", },
{ },
@ -582,7 +556,6 @@ MODULE_DEVICE_TABLE(of, tps23861_of_match);
static struct i2c_driver tps23861_driver = {
.probe = tps23861_probe,
.remove = tps23861_remove,
.driver = {
.name = "tps23861",
.of_match_table = of_match_ptr(tps23861_of_match),

View File

@ -105,7 +105,7 @@ struct xgene_hwmon_dev {
phys_addr_t comm_base_addr;
void *pcc_comm_addr;
u64 usecs_lat;
unsigned int usecs_lat;
};
/*

View File

@ -569,6 +569,7 @@
#define PCI_DEVICE_ID_AMD_17H_DF_F3 0x1463
#define PCI_DEVICE_ID_AMD_17H_M10H_DF_F3 0x15eb
#define PCI_DEVICE_ID_AMD_17H_M30H_DF_F3 0x1493
#define PCI_DEVICE_ID_AMD_17H_M40H_DF_F3 0x13f3
#define PCI_DEVICE_ID_AMD_17H_M60H_DF_F3 0x144b
#define PCI_DEVICE_ID_AMD_17H_M70H_DF_F3 0x1443
#define PCI_DEVICE_ID_AMD_17H_MA0H_DF_F3 0x1727