hwmon updates for v6.11-rc1

* Obsolete driver removals
 
   - Removed obsolete adm1021 and max6642 drivers
 
 * New drivers
 
   - MPS MP2891
 
   - MPS MP2993
 
   - MPS MP9941
 
   - MPS MP5920
 
   - SPD5118 (Temperature Sensor and EEPROM)
 
 * Added device support to existing drivers
 
   - g762: G761
 
   - dell-smm: Dell OptiPlex 7060
 
   - asus-ec-sensors: ProArt X670E-CREATOR WIFI
 
   - corsair-psu: HX1200i Series 2023 psu
 
   - nzxt-smart2: Additional USB IS for NZXT RGB & Fan Controller
 
 * Notable enhancements and fixes
 
   - Removed use of i2c_match_id()
 
   - Constified struct regmap_config where feasible
 
   - Cleaned up amc6821 driver, and converted to use regmap and with_info API
 
   - Converted max6639 driver to use with_info API; added support for
     additional sysfs attributes
 
   - Fixed various sysfs attribute underflows
 
   - Added PEC support to hwmon core, and use in lm90 and max31827 drivers
 
 * Various other minor fixes and improvements
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEiHPvMQj9QTOCiqgVyx8mb86fmYEFAmaUaiIACgkQyx8mb86f
 mYHnbRAAhyaqFF1w5B/XEuZOQ3obG+pAV1ZioE7wXpeRDlniJ9/WgFe4bPpvTNQm
 C2LHW6kVtwxybM/FOJz9BN32WiP9v2kp9upb7hhHHo32+5dv57iQR5lB+JC/7lpy
 HLisqLrPc5eDQlUiOdeph5fsKyuydxQkefbR1x4shxmcgn+D5M+AYjuAZOA6fSun
 spSzgTyPGWgGfhQjSzKoA7DHG3S2pFxRTfqOArvKLOM+ahyOCFuS8Kbq/JpqvalB
 moiyIJUeOrgJcTcHSecQ/uFxFiShGBs6EQ1Ao8O9kO0WFw7ke1fw/fWESeTvtiBm
 0Z3zfqAIKcnCXvKEupltWXf6kASx5LSlycODTZQlXEROeYhzhJw3J5qF4h0r4+EM
 oKmptVLLLekVYrjQlarBtHqpdfnwsL1GuQTk5fZz7ZbG85/ktKV6aGTeKGYdBvll
 zZSjz3Jys2u191uxATpJ3vmjaggPylX6dXgmJ9u9lW16+/OnNq7sWURekIfE3F+P
 dpZsaia2KN3WAnX/qLQWOOOBbkIIGxn48e+Hi6QY9igVUbY+P/BBfhb9UiWonLKU
 exRxAFNV5a4nM0ipJF4odwMqzC9ZjrpY6zFQFZCrG79zOO62/gADUAVzwu2LGGig
 p6hqGJmEOkJgYd0zo8KP8ABwgffn8EAtgXBtQU0RS/ls7Z0S58U=
 =ebWS
 -----END PGP SIGNATURE-----

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

Pull hwmon updates from Guenter Roeck:
 "Obsolete driver removals:

   - Removed obsolete adm1021 and max6642 drivers

  New drivers:

   - MPS MP2891, MP2993, MP9941, and MP5920

   - SPD5118 (Temperature Sensor and EEPROM)

  Added device support to existing drivers:

   - g762: G761

   - dell-smm: Dell OptiPlex 7060

   - asus-ec-sensors: ProArt X670E-CREATOR WIFI

   - corsair-psu: HX1200i Series 2023 psu

   - nzxt-smart2: Additional USB IS for NZXT RGB & Fan Controller

  Notable enhancements and fixes:

   - Removed use of i2c_match_id()

   - Constified struct regmap_config where feasible

   - Cleaned up amc6821 driver, and converted to use regmap and
     with_info API

   - Converted max6639 driver to use with_info API; added support for
     additional sysfs attributes

   - Fixed various sysfs attribute underflows

   - Added PEC support to hwmon core, and use in lm90 and max31827
     drivers

  And various other minor fixes and improvements"

* tag 'hwmon-for-v6.11' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (103 commits)
  hwmon: (max6697) Fix swapped temp{1,8} critical alarms
  hwmon: (max6697) Fix underflow when writing limit attributes
  hwmon: Remove obsolete adm1021 and max6642 drivers
  hwmon: (pmbus/ltc4286) Drop unused i2c device ids
  hwmon: (g762) Initialize fans after configuring clock
  hwmon: (amc6821) Add support for pwm1_mode attribute
  hwmon: (amc6821) Convert to with_info API
  hwmon: (amc6821) Convert to use regmap
  hwmon: (amc6821) Drop unnecessary enum chips
  hwmon: (amc6821) Use BIT() and GENMASK()
  hwmon: (amc6821) Use tabs for column alignment in defines
  hwmon: (amc6821) Reorder include files, drop unnecessary ones
  hwmon: (amc6821) Add support for fan1_target and pwm1_enable mode 4
  hwmon: (amc6821) Rename fan1_div to fan1_pulses
  hwmon: (amc6821) Make reading and writing fan speed limits consistent
  hwmon: (amc6821) Stop accepting invalid pwm values
  hwmon: (w83627ehf) Fix underflows seen when writing limit attributes
  hwmon: (nct6775-core) Fix underflows seen when writing limit attributes
  hwmon: (lm95234) Fix underflows seen when writing limit attributes
  hwmon: (adc128d818) Fix underflows seen when writing limit attributes
  ...
This commit is contained in:
Linus Torvalds 2024-07-15 17:39:13 -07:00
commit 500a711df6
92 changed files with 4473 additions and 2314 deletions

View File

@ -1,47 +0,0 @@
GMT G762/G763 PWM Fan controller
Required node properties:
- "compatible": must be either "gmt,g762" or "gmt,g763"
- "reg": I2C bus address of the device
- "clocks": a fixed clock providing input clock frequency
on CLK pin of the chip.
Optional properties:
- "fan_startv": fan startup voltage. Accepted values are 0, 1, 2 and 3.
The higher the more.
- "pwm_polarity": pwm polarity. Accepted values are 0 (positive duty)
and 1 (negative duty).
- "fan_gear_mode": fan gear mode. Supported values are 0, 1 and 2.
If an optional property is not set in .dts file, then current value is kept
unmodified (e.g. u-boot installed value).
Additional information on operational parameters for the device is available
in Documentation/hwmon/g762.rst. A detailed datasheet for the device is available
at http://natisbad.org/NAS/refs/GMT_EDS-762_763-080710-0.2.pdf.
Example g762 node:
clocks {
#address-cells = <1>;
#size-cells = <0>;
g762_clk: fixedclk {
compatible = "fixed-clock";
#clock-cells = <0>;
clock-frequency = <8192>;
}
}
g762: g762@3e {
compatible = "gmt,g762";
reg = <0x3e>;
clocks = <&g762_clk>
fan_gear_mode = <0>; /* chip default */
fan_startv = <1>; /* chip default */
pwm_polarity = <0>; /* chip default */
};

View File

@ -0,0 +1,95 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/gmt,g762.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: GMT G761/G762/G763 PWM Fan controller
maintainers:
- Christian Marangi <ansuelsmth@gmail.com>
description: |
GMT G761/G762/G763 PWM Fan controller.
G761 supports an internal-clock hence the clocks property is optional.
If not defined, internal-clock will be used. (31KHz is the clock of
the internal crystal oscillator)
If an optional property is not set in DT, then current value is kept
unmodified (e.g. bootloader installed value).
Additional information on operational parameters for the device is available
in Documentation/hwmon/g762.rst. A detailed datasheet for the device is available
at http://natisbad.org/NAS/refs/GMT_EDS-762_763-080710-0.2.pdf.
properties:
compatible:
enum:
- gmt,g761
- gmt,g762
- gmt,g763
reg:
maxItems: 1
clocks:
description: a fixed clock providing input clock frequency on CLK
pin of the chip.
maxItems: 1
fan_startv:
description: Fan startup voltage step
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1, 2, 3]
pwm_polarity:
description: PWM polarity (positive or negative duty)
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1]
fan_gear_mode:
description: FAN gear mode. Configure High speed fan setting factor
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1, 2]
required:
- compatible
- reg
if:
properties:
compatible:
contains:
enum:
- gmt,g762
- gmt,g763
then:
required:
- clocks
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
g762@3e {
compatible = "gmt,g762";
reg = <0x3e>;
clocks = <&g762_clk>;
fan_gear_mode = <0>;
fan_startv = <1>;
pwm_polarity = <0>;
};
g761@1e {
compatible = "gmt,g761";
reg = <0x1e>;
fan_gear_mode = <0>;
fan_startv = <1>;
pwm_polarity = <0>;
};
};

View File

@ -0,0 +1,92 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/maxim,max6639.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Maxim max6639
maintainers:
- Naresh Solanki <naresh.solanki@9elements.com>
description: |
The MAX6639 is a 2-channel temperature monitor with dual, automatic, PWM
fan-speed controller. It monitors its own temperature and one external
diode-connected transistor or the temperatures of two external diode-connected
transistors, typically available in CPUs, FPGAs, or GPUs.
Datasheets:
https://datasheets.maximintegrated.com/en/ds/MAX6639-MAX6639F.pdf
properties:
compatible:
enum:
- maxim,max6639
reg:
maxItems: 1
'#address-cells':
const: 1
'#size-cells':
const: 0
'#pwm-cells':
const: 3
required:
- compatible
- reg
patternProperties:
"^fan@[0-1]$":
type: object
description:
Represents the two fans and their specific configuration.
$ref: fan-common.yaml#
unevaluatedProperties: false
properties:
reg:
description:
The fan number.
required:
- reg
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
fan1: fan-controller@10 {
compatible = "maxim,max6639";
reg = <0x10>;
#address-cells = <1>;
#size-cells = <0>;
#pwm-cells = <3>;
fan@0 {
reg = <0x0>;
pulses-per-revolution = <2>;
max-rpm = <4000>;
target-rpm = <1000>;
pwms = <&fan1 0 25000 0>;
};
fan@1 {
reg = <0x1>;
pulses-per-revolution = <2>;
max-rpm = <8000>;
pwms = <&fan1 1 25000 0>;
};
};
};
...

View File

@ -66,6 +66,14 @@ properties:
description: phandle to the regulator that provides the VS supply typically
in range from 2.7 V to 5.5 V.
ti,alert-polarity-active-high:
description: Alert pin is asserted based on the value of Alert polarity Bit
of Mask/Enable register. Default value is Normal (0 which maps to
active-low open collector). The other value is Inverted
(1 which maps to active-high open collector). Specify this property to set
the alert polarity to active-high.
$ref: /schemas/types.yaml#/definitions/flag
required:
- compatible
- reg
@ -88,5 +96,6 @@ examples:
label = "vdd_3v0";
shunt-resistor = <1000>;
vs-supply = <&vdd_3v0>;
ti,alert-polarity-active-high;
};
};

View File

@ -9,6 +9,14 @@ title: TMP108 temperature sensor
maintainers:
- Krzysztof Kozlowski <krzk@kernel.org>
description: |
The TMP108 is a digital-output temperature sensor with a
dynamically-programmable limit window, and under- and overtemperature
alert functions.
Datasheets:
https://www.ti.com/product/TMP108
properties:
compatible:
enum:
@ -24,6 +32,9 @@ properties:
"#thermal-sensor-cells":
const: 0
vcc-supply:
description: phandle to the regulator that provides the V+ supply
required:
- compatible
- reg
@ -45,6 +56,7 @@ examples:
interrupts = <7 IRQ_TYPE_LEVEL_LOW>;
pinctrl-names = "default";
pinctrl-0 = <&tmp_alrt>;
vcc-supply = <&supply>;
#thermal-sensor-cells = <0>;
};
};

View File

@ -168,6 +168,8 @@ properties:
- isil,isl69269
# Intersil ISL76682 Ambient Light Sensor
- isil,isl76682
# JEDEC JESD300 (SPD5118) Hub and Serial Presence Detect
- jedec,spd5118
# Linear Technology LTC2488
- lineartechnology,ltc2488
# 5 Bit Programmable, Pulse-Width Modulator
@ -286,14 +288,22 @@ properties:
- mps,mp2857
# Monolithic Power Systems Inc. multi-phase controller mp2888
- 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
- mps,mp5920
# Monolithic Power Systems Inc. multi-phase hot-swap controller mp5990
- mps,mp5990
# Monolithic Power Systems Inc. digital step-down converter mp9941
- mps,mp9941
# Monolithic Power Systems Inc. synchronous step-down converter mpq8785
- mps,mpq8785
# Temperature sensor with integrated fan control

View File

@ -1,153 +0,0 @@
Kernel driver adm1021
=====================
Supported chips:
* Analog Devices ADM1021
Prefix: 'adm1021'
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
Datasheet: Publicly available at the Analog Devices website
* Analog Devices ADM1021A/ADM1023
Prefix: 'adm1023'
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
Datasheet: Publicly available at the Analog Devices website
* Genesys Logic GL523SM
Prefix: 'gl523sm'
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
Datasheet:
* Maxim MAX1617
Prefix: 'max1617'
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
Datasheet: Publicly available at the Maxim website
* Maxim MAX1617A
Prefix: 'max1617a'
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
Datasheet: Publicly available at the Maxim website
* National Semiconductor LM84
Prefix: 'lm84'
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
Datasheet: Publicly available at the National Semiconductor website
* Philips NE1617
Prefix: 'max1617' (probably detected as a max1617)
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
Datasheet: Publicly available at the Philips website
* Philips NE1617A
Prefix: 'max1617' (probably detected as a max1617)
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
Datasheet: Publicly available at the Philips website
* TI THMC10
Prefix: 'thmc10'
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
Datasheet: Publicly available at the TI website
* Onsemi MC1066
Prefix: 'mc1066'
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
Datasheet: Publicly available at the Onsemi website
Authors:
- Frodo Looijaard <frodol@dds.nl>,
- Philip Edelbrock <phil@netroedge.com>
Module Parameters
-----------------
* read_only: int
Don't set any values, read only mode
Description
-----------
The chips supported by this driver are very similar. The Maxim MAX1617 is
the oldest; it has the problem that it is not very well detectable. The
MAX1617A solves that. The ADM1021 is a straight clone of the MAX1617A.
Ditto for the THMC10. From here on, we will refer to all these chips as
ADM1021-clones.
The ADM1021 and MAX1617A reports a die code, which is a sort of revision
code. This can help us pinpoint problems; it is not very useful
otherwise.
ADM1021-clones implement two temperature sensors. One of them is internal,
and measures the temperature of the chip itself; the other is external and
is realised in the form of a transistor-like device. A special alarm
indicates whether the remote sensor is connected.
Each sensor has its own low and high limits. When they are crossed, the
corresponding alarm is set and remains on as long as the temperature stays
out of range. Temperatures are measured in degrees Celsius. Measurements
are possible between -65 and +127 degrees, with a resolution of one degree.
If an alarm triggers, it will remain triggered until the hardware register
is read at least once. This means that the cause for the alarm may already
have disappeared!
This driver only updates its values each 1.5 seconds; reading it more often
will do no harm, but will return 'old' values. It is possible to make
ADM1021-clones do faster measurements, but there is really no good reason
for that.
Netburst-based Xeon support
---------------------------
Some Xeon processors based on the Netburst (early Pentium 4, from 2001 to
2003) microarchitecture had real MAX1617, ADM1021, or compatible chips
within them, with two temperature sensors. Other Xeon processors of this
era (with 400 MHz FSB) had chips with only one temperature sensor.
If you have such an old Xeon, and you get two valid temperatures when
loading the adm1021 module, then things are good.
If nothing happens when loading the adm1021 module, and you are certain
that your specific Xeon processor model includes compatible sensors, you
will have to explicitly instantiate the sensor chips from user-space. See
method 4 in Documentation/i2c/instantiating-devices.rst. Possible slave
addresses are 0x18, 0x1a, 0x29, 0x2b, 0x4c, or 0x4e. It is likely that
only temp2 will be correct and temp1 will have to be ignored.
Previous generations of the Xeon processor (based on Pentium II/III)
didn't have these sensors. Next generations of Xeon processors (533 MHz
FSB and faster) lost them, until the Core-based generation which
introduced integrated digital thermal sensors. These are supported by
the coretemp driver.

View File

@ -47,13 +47,18 @@ fan1_input ro tachometer speed
fan1_min rw "
fan1_max rw "
fan1_fault ro "
fan1_div rw Fan divisor can be either 2 or 4.
fan1_pulses rw Pulses per revolution can be either 2 or 4.
fan1_target rw Target fan speed, to be used with pwm1_enable
mode 4.
pwm1 rw pwm1
pwm1_enable rw regulator mode, 1=open loop, 2=fan controlled
by remote temperature, 3=fan controlled by
combination of the on-chip temperature and
remote-sensor temperature,
4=fan controlled by target rpm set with
fan1_target attribute.
pwm1_mode rw Fan duty control mode (0=DC, 1=PWM)
pwm1_auto_channels_temp ro 1 if pwm_enable==2, 3 if pwm_enable==3
pwm1_auto_point1_pwm ro Hardwired to 0, shared for both
temperature channels.

View File

@ -8,6 +8,7 @@ Supported boards:
* PRIME X570-PRO
* Pro WS X570-ACE
* ProArt X570-CREATOR WIFI
* ProArt X670E-CREATOR WIFI
* ProArt B550-CREATOR
* ROG CROSSHAIR VIII DARK HERO
* ROG CROSSHAIR VIII HERO (WI-FI)

View File

@ -39,3 +39,11 @@ fan[1-6]_target Sets fan speed target rpm.
pwm[1-6] Sets the fan speed. Values from 0-255. Can only be read if pwm
was set directly.
======================= =====================================================================
Debugfs entries
---------------
======================= ===================
firmware_version Firmware version
bootloader_version Bootloader version
======================= ===================

View File

@ -15,11 +15,11 @@ Supported devices:
Corsair HX850i
Corsair HX1000i (Series 2022 and 2023)
Corsair HX1000i (Legacy and Series 2023)
Corsair HX1200i
Corsair HX1200i (Legacy and Series 2023)
Corsair HX1500i (Series 2022 and 2023)
Corsair HX1500i (Legacy and Series 2023)
Corsair RM550i

View File

@ -360,6 +360,8 @@ Firmware Bug Affected Machines
======================================================= =================
Reading of fan states return spurious errors. Precision 490
OptiPlex 7060
Reading of fan types causes erratic fan behaviour. Studio XPS 8000
Studio XPS 8100

View File

@ -25,7 +25,6 @@ Hardware Monitoring Kernel Drivers
acpi_power_meter
ad7314
adc128d818
adm1021
adm1025
adm1026
adm1031
@ -155,7 +154,6 @@ Hardware Monitoring Kernel Drivers
max34440
max6620
max6639
max6642
max6650
max6697
max8688
@ -166,9 +164,13 @@ Hardware Monitoring Kernel Drivers
mlxreg-fan
mp2856
mp2888
mp2891
mp2975
mp2993
mp5023
mp5920
mp5990
mp9941
mpq8785
nct6683
nct6775
@ -216,6 +218,7 @@ Hardware Monitoring Kernel Drivers
smsc47m192
smsc47m1
sparx5-temp
spd5118
stpddc60
surface_fan
sy7636a-hwmon

View File

@ -131,7 +131,14 @@ The Fault Queue bits select how many consecutive temperature faults must occur
before overtemperature or undertemperature faults are indicated in the
corresponding status bits.
Notes
-----
PEC Support
-----------
PEC is not implemented.
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
usage (+33% for both write and reads) in normal conditions.
Since this operation implies there will be an extra delay to each
transaction, PEC can be disabled or enabled through sysfs.
Just write 1 to the "pec" file for enabling PEC and 0 for disabling it.

View File

@ -1,27 +0,0 @@
Kernel driver max6642
=====================
Supported chips:
* Maxim MAX6642
Prefix: 'max6642'
Addresses scanned: I2C 0x48-0x4f
Datasheet: Publicly available at the Maxim website
http://datasheets.maxim-ic.com/en/ds/MAX6642.pdf
Authors:
Per Dalen <per.dalen@appeartv.com>
Description
-----------
The MAX6642 is a digital temperature sensor. It senses its own temperature as
well as the temperature on one external diode.
All temperature values are given in degrees Celsius. Resolution
is 0.25 degree for the local temperature and for the remote temperature.

View File

@ -0,0 +1,179 @@
.. SPDX-License-Identifier: GPL-2.0
Kernel driver mp2891
====================
Supported chips:
* MPS mp2891
Prefix: 'mp2891'
* Datasheet
Publicly available at the MPS website : https://www.monolithicpower.com/en/mp2891.html
Author:
Noah Wang <noahwang.wang@outlook.com>
Description
-----------
This driver implements support for Monolithic Power Systems, Inc. (MPS)
MP2891 Multi-phase Digital VR Controller.
Device compliant with:
- PMBus rev 1.3 interface.
Device supports direct and linear format for reading input voltage,
output voltage, input current, output current, input power, output
power, and temperature.
The driver exports the following attributes via the 'sysfs' files
for input voltage:
**in1_input**
**in1_label**
**in1_crit**
**in1_crit_alarm**
**in1_lcrit**
**in1_lcrit_alarm**
**in1_min**
**in1_min_alarm**
The driver provides the following attributes for output voltage:
**in2_input**
**in2_label**
**in2_crit**
**in2_crit_alarm**
**in2_lcrit**
**in2_lcrit_alarm**
**in2_min**
**in2_min_alarm**
**in3_input**
**in3_label**
**in3_crit**
**in3_crit_alarm**
**in3_lcrit**
**in3_lcrit_alarm**
**in3_min**
**in3_min_alarm**
The driver provides the following attributes for input current:
**curr1_input**
**curr1_label**
**curr1_max**
**curr1_max_alarm**
**curr2_input**
**curr2_label**
**curr2_max**
**curr2_max_alarm**
The driver provides the following attributes for output current:
**curr3_input**
**curr3_label**
**curr3_crit**
**curr3_crit_alarm**
**curr3_max**
**curr3_max_alarm**
**curr4_input**
**curr4_label**
**curr4_crit**
**curr4_crit_alarm**
**curr4_max**
**curr4_max_alarm**
The driver provides the following attributes for input power:
**power1_input**
**power1_label**
**power1_max**
**power1_alarm**
**power2_input**
**power2_label**
**power2_max**
**power2_alarm**
The driver provides the following attributes for output power:
**power3_input**
**power3_label**
**power4_input**
**power4_label**
The driver provides the following attributes for temperature:
**temp1_input**
**temp1_crit**
**temp1_crit_alarm**
**temp1_max**
**temp1_max_alarm**
**temp2_input**
**temp2_crit**
**temp2_crit_alarm**
**temp2_max**
**temp2_max_alarm**

View File

@ -0,0 +1,150 @@
.. SPDX-License-Identifier: GPL-2.0
Kernel driver mp2993
====================
Supported chips:
* MPS mp2993
Prefix: 'mp2993'
* Datasheet
https://scnbwymvp-my.sharepoint.com/:f:/g/personal/admin_scnbwy_com/Eth4kX1_J1hMsaASHiOYL4QBHU5a75r-tRfLKbHnJFdKLQ?e=vxj3DF
Author:
Noah Wang <noahwang.wang@outlook.com>
Description
-----------
This driver implements support for Monolithic Power Systems, Inc. (MPS)
MP2993 Dual Loop Digital Multi-phase Controller.
Device compliant with:
- PMBus rev 1.3 interface.
The driver exports the following attributes via the 'sysfs' files
for input voltage:
**in1_input**
**in1_label**
**in1_crit**
**in1_crit_alarm**
**in1_lcrit**
**in1_lcrit_alarm**
**in1_max**
**in1_max_alarm**
**in1_min**
**in1_min_alarm**
The driver provides the following attributes for output voltage:
**in2_input**
**in2_label**
**in2_crit**
**in2_crit_alarm**
**in2_lcrit**
**in2_lcrit_alarm**
**in3_input**
**in3_label**
**in3_crit**
**in3_crit_alarm**
**in3_lcrit**
**in3_lcrit_alarm**
The driver provides the following attributes for input current:
**curr1_input**
**curr1_label**
**curr1_max**
**curr1_max_alarm**
The driver provides the following attributes for output current:
**curr2_input**
**curr2_label**
**curr2_crit**
**curr2_crit_alarm**
**curr2_max**
**curr2_max_alarm**
**curr3_input**
**curr3_label**
**curr3_crit**
**curr3_crit_alarm**
**curr3_max**
**curr3_max_alarm**
The driver provides the following attributes for input power:
**power1_input**
**power1_label**
The driver provides the following attributes for output power:
**power2_input**
**power2_label**
**power3_input**
**power3_label**
The driver provides the following attributes for temperature:
**temp1_input**
**temp1_crit**
**temp1_crit_alarm**
**temp1_max**
**temp1_max_alarm**
**temp2_input**
**temp2_crit**
**temp2_crit_alarm**
**temp2_max**
**temp2_max_alarm**

View File

@ -0,0 +1,91 @@
.. SPDX-License-Identifier: GPL-2.0
Kernel driver mp5920
====================
Supported chips:
* MPS MP5920
Prefix: 'mp5920'
* Datasheet
Publicly available at the MPS website : https://www.monolithicpower.com/en/mp5920.html
Authors:
Tony Ao <tony_ao@wiwynn.com>
Alex Vdovydchenko <xzeol@yahoo.com>
Description
-----------
This driver implements support for Monolithic Power Systems, Inc. (MPS)
MP5920 Hot-Swap Controller.
Device compliant with:
- PMBus rev 1.3 interface.
Device supports direct and linear format for reading input voltage,
output voltage, output current, input power and temperature.
The driver exports the following attributes via the 'sysfs' files
for input voltage:
**in1_input**
**in1_label**
**in1_rated_max**
**in1_rated_min**
**in1_crit**
**in1_alarm**
The driver provides the following attributes for output voltage:
**in2_input**
**in2_label**
**in2_rated_max**
**in2_rated_min**
**in2_alarm**
The driver provides the following attributes for output current:
**curr1_input**
**curr1_label**
**curr1_crit**
**curr1_alarm**
**curr1_rated_max**
The driver provides the following attributes for input power:
**power1_input**
**power1_label**
**power1_max**
**power1_rated_max**
The driver provides the following attributes for temperature:
**temp1_input**
**temp1_max**
**temp1_crit**
**temp1_alarm**

View File

@ -0,0 +1,92 @@
.. SPDX-License-Identifier: GPL-2.0
Kernel driver mp9941
====================
Supported chips:
* MPS mp9941
Prefix: 'mp9941'
* Datasheet
https://scnbwymvp-my.sharepoint.com/:f:/g/personal/admin_scnbwy_com/Eth4kX1_J1hMsaASHiOYL4QBHU5a75r-tRfLKbHnJFdKLQ?e=vxj3DF
Author:
Noah Wang <noahwang.wang@outlook.com>
Description
-----------
This driver implements support for Monolithic Power Systems, Inc. (MPS)
MP9941 digital step-down converter.
Device compliant with:
- PMBus rev 1.3 interface.
The driver exports the following attributes via the 'sysfs' files
for input voltage:
**in1_input**
**in1_label**
**in1_crit**
**in1_crit_alarm**
The driver provides the following attributes for output voltage:
**in2_input**
**in2_label**
**in2_lcrit**
**in2_lcrit_alarm**
**in2_rated_max**
**in2_rated_min**
The driver provides the following attributes for input current:
**curr1_input**
**curr1_label**
**curr1_max**
**curr1_max_alarm**
The driver provides the following attributes for output current:
**curr2_input**
**curr2_label**
The driver provides the following attributes for input power:
**power1_input**
**power1_label**
The driver provides the following attributes for output power:
**power2_input**
**power2_label**
The driver provides the following attributes for temperature:
**temp1_input**
**temp1_crit**
**temp1_crit_alarm**
**temp1_max**
**temp1_max_alarm**

View File

@ -0,0 +1,63 @@
.. SPDX-License-Identifier: GPL-2.0-or-later
Kernel driver spd5118
=====================
Supported chips:
* SPD5118 (JEDEC JESD300) compliant temperature sensor chips
JEDEC standard download:
https://www.jedec.org/standards-documents/docs/jesd300-5b01
(account required)
Prefix: 'spd5118'
Addresses scanned: I2C 0x50 - 0x57
Author:
Guenter Roeck <linux@roeck-us.net>
Description
-----------
This driver implements support for SPD5118 (JEDEC JESD300) compliant temperature
sensors, which are used on many DDR5 memory modules. Some systems use the sensor
to prevent memory overheating by automatically throttling the memory controller.
The driver auto-detects SPD5118 compliant chips, but can also be instantiated
using devicetree/firmware nodes.
A SPD5118 compliant chip supports a single temperature sensor. Critical minimum,
minimum, maximum, and critical temperature can be configured. There are alarms
for low critical, low, high, and critical thresholds.
Hardware monitoring sysfs entries
---------------------------------
======================= ==================================
temp1_input Temperature (RO)
temp1_lcrit Low critical high temperature (RW)
temp1_min Minimum temperature (RW)
temp1_max Maximum temperature (RW)
temp1_crit Critical high temperature (RW)
temp1_lcrit_alarm Temperature low critical alarm
temp1_min_alarm Temperature low alarm
temp1_max_alarm Temperature high alarm
temp1_crit_alarm Temperature critical alarm
======================= ==================================
Alarm attributes are sticky until read and will be cleared afterwards
unless the alarm condition still applies.
SPD (Serial Presence Detect) support
------------------------------------
The driver also supports reading the SPD NVRAM on SPD5118 compatible chips.
SPD data is available from the 'eeprom' binary attribute file attached to the
chip's I2C device.

View File

@ -15265,6 +15265,27 @@ S: Maintained
F: Documentation/devicetree/bindings/leds/backlight/mps,mp3309c.yaml
F: drivers/video/backlight/mp3309c.c
MPS MP2891 DRIVER
M: Noah Wang <noahwang.wang@outlook.com>
L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/mp2891.rst
F: drivers/hwmon/pmbus/mp2891.c
MPS MP2993 DRIVER
M: Noah Wang <noahwang.wang@outlook.com>
L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/mp2993.rst
F: drivers/hwmon/pmbus/mp2993.c
MPS MP9941 DRIVER
M: Noah Wang <noahwang.wang@outlook.com>
L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/mp9941.rst
F: drivers/hwmon/pmbus/mp9941.c
MR800 AVERMEDIA USB FM RADIO DRIVER
M: Alexey Klimov <klimov.linux@gmail.com>
L: linux-media@vger.kernel.org

View File

@ -105,18 +105,6 @@ config SENSORS_AD7418
This driver can also be built as a module. If so, the module
will be called ad7418.
config SENSORS_ADM1021
tristate "Analog Devices ADM1021 and compatibles"
depends on I2C
depends on SENSORS_LM90=n
help
If you say yes here you get support for Analog Devices ADM1021
and ADM1023 sensor chips and clones: Maxim MAX1617 and MAX1617A,
Genesys Logic GL523SM, National Semiconductor LM84 and TI THMC10.
This driver can also be built as a module. If so, the module
will be called adm1021.
config SENSORS_ADM1025
tristate "Analog Devices ADM1025 and compatibles"
depends on I2C
@ -1252,18 +1240,6 @@ config SENSORS_MAX6639
This driver can also be built as a module. If so, the module
will be called max6639.
config SENSORS_MAX6642
tristate "Maxim MAX6642 sensor chip"
depends on I2C
depends on SENSORS_LM90=n
help
If you say yes here you get support for MAX6642 sensor chip.
MAX6642 is a SMBus-Compatible Remote/Local Temperature Sensor
with Overtemperature Alarm from Maxim.
This driver can also be built as a module. If so, the module
will be called max6642.
config SENSORS_MAX6650
tristate "Maxim MAX6650 sensor chip"
depends on I2C
@ -2138,6 +2114,7 @@ config SENSORS_ADS7871
config SENSORS_AMC6821
tristate "Texas Instruments AMC6821"
depends on I2C
select REGMAP_I2C
help
If you say yes here you get support for the Texas Instruments
AMC6821 hardware monitoring chips.
@ -2192,6 +2169,37 @@ config SENSORS_INA3221
This driver can also be built as a module. If so, the module
will be called ina3221.
config SENSORS_SPD5118
tristate "SPD5118 Compliant Temperature Sensors"
depends on I2C
select REGMAP_I2C
help
If you say yes here you get support for SPD5118 (JEDEC JESD300)
compliant temperature sensors. Such sensors are found on DDR5 memory
modules.
This driver can also be built as a module. If so, the module
will be called spd5118.
config SENSORS_SPD5118_DETECT
bool "Enable detect function"
depends on SENSORS_SPD5118
default (!DMI || !X86)
help
If enabled, the driver auto-detects if a chip in the SPD address
range is compliant to the SPD51888 standard and auto-instantiates
if that is the case. If disabled, SPD5118 compliant devices have
to be instantiated by other means. On X86 systems with DMI support
this will typically be done from DMI DDR detection code in the
I2C SMBus subsystem. Devicetree based systems will instantiate
attached devices if the DIMMs are listed in the devicetree file.
Disabling the detect function will speed up boot time and reduce
the risk of mis-detecting SPD5118 compliant devices. However, it
may result in missed DIMMs under some circumstances.
If unsure, say Y.
config SENSORS_TC74
tristate "Microchip TC74"
depends on I2C

View File

@ -31,7 +31,6 @@ obj-$(CONFIG_SENSORS_AD7414) += ad7414.o
obj-$(CONFIG_SENSORS_AD7418) += ad7418.o
obj-$(CONFIG_SENSORS_ADC128D818) += adc128d818.o
obj-$(CONFIG_SENSORS_ADCXX) += adcxx.o
obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o
obj-$(CONFIG_SENSORS_ADM1025) += adm1025.o
obj-$(CONFIG_SENSORS_ADM1026) += adm1026.o
obj-$(CONFIG_SENSORS_ADM1029) += adm1029.o
@ -155,7 +154,6 @@ obj-$(CONFIG_SENSORS_MAX31760) += max31760.o
obj-$(CONFIG_SENSORS_MAX6620) += max6620.o
obj-$(CONFIG_SENSORS_MAX6621) += max6621.o
obj-$(CONFIG_SENSORS_MAX6639) += max6639.o
obj-$(CONFIG_SENSORS_MAX6642) += max6642.o
obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
obj-$(CONFIG_SENSORS_MAX6697) += max6697.o
obj-$(CONFIG_SENSORS_MAX31790) += max31790.o
@ -208,6 +206,7 @@ obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o
obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o
obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o
obj-$(CONFIG_SENSORS_SPARX5) += sparx5-temp.o
obj-$(CONFIG_SENSORS_SPD5118) += spd5118.o
obj-$(CONFIG_SENSORS_STTS751) += stts751.o
obj-$(CONFIG_SENSORS_SURFACE_FAN)+= surface_fan.o
obj-$(CONFIG_SENSORS_SY7636A) += sy7636a-hwmon.o

View File

@ -230,8 +230,6 @@ static void ad7418_init_client(struct i2c_client *client)
}
}
static const struct i2c_device_id ad7418_id[];
static int ad7418_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
@ -252,10 +250,7 @@ static int ad7418_probe(struct i2c_client *client)
mutex_init(&data->lock);
data->client = client;
if (dev->of_node)
data->type = (uintptr_t)of_device_get_match_data(dev);
else
data->type = i2c_match_id(ad7418_id, client)->driver_data;
data->type = (uintptr_t)i2c_get_match_data(client);
switch (data->type) {
case ad7416:

View File

@ -175,7 +175,7 @@ static ssize_t adc128_in_store(struct device *dev,
mutex_lock(&data->update_lock);
/* 10 mV LSB on limit registers */
regval = clamp_val(DIV_ROUND_CLOSEST(val, 10), 0, 255);
regval = DIV_ROUND_CLOSEST(clamp_val(val, 0, 2550), 10);
data->in[index][nr] = regval << 4;
reg = index == 1 ? ADC128_REG_IN_MIN(nr) : ADC128_REG_IN_MAX(nr);
i2c_smbus_write_byte_data(data->client, reg, regval);
@ -213,7 +213,7 @@ static ssize_t adc128_temp_store(struct device *dev,
return err;
mutex_lock(&data->update_lock);
regval = clamp_val(DIV_ROUND_CLOSEST(val, 1000), -128, 127);
regval = DIV_ROUND_CLOSEST(clamp_val(val, -128000, 127000), 1000);
data->temp[index] = regval << 1;
i2c_smbus_write_byte_data(data->client,
index == 1 ? ADC128_REG_TEMP_MAX

View File

@ -1,505 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* adm1021.c - Part of lm_sensors, Linux kernel modules for hardware
* monitoring
* Copyright (c) 1998, 1999 Frodo Looijaard <frodol@dds.nl> and
* Philip Edelbrock <phil@netroedge.com>
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/mutex.h>
/* Addresses to scan */
static const unsigned short normal_i2c[] = {
0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END };
enum chips {
adm1021, adm1023, max1617, max1617a, thmc10, lm84, gl523sm, mc1066 };
/* adm1021 constants specified below */
/* The adm1021 registers */
/* Read-only */
/* For nr in 0-1 */
#define ADM1021_REG_TEMP(nr) (nr)
#define ADM1021_REG_STATUS 0x02
/* 0x41 = AD, 0x49 = TI, 0x4D = Maxim, 0x23 = Genesys , 0x54 = Onsemi */
#define ADM1021_REG_MAN_ID 0xFE
/* ADM1021 = 0x0X, ADM1023 = 0x3X */
#define ADM1021_REG_DEV_ID 0xFF
/* These use different addresses for reading/writing */
#define ADM1021_REG_CONFIG_R 0x03
#define ADM1021_REG_CONFIG_W 0x09
#define ADM1021_REG_CONV_RATE_R 0x04
#define ADM1021_REG_CONV_RATE_W 0x0A
/* These are for the ADM1023's additional precision on the remote temp sensor */
#define ADM1023_REG_REM_TEMP_PREC 0x10
#define ADM1023_REG_REM_OFFSET 0x11
#define ADM1023_REG_REM_OFFSET_PREC 0x12
#define ADM1023_REG_REM_TOS_PREC 0x13
#define ADM1023_REG_REM_THYST_PREC 0x14
/* limits */
/* For nr in 0-1 */
#define ADM1021_REG_TOS_R(nr) (0x05 + 2 * (nr))
#define ADM1021_REG_TOS_W(nr) (0x0B + 2 * (nr))
#define ADM1021_REG_THYST_R(nr) (0x06 + 2 * (nr))
#define ADM1021_REG_THYST_W(nr) (0x0C + 2 * (nr))
/* write-only */
#define ADM1021_REG_ONESHOT 0x0F
/* Initial values */
/*
* Note: Even though I left the low and high limits named os and hyst,
* they don't quite work like a thermostat the way the LM75 does. I.e.,
* a lower temp than THYST actually triggers an alarm instead of
* clearing it. Weird, ey? --Phil
*/
/* Each client has this additional data */
struct adm1021_data {
struct i2c_client *client;
enum chips type;
const struct attribute_group *groups[3];
struct mutex update_lock;
bool valid; /* true if following fields are valid */
char low_power; /* !=0 if device in low power mode */
unsigned long last_updated; /* In jiffies */
int temp_max[2]; /* Register values */
int temp_min[2];
int temp[2];
u8 alarms;
/* Special values for ADM1023 only */
u8 remote_temp_offset;
u8 remote_temp_offset_prec;
};
/* (amalysh) read only mode, otherwise any limit's writing confuse BIOS */
static bool read_only;
static struct adm1021_data *adm1021_update_device(struct device *dev)
{
struct adm1021_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
mutex_lock(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
|| !data->valid) {
int i;
dev_dbg(dev, "Starting adm1021 update\n");
for (i = 0; i < 2; i++) {
data->temp[i] = 1000 *
(s8) i2c_smbus_read_byte_data(
client, ADM1021_REG_TEMP(i));
data->temp_max[i] = 1000 *
(s8) i2c_smbus_read_byte_data(
client, ADM1021_REG_TOS_R(i));
if (data->type != lm84) {
data->temp_min[i] = 1000 *
(s8) i2c_smbus_read_byte_data(client,
ADM1021_REG_THYST_R(i));
}
}
data->alarms = i2c_smbus_read_byte_data(client,
ADM1021_REG_STATUS) & 0x7c;
if (data->type == adm1023) {
/*
* The ADM1023 provides 3 extra bits of precision for
* the remote sensor in extra registers.
*/
data->temp[1] += 125 * (i2c_smbus_read_byte_data(
client, ADM1023_REG_REM_TEMP_PREC) >> 5);
data->temp_max[1] += 125 * (i2c_smbus_read_byte_data(
client, ADM1023_REG_REM_TOS_PREC) >> 5);
data->temp_min[1] += 125 * (i2c_smbus_read_byte_data(
client, ADM1023_REG_REM_THYST_PREC) >> 5);
data->remote_temp_offset =
i2c_smbus_read_byte_data(client,
ADM1023_REG_REM_OFFSET);
data->remote_temp_offset_prec =
i2c_smbus_read_byte_data(client,
ADM1023_REG_REM_OFFSET_PREC);
}
data->last_updated = jiffies;
data->valid = true;
}
mutex_unlock(&data->update_lock);
return data;
}
static ssize_t temp_show(struct device *dev, struct device_attribute *devattr,
char *buf)
{
int index = to_sensor_dev_attr(devattr)->index;
struct adm1021_data *data = adm1021_update_device(dev);
return sprintf(buf, "%d\n", data->temp[index]);
}
static ssize_t temp_max_show(struct device *dev,
struct device_attribute *devattr, char *buf)
{
int index = to_sensor_dev_attr(devattr)->index;
struct adm1021_data *data = adm1021_update_device(dev);
return sprintf(buf, "%d\n", data->temp_max[index]);
}
static ssize_t temp_min_show(struct device *dev,
struct device_attribute *devattr, char *buf)
{
int index = to_sensor_dev_attr(devattr)->index;
struct adm1021_data *data = adm1021_update_device(dev);
return sprintf(buf, "%d\n", data->temp_min[index]);
}
static ssize_t alarm_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
int index = to_sensor_dev_attr(attr)->index;
struct adm1021_data *data = adm1021_update_device(dev);
return sprintf(buf, "%u\n", (data->alarms >> index) & 1);
}
static ssize_t alarms_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct adm1021_data *data = adm1021_update_device(dev);
return sprintf(buf, "%u\n", data->alarms);
}
static ssize_t temp_max_store(struct device *dev,
struct device_attribute *devattr,
const char *buf, size_t count)
{
int index = to_sensor_dev_attr(devattr)->index;
struct adm1021_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
long temp;
int reg_val, err;
err = kstrtol(buf, 10, &temp);
if (err)
return err;
temp /= 1000;
mutex_lock(&data->update_lock);
reg_val = clamp_val(temp, -128, 127);
data->temp_max[index] = reg_val * 1000;
if (!read_only)
i2c_smbus_write_byte_data(client, ADM1021_REG_TOS_W(index),
reg_val);
mutex_unlock(&data->update_lock);
return count;
}
static ssize_t temp_min_store(struct device *dev,
struct device_attribute *devattr,
const char *buf, size_t count)
{
int index = to_sensor_dev_attr(devattr)->index;
struct adm1021_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
long temp;
int reg_val, err;
err = kstrtol(buf, 10, &temp);
if (err)
return err;
temp /= 1000;
mutex_lock(&data->update_lock);
reg_val = clamp_val(temp, -128, 127);
data->temp_min[index] = reg_val * 1000;
if (!read_only)
i2c_smbus_write_byte_data(client, ADM1021_REG_THYST_W(index),
reg_val);
mutex_unlock(&data->update_lock);
return count;
}
static ssize_t low_power_show(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct adm1021_data *data = adm1021_update_device(dev);
return sprintf(buf, "%d\n", data->low_power);
}
static ssize_t low_power_store(struct device *dev,
struct device_attribute *devattr,
const char *buf, size_t count)
{
struct adm1021_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
char low_power;
unsigned long val;
int err;
err = kstrtoul(buf, 10, &val);
if (err)
return err;
low_power = val != 0;
mutex_lock(&data->update_lock);
if (low_power != data->low_power) {
int config = i2c_smbus_read_byte_data(
client, ADM1021_REG_CONFIG_R);
data->low_power = low_power;
i2c_smbus_write_byte_data(client, ADM1021_REG_CONFIG_W,
(config & 0xBF) | (low_power << 6));
}
mutex_unlock(&data->update_lock);
return count;
}
static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, 0);
static SENSOR_DEVICE_ATTR_RW(temp1_max, temp_max, 0);
static SENSOR_DEVICE_ATTR_RW(temp1_min, temp_min, 0);
static SENSOR_DEVICE_ATTR_RO(temp2_input, temp, 1);
static SENSOR_DEVICE_ATTR_RW(temp2_max, temp_max, 1);
static SENSOR_DEVICE_ATTR_RW(temp2_min, temp_min, 1);
static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, alarm, 6);
static SENSOR_DEVICE_ATTR_RO(temp1_min_alarm, alarm, 5);
static SENSOR_DEVICE_ATTR_RO(temp2_max_alarm, alarm, 4);
static SENSOR_DEVICE_ATTR_RO(temp2_min_alarm, alarm, 3);
static SENSOR_DEVICE_ATTR_RO(temp2_fault, alarm, 2);
static DEVICE_ATTR_RO(alarms);
static DEVICE_ATTR_RW(low_power);
static struct attribute *adm1021_attributes[] = {
&sensor_dev_attr_temp1_max.dev_attr.attr,
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_temp2_max.dev_attr.attr,
&sensor_dev_attr_temp2_input.dev_attr.attr,
&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp2_fault.dev_attr.attr,
&dev_attr_alarms.attr,
&dev_attr_low_power.attr,
NULL
};
static const struct attribute_group adm1021_group = {
.attrs = adm1021_attributes,
};
static struct attribute *adm1021_min_attributes[] = {
&sensor_dev_attr_temp1_min.dev_attr.attr,
&sensor_dev_attr_temp2_min.dev_attr.attr,
&sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
&sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
NULL
};
static const struct attribute_group adm1021_min_group = {
.attrs = adm1021_min_attributes,
};
/* Return 0 if detection is successful, -ENODEV otherwise */
static int adm1021_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
const char *type_name;
int reg, conv_rate, status, config, man_id, dev_id;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
pr_debug("detect failed, smbus byte data not supported!\n");
return -ENODEV;
}
status = i2c_smbus_read_byte_data(client, ADM1021_REG_STATUS);
conv_rate = i2c_smbus_read_byte_data(client,
ADM1021_REG_CONV_RATE_R);
config = i2c_smbus_read_byte_data(client, ADM1021_REG_CONFIG_R);
/* Check unused bits */
if ((status & 0x03) || (config & 0x3F) || (conv_rate & 0xF8)) {
pr_debug("detect failed, chip not detected!\n");
return -ENODEV;
}
/* Determine the chip type. */
man_id = i2c_smbus_read_byte_data(client, ADM1021_REG_MAN_ID);
dev_id = i2c_smbus_read_byte_data(client, ADM1021_REG_DEV_ID);
if (man_id < 0 || dev_id < 0)
return -ENODEV;
if (man_id == 0x4d && dev_id == 0x01) {
/*
* dev_id 0x01 matches MAX6680, MAX6695, MAX6696, and possibly
* others. Read register which is unsupported on MAX1617 but
* exists on all those chips and compare with the dev_id
* register. If it matches, it may be a MAX1617A.
*/
reg = i2c_smbus_read_byte_data(client,
ADM1023_REG_REM_TEMP_PREC);
if (reg != dev_id)
return -ENODEV;
type_name = "max1617a";
} else if (man_id == 0x41) {
if ((dev_id & 0xF0) == 0x30)
type_name = "adm1023";
else if ((dev_id & 0xF0) == 0x00)
type_name = "adm1021";
else
return -ENODEV;
} else if (man_id == 0x49)
type_name = "thmc10";
else if (man_id == 0x23)
type_name = "gl523sm";
else if (man_id == 0x54)
type_name = "mc1066";
else {
int lte, rte, lhi, rhi, llo, rlo;
/* extra checks for LM84 and MAX1617 to avoid misdetections */
llo = i2c_smbus_read_byte_data(client, ADM1021_REG_THYST_R(0));
rlo = i2c_smbus_read_byte_data(client, ADM1021_REG_THYST_R(1));
/* fail if any of the additional register reads failed */
if (llo < 0 || rlo < 0)
return -ENODEV;
lte = i2c_smbus_read_byte_data(client, ADM1021_REG_TEMP(0));
rte = i2c_smbus_read_byte_data(client, ADM1021_REG_TEMP(1));
lhi = i2c_smbus_read_byte_data(client, ADM1021_REG_TOS_R(0));
rhi = i2c_smbus_read_byte_data(client, ADM1021_REG_TOS_R(1));
/*
* Fail for negative temperatures and negative high limits.
* This check also catches read errors on the tested registers.
*/
if ((s8)lte < 0 || (s8)rte < 0 || (s8)lhi < 0 || (s8)rhi < 0)
return -ENODEV;
/* fail if all registers hold the same value */
if (lte == rte && lte == lhi && lte == rhi && lte == llo
&& lte == rlo)
return -ENODEV;
/*
* LM84 Mfr ID is in a different place,
* and it has more unused bits. Registers at 0xfe and 0xff
* are undefined and return the most recently read value,
* here the value of the configuration register.
*/
if (conv_rate == 0x00
&& man_id == config && dev_id == config
&& (config & 0x7F) == 0x00
&& (status & 0xAB) == 0x00) {
type_name = "lm84";
} else {
if ((config & 0x3f) || (status & 0x03))
return -ENODEV;
/* fail if low limits are larger than high limits */
if ((s8)llo > lhi || (s8)rlo > rhi)
return -ENODEV;
type_name = "max1617";
}
}
pr_debug("Detected chip %s at adapter %d, address 0x%02x.\n",
type_name, i2c_adapter_id(adapter), client->addr);
strscpy(info->type, type_name, I2C_NAME_SIZE);
return 0;
}
static void adm1021_init_client(struct i2c_client *client)
{
/* Enable ADC and disable suspend mode */
i2c_smbus_write_byte_data(client, ADM1021_REG_CONFIG_W,
i2c_smbus_read_byte_data(client, ADM1021_REG_CONFIG_R) & 0xBF);
/* Set Conversion rate to 1/sec (this can be tinkered with) */
i2c_smbus_write_byte_data(client, ADM1021_REG_CONV_RATE_W, 0x04);
}
static const struct i2c_device_id adm1021_id[];
static int adm1021_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct adm1021_data *data;
struct device *hwmon_dev;
data = devm_kzalloc(dev, sizeof(struct adm1021_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->client = client;
data->type = i2c_match_id(adm1021_id, client)->driver_data;
mutex_init(&data->update_lock);
/* Initialize the ADM1021 chip */
if (data->type != lm84 && !read_only)
adm1021_init_client(client);
data->groups[0] = &adm1021_group;
if (data->type != lm84)
data->groups[1] = &adm1021_min_group;
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
data, data->groups);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
static const struct i2c_device_id adm1021_id[] = {
{ "adm1021", adm1021 },
{ "adm1023", adm1023 },
{ "max1617", max1617 },
{ "max1617a", max1617a },
{ "thmc10", thmc10 },
{ "lm84", lm84 },
{ "gl523sm", gl523sm },
{ "mc1066", mc1066 },
{ }
};
MODULE_DEVICE_TABLE(i2c, adm1021_id);
static struct i2c_driver adm1021_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "adm1021",
},
.probe = adm1021_probe,
.id_table = adm1021_id,
.detect = adm1021_detect,
.address_list = normal_i2c,
};
module_i2c_driver(adm1021_driver);
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and "
"Philip Edelbrock <phil@netroedge.com>");
MODULE_DESCRIPTION("adm1021 driver");
MODULE_LICENSE("GPL");
module_param(read_only, bool, 0);
MODULE_PARM_DESC(read_only, "Don't set any values, read only mode");

View File

@ -1021,8 +1021,6 @@ static void adm1031_init_client(struct i2c_client *client)
data->update_interval = update_intervals[i];
}
static const struct i2c_device_id adm1031_id[];
static int adm1031_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
@ -1035,7 +1033,7 @@ static int adm1031_probe(struct i2c_client *client)
i2c_set_clientdata(client, data);
data->client = client;
data->chip_type = i2c_match_id(adm1031_id, client)->driver_data;
data->chip_type = (uintptr_t)i2c_get_match_data(client);
mutex_init(&data->update_lock);
if (data->chip_type == adm1030)

View File

@ -99,8 +99,6 @@ static const struct regmap_config ads2830_regmap_config = {
.val_bits = 8,
};
static const struct i2c_device_id ads7828_device_ids[];
static int ads7828_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
@ -138,10 +136,7 @@ static int ads7828_probe(struct i2c_client *client)
}
}
if (client->dev.of_node)
chip = (uintptr_t)of_device_get_match_data(&client->dev);
else
chip = i2c_match_id(ads7828_device_ids, client)->driver_data;
chip = (uintptr_t)i2c_get_match_data(client);
/* Bound Vref with min/max values */
vref_mv = clamp_val(vref_mv, ADS7828_EXT_VREF_MV_MIN,

View File

@ -1676,7 +1676,6 @@ static int adt7475_probe(struct i2c_client *client)
struct device *hwmon_dev;
int i, ret = 0, revision, group_num = 0;
u8 config3;
const struct i2c_device_id *id = i2c_match_id(adt7475_id, client);
data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
if (data == NULL)
@ -1686,10 +1685,7 @@ static int adt7475_probe(struct i2c_client *client)
data->client = client;
i2c_set_clientdata(client, data);
if (client->dev.of_node)
chip = (uintptr_t)of_device_get_match_data(&client->dev);
else
chip = id->driver_data;
chip = (uintptr_t)i2c_get_match_data(client);
/* Initialize device-specific values */
switch (chip) {
@ -1717,7 +1713,7 @@ static int adt7475_probe(struct i2c_client *client)
if (!(config3 & CONFIG3_SMBALERT))
data->has_pwm2 = 1;
/* Meaning of this bit is inverted for the ADT7473-1 */
if (id->driver_data == adt7473 && revision >= 1)
if (chip == adt7473 && revision >= 1)
data->has_pwm2 = !data->has_pwm2;
data->config4 = adt7475_read(REG_CONFIG4);
@ -1730,12 +1726,12 @@ static int adt7475_probe(struct i2c_client *client)
* because 2 different pins (TACH4 and +2.5 Vin) can be used for
* this function
*/
if (id->driver_data == adt7490) {
if (chip == adt7490) {
if ((data->config4 & CONFIG4_PINFUNC) == 0x1 &&
!(config3 & CONFIG3_THERM))
data->has_fan4 = 1;
}
if (id->driver_data == adt7476 || id->driver_data == adt7490) {
if (chip == adt7476 || chip == adt7490) {
if (!(config3 & CONFIG3_THERM) ||
(data->config4 & CONFIG4_PINFUNC) == 0x1)
data->has_voltage |= (1 << 0); /* in0 */
@ -1745,7 +1741,7 @@ static int adt7475_probe(struct i2c_client *client)
* On the ADT7476, the +12V input pin may instead be used as VID5,
* and VID pins may alternatively be used as GPIO
*/
if (id->driver_data == adt7476) {
if (chip == adt7476) {
u8 vid = adt7475_read(REG_VID);
if (!(vid & VID_VIDSEL))
data->has_voltage |= (1 << 4); /* in4 */
@ -1829,7 +1825,7 @@ static int adt7475_probe(struct i2c_client *client)
}
dev_info(&client->dev, "%s device, revision %d\n",
names[id->driver_data], revision);
names[chip], revision);
if ((data->has_voltage & 0x11) || data->has_fan4 || data->has_pwm2)
dev_info(&client->dev, "Optional features:%s%s%s%s%s\n",
(data->has_voltage & (1 << 0)) ? " in0" : "",
@ -1900,7 +1896,7 @@ static void adt7475_read_pwm(struct i2c_client *client, int index)
data->pwm[CONTROL][index] &= ~0xE0;
data->pwm[CONTROL][index] |= (7 << 5);
i2c_smbus_write_byte_data(client, PWM_CONFIG_REG(index),
i2c_smbus_write_byte_data(client, PWM_REG(index),
data->pwm[INPUT][index]);
i2c_smbus_write_byte_data(client, PWM_CONFIG_REG(index),

View File

@ -331,8 +331,7 @@ static const struct hwmon_chip_info aht10_chip_info = {
static int aht10_probe(struct i2c_client *client)
{
const struct i2c_device_id *id = i2c_match_id(aht10_id, client);
enum aht10_variant variant = id->driver_data;
enum aht10_variant variant = (uintptr_t)i2c_get_match_data(client);
struct device *device = &client->dev;
struct device *hwmon_dev;
struct aht10_data *data;

File diff suppressed because it is too large Load Diff

View File

@ -322,6 +322,14 @@ static const struct ec_board_info board_info_pro_art_x570_creator_wifi = {
.family = family_amd_500_series,
};
static const struct ec_board_info board_info_pro_art_x670E_creator_wifi = {
.sensors = SENSOR_TEMP_CPU | SENSOR_TEMP_CPU_PACKAGE |
SENSOR_TEMP_MB | SENSOR_TEMP_VRM |
SENSOR_TEMP_T_SENSOR,
.mutex_path = ACPI_GLOBAL_LOCK_PSEUDO_PATH,
.family = family_amd_600_series,
};
static const struct ec_board_info board_info_pro_art_b550_creator = {
.sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
SENSOR_TEMP_T_SENSOR |
@ -486,6 +494,8 @@ static const struct dmi_system_id dmi_table[] = {
&board_info_prime_x570_pro),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ProArt X570-CREATOR WIFI",
&board_info_pro_art_x570_creator_wifi),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ProArt X670E-CREATOR WIFI",
&board_info_pro_art_x670E_creator_wifi),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ProArt B550-CREATOR",
&board_info_pro_art_b550_creator),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("Pro WS X570-ACE",

View File

@ -1389,4 +1389,5 @@ static void __exit atk0110_exit(void)
module_init(atk0110_init);
module_exit(atk0110_exit);
MODULE_DESCRIPTION("ASUS ATK0110 driver");
MODULE_LICENSE("GPL");

View File

@ -10,11 +10,13 @@
#include <linux/bitops.h>
#include <linux/completion.h>
#include <linux/debugfs.h>
#include <linux/hid.h>
#include <linux/hwmon.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/types.h>
@ -28,6 +30,8 @@
#define LABEL_LENGTH 11
#define REQ_TIMEOUT 300
#define CTL_GET_FW_VER 0x02 /* returns the firmware version in bytes 1-3 */
#define CTL_GET_BL_VER 0x06 /* returns the bootloader version in bytes 1-2 */
#define CTL_GET_TMP_CNCT 0x10 /*
* returns in bytes 1-4 for each temp sensor:
* 0 not connected
@ -78,6 +82,7 @@
struct ccp_device {
struct hid_device *hdev;
struct device *hwmon_dev;
struct dentry *debugfs;
/* For reinitializing the completion below */
spinlock_t wait_input_report_lock;
struct completion wait_input_report;
@ -88,6 +93,8 @@ struct ccp_device {
DECLARE_BITMAP(temp_cnct, NUM_TEMP_SENSORS);
DECLARE_BITMAP(fan_cnct, NUM_FANS);
char fan_label[6][LABEL_LENGTH];
u8 firmware_ver[3];
u8 bootloader_ver[2];
};
/* converts response error in buffer to errno */
@ -496,6 +503,83 @@ static int get_temp_cnct(struct ccp_device *ccp)
return 0;
}
/* read firmware version */
static int get_fw_version(struct ccp_device *ccp)
{
int ret;
ret = send_usb_cmd(ccp, CTL_GET_FW_VER, 0, 0, 0);
if (ret) {
hid_notice(ccp->hdev, "Failed to read firmware version.\n");
return ret;
}
ccp->firmware_ver[0] = ccp->buffer[1];
ccp->firmware_ver[1] = ccp->buffer[2];
ccp->firmware_ver[2] = ccp->buffer[3];
return 0;
}
/* read bootloader version */
static int get_bl_version(struct ccp_device *ccp)
{
int ret;
ret = send_usb_cmd(ccp, CTL_GET_BL_VER, 0, 0, 0);
if (ret) {
hid_notice(ccp->hdev, "Failed to read bootloader version.\n");
return ret;
}
ccp->bootloader_ver[0] = ccp->buffer[1];
ccp->bootloader_ver[1] = ccp->buffer[2];
return 0;
}
static int firmware_show(struct seq_file *seqf, void *unused)
{
struct ccp_device *ccp = seqf->private;
seq_printf(seqf, "%d.%d.%d\n",
ccp->firmware_ver[0],
ccp->firmware_ver[1],
ccp->firmware_ver[2]);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(firmware);
static int bootloader_show(struct seq_file *seqf, void *unused)
{
struct ccp_device *ccp = seqf->private;
seq_printf(seqf, "%d.%d\n",
ccp->bootloader_ver[0],
ccp->bootloader_ver[1]);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(bootloader);
static void ccp_debugfs_init(struct ccp_device *ccp)
{
char name[32];
int ret;
scnprintf(name, sizeof(name), "corsaircpro-%s", dev_name(&ccp->hdev->dev));
ccp->debugfs = debugfs_create_dir(name, NULL);
ret = get_fw_version(ccp);
if (!ret)
debugfs_create_file("firmware_version", 0444,
ccp->debugfs, ccp, &firmware_fops);
ret = get_bl_version(ccp);
if (!ret)
debugfs_create_file("bootloader_version", 0444,
ccp->debugfs, ccp, &bootloader_fops);
}
static int ccp_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
struct ccp_device *ccp;
@ -542,6 +626,9 @@ static int ccp_probe(struct hid_device *hdev, const struct hid_device_id *id)
ret = get_fan_cnct(ccp);
if (ret)
goto out_hw_close;
ccp_debugfs_init(ccp);
ccp->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "corsaircpro",
ccp, &ccp_chip_info, NULL);
if (IS_ERR(ccp->hwmon_dev)) {
@ -562,6 +649,7 @@ static void ccp_remove(struct hid_device *hdev)
{
struct ccp_device *ccp = hid_get_drvdata(hdev);
debugfs_remove_recursive(ccp->debugfs);
hwmon_device_unregister(ccp->hwmon_dev);
hid_hw_close(hdev);
hid_hw_stop(hdev);
@ -582,6 +670,7 @@ static struct hid_driver ccp_driver = {
};
MODULE_DEVICE_TABLE(hid, ccp_devices);
MODULE_DESCRIPTION("Corsair Commander Pro controller driver");
MODULE_LICENSE("GPL");
static int __init ccp_init(void)

View File

@ -875,15 +875,16 @@ static const struct hid_device_id corsairpsu_idtable[] = {
{ HID_USB_DEVICE(0x1b1c, 0x1c04) }, /* Corsair HX650i */
{ HID_USB_DEVICE(0x1b1c, 0x1c05) }, /* Corsair HX750i */
{ HID_USB_DEVICE(0x1b1c, 0x1c06) }, /* Corsair HX850i */
{ HID_USB_DEVICE(0x1b1c, 0x1c07) }, /* Corsair HX1000i Series 2022 */
{ HID_USB_DEVICE(0x1b1c, 0x1c08) }, /* Corsair HX1200i */
{ HID_USB_DEVICE(0x1b1c, 0x1c07) }, /* Corsair HX1000i Legacy */
{ HID_USB_DEVICE(0x1b1c, 0x1c08) }, /* Corsair HX1200i Legacy */
{ HID_USB_DEVICE(0x1b1c, 0x1c09) }, /* Corsair RM550i */
{ HID_USB_DEVICE(0x1b1c, 0x1c0a) }, /* Corsair RM650i */
{ HID_USB_DEVICE(0x1b1c, 0x1c0b) }, /* Corsair RM750i */
{ HID_USB_DEVICE(0x1b1c, 0x1c0c) }, /* Corsair RM850i */
{ HID_USB_DEVICE(0x1b1c, 0x1c0d) }, /* Corsair RM1000i */
{ HID_USB_DEVICE(0x1b1c, 0x1c1e) }, /* Corsair HX1000i Series 2023 */
{ HID_USB_DEVICE(0x1b1c, 0x1c1f) }, /* Corsair HX1500i Series 2022 and 2023 */
{ HID_USB_DEVICE(0x1b1c, 0x1c1f) }, /* Corsair HX1500i Legacy and Series 2023 */
{ HID_USB_DEVICE(0x1b1c, 0x1c23) }, /* Corsair HX1200i Series 2023 */
{ },
};
MODULE_DEVICE_TABLE(hid, corsairpsu_idtable);

View File

@ -1263,6 +1263,13 @@ static const struct dmi_system_id i8k_dmi_table[] __initconst = {
DMI_MATCH(DMI_PRODUCT_NAME, "MP061"),
},
},
{
.ident = "Dell OptiPlex 7060",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "OptiPlex 7060"),
},
},
{
.ident = "Dell Precision",
.matches = {

View File

@ -2461,8 +2461,6 @@ static int dme1737_i2c_detect(struct i2c_client *client,
return 0;
}
static const struct i2c_device_id dme1737_id[];
static int dme1737_i2c_probe(struct i2c_client *client)
{
struct dme1737_data *data;
@ -2474,7 +2472,7 @@ static int dme1737_i2c_probe(struct i2c_client *client)
return -ENOMEM;
i2c_set_clientdata(client, data);
data->type = i2c_match_id(dme1737_id, client)->driver_data;
data->type = (uintptr_t)i2c_get_match_data(client);
data->client = client;
data->name = client->name;
mutex_init(&data->update_lock);

View File

@ -342,8 +342,6 @@ static const struct attribute_group ds1621_group = {
};
__ATTRIBUTE_GROUPS(ds1621);
static const struct i2c_device_id ds1621_id[];
static int ds1621_probe(struct i2c_client *client)
{
struct ds1621_data *data;
@ -356,7 +354,7 @@ static int ds1621_probe(struct i2c_client *client)
mutex_init(&data->update_lock);
data->kind = i2c_match_id(ds1621_id, client)->driver_data;
data->kind = (uintptr_t)i2c_get_match_data(client);
data->client = client;
/* Initialize the DS1621 chip */

View File

@ -111,31 +111,6 @@ struct f75375_data {
s8 temp_max_hyst[2];
};
static int f75375_detect(struct i2c_client *client,
struct i2c_board_info *info);
static int f75375_probe(struct i2c_client *client);
static void f75375_remove(struct i2c_client *client);
static const struct i2c_device_id f75375_id[] = {
{ "f75373", f75373 },
{ "f75375", f75375 },
{ "f75387", f75387 },
{ }
};
MODULE_DEVICE_TABLE(i2c, f75375_id);
static struct i2c_driver f75375_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "f75375",
},
.probe = f75375_probe,
.remove = f75375_remove,
.id_table = f75375_id,
.detect = f75375_detect,
.address_list = normal_i2c,
};
static inline int f75375_read8(struct i2c_client *client, u8 reg)
{
return i2c_smbus_read_byte_data(client, reg);
@ -830,7 +805,7 @@ static int f75375_probe(struct i2c_client *client)
i2c_set_clientdata(client, data);
mutex_init(&data->update_lock);
data->kind = i2c_match_id(f75375_id, client)->driver_data;
data->kind = (uintptr_t)i2c_get_match_data(client);
err = sysfs_create_group(&client->dev.kobj, &f75375_group);
if (err)
@ -901,6 +876,25 @@ static int f75375_detect(struct i2c_client *client,
return 0;
}
static const struct i2c_device_id f75375_id[] = {
{ "f75373", f75373 },
{ "f75375", f75375 },
{ "f75387", f75387 },
{ }
};
MODULE_DEVICE_TABLE(i2c, f75375_id);
static struct i2c_driver f75375_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "f75375",
},
.probe = f75375_probe,
.remove = f75375_remove,
.id_table = f75375_id,
.detect = f75375_detect,
.address_list = normal_i2c,
};
module_i2c_driver(f75375_driver);
MODULE_AUTHOR("Riku Voipio");

View File

@ -1087,7 +1087,7 @@ static int fschmd_probe(struct i2c_client *client)
"Heracles", "Heimdall", "Hades", "Syleus" };
static const int watchdog_minors[] = { WATCHDOG_MINOR, 212, 213, 214, 215 };
int i, err;
enum chips kind = i2c_match_id(fschmd_id, client)->driver_data;
enum chips kind = (uintptr_t)i2c_get_match_data(client);
data = kzalloc(sizeof(struct fschmd_data), GFP_KERNEL);
if (!data)

View File

@ -44,6 +44,7 @@
#define DRVNAME "g762"
static const struct i2c_device_id g762_id[] = {
{ "g761" },
{ "g762" },
{ "g763" },
{ }
@ -69,6 +70,7 @@ enum g762_regs {
#define G762_REG_FAN_CMD1_PWM_POLARITY 0x02 /* PWM polarity */
#define G762_REG_FAN_CMD1_PULSE_PER_REV 0x01 /* pulse per fan revolution */
#define G761_REG_FAN_CMD2_FAN_CLOCK 0x20 /* choose internal clock*/
#define G762_REG_FAN_CMD2_GEAR_MODE_1 0x08 /* fan gear mode */
#define G762_REG_FAN_CMD2_GEAR_MODE_0 0x04
#define G762_REG_FAN_CMD2_FAN_STARTV_1 0x02 /* fan startup voltage */
@ -115,6 +117,7 @@ enum g762_regs {
struct g762_data {
struct i2c_client *client;
bool internal_clock;
struct clk *clk;
/* update mutex */
@ -566,6 +569,7 @@ static int do_set_fan_startv(struct device *dev, unsigned long val)
#ifdef CONFIG_OF
static const struct of_device_id g762_dt_match[] = {
{ .compatible = "gmt,g761" },
{ .compatible = "gmt,g762" },
{ .compatible = "gmt,g763" },
{ },
@ -597,6 +601,21 @@ static int g762_of_clock_enable(struct i2c_client *client)
if (!client->dev.of_node)
return 0;
data = i2c_get_clientdata(client);
/*
* Skip CLK detection and handling if we use internal clock.
* This is only valid for g761.
*/
data->internal_clock = of_device_is_compatible(client->dev.of_node,
"gmt,g761") &&
!of_property_present(client->dev.of_node,
"clocks");
if (data->internal_clock) {
do_set_clk_freq(&client->dev, 32768);
return 0;
}
clk = of_clk_get(client->dev.of_node, 0);
if (IS_ERR(clk)) {
dev_err(&client->dev, "failed to get clock\n");
@ -616,7 +635,6 @@ static int g762_of_clock_enable(struct i2c_client *client)
goto clk_unprep;
}
data = i2c_get_clientdata(client);
data->clk = clk;
ret = devm_add_action(&client->dev, g762_of_clock_disable, data);
@ -1025,16 +1043,26 @@ ATTRIBUTE_GROUPS(g762);
static inline int g762_fan_init(struct device *dev)
{
struct g762_data *data = g762_update_client(dev);
int ret;
if (IS_ERR(data))
return PTR_ERR(data);
/* internal_clock can only be set with compatible g761 */
if (data->internal_clock)
data->fan_cmd2 |= G761_REG_FAN_CMD2_FAN_CLOCK;
data->fan_cmd1 |= G762_REG_FAN_CMD1_DET_FAN_FAIL;
data->fan_cmd1 |= G762_REG_FAN_CMD1_DET_FAN_OOC;
data->valid = false;
return i2c_smbus_write_byte_data(data->client, G762_REG_FAN_CMD1,
data->fan_cmd1);
ret = i2c_smbus_write_byte_data(data->client, G762_REG_FAN_CMD1,
data->fan_cmd1);
if (ret)
return ret;
return i2c_smbus_write_byte_data(data->client, G762_REG_FAN_CMD2,
data->fan_cmd2);
}
static int g762_probe(struct i2c_client *client)
@ -1056,15 +1084,16 @@ static int g762_probe(struct i2c_client *client)
data->client = client;
mutex_init(&data->update_lock);
/* Get configuration via DT ... */
ret = g762_of_clock_enable(client);
if (ret)
return ret;
/* Enable fan failure detection and fan out of control protection */
ret = g762_fan_init(dev);
if (ret)
return ret;
/* Get configuration via DT ... */
ret = g762_of_clock_enable(client);
if (ret)
return ret;
ret = g762_of_prop_import(client);
if (ret)
return ret;

View File

@ -39,7 +39,7 @@ struct gsc_hwmon_data {
struct hwmon_chip_info chip;
};
static struct regmap_bus gsc_hwmon_regmap_bus = {
static const struct regmap_bus gsc_hwmon_regmap_bus = {
.reg_read = gsc_read,
.reg_write = gsc_write,
};
@ -249,7 +249,6 @@ gsc_hwmon_get_devtree_pdata(struct device *dev)
{
struct gsc_hwmon_platform_data *pdata;
struct gsc_hwmon_channel *ch;
struct fwnode_handle *child;
struct device_node *fan;
int nchannels;
@ -276,25 +275,21 @@ gsc_hwmon_get_devtree_pdata(struct device *dev)
ch = pdata->channels;
/* allocate structures for channels and count instances of each type */
device_for_each_child_node(dev, child) {
device_for_each_child_node_scoped(dev, child) {
if (fwnode_property_read_string(child, "label", &ch->name)) {
dev_err(dev, "channel without label\n");
fwnode_handle_put(child);
return ERR_PTR(-EINVAL);
}
if (fwnode_property_read_u32(child, "reg", &ch->reg)) {
dev_err(dev, "channel without reg\n");
fwnode_handle_put(child);
return ERR_PTR(-EINVAL);
}
if (fwnode_property_read_u32(child, "gw,mode", &ch->mode)) {
dev_err(dev, "channel without mode\n");
fwnode_handle_put(child);
return ERR_PTR(-EINVAL);
}
if (ch->mode > mode_max) {
dev_err(dev, "invalid channel mode\n");
fwnode_handle_put(child);
return ERR_PTR(-EINVAL);
}

View File

@ -14,6 +14,7 @@
#include <linux/err.h>
#include <linux/gfp.h>
#include <linux/hwmon.h>
#include <linux/i2c.h>
#include <linux/idr.h>
#include <linux/kstrtox.h>
#include <linux/list.h>
@ -136,7 +137,7 @@ static void hwmon_dev_release(struct device *dev)
kfree(hwdev);
}
static struct class hwmon_class = {
static const struct class hwmon_class = {
.name = "hwmon",
.dev_groups = hwmon_dev_attr_groups,
.dev_release = hwmon_dev_release,
@ -309,6 +310,114 @@ static int hwmon_attr_base(enum hwmon_sensor_types type)
return 1;
}
#if IS_REACHABLE(CONFIG_I2C)
/*
* PEC support
*
* The 'pec' attribute is attached to I2C client devices. It is only provided
* if the i2c controller supports PEC.
*
* The mutex ensures that PEC configuration between i2c device and the hardware
* is consistent. Use a single mutex because attribute writes are supposed to be
* rare, and maintaining a separate mutex for each hardware monitoring device
* would add substantial complexity to the driver for little if any gain.
*
* The hardware monitoring device is identified as child of the i2c client
* device. This assumes that only a single hardware monitoring device is
* attached to an i2c client device.
*/
static DEFINE_MUTEX(hwmon_pec_mutex);
static int hwmon_match_device(struct device *dev, void *data)
{
return dev->class == &hwmon_class;
}
static ssize_t pec_show(struct device *dev, struct device_attribute *dummy,
char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
return sysfs_emit(buf, "%d\n", !!(client->flags & I2C_CLIENT_PEC));
}
static ssize_t pec_store(struct device *dev, struct device_attribute *devattr,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct hwmon_device *hwdev;
struct device *hdev;
bool val;
int err;
err = kstrtobool(buf, &val);
if (err < 0)
return err;
hdev = device_find_child(dev, NULL, hwmon_match_device);
if (!hdev)
return -ENODEV;
mutex_lock(&hwmon_pec_mutex);
/*
* If there is no write function, we assume that chip specific
* handling is not required.
*/
hwdev = to_hwmon_device(hdev);
if (hwdev->chip->ops->write) {
err = hwdev->chip->ops->write(hdev, hwmon_chip, hwmon_chip_pec, 0, val);
if (err && err != -EOPNOTSUPP)
goto unlock;
}
if (!val)
client->flags &= ~I2C_CLIENT_PEC;
else
client->flags |= I2C_CLIENT_PEC;
err = count;
unlock:
mutex_unlock(&hwmon_pec_mutex);
put_device(hdev);
return err;
}
static DEVICE_ATTR_RW(pec);
static void hwmon_remove_pec(void *dev)
{
device_remove_file(dev, &dev_attr_pec);
}
static int hwmon_pec_register(struct device *hdev)
{
struct i2c_client *client = i2c_verify_client(hdev->parent);
int err;
if (!client)
return -EINVAL;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_PEC))
return 0;
err = device_create_file(&client->dev, &dev_attr_pec);
if (err)
return err;
return devm_add_action_or_reset(hdev, hwmon_remove_pec, &client->dev);
}
#else /* CONFIG_I2C */
static int hwmon_pec_register(struct device *hdev)
{
return -EINVAL;
}
#endif /* CONFIG_I2C */
/* sysfs attribute management */
static ssize_t hwmon_attr_show(struct device *dev,
@ -397,10 +506,6 @@ static struct attribute *hwmon_genattr(const void *drvdata,
const char *name;
bool is_string = is_string_attr(type, attr);
/* The attribute is invisible if there is no template string */
if (!template)
return ERR_PTR(-ENOENT);
mode = ops->is_visible(drvdata, type, attr, index);
if (!mode)
return ERR_PTR(-ENOENT);
@ -712,8 +817,8 @@ static int hwmon_genattrs(const void *drvdata,
attr = __ffs(attr_mask);
attr_mask &= ~BIT(attr);
if (attr >= template_size)
return -EINVAL;
if (attr >= template_size || !templates[attr])
continue; /* attribute is invisible */
a = hwmon_genattr(drvdata, info->type, attr, i,
templates[attr], ops);
if (IS_ERR(a)) {
@ -849,16 +954,26 @@ __hwmon_device_register(struct device *dev, const char *name, void *drvdata,
INIT_LIST_HEAD(&hwdev->tzdata);
if (hdev->of_node && chip && chip->ops->read &&
chip->info[0]->type == hwmon_chip &&
(chip->info[0]->config[0] & HWMON_C_REGISTER_TZ)) {
err = hwmon_thermal_register_sensors(hdev);
if (err) {
device_unregister(hdev);
/*
* Don't worry about hwdev; hwmon_dev_release(), called
* from device_unregister(), will free it.
*/
goto ida_remove;
chip->info[0]->type == hwmon_chip) {
u32 config = chip->info[0]->config[0];
if (config & HWMON_C_REGISTER_TZ) {
err = hwmon_thermal_register_sensors(hdev);
if (err) {
device_unregister(hdev);
/*
* Don't worry about hwdev; hwmon_dev_release(),
* called from device_unregister(), will free it.
*/
goto ida_remove;
}
}
if (config & HWMON_C_PEC) {
err = hwmon_pec_register(hdev);
if (err) {
device_unregister(hdev);
goto ida_remove;
}
}
}

View File

@ -49,16 +49,17 @@ static ssize_t iio_hwmon_read_val(struct device *dev,
struct iio_channel *chan = &state->channels[sattr->index];
enum iio_chan_type type;
ret = iio_read_channel_processed(chan, &result);
if (ret < 0)
return ret;
ret = iio_get_channel_type(chan, &type);
if (ret < 0)
return ret;
if (type == IIO_POWER)
result *= 1000; /* mili-Watts to micro-Watts conversion */
/* mili-Watts to micro-Watts conversion */
ret = iio_read_channel_processed_scale(chan, &result, 1000);
else
ret = iio_read_channel_processed(chan, &result);
if (ret < 0)
return ret;
return sprintf(buf, "%d\n", result);
}

View File

@ -96,7 +96,7 @@
#define INA238_BUS_VOLTAGE_LSB 3125 /* 3.125 mV/lsb */
#define INA238_DIE_TEMP_LSB 125 /* 125 mC/lsb */
static struct regmap_config ina238_regmap_config = {
static const struct regmap_config ina238_regmap_config = {
.max_register = INA238_REGISTERS,
.reg_bits = 8,
.val_bits = 16,

View File

@ -73,6 +73,11 @@
#define INA226_READ_AVG(reg) (((reg) & INA226_AVG_RD_MASK) >> 9)
#define INA226_SHIFT_AVG(val) ((val) << 9)
#define INA226_ALERT_POLARITY_MASK 0x0002
#define INA226_SHIFT_ALERT_POLARITY(val) ((val) << 1)
#define INA226_ALERT_POL_LOW 0
#define INA226_ALERT_POL_HIGH 1
/* bit number of alert functions in Mask/Enable Register */
#define INA226_SHUNT_OVER_VOLTAGE_BIT 15
#define INA226_SHUNT_UNDER_VOLTAGE_BIT 14
@ -178,6 +183,14 @@ static u16 ina226_interval_to_reg(int interval)
return INA226_SHIFT_AVG(avg_bits);
}
static int ina2xx_set_alert_polarity(struct ina2xx_data *data,
unsigned long val)
{
return regmap_update_bits(data->regmap, INA226_MASK_ENABLE,
INA226_ALERT_POLARITY_MASK,
INA226_SHIFT_ALERT_POLARITY(val));
}
/*
* Calibration register is set to the best value, which eliminates
* truncation errors on calculating current register in hardware.
@ -612,8 +625,6 @@ static const struct attribute_group ina226_group = {
.attrs = ina226_attrs,
};
static const struct i2c_device_id ina2xx_id[];
static int ina2xx_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
@ -623,10 +634,7 @@ static int ina2xx_probe(struct i2c_client *client)
int ret, group = 0;
enum ina2xx_ids chip;
if (client->dev.of_node)
chip = (uintptr_t)of_device_get_match_data(&client->dev);
else
chip = i2c_match_id(ina2xx_id, client)->driver_data;
chip = (uintptr_t)i2c_get_match_data(client);
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
@ -659,6 +667,25 @@ static int ina2xx_probe(struct i2c_client *client)
if (ret)
return dev_err_probe(dev, ret, "failed to enable vs regulator\n");
if (chip == ina226) {
if (of_property_read_bool(dev->of_node, "ti,alert-polarity-active-high")) {
ret = ina2xx_set_alert_polarity(data,
INA226_ALERT_POL_HIGH);
if (ret < 0) {
return dev_err_probe(dev, ret,
"failed to set alert polarity active high\n");
}
} else {
/* Set default value i.e active low */
ret = ina2xx_set_alert_polarity(data,
INA226_ALERT_POL_LOW);
if (ret < 0) {
return dev_err_probe(dev, ret,
"failed to set alert polarity active low\n");
}
}
}
ret = ina2xx_init(data);
if (ret < 0) {
dev_err(dev, "error configuring the device: %d\n", ret);

View File

@ -79,20 +79,9 @@ static const unsigned short normal_i2c[] = {
#define AT30TS00_DEVID 0x8201
#define AT30TS00_DEVID_MASK 0xffff
#define AT30TSE004_DEVID 0x2200
#define AT30TSE004_DEVID_MASK 0xffff
/* Giantec */
#define GT30TS00_DEVID 0x2200
#define GT30TS00_DEVID_MASK 0xff00
#define GT34TS02_DEVID 0x3300
#define GT34TS02_DEVID_MASK 0xff00
/* IDT */
#define TSE2004_DEVID 0x2200
#define TSE2004_DEVID_MASK 0xff00
#define TS3000_DEVID 0x2900 /* Also matches TSE2002 */
#define TS3000_DEVID_MASK 0xff00
@ -116,9 +105,6 @@ static const unsigned short normal_i2c[] = {
#define MCP98243_DEVID 0x2100
#define MCP98243_DEVID_MASK 0xfffc
#define MCP98244_DEVID 0x2200
#define MCP98244_DEVID_MASK 0xfffc
#define MCP9843_DEVID 0x0000 /* Also matches mcp9805 */
#define MCP9843_DEVID_MASK 0xfffe
@ -136,12 +122,6 @@ static const unsigned short normal_i2c[] = {
#define CAT34TS02C_DEVID 0x0a00
#define CAT34TS02C_DEVID_MASK 0xfff0
#define CAT34TS04_DEVID 0x2200
#define CAT34TS04_DEVID_MASK 0xfff0
#define N34TS04_DEVID 0x2230
#define N34TS04_DEVID_MASK 0xfff0
/* ST Microelectronics */
#define STTS424_DEVID 0x0101
#define STTS424_DEVID_MASK 0xffff
@ -152,15 +132,12 @@ static const unsigned short normal_i2c[] = {
#define STTS2002_DEVID 0x0300
#define STTS2002_DEVID_MASK 0xffff
#define STTS2004_DEVID 0x2201
#define STTS2004_DEVID_MASK 0xffff
#define STTS3000_DEVID 0x0200
#define STTS3000_DEVID_MASK 0xffff
/* Seiko Instruments */
#define S34TS04A_DEVID 0x2221
#define S34TS04A_DEVID_MASK 0xffff
/* TSE2004 compliant sensors */
#define TSE2004_DEVID 0x2200
#define TSE2004_DEVID_MASK 0xff00
static u16 jc42_hysteresis[] = { 0, 1500, 3000, 6000 };
@ -173,8 +150,8 @@ struct jc42_chips {
static struct jc42_chips jc42_chips[] = {
{ ADT_MANID, ADT7408_DEVID, ADT7408_DEVID_MASK },
{ ATMEL_MANID, AT30TS00_DEVID, AT30TS00_DEVID_MASK },
{ ATMEL_MANID2, AT30TSE004_DEVID, AT30TSE004_DEVID_MASK },
{ GT_MANID, GT30TS00_DEVID, GT30TS00_DEVID_MASK },
{ ATMEL_MANID2, TSE2004_DEVID, TSE2004_DEVID_MASK },
{ GT_MANID, TSE2004_DEVID, TSE2004_DEVID_MASK },
{ GT_MANID2, GT34TS02_DEVID, GT34TS02_DEVID_MASK },
{ IDT_MANID, TSE2004_DEVID, TSE2004_DEVID_MASK },
{ IDT_MANID, TS3000_DEVID, TS3000_DEVID_MASK },
@ -184,19 +161,19 @@ static struct jc42_chips jc42_chips[] = {
{ MCP_MANID, MCP9808_DEVID, MCP9808_DEVID_MASK },
{ MCP_MANID, MCP98242_DEVID, MCP98242_DEVID_MASK },
{ MCP_MANID, MCP98243_DEVID, MCP98243_DEVID_MASK },
{ MCP_MANID, MCP98244_DEVID, MCP98244_DEVID_MASK },
{ MCP_MANID, TSE2004_DEVID, TSE2004_DEVID_MASK },
{ MCP_MANID, MCP9843_DEVID, MCP9843_DEVID_MASK },
{ NXP_MANID, SE97_DEVID, SE97_DEVID_MASK },
{ ONS_MANID, CAT6095_DEVID, CAT6095_DEVID_MASK },
{ ONS_MANID, CAT34TS02C_DEVID, CAT34TS02C_DEVID_MASK },
{ ONS_MANID, CAT34TS04_DEVID, CAT34TS04_DEVID_MASK },
{ ONS_MANID, N34TS04_DEVID, N34TS04_DEVID_MASK },
{ ONS_MANID, TSE2004_DEVID, TSE2004_DEVID_MASK },
{ ONS_MANID, TSE2004_DEVID, TSE2004_DEVID_MASK },
{ NXP_MANID, SE98_DEVID, SE98_DEVID_MASK },
{ SI_MANID, S34TS04A_DEVID, S34TS04A_DEVID_MASK },
{ SI_MANID, TSE2004_DEVID, TSE2004_DEVID_MASK },
{ STM_MANID, STTS424_DEVID, STTS424_DEVID_MASK },
{ STM_MANID, STTS424E_DEVID, STTS424E_DEVID_MASK },
{ STM_MANID, STTS2002_DEVID, STTS2002_DEVID_MASK },
{ STM_MANID, STTS2004_DEVID, STTS2004_DEVID_MASK },
{ STM_MANID, TSE2004_DEVID, TSE2004_DEVID_MASK },
{ STM_MANID, STTS3000_DEVID, STTS3000_DEVID_MASK },
};
@ -436,7 +413,11 @@ static int jc42_detect(struct i2c_client *client, struct i2c_board_info *info)
if (cap < 0 || config < 0 || manid < 0 || devid < 0)
return -ENODEV;
if ((cap & 0xff00) || (config & 0xf800))
if ((cap & 0xff00) || (config & 0xf820))
return -ENODEV;
if ((devid & TSE2004_DEVID_MASK) == TSE2004_DEVID &&
(cap & 0x00e7) != 0x00e7)
return -ENODEV;
for (i = 0; i < ARRAY_SIZE(jc42_chips); i++) {

View File

@ -1104,10 +1104,7 @@ static int lm63_probe(struct i2c_client *client)
mutex_init(&data->update_lock);
/* Set the device type */
if (client->dev.of_node)
data->kind = (uintptr_t)of_device_get_match_data(&client->dev);
else
data->kind = i2c_match_id(lm63_id, client)->driver_data;
data->kind = (uintptr_t)i2c_get_match_data(client);
if (data->kind == lm64)
data->temp2_offset = 16000;

View File

@ -169,11 +169,7 @@ static int lm70_probe(struct spi_device *spi)
struct lm70 *p_lm70;
int chip;
if (dev_fwnode(&spi->dev))
chip = (int)(uintptr_t)device_get_match_data(&spi->dev);
else
chip = spi_get_device_id(spi)->driver_data;
chip = (kernel_ulong_t)spi_get_device_match_data(spi);
/* signaling is SPI_MODE_0 */
if ((spi->mode & SPI_MODE_X_MASK) != SPI_MODE_0)

View File

@ -625,20 +625,12 @@ static void lm75_remove(void *data)
i2c_smbus_write_byte_data(client, LM75_REG_CONF, lm75->orig_conf);
}
static const struct i2c_device_id lm75_ids[];
static int lm75_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct device *hwmon_dev;
struct lm75_data *data;
int status, err;
enum lm75_type kind;
if (client->dev.of_node)
kind = (uintptr_t)of_device_get_match_data(&client->dev);
else
kind = i2c_match_id(lm75_ids, client)->driver_data;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA))
@ -649,7 +641,7 @@ static int lm75_probe(struct i2c_client *client)
return -ENOMEM;
data->client = client;
data->kind = kind;
data->kind = (uintptr_t)i2c_get_match_data(client);
data->vs = devm_regulator_get(dev, "vs");
if (IS_ERR(data->vs))

View File

@ -627,8 +627,6 @@ static int lm78_i2c_detect(struct i2c_client *client,
return -ENODEV;
}
static const struct i2c_device_id lm78_i2c_id[];
static int lm78_i2c_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
@ -640,7 +638,7 @@ static int lm78_i2c_probe(struct i2c_client *client)
return -ENOMEM;
data->client = client;
data->type = i2c_match_id(lm78_i2c_id, client)->driver_data;
data->type = (uintptr_t)i2c_get_match_data(client);
/* Initialize the LM78 chip */
lm78_init_device(data);

View File

@ -417,13 +417,6 @@ static int lm83_detect(struct i2c_client *client,
return 0;
}
static const struct i2c_device_id lm83_id[] = {
{ "lm83", lm83 },
{ "lm82", lm82 },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm83_id);
static int lm83_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
@ -438,7 +431,7 @@ static int lm83_probe(struct i2c_client *client)
if (IS_ERR(data->regmap))
return PTR_ERR(data->regmap);
data->type = i2c_match_id(lm83_id, client)->driver_data;
data->type = (uintptr_t)i2c_get_match_data(client);
hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
data, &lm83_chip_info, NULL);
@ -449,6 +442,13 @@ static int lm83_probe(struct i2c_client *client)
* Driver data (common to all clients)
*/
static const struct i2c_device_id lm83_id[] = {
{ "lm83", lm83 },
{ "lm82", lm82 },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm83_id);
static struct i2c_driver lm83_driver = {
.class = I2C_CLASS_HWMON,
.driver = {

View File

@ -1544,8 +1544,6 @@ static int lm85_detect(struct i2c_client *client, struct i2c_board_info *info)
return 0;
}
static const struct i2c_device_id lm85_id[];
static int lm85_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
@ -1558,10 +1556,7 @@ static int lm85_probe(struct i2c_client *client)
return -ENOMEM;
data->client = client;
if (client->dev.of_node)
data->type = (uintptr_t)of_device_get_match_data(&client->dev);
else
data->type = i2c_match_id(lm85_id, client)->driver_data;
data->type = (uintptr_t)i2c_get_match_data(client);
mutex_init(&data->update_lock);
/* Fill in the chip specific driver values */

View File

@ -1270,42 +1270,6 @@ static int lm90_update_device(struct device *dev)
return 0;
}
/* pec used for devices with PEC support */
static ssize_t pec_show(struct device *dev, struct device_attribute *dummy,
char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
return sprintf(buf, "%d\n", !!(client->flags & I2C_CLIENT_PEC));
}
static ssize_t pec_store(struct device *dev, struct device_attribute *dummy,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
long val;
int err;
err = kstrtol(buf, 10, &val);
if (err < 0)
return err;
switch (val) {
case 0:
client->flags &= ~I2C_CLIENT_PEC;
break;
case 1:
client->flags |= I2C_CLIENT_PEC;
break;
default:
return -EINVAL;
}
return count;
}
static DEVICE_ATTR_RW(pec);
static int lm90_temp_get_resolution(struct lm90_data *data, int index)
{
switch (index) {
@ -2659,11 +2623,6 @@ static irqreturn_t lm90_irq_thread(int irq, void *dev_id)
return IRQ_NONE;
}
static void lm90_remove_pec(void *dev)
{
device_remove_file(dev, &dev_attr_pec);
}
static int lm90_probe_channel_from_dt(struct i2c_client *client,
struct device_node *child,
struct lm90_data *data)
@ -2764,10 +2723,7 @@ static int lm90_probe(struct i2c_client *client)
INIT_WORK(&data->report_work, lm90_report_alarms);
/* Set the device type */
if (client->dev.of_node)
data->kind = (uintptr_t)of_device_get_match_data(&client->dev);
else
data->kind = i2c_match_id(lm90_id, client)->driver_data;
data->kind = (uintptr_t)i2c_get_match_data(client);
/*
* Different devices have different alarm bits triggering the
@ -2802,6 +2758,8 @@ static int lm90_probe(struct i2c_client *client)
data->chip_config[0] |= HWMON_C_UPDATE_INTERVAL;
if (data->flags & LM90_HAVE_FAULTQUEUE)
data->chip_config[0] |= HWMON_C_TEMP_SAMPLES;
if (data->flags & (LM90_HAVE_PEC | LM90_HAVE_PARTIAL_PEC))
data->chip_config[0] |= HWMON_C_PEC;
data->info[1] = &data->temp_info;
info = &data->temp_info;
@ -2878,19 +2836,6 @@ static int lm90_probe(struct i2c_client *client)
return err;
}
/*
* The 'pec' attribute is attached to the i2c device and thus created
* separately.
*/
if (data->flags & (LM90_HAVE_PEC | LM90_HAVE_PARTIAL_PEC)) {
err = device_create_file(dev, &dev_attr_pec);
if (err)
return err;
err = devm_add_action_or_reset(dev, lm90_remove_pec, dev);
if (err)
return err;
}
hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
data, &data->chip,
NULL);

View File

@ -301,7 +301,8 @@ static ssize_t tcrit2_store(struct device *dev, struct device_attribute *attr,
if (ret < 0)
return ret;
val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0, index ? 255 : 127);
val = DIV_ROUND_CLOSEST(clamp_val(val, 0, (index ? 255 : 127) * 1000),
1000);
mutex_lock(&data->update_lock);
data->tcrit2[index] = val;
@ -350,7 +351,7 @@ static ssize_t tcrit1_store(struct device *dev, struct device_attribute *attr,
if (ret < 0)
return ret;
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->tcrit1[index] = val;
@ -391,7 +392,7 @@ static ssize_t tcrit1_hyst_store(struct device *dev,
if (ret < 0)
return ret;
val = DIV_ROUND_CLOSEST(val, 1000);
val = DIV_ROUND_CLOSEST(clamp_val(val, -255000, 255000), 1000);
val = clamp_val((int)data->tcrit1[index] - val, 0, 31);
mutex_lock(&data->update_lock);
@ -431,7 +432,7 @@ static ssize_t offset_store(struct device *dev, struct device_attribute *attr,
return ret;
/* Accuracy is 1/2 degrees C */
val = clamp_val(DIV_ROUND_CLOSEST(val, 500), -128, 127);
val = DIV_ROUND_CLOSEST(clamp_val(val, -64000, 63500), 500);
mutex_lock(&data->update_lock);
data->toffset[index] = val;
@ -677,10 +678,9 @@ static int lm95234_init_client(struct i2c_client *client)
return 0;
}
static const struct i2c_device_id lm95234_id[];
static int lm95234_probe(struct i2c_client *client)
{
enum chips type = (uintptr_t)i2c_get_match_data(client);
struct device *dev = &client->dev;
struct lm95234_data *data;
struct device *hwmon_dev;
@ -699,7 +699,7 @@ static int lm95234_probe(struct i2c_client *client)
return err;
data->groups[0] = &lm95234_common_group;
if (i2c_match_id(lm95234_id, client)->driver_data == lm95234)
if (type == lm95234)
data->groups[1] = &lm95234_group;
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,

View File

@ -225,8 +225,8 @@ static umode_t ltc2991_is_visible(const void *data,
case hwmon_temp:
switch (attr) {
case hwmon_temp_input:
if (st->temp_en[channel] ||
channel == LTC2991_T_INT_CH_NR)
if (channel == LTC2991_T_INT_CH_NR ||
st->temp_en[channel])
return 0444;
break;
}
@ -284,7 +284,6 @@ static const struct regmap_config ltc2991_regmap_config = {
static int ltc2991_init(struct ltc2991_state *st, struct device *dev)
{
struct fwnode_handle *child;
int ret;
u32 val, addr;
u8 v5_v8_reg_data = 0, v1_v4_reg_data = 0;
@ -294,17 +293,13 @@ static int ltc2991_init(struct ltc2991_state *st, struct device *dev)
return dev_err_probe(dev, ret,
"failed to enable regulator\n");
device_for_each_child_node(dev, child) {
device_for_each_child_node_scoped(dev, child) {
ret = fwnode_property_read_u32(child, "reg", &addr);
if (ret < 0) {
fwnode_handle_put(child);
if (ret < 0)
return ret;
}
if (addr > 3) {
fwnode_handle_put(child);
if (addr > 3)
return -EINVAL;
}
ret = fwnode_property_read_u32(child,
"shunt-resistor-micro-ohms",

View File

@ -493,8 +493,6 @@ static const struct attribute_group max16065_max_group = {
.is_visible = max16065_secondary_is_visible,
};
static const struct i2c_device_id max16065_id[];
static int max16065_probe(struct i2c_client *client)
{
struct i2c_adapter *adapter = client->adapter;
@ -505,7 +503,7 @@ static int max16065_probe(struct i2c_client *client)
bool have_secondary; /* true if chip has secondary limits */
bool secondary_is_max = false; /* secondary limits reflect max */
int groups = 0;
const struct i2c_device_id *id = i2c_match_id(max16065_id, client);
enum chips chip = (uintptr_t)i2c_get_match_data(client);
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA
| I2C_FUNC_SMBUS_READ_WORD_DATA))
@ -518,9 +516,9 @@ static int max16065_probe(struct i2c_client *client)
data->client = client;
mutex_init(&data->update_lock);
data->num_adc = max16065_num_adc[id->driver_data];
data->have_current = max16065_have_current[id->driver_data];
have_secondary = max16065_have_secondary[id->driver_data];
data->num_adc = max16065_num_adc[chip];
data->have_current = max16065_have_current[chip];
have_secondary = max16065_have_secondary[chip];
if (have_secondary) {
val = i2c_smbus_read_byte_data(client, MAX16065_SW_ENABLE);

View File

@ -391,8 +391,6 @@ static int max1668_detect(struct i2c_client *client,
return 0;
}
static const struct i2c_device_id max1668_id[];
static int max1668_probe(struct i2c_client *client)
{
struct i2c_adapter *adapter = client->adapter;
@ -408,7 +406,7 @@ static int max1668_probe(struct i2c_client *client)
return -ENOMEM;
data->client = client;
data->type = i2c_match_id(max1668_id, client)->driver_data;
data->type = (uintptr_t)i2c_get_match_data(client);
mutex_init(&data->update_lock);
/* sysfs hooks */

View File

@ -24,6 +24,7 @@
#define MAX31827_CONFIGURATION_1SHOT_MASK BIT(0)
#define MAX31827_CONFIGURATION_CNV_RATE_MASK GENMASK(3, 1)
#define MAX31827_CONFIGURATION_PEC_EN_MASK BIT(4)
#define MAX31827_CONFIGURATION_TIMEOUT_MASK BIT(5)
#define MAX31827_CONFIGURATION_RESOLUTION_MASK GENMASK(7, 6)
#define MAX31827_CONFIGURATION_ALRM_POL_MASK BIT(8)
@ -46,6 +47,11 @@
#define MAX31827_M_DGR_TO_16_BIT(x) (((x) << 4) / 1000)
#define MAX31827_DEVICE_ENABLE(x) ((x) ? 0xA : 0x0)
/*
* The enum passed in the .data pointer of struct of_device_id must
* start with a value != 0 since that is a requirement for using
* device_get_match_data().
*/
enum chips { max31827 = 1, max31828, max31829 };
enum max31827_cnv {
@ -382,7 +388,8 @@ static int max31827_write(struct device *dev, enum hwmon_sensor_types type,
}
case hwmon_chip:
if (attr == hwmon_chip_update_interval) {
switch (attr) {
case hwmon_chip_update_interval:
if (!st->enable)
return -EINVAL;
@ -410,14 +417,18 @@ static int max31827_write(struct device *dev, enum hwmon_sensor_types type,
return ret;
st->update_interval = val;
}
break;
return 0;
case hwmon_chip_pec:
return regmap_update_bits(st->regmap, MAX31827_CONFIGURATION_REG,
MAX31827_CONFIGURATION_PEC_EN_MASK,
val ? MAX31827_CONFIGURATION_PEC_EN_MASK : 0);
default:
return -EOPNOTSUPP;
}
default:
return -EOPNOTSUPP;
}
return 0;
}
static ssize_t temp1_resolution_show(struct device *dev,
@ -583,7 +594,7 @@ static const struct hwmon_channel_info *max31827_info[] = {
HWMON_T_MIN_HYST | HWMON_T_MIN_ALARM |
HWMON_T_MAX | HWMON_T_MAX_HYST |
HWMON_T_MAX_ALARM),
HWMON_CHANNEL_INFO(chip, HWMON_C_UPDATE_INTERVAL),
HWMON_CHANNEL_INFO(chip, HWMON_C_UPDATE_INTERVAL | HWMON_C_PEC),
NULL,
};

View File

@ -21,6 +21,7 @@
#include <linux/mutex.h>
#include <linux/platform_data/max6639.h>
#include <linux/regmap.h>
#include <linux/util_macros.h>
/* Addresses to scan */
static const unsigned short normal_i2c[] = { 0x2c, 0x2e, 0x2f, I2C_CLIENT_END };
@ -55,13 +56,17 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2e, 0x2f, I2C_CLIENT_END };
#define MAX6639_GCONFIG_PWM_FREQ_HI 0x08
#define MAX6639_FAN_CONFIG1_PWM 0x80
#define MAX6639_FAN_CONFIG3_FREQ_MASK 0x03
#define MAX6639_FAN_CONFIG3_THERM_FULL_SPEED 0x40
#define MAX6639_NUM_CHANNELS 2
static const int rpm_ranges[] = { 2000, 4000, 8000, 16000 };
/* Supported PWM frequency */
static const unsigned int freq_table[] = { 20, 33, 50, 100, 5000, 8333, 12500,
25000 };
#define FAN_FROM_REG(val, rpm_range) ((val) == 0 || (val) == 255 ? \
0 : (rpm_ranges[rpm_range] * 30) / (val))
#define TEMP_LIMIT_TO_REG(val) clamp_val((val) / 1000, 0, 255)
@ -71,21 +76,19 @@ static const int rpm_ranges[] = { 2000, 4000, 8000, 16000 };
*/
struct max6639_data {
struct regmap *regmap;
struct mutex update_lock;
/* Register values initialized only once */
u8 ppr; /* Pulses per rotation 0..3 for 1..4 ppr */
u8 rpm_range; /* Index in above rpm_ranges table */
u8 ppr[MAX6639_NUM_CHANNELS]; /* Pulses per rotation 0..3 for 1..4 ppr */
u8 rpm_range[MAX6639_NUM_CHANNELS]; /* Index in above rpm_ranges table */
/* Optional regulator for FAN supply */
struct regulator *reg;
};
static ssize_t temp_input_show(struct device *dev,
struct device_attribute *dev_attr, char *buf)
static int max6639_temp_read_input(struct device *dev, int channel, long *temp)
{
long temp;
struct max6639_data *data = dev_get_drvdata(dev);
struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
unsigned int val;
int res;
@ -93,191 +96,84 @@ static ssize_t temp_input_show(struct device *dev,
* Lock isn't needed as MAX6639_REG_TEMP wpnt change for at least 250ms after reading
* MAX6639_REG_TEMP_EXT
*/
res = regmap_read(data->regmap, MAX6639_REG_TEMP_EXT(attr->index), &val);
res = regmap_read(data->regmap, MAX6639_REG_TEMP_EXT(channel), &val);
if (res < 0)
return res;
temp = val >> 5;
res = regmap_read(data->regmap, MAX6639_REG_TEMP(attr->index), &val);
*temp = val >> 5;
res = regmap_read(data->regmap, MAX6639_REG_TEMP(channel), &val);
if (res < 0)
return res;
temp |= val << 3;
temp *= 125;
*temp |= val << 3;
*temp *= 125;
return sprintf(buf, "%ld\n", temp);
return 0;
}
static ssize_t temp_fault_show(struct device *dev,
struct device_attribute *dev_attr, char *buf)
static int max6639_temp_read_fault(struct device *dev, int channel, long *fault)
{
struct max6639_data *data = dev_get_drvdata(dev);
struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
unsigned int val;
int res;
res = regmap_read(data->regmap, MAX6639_REG_TEMP_EXT(attr->index), &val);
if (res < 0)
return res;
return sprintf(buf, "%d\n", val & 1);
}
static ssize_t temp_max_show(struct device *dev,
struct device_attribute *dev_attr, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
struct max6639_data *data = dev_get_drvdata(dev);
unsigned int val;
int res;
res = regmap_read(data->regmap, MAX6639_REG_THERM_LIMIT(attr->index), &val);
res = regmap_read(data->regmap, MAX6639_REG_TEMP_EXT(channel), &val);
if (res < 0)
return res;
return sprintf(buf, "%d\n", (val * 1000));
*fault = val & 1;
return 0;
}
static ssize_t temp_max_store(struct device *dev,
struct device_attribute *dev_attr,
const char *buf, size_t count)
static int max6639_temp_read_max(struct device *dev, int channel, long *max)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
struct max6639_data *data = dev_get_drvdata(dev);
unsigned long val;
int res;
res = kstrtoul(buf, 10, &val);
if (res)
return res;
regmap_write(data->regmap, MAX6639_REG_THERM_LIMIT(attr->index),
TEMP_LIMIT_TO_REG(val));
return count;
}
static ssize_t temp_crit_show(struct device *dev,
struct device_attribute *dev_attr, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
struct max6639_data *data = dev_get_drvdata(dev);
unsigned int val;
int res;
res = regmap_read(data->regmap, MAX6639_REG_ALERT_LIMIT(attr->index), &val);
res = regmap_read(data->regmap, MAX6639_REG_THERM_LIMIT(channel), &val);
if (res < 0)
return res;
return sprintf(buf, "%d\n", (val * 1000));
*max = (long)val * 1000;
return 0;
}
static ssize_t temp_crit_store(struct device *dev,
struct device_attribute *dev_attr,
const char *buf, size_t count)
static int max6639_temp_read_crit(struct device *dev, int channel, long *crit)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
struct max6639_data *data = dev_get_drvdata(dev);
unsigned long val;
int res;
res = kstrtoul(buf, 10, &val);
if (res)
return res;
regmap_write(data->regmap, MAX6639_REG_ALERT_LIMIT(attr->index),
TEMP_LIMIT_TO_REG(val));
return count;
}
static ssize_t temp_emergency_show(struct device *dev,
struct device_attribute *dev_attr,
char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
struct max6639_data *data = dev_get_drvdata(dev);
unsigned int val;
int res;
res = regmap_read(data->regmap, MAX6639_REG_OT_LIMIT(attr->index), &val);
res = regmap_read(data->regmap, MAX6639_REG_ALERT_LIMIT(channel), &val);
if (res < 0)
return res;
return sprintf(buf, "%d\n", (val * 1000));
*crit = (long)val * 1000;
return 0;
}
static ssize_t temp_emergency_store(struct device *dev,
struct device_attribute *dev_attr,
const char *buf, size_t count)
static int max6639_temp_read_emergency(struct device *dev, int channel, long *emerg)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
struct max6639_data *data = dev_get_drvdata(dev);
unsigned long val;
int res;
res = kstrtoul(buf, 10, &val);
if (res)
return res;
regmap_write(data->regmap, MAX6639_REG_OT_LIMIT(attr->index), TEMP_LIMIT_TO_REG(val));
return count;
}
static ssize_t pwm_show(struct device *dev, struct device_attribute *dev_attr,
char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
struct max6639_data *data = dev_get_drvdata(dev);
unsigned int val;
int res;
res = regmap_read(data->regmap, MAX6639_REG_TARGTDUTY(attr->index), &val);
res = regmap_read(data->regmap, MAX6639_REG_OT_LIMIT(channel), &val);
if (res < 0)
return res;
return sprintf(buf, "%d\n", val * 255 / 120);
*emerg = (long)val * 1000;
return 0;
}
static ssize_t pwm_store(struct device *dev,
struct device_attribute *dev_attr, const char *buf,
size_t count)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
struct max6639_data *data = dev_get_drvdata(dev);
unsigned long val;
int res;
res = kstrtoul(buf, 10, &val);
if (res)
return res;
val = clamp_val(val, 0, 255);
regmap_write(data->regmap, MAX6639_REG_TARGTDUTY(attr->index), val * 120 / 255);
return count;
}
static ssize_t fan_input_show(struct device *dev,
struct device_attribute *dev_attr, char *buf)
static int max6639_get_status(struct device *dev, unsigned int *status)
{
struct max6639_data *data = dev_get_drvdata(dev);
struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
unsigned int val;
int res;
res = regmap_read(data->regmap, MAX6639_REG_FAN_CNT(attr->index), &val);
if (res < 0)
return res;
return sprintf(buf, "%d\n", FAN_FROM_REG(val, data->rpm_range));
}
static ssize_t alarm_show(struct device *dev,
struct device_attribute *dev_attr, char *buf)
{
struct max6639_data *data = dev_get_drvdata(dev);
struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
unsigned int val;
int res;
@ -285,59 +181,359 @@ static ssize_t alarm_show(struct device *dev,
if (res < 0)
return res;
return sprintf(buf, "%d\n", !!(val & (1 << attr->index)));
*status = val;
return 0;
}
static SENSOR_DEVICE_ATTR_RO(temp1_input, temp_input, 0);
static SENSOR_DEVICE_ATTR_RO(temp2_input, temp_input, 1);
static SENSOR_DEVICE_ATTR_RO(temp1_fault, temp_fault, 0);
static SENSOR_DEVICE_ATTR_RO(temp2_fault, temp_fault, 1);
static SENSOR_DEVICE_ATTR_RW(temp1_max, temp_max, 0);
static SENSOR_DEVICE_ATTR_RW(temp2_max, temp_max, 1);
static SENSOR_DEVICE_ATTR_RW(temp1_crit, temp_crit, 0);
static SENSOR_DEVICE_ATTR_RW(temp2_crit, temp_crit, 1);
static SENSOR_DEVICE_ATTR_RW(temp1_emergency, temp_emergency, 0);
static SENSOR_DEVICE_ATTR_RW(temp2_emergency, temp_emergency, 1);
static SENSOR_DEVICE_ATTR_RW(pwm1, pwm, 0);
static SENSOR_DEVICE_ATTR_RW(pwm2, pwm, 1);
static SENSOR_DEVICE_ATTR_RO(fan1_input, fan_input, 0);
static SENSOR_DEVICE_ATTR_RO(fan2_input, fan_input, 1);
static SENSOR_DEVICE_ATTR_RO(fan1_fault, alarm, 1);
static SENSOR_DEVICE_ATTR_RO(fan2_fault, alarm, 0);
static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, alarm, 3);
static SENSOR_DEVICE_ATTR_RO(temp2_max_alarm, alarm, 2);
static SENSOR_DEVICE_ATTR_RO(temp1_crit_alarm, alarm, 7);
static SENSOR_DEVICE_ATTR_RO(temp2_crit_alarm, alarm, 6);
static SENSOR_DEVICE_ATTR_RO(temp1_emergency_alarm, alarm, 5);
static SENSOR_DEVICE_ATTR_RO(temp2_emergency_alarm, alarm, 4);
static int max6639_temp_set_max(struct max6639_data *data, int channel, long val)
{
int res;
res = regmap_write(data->regmap, MAX6639_REG_THERM_LIMIT(channel),
TEMP_LIMIT_TO_REG(val));
return res;
}
static struct attribute *max6639_attrs[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_temp2_input.dev_attr.attr,
&sensor_dev_attr_temp1_fault.dev_attr.attr,
&sensor_dev_attr_temp2_fault.dev_attr.attr,
&sensor_dev_attr_temp1_max.dev_attr.attr,
&sensor_dev_attr_temp2_max.dev_attr.attr,
&sensor_dev_attr_temp1_crit.dev_attr.attr,
&sensor_dev_attr_temp2_crit.dev_attr.attr,
&sensor_dev_attr_temp1_emergency.dev_attr.attr,
&sensor_dev_attr_temp2_emergency.dev_attr.attr,
&sensor_dev_attr_pwm1.dev_attr.attr,
&sensor_dev_attr_pwm2.dev_attr.attr,
&sensor_dev_attr_fan1_input.dev_attr.attr,
&sensor_dev_attr_fan2_input.dev_attr.attr,
&sensor_dev_attr_fan1_fault.dev_attr.attr,
&sensor_dev_attr_fan2_fault.dev_attr.attr,
&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
&sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
&sensor_dev_attr_temp1_emergency_alarm.dev_attr.attr,
&sensor_dev_attr_temp2_emergency_alarm.dev_attr.attr,
static int max6639_temp_set_crit(struct max6639_data *data, int channel, long val)
{
int res;
res = regmap_write(data->regmap, MAX6639_REG_ALERT_LIMIT(channel), TEMP_LIMIT_TO_REG(val));
return res;
}
static int max6639_temp_set_emergency(struct max6639_data *data, int channel, long val)
{
int res;
res = regmap_write(data->regmap, MAX6639_REG_OT_LIMIT(channel), TEMP_LIMIT_TO_REG(val));
return res;
}
static int max6639_read_fan(struct device *dev, u32 attr, int channel,
long *fan_val)
{
struct max6639_data *data = dev_get_drvdata(dev);
unsigned int val;
int res;
switch (attr) {
case hwmon_fan_input:
res = regmap_read(data->regmap, MAX6639_REG_FAN_CNT(channel), &val);
if (res < 0)
return res;
*fan_val = FAN_FROM_REG(val, data->rpm_range[channel]);
return 0;
case hwmon_fan_fault:
res = max6639_get_status(dev, &val);
if (res < 0)
return res;
*fan_val = !!(val & BIT(1 - channel));
return 0;
case hwmon_fan_pulses:
*fan_val = data->ppr[channel];
return 0;
default:
return -EOPNOTSUPP;
}
}
static int max6639_set_ppr(struct max6639_data *data, int channel, u8 ppr)
{
/* Decrement the PPR value and shift left by 6 to match the register format */
return regmap_write(data->regmap, MAX6639_REG_FAN_PPR(channel), ppr-- << 6);
}
static int max6639_write_fan(struct device *dev, u32 attr, int channel,
long val)
{
struct max6639_data *data = dev_get_drvdata(dev);
int err;
switch (attr) {
case hwmon_fan_pulses:
if (val <= 0 || val > 4)
return -EINVAL;
mutex_lock(&data->update_lock);
/* Set Fan pulse per revolution */
err = max6639_set_ppr(data, channel, val);
if (err < 0) {
mutex_unlock(&data->update_lock);
return err;
}
data->ppr[channel] = val;
mutex_unlock(&data->update_lock);
return 0;
default:
return -EOPNOTSUPP;
}
}
static umode_t max6639_fan_is_visible(const void *_data, u32 attr, int channel)
{
switch (attr) {
case hwmon_fan_input:
case hwmon_fan_fault:
return 0444;
case hwmon_fan_pulses:
return 0644;
default:
return 0;
}
}
static int max6639_read_pwm(struct device *dev, u32 attr, int channel,
long *pwm_val)
{
struct max6639_data *data = dev_get_drvdata(dev);
unsigned int val;
int res;
u8 i;
switch (attr) {
case hwmon_pwm_input:
res = regmap_read(data->regmap, MAX6639_REG_TARGTDUTY(channel), &val);
if (res < 0)
return res;
*pwm_val = val * 255 / 120;
return 0;
case hwmon_pwm_freq:
mutex_lock(&data->update_lock);
res = regmap_read(data->regmap, MAX6639_REG_FAN_CONFIG3(channel), &val);
if (res < 0) {
mutex_unlock(&data->update_lock);
return res;
}
i = val & MAX6639_FAN_CONFIG3_FREQ_MASK;
res = regmap_read(data->regmap, MAX6639_REG_GCONFIG, &val);
if (res < 0) {
mutex_unlock(&data->update_lock);
return res;
}
if (val & MAX6639_GCONFIG_PWM_FREQ_HI)
i |= 0x4;
i &= 0x7;
*pwm_val = freq_table[i];
mutex_unlock(&data->update_lock);
return 0;
default:
return -EOPNOTSUPP;
}
}
static int max6639_write_pwm(struct device *dev, u32 attr, int channel,
long val)
{
struct max6639_data *data = dev_get_drvdata(dev);
int err;
u8 i;
switch (attr) {
case hwmon_pwm_input:
if (val < 0 || val > 255)
return -EINVAL;
err = regmap_write(data->regmap, MAX6639_REG_TARGTDUTY(channel),
val * 120 / 255);
return err;
case hwmon_pwm_freq:
val = clamp_val(val, 0, 25000);
i = find_closest(val, freq_table, ARRAY_SIZE(freq_table));
mutex_lock(&data->update_lock);
err = regmap_update_bits(data->regmap, MAX6639_REG_FAN_CONFIG3(channel),
MAX6639_FAN_CONFIG3_FREQ_MASK, i);
if (err < 0) {
mutex_unlock(&data->update_lock);
return err;
}
if (i >> 2)
err = regmap_set_bits(data->regmap, MAX6639_REG_GCONFIG,
MAX6639_GCONFIG_PWM_FREQ_HI);
else
err = regmap_clear_bits(data->regmap, MAX6639_REG_GCONFIG,
MAX6639_GCONFIG_PWM_FREQ_HI);
mutex_unlock(&data->update_lock);
return err;
default:
return -EOPNOTSUPP;
}
}
static umode_t max6639_pwm_is_visible(const void *_data, u32 attr, int channel)
{
switch (attr) {
case hwmon_pwm_input:
case hwmon_pwm_freq:
return 0644;
default:
return 0;
}
}
static int max6639_read_temp(struct device *dev, u32 attr, int channel,
long *val)
{
unsigned int status;
int res;
switch (attr) {
case hwmon_temp_input:
res = max6639_temp_read_input(dev, channel, val);
return res;
case hwmon_temp_fault:
res = max6639_temp_read_fault(dev, channel, val);
return res;
case hwmon_temp_max:
res = max6639_temp_read_max(dev, channel, val);
return res;
case hwmon_temp_crit:
res = max6639_temp_read_crit(dev, channel, val);
return res;
case hwmon_temp_emergency:
res = max6639_temp_read_emergency(dev, channel, val);
return res;
case hwmon_temp_max_alarm:
res = max6639_get_status(dev, &status);
if (res < 0)
return res;
*val = !!(status & BIT(3 - channel));
return 0;
case hwmon_temp_crit_alarm:
res = max6639_get_status(dev, &status);
if (res < 0)
return res;
*val = !!(status & BIT(7 - channel));
return 0;
case hwmon_temp_emergency_alarm:
res = max6639_get_status(dev, &status);
if (res < 0)
return res;
*val = !!(status & BIT(5 - channel));
return 0;
default:
return -EOPNOTSUPP;
}
}
static int max6639_write_temp(struct device *dev, u32 attr, int channel,
long val)
{
struct max6639_data *data = dev_get_drvdata(dev);
switch (attr) {
case hwmon_temp_max:
return max6639_temp_set_max(data, channel, val);
case hwmon_temp_crit:
return max6639_temp_set_crit(data, channel, val);
case hwmon_temp_emergency:
return max6639_temp_set_emergency(data, channel, val);
default:
return -EOPNOTSUPP;
}
}
static umode_t max6639_temp_is_visible(const void *_data, u32 attr, int channel)
{
switch (attr) {
case hwmon_temp_input:
case hwmon_temp_fault:
case hwmon_temp_max_alarm:
case hwmon_temp_crit_alarm:
case hwmon_temp_emergency_alarm:
return 0444;
case hwmon_temp_max:
case hwmon_temp_crit:
case hwmon_temp_emergency:
return 0644;
default:
return 0;
}
}
static int max6639_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
switch (type) {
case hwmon_fan:
return max6639_read_fan(dev, attr, channel, val);
case hwmon_pwm:
return max6639_read_pwm(dev, attr, channel, val);
case hwmon_temp:
return max6639_read_temp(dev, attr, channel, val);
default:
return -EOPNOTSUPP;
}
}
static int max6639_write(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long val)
{
switch (type) {
case hwmon_fan:
return max6639_write_fan(dev, attr, channel, val);
case hwmon_pwm:
return max6639_write_pwm(dev, attr, channel, val);
case hwmon_temp:
return max6639_write_temp(dev, attr, channel, val);
default:
return -EOPNOTSUPP;
}
}
static umode_t max6639_is_visible(const void *data,
enum hwmon_sensor_types type,
u32 attr, int channel)
{
switch (type) {
case hwmon_fan:
return max6639_fan_is_visible(data, attr, channel);
case hwmon_pwm:
return max6639_pwm_is_visible(data, attr, channel);
case hwmon_temp:
return max6639_temp_is_visible(data, attr, channel);
default:
return 0;
}
}
static const struct hwmon_channel_info * const max6639_info[] = {
HWMON_CHANNEL_INFO(fan,
HWMON_F_INPUT | HWMON_F_FAULT | HWMON_F_PULSES,
HWMON_F_INPUT | HWMON_F_FAULT | HWMON_F_PULSES),
HWMON_CHANNEL_INFO(pwm,
HWMON_PWM_INPUT | HWMON_PWM_FREQ,
HWMON_PWM_INPUT | HWMON_PWM_FREQ),
HWMON_CHANNEL_INFO(temp,
HWMON_T_INPUT | HWMON_T_FAULT | HWMON_T_MAX | HWMON_T_MAX_ALARM |
HWMON_T_CRIT | HWMON_T_CRIT_ALARM | HWMON_T_EMERGENCY |
HWMON_T_EMERGENCY_ALARM,
HWMON_T_INPUT | HWMON_T_FAULT | HWMON_T_MAX | HWMON_T_MAX_ALARM |
HWMON_T_CRIT | HWMON_T_CRIT_ALARM | HWMON_T_EMERGENCY |
HWMON_T_EMERGENCY_ALARM),
NULL
};
ATTRIBUTE_GROUPS(max6639);
static const struct hwmon_ops max6639_hwmon_ops = {
.is_visible = max6639_is_visible,
.read = max6639_read,
.write = max6639_write,
};
static const struct hwmon_chip_info max6639_chip_info = {
.ops = &max6639_hwmon_ops,
.info = max6639_info,
};
/*
* returns respective index in rpm_ranges table
@ -355,11 +551,6 @@ static int rpm_range_to_reg(int range)
return 1; /* default: 4000 RPM */
}
static int max6639_set_ppr(struct max6639_data *data, u8 channel, u8 ppr)
{
return regmap_write(data->regmap, MAX6639_REG_FAN_PPR(channel), ppr << 6);
}
static int max6639_init_client(struct i2c_client *client,
struct max6639_data *data)
{
@ -380,30 +571,34 @@ static int max6639_init_client(struct i2c_client *client,
ppr = max6639_info->ppr;
else
ppr = 2;
ppr -= 1;
data->ppr[0] = ppr;
data->ppr[1] = ppr;
if (max6639_info)
rpm_range = rpm_range_to_reg(max6639_info->rpm_range);
data->rpm_range = rpm_range;
data->rpm_range[0] = rpm_range;
data->rpm_range[1] = rpm_range;
for (i = 0; i < MAX6639_NUM_CHANNELS; i++) {
/* Set Fan pulse per revolution */
err = max6639_set_ppr(data, i, ppr);
err = max6639_set_ppr(data, i, data->ppr[i]);
if (err)
return err;
/* Fans config PWM, RPM */
err = regmap_write(data->regmap, MAX6639_REG_FAN_CONFIG1(i),
MAX6639_FAN_CONFIG1_PWM | rpm_range);
MAX6639_FAN_CONFIG1_PWM | data->rpm_range[i]);
if (err)
return err;
/* Fans PWM polarity high by default */
if (max6639_info && 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);
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);
}
if (err)
return err;
@ -529,14 +724,17 @@ static int max6639_probe(struct i2c_client *client)
}
}
mutex_init(&data->update_lock);
/* Initialize the max6639 chip */
err = max6639_init_client(client, data);
if (err < 0)
return err;
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
data,
max6639_groups);
hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
data, &max6639_chip_info,
NULL);
return PTR_ERR_OR_ZERO(hwmon_dev);
}

View File

@ -1,314 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for +/-1 degree C, SMBus-Compatible Remote/Local Temperature Sensor
* with Overtemperature Alarm
*
* Copyright (C) 2011 AppearTV AS
*
* Derived from:
*
* Based on the max1619 driver.
* Copyright (C) 2003-2004 Oleksij Rempel <bug-track@fisher-privat.net>
* Jean Delvare <jdelvare@suse.de>
*
* The MAX6642 is a sensor chip made by Maxim.
* It reports up to two temperatures (its own plus up to
* one external one). Complete datasheet can be
* obtained from Maxim's website at:
* http://datasheets.maxim-ic.com/en/ds/MAX6642.pdf
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/sysfs.h>
static const unsigned short normal_i2c[] = {
0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, I2C_CLIENT_END };
/*
* The MAX6642 registers
*/
#define MAX6642_REG_R_MAN_ID 0xFE
#define MAX6642_REG_R_CONFIG 0x03
#define MAX6642_REG_W_CONFIG 0x09
#define MAX6642_REG_R_STATUS 0x02
#define MAX6642_REG_R_LOCAL_TEMP 0x00
#define MAX6642_REG_R_LOCAL_TEMPL 0x11
#define MAX6642_REG_R_LOCAL_HIGH 0x05
#define MAX6642_REG_W_LOCAL_HIGH 0x0B
#define MAX6642_REG_R_REMOTE_TEMP 0x01
#define MAX6642_REG_R_REMOTE_TEMPL 0x10
#define MAX6642_REG_R_REMOTE_HIGH 0x07
#define MAX6642_REG_W_REMOTE_HIGH 0x0D
/*
* Conversions
*/
static int temp_from_reg10(int val)
{
return val * 250;
}
static int temp_from_reg(int val)
{
return val * 1000;
}
static int temp_to_reg(int val)
{
return val / 1000;
}
/*
* Client data (each client gets its own)
*/
struct max6642_data {
struct i2c_client *client;
struct mutex update_lock;
bool valid; /* zero until following fields are valid */
unsigned long last_updated; /* in jiffies */
/* registers values */
u16 temp_input[2]; /* local/remote */
u16 temp_high[2]; /* local/remote */
u8 alarms;
};
/*
* Real code
*/
static void max6642_init_client(struct max6642_data *data,
struct i2c_client *client)
{
u8 config;
/*
* Start the conversions.
*/
config = i2c_smbus_read_byte_data(client, MAX6642_REG_R_CONFIG);
if (config & 0x40)
i2c_smbus_write_byte_data(client, MAX6642_REG_W_CONFIG,
config & 0xBF); /* run */
data->temp_high[0] = i2c_smbus_read_byte_data(client,
MAX6642_REG_R_LOCAL_HIGH);
data->temp_high[1] = i2c_smbus_read_byte_data(client,
MAX6642_REG_R_REMOTE_HIGH);
}
/* Return 0 if detection is successful, -ENODEV otherwise */
static int max6642_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
u8 reg_config, reg_status, man_id;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
/* identification */
man_id = i2c_smbus_read_byte_data(client, MAX6642_REG_R_MAN_ID);
if (man_id != 0x4D)
return -ENODEV;
/* sanity check */
if (i2c_smbus_read_byte_data(client, 0x04) != 0x4D
|| i2c_smbus_read_byte_data(client, 0x06) != 0x4D
|| i2c_smbus_read_byte_data(client, 0xff) != 0x4D)
return -ENODEV;
/*
* We read the config and status register, the 4 lower bits in the
* config register should be zero and bit 5, 3, 1 and 0 should be
* zero in the status register.
*/
reg_config = i2c_smbus_read_byte_data(client, MAX6642_REG_R_CONFIG);
if ((reg_config & 0x0f) != 0x00)
return -ENODEV;
/* in between, another round of sanity checks */
if (i2c_smbus_read_byte_data(client, 0x04) != reg_config
|| i2c_smbus_read_byte_data(client, 0x06) != reg_config
|| i2c_smbus_read_byte_data(client, 0xff) != reg_config)
return -ENODEV;
reg_status = i2c_smbus_read_byte_data(client, MAX6642_REG_R_STATUS);
if ((reg_status & 0x2b) != 0x00)
return -ENODEV;
strscpy(info->type, "max6642", I2C_NAME_SIZE);
return 0;
}
static struct max6642_data *max6642_update_device(struct device *dev)
{
struct max6642_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
u16 val, tmp;
mutex_lock(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
dev_dbg(dev, "Updating max6642 data.\n");
val = i2c_smbus_read_byte_data(client,
MAX6642_REG_R_LOCAL_TEMPL);
tmp = (val >> 6) & 3;
val = i2c_smbus_read_byte_data(client,
MAX6642_REG_R_LOCAL_TEMP);
val = (val << 2) | tmp;
data->temp_input[0] = val;
val = i2c_smbus_read_byte_data(client,
MAX6642_REG_R_REMOTE_TEMPL);
tmp = (val >> 6) & 3;
val = i2c_smbus_read_byte_data(client,
MAX6642_REG_R_REMOTE_TEMP);
val = (val << 2) | tmp;
data->temp_input[1] = val;
data->alarms = i2c_smbus_read_byte_data(client,
MAX6642_REG_R_STATUS);
data->last_updated = jiffies;
data->valid = true;
}
mutex_unlock(&data->update_lock);
return data;
}
/*
* Sysfs stuff
*/
static ssize_t temp_max10_show(struct device *dev,
struct device_attribute *dev_attr, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(dev_attr);
struct max6642_data *data = max6642_update_device(dev);
return sprintf(buf, "%d\n",
temp_from_reg10(data->temp_input[attr->index]));
}
static ssize_t temp_max_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(attr);
struct max6642_data *data = max6642_update_device(dev);
return sprintf(buf, "%d\n", temp_from_reg(data->temp_high[attr2->nr]));
}
static ssize_t temp_max_store(struct device *dev,
struct device_attribute *attr, const char *buf,
size_t count)
{
struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(attr);
struct max6642_data *data = dev_get_drvdata(dev);
unsigned long val;
int err;
err = kstrtoul(buf, 10, &val);
if (err < 0)
return err;
mutex_lock(&data->update_lock);
data->temp_high[attr2->nr] = clamp_val(temp_to_reg(val), 0, 255);
i2c_smbus_write_byte_data(data->client, attr2->index,
data->temp_high[attr2->nr]);
mutex_unlock(&data->update_lock);
return count;
}
static ssize_t alarm_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
int bitnr = to_sensor_dev_attr(attr)->index;
struct max6642_data *data = max6642_update_device(dev);
return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1);
}
static SENSOR_DEVICE_ATTR_RO(temp1_input, temp_max10, 0);
static SENSOR_DEVICE_ATTR_RO(temp2_input, temp_max10, 1);
static SENSOR_DEVICE_ATTR_2_RW(temp1_max, temp_max, 0,
MAX6642_REG_W_LOCAL_HIGH);
static SENSOR_DEVICE_ATTR_2_RW(temp2_max, temp_max, 1,
MAX6642_REG_W_REMOTE_HIGH);
static SENSOR_DEVICE_ATTR_RO(temp2_fault, alarm, 2);
static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, alarm, 6);
static SENSOR_DEVICE_ATTR_RO(temp2_max_alarm, alarm, 4);
static struct attribute *max6642_attrs[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_temp2_input.dev_attr.attr,
&sensor_dev_attr_temp1_max.dev_attr.attr,
&sensor_dev_attr_temp2_max.dev_attr.attr,
&sensor_dev_attr_temp2_fault.dev_attr.attr,
&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
NULL
};
ATTRIBUTE_GROUPS(max6642);
static int max6642_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct max6642_data *data;
struct device *hwmon_dev;
data = devm_kzalloc(dev, sizeof(struct max6642_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->client = client;
mutex_init(&data->update_lock);
/* Initialize the MAX6642 chip */
max6642_init_client(data, client);
hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev,
client->name, data,
max6642_groups);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
/*
* Driver data (common to all clients)
*/
static const struct i2c_device_id max6642_id[] = {
{ "max6642" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max6642_id);
static struct i2c_driver max6642_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "max6642",
},
.probe = max6642_probe,
.id_table = max6642_id,
.detect = max6642_detect,
.address_list = normal_i2c,
};
module_i2c_driver(max6642_driver);
MODULE_AUTHOR("Per Dalen <per.dalen@appeartv.com>");
MODULE_DESCRIPTION("MAX6642 sensor driver");
MODULE_LICENSE("GPL");

View File

@ -311,6 +311,7 @@ static ssize_t temp_store(struct device *dev,
return ret;
mutex_lock(&data->update_lock);
temp = clamp_val(temp, -1000000, 1000000); /* prevent underflow */
temp = DIV_ROUND_CLOSEST(temp, 1000) + data->temp_offset;
temp = clamp_val(temp, 0, data->type == max6581 ? 255 : 127);
data->temp[nr][index] = temp;
@ -428,14 +429,14 @@ static SENSOR_DEVICE_ATTR_RO(temp6_max_alarm, alarm, 20);
static SENSOR_DEVICE_ATTR_RO(temp7_max_alarm, alarm, 21);
static SENSOR_DEVICE_ATTR_RO(temp8_max_alarm, alarm, 23);
static SENSOR_DEVICE_ATTR_RO(temp1_crit_alarm, alarm, 14);
static SENSOR_DEVICE_ATTR_RO(temp1_crit_alarm, alarm, 15);
static SENSOR_DEVICE_ATTR_RO(temp2_crit_alarm, alarm, 8);
static SENSOR_DEVICE_ATTR_RO(temp3_crit_alarm, alarm, 9);
static SENSOR_DEVICE_ATTR_RO(temp4_crit_alarm, alarm, 10);
static SENSOR_DEVICE_ATTR_RO(temp5_crit_alarm, alarm, 11);
static SENSOR_DEVICE_ATTR_RO(temp6_crit_alarm, alarm, 12);
static SENSOR_DEVICE_ATTR_RO(temp7_crit_alarm, alarm, 13);
static SENSOR_DEVICE_ATTR_RO(temp8_crit_alarm, alarm, 15);
static SENSOR_DEVICE_ATTR_RO(temp8_crit_alarm, alarm, 14);
static SENSOR_DEVICE_ATTR_RO(temp2_fault, alarm, 1);
static SENSOR_DEVICE_ATTR_RO(temp3_fault, alarm, 2);
@ -684,8 +685,6 @@ static int max6697_init_chip(struct max6697_data *data,
return 0;
}
static const struct i2c_device_id max6697_id[];
static int max6697_probe(struct i2c_client *client)
{
struct i2c_adapter *adapter = client->adapter;
@ -701,10 +700,7 @@ static int max6697_probe(struct i2c_client *client)
if (!data)
return -ENOMEM;
if (client->dev.of_node)
data->type = (uintptr_t)of_device_get_match_data(&client->dev);
else
data->type = i2c_match_id(max6697_id, client)->driver_data;
data->type = (uintptr_t)i2c_get_match_data(client);
data->chip = &max6697_chip_data[data->type];
data->client = client;
mutex_init(&data->update_lock);

View File

@ -116,13 +116,12 @@ static const struct hwmon_chip_info mcp3021_chip_info = {
.info = mcp3021_info,
};
static const struct i2c_device_id mcp3021_id[];
static int mcp3021_probe(struct i2c_client *client)
{
struct mcp3021_data *data = NULL;
struct device_node *np = client->dev.of_node;
struct device *hwmon_dev;
enum chips type;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
return -ENODEV;
@ -149,7 +148,8 @@ static int mcp3021_probe(struct i2c_client *client)
data->vdd = MCP3021_VDD_REF_DEFAULT;
}
switch (i2c_match_id(mcp3021_id, client)->driver_data) {
type = (uintptr_t)i2c_get_match_data(client);
switch (type) {
case mcp3021:
data->sar_shift = MCP3021_SAR_SHIFT;
data->sar_mask = MCP3021_SAR_MASK;

View File

@ -925,4 +925,5 @@ static struct platform_driver moortec_pvt_driver = {
};
module_platform_driver(moortec_pvt_driver);
MODULE_DESCRIPTION("Moortec Semiconductor MR75203 PVT Controller driver");
MODULE_LICENSE("GPL v2");

View File

@ -1236,6 +1236,8 @@ static int nct6683_probe(struct platform_device *pdev)
default:
if (!force)
return -ENODEV;
dev_warn(dev, "Enabling support for unknown customer ID 0x%04x\n", data->customer_id);
break;
}
nct6683_init_device(data);

View File

@ -2262,7 +2262,7 @@ store_temp_offset(struct device *dev, struct device_attribute *attr,
if (err < 0)
return err;
val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), -128, 127);
val = DIV_ROUND_CLOSEST(clamp_val(val, -128000, 127000), 1000);
mutex_lock(&data->update_lock);
data->temp_offset[nr] = val;

View File

@ -4,7 +4,7 @@
#include <linux/types.h>
enum kinds { nct6106 = 1, nct6116, nct6775, nct6776, nct6779, nct6791, nct6792,
enum kinds { nct6106, nct6116, nct6775, nct6776, nct6779, nct6791, nct6792,
nct6793, nct6795, nct6796, nct6797, nct6798, nct6799 };
enum pwm_enable { off, manual, thermal_cruise, speed_cruise, sf3, sf4 };

View File

@ -799,6 +799,7 @@ static const struct hid_device_id nzxt_smart2_hid_id_table[] = {
{ HID_USB_DEVICE(0x1e71, 0x2010) }, /* NZXT RGB & Fan Controller */
{ HID_USB_DEVICE(0x1e71, 0x2011) }, /* NZXT RGB & Fan Controller (6 RGB) */
{ HID_USB_DEVICE(0x1e71, 0x2019) }, /* NZXT RGB & Fan Controller (6 RGB) */
{ HID_USB_DEVICE(0x1e71, 0x2020) }, /* NZXT RGB & Fan Controller (6 RGB) */
{},
};

View File

@ -337,6 +337,15 @@ config SENSORS_MP2888
This driver can also be built as a module. If so, the module will
be called mp2888.
config SENSORS_MP2891
tristate "MPS MP2891"
help
If you say yes here you get hardware monitoring support for MPS
MP2891 Dual Loop Digital Multi-Phase Controller.
This driver can also be built as a module. If so, the module will
be called mp2891.
config SENSORS_MP2975
tristate "MPS MP2975"
help
@ -346,6 +355,15 @@ config SENSORS_MP2975
This driver can also be built as a module. If so, the module will
be called mp2975.
config SENSORS_MP2993
tristate "MPS MP2993"
help
If you say yes here you get hardware monitoring support for MPS
MP2993 Dual Loop Digital Multi-Phase Controller.
This driver can also be built as a module. If so, the module will
be called mp2993.
config SENSORS_MP2975_REGULATOR
depends on SENSORS_MP2975 && REGULATOR
bool "Regulator support for MPS MP2975"
@ -362,6 +380,15 @@ config SENSORS_MP5023
This driver can also be built as a module. If so, the module will
be called mp5023.
config SENSORS_MP5920
tristate "MPS MP5920"
help
If you say yes here you get hardware monitoring support for Monolithic
MP5920.
This driver can also be built as a module. If so, the module will
be called mp5920.
config SENSORS_MP5990
tristate "MPS MP5990"
help
@ -371,6 +398,15 @@ config SENSORS_MP5990
This driver can also be built as a module. If so, the module will
be called mp5990.
config SENSORS_MP9941
tristate "MPS MP9941"
help
If you say yes here you get hardware monitoring support for MPS
MP9941.
This driver can also be built as a module. If so, the module will
be called mp9941.
config SENSORS_MPQ7932_REGULATOR
bool "Regulator support for MPQ7932"
depends on SENSORS_MPQ7932 && REGULATOR

View File

@ -36,9 +36,13 @@ obj-$(CONFIG_SENSORS_MAX34440) += max34440.o
obj-$(CONFIG_SENSORS_MAX8688) += max8688.o
obj-$(CONFIG_SENSORS_MP2856) += mp2856.o
obj-$(CONFIG_SENSORS_MP2888) += mp2888.o
obj-$(CONFIG_SENSORS_MP2891) += mp2891.o
obj-$(CONFIG_SENSORS_MP2975) += mp2975.o
obj-$(CONFIG_SENSORS_MP2993) += mp2993.o
obj-$(CONFIG_SENSORS_MP5023) += mp5023.o
obj-$(CONFIG_SENSORS_MP5920) += mp5920.o
obj-$(CONFIG_SENSORS_MP5990) += mp5990.o
obj-$(CONFIG_SENSORS_MP9941) += mp9941.o
obj-$(CONFIG_SENSORS_MPQ7932) += mpq7932.o
obj-$(CONFIG_SENSORS_MPQ8785) += mpq8785.o
obj-$(CONFIG_SENSORS_PLI1209BC) += pli1209bc.o

View File

@ -17,7 +17,7 @@
#include <linux/of.h>
#include "pmbus.h"
enum chips { lm25056 = 1, lm25066, lm5064, lm5066, lm5066i };
enum chips { lm25056, lm25066, lm5064, lm5066, lm5066i };
#define LM25066_READ_VAUX 0xd0
#define LM25066_MFR_READ_IIN 0xd1

View File

@ -58,8 +58,8 @@ static struct pmbus_driver_info ltc4286_info = {
};
static const struct i2c_device_id ltc4286_id[] = {
{ "ltc4286", 0 },
{ "ltc4287", 1 },
{ "ltc4286", },
{ "ltc4287", },
{}
};
MODULE_DEVICE_TABLE(i2c, ltc4286_id);

View File

@ -46,7 +46,7 @@
#define MP2856_PAGE_NUM 2
enum chips { mp2856 = 1, mp2857 };
enum chips { mp2856, mp2857 };
static const int mp2856_max_phases[][MP2856_PAGE_NUM] = {
[mp2856] = { MP2856_MAX_PHASE_RAIL1, MP2856_MAX_PHASE_RAIL2 },
@ -66,7 +66,6 @@ struct mp2856_data {
int vout_format[MP2856_PAGE_NUM];
int curr_sense_gain[MP2856_PAGE_NUM];
int max_phases[MP2856_PAGE_NUM];
enum chips chip_id;
};
#define to_mp2856_data(x) container_of(x, struct mp2856_data, info)
@ -397,6 +396,7 @@ static int mp2856_probe(struct i2c_client *client)
{
struct pmbus_driver_info *info;
struct mp2856_data *data;
enum chips chip_id;
int ret;
data = devm_kzalloc(&client->dev, sizeof(struct mp2856_data),
@ -404,9 +404,9 @@ static int mp2856_probe(struct i2c_client *client)
if (!data)
return -ENOMEM;
data->chip_id = (enum chips)(uintptr_t)i2c_get_match_data(client);
chip_id = (kernel_ulong_t)i2c_get_match_data(client);
memcpy(data->max_phases, mp2856_max_phases[data->chip_id],
memcpy(data->max_phases, mp2856_max_phases[chip_id],
sizeof(data->max_phases));
memcpy(&data->info, &mp2856_info, sizeof(*info));

View File

@ -0,0 +1,600 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Hardware monitoring driver for MPS Multi-phase Digital VR Controllers(MP2891)
*/
#include <linux/bitfield.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include "pmbus.h"
/*
* Vender specific registers, the register MFR_SVI3_IOUT_PRT(0x65),
* MFR_VOUT_LOOP_CTRL(0xBD), READ_PIN_EST(0x94)and READ_IIN_EST(0x95)
* redefine the standard PMBUS register. The MFR_SVI3_IOUT_PRT(0x65)
* is used to identify the iout scale and the MFR_VOUT_LOOP_CTRL(0xBD)
* is used to identify the vout scale. The READ_PIN_EST(0x94) is used
* to read input power per rail. The MP2891 does not have standard
* READ_IIN register(0x89), the iin telemetry can be obtained through
* the vendor redefined register READ_IIN_EST(0x95).
*/
#define MFR_VOUT_LOOP_CTRL 0xBD
#define READ_PIN_EST 0x94
#define READ_IIN_EST 0x95
#define MFR_SVI3_IOUT_PRT 0x65
#define MP2891_TEMP_LIMIT_OFFSET 40
#define MP2891_PIN_LIMIT_UINT 2
#define MP2891_IOUT_LIMIT_UINT 8
#define MP2891_IOUT_SCALE_DIV 32
#define MP2891_VOUT_SCALE_DIV 100
#define MP2891_OVUV_DELTA_SCALE 50
#define MP2891_OV_LIMIT_SCALE 20
#define MP2891_UV_LIMIT_SCALE 5
#define MP2891_PAGE_NUM 2
#define MP2891_RAIL1_FUNC (PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | \
PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP | \
PMBUS_HAVE_POUT | PMBUS_HAVE_PIN | \
PMBUS_HAVE_IIN | PMBUS_HAVE_STATUS_VOUT | \
PMBUS_HAVE_STATUS_IOUT | \
PMBUS_HAVE_STATUS_INPUT | \
PMBUS_HAVE_STATUS_TEMP)
#define MP2891_RAIL2_FUNC (PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | \
PMBUS_HAVE_TEMP | PMBUS_HAVE_POUT | \
PMBUS_HAVE_PIN | PMBUS_HAVE_IIN | \
PMBUS_HAVE_STATUS_VOUT | \
PMBUS_HAVE_STATUS_IOUT | \
PMBUS_HAVE_STATUS_INPUT | \
PMBUS_HAVE_STATUS_TEMP)
struct mp2891_data {
struct pmbus_driver_info info;
int vout_scale[MP2891_PAGE_NUM];
int iout_scale[MP2891_PAGE_NUM];
};
#define to_mp2891_data(x) container_of(x, struct mp2891_data, info)
/* Converts a LINEAR11 value to DIRECT format */
static u16 mp2891_reg2data_linear11(u16 word)
{
s16 exponent;
s32 mantissa;
s64 val;
exponent = ((s16)word) >> 11;
mantissa = ((s16)((word & 0x7ff) << 5)) >> 5;
val = mantissa;
if (exponent >= 0)
val <<= exponent;
else
val >>= -exponent;
return val;
}
static int
mp2891_identify_vout_scale(struct i2c_client *client, struct pmbus_driver_info *info,
int page)
{
struct mp2891_data *data = to_mp2891_data(info);
int ret;
ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page);
if (ret < 0)
return ret;
ret = i2c_smbus_read_word_data(client, MFR_VOUT_LOOP_CTRL);
if (ret < 0)
return ret;
/*
* The output voltage is equal to the READ_VOUT(0x8B) register value multiplied
* by vout_scale.
* Obtain vout scale from the register MFR_VOUT_LOOP_CTRL, bits 15-14,bit 13.
* If MFR_VOUT_LOOP_CTRL[13] = 1, the vout scale is below:
* 2.5mV/LSB
* If MFR_VOUT_LOOP_CTRL[13] = 0, the vout scale is decided by
* MFR_VOUT_LOOP_CTRL[15:14]:
* 00b - 6.25mV/LSB, 01b - 5mV/LSB, 10b - 2mV/LSB, 11b - 1mV
*/
if (ret & GENMASK(13, 13)) {
data->vout_scale[page] = 250;
} else {
ret = FIELD_GET(GENMASK(15, 14), ret);
if (ret == 0)
data->vout_scale[page] = 625;
else if (ret == 1)
data->vout_scale[page] = 500;
else if (ret == 2)
data->vout_scale[page] = 200;
else
data->vout_scale[page] = 100;
}
return 0;
}
static int
mp2891_identify_iout_scale(struct i2c_client *client, struct pmbus_driver_info *info,
int page)
{
struct mp2891_data *data = to_mp2891_data(info);
int ret;
ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page);
if (ret < 0)
return ret;
ret = i2c_smbus_read_word_data(client, MFR_SVI3_IOUT_PRT);
if (ret < 0)
return ret;
/*
* The output current is equal to the READ_IOUT(0x8C) register value
* multiplied by iout_scale.
* Obtain iout_scale from the register MFR_SVI3_IOUT_PRT[2:0].
* The value is selected as below:
* 000b - 1A/LSB, 001b - (1/32)A/LSB, 010b - (1/16)A/LSB,
* 011b - (1/8)A/LSB, 100b - (1/4)A/LSB, 101b - (1/2)A/LSB
* 110b - 1A/LSB, 111b - 2A/LSB
*/
switch (ret & GENMASK(2, 0)) {
case 0:
case 6:
data->iout_scale[page] = 32;
break;
case 1:
data->iout_scale[page] = 1;
break;
case 2:
data->iout_scale[page] = 2;
break;
case 3:
data->iout_scale[page] = 4;
break;
case 4:
data->iout_scale[page] = 8;
break;
case 5:
data->iout_scale[page] = 16;
break;
default:
data->iout_scale[page] = 64;
break;
}
return 0;
}
static int mp2891_identify(struct i2c_client *client, struct pmbus_driver_info *info)
{
int ret;
/* Identify vout scale for rail 1. */
ret = mp2891_identify_vout_scale(client, info, 0);
if (ret < 0)
return ret;
/* Identify vout scale for rail 2. */
ret = mp2891_identify_vout_scale(client, info, 1);
if (ret < 0)
return ret;
/* Identify iout scale for rail 1. */
ret = mp2891_identify_iout_scale(client, info, 0);
if (ret < 0)
return ret;
/* Identify iout scale for rail 2. */
return mp2891_identify_iout_scale(client, info, 1);
}
static int mp2891_read_byte_data(struct i2c_client *client, int page, int reg)
{
int ret;
switch (reg) {
case PMBUS_VOUT_MODE:
/*
* The MP2891 does not follow standard PMBus protocol completely, the
* PMBUS_VOUT_MODE(0x20) in MP2891 is reserved and 0x00 is always
* returned when the register is read. But the calculation of vout in
* this driver is based on direct format. As a result, the format of
* vout is enforced to direct.
*/
ret = PB_VOUT_MODE_DIRECT;
break;
default:
ret = -ENODATA;
break;
}
return ret;
}
static int mp2891_read_word_data(struct i2c_client *client, int page,
int phase, int reg)
{
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
struct mp2891_data *data = to_mp2891_data(info);
int ret;
switch (reg) {
case PMBUS_READ_VIN:
ret = pmbus_read_word_data(client, page, phase, reg);
if (ret < 0)
return ret;
ret = ret & GENMASK(9, 0);
break;
case PMBUS_READ_IIN:
/*
* The MP2891 does not have standard PMBUS_READ_IIN register(0x89),
* the iin telemetry can be obtained through the vender redefined
* register READ_IIN_EST(0x95). The MP2891 PMBUS_READ_IIN register
* is linear11 format, But the pout scale is set to 1A/Lsb(using
* r/m/b scale). As a result, the iin read from MP2891 should be
* calculated to A, then return the result to pmbus core.
*/
ret = pmbus_read_word_data(client, page, phase, READ_IIN_EST);
if (ret < 0)
return ret;
ret = mp2891_reg2data_linear11(ret);
break;
case PMBUS_READ_PIN:
/*
* The MP2891 has standard PMBUS_READ_PIN register(0x97), but this
* is not used to read the input power per rail. The input power
* per rail is read through the vender redefined register
* READ_PIN_EST(0x94). The MP2891 PMBUS_READ_PIN register is linear11
* format, But the pout scale is set to 1W/Lsb(using r/m/b scale).
* As a result, the pin read from MP2891 should be calculated to W,
* then return the result to pmbus core.
*/
ret = pmbus_read_word_data(client, page, phase, READ_PIN_EST);
if (ret < 0)
return ret;
ret = mp2891_reg2data_linear11(ret);
break;
case PMBUS_READ_POUT:
/*
* The MP2891 PMBUS_READ_POUT register is linear11 format, and the
* exponent is not a constant value. But the pout scale is set to
* 1W/Lsb(using r/m/b scale). As a result, the pout read from MP2891
* should be calculated to W, then return the result to pmbus core.
*/
ret = pmbus_read_word_data(client, page, phase, reg);
if (ret < 0)
return ret;
ret = mp2891_reg2data_linear11(ret);
break;
case PMBUS_READ_VOUT:
case PMBUS_VOUT_UV_WARN_LIMIT:
ret = pmbus_read_word_data(client, page, phase, reg);
if (ret < 0)
return ret;
ret = DIV_ROUND_CLOSEST(ret * data->vout_scale[page], MP2891_VOUT_SCALE_DIV);
break;
case PMBUS_READ_IOUT:
ret = pmbus_read_word_data(client, page, phase, reg);
if (ret < 0)
return ret;
ret = DIV_ROUND_CLOSEST((ret & GENMASK(10, 0)) * data->iout_scale[page],
MP2891_IOUT_SCALE_DIV);
break;
case PMBUS_OT_FAULT_LIMIT:
case PMBUS_OT_WARN_LIMIT:
/*
* The scale of MP2891 PMBUS_OT_FAULT_LIMIT and PMBUS_OT_WARN_LIMIT
* is 1°C/LSB and they have 40°C offset.
*/
ret = pmbus_read_word_data(client, page, phase, reg);
if (ret < 0)
return ret;
ret = (ret & GENMASK(7, 0)) - MP2891_TEMP_LIMIT_OFFSET;
break;
case PMBUS_VIN_OV_FAULT_LIMIT:
/*
* The MP2891 PMBUS_VIN_OV_FAULT_LIMIT scale is 125mV/Lsb.
* but the vin scale is set to 31.25mV/Lsb(using r/m/b scale).
* As a result, the limit value should be multiplied by 4.
*/
ret = pmbus_read_word_data(client, page, phase, reg);
if (ret < 0)
return ret;
ret = (ret & GENMASK(7, 0)) * 4;
break;
case PMBUS_VOUT_UV_FAULT_LIMIT:
ret = pmbus_read_word_data(client, page, phase, reg);
if (ret < 0)
return ret;
if (FIELD_GET(GENMASK(11, 8), ret))
ret = FIELD_GET(GENMASK(7, 0), ret) * MP2891_UV_LIMIT_SCALE -
(FIELD_GET(GENMASK(11, 8), ret) + 1) * MP2891_OVUV_DELTA_SCALE;
else
ret = FIELD_GET(GENMASK(7, 0), ret) * MP2891_UV_LIMIT_SCALE;
ret = ret < 0 ? 0 : ret;
break;
case PMBUS_VOUT_OV_FAULT_LIMIT:
ret = pmbus_read_word_data(client, page, phase, reg);
if (ret < 0)
return ret;
if (FIELD_GET(GENMASK(11, 8), ret))
ret = FIELD_GET(GENMASK(7, 0), ret) * MP2891_OV_LIMIT_SCALE +
(FIELD_GET(GENMASK(11, 8), ret) + 1) * MP2891_OVUV_DELTA_SCALE;
else
ret = FIELD_GET(GENMASK(7, 0), ret) * MP2891_OV_LIMIT_SCALE;
break;
case PMBUS_IOUT_OC_WARN_LIMIT:
case PMBUS_IOUT_OC_FAULT_LIMIT:
ret = pmbus_read_word_data(client, page, phase, reg);
if (ret < 0)
return ret;
ret = DIV_ROUND_CLOSEST((ret & GENMASK(7, 0)) * data->iout_scale[page] *
MP2891_IOUT_LIMIT_UINT, MP2891_IOUT_SCALE_DIV);
break;
case PMBUS_IIN_OC_WARN_LIMIT:
/*
* The scale of PMBUS_IIN_OC_WARN_LIMIT is 0.5A/Lsb, but the iin scale
* is set to 1A/Lsb(using r/m/b scale), so the word data should be
* divided by 2.
*/
ret = pmbus_read_word_data(client, 0, phase, reg);
if (ret < 0)
return ret;
ret = DIV_ROUND_CLOSEST((ret & GENMASK(9, 0)), 2);
break;
case PMBUS_PIN_OP_WARN_LIMIT:
/*
* The scale of PMBUS_PIN_OP_WARN_LIMIT is 2W/Lsb, but the pin scale
* is set to 1W/Lsb(using r/m/b scale), so the word data should be
* multiplied by 2.
*/
ret = pmbus_read_word_data(client, 0, phase, reg);
if (ret < 0)
return ret;
ret = (ret & GENMASK(9, 0)) * MP2891_PIN_LIMIT_UINT;
break;
case PMBUS_READ_TEMPERATURE_1:
case PMBUS_VIN_UV_FAULT_LIMIT:
case PMBUS_VIN_UV_WARN_LIMIT:
ret = -ENODATA;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static int mp2891_write_word_data(struct i2c_client *client, int page, int reg,
u16 word)
{
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
struct mp2891_data *data = to_mp2891_data(info);
int ret;
switch (reg) {
case PMBUS_VOUT_UV_WARN_LIMIT:
ret = pmbus_write_word_data(client, page, reg,
DIV_ROUND_CLOSEST(word * MP2891_VOUT_SCALE_DIV,
data->vout_scale[page]));
break;
case PMBUS_VOUT_UV_FAULT_LIMIT:
/*
* The PMBUS_VOUT_UV_FAULT_LIMIT[7:0] is the limit value, and bit8-bit15
* should not be changed.
*/
ret = pmbus_read_word_data(client, page, 0xff, reg);
if (ret < 0)
return ret;
if (FIELD_GET(GENMASK(11, 8), ret))
ret = pmbus_write_word_data(client, page, reg,
(ret & ~GENMASK(7, 0)) |
FIELD_PREP(GENMASK(7, 0),
DIV_ROUND_CLOSEST(word +
(FIELD_GET(GENMASK(11, 8), ret) + 1) *
MP2891_OVUV_DELTA_SCALE,
MP2891_UV_LIMIT_SCALE)));
else
ret = pmbus_write_word_data(client, page, reg,
(ret & ~GENMASK(7, 0)) |
FIELD_PREP(GENMASK(7, 0),
DIV_ROUND_CLOSEST(word,
MP2891_UV_LIMIT_SCALE)));
break;
case PMBUS_VOUT_OV_FAULT_LIMIT:
/*
* The PMBUS_VOUT_OV_FAULT_LIMIT[7:0] is the limit value, and bit8-bit15
* should not be changed.
*/
ret = pmbus_read_word_data(client, page, 0xff, reg);
if (ret < 0)
return ret;
if (FIELD_GET(GENMASK(11, 8), ret))
ret = pmbus_write_word_data(client, page, reg,
(ret & ~GENMASK(7, 0)) |
FIELD_PREP(GENMASK(7, 0),
DIV_ROUND_CLOSEST(word -
(FIELD_GET(GENMASK(11, 8), ret) + 1) *
MP2891_OVUV_DELTA_SCALE,
MP2891_OV_LIMIT_SCALE)));
else
ret = pmbus_write_word_data(client, page, reg,
(ret & ~GENMASK(7, 0)) |
FIELD_PREP(GENMASK(7, 0),
DIV_ROUND_CLOSEST(word,
MP2891_OV_LIMIT_SCALE)));
break;
case PMBUS_VIN_OV_FAULT_LIMIT:
/*
* The PMBUS_VIN_OV_FAULT_LIMIT[7:0] is the limit value, and bit8-bit15
* should not be changed. The scale of PMBUS_VIN_OV_FAULT_LIMIT is 125mV/Lsb,
* but the vin scale is set to 31.25mV/Lsb(using r/m/b scale), so the word data
* should be divided by 4.
*/
ret = pmbus_read_word_data(client, page, 0xff, reg);
if (ret < 0)
return ret;
ret = pmbus_write_word_data(client, page, reg,
(ret & ~GENMASK(7, 0)) |
FIELD_PREP(GENMASK(7, 0),
DIV_ROUND_CLOSEST(word, 4)));
break;
case PMBUS_OT_FAULT_LIMIT:
case PMBUS_OT_WARN_LIMIT:
/*
* The scale of MP2891 PMBUS_OT_FAULT_LIMIT and PMBUS_OT_WARN_LIMIT
* have 40°C offset. The bit0-bit7 is the limit value, and bit8-bit15
* should not be changed.
*/
ret = pmbus_read_word_data(client, page, 0xff, reg);
if (ret < 0)
return ret;
ret = pmbus_write_word_data(client, page, reg,
(ret & ~GENMASK(7, 0)) |
FIELD_PREP(GENMASK(7, 0), word + MP2891_TEMP_LIMIT_OFFSET));
break;
case PMBUS_IOUT_OC_WARN_LIMIT:
case PMBUS_IOUT_OC_FAULT_LIMIT:
ret = pmbus_write_word_data(client, page, reg,
DIV_ROUND_CLOSEST(word * MP2891_IOUT_SCALE_DIV,
MP2891_IOUT_LIMIT_UINT *
data->iout_scale[page]));
break;
case PMBUS_IIN_OC_WARN_LIMIT:
/*
* The scale of PMBUS_IIN_OC_WARN_LIMIT is 0.5A/Lsb, but the iin scale
* is set to 1A/Lsb(using r/m/b scale), so the word data should be
* multiplied by 2.
*/
ret = pmbus_write_word_data(client, page, reg, word * 2);
break;
case PMBUS_PIN_OP_WARN_LIMIT:
/*
* The scale of PMBUS_PIN_OP_WARN_LIMIT is 2W/Lsb, but the pin scale
* is set to 1W/Lsb(using r/m/b scale), so the word data should be
* divided by 2.
*/
ret = pmbus_write_word_data(client, page, reg,
DIV_ROUND_CLOSEST(word, MP2891_PIN_LIMIT_UINT));
break;
case PMBUS_VIN_UV_FAULT_LIMIT:
case PMBUS_VIN_UV_WARN_LIMIT:
ret = -ENODATA;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static const struct pmbus_driver_info mp2891_info = {
.pages = MP2891_PAGE_NUM,
.format[PSC_VOLTAGE_IN] = direct,
.format[PSC_CURRENT_IN] = direct,
.format[PSC_CURRENT_OUT] = direct,
.format[PSC_TEMPERATURE] = direct,
.format[PSC_POWER] = direct,
.format[PSC_VOLTAGE_OUT] = direct,
/* set vin scale 31.25mV/Lsb */
.m[PSC_VOLTAGE_IN] = 32,
.R[PSC_VOLTAGE_IN] = 0,
.b[PSC_VOLTAGE_IN] = 0,
/* set temp scale 1000m°C/Lsb */
.m[PSC_TEMPERATURE] = 1,
.R[PSC_TEMPERATURE] = 0,
.b[PSC_TEMPERATURE] = 0,
.m[PSC_CURRENT_IN] = 1,
.R[PSC_CURRENT_IN] = 0,
.b[PSC_CURRENT_IN] = 0,
.m[PSC_CURRENT_OUT] = 1,
.R[PSC_CURRENT_OUT] = 0,
.b[PSC_CURRENT_OUT] = 0,
.m[PSC_POWER] = 1,
.R[PSC_POWER] = 0,
.b[PSC_POWER] = 0,
.m[PSC_VOLTAGE_OUT] = 1,
.R[PSC_VOLTAGE_OUT] = 3,
.b[PSC_VOLTAGE_OUT] = 0,
.func[0] = MP2891_RAIL1_FUNC,
.func[1] = MP2891_RAIL2_FUNC,
.read_word_data = mp2891_read_word_data,
.write_word_data = mp2891_write_word_data,
.read_byte_data = mp2891_read_byte_data,
.identify = mp2891_identify,
};
static int mp2891_probe(struct i2c_client *client)
{
struct mp2891_data *data;
data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
memcpy(&data->info, &mp2891_info, sizeof(mp2891_info));
return pmbus_do_probe(client, &data->info);
}
static const struct i2c_device_id mp2891_id[] = {
{"mp2891", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, mp2891_id);
static const struct of_device_id __maybe_unused mp2891_of_match[] = {
{.compatible = "mps,mp2891"},
{}
};
MODULE_DEVICE_TABLE(of, mp2891_of_match);
static struct i2c_driver mp2891_driver = {
.driver = {
.name = "mp2891",
.of_match_table = mp2891_of_match,
},
.probe = mp2891_probe,
.id_table = mp2891_id,
};
module_i2c_driver(mp2891_driver);
MODULE_AUTHOR("Noah Wang <noahwang.wang@outlook.com>");
MODULE_DESCRIPTION("PMBus driver for MPS MP2891");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(PMBUS);

View File

@ -0,0 +1,261 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Hardware monitoring driver for MPS Multi-phase Digital VR Controllers(MP2993)
*/
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include "pmbus.h"
#define MP2993_VOUT_OVUV_UINT 125
#define MP2993_VOUT_OVUV_DIV 64
#define MP2993_VIN_LIMIT_UINT 1
#define MP2993_VIN_LIMIT_DIV 8
#define MP2993_READ_VIN_UINT 1
#define MP2993_READ_VIN_DIV 32
#define MP2993_PAGE_NUM 2
#define MP2993_RAIL1_FUNC (PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | \
PMBUS_HAVE_IOUT | PMBUS_HAVE_POUT | \
PMBUS_HAVE_TEMP | PMBUS_HAVE_PIN | \
PMBUS_HAVE_IIN | \
PMBUS_HAVE_STATUS_VOUT | \
PMBUS_HAVE_STATUS_IOUT | \
PMBUS_HAVE_STATUS_TEMP | \
PMBUS_HAVE_STATUS_INPUT)
#define MP2993_RAIL2_FUNC (PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | \
PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP | \
PMBUS_HAVE_STATUS_VOUT | \
PMBUS_HAVE_STATUS_IOUT | \
PMBUS_HAVE_STATUS_TEMP | \
PMBUS_HAVE_STATUS_INPUT)
/* Converts a linear11 data exponent to a specified value */
static u16 mp2993_linear11_exponent_transfer(u16 word, u16 expect_exponent)
{
s16 exponent, mantissa, target_exponent;
exponent = ((s16)word) >> 11;
mantissa = ((s16)((word & 0x7ff) << 5)) >> 5;
target_exponent = (s16)((expect_exponent & 0x1f) << 11) >> 11;
if (exponent > target_exponent)
mantissa = mantissa << (exponent - target_exponent);
else
mantissa = mantissa >> (target_exponent - exponent);
return (mantissa & 0x7ff) | ((expect_exponent << 11) & 0xf800);
}
static int
mp2993_set_vout_format(struct i2c_client *client, int page, int format)
{
int ret;
ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page);
if (ret < 0)
return ret;
return i2c_smbus_write_byte_data(client, PMBUS_VOUT_MODE, format);
}
static int mp2993_identify(struct i2c_client *client, struct pmbus_driver_info *info)
{
int ret;
/* Set vout to direct format for rail1. */
ret = mp2993_set_vout_format(client, 0, PB_VOUT_MODE_DIRECT);
if (ret < 0)
return ret;
/* Set vout to direct format for rail2. */
return mp2993_set_vout_format(client, 1, PB_VOUT_MODE_DIRECT);
}
static int mp2993_read_word_data(struct i2c_client *client, int page, int phase,
int reg)
{
int ret;
switch (reg) {
case PMBUS_VOUT_OV_FAULT_LIMIT:
case PMBUS_VOUT_UV_FAULT_LIMIT:
ret = pmbus_read_word_data(client, page, phase, reg);
if (ret < 0)
return ret;
ret = DIV_ROUND_CLOSEST(ret * MP2993_VOUT_OVUV_UINT, MP2993_VOUT_OVUV_DIV);
break;
case PMBUS_OT_FAULT_LIMIT:
case PMBUS_OT_WARN_LIMIT:
/*
* The MP2993 ot fault limit value and ot warn limit value
* per rail are always the same, so only PMBUS_OT_FAULT_LIMIT
* and PMBUS_OT_WARN_LIMIT register in page 0 are defined to
* indicates the limit value.
*/
ret = pmbus_read_word_data(client, 0, phase, reg);
break;
case PMBUS_READ_VIN:
/* The MP2993 vin scale is (1/32V)/Lsb */
ret = pmbus_read_word_data(client, page, phase, reg);
if (ret < 0)
return ret;
ret = DIV_ROUND_CLOSEST((ret & GENMASK(9, 0)) * MP2993_READ_VIN_UINT,
MP2993_READ_VIN_DIV);
break;
case PMBUS_VIN_OV_FAULT_LIMIT:
case PMBUS_VIN_OV_WARN_LIMIT:
case PMBUS_VIN_UV_WARN_LIMIT:
case PMBUS_VIN_UV_FAULT_LIMIT:
/* The MP2993 vin limit scale is (1/8V)/Lsb */
ret = pmbus_read_word_data(client, page, phase, reg);
if (ret < 0)
return ret;
ret = DIV_ROUND_CLOSEST((ret & GENMASK(7, 0)) * MP2993_VIN_LIMIT_UINT,
MP2993_VIN_LIMIT_DIV);
break;
case PMBUS_READ_IOUT:
case PMBUS_READ_IIN:
case PMBUS_IIN_OC_WARN_LIMIT:
case PMBUS_IOUT_OC_FAULT_LIMIT:
case PMBUS_IOUT_OC_WARN_LIMIT:
case PMBUS_READ_VOUT:
case PMBUS_READ_PIN:
case PMBUS_READ_POUT:
case PMBUS_READ_TEMPERATURE_1:
ret = -ENODATA;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static int mp2993_write_word_data(struct i2c_client *client, int page, int reg,
u16 word)
{
int ret;
switch (reg) {
case PMBUS_VOUT_OV_FAULT_LIMIT:
case PMBUS_VOUT_UV_FAULT_LIMIT:
ret = DIV_ROUND_CLOSEST(word * MP2993_VOUT_OVUV_DIV, MP2993_VOUT_OVUV_UINT);
ret = pmbus_write_word_data(client, 0, reg, ret);
break;
case PMBUS_OT_FAULT_LIMIT:
case PMBUS_OT_WARN_LIMIT:
/*
* The MP2993 ot fault limit value and ot warn limit value
* per rail are always the same, so only PMBUS_OT_FAULT_LIMIT
* and PMBUS_OT_WARN_LIMIT register in page 0 are defined to
* config the ot limit value.
*/
ret = pmbus_write_word_data(client, 0, reg, word);
break;
case PMBUS_VIN_OV_FAULT_LIMIT:
case PMBUS_VIN_OV_WARN_LIMIT:
case PMBUS_VIN_UV_WARN_LIMIT:
case PMBUS_VIN_UV_FAULT_LIMIT:
/* The MP2993 vin limit scale is (1/8V)/Lsb */
ret = pmbus_write_word_data(client, 0, reg,
DIV_ROUND_CLOSEST(word * MP2993_VIN_LIMIT_DIV,
MP2993_VIN_LIMIT_UINT));
break;
case PMBUS_IIN_OC_WARN_LIMIT:
/*
* The PMBUS_IIN_OC_WARN_LIMIT of MP2993 is linear11 format,
* and the exponent is a constant value(5'b00000) so the
* exponent of word parameter should be converted to 5'b00000.
*/
ret = pmbus_write_word_data(client, page, reg,
mp2993_linear11_exponent_transfer(word, 0x00));
break;
//
case PMBUS_IOUT_OC_FAULT_LIMIT:
case PMBUS_IOUT_OC_WARN_LIMIT:
/*
* The PMBUS_IOUT_OC_FAULT_LIMIT and PMBUS_IOUT_OC_WARN_LIMIT
* of MP2993 can be regarded as linear11 format, and the
* exponent is a 5'b00001 or 5'b00000. To ensure a larger
* range of limit value, so the exponent of word parameter
* should be converted to 5'b00001.
*/
ret = pmbus_write_word_data(client, page, reg,
mp2993_linear11_exponent_transfer(word, 0x01));
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static struct pmbus_driver_info mp2993_info = {
.pages = MP2993_PAGE_NUM,
.format[PSC_VOLTAGE_IN] = direct,
.format[PSC_CURRENT_IN] = linear,
.format[PSC_CURRENT_OUT] = linear,
.format[PSC_TEMPERATURE] = direct,
.format[PSC_POWER] = linear,
.format[PSC_VOLTAGE_OUT] = direct,
.m[PSC_VOLTAGE_OUT] = 1,
.R[PSC_VOLTAGE_OUT] = 3,
.b[PSC_VOLTAGE_OUT] = 0,
.m[PSC_VOLTAGE_IN] = 1,
.R[PSC_VOLTAGE_IN] = 0,
.b[PSC_VOLTAGE_IN] = 0,
.m[PSC_TEMPERATURE] = 1,
.R[PSC_TEMPERATURE] = 0,
.b[PSC_TEMPERATURE] = 0,
.func[0] = MP2993_RAIL1_FUNC,
.func[1] = MP2993_RAIL2_FUNC,
.read_word_data = mp2993_read_word_data,
.write_word_data = mp2993_write_word_data,
.identify = mp2993_identify,
};
static int mp2993_probe(struct i2c_client *client)
{
return pmbus_do_probe(client, &mp2993_info);
}
static const struct i2c_device_id mp2993_id[] = {
{"mp2993", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, mp2993_id);
static const struct of_device_id __maybe_unused mp2993_of_match[] = {
{.compatible = "mps,mp2993"},
{}
};
MODULE_DEVICE_TABLE(of, mp2993_of_match);
static struct i2c_driver mp2993_driver = {
.driver = {
.name = "mp2993",
.of_match_table = mp2993_of_match,
},
.probe = mp2993_probe,
.id_table = mp2993_id,
};
module_i2c_driver(mp2993_driver);
MODULE_AUTHOR("Noah Wang <noahwang.wang@outlook.com>");
MODULE_DESCRIPTION("PMBus driver for MPS MP2993");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(PMBUS);

View File

@ -0,0 +1,90 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Hardware monitoring driver for MP5920 and compatible chips.
*/
#include <linux/i2c.h>
#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include "pmbus.h"
static struct pmbus_driver_info mp5920_info = {
.pages = 1,
.format[PSC_VOLTAGE_IN] = direct,
.format[PSC_VOLTAGE_OUT] = direct,
.format[PSC_CURRENT_OUT] = direct,
.format[PSC_POWER] = direct,
.format[PSC_TEMPERATURE] = direct,
.m[PSC_VOLTAGE_IN] = 2266,
.b[PSC_VOLTAGE_IN] = 0,
.R[PSC_VOLTAGE_IN] = -1,
.m[PSC_VOLTAGE_OUT] = 2266,
.b[PSC_VOLTAGE_OUT] = 0,
.R[PSC_VOLTAGE_OUT] = -1,
.m[PSC_CURRENT_OUT] = 546,
.b[PSC_CURRENT_OUT] = 0,
.R[PSC_CURRENT_OUT] = -2,
.m[PSC_POWER] = 5840,
.b[PSC_POWER] = 0,
.R[PSC_POWER] = -3,
.m[PSC_TEMPERATURE] = 1067,
.b[PSC_TEMPERATURE] = 20500,
.R[PSC_TEMPERATURE] = -2,
.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT |
PMBUS_HAVE_IOUT | PMBUS_HAVE_POUT |
PMBUS_HAVE_TEMP,
};
static int mp5920_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
int ret;
u8 buf[I2C_SMBUS_BLOCK_MAX];
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_READ_WORD_DATA))
return -ENODEV;
ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf);
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to read PMBUS_MFR_MODEL\n");
if (ret != 6 || strncmp(buf, "MP5920", 6)) {
return dev_err_probe(dev, -ENODEV, "Model '%.*s' not supported\n",
min_t(int, ret, sizeof(buf)), buf);
}
return pmbus_do_probe(client, &mp5920_info);
}
static const struct of_device_id mp5920_of_match[] = {
{ .compatible = "mps,mp5920" },
{ }
};
MODULE_DEVICE_TABLE(of, mp5920_of_match);
static const struct i2c_device_id mp5920_id[] = {
{ "mp5920" },
{ }
};
MODULE_DEVICE_TABLE(i2c, mp5920_id);
static struct i2c_driver mp5920_driver = {
.driver = {
.name = "mp5920",
.of_match_table = mp5920_of_match,
},
.probe = mp5920_probe,
.id_table = mp5920_id,
};
module_i2c_driver(mp5920_driver);
MODULE_AUTHOR("Tony Ao <tony_ao@wiwynn.com>");
MODULE_AUTHOR("Alex Vdovydchenko <xzeol@yahoo.com>");
MODULE_DESCRIPTION("PMBus driver for MP5920 HSC");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(PMBUS);

View File

@ -0,0 +1,319 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Hardware monitoring driver for MPS Multi-phase Digital VR Controllers(MP9941)
*/
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include "pmbus.h"
/*
* Vender specific registers. The MFR_ICC_MAX(0x02) is used to
* config the iin scale. The MFR_RESO_SET(0xC7) is used to
* config the vout format. The MFR_VR_MULTI_CONFIG_R1(0x0D) is
* used to identify the vout vid step.
*/
#define MFR_ICC_MAX 0x02
#define MFR_RESO_SET 0xC7
#define MFR_VR_MULTI_CONFIG_R1 0x0D
#define MP9941_VIN_LIMIT_UINT 1
#define MP9941_VIN_LIMIT_DIV 8
#define MP9941_READ_VIN_UINT 1
#define MP9941_READ_VIN_DIV 32
#define MP9941_PAGE_NUM 1
#define MP9941_RAIL1_FUNC (PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | \
PMBUS_HAVE_IOUT | PMBUS_HAVE_POUT | \
PMBUS_HAVE_TEMP | PMBUS_HAVE_PIN | \
PMBUS_HAVE_IIN | \
PMBUS_HAVE_STATUS_VOUT | \
PMBUS_HAVE_STATUS_IOUT | \
PMBUS_HAVE_STATUS_TEMP | \
PMBUS_HAVE_STATUS_INPUT)
struct mp9941_data {
struct pmbus_driver_info info;
int vid_resolution;
};
#define to_mp9941_data(x) container_of(x, struct mp9941_data, info)
static int mp9941_set_vout_format(struct i2c_client *client)
{
int ret;
ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0);
if (ret < 0)
return ret;
ret = i2c_smbus_read_word_data(client, MFR_RESO_SET);
if (ret < 0)
return ret;
/*
* page = 0, MFR_RESO_SET[7:6] defines the vout format
* 2'b11 set the vout format as direct
*/
ret = (ret & ~GENMASK(7, 6)) | FIELD_PREP(GENMASK(7, 6), 3);
return i2c_smbus_write_word_data(client, MFR_RESO_SET, ret);
}
static int
mp9941_identify_vid_resolution(struct i2c_client *client, struct pmbus_driver_info *info)
{
struct mp9941_data *data = to_mp9941_data(info);
int ret;
/*
* page = 2, MFR_VR_MULTI_CONFIG_R1[4:4] defines rail1 vid step value
* 1'b0 represents the vid step value is 10mV
* 1'b1 represents the vid step value is 5mV
*/
ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 2);
if (ret < 0)
return ret;
ret = i2c_smbus_read_word_data(client, MFR_VR_MULTI_CONFIG_R1);
if (ret < 0)
return ret;
if (FIELD_GET(GENMASK(4, 4), ret))
data->vid_resolution = 5;
else
data->vid_resolution = 10;
return 0;
}
static int mp9941_identify_iin_scale(struct i2c_client *client)
{
int ret;
ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0);
if (ret < 0)
return ret;
ret = i2c_smbus_read_word_data(client, MFR_RESO_SET);
if (ret < 0)
return ret;
ret = (ret & ~GENMASK(3, 2)) | FIELD_PREP(GENMASK(3, 2), 0);
ret = i2c_smbus_write_word_data(client, MFR_RESO_SET, ret);
if (ret < 0)
return ret;
/*
* page = 2, MFR_ICC_MAX[15:13] defines the iin scale
* 3'b000 set the iout scale as 0.5A/Lsb
*/
ret = i2c_smbus_write_byte_data(client, PMBUS_PAGE, 2);
if (ret < 0)
return ret;
ret = i2c_smbus_read_word_data(client, MFR_ICC_MAX);
if (ret < 0)
return ret;
ret = (ret & ~GENMASK(15, 13)) | FIELD_PREP(GENMASK(15, 13), 0);
return i2c_smbus_write_word_data(client, MFR_ICC_MAX, ret);
}
static int mp9941_identify(struct i2c_client *client, struct pmbus_driver_info *info)
{
int ret;
ret = mp9941_identify_iin_scale(client);
if (ret < 0)
return ret;
ret = mp9941_identify_vid_resolution(client, info);
if (ret < 0)
return ret;
return mp9941_set_vout_format(client);
}
static int mp9941_read_word_data(struct i2c_client *client, int page, int phase,
int reg)
{
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
struct mp9941_data *data = to_mp9941_data(info);
int ret;
switch (reg) {
case PMBUS_READ_VIN:
/* The MP9941 vin scale is (1/32V)/Lsb */
ret = pmbus_read_word_data(client, page, phase, reg);
if (ret < 0)
return ret;
ret = DIV_ROUND_CLOSEST((ret & GENMASK(9, 0)) * MP9941_READ_VIN_UINT,
MP9941_READ_VIN_DIV);
break;
case PMBUS_READ_IIN:
ret = pmbus_read_word_data(client, page, phase, reg);
if (ret < 0)
return ret;
ret = ret & GENMASK(10, 0);
break;
case PMBUS_VIN_OV_FAULT_LIMIT:
/* The MP9941 vin ov limit scale is (1/8V)/Lsb */
ret = pmbus_read_word_data(client, page, phase, reg);
if (ret < 0)
return ret;
ret = DIV_ROUND_CLOSEST((ret & GENMASK(7, 0)) * MP9941_VIN_LIMIT_UINT,
MP9941_VIN_LIMIT_DIV);
break;
case PMBUS_IIN_OC_WARN_LIMIT:
ret = pmbus_read_word_data(client, page, phase, reg);
if (ret < 0)
return ret;
ret = ret & GENMASK(7, 0);
break;
case PMBUS_VOUT_UV_FAULT_LIMIT:
case PMBUS_MFR_VOUT_MIN:
case PMBUS_MFR_VOUT_MAX:
/*
* The vout scale is set to 1mV/Lsb(using r/m/b scale).
* But the vout uv limit and vout max/min scale is 1VID/Lsb,
* so the vout uv limit and vout max/min value should be
* multiplied by vid resolution.
*/
ret = pmbus_read_word_data(client, page, phase, reg);
if (ret < 0)
return ret;
ret = ret * data->vid_resolution;
break;
case PMBUS_READ_IOUT:
case PMBUS_READ_POUT:
case PMBUS_READ_TEMPERATURE_1:
case PMBUS_READ_VOUT:
case PMBUS_READ_PIN:
case PMBUS_OT_FAULT_LIMIT:
case PMBUS_OT_WARN_LIMIT:
ret = -ENODATA;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static int mp9941_write_word_data(struct i2c_client *client, int page, int reg,
u16 word)
{
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
struct mp9941_data *data = to_mp9941_data(info);
int ret;
switch (reg) {
case PMBUS_VIN_OV_FAULT_LIMIT:
/* The MP9941 vin ov limit scale is (1/8V)/Lsb */
ret = pmbus_write_word_data(client, page, reg,
DIV_ROUND_CLOSEST(word * MP9941_VIN_LIMIT_DIV,
MP9941_VIN_LIMIT_UINT));
break;
case PMBUS_VOUT_UV_FAULT_LIMIT:
case PMBUS_MFR_VOUT_MIN:
case PMBUS_MFR_VOUT_MAX:
ret = pmbus_write_word_data(client, page, reg,
DIV_ROUND_CLOSEST(word, data->vid_resolution));
break;
case PMBUS_IIN_OC_WARN_LIMIT:
case PMBUS_OT_FAULT_LIMIT:
case PMBUS_OT_WARN_LIMIT:
ret = -ENODATA;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static const struct pmbus_driver_info mp9941_info = {
.pages = MP9941_PAGE_NUM,
.format[PSC_VOLTAGE_IN] = direct,
.format[PSC_CURRENT_IN] = direct,
.format[PSC_CURRENT_OUT] = linear,
.format[PSC_POWER] = linear,
.format[PSC_TEMPERATURE] = direct,
.format[PSC_VOLTAGE_OUT] = direct,
.m[PSC_TEMPERATURE] = 1,
.R[PSC_TEMPERATURE] = 0,
.b[PSC_TEMPERATURE] = 0,
.m[PSC_VOLTAGE_IN] = 1,
.R[PSC_VOLTAGE_IN] = 0,
.b[PSC_VOLTAGE_IN] = 0,
.m[PSC_CURRENT_IN] = 2,
.R[PSC_CURRENT_IN] = 0,
.b[PSC_CURRENT_IN] = 0,
.m[PSC_VOLTAGE_OUT] = 1,
.R[PSC_VOLTAGE_OUT] = 3,
.b[PSC_VOLTAGE_OUT] = 0,
.func[0] = MP9941_RAIL1_FUNC,
.read_word_data = mp9941_read_word_data,
.write_word_data = mp9941_write_word_data,
.identify = mp9941_identify,
};
static int mp9941_probe(struct i2c_client *client)
{
struct mp9941_data *data;
data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
memcpy(&data->info, &mp9941_info, sizeof(mp9941_info));
return pmbus_do_probe(client, &data->info);
}
static const struct i2c_device_id mp9941_id[] = {
{"mp9941", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, mp9941_id);
static const struct of_device_id __maybe_unused mp9941_of_match[] = {
{.compatible = "mps,mp9941"},
{}
};
MODULE_DEVICE_TABLE(of, mp9941_of_match);
static struct i2c_driver mp9941_driver = {
.driver = {
.name = "mp9941",
.of_match_table = mp9941_of_match,
},
.probe = mp9941_probe,
.id_table = mp9941_id,
};
module_i2c_driver(mp9941_driver);
MODULE_AUTHOR("Noah Wang <noahwang.wang@outlook.com>");
MODULE_DESCRIPTION("PMBus driver for MPS MP9941");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(PMBUS);

View File

@ -279,12 +279,11 @@ static const struct hwmon_chip_info powr1220_chip_info = {
.info = powr1220_info,
};
static const struct i2c_device_id powr1220_ids[];
static int powr1220_probe(struct i2c_client *client)
{
struct powr1220_data *data;
struct device *hwmon_dev;
enum powr1xxx_chips chip;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
@ -293,7 +292,8 @@ static int powr1220_probe(struct i2c_client *client)
if (!data)
return -ENOMEM;
switch (i2c_match_id(powr1220_ids, client)->driver_data) {
chip = (uintptr_t)i2c_get_match_data(client);
switch (chip) {
case powr1014:
data->max_channels = 10;
break;

View File

@ -882,15 +882,6 @@ static const struct hwmon_chip_info sht3x_chip_info = {
.info = sht3x_channel_info,
};
/* device ID table */
static const struct i2c_device_id sht3x_ids[] = {
{"sht3x", sht3x},
{"sts3x", sts3x},
{}
};
MODULE_DEVICE_TABLE(i2c, sht3x_ids);
static int sht3x_probe(struct i2c_client *client)
{
int ret;
@ -920,7 +911,7 @@ static int sht3x_probe(struct i2c_client *client)
data->mode = 0;
data->last_update = jiffies - msecs_to_jiffies(3000);
data->client = client;
data->chip_id = i2c_match_id(sht3x_ids, client)->driver_data;
data->chip_id = (uintptr_t)i2c_get_match_data(client);
crc8_populate_msb(sht3x_crc8_table, SHT3X_CRC8_POLYNOMIAL);
sht3x_select_command(data);
@ -963,6 +954,15 @@ static int sht3x_probe(struct i2c_client *client)
return PTR_ERR_OR_ZERO(hwmon_dev);
}
/* device ID table */
static const struct i2c_device_id sht3x_ids[] = {
{"sht3x", sht3x},
{"sts3x", sts3x},
{}
};
MODULE_DEVICE_TABLE(i2c, sht3x_ids);
static struct i2c_driver sht3x_i2c_driver = {
.driver.name = "sht3x",
.probe = sht3x_probe,

View File

@ -186,8 +186,6 @@ static void shtc1_select_command(struct shtc1_data *data)
}
}
static const struct i2c_device_id shtc1_id[];
static int shtc1_probe(struct i2c_client *client)
{
int ret;
@ -195,7 +193,7 @@ static int shtc1_probe(struct i2c_client *client)
char id_reg_buf[2];
struct shtc1_data *data;
struct device *hwmon_dev;
enum shtcx_chips chip = i2c_match_id(shtc1_id, client)->driver_data;
enum shtcx_chips chip = (uintptr_t)i2c_get_match_data(client);
struct i2c_adapter *adap = client->adapter;
struct device *dev = &client->dev;
struct device_node *np = dev->of_node;

703
drivers/hwmon/spd5118.c Normal file
View File

@ -0,0 +1,703 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Jedec 5118 compliant temperature sensors
*
* Derived from https://github.com/Steve-Tech/SPD5118-DKMS
* Originally from T/2 driver at https://t2sde.org/packages/linux
* Copyright (c) 2023 René Rebe, ExactCODE GmbH; Germany.
*
* Copyright (c) 2024 Guenter Roeck
*
* Inspired by ee1004.c and jc42.c.
*
* SPD5118 compliant temperature sensors are typically used on DDR5
* memory modules.
*/
#include <linux/bitops.h>
#include <linux/bits.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/hwmon.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/nvmem-provider.h>
#include <linux/pm.h>
#include <linux/regmap.h>
#include <linux/units.h>
/* Addresses to scan */
static const unsigned short normal_i2c[] = {
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, I2C_CLIENT_END };
/* SPD5118 registers. */
#define SPD5118_REG_TYPE 0x00 /* MR0:MR1 */
#define SPD5118_REG_REVISION 0x02 /* MR2 */
#define SPD5118_REG_VENDOR 0x03 /* MR3:MR4 */
#define SPD5118_REG_CAPABILITY 0x05 /* MR5 */
#define SPD5118_REG_I2C_LEGACY_MODE 0x0B /* MR11 */
#define SPD5118_REG_TEMP_CLR 0x13 /* MR19 */
#define SPD5118_REG_ERROR_CLR 0x14 /* MR20 */
#define SPD5118_REG_TEMP_CONFIG 0x1A /* MR26 */
#define SPD5118_REG_TEMP_MAX 0x1c /* MR28:MR29 */
#define SPD5118_REG_TEMP_MIN 0x1e /* MR30:MR31 */
#define SPD5118_REG_TEMP_CRIT 0x20 /* MR32:MR33 */
#define SPD5118_REG_TEMP_LCRIT 0x22 /* MR34:MR35 */
#define SPD5118_REG_TEMP 0x31 /* MR49:MR50 */
#define SPD5118_REG_TEMP_STATUS 0x33 /* MR51 */
#define SPD5118_TEMP_STATUS_HIGH BIT(0)
#define SPD5118_TEMP_STATUS_LOW BIT(1)
#define SPD5118_TEMP_STATUS_CRIT BIT(2)
#define SPD5118_TEMP_STATUS_LCRIT BIT(3)
#define SPD5118_CAP_TS_SUPPORT BIT(1) /* temperature sensor support */
#define SPD5118_TS_DISABLE BIT(0) /* temperature sensor disable */
#define SPD5118_LEGACY_MODE_ADDR BIT(3)
#define SPD5118_LEGACY_PAGE_MASK GENMASK(2, 0)
#define SPD5118_LEGACY_MODE_MASK (SPD5118_LEGACY_MODE_ADDR | SPD5118_LEGACY_PAGE_MASK)
#define SPD5118_NUM_PAGES 8
#define SPD5118_PAGE_SIZE 128
#define SPD5118_PAGE_SHIFT 7
#define SPD5118_PAGE_MASK GENMASK(6, 0)
#define SPD5118_EEPROM_BASE 0x80
#define SPD5118_EEPROM_SIZE (SPD5118_PAGE_SIZE * SPD5118_NUM_PAGES)
/* Temperature unit in millicelsius */
#define SPD5118_TEMP_UNIT (MILLIDEGREE_PER_DEGREE / 4)
/* Representable temperature range in millicelsius */
#define SPD5118_TEMP_RANGE_MIN -256000
#define SPD5118_TEMP_RANGE_MAX 255750
struct spd5118_data {
struct regmap *regmap;
struct mutex nvmem_lock;
};
/* hwmon */
static int spd5118_temp_from_reg(u16 reg)
{
int temp = sign_extend32(reg >> 2, 10);
return temp * SPD5118_TEMP_UNIT;
}
static u16 spd5118_temp_to_reg(long temp)
{
temp = clamp_val(temp, SPD5118_TEMP_RANGE_MIN, SPD5118_TEMP_RANGE_MAX);
return (DIV_ROUND_CLOSEST(temp, SPD5118_TEMP_UNIT) & 0x7ff) << 2;
}
static int spd5118_read_temp(struct regmap *regmap, u32 attr, long *val)
{
int reg, err;
u8 regval[2];
u16 temp;
switch (attr) {
case hwmon_temp_input:
reg = SPD5118_REG_TEMP;
break;
case hwmon_temp_max:
reg = SPD5118_REG_TEMP_MAX;
break;
case hwmon_temp_min:
reg = SPD5118_REG_TEMP_MIN;
break;
case hwmon_temp_crit:
reg = SPD5118_REG_TEMP_CRIT;
break;
case hwmon_temp_lcrit:
reg = SPD5118_REG_TEMP_LCRIT;
break;
default:
return -EOPNOTSUPP;
}
err = regmap_bulk_read(regmap, reg, regval, 2);
if (err)
return err;
temp = (regval[1] << 8) | regval[0];
*val = spd5118_temp_from_reg(temp);
return 0;
}
static int spd5118_read_alarm(struct regmap *regmap, u32 attr, long *val)
{
unsigned int mask, regval;
int err;
switch (attr) {
case hwmon_temp_max_alarm:
mask = SPD5118_TEMP_STATUS_HIGH;
break;
case hwmon_temp_min_alarm:
mask = SPD5118_TEMP_STATUS_LOW;
break;
case hwmon_temp_crit_alarm:
mask = SPD5118_TEMP_STATUS_CRIT;
break;
case hwmon_temp_lcrit_alarm:
mask = SPD5118_TEMP_STATUS_LCRIT;
break;
default:
return -EOPNOTSUPP;
}
err = regmap_read(regmap, SPD5118_REG_TEMP_STATUS, &regval);
if (err < 0)
return err;
*val = !!(regval & mask);
if (*val)
return regmap_write(regmap, SPD5118_REG_TEMP_CLR, mask);
return 0;
}
static int spd5118_read_enable(struct regmap *regmap, long *val)
{
u32 regval;
int err;
err = regmap_read(regmap, SPD5118_REG_TEMP_CONFIG, &regval);
if (err < 0)
return err;
*val = !(regval & SPD5118_TS_DISABLE);
return 0;
}
static int spd5118_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
struct regmap *regmap = dev_get_drvdata(dev);
if (type != hwmon_temp)
return -EOPNOTSUPP;
switch (attr) {
case hwmon_temp_input:
case hwmon_temp_max:
case hwmon_temp_min:
case hwmon_temp_crit:
case hwmon_temp_lcrit:
return spd5118_read_temp(regmap, attr, val);
case hwmon_temp_max_alarm:
case hwmon_temp_min_alarm:
case hwmon_temp_crit_alarm:
case hwmon_temp_lcrit_alarm:
return spd5118_read_alarm(regmap, attr, val);
case hwmon_temp_enable:
return spd5118_read_enable(regmap, val);
default:
return -EOPNOTSUPP;
}
}
static int spd5118_write_temp(struct regmap *regmap, u32 attr, long val)
{
u8 regval[2];
u16 temp;
int reg;
switch (attr) {
case hwmon_temp_max:
reg = SPD5118_REG_TEMP_MAX;
break;
case hwmon_temp_min:
reg = SPD5118_REG_TEMP_MIN;
break;
case hwmon_temp_crit:
reg = SPD5118_REG_TEMP_CRIT;
break;
case hwmon_temp_lcrit:
reg = SPD5118_REG_TEMP_LCRIT;
break;
default:
return -EOPNOTSUPP;
}
temp = spd5118_temp_to_reg(val);
regval[0] = temp & 0xff;
regval[1] = temp >> 8;
return regmap_bulk_write(regmap, reg, regval, 2);
}
static int spd5118_write_enable(struct regmap *regmap, long val)
{
if (val && val != 1)
return -EINVAL;
return regmap_update_bits(regmap, SPD5118_REG_TEMP_CONFIG,
SPD5118_TS_DISABLE,
val ? 0 : SPD5118_TS_DISABLE);
}
static int spd5118_temp_write(struct regmap *regmap, u32 attr, long val)
{
switch (attr) {
case hwmon_temp_max:
case hwmon_temp_min:
case hwmon_temp_crit:
case hwmon_temp_lcrit:
return spd5118_write_temp(regmap, attr, val);
case hwmon_temp_enable:
return spd5118_write_enable(regmap, val);
default:
return -EOPNOTSUPP;
}
}
static int spd5118_write(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long val)
{
struct regmap *regmap = dev_get_drvdata(dev);
switch (type) {
case hwmon_temp:
return spd5118_temp_write(regmap, attr, val);
default:
return -EOPNOTSUPP;
}
}
static umode_t spd5118_is_visible(const void *_data, enum hwmon_sensor_types type,
u32 attr, int channel)
{
if (type != hwmon_temp)
return 0;
switch (attr) {
case hwmon_temp_input:
return 0444;
case hwmon_temp_min:
case hwmon_temp_max:
case hwmon_temp_lcrit:
case hwmon_temp_crit:
case hwmon_temp_enable:
return 0644;
case hwmon_temp_min_alarm:
case hwmon_temp_max_alarm:
case hwmon_temp_crit_alarm:
case hwmon_temp_lcrit_alarm:
return 0444;
default:
return 0;
}
}
static inline bool spd5118_parity8(u8 w)
{
w ^= w >> 4;
return (0x6996 >> (w & 0xf)) & 1;
}
/*
* Bank and vendor id are 8-bit fields with seven data bits and odd parity.
* Vendor IDs 0 and 0x7f are invalid.
* See Jedec standard JEP106BJ for details and a list of assigned vendor IDs.
*/
static bool spd5118_vendor_valid(u8 bank, u8 id)
{
if (!spd5118_parity8(bank) || !spd5118_parity8(id))
return false;
id &= 0x7f;
return id && id != 0x7f;
}
/* Return 0 if detection is successful, -ENODEV otherwise */
static int spd5118_detect(struct i2c_client *client, struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
int regval;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA))
return -ENODEV;
regval = i2c_smbus_read_word_swapped(client, SPD5118_REG_TYPE);
if (regval != 0x5118)
return -ENODEV;
regval = i2c_smbus_read_word_data(client, SPD5118_REG_VENDOR);
if (regval < 0 || !spd5118_vendor_valid(regval & 0xff, regval >> 8))
return -ENODEV;
regval = i2c_smbus_read_byte_data(client, SPD5118_REG_CAPABILITY);
if (regval < 0)
return -ENODEV;
if (!(regval & SPD5118_CAP_TS_SUPPORT) || (regval & 0xfc))
return -ENODEV;
regval = i2c_smbus_read_byte_data(client, SPD5118_REG_TEMP_CLR);
if (regval)
return -ENODEV;
regval = i2c_smbus_read_byte_data(client, SPD5118_REG_ERROR_CLR);
if (regval)
return -ENODEV;
regval = i2c_smbus_read_byte_data(client, SPD5118_REG_REVISION);
if (regval < 0 || (regval & 0xc1))
return -ENODEV;
regval = i2c_smbus_read_byte_data(client, SPD5118_REG_TEMP_CONFIG);
if (regval < 0)
return -ENODEV;
if (regval & ~SPD5118_TS_DISABLE)
return -ENODEV;
strscpy(info->type, "spd5118", I2C_NAME_SIZE);
return 0;
}
static const struct hwmon_channel_info *spd5118_info[] = {
HWMON_CHANNEL_INFO(chip,
HWMON_C_REGISTER_TZ),
HWMON_CHANNEL_INFO(temp,
HWMON_T_INPUT |
HWMON_T_LCRIT | HWMON_T_LCRIT_ALARM |
HWMON_T_MIN | HWMON_T_MIN_ALARM |
HWMON_T_MAX | HWMON_T_MAX_ALARM |
HWMON_T_CRIT | HWMON_T_CRIT_ALARM |
HWMON_T_ENABLE),
NULL
};
static const struct hwmon_ops spd5118_hwmon_ops = {
.is_visible = spd5118_is_visible,
.read = spd5118_read,
.write = spd5118_write,
};
static const struct hwmon_chip_info spd5118_chip_info = {
.ops = &spd5118_hwmon_ops,
.info = spd5118_info,
};
/* nvmem */
static ssize_t spd5118_nvmem_read_page(struct regmap *regmap, char *buf,
unsigned int offset, size_t count)
{
int addr = (offset >> SPD5118_PAGE_SHIFT) * 0x100 + SPD5118_EEPROM_BASE;
int err;
offset &= SPD5118_PAGE_MASK;
/* Can't cross page boundaries */
if (offset + count > SPD5118_PAGE_SIZE)
count = SPD5118_PAGE_SIZE - offset;
err = regmap_bulk_read(regmap, addr + offset, buf, count);
if (err)
return err;
return count;
}
static int spd5118_nvmem_read(void *priv, unsigned int off, void *val, size_t count)
{
struct spd5118_data *data = priv;
char *buf = val;
int ret;
if (unlikely(!count))
return count;
if (off + count > SPD5118_EEPROM_SIZE)
return -EINVAL;
mutex_lock(&data->nvmem_lock);
while (count) {
ret = spd5118_nvmem_read_page(data->regmap, buf, off, count);
if (ret < 0) {
mutex_unlock(&data->nvmem_lock);
return ret;
}
buf += ret;
off += ret;
count -= ret;
}
mutex_unlock(&data->nvmem_lock);
return 0;
}
static int spd5118_nvmem_init(struct device *dev, struct spd5118_data *data)
{
struct nvmem_config nvmem_config = {
.type = NVMEM_TYPE_EEPROM,
.name = dev_name(dev),
.id = NVMEM_DEVID_NONE,
.dev = dev,
.base_dev = dev,
.read_only = true,
.root_only = false,
.owner = THIS_MODULE,
.compat = true,
.reg_read = spd5118_nvmem_read,
.priv = data,
.stride = 1,
.word_size = 1,
.size = SPD5118_EEPROM_SIZE,
};
struct nvmem_device *nvmem;
nvmem = devm_nvmem_register(dev, &nvmem_config);
return PTR_ERR_OR_ZERO(nvmem);
}
/* regmap */
static bool spd5118_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case SPD5118_REG_I2C_LEGACY_MODE:
case SPD5118_REG_TEMP_CLR:
case SPD5118_REG_TEMP_CONFIG:
case SPD5118_REG_TEMP_MAX:
case SPD5118_REG_TEMP_MAX + 1:
case SPD5118_REG_TEMP_MIN:
case SPD5118_REG_TEMP_MIN + 1:
case SPD5118_REG_TEMP_CRIT:
case SPD5118_REG_TEMP_CRIT + 1:
case SPD5118_REG_TEMP_LCRIT:
case SPD5118_REG_TEMP_LCRIT + 1:
return true;
default:
return false;
}
}
static bool spd5118_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case SPD5118_REG_TEMP_CLR:
case SPD5118_REG_ERROR_CLR:
case SPD5118_REG_TEMP:
case SPD5118_REG_TEMP + 1:
case SPD5118_REG_TEMP_STATUS:
return true;
default:
return false;
}
}
static const struct regmap_range_cfg spd5118_regmap_range_cfg[] = {
{
.selector_reg = SPD5118_REG_I2C_LEGACY_MODE,
.selector_mask = SPD5118_LEGACY_PAGE_MASK,
.selector_shift = 0,
.window_start = 0,
.window_len = 0x100,
.range_min = 0,
.range_max = 0x7ff,
},
};
static const struct regmap_config spd5118_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0x7ff,
.writeable_reg = spd5118_writeable_reg,
.volatile_reg = spd5118_volatile_reg,
.cache_type = REGCACHE_MAPLE,
.ranges = spd5118_regmap_range_cfg,
.num_ranges = ARRAY_SIZE(spd5118_regmap_range_cfg),
};
static int spd5118_init(struct i2c_client *client)
{
struct i2c_adapter *adapter = client->adapter;
int err, regval, mode;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA))
return -ENODEV;
regval = i2c_smbus_read_word_swapped(client, SPD5118_REG_TYPE);
if (regval < 0 || (regval && regval != 0x5118))
return -ENODEV;
/*
* If the device type registers return 0, it is possible that the chip
* has a non-zero page selected and takes the specification literally,
* i.e. disables access to volatile registers besides the page register
* if the page is not 0. Try to identify such chips.
*/
if (!regval) {
/* Vendor ID registers must also be 0 */
regval = i2c_smbus_read_word_data(client, SPD5118_REG_VENDOR);
if (regval)
return -ENODEV;
/* The selected page in MR11 must not be 0 */
mode = i2c_smbus_read_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE);
if (mode < 0 || (mode & ~SPD5118_LEGACY_MODE_MASK) ||
!(mode & SPD5118_LEGACY_PAGE_MASK))
return -ENODEV;
err = i2c_smbus_write_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE,
mode & SPD5118_LEGACY_MODE_ADDR);
if (err)
return -ENODEV;
/*
* If the device type registers are still bad after selecting
* page 0, this is not a SPD5118 device. Restore original
* legacy mode register value and abort.
*/
regval = i2c_smbus_read_word_swapped(client, SPD5118_REG_TYPE);
if (regval != 0x5118) {
i2c_smbus_write_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE, mode);
return -ENODEV;
}
}
/* We are reasonably sure that this is really a SPD5118 hub controller */
return 0;
}
static int spd5118_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
unsigned int regval, revision, vendor, bank;
struct spd5118_data *data;
struct device *hwmon_dev;
struct regmap *regmap;
int err;
err = spd5118_init(client);
if (err)
return err;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
regmap = devm_regmap_init_i2c(client, &spd5118_regmap_config);
if (IS_ERR(regmap))
return dev_err_probe(dev, PTR_ERR(regmap), "regmap init failed\n");
err = regmap_read(regmap, SPD5118_REG_CAPABILITY, &regval);
if (err)
return err;
if (!(regval & SPD5118_CAP_TS_SUPPORT))
return -ENODEV;
err = regmap_read(regmap, SPD5118_REG_REVISION, &revision);
if (err)
return err;
err = regmap_read(regmap, SPD5118_REG_VENDOR, &bank);
if (err)
return err;
err = regmap_read(regmap, SPD5118_REG_VENDOR + 1, &vendor);
if (err)
return err;
if (!spd5118_vendor_valid(bank, vendor))
return -ENODEV;
data->regmap = regmap;
mutex_init(&data->nvmem_lock);
dev_set_drvdata(dev, data);
err = spd5118_nvmem_init(dev, data);
/* Ignore if NVMEM support is disabled */
if (err && err != -EOPNOTSUPP) {
dev_err_probe(dev, err, "failed to register nvmem\n");
return err;
}
hwmon_dev = devm_hwmon_device_register_with_info(dev, "spd5118",
regmap, &spd5118_chip_info,
NULL);
if (IS_ERR(hwmon_dev))
return PTR_ERR(hwmon_dev);
/*
* From JESD300-5B
* MR2 bits [5:4]: Major revision, 1..4
* MR2 bits [3:1]: Minor revision, 0..8? Probably a typo, assume 1..8
*/
dev_info(dev, "DDR5 temperature sensor: vendor 0x%02x:0x%02x revision %d.%d\n",
bank & 0x7f, vendor, ((revision >> 4) & 0x03) + 1, ((revision >> 1) & 0x07) + 1);
return 0;
}
static int spd5118_suspend(struct device *dev)
{
struct spd5118_data *data = dev_get_drvdata(dev);
struct regmap *regmap = data->regmap;
u32 regval;
int err;
/*
* Make sure the configuration register in the regmap cache is current
* before bypassing it.
*/
err = regmap_read(regmap, SPD5118_REG_TEMP_CONFIG, &regval);
if (err < 0)
return err;
regcache_cache_bypass(regmap, true);
regmap_update_bits(regmap, SPD5118_REG_TEMP_CONFIG, SPD5118_TS_DISABLE,
SPD5118_TS_DISABLE);
regcache_cache_bypass(regmap, false);
regcache_cache_only(regmap, true);
regcache_mark_dirty(regmap);
return 0;
}
static int spd5118_resume(struct device *dev)
{
struct spd5118_data *data = dev_get_drvdata(dev);
struct regmap *regmap = data->regmap;
regcache_cache_only(regmap, false);
return regcache_sync(regmap);
}
static DEFINE_SIMPLE_DEV_PM_OPS(spd5118_pm_ops, spd5118_suspend, spd5118_resume);
static const struct i2c_device_id spd5118_id[] = {
{ "spd5118", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, spd5118_id);
static const struct of_device_id spd5118_of_ids[] = {
{ .compatible = "jedec,spd5118", },
{ }
};
MODULE_DEVICE_TABLE(of, spd5118_of_ids);
static struct i2c_driver spd5118_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "spd5118",
.of_match_table = spd5118_of_ids,
.pm = pm_sleep_ptr(&spd5118_pm_ops),
},
.probe = spd5118_probe,
.id_table = spd5118_id,
.detect = IS_ENABLED(CONFIG_SENSORS_SPD5118_DETECT) ? spd5118_detect : NULL,
.address_list = IS_ENABLED(CONFIG_SENSORS_SPD5118_DETECT) ? normal_i2c : NULL,
};
module_i2c_driver(spd5118_driver);
MODULE_AUTHOR("René Rebe <rene@exactcode.de>");
MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
MODULE_DESCRIPTION("SPD 5118 driver");
MODULE_LICENSE("GPL");

View File

@ -377,8 +377,6 @@ static void thmc50_init_client(struct thmc50_data *data)
i2c_smbus_write_byte_data(client, THMC50_REG_CONF, config);
}
static const struct i2c_device_id thmc50_id[];
static int thmc50_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
@ -391,7 +389,7 @@ static int thmc50_probe(struct i2c_client *client)
return -ENOMEM;
data->client = client;
data->type = i2c_match_id(thmc50_id, client)->driver_data;
data->type = (uintptr_t)i2c_get_match_data(client);
mutex_init(&data->update_lock);
thmc50_init_client(data);

View File

@ -693,7 +693,7 @@ static int tmp401_probe(struct i2c_client *client)
data->client = client;
mutex_init(&data->update_lock);
data->kind = i2c_match_id(tmp401_id, client)->driver_data;
data->kind = (uintptr_t)i2c_get_match_data(client);
data->regmap = devm_regmap_init(dev, NULL, data, &tmp401_regmap_config);
if (IS_ERR(data->regmap))

View File

@ -446,11 +446,7 @@ static int tmp421_probe(struct i2c_client *client)
return -ENOMEM;
mutex_init(&data->update_lock);
if (client->dev.of_node)
data->channels = (unsigned long)
of_device_get_match_data(&client->dev);
else
data->channels = i2c_match_id(tmp421_id, client)->driver_data;
data->channels = (unsigned long)i2c_get_match_data(client);
data->client = client;
for (i = 0; i < data->channels; i++) {

View File

@ -666,10 +666,7 @@ static int tmp464_probe(struct i2c_client *client)
mutex_init(&data->update_lock);
if (dev->of_node)
data->channels = (int)(unsigned long)of_device_get_match_data(&client->dev);
else
data->channels = i2c_match_id(tmp464_id, client)->driver_data;
data->channels = (int)(unsigned long)i2c_get_match_data(client);
data->regmap = devm_regmap_init_i2c(client, &tmp464_regmap_config);
if (IS_ERR(data->regmap))

View File

@ -159,7 +159,7 @@ static const u8 TMP51X_CURR_INPUT[2] = {
TMP51X_BUS_CURRENT_RESULT
};
static struct regmap_config tmp51x_regmap_config = {
static const struct regmap_config tmp51x_regmap_config = {
.reg_bits = 8,
.val_bits = 16,
.max_register = TMP51X_MAX_REGISTER_ADDR,

View File

@ -117,7 +117,7 @@ struct tps23861_data {
struct dentry *debugfs_dir;
};
static struct regmap_config tps23861_regmap_config = {
static const struct regmap_config tps23861_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0x6f,

View File

@ -895,7 +895,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, 127);
val = DIV_ROUND_CLOSEST(clamp_val(val, 0, 127000), 1000);
mutex_lock(&data->update_lock);
data->target_temp[nr] = val;
@ -920,7 +920,7 @@ store_tolerance(struct device *dev, struct device_attribute *attr,
return err;
/* Limit the temp to 0C - 15C */
val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), 0, 15);
val = DIV_ROUND_CLOSEST(clamp_val(val, 0, 15000), 1000);
mutex_lock(&data->update_lock);
reg = w83627ehf_read_value(data, W83627EHF_REG_TOLERANCE[nr]);

View File

@ -1192,8 +1192,6 @@ static void w83781d_remove_files(struct device *dev)
sysfs_remove_group(&dev->kobj, &w83781d_group_other);
}
static const struct i2c_device_id w83781d_ids[];
static int w83781d_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
@ -1208,7 +1206,7 @@ static int w83781d_probe(struct i2c_client *client)
mutex_init(&data->lock);
mutex_init(&data->update_lock);
data->type = i2c_match_id(w83781d_ids, client)->driver_data;
data->type = (uintptr_t)i2c_get_match_data(client);
data->client = client;
/* attach secondary i2c lm75-like clients */

View File

@ -2134,8 +2134,6 @@ static void w83795_apply_temp_config(struct w83795_data *data, u8 config,
}
}
static const struct i2c_device_id w83795_id[];
static int w83795_probe(struct i2c_client *client)
{
int i;
@ -2149,7 +2147,7 @@ static int w83795_probe(struct i2c_client *client)
return -ENOMEM;
i2c_set_clientdata(client, data);
data->chip_type = i2c_match_id(w83795_id, client)->driver_data;
data->chip_type = (uintptr_t)i2c_get_match_data(client);
data->bank = i2c_smbus_read_byte_data(client, W83795_REG_BANKSEL);
mutex_init(&data->update_lock);

View File

@ -308,7 +308,7 @@ EXPORT_SYMBOL_GPL(i2c_free_slave_host_notify_device);
* target systems are the same.
* Restrictions to automatic SPD instantiation:
* - Only works if all filled slots have the same memory type
* - Only works for DDR, DDR2, DDR3 and DDR4 for now
* - Only works for (LP)DDR memory types up to DDR5
* - Only works on systems with 1 to 8 memory slots
*/
#if IS_ENABLED(CONFIG_DMI)
@ -382,6 +382,10 @@ void i2c_register_spd(struct i2c_adapter *adap)
case 0x1E: /* LPDDR4 */
name = "ee1004";
break;
case 0x22: /* DDR5 */
case 0x23: /* LPDDR5 */
name = "spd5118";
break;
default:
dev_info(&adap->dev,
"Memory type 0x%02x not supported yet, not instantiating SPD\n",

View File

@ -45,6 +45,7 @@ enum hwmon_chip_attributes {
hwmon_chip_power_samples,
hwmon_chip_temp_samples,
hwmon_chip_beep_enable,
hwmon_chip_pec,
};
#define HWMON_C_TEMP_RESET_HISTORY BIT(hwmon_chip_temp_reset_history)
@ -60,6 +61,7 @@ enum hwmon_chip_attributes {
#define HWMON_C_POWER_SAMPLES BIT(hwmon_chip_power_samples)
#define HWMON_C_TEMP_SAMPLES BIT(hwmon_chip_temp_samples)
#define HWMON_C_BEEP_ENABLE BIT(hwmon_chip_beep_enable)
#define HWMON_C_PEC BIT(hwmon_chip_pec)
enum hwmon_temp_attributes {
hwmon_temp_enable,