hmon updates for v6.13-rc1

* New drivers
 
   - ISL28022 power monitor
 
   - Nuvoton NCT7363Y
 
 * Added support for new chips to existing drivers
 
   - The tmp180 driver now supports NXP p3t1085, including its I3C mode
 
   - The ina2xx driver now supports SY24655 and INA260
 
   - The amc6821 driver now supports tsd,mule
 
 * Other notable improvements
 
   - The sht4x driver now supports the chip heater
 
   - The pmbus/isl68137 driver now supports a voltage divider on Vout
 
   - The cros_ec driver registers with the thermal framework
 
   - The pmbus/ltc2978 driver now supports LTC7841
 
   - The PMBus core now allow drivers to override WRITE_PROTECT
 
 * Various other minor improvements and cleanups
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEiHPvMQj9QTOCiqgVyx8mb86fmYEFAmc6tlAACgkQyx8mb86f
 mYEmThAAi85J4hUepnrpfKwzqzTgXhU3Y1ih7qWyIK0ErI5tjmYiCR3PHHd7jNb0
 A5STfbpRkomvepQq9H79lv5EiOMFvudYLj2QuxFbSchz0J2XqXtg2RxUi9AK0/SE
 b+KLRjNNNHc2J2tDrLj14ZJJxMbqx35kx2x6TDGI+zoBhQ0XwEWkbtNuXipSpHQ8
 OkTjbtGKlQrjtc7fJ/fWGlTDeVS2LWtCbJhybcL47IkPeIBQH/FIhoEnnKqj9/EV
 KROuVAIoAYK9r+w7ZDfp2iBpR0gOaWZ5I2w2cCufsO9pFEuMIeVDgfFP2XGLBXEi
 NlVlMMyk6X7XqvnHYmun6r9chuPtFc9+Rr7SnJkVaNviY7BmRcuWk1BGxA4GIAIR
 6F3sBxlXdisnjVkYd1ENhCGlso/fXQizKUro3KZka0sXEnjxA+SE0dzIK8d77kip
 P6Mjg3hC6DZlA0eIlnbCWasu0gra4duVTpGTOcbfZQHN+50Fi4va9w2+1UDfIPnn
 f2gIUubtzrWZqKAPnN6ckIE7BQubsPukx7Zef0wgDRpxOgCBrKeQVX8LYtXQfkda
 G+LkSk1N1bUIpTbzGQPg7YLbVCQPQYqaSryFPL2lQtfEY58Tgt+EMCKSROmXMBOV
 tk9uDaN8OjXdYmU90jcHJR7v1WDkLRkRV0fsh6eFUpc8gQDiY+k=
 =BG/M
 -----END PGP SIGNATURE-----

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

Pull hmon updates from Guenter Roeck:
 "New drivers:
   - ISL28022 power monitor
   - Nuvoton NCT7363Y

  Added support for new chips to existing drivers:
   - The tmp180 driver now supports NXP p3t1085, including its I3C mode
   - The ina2xx driver now supports SY24655 and INA260
   - The amc6821 driver now supports tsd,mule

  Other notable improvements:
   - The sht4x driver now supports the chip heater
   - The pmbus/isl68137 driver now supports a voltage divider on Vout
   - The cros_ec driver registers with the thermal framework
   - The pmbus/ltc2978 driver now supports LTC7841
   - The PMBus core now allow drivers to override WRITE_PROTECT

  Various other minor improvements and cleanups"

* tag 'hwmon-for-v6.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (51 commits)
  hwmon: (pmbus/isl68137) add support for voltage divider on Vout
  dt-bindings: hwmon: isl68137: add bindings to support voltage dividers
  hwmon: tmp108: fix I3C dependency
  hwmon: (cros_ec) register thermal sensors to thermal framework
  hwmon: (tmp108) Add support for I3C device
  hwmon: (tmp108) Add helper function tmp108_common_probe() to prepare I3C support
  hwmon: (acpi_power_meter) Fix fail to load module on platform without _PMD method
  hwmon: (nct6775-core) Fix overflows seen when writing limit attributes
  hwmon: (pwm-fan) Introduce start from stopped state handling
  dt-bindings: hwmon: pwm-fan: Document start from stopped state properties
  hwmon: (tmp108) Add NXP p3t1085 support
  dt-bindings: hwmon: ti,tmp108: Add nxp,p3t1085 compatible string
  hwmon: (sch5627, max31827) Fix typos in driver documentation
  hwmon: (jc42) Drop of_match_ptr() protection
  hwmon: (f71882fg) Fix grammar in fan speed trip points explanation
  dt-bindings: hwmon: pmbus: add ti tps25990 support
  hwmon: (pmbus/core) clear faults after setting smbalert mask
  hwmon: (pmbus/core) allow drivers to override WRITE_PROTECT
  hwmon: (pmbus) add documentation for existing flags
  hwmon: (ina226) Add support for SY24655
  ...
This commit is contained in:
Linus Torvalds 2024-11-19 11:23:52 -08:00
commit 2b5d5f23d4
85 changed files with 2620 additions and 250 deletions

View File

@ -26,6 +26,7 @@ properties:
- lltc,ltc3886
- lltc,ltc3887
- lltc,ltc3889
- lltc,ltc7841
- lltc,ltc7880
- lltc,ltm2987
- lltc,ltm4664
@ -50,6 +51,7 @@ properties:
* ltc2977, ltc2979, ltc2980, ltm2987 : vout0 - vout7
* ltc2978 : vout0 - vout7
* ltc3880, ltc3882, ltc3884, ltc3886, ltc3887, ltc3889 : vout0 - vout1
* ltc7841 : vout0
* ltc7880 : vout0 - vout1
* ltc3883 : vout0
* ltm4664 : vout0 - vout1

View File

@ -0,0 +1,66 @@
# 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#
title: Nuvoton NCT7363Y Hardware Monitoring IC
maintainers:
- Ban Feng <kcfeng0@nuvoton.com>
description: |
The NCT7363Y is a fan controller which provides up to 16 independent
FAN input monitors, and up to 16 independent PWM outputs with SMBus interface.
Datasheets: Available from Nuvoton upon request
properties:
compatible:
enum:
- nuvoton,nct7363
- nuvoton,nct7362
reg:
maxItems: 1
"#pwm-cells":
const: 2
patternProperties:
"^fan-[0-9]+$":
$ref: fan-common.yaml#
unevaluatedProperties: false
required:
- pwms
- tach-ch
required:
- compatible
- reg
- "#pwm-cells"
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
hwmon: hwmon@22 {
compatible = "nuvoton,nct7363";
reg = <0x22>;
#pwm-cells = <2>;
fan-0 {
pwms = <&hwmon 0 50000>;
tach-ch = /bits/ 8 <0x00>;
};
fan-1 {
pwms = <&hwmon 1 50000>;
tach-ch = /bits/ 8 <0x01>;
};
};
};

View File

@ -0,0 +1,148 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/pmbus/isil,isl68137.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Renesas Digital Multiphase Voltage Regulators with PMBus
maintainers:
- Grant Peltier <grant.peltier.jg@renesas.com>
description: |
Renesas digital multiphase voltage regulators with PMBus.
https://www.renesas.com/en/products/power-management/multiphase-power/multiphase-dcdc-switching-controllers
properties:
compatible:
enum:
- isil,isl68137
- renesas,isl68220
- renesas,isl68221
- renesas,isl68222
- renesas,isl68223
- renesas,isl68224
- renesas,isl68225
- renesas,isl68226
- renesas,isl68227
- renesas,isl68229
- renesas,isl68233
- renesas,isl68239
- renesas,isl69222
- renesas,isl69223
- renesas,isl69224
- renesas,isl69225
- renesas,isl69227
- renesas,isl69228
- renesas,isl69234
- renesas,isl69236
- renesas,isl69239
- renesas,isl69242
- renesas,isl69243
- renesas,isl69247
- renesas,isl69248
- renesas,isl69254
- renesas,isl69255
- renesas,isl69256
- renesas,isl69259
- isil,isl69260
- renesas,isl69268
- isil,isl69269
- renesas,isl69298
- renesas,raa228000
- renesas,raa228004
- renesas,raa228006
- renesas,raa228228
- renesas,raa229001
- renesas,raa229004
reg:
maxItems: 1
'#address-cells':
const: 1
'#size-cells':
const: 0
patternProperties:
"^channel@([0-3])$":
type: object
description:
Container for properties specific to a particular channel (rail).
properties:
reg:
description: The channel (rail) index.
items:
minimum: 0
maximum: 3
vout-voltage-divider:
description: |
Resistances of a voltage divider placed between Vout and the voltage
sense (Vsense) pin for the given channel (rail). It has two numbers
representing the resistances of the voltage divider provided as
<Rout Rtotal> which yields an adjusted Vout as
Vout_adj = Vout * Rtotal / Rout given the original Vout as reported
by the Vsense pin. Given a circuit configuration similar to the one
below, Rtotal = R1 + Rout.
Vout ----.
|
.-----.
| R1 |
'-----'
|
+---- Vsense
|
.-----.
| Rout|
'-----'
|
GND
$ref: /schemas/types.yaml#/definitions/uint32-array
minItems: 2
maxItems: 2
required:
- reg
additionalProperties: false
required:
- compatible
- reg
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
isl68239@60 {
compatible = "isil,isl68137";
reg = <0x60>;
};
};
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
isl68239@60 {
compatible = "renesas,isl68239";
reg = <0x60>;
#address-cells = <1>;
#size-cells = <0>;
channel@0 {
reg = <0>;
vout-voltage-divider = <1000 2000>; // Reported Vout/Pout would be scaled by 2
};
};
};

View File

@ -0,0 +1,75 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/pmbus/mps,mp2975.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: MPS MP2975 Synchronous Buck Regulator
maintainers:
- Naresh Solanki <naresh.solanki@9elements.com>
description:
The MPS MP2971, MP2973 & MP2975 is a multi-phase voltage regulator
designed for use in high-performance computing and server
applications. It supports I2C/PMBus for control and monitoring.
properties:
compatible:
enum:
- mps,mp2971
- mps,mp2973
- mps,mp2975
reg:
maxItems: 1
interrupts:
maxItems: 1
regulators:
type: object
description:
List of regulators provided by this controller.
patternProperties:
"^vout[0-1]$":
$ref: /schemas/regulator/regulator.yaml#
type: object
unevaluatedProperties: false
additionalProperties: false
required:
- compatible
- reg
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
regulator@58 {
compatible = "mps,mp2973";
reg = <0x58>;
interrupt-parent = <&smb_pex_cpu1_event>;
interrupts = <12 IRQ_TYPE_LEVEL_LOW>;
regulators {
vout0 {
regulator-name = "pvccin_cpu1";
regulator-enable-ramp-delay = <200>;
};
vout1 {
regulator-name = "pvccfa_ehv_fivra_cpu1";
regulator-enable-ramp-delay = <200>;
};
};
};
};

View File

@ -0,0 +1,83 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/pmbus/ti,tps25990.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Texas Instruments TPS25990 Stackable eFuse
maintainers:
- Jerome Brunet <jbrunet@baylibre.com>
description:
The TI TPS25990 is an integrated, high-current circuit
protection and power management device with PMBUS interface
properties:
compatible:
const: ti,tps25990
reg:
maxItems: 1
ti,rimon-micro-ohms:
description:
micro Ohms value of the resistance installed between the Imon pin
and the ground reference.
interrupts:
description: PMBUS SMB Alert Interrupt.
maxItems: 1
regulators:
type: object
description:
list of regulators provided by this controller.
properties:
vout:
$ref: /schemas/regulator/regulator.yaml#
type: object
unevaluatedProperties: false
gpdac1:
$ref: /schemas/regulator/regulator.yaml#
type: object
unevaluatedProperties: false
gpdac2:
$ref: /schemas/regulator/regulator.yaml#
type: object
unevaluatedProperties: false
additionalProperties: false
required:
- compatible
- reg
- ti,rimon-micro-ohms
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
hw-monitor@46 {
compatible = "ti,tps25990";
reg = <0x46>;
interrupt-parent = <&gpio>;
interrupts = <42 IRQ_TYPE_LEVEL_LOW>;
ti,rimon-micro-ohms = <1370000000>;
regulators {
cpu0_vout: vout {
regulator-name = "main_cpu0";
};
};
};
};

View File

@ -0,0 +1,62 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/pmbus/vicor,pli1209bc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Vicor PLI1209BC Power Regulator
maintainers:
- Marcello Sylvester Bauer <sylv@sylv.io>
- Naresh Solanki <naresh.solanki@9elements.com>
description:
The Vicor PLI1209BC is a Digital Supervisor with Isolation for use
with BCM Bus Converter Modules.
properties:
compatible:
enum:
- vicor,pli1209bc
reg:
maxItems: 1
regulators:
type: object
description:
List of regulators provided by this controller.
properties:
vout2:
$ref: /schemas/regulator/regulator.yaml#
type: object
unevaluatedProperties: false
additionalProperties: false
required:
- compatible
- reg
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
regulator@5f {
compatible = "vicor,pli1209bc";
reg = <0x5f>;
regulators {
p12v_d: vout2 {
regulator-name = "bcm3";
regulator-boot-on;
};
};
};
};

View File

@ -31,6 +31,16 @@ properties:
it must be self resetting edge interrupts.
maxItems: 1
fan-stop-to-start-percent:
description:
Minimum fan RPM in percent to start when stopped.
minimum: 0
maximum: 100
fan-stop-to-start-us:
description:
Time to wait in microseconds after start when stopped.
pulses-per-revolution:
description:
Define the number of pulses per fan revolution for each tachometer

View File

@ -0,0 +1,64 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/renesas,isl28022.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Renesas ISL28022 power monitor
maintainers:
- Carsten Spieß <mail@carsten-spiess.de>
description: |
The ISL28022 is a power monitor with I2C interface. The device monitors
voltage, current via shunt resistor and calculated power.
Datasheets:
https://www.renesas.com/us/en/www/doc/datasheet/isl28022.pdf
properties:
compatible:
const: renesas,isl28022
reg:
maxItems: 1
shunt-resistor-micro-ohms:
description:
Shunt resistor value in micro-Ohm
minimum: 800
default: 10000
renesas,shunt-range-microvolt:
description:
Maximal shunt voltage range of +/- 40 mV, 80 mV, 160 mV or 320 mV
default: 320000
enum: [40000, 80000, 160000, 320000]
renesas,average-samples:
description:
Number of samples to be used to report voltage, current and power values.
default: 1
$ref: /schemas/types.yaml#/definitions/uint32
enum: [1, 2, 4, 8, 16, 32, 64, 128]
required:
- compatible
- reg
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
power-monitor@40 {
compatible = "renesas,isl28022";
reg = <0x40>;
shunt-resistor-micro-ohms = <8000>;
renesas,shunt-range-microvolt = <40000>;
renesas,average-samples = <128>;
};
};

View File

@ -0,0 +1,86 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/ti,amc6821.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: AMC6821 Intelligent Temperature Monitor and PWM Fan Controller
maintainers:
- Farouk Bouabid <farouk.bouabid@cherry.de>
- Quentin Schulz <quentin.schulz@cherry.de>
description:
Intelligent temperature monitor and pulse-width modulation (PWM) fan
controller.
properties:
compatible:
oneOf:
- items:
- const: tsd,mule
- const: ti,amc6821
- const: ti,amc6821
reg:
maxItems: 1
i2c-mux:
type: object
required:
- compatible
- reg
if:
properties:
compatible:
contains:
const: tsd,mule
then:
required:
- i2c-mux
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
fan@18 {
compatible = "ti,amc6821";
reg = <0x18>;
};
};
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
fan@18 {
compatible = "tsd,mule", "ti,amc6821";
reg = <0x18>;
i2c-mux {
compatible = "tsd,mule-i2c-mux";
#address-cells = <1>;
#size-cells = <0>;
i2c@0 {
reg = <0x0>;
#address-cells = <1>;
#size-cells = <0>;
rtc@6f {
compatible = "isil,isl1208";
reg = <0x6f>;
};
};
};
};
};
...

View File

@ -20,6 +20,7 @@ description: |
properties:
compatible:
enum:
- silergy,sy24655
- ti,ina209
- ti,ina219
- ti,ina220

View File

@ -4,22 +4,26 @@
$id: http://devicetree.org/schemas/hwmon/ti,tmp108.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: TMP108 temperature sensor
title: TMP108/P3T1085(NXP) temperature sensor
maintainers:
- Krzysztof Kozlowski <krzk@kernel.org>
description: |
The TMP108 is a digital-output temperature sensor with a
The TMP108/P3T1085(NXP) is a digital-output temperature sensor with a
dynamically-programmable limit window, and under- and overtemperature
alert functions.
P3T1085(NXP) support I3C.
Datasheets:
https://www.ti.com/product/TMP108
https://www.nxp.com/docs/en/data-sheet/P3T1085UK.pdf
properties:
compatible:
enum:
- nxp,p3t1085
- ti,tmp108
interrupts:

View File

@ -153,12 +153,6 @@ properties:
- isil,isl29028
# Intersil ISL29030 Ambient Light and Proximity Sensor
- isil,isl29030
# Intersil ISL68137 Digital Output Configurable PWM Controller
- isil,isl68137
# Intersil ISL69260 PMBus Voltage Regulator
- isil,isl69260
# Intersil ISL69269 PMBus Voltage Regulator
- isil,isl69269
# Intersil ISL76682 Ambient Light Sensor
- isil,isl76682
# JEDEC JESD300 (SPD5118) Hub and Serial Presence Detect
@ -279,12 +273,6 @@ properties:
- mps,mp2888
# Monolithic Power Systems Inc. multi-phase controller mp2891
- mps,mp2891
# Monolithic Power Systems Inc. multi-phase controller mp2971
- mps,mp2971
# Monolithic Power Systems Inc. multi-phase controller mp2973
- mps,mp2973
# Monolithic Power Systems Inc. multi-phase controller mp2975
- mps,mp2975
# Monolithic Power Systems Inc. multi-phase controller mp2993
- mps,mp2993
# Monolithic Power Systems Inc. multi-phase hot-swap controller mp5920
@ -357,8 +345,6 @@ properties:
- swir,mangoh-iotport-spi
# Ambient Light Sensor with SMBUS/Two Wire Serial Interface
- taos,tsl2550
# Temperature Monitoring and Fan Control
- ti,amc6821
# Temperature and humidity sensor with i2c interface
- ti,hdc1000
# Temperature and humidity sensor with i2c interface
@ -400,8 +386,6 @@ properties:
- ti,tps546d24
# I2C Touch-Screen Controller
- ti,tsc2003
# Vicor Corporation Digital Supervisor
- vicor,pli1209bc
# Winbond/Nuvoton H/W Monitor
- winbond,w83793

View File

@ -178,10 +178,11 @@ Writing an unsupported mode will result in an invalid parameter error.
available on the F71858FG / F8000 if the fan channel is in RPM mode.
* 2: Normal auto mode
You can define a number of temperature/fan speed trip points, which % the
fan should run at at this temp and which temp a fan should follow using the
standard sysfs interface. The number and type of trip points is chip
depended, see which files are available in sysfs.
You can define a number of temperature/fan speed trip points that specify
the percentage at which the fan should run at each temperature, and which
temperature sensor a fan should follow, using the standard sysfs interface.
The number and type of trip points are chip dependent - see the available
files in sysfs.
Fan/PWM channel 3 of the F8000 is always in this mode!
* 3: Thermostat mode (Only available on the F8000 when in duty cycle mode)

View File

@ -53,6 +53,27 @@ Supported chips:
https://www.ti.com/
* Texas Instruments INA260
Prefix: 'ina260'
Addresses: I2C 0x40 - 0x4f
Datasheet: Publicly available at the Texas Instruments website
https://www.ti.com/
* Silergy SY24655
Prefix: 'sy24655'
Addresses: I2C 0x40 - 0x4f
Datasheet: Publicly available at the Silergy website
https://us1.silergy.com/
Author: Lothar Felten <lothar.felten@gmail.com>
Description
@ -72,6 +93,14 @@ INA230 and INA231 are high or low side current shunt and power monitors
with an I2C interface. The chips monitor both a shunt voltage drop and
bus supply voltage.
INA260 is a high or low side current and power monitor with integrated shunt
resistor.
The SY24655 is a high- and low-side current shunt and power monitor with an I2C
interface. The SY24655 supports both shunt drop and supply voltage, with
programmable calibration value and conversion times. The SY24655 can also
calculate average power for use in energy conversion.
The shunt value in micro-ohms can be set via platform data or device tree at
compile-time or via the shunt_resistor attribute in sysfs at run-time. Please
refer to the Documentation/devicetree/bindings/hwmon/ti,ina2xx.yaml for bindings
@ -87,16 +116,16 @@ The actual programmed interval may vary from the desired value.
General sysfs entries
---------------------
======================= ===============================
======================= ===============================================
in0_input Shunt voltage(mV) channel
in1_input Bus voltage(mV) channel
curr1_input Current(mA) measurement channel
power1_input Power(uW) measurement channel
shunt_resistor Shunt resistance(uOhm) channel
======================= ===============================
shunt_resistor Shunt resistance(uOhm) channel (not for ina260)
======================= ===============================================
Sysfs entries for ina226, ina230 and ina231 only
------------------------------------------------
Additional sysfs entries for ina226, ina230, ina231, ina260, and sy24655
------------------------------------------------------------------------
======================= ====================================================
curr1_lcrit Critical low current
@ -117,6 +146,13 @@ update_interval data conversion time; affects number of samples used
to average results for shunt and bus voltages.
======================= ====================================================
Sysfs entries for sy24655 only
------------------------------
======================= ====================================================
power1_average average power from last reading to the present.
======================= ====================================================
.. note::
- Configure `shunt_resistor` before configure `power1_crit`, because power

View File

@ -96,6 +96,7 @@ Hardware Monitoring Kernel Drivers
ir35221
ir38064
ir36021
isl28022
isl68137
it87
jc42
@ -174,6 +175,7 @@ Hardware Monitoring Kernel Drivers
mpq8785
nct6683
nct6775
nct7363
nct7802
nct7904
npcm750-pwm-fan

View File

@ -0,0 +1,63 @@
.. SPDX-License-Identifier: GPL-2.0-or-later
Kernel driver isl28022
======================
Supported chips:
* Renesas ISL28022
Prefix: 'isl28022'
Addresses scanned: none
Datasheet: Publicly available at the Renesas website
https://www.renesas.com/us/en/www/doc/datasheet/isl28022.pdf
Author:
Carsten Spieß <mail@carsten-spiess.de>
Description
-----------
The ISL28022 is a power monitor with I2C interface. The device monitors
voltage, current via shunt resistor and calculated power.
Usage Notes
-----------
This driver does not auto-detect devices. You will have to instantiate the
device explicitly. Please see Documentation/i2c/instantiating-devices.rst for
details.
The shunt value in micro-ohms, shunt voltage range and averaging can be set
with device properties.
Please refer to the Documentation/devicetree/bindings/hwmon/isl,isl28022.yaml
for bindings if the device tree is used.
The driver supports only shunt and bus continuous ADC mode at 15bit resolution.
Averaging can be set from 1 to 128 samples (power of 2) on both channels.
Shunt voltage range of 40, 80, 160 or 320mV is allowed
The bus voltage range is 60V fixed.
Sysfs entries
-------------
The following attributes are supported. All attributes are read-only.
======================= =======================================================
in0_input bus voltage (milli Volt)
curr1_input current (milli Ampere)
power1_input power (micro Watt)
======================= =======================================================
Debugfs entries
---------------
The following attributes are supported. All attributes are read-only.
======================= =======================================================
shunt_voltage shunt voltage (micro Volt)
======================= =======================================================

View File

@ -1,3 +1,5 @@
.. SPDX-License-Identifier: GPL-2.0
Kernel driver ltc2978
=====================
@ -117,6 +119,14 @@ Supported chips:
Datasheet: https://www.analog.com/en/products/ltc3889
* Linear Technology LTC7841
Prefix: 'ltc7841'
Addresses scanned: -
Datasheet: https://www.analog.com/en/products/ltc7841
* Linear Technology LTC7880
Prefix: 'ltc7880'
@ -290,6 +300,7 @@ in[N]_label "vout[1-8]".
LTC7880, LTM4644, LTM4675, LTM4676, LTM4677, LTM4678,
LTM4680, LTM4700: N=2-3
- LTC3883: N=2
- LTC7841: N=2
in[N]_input Measured output voltage.
@ -420,6 +431,7 @@ curr[N]_label "iout[1-4]".
LTM4664, LTM4675, LTM4676, LTM4677, LTM4678, LTM4680,
LTM4700: N=2-3
- LTC3883: N=2
- LTC7841: N=2
curr[N]_input Measured output current.

View File

@ -136,7 +136,7 @@ PEC Support
When reading a register value, the PEC byte is computed and sent by the chip.
PEC on word data transaction respresents a signifcant increase in bandwitdh
PEC on word data transaction represents a significant increase in bandwidth
usage (+33% for both write and reads) in normal conditions.
Since this operation implies there will be an extra delay to each

View File

@ -0,0 +1,35 @@
.. SPDX-License-Identifier: GPL-2.0
Kernel driver nct7363
=====================
Supported chip:
* Nuvoton NCT7363Y
Prefix: nct7363
Addresses: I2C 0x20, 0x21, 0x22, 0x23
Author: Ban Feng <kcfeng0@nuvoton.com>
Description
-----------
The NCT7363Y is a fan controller which provides up to 16 independent
FAN input monitors, and up to 16 independent PWM outputs with SMBus interface.
Sysfs entries
-------------
Currently, the driver supports the following features:
========== ==========================================
fanX_input provide current fan rotation value in RPM
fanX_alarm report fan low speed real status
fanX_min get or set fan count threshold
pwmX get or set PWM fan control value.
========== ==========================================

View File

@ -308,6 +308,10 @@ currently provides a flags field with four bits used::
#define PMBUS_READ_STATUS_AFTER_FAILED_CHECK BIT(3)
#define PMBUS_NO_WRITE_PROTECT BIT(4)
#define PMBUS_USE_COEFFICIENTS_CMD BIT(5)
struct pmbus_platform_data {
u32 flags; /* Device specific flags */
@ -358,3 +362,14 @@ This can be done by reading a known register. By setting this flag the
driver will try to read the STATUS register after each failed
register check. This read may fail, but it will put the chip into a
known state.
PMBUS_NO_WRITE_PROTECT
Some PMBus chips respond with invalid data when reading the WRITE_PROTECT
register. For such chips, this flag should be set so that the PMBus core
driver doesn't use the WRITE_PROTECT command to determine its behavior.
PMBUS_USE_COEFFICIENTS_CMD
When this flag is set the PMBus core driver will use the COEFFICIENTS
register to initialize the coefficients for the direct mode format.

View File

@ -39,7 +39,7 @@ Controlling fan speed
---------------------
The SCH5627 allows for partially controlling the fan speed. If a temperature
channel excedes tempX_max, all fans are forced to maximum speed. The same is not
channel exceeds tempX_max, all fans are forced to maximum speed. The same is not
true for tempX_crit, presumably some other measures to cool down the system are
take in this case.
In which way the value of fanX_min affects the fan speed is currently unknown.

View File

@ -42,4 +42,18 @@ humidity1_input Measured humidity in %H
update_interval The minimum interval for polling the sensor,
in milliseconds. Writable. Must be at least
2000.
heater_power The requested heater power, in milliwatts.
Available values: 20, 110, 200 (default: 200).
heater_time The requested operating time of the heater,
in milliseconds.
Available values: 100, 1000 (default 1000).
heater_enable Enable the heater with the selected power
and for the selected time in order to remove
condensed water from the sensor surface. The
heater cannot be manually turned off once
enabled (it will automatically turn off
after completing its operation).
- 0: turned off (read-only value)
- 1: turn on
=============== ============================================

View File

@ -3,6 +3,14 @@ Kernel driver tmp108
Supported chips:
* NXP P3T1085
Prefix: 'p3t1085'
Addresses scanned: none
Datasheet: https://www.nxp.com/docs/en/data-sheet/P3T1085UK.pdf
* Texas Instruments TMP108
Prefix: 'tmp108'

View File

@ -12104,6 +12104,14 @@ F: drivers/isdn/Makefile
F: drivers/isdn/hardware/
F: drivers/isdn/mISDN/
ISL28022 HARDWARE MONITORING DRIVER
M: Carsten Spieß <mail@carsten-spiess.de>
L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/hwmon/renesas,isl28022.yaml
F: Documentation/hwmon/isl28022.rst
F: drivers/hwmon/isl28022.c
ISOFS FILESYSTEM
M: Jan Kara <jack@suse.cz>
L: linux-fsdevel@vger.kernel.org
@ -15947,6 +15955,14 @@ S: Maintained
F: Documentation/devicetree/bindings/hwmon/nuvoton,nct6775.yaml
F: drivers/hwmon/nct6775-i2c.c
NCT7363 HARDWARE MONITOR DRIVER
M: Ban Feng <kcfeng0@nuvoton.com>
L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/hwmon/nuvoton,nct7363.yaml
F: Documentation/hwmon/nct7363.rst
F: drivers/hwmon/nct7363.c
NETCONSOLE
M: Breno Leitao <leitao@debian.org>
S: Maintained
@ -16743,13 +16759,6 @@ S: Maintained
F: Documentation/hwmon/nzxt-kraken3.rst
F: drivers/hwmon/nzxt-kraken3.c
NZXT-SMART2 HARDWARE MONITORING DRIVER
M: Aleksandr Mezin <mezin.alexander@gmail.com>
L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/nzxt-smart2.rst
F: drivers/hwmon/nzxt-smart2.c
OBJAGG
M: Jiri Pirko <jiri@resnulli.us>
L: netdev@vger.kernel.org
@ -22928,6 +22937,12 @@ F: include/linux/dma/k3-udma-glue.h
F: include/linux/dma/ti-cppi5.h
X: drivers/dma/ti/cppi41.c
TEXAS INSTRUMENTS TPS25990 HARDWARE MONITOR DRIVER
M: Jerome Brunet <jbrunet@baylibre.com>
L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/hwmon/pmbus/ti,tps25990.yaml
TEXAS INSTRUMENTS TPS23861 PoE PSE DRIVER
M: Robert Marko <robert.marko@sartura.hr>
M: Luka Perkov <luka.perkov@sartura.hr>

View File

@ -855,6 +855,17 @@ config SENSORS_CORETEMP
sensor inside your CPU. Most of the family 6 CPUs
are supported. Check Documentation/hwmon/coretemp.rst for details.
config SENSORS_ISL28022
tristate "Renesas ISL28022"
depends on I2C
select REGMAP_I2C
help
If you say yes here you get support for ISL28022 power monitor.
Check Documentation/hwmon/isl28022.rst for details.
This driver can also be built as a module. If so, the module
will be called isl28022.
config SENSORS_IT87
tristate "ITE IT87xx and compatibles"
depends on HAS_IOPORT
@ -1670,6 +1681,17 @@ config SENSORS_NCT6775_I2C
This driver can also be built as a module. If so, the module
will be called nct6775-i2c.
config SENSORS_NCT7363
tristate "Nuvoton NCT7363Y"
depends on I2C
select REGMAP_I2C
help
If you say yes here you get support for the Nuvoton NCT7363Y
hardware monitoring chip.
This driver can also be built as a module. If so, the module
will be called nct7363.
config SENSORS_NCT7802
tristate "Nuvoton NCT7802Y"
depends on I2C
@ -2167,11 +2189,12 @@ config SENSORS_INA2XX
select REGMAP_I2C
help
If you say yes here you get support for INA219, INA220, INA226,
INA230, and INA231 power monitor chips.
INA230, INA231, INA260, and SY24655 power monitor chips.
The INA2xx driver is configured for the default configuration of
the part as described in the datasheet.
Default value for Rshunt is 10 mOhms.
Default value for Rshunt is 10 mOhms except for INA260 which has an
internal 2 mOhm shunt resistor.
This driver can also be built as a module. If so, the module
will be called ina2xx.
@ -2274,10 +2297,12 @@ config SENSORS_TMP103
config SENSORS_TMP108
tristate "Texas Instruments TMP108"
depends on I2C
depends on I3C || !I3C
select REGMAP_I2C
select REGMAP_I3C if I3C
help
If you say yes here you get support for Texas Instruments TMP108
sensor chips.
sensor chips and NXP P3T1085.
This driver can also be built as a module. If so, the module
will be called tmp108.

View File

@ -103,6 +103,7 @@ obj-$(CONFIG_SENSORS_INA2XX) += ina2xx.o
obj-$(CONFIG_SENSORS_INA238) += ina238.o
obj-$(CONFIG_SENSORS_INA3221) += ina3221.o
obj-$(CONFIG_SENSORS_INTEL_M10_BMC_HWMON) += intel-m10-bmc-hwmon.o
obj-$(CONFIG_SENSORS_ISL28022) += isl28022.o
obj-$(CONFIG_SENSORS_IT87) += it87.o
obj-$(CONFIG_SENSORS_JC42) += jc42.o
obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o
@ -171,6 +172,7 @@ obj-$(CONFIG_SENSORS_NCT6775_CORE) += nct6775-core.o
nct6775-objs := nct6775-platform.o
obj-$(CONFIG_SENSORS_NCT6775) += nct6775.o
obj-$(CONFIG_SENSORS_NCT6775_I2C) += nct6775-i2c.o
obj-$(CONFIG_SENSORS_NCT7363) += nct7363.o
obj-$(CONFIG_SENSORS_NCT7802) += nct7802.o
obj-$(CONFIG_SENSORS_NCT7904) += nct7904.o
obj-$(CONFIG_SENSORS_NPCM7XX) += npcm750-pwm-fan.o

View File

@ -1531,7 +1531,7 @@ static struct platform_driver abituguru_driver = {
.pm = pm_sleep_ptr(&abituguru_pm),
},
.probe = abituguru_probe,
.remove_new = abituguru_remove,
.remove = abituguru_remove,
};
static int __init abituguru_detect(void)

View File

@ -1147,12 +1147,12 @@ static int abituguru3_resume(struct device *dev)
static DEFINE_SIMPLE_DEV_PM_OPS(abituguru3_pm, abituguru3_suspend, abituguru3_resume);
static struct platform_driver abituguru3_driver = {
.driver = {
.driver = {
.name = ABIT_UGURU3_NAME,
.pm = pm_sleep_ptr(&abituguru3_pm),
},
.probe = abituguru3_probe,
.remove_new = abituguru3_remove,
.remove = abituguru3_remove,
};
static int __init abituguru3_dmi_detect(void)

View File

@ -680,8 +680,9 @@ static int setup_attrs(struct acpi_power_meter_resource *resource)
{
int res = 0;
/* _PMD method is optional. */
res = read_domain_devices(resource);
if (res)
if (res != -ENODEV)
return res;
if (resource->caps.flags & POWER_METER_CAN_MEASURE) {

View File

@ -22,6 +22,7 @@
#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of_platform.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@ -893,7 +894,6 @@ static bool amc6821_volatile_reg(struct device *dev, unsigned int reg)
static const struct regmap_config amc6821_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = AMC6821_REG_CONF3,
.volatile_reg = amc6821_volatile_reg,
.cache_type = REGCACHE_MAPLE,
};
@ -920,6 +920,13 @@ static int amc6821_probe(struct i2c_client *client)
if (err)
return err;
if (of_device_is_compatible(dev->of_node, "tsd,mule")) {
err = devm_of_platform_populate(dev);
if (err)
return dev_err_probe(dev, err,
"Failed to create sub-devices\n");
}
hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
data, &amc6821_chip_info,
amc6821_groups);
@ -927,7 +934,7 @@ static int amc6821_probe(struct i2c_client *client)
}
static const struct i2c_device_id amc6821_id[] = {
{ "amc6821", 0 },
{ "amc6821" },
{ }
};
@ -937,6 +944,9 @@ static const struct of_device_id __maybe_unused amc6821_of_match[] = {
{
.compatible = "ti,amc6821",
},
{
.compatible = "tsd,mule",
},
{ }
};

View File

@ -534,7 +534,7 @@ MODULE_DEVICE_TABLE(of, aspeed_pwm_tach_match);
static struct platform_driver aspeed_pwm_tach_driver = {
.probe = aspeed_pwm_tach_probe,
.remove_new = aspeed_pwm_tach_remove,
.remove = aspeed_pwm_tach_remove,
.driver = {
.name = "aspeed-g6-pwm-tach",
.of_match_table = aspeed_pwm_tach_match,

View File

@ -141,6 +141,7 @@ static umode_t cros_ec_hwmon_is_visible(const void *data, enum hwmon_sensor_type
}
static const struct hwmon_channel_info * const cros_ec_hwmon_info[] = {
HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ),
HWMON_CHANNEL_INFO(fan,
HWMON_F_INPUT | HWMON_F_FAULT,
HWMON_F_INPUT | HWMON_F_FAULT,

View File

@ -473,7 +473,7 @@ static void da9052_hwmon_remove(struct platform_device *pdev)
static struct platform_driver da9052_hwmon_driver = {
.probe = da9052_hwmon_probe,
.remove_new = da9052_hwmon_remove,
.remove = da9052_hwmon_remove,
.driver = {
.name = "da9052-hwmon",
},

View File

@ -2721,7 +2721,7 @@ static struct platform_driver dme1737_isa_driver = {
.name = "dme1737",
},
.probe = dme1737_isa_probe,
.remove_new = dme1737_isa_remove,
.remove = dme1737_isa_remove,
};
/* ---------------------------------------------------------------------

View File

@ -1497,7 +1497,7 @@ static struct platform_driver f71805f_driver = {
.name = DRVNAME,
},
.probe = f71805f_probe,
.remove_new = f71805f_remove,
.remove = f71805f_remove,
};
static int __init f71805f_device_add(unsigned short address,

View File

@ -2658,7 +2658,7 @@ static struct platform_driver f71882fg_driver = {
.name = DRVNAME,
},
.probe = f71882fg_probe,
.remove_new = f71882fg_remove,
.remove = f71882fg_remove,
};
static int __init f71882fg_init(void)

View File

@ -231,15 +231,8 @@ gsc_hwmon_read_string(struct device *dev, enum hwmon_sensor_types type,
return 0;
}
static umode_t
gsc_hwmon_is_visible(const void *_data, enum hwmon_sensor_types type, u32 attr,
int ch)
{
return 0444;
}
static const struct hwmon_ops gsc_hwmon_ops = {
.is_visible = gsc_hwmon_is_visible,
.visible = 0444,
.read = gsc_hwmon_read,
.read_string = gsc_hwmon_read_string,
};

View File

@ -145,6 +145,17 @@ static const struct class hwmon_class = {
static DEFINE_IDA(hwmon_ida);
static umode_t hwmon_is_visible(const struct hwmon_ops *ops,
const void *drvdata,
enum hwmon_sensor_types type,
u32 attr, int channel)
{
if (ops->visible)
return ops->visible;
return ops->is_visible(drvdata, type, attr, channel);
}
/* Thermal zone handling */
/*
@ -267,8 +278,8 @@ static int hwmon_thermal_register_sensors(struct device *dev)
int err;
if (!(info[i]->config[j] & HWMON_T_INPUT) ||
!chip->ops->is_visible(drvdata, hwmon_temp,
hwmon_temp_input, j))
!hwmon_is_visible(chip->ops, drvdata, hwmon_temp,
hwmon_temp_input, j))
continue;
err = hwmon_thermal_add_sensor(dev, j);
@ -506,7 +517,7 @@ static struct attribute *hwmon_genattr(const void *drvdata,
const char *name;
bool is_string = is_string_attr(type, attr);
mode = ops->is_visible(drvdata, type, attr, index);
mode = hwmon_is_visible(ops, drvdata, type, attr, index);
if (!mode)
return ERR_PTR(-ENOENT);
@ -1033,7 +1044,7 @@ hwmon_device_register_with_info(struct device *dev, const char *name,
if (!dev || !name || !chip)
return ERR_PTR(-EINVAL);
if (!chip->ops || !chip->ops->is_visible || !chip->info)
if (!chip->ops || !(chip->ops->visible || chip->ops->is_visible) || !chip->info)
return ERR_PTR(-EINVAL);
return __hwmon_device_register(dev, name, drvdata, chip, extra_groups);

View File

@ -29,12 +29,6 @@
#define REG_CTCTRL 0xF7
#define REG_TSTIMER 0xF8
static umode_t i5500_is_visible(const void *drvdata, enum hwmon_sensor_types type, u32 attr,
int channel)
{
return 0444;
}
static int i5500_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
long *val)
{
@ -84,7 +78,7 @@ static int i5500_read(struct device *dev, enum hwmon_sensor_types type, u32 attr
}
static const struct hwmon_ops i5500_ops = {
.is_visible = i5500_is_visible,
.visible = 0444,
.read = i5500_read,
};

View File

@ -568,7 +568,7 @@ static struct platform_driver i5k_amb_driver = {
.name = DRVNAME,
},
.probe = i5k_amb_probe,
.remove_new = i5k_amb_remove,
.remove = i5k_amb_remove,
};
static int __init i5k_amb_init(void)

View File

@ -51,17 +51,26 @@
#define INA226_ALERT_LIMIT 0x07
#define INA226_DIE_ID 0xFF
#define INA2XX_MAX_REGISTERS 8
/* SY24655 register definitions */
#define SY24655_EIN 0x0A
#define SY24655_ACCUM_CONFIG 0x0D
#define INA2XX_MAX_REGISTERS 0x0D
/* settings - depend on use case */
#define INA219_CONFIG_DEFAULT 0x399F /* PGA=8 */
#define INA226_CONFIG_DEFAULT 0x4527 /* averages=16 */
#define INA260_CONFIG_DEFAULT 0x6527 /* averages=16 */
#define SY24655_CONFIG_DEFAULT 0x4527 /* averages=16 */
/* (only for sy24655) */
#define SY24655_ACCUM_CONFIG_DEFAULT 0x044C /* continuous mode, clear after read*/
/* worst case is 68.10 ms (~14.6Hz, ina219) */
#define INA2XX_CONVERSION_RATE 15
#define INA2XX_MAX_DELAY 69 /* worst case delay in ms */
#define INA2XX_RSHUNT_DEFAULT 10000
#define INA260_RSHUNT 2000
/* bit mask for reading the averaging setting in the configuration register */
#define INA226_AVG_RD_MASK GENMASK(11, 9)
@ -95,6 +104,7 @@ static bool ina2xx_writeable_reg(struct device *dev, unsigned int reg)
case INA2XX_CALIBRATION:
case INA226_MASK_ENABLE:
case INA226_ALERT_LIMIT:
case SY24655_ACCUM_CONFIG:
return true;
default:
return false;
@ -125,10 +135,13 @@ static const struct regmap_config ina2xx_regmap_config = {
.writeable_reg = ina2xx_writeable_reg,
};
enum ina2xx_ids { ina219, ina226 };
enum ina2xx_ids { ina219, ina226, ina260, sy24655 };
struct ina2xx_config {
u16 config_default;
bool has_alerts; /* chip supports alerts and limits */
bool has_ishunt; /* chip has internal shunt resistor */
bool has_power_average; /* chip has internal shunt resistor */
int calibration_value;
int shunt_div;
int bus_voltage_shift;
@ -145,6 +158,7 @@ struct ina2xx_data {
long power_lsb_uW;
struct mutex config_lock;
struct regmap *regmap;
struct i2c_client *client;
};
static const struct ina2xx_config ina2xx_config[] = {
@ -155,6 +169,9 @@ static const struct ina2xx_config ina2xx_config[] = {
.bus_voltage_shift = 3,
.bus_voltage_lsb = 4000,
.power_lsb_factor = 20,
.has_alerts = false,
.has_ishunt = false,
.has_power_average = false,
},
[ina226] = {
.config_default = INA226_CONFIG_DEFAULT,
@ -163,6 +180,30 @@ static const struct ina2xx_config ina2xx_config[] = {
.bus_voltage_shift = 0,
.bus_voltage_lsb = 1250,
.power_lsb_factor = 25,
.has_alerts = true,
.has_ishunt = false,
.has_power_average = false,
},
[ina260] = {
.config_default = INA260_CONFIG_DEFAULT,
.shunt_div = 400,
.bus_voltage_shift = 0,
.bus_voltage_lsb = 1250,
.power_lsb_factor = 8,
.has_alerts = true,
.has_ishunt = true,
.has_power_average = false,
},
[sy24655] = {
.config_default = SY24655_CONFIG_DEFAULT,
.calibration_value = 4096,
.shunt_div = 400,
.bus_voltage_shift = 0,
.bus_voltage_lsb = 1250,
.power_lsb_factor = 25,
.has_alerts = true,
.has_ishunt = false,
.has_power_average = true,
},
};
@ -254,6 +295,15 @@ static int ina2xx_read_init(struct device *dev, int reg, long *val)
unsigned int regval;
int ret, retry;
if (data->config->has_ishunt) {
/* No calibration needed */
ret = regmap_read(regmap, reg, &regval);
if (ret < 0)
return ret;
*val = ina2xx_get_value(data, reg, regval);
return 0;
}
for (retry = 5; retry; retry--) {
ret = regmap_read(regmap, reg, &regval);
if (ret < 0)
@ -459,6 +509,41 @@ static int ina2xx_in_read(struct device *dev, u32 attr, int channel, long *val)
return 0;
}
/*
* Configuring the READ_EIN (bit 10) of the ACCUM_CONFIG register to 1
* can clear accumulator and sample_count after reading the EIN register.
* This way, the average power between the last read and the current
* read can be obtained. By combining with accurate time data from
* outside, the energy consumption during that period can be calculated.
*/
static int sy24655_average_power_read(struct ina2xx_data *data, u8 reg, long *val)
{
u8 template[6];
int ret;
long accumulator_24, sample_count;
/* 48-bit register read */
ret = i2c_smbus_read_i2c_block_data(data->client, reg, 6, template);
if (ret < 0)
return ret;
if (ret != 6)
return -EIO;
accumulator_24 = ((template[3] << 16) |
(template[4] << 8) |
template[5]);
sample_count = ((template[0] << 16) |
(template[1] << 8) |
template[2]);
if (sample_count <= 0) {
*val = 0;
return 0;
}
*val = DIV_ROUND_CLOSEST(accumulator_24, sample_count) * data->power_lsb_uW;
return 0;
}
static int ina2xx_power_read(struct device *dev, u32 attr, long *val)
{
struct ina2xx_data *data = dev_get_drvdata(dev);
@ -466,6 +551,8 @@ static int ina2xx_power_read(struct device *dev, u32 attr, long *val)
switch (attr) {
case hwmon_power_input:
return ina2xx_read_init(dev, INA2XX_POWER, val);
case hwmon_power_average:
return sy24655_average_power_read(data, SY24655_EIN, val);
case hwmon_power_crit:
return ina226_alert_limit_read(data, INA226_POWER_OVER_LIMIT_MASK,
INA2XX_POWER, val);
@ -624,6 +711,8 @@ static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type
u32 attr, int channel)
{
const struct ina2xx_data *data = _data;
bool has_alerts = data->config->has_alerts;
bool has_power_average = data->config->has_power_average;
enum ina2xx_ids chip = data->chip;
switch (type) {
@ -633,12 +722,12 @@ static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type
return 0444;
case hwmon_in_lcrit:
case hwmon_in_crit:
if (chip == ina226)
if (has_alerts)
return 0644;
break;
case hwmon_in_lcrit_alarm:
case hwmon_in_crit_alarm:
if (chip == ina226)
if (has_alerts)
return 0444;
break;
default:
@ -651,12 +740,12 @@ static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type
return 0444;
case hwmon_curr_lcrit:
case hwmon_curr_crit:
if (chip == ina226)
if (has_alerts)
return 0644;
break;
case hwmon_curr_lcrit_alarm:
case hwmon_curr_crit_alarm:
if (chip == ina226)
if (has_alerts)
return 0444;
break;
default:
@ -668,11 +757,15 @@ static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type
case hwmon_power_input:
return 0444;
case hwmon_power_crit:
if (chip == ina226)
if (has_alerts)
return 0644;
break;
case hwmon_power_crit_alarm:
if (chip == ina226)
if (has_alerts)
return 0444;
break;
case hwmon_power_average:
if (has_power_average)
return 0444;
break;
default:
@ -682,7 +775,7 @@ static umode_t ina2xx_is_visible(const void *_data, enum hwmon_sensor_types type
case hwmon_chip:
switch (attr) {
case hwmon_chip_update_interval:
if (chip == ina226)
if (chip == ina226 || chip == ina260)
return 0644;
break;
default:
@ -707,7 +800,8 @@ static const struct hwmon_channel_info * const ina2xx_info[] = {
HWMON_CHANNEL_INFO(curr, HWMON_C_INPUT | HWMON_C_CRIT | HWMON_C_CRIT_ALARM |
HWMON_C_LCRIT | HWMON_C_LCRIT_ALARM),
HWMON_CHANNEL_INFO(power,
HWMON_P_INPUT | HWMON_P_CRIT | HWMON_P_CRIT_ALARM),
HWMON_P_INPUT | HWMON_P_CRIT | HWMON_P_CRIT_ALARM |
HWMON_P_AVERAGE),
NULL
};
@ -791,7 +885,9 @@ static int ina2xx_init(struct device *dev, struct ina2xx_data *data)
u32 shunt;
int ret;
if (device_property_read_u32(dev, "shunt-resistor", &shunt) < 0)
if (data->config->has_ishunt)
shunt = INA260_RSHUNT;
else if (device_property_read_u32(dev, "shunt-resistor", &shunt) < 0)
shunt = INA2XX_RSHUNT_DEFAULT;
ret = ina2xx_set_shunt(data, shunt);
@ -802,7 +898,7 @@ static int ina2xx_init(struct device *dev, struct ina2xx_data *data)
if (ret < 0)
return ret;
if (data->chip == ina226) {
if (data->config->has_alerts) {
bool active_high = device_property_read_bool(dev, "ti,alert-polarity-active-high");
regmap_update_bits(regmap, INA226_MASK_ENABLE,
@ -810,6 +906,22 @@ static int ina2xx_init(struct device *dev, struct ina2xx_data *data)
INA226_ALERT_LATCH_ENABLE |
FIELD_PREP(INA226_ALERT_POLARITY, active_high));
}
if (data->config->has_power_average) {
if (data->chip == sy24655) {
/*
* Initialize the power accumulation method to continuous
* mode and clear the EIN register after each read of the
* EIN register
*/
ret = regmap_write(regmap, SY24655_ACCUM_CONFIG,
SY24655_ACCUM_CONFIG_DEFAULT);
if (ret < 0)
return ret;
}
}
if (data->config->has_ishunt)
return 0;
/*
* Calibration register is set to the best value, which eliminates
@ -836,6 +948,7 @@ static int ina2xx_probe(struct i2c_client *client)
return -ENOMEM;
/* set the device type */
data->client = client;
data->config = &ina2xx_config[chip];
data->chip = chip;
mutex_init(&data->config_lock);
@ -856,7 +969,8 @@ static int ina2xx_probe(struct i2c_client *client)
hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
data, &ina2xx_chip_info,
ina2xx_groups);
data->config->has_ishunt ?
NULL : ina2xx_groups);
if (IS_ERR(hwmon_dev))
return PTR_ERR(hwmon_dev);
@ -872,11 +986,17 @@ static const struct i2c_device_id ina2xx_id[] = {
{ "ina226", ina226 },
{ "ina230", ina226 },
{ "ina231", ina226 },
{ "ina260", ina260 },
{ "sy24655", sy24655 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ina2xx_id);
static const struct of_device_id __maybe_unused ina2xx_of_match[] = {
{
.compatible = "silergy,sy24655",
.data = (void *)sy24655
},
{
.compatible = "ti,ina219",
.data = (void *)ina219
@ -897,7 +1017,11 @@ static const struct of_device_id __maybe_unused ina2xx_of_match[] = {
.compatible = "ti,ina231",
.data = (void *)ina226
},
{ },
{
.compatible = "ti,ina260",
.data = (void *)ina260
},
{ }
};
MODULE_DEVICE_TABLE(of, ina2xx_of_match);

View File

@ -565,13 +565,6 @@ static const struct m10bmc_hwmon_board_data n6000bmc_hwmon_bdata = {
.hinfo = n6000bmc_hinfo,
};
static umode_t
m10bmc_hwmon_is_visible(const void *data, enum hwmon_sensor_types type,
u32 attr, int channel)
{
return 0444;
}
static const struct m10bmc_sdata *
find_sensor_data(struct m10bmc_hwmon *hw, enum hwmon_sensor_types type,
int channel)
@ -729,7 +722,7 @@ static int m10bmc_hwmon_read_string(struct device *dev,
}
static const struct hwmon_ops m10bmc_hwmon_ops = {
.is_visible = m10bmc_hwmon_is_visible,
.visible = 0444,
.read = m10bmc_hwmon_read,
.read_string = m10bmc_hwmon_read_string,
};

535
drivers/hwmon/isl28022.c Normal file
View File

@ -0,0 +1,535 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* isl28022.c - driver for Renesas ISL28022 power monitor chip monitoring
*
* Copyright (c) 2023 Carsten Spieß <mail@carsten-spiess.de>
*/
#include <linux/debugfs.h>
#include <linux/err.h>
#include <linux/hwmon.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/regmap.h>
/* ISL28022 registers */
#define ISL28022_REG_CONFIG 0x00
#define ISL28022_REG_SHUNT 0x01
#define ISL28022_REG_BUS 0x02
#define ISL28022_REG_POWER 0x03
#define ISL28022_REG_CURRENT 0x04
#define ISL28022_REG_CALIB 0x05
#define ISL28022_REG_SHUNT_THR 0x06
#define ISL28022_REG_BUS_THR 0x07
#define ISL28022_REG_INT 0x08
#define ISL28022_REG_AUX 0x09
#define ISL28022_REG_MAX ISL28022_REG_AUX
/* ISL28022 config flags */
/* mode flags */
#define ISL28022_MODE_SHIFT 0
#define ISL28022_MODE_MASK 0x0007
#define ISL28022_MODE_PWR_DOWN 0x0
#define ISL28022_MODE_TRG_S 0x1
#define ISL28022_MODE_TRG_B 0x2
#define ISL28022_MODE_TRG_SB 0x3
#define ISL28022_MODE_ADC_OFF 0x4
#define ISL28022_MODE_CONT_S 0x5
#define ISL28022_MODE_CONT_B 0x6
#define ISL28022_MODE_CONT_SB 0x7
/* shunt ADC settings */
#define ISL28022_SADC_SHIFT 3
#define ISL28022_SADC_MASK 0x0078
#define ISL28022_BADC_SHIFT 7
#define ISL28022_BADC_MASK 0x0780
#define ISL28022_ADC_12 0x0 /* 12 bit ADC */
#define ISL28022_ADC_13 0x1 /* 13 bit ADC */
#define ISL28022_ADC_14 0x2 /* 14 bit ADC */
#define ISL28022_ADC_15 0x3 /* 15 bit ADC */
#define ISL28022_ADC_15_1 0x8 /* 15 bit ADC, 1 sample */
#define ISL28022_ADC_15_2 0x9 /* 15 bit ADC, 2 samples */
#define ISL28022_ADC_15_4 0xA /* 15 bit ADC, 4 samples */
#define ISL28022_ADC_15_8 0xB /* 15 bit ADC, 8 samples */
#define ISL28022_ADC_15_16 0xC /* 15 bit ADC, 16 samples */
#define ISL28022_ADC_15_32 0xD /* 15 bit ADC, 32 samples */
#define ISL28022_ADC_15_64 0xE /* 15 bit ADC, 64 samples */
#define ISL28022_ADC_15_128 0xF /* 15 bit ADC, 128 samples */
/* shunt voltage range */
#define ISL28022_PG_SHIFT 11
#define ISL28022_PG_MASK 0x1800
#define ISL28022_PG_40 0x0 /* +/-40 mV */
#define ISL28022_PG_80 0x1 /* +/-80 mV */
#define ISL28022_PG_160 0x2 /* +/-160 mV */
#define ISL28022_PG_320 0x3 /* +/-3200 mV */
/* bus voltage range */
#define ISL28022_BRNG_SHIFT 13
#define ISL28022_BRNG_MASK 0x6000
#define ISL28022_BRNG_16 0x0 /* 16 V */
#define ISL28022_BRNG_32 0x1 /* 32 V */
#define ISL28022_BRNG_60 0x3 /* 60 V */
/* reset */
#define ISL28022_RESET 0x8000
struct isl28022_data {
struct regmap *regmap;
u32 shunt;
u32 gain;
u32 average;
};
static int isl28022_read_in(struct device *dev, u32 attr, int channel, long *val)
{
struct isl28022_data *data = dev_get_drvdata(dev);
unsigned int regval;
int err;
u16 sign_bit;
switch (channel) {
case 0:
switch (attr) {
case hwmon_in_input:
err = regmap_read(data->regmap,
ISL28022_REG_BUS, &regval);
if (err < 0)
return err;
/* driver supports only 60V mode (BRNG 11) */
*val = (long)(((u16)regval) & 0xFFFC);
break;
default:
return -EOPNOTSUPP;
}
break;
case 1:
switch (attr) {
case hwmon_in_input:
err = regmap_read(data->regmap,
ISL28022_REG_SHUNT, &regval);
if (err < 0)
return err;
switch (data->gain) {
case 8:
sign_bit = (regval >> 15) & 0x01;
*val = (long)((((u16)regval) & 0x7FFF) -
(sign_bit * 32768)) / 100;
break;
case 4:
sign_bit = (regval >> 14) & 0x01;
*val = (long)((((u16)regval) & 0x3FFF) -
(sign_bit * 16384)) / 100;
break;
case 2:
sign_bit = (regval >> 13) & 0x01;
*val = (long)((((u16)regval) & 0x1FFF) -
(sign_bit * 8192)) / 100;
break;
case 1:
sign_bit = (regval >> 12) & 0x01;
*val = (long)((((u16)regval) & 0x0FFF) -
(sign_bit * 4096)) / 100;
break;
}
break;
default:
return -EOPNOTSUPP;
}
break;
default:
return -EOPNOTSUPP;
}
return 0;
}
static int isl28022_read_current(struct device *dev, u32 attr, long *val)
{
struct isl28022_data *data = dev_get_drvdata(dev);
unsigned int regval;
int err;
switch (attr) {
case hwmon_curr_input:
err = regmap_read(data->regmap,
ISL28022_REG_CURRENT, &regval);
if (err < 0)
return err;
*val = ((long)regval * 1250L * (long)data->gain) /
(long)data->shunt;
break;
default:
return -EOPNOTSUPP;
}
return 0;
}
static int isl28022_read_power(struct device *dev, u32 attr, long *val)
{
struct isl28022_data *data = dev_get_drvdata(dev);
unsigned int regval;
int err;
switch (attr) {
case hwmon_power_input:
err = regmap_read(data->regmap,
ISL28022_REG_POWER, &regval);
if (err < 0)
return err;
*val = ((51200000L * ((long)data->gain)) /
(long)data->shunt) * (long)regval;
break;
default:
return -EOPNOTSUPP;
}
return 0;
}
static int isl28022_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
switch (type) {
case hwmon_in:
return isl28022_read_in(dev, attr, channel, val);
case hwmon_curr:
return isl28022_read_current(dev, attr, val);
case hwmon_power:
return isl28022_read_power(dev, attr, val);
default:
return -EOPNOTSUPP;
}
return 0;
}
static umode_t isl28022_is_visible(const void *data, enum hwmon_sensor_types type,
u32 attr, int channel)
{
switch (type) {
case hwmon_in:
switch (attr) {
case hwmon_in_input:
return 0444;
default:
break;
}
break;
case hwmon_curr:
switch (attr) {
case hwmon_curr_input:
return 0444;
default:
break;
}
break;
case hwmon_power:
switch (attr) {
case hwmon_power_input:
return 0444;
default:
break;
}
break;
default:
break;
}
return 0;
}
static const struct hwmon_channel_info *isl28022_info[] = {
HWMON_CHANNEL_INFO(in,
HWMON_I_INPUT, /* channel 0: bus voltage (mV) */
HWMON_I_INPUT), /* channel 1: shunt voltage (mV) */
HWMON_CHANNEL_INFO(curr,
HWMON_C_INPUT), /* channel 1: current (mA) */
HWMON_CHANNEL_INFO(power,
HWMON_P_INPUT), /* channel 1: power (µW) */
NULL
};
static const struct hwmon_ops isl28022_hwmon_ops = {
.is_visible = isl28022_is_visible,
.read = isl28022_read,
};
static const struct hwmon_chip_info isl28022_chip_info = {
.ops = &isl28022_hwmon_ops,
.info = isl28022_info,
};
static bool isl28022_is_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case ISL28022_REG_CONFIG:
case ISL28022_REG_CALIB:
case ISL28022_REG_SHUNT_THR:
case ISL28022_REG_BUS_THR:
case ISL28022_REG_INT:
case ISL28022_REG_AUX:
return true;
}
return false;
}
static bool isl28022_is_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case ISL28022_REG_CONFIG:
case ISL28022_REG_SHUNT:
case ISL28022_REG_BUS:
case ISL28022_REG_POWER:
case ISL28022_REG_CURRENT:
case ISL28022_REG_INT:
case ISL28022_REG_AUX:
return true;
}
return true;
}
static const struct regmap_config isl28022_regmap_config = {
.reg_bits = 8,
.val_bits = 16,
.max_register = ISL28022_REG_MAX,
.writeable_reg = isl28022_is_writeable_reg,
.volatile_reg = isl28022_is_volatile_reg,
.val_format_endian = REGMAP_ENDIAN_BIG,
.cache_type = REGCACHE_RBTREE,
.use_single_read = true,
.use_single_write = true,
};
static int shunt_voltage_show(struct seq_file *seqf, void *unused)
{
struct isl28022_data *data = seqf->private;
unsigned int regval;
int err;
err = regmap_read(data->regmap,
ISL28022_REG_SHUNT, &regval);
if (err)
return err;
/* print shunt voltage in micro volt */
seq_printf(seqf, "%d\n", regval * 10);
return 0;
}
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.
*
* following values for shunt range and resistor are allowed:
* 40 mV -> gain 1, shunt min. 800 micro ohms
* 80 mV -> gain 2, shunt min. 1600 micro ohms
* 160 mV -> gain 4, shunt min. 3200 micro ohms
* 320 mV -> gain 8, shunt min. 6400 micro ohms
*/
static int isl28022_read_properties(struct device *dev, struct isl28022_data *data)
{
u32 val;
int err;
err = device_property_read_u32(dev, "shunt-resistor-micro-ohms", &val);
if (err == -EINVAL)
val = 10000;
else if (err < 0)
return err;
data->shunt = val;
err = device_property_read_u32(dev, "renesas,shunt-range-microvolt", &val);
if (err == -EINVAL)
val = 320000;
else if (err < 0)
return err;
switch (val) {
case 40000:
data->gain = 1;
if (data->shunt < 800)
goto shunt_invalid;
break;
case 80000:
data->gain = 2;
if (data->shunt < 1600)
goto shunt_invalid;
break;
case 160000:
data->gain = 4;
if (data->shunt < 3200)
goto shunt_invalid;
break;
case 320000:
data->gain = 8;
if (data->shunt < 6400)
goto shunt_invalid;
break;
default:
return dev_err_probe(dev, -EINVAL,
"renesas,shunt-range-microvolt invalid value %d\n",
val);
}
err = device_property_read_u32(dev, "renesas,average-samples", &val);
if (err == -EINVAL)
val = 1;
else if (err < 0)
return err;
if (val > 128 || hweight32(val) != 1)
return dev_err_probe(dev, -EINVAL,
"renesas,average-samples invalid value %d\n",
val);
data->average = val;
return 0;
shunt_invalid:
return dev_err_probe(dev, -EINVAL,
"renesas,shunt-resistor-microvolt invalid value %d\n",
data->shunt);
}
/*
* write configuration and calibration registers
*
* The driver supports only shunt and bus continuous ADC mode at 15bit resolution
* with averaging from 1 to 128 samples (pow of 2) on both channels.
* Shunt voltage gain 1,2,4 or 8 is allowed.
* The bus voltage range is 60V fixed.
*/
static int isl28022_config(struct isl28022_data *data)
{
int err;
u16 config;
u16 calib;
config = (ISL28022_MODE_CONT_SB << ISL28022_MODE_SHIFT) |
(ISL28022_BRNG_60 << ISL28022_BRNG_SHIFT) |
(__ffs(data->gain) << ISL28022_PG_SHIFT) |
((ISL28022_ADC_15_1 + __ffs(data->average)) << ISL28022_SADC_SHIFT) |
((ISL28022_ADC_15_1 + __ffs(data->average)) << ISL28022_BADC_SHIFT);
calib = data->shunt ? 0x8000 / data->gain : 0;
err = regmap_write(data->regmap, ISL28022_REG_CONFIG, config);
if (err < 0)
return err;
return regmap_write(data->regmap, ISL28022_REG_CALIB, calib);
}
static int isl28022_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct device *hwmon_dev;
struct isl28022_data *data;
int err;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA))
return -ENODEV;
data = devm_kzalloc(dev, sizeof(struct isl28022_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
err = isl28022_read_properties(dev, data);
if (err)
return err;
data->regmap = devm_regmap_init_i2c(client, &isl28022_regmap_config);
if (IS_ERR(data->regmap))
return PTR_ERR(data->regmap);
err = isl28022_config(data);
if (err)
return err;
isl28022_debugfs_init(client, data);
hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
data, &isl28022_chip_info, NULL);
if (IS_ERR(hwmon_dev))
return PTR_ERR(hwmon_dev);
return 0;
}
static const struct i2c_device_id isl28022_ids[] = {
{ "isl28022", 0},
{ /* LIST END */ }
};
MODULE_DEVICE_TABLE(i2c, isl28022_ids);
static const struct of_device_id __maybe_unused isl28022_of_match[] = {
{ .compatible = "renesas,isl28022"},
{ /* LIST END */ }
};
MODULE_DEVICE_TABLE(of, isl28022_of_match);
static struct i2c_driver isl28022_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "isl28022",
},
.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;
}
static void __exit
isl28022_exit(void)
{
i2c_del_driver(&isl28022_driver);
debugfs_remove_recursive(isl28022_debugfs_root);
}
module_init(isl28022_init);
module_exit(isl28022_exit);
MODULE_AUTHOR("Carsten Spieß <mail@carsten-spiess.de>");
MODULE_DESCRIPTION("ISL28022 driver");
MODULE_LICENSE("GPL");

View File

@ -11,6 +11,7 @@
#include <linux/bitops.h>
#include <linux/bitfield.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
@ -19,7 +20,6 @@
#include <linux/hwmon.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/regmap.h>
/* Addresses to scan */
@ -595,20 +595,18 @@ static const struct i2c_device_id jc42_id[] = {
};
MODULE_DEVICE_TABLE(i2c, jc42_id);
#ifdef CONFIG_OF
static const struct of_device_id jc42_of_ids[] = {
{ .compatible = "jedec,jc-42.4-temp", },
{ }
};
MODULE_DEVICE_TABLE(of, jc42_of_ids);
#endif
static struct i2c_driver jc42_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "jc42",
.pm = JC42_DEV_PM_OPS,
.of_match_table = of_match_ptr(jc42_of_ids),
.of_match_table = jc42_of_ids,
},
.probe = jc42_probe,
.remove = jc42_remove,

View File

@ -332,7 +332,7 @@ static struct platform_driver max197_driver = {
.name = "max197",
},
.probe = max197_probe,
.remove_new = max197_remove,
.remove = max197_remove,
.id_table = max197_device_ids,
};
module_platform_driver(max197_driver);

View File

@ -19,7 +19,6 @@
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/platform_data/max6639.h>
#include <linux/regmap.h>
#include <linux/util_macros.h>
@ -531,14 +530,49 @@ static int rpm_range_to_reg(int range)
return 1; /* default: 4000 RPM */
}
static int max6639_probe_child_from_dt(struct i2c_client *client,
struct device_node *child,
struct max6639_data *data)
{
struct device *dev = &client->dev;
u32 i;
int err, val;
err = of_property_read_u32(child, "reg", &i);
if (err) {
dev_err(dev, "missing reg property of %pOFn\n", child);
return err;
}
if (i > 1) {
dev_err(dev, "Invalid fan index reg %d\n", i);
return -EINVAL;
}
err = of_property_read_u32(child, "pulses-per-revolution", &val);
if (!err) {
if (val < 1 || val > 5) {
dev_err(dev, "invalid pulses-per-revolution %d of %pOFn\n", val, child);
return -EINVAL;
}
data->ppr[i] = val;
}
err = of_property_read_u32(child, "max-rpm", &val);
if (!err)
data->rpm_range[i] = rpm_range_to_reg(val);
return 0;
}
static int max6639_init_client(struct i2c_client *client,
struct max6639_data *data)
{
struct max6639_platform_data *max6639_info =
dev_get_platdata(&client->dev);
int i;
int rpm_range = 1; /* default: 4000 RPM */
int err, ppr;
struct device *dev = &client->dev;
const struct device_node *np = dev->of_node;
struct device_node *child;
int i, err;
/* Reset chip to default values, see below for GCONFIG setup */
err = regmap_write(data->regmap, MAX6639_REG_GCONFIG, MAX6639_GCONFIG_POR);
@ -546,21 +580,29 @@ static int max6639_init_client(struct i2c_client *client,
return err;
/* Fans pulse per revolution is 2 by default */
if (max6639_info && max6639_info->ppr > 0 &&
max6639_info->ppr < 5)
ppr = max6639_info->ppr;
else
ppr = 2;
data->ppr[0] = 2;
data->ppr[1] = 2;
data->ppr[0] = ppr;
data->ppr[1] = ppr;
/* default: 4000 RPM */
data->rpm_range[0] = 1;
data->rpm_range[1] = 1;
if (max6639_info)
rpm_range = rpm_range_to_reg(max6639_info->rpm_range);
data->rpm_range[0] = rpm_range;
data->rpm_range[1] = rpm_range;
for_each_child_of_node(np, child) {
if (strcmp(child->name, "fan"))
continue;
err = max6639_probe_child_from_dt(client, child, data);
if (err) {
of_node_put(child);
return err;
}
}
for (i = 0; i < MAX6639_NUM_CHANNELS; i++) {
err = regmap_set_bits(data->regmap, MAX6639_REG_OUTPUT_MASK, BIT(1 - i));
if (err)
return err;
/* Set Fan pulse per revolution */
err = max6639_set_ppr(data, i, data->ppr[i]);
if (err)
@ -573,12 +615,7 @@ static int max6639_init_client(struct i2c_client *client,
return err;
/* Fans PWM polarity high by default */
if (max6639_info) {
if (max6639_info->pwm_polarity == 0)
err = regmap_write(data->regmap, MAX6639_REG_FAN_CONFIG2a(i), 0x00);
else
err = regmap_write(data->regmap, MAX6639_REG_FAN_CONFIG2a(i), 0x02);
}
err = regmap_write(data->regmap, MAX6639_REG_FAN_CONFIG2a(i), 0x00);
if (err)
return err;

View File

@ -315,7 +315,7 @@ static const struct platform_device_id mc13783_adc_idtable[] = {
MODULE_DEVICE_TABLE(platform, mc13783_adc_idtable);
static struct platform_driver mc13783_adc_driver = {
.remove_new = mc13783_adc_remove,
.remove = mc13783_adc_remove,
.driver = {
.name = DRIVER_NAME,
},

View File

@ -2878,8 +2878,7 @@ store_target_temp(struct device *dev, struct device_attribute *attr,
if (err < 0)
return err;
val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0,
data->target_temp_mask);
val = DIV_ROUND_CLOSEST(clamp_val(val, 0, data->target_temp_mask * 1000), 1000);
mutex_lock(&data->update_lock);
data->target_temp[nr] = val;
@ -2959,7 +2958,7 @@ store_temp_tolerance(struct device *dev, struct device_attribute *attr,
return err;
/* Limit tolerance as needed */
val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0, data->tolerance_mask);
val = DIV_ROUND_CLOSEST(clamp_val(val, 0, data->tolerance_mask * 1000), 1000);
mutex_lock(&data->update_lock);
data->temp_tolerance[index][nr] = val;
@ -3085,7 +3084,7 @@ store_weight_temp(struct device *dev, struct device_attribute *attr,
if (err < 0)
return err;
val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0, 255);
val = DIV_ROUND_CLOSEST(clamp_val(val, 0, 255000), 1000);
mutex_lock(&data->update_lock);
data->weight_temp[index][nr] = val;

View File

@ -1350,6 +1350,8 @@ static const char * const asus_msi_boards[] = {
"Pro H610M-CT D4",
"Pro H610T D4",
"Pro Q670M-C",
"Pro WS 600M-CL",
"Pro WS 665-ACE",
"Pro WS W680-ACE",
"Pro WS W680-ACE IPMI",
"Pro WS W790-ACE",

447
drivers/hwmon/nct7363.c Normal file
View File

@ -0,0 +1,447 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2023 Nuvoton Technology corporation.
*/
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/err.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#define NCT7363_REG_FUNC_CFG_BASE(x) (0x20 + (x))
#define NCT7363_REG_LSRS(x) (0x34 + ((x) / 8))
#define NCT7363_REG_PWMEN_BASE(x) (0x38 + (x))
#define NCT7363_REG_FANINEN_BASE(x) (0x41 + (x))
#define NCT7363_REG_FANINX_HVAL(x) (0x48 + ((x) * 2))
#define NCT7363_REG_FANINX_LVAL(x) (0x49 + ((x) * 2))
#define NCT7363_REG_FANINX_HL(x) (0x6C + ((x) * 2))
#define NCT7363_REG_FANINX_LL(x) (0x6D + ((x) * 2))
#define NCT7363_REG_FSCPXDUTY(x) (0x90 + ((x) * 2))
#define NCT7363_REG_FSCPXDIV(x) (0x91 + ((x) * 2))
#define PWM_SEL(x) (BIT(0) << ((x) * 2))
#define FANIN_SEL(_x) ({typeof(_x) (x) = (_x); \
BIT(1) << (((x) < 8) ? \
(((x) + 8) * 2) : \
(((x) % 8) * 2)); })
#define ALARM_SEL(x, y) ((x) & (BIT((y) % 8)))
#define VALUE_TO_REG(x, y) (((x) >> ((y) * 8)) & 0xFF)
#define NCT7363_FANINX_LVAL_MASK GENMASK(4, 0)
#define NCT7363_FANIN_MASK GENMASK(12, 0)
#define NCT7363_PWM_COUNT 16
static inline unsigned int fan_from_reg(u16 val)
{
if (val == NCT7363_FANIN_MASK || val == 0)
return 0;
return (1350000UL / val);
}
static const struct of_device_id nct7363_of_match[] = {
{ .compatible = "nuvoton,nct7363", },
{ .compatible = "nuvoton,nct7362", },
{ }
};
MODULE_DEVICE_TABLE(of, nct7363_of_match);
struct nct7363_data {
struct regmap *regmap;
u16 fanin_mask;
u16 pwm_mask;
};
static int nct7363_read_fan(struct device *dev, u32 attr, int channel,
long *val)
{
struct nct7363_data *data = dev_get_drvdata(dev);
unsigned int reg;
u8 regval[2];
int ret;
u16 cnt;
switch (attr) {
case hwmon_fan_input:
/*
* High-byte register should be read first to latch
* synchronous low-byte value
*/
ret = regmap_bulk_read(data->regmap,
NCT7363_REG_FANINX_HVAL(channel),
&regval, 2);
if (ret)
return ret;
cnt = (regval[0] << 5) | (regval[1] & NCT7363_FANINX_LVAL_MASK);
*val = fan_from_reg(cnt);
return 0;
case hwmon_fan_min:
ret = regmap_bulk_read(data->regmap,
NCT7363_REG_FANINX_HL(channel),
&regval, 2);
if (ret)
return ret;
cnt = (regval[0] << 5) | (regval[1] & NCT7363_FANINX_LVAL_MASK);
*val = fan_from_reg(cnt);
return 0;
case hwmon_fan_alarm:
ret = regmap_read(data->regmap,
NCT7363_REG_LSRS(channel), &reg);
if (ret)
return ret;
*val = (long)ALARM_SEL(reg, channel) > 0 ? 1 : 0;
return 0;
default:
return -EOPNOTSUPP;
}
}
static int nct7363_write_fan(struct device *dev, u32 attr, int channel,
long val)
{
struct nct7363_data *data = dev_get_drvdata(dev);
u8 regval[2];
int ret;
if (val <= 0)
return -EINVAL;
switch (attr) {
case hwmon_fan_min:
val = clamp_val(DIV_ROUND_CLOSEST(1350000, val),
1, NCT7363_FANIN_MASK);
regval[0] = val >> 5;
regval[1] = val & NCT7363_FANINX_LVAL_MASK;
ret = regmap_bulk_write(data->regmap,
NCT7363_REG_FANINX_HL(channel),
regval, 2);
return ret;
default:
return -EOPNOTSUPP;
}
}
static umode_t nct7363_fan_is_visible(const void *_data, u32 attr, int channel)
{
const struct nct7363_data *data = _data;
switch (attr) {
case hwmon_fan_input:
case hwmon_fan_alarm:
if (data->fanin_mask & BIT(channel))
return 0444;
break;
case hwmon_fan_min:
if (data->fanin_mask & BIT(channel))
return 0644;
break;
default:
break;
}
return 0;
}
static int nct7363_read_pwm(struct device *dev, u32 attr, int channel,
long *val)
{
struct nct7363_data *data = dev_get_drvdata(dev);
unsigned int regval;
int ret;
switch (attr) {
case hwmon_pwm_input:
ret = regmap_read(data->regmap,
NCT7363_REG_FSCPXDUTY(channel), &regval);
if (ret)
return ret;
*val = (long)regval;
return 0;
default:
return -EOPNOTSUPP;
}
}
static int nct7363_write_pwm(struct device *dev, u32 attr, int channel,
long val)
{
struct nct7363_data *data = dev_get_drvdata(dev);
int ret;
switch (attr) {
case hwmon_pwm_input:
if (val < 0 || val > 255)
return -EINVAL;
ret = regmap_write(data->regmap,
NCT7363_REG_FSCPXDUTY(channel), val);
return ret;
default:
return -EOPNOTSUPP;
}
}
static umode_t nct7363_pwm_is_visible(const void *_data, u32 attr, int channel)
{
const struct nct7363_data *data = _data;
switch (attr) {
case hwmon_pwm_input:
if (data->pwm_mask & BIT(channel))
return 0644;
break;
default:
break;
}
return 0;
}
static int nct7363_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
switch (type) {
case hwmon_fan:
return nct7363_read_fan(dev, attr, channel, val);
case hwmon_pwm:
return nct7363_read_pwm(dev, attr, channel, val);
default:
return -EOPNOTSUPP;
}
}
static int nct7363_write(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long val)
{
switch (type) {
case hwmon_fan:
return nct7363_write_fan(dev, attr, channel, val);
case hwmon_pwm:
return nct7363_write_pwm(dev, attr, channel, val);
default:
return -EOPNOTSUPP;
}
}
static umode_t nct7363_is_visible(const void *data,
enum hwmon_sensor_types type,
u32 attr, int channel)
{
switch (type) {
case hwmon_fan:
return nct7363_fan_is_visible(data, attr, channel);
case hwmon_pwm:
return nct7363_pwm_is_visible(data, attr, channel);
default:
return 0;
}
}
static const struct hwmon_channel_info *nct7363_info[] = {
HWMON_CHANNEL_INFO(fan,
HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM,
HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM),
HWMON_CHANNEL_INFO(pwm,
HWMON_PWM_INPUT,
HWMON_PWM_INPUT,
HWMON_PWM_INPUT,
HWMON_PWM_INPUT,
HWMON_PWM_INPUT,
HWMON_PWM_INPUT,
HWMON_PWM_INPUT,
HWMON_PWM_INPUT,
HWMON_PWM_INPUT,
HWMON_PWM_INPUT,
HWMON_PWM_INPUT,
HWMON_PWM_INPUT,
HWMON_PWM_INPUT,
HWMON_PWM_INPUT,
HWMON_PWM_INPUT,
HWMON_PWM_INPUT),
NULL
};
static const struct hwmon_ops nct7363_hwmon_ops = {
.is_visible = nct7363_is_visible,
.read = nct7363_read,
.write = nct7363_write,
};
static const struct hwmon_chip_info nct7363_chip_info = {
.ops = &nct7363_hwmon_ops,
.info = nct7363_info,
};
static int nct7363_init_chip(struct nct7363_data *data)
{
u32 func_config = 0;
int i, ret;
/* Pin Function Configuration */
for (i = 0; i < NCT7363_PWM_COUNT; i++) {
if (data->pwm_mask & BIT(i))
func_config |= PWM_SEL(i);
if (data->fanin_mask & BIT(i))
func_config |= FANIN_SEL(i);
}
for (i = 0; i < 4; i++) {
ret = regmap_write(data->regmap, NCT7363_REG_FUNC_CFG_BASE(i),
VALUE_TO_REG(func_config, i));
if (ret < 0)
return ret;
}
/* PWM and FANIN Monitoring Enable */
for (i = 0; i < 2; i++) {
ret = regmap_write(data->regmap, NCT7363_REG_PWMEN_BASE(i),
VALUE_TO_REG(data->pwm_mask, i));
if (ret < 0)
return ret;
ret = regmap_write(data->regmap, NCT7363_REG_FANINEN_BASE(i),
VALUE_TO_REG(data->fanin_mask, i));
if (ret < 0)
return ret;
}
return 0;
}
static int nct7363_present_pwm_fanin(struct device *dev,
struct device_node *child,
struct nct7363_data *data)
{
u8 fanin_ch[NCT7363_PWM_COUNT];
struct of_phandle_args args;
int ret, fanin_cnt;
u8 ch, index;
ret = of_parse_phandle_with_args(child, "pwms", "#pwm-cells",
0, &args);
if (ret)
return ret;
if (args.args[0] >= NCT7363_PWM_COUNT)
return -EINVAL;
data->pwm_mask |= BIT(args.args[0]);
fanin_cnt = of_property_count_u8_elems(child, "tach-ch");
if (fanin_cnt < 1 || fanin_cnt > NCT7363_PWM_COUNT)
return -EINVAL;
ret = of_property_read_u8_array(child, "tach-ch", fanin_ch, fanin_cnt);
if (ret)
return ret;
for (ch = 0; ch < fanin_cnt; ch++) {
index = fanin_ch[ch];
if (index >= NCT7363_PWM_COUNT)
return -EINVAL;
data->fanin_mask |= BIT(index);
}
return 0;
}
static bool nct7363_regmap_is_volatile(struct device *dev, unsigned int reg)
{
switch (reg) {
case NCT7363_REG_LSRS(0) ... NCT7363_REG_LSRS(15):
case NCT7363_REG_FANINX_HVAL(0) ... NCT7363_REG_FANINX_LVAL(15):
case NCT7363_REG_FANINX_HL(0) ... NCT7363_REG_FANINX_LL(15):
case NCT7363_REG_FSCPXDUTY(0) ... NCT7363_REG_FSCPXDIV(15):
return true;
default:
return false;
}
}
static const struct regmap_config nct7363_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.use_single_read = true,
.use_single_write = true,
.cache_type = REGCACHE_RBTREE,
.volatile_reg = nct7363_regmap_is_volatile,
};
static int nct7363_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct device_node *child;
struct nct7363_data *data;
struct device *hwmon_dev;
int ret;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->regmap = devm_regmap_init_i2c(client, &nct7363_regmap_config);
if (IS_ERR(data->regmap))
return PTR_ERR(data->regmap);
for_each_child_of_node(dev->of_node, child) {
ret = nct7363_present_pwm_fanin(dev, child, data);
if (ret) {
of_node_put(child);
return ret;
}
}
/* Initialize the chip */
ret = nct7363_init_chip(data);
if (ret)
return ret;
hwmon_dev =
devm_hwmon_device_register_with_info(dev, client->name, data,
&nct7363_chip_info, NULL);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
static struct i2c_driver nct7363_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "nct7363",
.of_match_table = nct7363_of_match,
},
.probe = nct7363_probe,
};
module_i2c_driver(nct7363_driver);
MODULE_AUTHOR("CW Ho <cwho@nuvoton.com>");
MODULE_AUTHOR("Ban Feng <kcfeng0@nuvoton.com>");
MODULE_DESCRIPTION("NCT7363 Hardware Monitoring Driver");
MODULE_LICENSE("GPL");

View File

@ -35,13 +35,6 @@ struct kraken2_priv_data {
unsigned long updated; /* jiffies */
};
static umode_t kraken2_is_visible(const void *data,
enum hwmon_sensor_types type,
u32 attr, int channel)
{
return 0444;
}
static int kraken2_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
@ -81,7 +74,7 @@ static int kraken2_read_string(struct device *dev, enum hwmon_sensor_types type,
}
static const struct hwmon_ops kraken2_hwmon_ops = {
.is_visible = kraken2_is_visible,
.visible = 0444,
.read = kraken2_read,
.read_string = kraken2_read_string,
};

View File

@ -192,8 +192,8 @@ static struct platform_driver p9_sbe_occ_driver = {
.name = "occ-hwmon",
.of_match_table = p9_sbe_occ_of_match,
},
.probe = p9_sbe_occ_probe,
.remove_new = p9_sbe_occ_remove,
.probe = p9_sbe_occ_probe,
.remove = p9_sbe_occ_remove,
};
module_platform_driver(p9_sbe_occ_driver);

View File

@ -1606,7 +1606,7 @@ static struct platform_driver pc87360_driver = {
.name = DRIVER_NAME,
},
.probe = pc87360_probe,
.remove_new = pc87360_remove,
.remove = pc87360_remove,
};
/*

View File

@ -1129,7 +1129,7 @@ static struct platform_driver pc87427_driver = {
.name = DRVNAME,
},
.probe = pc87427_probe,
.remove_new = pc87427_remove,
.remove = pc87427_remove,
};
static int __init pc87427_device_add(const struct pc87427_sio_data *sio_data)

View File

@ -224,9 +224,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, LTC7880,
LTM4644, LTM4675, LTM4676, LTM4677, LTM4678, LTM4680, LTM4686,
and LTM4700.
LTC3880, LTC3883, LTC3884, LTC3886, LTC3887, LTC3889, LTC7841,
LTC7880, LTM4644, LTM4675, LTM4676, LTM4677, LTM4678, LTM4680,
LTM4686, and LTM4700.
config SENSORS_LTC3815
tristate "Linear Technologies LTC3815"

View File

@ -13,6 +13,7 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/string.h>
#include <linux/sysfs.h>
@ -20,6 +21,7 @@
#define ISL68137_VOUT_AVS 0x30
#define RAA_DMPVR2_READ_VMON 0xc8
#define MAX_CHANNELS 4
enum chips {
isl68137,
@ -72,6 +74,17 @@ enum variants {
raa_dmpvr2_hv,
};
struct isl68137_channel {
u32 vout_voltage_divider[2];
};
struct isl68137_data {
struct pmbus_driver_info info;
struct isl68137_channel channel[MAX_CHANNELS];
};
#define to_isl68137_data(x) container_of(x, struct isl68137_data, info)
static const struct i2c_device_id raa_dmpvr_id[];
static ssize_t isl68137_avs_enable_show_page(struct i2c_client *client,
@ -163,13 +176,32 @@ static const struct attribute_group *isl68137_attribute_groups[] = {
static int raa_dmpvr2_read_word_data(struct i2c_client *client, int page,
int phase, int reg)
{
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
const struct isl68137_data *data = to_isl68137_data(info);
int ret;
u64 temp;
switch (reg) {
case PMBUS_VIRT_READ_VMON:
ret = pmbus_read_word_data(client, page, phase,
RAA_DMPVR2_READ_VMON);
break;
case PMBUS_READ_POUT:
case PMBUS_READ_VOUT:
/*
* In cases where a voltage divider is attached to the target
* rail between Vout and the Vsense pin, both Vout and Pout
* should be scaled by the voltage divider scaling factor.
* I.e. Vout = Vsense * Rtotal / Rout
*/
ret = pmbus_read_word_data(client, page, phase, reg);
if (ret > 0) {
temp = DIV_U64_ROUND_CLOSEST((u64)ret *
data->channel[page].vout_voltage_divider[1],
data->channel[page].vout_voltage_divider[0]);
ret = clamp_val(temp, 0, 0xffff);
}
break;
default:
ret = -ENODATA;
break;
@ -178,6 +210,40 @@ static int raa_dmpvr2_read_word_data(struct i2c_client *client, int page,
return ret;
}
static int raa_dmpvr2_write_word_data(struct i2c_client *client, int page,
int reg, u16 word)
{
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
const struct isl68137_data *data = to_isl68137_data(info);
int ret;
u64 temp;
switch (reg) {
case PMBUS_VOUT_MAX:
case PMBUS_VOUT_MARGIN_HIGH:
case PMBUS_VOUT_MARGIN_LOW:
case PMBUS_VOUT_OV_FAULT_LIMIT:
case PMBUS_VOUT_UV_FAULT_LIMIT:
case PMBUS_VOUT_COMMAND:
/*
* In cases where a voltage divider is attached to the target
* rail between Vout and the Vsense pin, Vout related PMBus
* commands should be scaled based on the expected voltage
* at the Vsense pin.
* I.e. Vsense = Vout * Rout / Rtotal
*/
temp = DIV_U64_ROUND_CLOSEST((u64)word *
data->channel[page].vout_voltage_divider[0],
data->channel[page].vout_voltage_divider[1]);
ret = clamp_val(temp, 0, 0xffff);
break;
default:
ret = -ENODATA;
break;
}
return ret;
}
static struct pmbus_driver_info raa_dmpvr_info = {
.pages = 3,
.format[PSC_VOLTAGE_IN] = direct,
@ -220,14 +286,90 @@ static struct pmbus_driver_info raa_dmpvr_info = {
| PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_POUT,
};
static int isl68137_probe_child_from_dt(struct device *dev,
struct device_node *child,
struct isl68137_data *data)
{
u32 channel, rout, rtotal;
int err;
err = of_property_read_u32(child, "reg", &channel);
if (err) {
dev_err(dev, "missing reg property of %pOFn\n", child);
return err;
}
if (channel >= data->info.pages) {
dev_err(dev, "invalid reg %d of %pOFn\n", channel, child);
return -EINVAL;
}
err = of_property_read_u32_array(child, "vout-voltage-divider",
data->channel[channel].vout_voltage_divider,
ARRAY_SIZE(data->channel[channel].vout_voltage_divider));
if (err && err != -EINVAL) {
dev_err(dev,
"malformed vout-voltage-divider value for channel %d\n",
channel);
return err;
}
rout = data->channel[channel].vout_voltage_divider[0];
rtotal = data->channel[channel].vout_voltage_divider[1];
if (rout == 0) {
dev_err(dev,
"Voltage divider output resistance must be greater than 0\n");
return -EINVAL;
}
if (rtotal < rout) {
dev_err(dev,
"Voltage divider total resistance is less than output resistance\n");
return -EINVAL;
}
return 0;
}
static int isl68137_probe_from_dt(struct device *dev,
struct isl68137_data *data)
{
const struct device_node *np = dev->of_node;
struct device_node *child;
int err;
for_each_child_of_node(np, child) {
if (strcmp(child->name, "channel"))
continue;
err = isl68137_probe_child_from_dt(dev, child, data);
if (err)
return err;
}
return 0;
}
static int isl68137_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct pmbus_driver_info *info;
struct isl68137_data *data;
int i, err;
info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL);
if (!info)
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
memcpy(info, &raa_dmpvr_info, sizeof(*info));
/*
* Initialize all voltage dividers to Rout=1 and Rtotal=1 to simplify
* logic in PMBus word read/write functions
*/
for (i = 0; i < MAX_CHANNELS; i++)
memset(data->channel[i].vout_voltage_divider,
1,
sizeof(data->channel[i].vout_voltage_divider));
memcpy(&data->info, &raa_dmpvr_info, sizeof(data->info));
info = &data->info;
switch (i2c_match_id(raa_dmpvr_id, client)->driver_data) {
case raa_dmpvr1_2rail:
@ -237,11 +379,14 @@ static int isl68137_probe(struct i2c_client *client)
info->func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
| PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
| PMBUS_HAVE_POUT;
info->read_word_data = raa_dmpvr2_read_word_data;
info->write_word_data = raa_dmpvr2_write_word_data;
info->groups = isl68137_attribute_groups;
break;
case raa_dmpvr2_1rail:
info->pages = 1;
info->read_word_data = raa_dmpvr2_read_word_data;
info->write_word_data = raa_dmpvr2_write_word_data;
break;
case raa_dmpvr2_2rail_nontc:
info->func[0] &= ~PMBUS_HAVE_TEMP3;
@ -250,9 +395,11 @@ static int isl68137_probe(struct i2c_client *client)
case raa_dmpvr2_2rail:
info->pages = 2;
info->read_word_data = raa_dmpvr2_read_word_data;
info->write_word_data = raa_dmpvr2_write_word_data;
break;
case raa_dmpvr2_3rail:
info->read_word_data = raa_dmpvr2_read_word_data;
info->write_word_data = raa_dmpvr2_write_word_data;
break;
case raa_dmpvr2_hv:
info->pages = 1;
@ -263,11 +410,16 @@ static int isl68137_probe(struct i2c_client *client)
info->m[PSC_POWER] = 2;
info->R[PSC_POWER] = -1;
info->read_word_data = raa_dmpvr2_read_word_data;
info->write_word_data = raa_dmpvr2_write_word_data;
break;
default:
return -ENODEV;
}
err = isl68137_probe_from_dt(dev, data);
if (err)
return err;
return pmbus_do_probe(client, info);
}
@ -318,11 +470,59 @@ static const struct i2c_device_id raa_dmpvr_id[] = {
MODULE_DEVICE_TABLE(i2c, raa_dmpvr_id);
static const struct of_device_id isl68137_of_match[] = {
{ .compatible = "isil,isl68137", .data = (void *)raa_dmpvr1_2rail },
{ .compatible = "renesas,isl68220", .data = (void *)raa_dmpvr2_2rail },
{ .compatible = "renesas,isl68221", .data = (void *)raa_dmpvr2_3rail },
{ .compatible = "renesas,isl68222", .data = (void *)raa_dmpvr2_2rail },
{ .compatible = "renesas,isl68223", .data = (void *)raa_dmpvr2_2rail },
{ .compatible = "renesas,isl68224", .data = (void *)raa_dmpvr2_3rail },
{ .compatible = "renesas,isl68225", .data = (void *)raa_dmpvr2_2rail },
{ .compatible = "renesas,isl68226", .data = (void *)raa_dmpvr2_3rail },
{ .compatible = "renesas,isl68227", .data = (void *)raa_dmpvr2_1rail },
{ .compatible = "renesas,isl68229", .data = (void *)raa_dmpvr2_3rail },
{ .compatible = "renesas,isl68233", .data = (void *)raa_dmpvr2_2rail },
{ .compatible = "renesas,isl68239", .data = (void *)raa_dmpvr2_3rail },
{ .compatible = "renesas,isl69222", .data = (void *)raa_dmpvr2_2rail },
{ .compatible = "renesas,isl69223", .data = (void *)raa_dmpvr2_3rail },
{ .compatible = "renesas,isl69224", .data = (void *)raa_dmpvr2_2rail },
{ .compatible = "renesas,isl69225", .data = (void *)raa_dmpvr2_2rail },
{ .compatible = "renesas,isl69227", .data = (void *)raa_dmpvr2_3rail },
{ .compatible = "renesas,isl69228", .data = (void *)raa_dmpvr2_3rail },
{ .compatible = "renesas,isl69234", .data = (void *)raa_dmpvr2_2rail },
{ .compatible = "renesas,isl69236", .data = (void *)raa_dmpvr2_2rail },
{ .compatible = "renesas,isl69239", .data = (void *)raa_dmpvr2_3rail },
{ .compatible = "renesas,isl69242", .data = (void *)raa_dmpvr2_2rail },
{ .compatible = "renesas,isl69243", .data = (void *)raa_dmpvr2_1rail },
{ .compatible = "renesas,isl69247", .data = (void *)raa_dmpvr2_2rail },
{ .compatible = "renesas,isl69248", .data = (void *)raa_dmpvr2_2rail },
{ .compatible = "renesas,isl69254", .data = (void *)raa_dmpvr2_2rail },
{ .compatible = "renesas,isl69255", .data = (void *)raa_dmpvr2_2rail },
{ .compatible = "renesas,isl69256", .data = (void *)raa_dmpvr2_2rail },
{ .compatible = "renesas,isl69259", .data = (void *)raa_dmpvr2_2rail },
{ .compatible = "isil,isl69260", .data = (void *)raa_dmpvr2_2rail },
{ .compatible = "renesas,isl69268", .data = (void *)raa_dmpvr2_2rail },
{ .compatible = "isil,isl69269", .data = (void *)raa_dmpvr2_3rail },
{ .compatible = "renesas,isl69298", .data = (void *)raa_dmpvr2_2rail },
{ .compatible = "renesas,raa228000", .data = (void *)raa_dmpvr2_hv },
{ .compatible = "renesas,raa228004", .data = (void *)raa_dmpvr2_hv },
{ .compatible = "renesas,raa228006", .data = (void *)raa_dmpvr2_hv },
{ .compatible = "renesas,raa228228", .data = (void *)raa_dmpvr2_2rail_nontc },
{ .compatible = "renesas,raa229001", .data = (void *)raa_dmpvr2_2rail },
{ .compatible = "renesas,raa229004", .data = (void *)raa_dmpvr2_2rail },
{ },
};
MODULE_DEVICE_TABLE(of, isl68137_of_match);
/* This is the driver that will be inserted */
static struct i2c_driver isl68137_driver = {
.driver = {
.name = "isl68137",
},
.name = "isl68137",
.of_match_table = isl68137_of_match,
},
.probe = isl68137_probe,
.id_table = raa_dmpvr_id,
};

View File

@ -23,7 +23,8 @@ enum chips {
/* Managers */
ltc2972, ltc2974, ltc2975, ltc2977, ltc2978, ltc2979, ltc2980,
/* Controllers */
ltc3880, ltc3882, ltc3883, ltc3884, ltc3886, ltc3887, ltc3889, ltc7132, ltc7880,
ltc3880, ltc3882, ltc3883, ltc3884, ltc3886, ltc3887, ltc3889, ltc7132,
ltc7841, ltc7880,
/* Modules */
ltm2987, ltm4664, ltm4675, ltm4676, ltm4677, ltm4678, ltm4680, ltm4686,
ltm4700,
@ -50,7 +51,7 @@ enum chips {
#define LTC3880_MFR_CLEAR_PEAKS 0xe3
#define LTC3880_MFR_TEMPERATURE2_PEAK 0xf4
/* LTC3883, LTC3884, LTC3886, LTC3889, LTC7132, LTC7880 */
/* LTC3883, LTC3884, LTC3886, LTC3889, LTC7132, LTC7841 and LTC7880 only */
#define LTC3883_MFR_IIN_PEAK 0xe1
/* LTC2975 only */
@ -80,6 +81,7 @@ enum chips {
#define LTC3887_ID 0x4700
#define LTC3889_ID 0x4900
#define LTC7132_ID 0x4CE0
#define LTC7841_ID 0x40D0
#define LTC7880_ID 0x49E0
#define LTM2987_ID_A 0x8010 /* A/B for two die IDs */
#define LTM2987_ID_B 0x8020
@ -548,6 +550,7 @@ static const struct i2c_device_id ltc2978_id[] = {
{"ltc3887", ltc3887},
{"ltc3889", ltc3889},
{"ltc7132", ltc7132},
{"ltc7841", ltc7841},
{"ltc7880", ltc7880},
{"ltm2987", ltm2987},
{"ltm4664", ltm4664},
@ -654,6 +657,8 @@ static int ltc2978_get_id(struct i2c_client *client)
return ltc3889;
else if (chip_id == LTC7132_ID)
return ltc7132;
else if (chip_id == LTC7841_ID)
return ltc7841;
else if (chip_id == LTC7880_ID)
return ltc7880;
else if (chip_id == LTM2987_ID_A || chip_id == LTM2987_ID_B)
@ -854,6 +859,16 @@ static int ltc2978_probe(struct i2c_client *client)
| PMBUS_HAVE_POUT
| PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
break;
case ltc7841:
data->features |= FEAT_CLEAR_PEAKS;
info->read_word_data = ltc3883_read_word_data;
info->pages = LTC3883_NUM_PAGES;
info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN
| PMBUS_HAVE_STATUS_INPUT
| PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
| PMBUS_HAVE_IOUT
| PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP;
break;
default:
return -ENODEV;
}
@ -907,6 +922,7 @@ static const struct of_device_id ltc2978_of_match[] = {
{ .compatible = "lltc,ltc3887" },
{ .compatible = "lltc,ltc3889" },
{ .compatible = "lltc,ltc7132" },
{ .compatible = "lltc,ltc7841" },
{ .compatible = "lltc,ltc7880" },
{ .compatible = "lltc,ltm2987" },
{ .compatible = "lltc,ltm4664" },

View File

@ -572,8 +572,8 @@ static int mp2891_probe(struct i2c_client *client)
}
static const struct i2c_device_id mp2891_id[] = {
{"mp2891", 0},
{}
{ "mp2891" },
{ }
};
MODULE_DEVICE_TABLE(i2c, mp2891_id);

View File

@ -233,8 +233,8 @@ static int mp2993_probe(struct i2c_client *client)
}
static const struct i2c_device_id mp2993_id[] = {
{"mp2993", 0},
{}
{ "mp2993" },
{ }
};
MODULE_DEVICE_TABLE(i2c, mp2993_id);

View File

@ -291,8 +291,8 @@ static int mp9941_probe(struct i2c_client *client)
}
static const struct i2c_device_id mp9941_id[] = {
{"mp9941", 0},
{}
{ "mp9941" },
{ }
};
MODULE_DEVICE_TABLE(i2c, mp9941_id);

View File

@ -22,7 +22,7 @@ static int mpq8785_identify(struct i2c_client *client,
break;
case 1:
case 2:
info->format[PSC_VOLTAGE_OUT] = direct,
info->format[PSC_VOLTAGE_OUT] = direct;
info->m[PSC_VOLTAGE_OUT] = 64;
info->b[PSC_VOLTAGE_OUT] = 0;
info->R[PSC_VOLTAGE_OUT] = 1;

View File

@ -2719,9 +2719,7 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data,
* limit registers need to be disabled.
*/
if (!(data->flags & PMBUS_NO_WRITE_PROTECT)) {
pmbus_wait(client);
ret = i2c_smbus_read_byte_data(client, PMBUS_WRITE_PROTECT);
pmbus_update_ts(client, false);
ret = _pmbus_read_byte_data(client, -1, PMBUS_WRITE_PROTECT);
if (ret > 0 && (ret & PB_WP_ANY))
data->flags |= PMBUS_WRITE_PROTECTED | PMBUS_SKIP_STATUS_CHECK;
@ -3279,7 +3277,17 @@ static int pmbus_regulator_notify(struct pmbus_data *data, int page, int event)
static int pmbus_write_smbalert_mask(struct i2c_client *client, u8 page, u8 reg, u8 val)
{
return _pmbus_write_word_data(client, page, PMBUS_SMBALERT_MASK, reg | (val << 8));
int ret;
ret = _pmbus_write_word_data(client, page, PMBUS_SMBALERT_MASK, reg | (val << 8));
/*
* Clear fault systematically in case writing PMBUS_SMBALERT_MASK
* is not supported by the chip.
*/
pmbus_clear_fault_page(client, page);
return ret;
}
static irqreturn_t pmbus_fault_handler(int irq, void *pdata)

View File

@ -54,12 +54,6 @@ static const struct hwmon_channel_info *const powerz_info[] = {
NULL
};
static umode_t powerz_is_visible(const void *data, enum hwmon_sensor_types type,
u32 attr, int channel)
{
return 0444;
}
static int powerz_read_string(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, const char **str)
{
@ -201,7 +195,7 @@ static int powerz_read(struct device *dev, enum hwmon_sensor_types type,
}
static const struct hwmon_ops powerz_hwmon_ops = {
.is_visible = powerz_is_visible,
.visible = 0444,
.read = powerz_read,
.read_string = powerz_read_string,
};

View File

@ -7,6 +7,7 @@
* Author: Kamil Debski <k.debski@samsung.com>
*/
#include <linux/delay.h>
#include <linux/hwmon.h>
#include <linux/interrupt.h>
#include <linux/mod_devicetable.h>
@ -60,6 +61,9 @@ struct pwm_fan_ctx {
struct hwmon_chip_info info;
struct hwmon_channel_info fan_channel;
u64 pwm_duty_cycle_from_stopped;
u32 pwm_usec_from_stopped;
};
/* This handler assumes self resetting edge triggered interrupt. */
@ -199,7 +203,9 @@ static int pwm_fan_power_off(struct pwm_fan_ctx *ctx, bool force_disable)
static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
{
struct pwm_state *state = &ctx->pwm_state;
unsigned long final_pwm = pwm;
unsigned long period;
bool update = false;
int ret = 0;
if (pwm > 0) {
@ -208,11 +214,22 @@ static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
return 0;
period = state->period;
state->duty_cycle = DIV_ROUND_UP(pwm * (period - 1), MAX_PWM);
update = state->duty_cycle < ctx->pwm_duty_cycle_from_stopped;
if (update)
state->duty_cycle = ctx->pwm_duty_cycle_from_stopped;
else
state->duty_cycle = DIV_ROUND_UP(pwm * (period - 1), MAX_PWM);
ret = pwm_apply_might_sleep(ctx->pwm, state);
if (ret)
return ret;
ret = pwm_fan_power_on(ctx);
if (!ret && update) {
pwm = final_pwm;
state->duty_cycle = DIV_ROUND_UP(pwm * (period - 1), MAX_PWM);
usleep_range(ctx->pwm_usec_from_stopped,
ctx->pwm_usec_from_stopped * 2);
ret = pwm_apply_might_sleep(ctx->pwm, state);
}
} else {
ret = pwm_fan_power_off(ctx, false);
}
@ -480,6 +497,7 @@ static int pwm_fan_probe(struct platform_device *pdev)
struct device *hwmon;
int ret;
const struct hwmon_channel_info **channels;
u32 pwm_min_from_stopped = 0;
u32 *fan_channel_config;
int channel_count = 1; /* We always have a PWM channel. */
int i;
@ -620,6 +638,19 @@ static int pwm_fan_probe(struct platform_device *pdev)
channels[1] = &ctx->fan_channel;
}
ret = of_property_read_u32(dev->of_node, "fan-stop-to-start-percent",
&pwm_min_from_stopped);
if (!ret && pwm_min_from_stopped) {
ctx->pwm_duty_cycle_from_stopped =
DIV_ROUND_UP_ULL(pwm_min_from_stopped *
(ctx->pwm_state.period - 1),
100);
}
ret = of_property_read_u32(dev->of_node, "fan-stop-to-start-us",
&ctx->pwm_usec_from_stopped);
if (ret)
ctx->pwm_usec_from_stopped = 250000;
ctx->info.ops = &pwm_fan_hwmon_ops;
ctx->info.info = channels;

View File

@ -81,12 +81,6 @@ static int rpi_read(struct device *dev, enum hwmon_sensor_types type,
return 0;
}
static umode_t rpi_is_visible(const void *_data, enum hwmon_sensor_types type,
u32 attr, int channel)
{
return 0444;
}
static const struct hwmon_channel_info * const rpi_info[] = {
HWMON_CHANNEL_INFO(in,
HWMON_I_LCRIT_ALARM),
@ -94,7 +88,7 @@ static const struct hwmon_channel_info * const rpi_info[] = {
};
static const struct hwmon_ops rpi_hwmon_ops = {
.is_visible = rpi_is_visible,
.visible = 0444,
.read = rpi_read,
};

View File

@ -512,7 +512,7 @@ static struct platform_driver sch5636_driver = {
.name = DRVNAME,
},
.probe = sch5636_probe,
.remove_new = sch5636_remove,
.remove = sch5636_remove,
.id_table = sch5636_device_id,
};

View File

@ -346,8 +346,8 @@ static void sg2042_mcu_i2c_remove(struct i2c_client *client)
}
static const struct i2c_device_id sg2042_mcu_id[] = {
{ "sg2042-hwmon-mcu", 0 },
{},
{ "sg2042-hwmon-mcu" },
{ }
};
MODULE_DEVICE_TABLE(i2c, sg2042_mcu_id);

View File

@ -1051,7 +1051,7 @@ static struct platform_driver sht15_driver = {
.of_match_table = of_match_ptr(sht15_dt_match),
},
.probe = sht15_probe,
.remove_new = sht15_remove,
.remove = sht15_remove,
.id_table = sht15_device_ids,
};
module_platform_driver(sht15_driver);

View File

@ -11,6 +11,7 @@
#include <linux/crc8.h>
#include <linux/delay.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/i2c.h>
#include <linux/jiffies.h>
#include <linux/module.h>
@ -31,6 +32,12 @@
*/
#define SHT4X_CMD_MEASURE_HPM 0b11111101
#define SHT4X_CMD_RESET 0b10010100
#define SHT4X_CMD_HEATER_20_1 0b00011110
#define SHT4X_CMD_HEATER_20_01 0b00010101
#define SHT4X_CMD_HEATER_110_1 0b00101111
#define SHT4X_CMD_HEATER_110_01 0b00100100
#define SHT4X_CMD_HEATER_200_1 0b00111001
#define SHT4X_CMD_HEATER_200_01 0b00110010
#define SHT4X_CMD_LEN 1
#define SHT4X_CRC8_LEN 1
@ -49,6 +56,10 @@ DECLARE_CRC8_TABLE(sht4x_crc8_table);
* struct sht4x_data - All the data required to operate an SHT4X chip
* @client: the i2c client associated with the SHT4X
* @lock: a mutex that is used to prevent parallel access to the i2c client
* @heating_complete: the time that the last heating finished
* @data_pending: true if and only if there are measurements to retrieve after heating
* @heater_power: the power at which the heater will be started
* @heater_time: the time for which the heater will remain turned on
* @valid: validity of fields below
* @update_interval: the minimum poll interval
* @last_updated: the previous time that the SHT4X was polled
@ -58,6 +69,10 @@ DECLARE_CRC8_TABLE(sht4x_crc8_table);
struct sht4x_data {
struct i2c_client *client;
struct mutex lock; /* atomic read data updates */
unsigned long heating_complete; /* in jiffies */
bool data_pending;
u32 heater_power; /* in milli-watts */
u32 heater_time; /* in milli-seconds */
bool valid; /* validity of fields below */
long update_interval; /* in milli-seconds */
long last_updated; /* in jiffies */
@ -79,19 +94,30 @@ static int sht4x_read_values(struct sht4x_data *data)
u8 crc;
u8 cmd[SHT4X_CMD_LEN] = {SHT4X_CMD_MEASURE_HPM};
u8 raw_data[SHT4X_RESPONSE_LENGTH];
unsigned long curr_jiffies;
mutex_lock(&data->lock);
next_update = data->last_updated +
msecs_to_jiffies(data->update_interval);
if (data->valid && time_before_eq(jiffies, next_update))
goto unlock;
curr_jiffies = jiffies;
if (time_before(curr_jiffies, data->heating_complete))
msleep(jiffies_to_msecs(data->heating_complete - curr_jiffies));
ret = i2c_master_send(client, cmd, SHT4X_CMD_LEN);
if (ret < 0)
goto unlock;
if (data->data_pending &&
time_before(jiffies, data->heating_complete + data->update_interval)) {
data->data_pending = false;
} else {
next_update = data->last_updated +
msecs_to_jiffies(data->update_interval);
usleep_range(SHT4X_MEAS_DELAY_HPM, SHT4X_MEAS_DELAY_HPM + SHT4X_DELAY_EXTRA);
if (data->valid && time_before_eq(jiffies, next_update))
goto unlock;
ret = i2c_master_send(client, cmd, SHT4X_CMD_LEN);
if (ret < 0)
goto unlock;
usleep_range(SHT4X_MEAS_DELAY_HPM, SHT4X_MEAS_DELAY_HPM + SHT4X_DELAY_EXTRA);
}
ret = i2c_master_recv(client, raw_data, SHT4X_RESPONSE_LENGTH);
if (ret != SHT4X_RESPONSE_LENGTH) {
@ -215,6 +241,143 @@ static int sht4x_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
}
}
static ssize_t heater_enable_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct sht4x_data *data = dev_get_drvdata(dev);
return sysfs_emit(buf, "%u\n", time_before(jiffies, data->heating_complete));
}
static ssize_t heater_enable_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
struct sht4x_data *data = dev_get_drvdata(dev);
bool status;
ssize_t ret;
u8 cmd;
u32 heating_time_bound;
ret = kstrtobool(buf, &status);
if (ret)
return ret;
if (!status)
return -EINVAL;
if (data->heater_time == 100) {
if (data->heater_power == 20)
cmd = SHT4X_CMD_HEATER_20_01;
else if (data->heater_power == 110)
cmd = SHT4X_CMD_HEATER_110_01;
else /* data->heater_power == 200 */
cmd = SHT4X_CMD_HEATER_200_01;
heating_time_bound = 110;
} else { /* data->heater_time == 1000 */
if (data->heater_power == 20)
cmd = SHT4X_CMD_HEATER_20_1;
else if (data->heater_power == 110)
cmd = SHT4X_CMD_HEATER_110_1;
else /* data->heater_power == 200 */
cmd = SHT4X_CMD_HEATER_200_1;
heating_time_bound = 1100;
}
mutex_lock(&data->lock);
if (time_before(jiffies, data->heating_complete)) {
ret = -EBUSY;
goto unlock;
}
ret = i2c_master_send(data->client, &cmd, SHT4X_CMD_LEN);
if (ret < 0)
goto unlock;
data->heating_complete = jiffies + msecs_to_jiffies(heating_time_bound);
data->data_pending = true;
unlock:
mutex_unlock(&data->lock);
return ret;
}
static ssize_t heater_power_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct sht4x_data *data = dev_get_drvdata(dev);
return sysfs_emit(buf, "%u\n", data->heater_power);
}
static ssize_t heater_power_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
struct sht4x_data *data = dev_get_drvdata(dev);
u32 power;
ssize_t ret;
ret = kstrtou32(buf, 10, &power);
if (ret)
return ret;
if (power != 20 && power != 110 && power != 200)
return -EINVAL;
data->heater_power = power;
return count;
}
static ssize_t heater_time_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct sht4x_data *data = dev_get_drvdata(dev);
return sysfs_emit(buf, "%u\n", data->heater_time);
}
static ssize_t heater_time_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
struct sht4x_data *data = dev_get_drvdata(dev);
u32 time;
ssize_t ret;
ret = kstrtou32(buf, 10, &time);
if (ret)
return ret;
if (time != 100 && time != 1000)
return -EINVAL;
data->heater_time = time;
return count;
}
static DEVICE_ATTR_RW(heater_enable);
static DEVICE_ATTR_RW(heater_power);
static DEVICE_ATTR_RW(heater_time);
static struct attribute *sht4x_attrs[] = {
&dev_attr_heater_enable.attr,
&dev_attr_heater_power.attr,
&dev_attr_heater_time.attr,
NULL
};
ATTRIBUTE_GROUPS(sht4x);
static const struct hwmon_channel_info * const sht4x_info[] = {
HWMON_CHANNEL_INFO(chip, HWMON_C_UPDATE_INTERVAL),
HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
@ -255,6 +418,9 @@ static int sht4x_probe(struct i2c_client *client)
data->update_interval = SHT4X_MIN_POLL_INTERVAL;
data->client = client;
data->heater_power = 200;
data->heater_time = 1000;
data->heating_complete = jiffies;
mutex_init(&data->lock);
@ -270,7 +436,7 @@ static int sht4x_probe(struct i2c_client *client)
client->name,
data,
&sht4x_chip_info,
NULL);
sht4x_groups);
return PTR_ERR_OR_ZERO(hwmon_dev);
}

View File

@ -784,7 +784,7 @@ static struct platform_driver sis5595_driver = {
.name = DRIVER_NAME,
},
.probe = sis5595_probe,
.remove_new = sis5595_remove,
.remove = sis5595_remove,
};
static int sis5595_pci_probe(struct pci_dev *dev,

View File

@ -23,13 +23,6 @@ struct sl28cpld_hwmon {
u32 offset;
};
static umode_t sl28cpld_hwmon_is_visible(const void *data,
enum hwmon_sensor_types type,
u32 attr, int channel)
{
return 0444;
}
static int sl28cpld_hwmon_read(struct device *dev,
enum hwmon_sensor_types type, u32 attr,
int channel, long *input)
@ -73,7 +66,7 @@ static const struct hwmon_channel_info * const sl28cpld_hwmon_info[] = {
};
static const struct hwmon_ops sl28cpld_hwmon_ops = {
.is_visible = sl28cpld_hwmon_is_visible,
.visible = 0444,
.read = sl28cpld_hwmon_read,
};

View File

@ -858,7 +858,7 @@ static struct platform_driver smsc47m1_driver __refdata = {
.driver = {
.name = DRVNAME,
},
.remove_new = __exit_p(smsc47m1_remove),
.remove = __exit_p(smsc47m1_remove),
};
static int __init smsc47m1_device_add(unsigned short address,

View File

@ -671,7 +671,7 @@ static int spd5118_resume(struct device *dev)
static DEFINE_SIMPLE_DEV_PM_OPS(spd5118_pm_ops, spd5118_suspend, spd5118_resume);
static const struct i2c_device_id spd5118_id[] = {
{ "spd5118", 0 },
{ "spd5118" },
{ }
};
MODULE_DEVICE_TABLE(i2c, spd5118_id);

View File

@ -18,14 +18,6 @@ SSAM_DEFINE_SYNC_REQUEST_CL_R(__ssam_fan_rpm_get, __le16, {
.command_id = 0x01,
});
// hwmon
static umode_t surface_fan_hwmon_is_visible(const void *drvdata,
enum hwmon_sensor_types type, u32 attr,
int channel)
{
return 0444;
}
static int surface_fan_hwmon_read(struct device *dev,
enum hwmon_sensor_types type, u32 attr,
int channel, long *val)
@ -49,7 +41,7 @@ static const struct hwmon_channel_info *const surface_fan_info[] = {
};
static const struct hwmon_ops surface_fan_hwmon_ops = {
.is_visible = surface_fan_hwmon_is_visible,
.visible = 0444,
.read = surface_fan_hwmon_read,
};

View File

@ -13,6 +13,7 @@
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/i2c.h>
#include <linux/i3c/device.h>
#include <linux/init.h>
#include <linux/jiffies.h>
#include <linux/regmap.h>
@ -323,33 +324,19 @@ static const struct regmap_config tmp108_regmap_config = {
.use_single_write = true,
};
static int tmp108_probe(struct i2c_client *client)
static int tmp108_common_probe(struct device *dev, struct regmap *regmap, char *name)
{
struct device *dev = &client->dev;
struct device *hwmon_dev;
struct tmp108 *tmp108;
int err;
u32 config;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_WORD_DATA)) {
dev_err(dev,
"adapter doesn't support SMBus word transactions\n");
return -ENODEV;
}
int err;
tmp108 = devm_kzalloc(dev, sizeof(*tmp108), GFP_KERNEL);
if (!tmp108)
return -ENOMEM;
dev_set_drvdata(dev, tmp108);
tmp108->regmap = devm_regmap_init_i2c(client, &tmp108_regmap_config);
if (IS_ERR(tmp108->regmap)) {
err = PTR_ERR(tmp108->regmap);
dev_err(dev, "regmap init failed: %d", err);
return err;
}
tmp108->regmap = regmap;
err = regmap_read(tmp108->regmap, TMP108_REG_CONF, &config);
if (err < 0) {
@ -383,13 +370,30 @@ static int tmp108_probe(struct i2c_client *client)
return err;
}
hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
hwmon_dev = devm_hwmon_device_register_with_info(dev, name,
tmp108,
&tmp108_chip_info,
NULL);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
static int tmp108_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct regmap *regmap;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_WORD_DATA))
return dev_err_probe(dev, -ENODEV,
"adapter doesn't support SMBus word transactions\n");
regmap = devm_regmap_init_i2c(client, &tmp108_regmap_config);
if (IS_ERR(regmap))
return dev_err_probe(dev, PTR_ERR(regmap), "regmap init failed");
return tmp108_common_probe(dev, regmap, client->name);
}
static int tmp108_suspend(struct device *dev)
{
struct tmp108 *tmp108 = dev_get_drvdata(dev);
@ -420,6 +424,7 @@ MODULE_DEVICE_TABLE(i2c, tmp108_i2c_ids);
#ifdef CONFIG_OF
static const struct of_device_id tmp108_of_ids[] = {
{ .compatible = "nxp,p3t1085", },
{ .compatible = "ti,tmp108", },
{}
};
@ -436,7 +441,39 @@ static struct i2c_driver tmp108_driver = {
.id_table = tmp108_i2c_ids,
};
module_i2c_driver(tmp108_driver);
static const struct i3c_device_id p3t1085_i3c_ids[] = {
I3C_DEVICE(0x011b, 0x1529, NULL),
{}
};
MODULE_DEVICE_TABLE(i3c, p3t1085_i3c_ids);
static int p3t1085_i3c_probe(struct i3c_device *i3cdev)
{
struct device *dev = i3cdev_to_dev(i3cdev);
struct regmap *regmap;
#ifdef CONFIG_REGMAP_I3C
regmap = devm_regmap_init_i3c(i3cdev, &tmp108_regmap_config);
#else
regmap = ERR_PTR(-ENODEV);
#endif
if (IS_ERR(regmap))
return dev_err_probe(dev, PTR_ERR(regmap),
"Failed to register i3c regmap\n");
return tmp108_common_probe(dev, regmap, "p3t1085_i3c");
}
static struct i3c_driver p3t1085_driver = {
.driver = {
.name = "p3t1085_i3c",
},
.probe = p3t1085_i3c_probe,
.id_table = p3t1085_i3c_ids,
};
module_i3c_i2c_driver(p3t1085_driver, &tmp108_driver)
MODULE_AUTHOR("John Muir <john@jmuir.com>");
MODULE_DESCRIPTION("Texas Instruments TMP108 temperature sensor driver");

View File

@ -317,7 +317,7 @@ static struct platform_driver env_driver = {
.of_match_table = env_match,
},
.probe = env_probe,
.remove_new = env_remove,
.remove = env_remove,
};
module_platform_driver(env_driver);

View File

@ -197,7 +197,7 @@ static struct platform_driver via_cputemp_driver = {
.name = DRVNAME,
},
.probe = via_cputemp_probe,
.remove_new = via_cputemp_remove,
.remove = via_cputemp_remove,
};
struct pdev_entry {

View File

@ -799,7 +799,7 @@ static struct platform_driver via686a_driver = {
.name = DRIVER_NAME,
},
.probe = via686a_probe,
.remove_new = via686a_remove,
.remove = via686a_remove,
};
static const struct pci_device_id via686a_pci_ids[] = {

View File

@ -1221,7 +1221,7 @@ static struct platform_driver vt1211_driver = {
.name = DRVNAME,
},
.probe = vt1211_probe,
.remove_new = vt1211_remove,
.remove = vt1211_remove,
};
static int __init vt1211_device_add(unsigned short address)

View File

@ -910,11 +910,11 @@ static void vt8231_remove(struct platform_device *pdev)
static struct platform_driver vt8231_driver = {
.driver = {
.driver = {
.name = DRIVER_NAME,
},
.probe = vt8231_probe,
.remove_new = vt8231_remove,
.remove = vt8231_remove,
};
static const struct pci_device_id vt8231_pci_ids[] = {

View File

@ -1844,7 +1844,7 @@ static struct platform_driver w83627hf_driver = {
.pm = W83627HF_DEV_PM_OPS,
},
.probe = w83627hf_probe,
.remove_new = w83627hf_remove,
.remove = w83627hf_remove,
};
static int __init w83627hf_find(int sioaddr, unsigned short *addr,

View File

@ -1828,7 +1828,7 @@ static struct platform_driver w83781d_isa_driver = {
.name = "w83781d",
},
.probe = w83781d_isa_probe,
.remove_new = w83781d_isa_remove,
.remove = w83781d_isa_remove,
};
/* return 1 if a supported chip is found, 0 otherwise */

View File

@ -772,7 +772,7 @@ MODULE_DEVICE_TABLE(of, xgene_hwmon_of_match);
static struct platform_driver xgene_hwmon_driver = {
.probe = xgene_hwmon_probe,
.remove_new = xgene_hwmon_remove,
.remove = xgene_hwmon_remove,
.driver = {
.name = "xgene-slimpro-hwmon",
.of_match_table = xgene_hwmon_of_match,

View File

@ -368,7 +368,9 @@ enum hwmon_intrusion_attributes {
/**
* struct hwmon_ops - hwmon device operations
* @is_visible: Callback to return attribute visibility. Mandatory.
* @visible: Static visibility. If non-zero, 'is_visible' is ignored.
* @is_visible: Callback to return attribute visibility. Mandatory unless
* 'visible' is non-zero.
* Parameters are:
* @const void *drvdata:
* Pointer to driver-private data structure passed
@ -412,6 +414,7 @@ enum hwmon_intrusion_attributes {
* The function returns 0 on success or a negative error number.
*/
struct hwmon_ops {
umode_t visible;
umode_t (*is_visible)(const void *drvdata, enum hwmon_sensor_types type,
u32 attr, int channel);
int (*read)(struct device *dev, enum hwmon_sensor_types type,

View File

@ -1,15 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _LINUX_MAX6639_H
#define _LINUX_MAX6639_H
#include <linux/types.h>
/* platform data for the MAX6639 temperature sensor and fan control */
struct max6639_platform_data {
bool pwm_polarity; /* Polarity low (0) or high (1, default) */
int ppr; /* Pulses per rotation 1..4 (default == 2) */
int rpm_range; /* 2000, 4000 (default), 8000 or 16000 */
};
#endif /* _LINUX_MAX6639_H */