mirror of
https://github.com/torvalds/linux.git
synced 2026-06-01 02:53:36 +02:00
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:
commit
c07666e29b
|
|
@ -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#
|
||||
|
||||
|
|
|
|||
|
|
@ -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#
|
||||
|
||||
|
|
|
|||
|
|
@ -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#
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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#
|
||||
|
||||
|
|
|
|||
|
|
@ -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#
|
||||
|
||||
|
|
|
|||
|
|
@ -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#
|
||||
|
||||
|
|
|
|||
111
Documentation/devicetree/bindings/hwmon/microchip,emc2305.yaml
Normal file
111
Documentation/devicetree/bindings/hwmon/microchip,emc2305.yaml
Normal 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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
||||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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#
|
||||
|
||||
|
|
|
|||
|
|
@ -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#
|
||||
|
||||
|
|
|
|||
|
|
@ -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#
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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#
|
||||
|
||||
|
|
|
|||
|
|
@ -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#
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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#
|
||||
|
||||
|
|
|
|||
|
|
@ -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#
|
||||
|
||||
|
|
|
|||
|
|
@ -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#
|
||||
|
||||
|
|
|
|||
|
|
@ -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#
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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>,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
63
Documentation/hwmon/cgbc-hwmon.rst
Normal file
63
Documentation/hwmon/cgbc-hwmon.rst
Normal 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
|
||||
============= ======================
|
||||
|
|
@ -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
|
||||
|
|
|
|||
37
Documentation/hwmon/htu31.rst
Normal file
37
Documentation/hwmon/htu31.rst
Normal 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
|
||||
=================== =================
|
||||
75
Documentation/hwmon/ina233.rst
Normal file
75
Documentation/hwmon/ina233.rst
Normal 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**
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
14
MAINTAINERS
14
MAINTAINERS
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
304
drivers/hwmon/cgbc-hwmon.c
Normal 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");
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
350
drivers/hwmon/htu31.c
Normal 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");
|
||||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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) },
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
<c4282_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,
|
||||
<c4282_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,
|
||||
<c4282_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,
|
||||
<c4282_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,
|
||||
<c4282_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,
|
||||
<c4282_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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
191
drivers/hwmon/pmbus/ina233.c
Normal file
191
drivers/hwmon/pmbus/ina233.c
Normal 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");
|
||||
|
|
@ -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" },
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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>");
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user