mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2024-12-28 16:52:18 +00:00
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:
commit
500a711df6
@ -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 */
|
||||
};
|
95
Documentation/devicetree/bindings/hwmon/gmt,g762.yaml
Normal file
95
Documentation/devicetree/bindings/hwmon/gmt,g762.yaml
Normal 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>;
|
||||
};
|
||||
};
|
92
Documentation/devicetree/bindings/hwmon/maxim,max6639.yaml
Normal file
92
Documentation/devicetree/bindings/hwmon/maxim,max6639.yaml
Normal 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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
...
|
@ -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;
|
||||
};
|
||||
};
|
||||
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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.
|
@ -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.
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
======================= ===================
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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.
|
179
Documentation/hwmon/mp2891.rst
Normal file
179
Documentation/hwmon/mp2891.rst
Normal 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**
|
150
Documentation/hwmon/mp2993.rst
Normal file
150
Documentation/hwmon/mp2993.rst
Normal 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**
|
91
Documentation/hwmon/mp5920.rst
Normal file
91
Documentation/hwmon/mp5920.rst
Normal 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**
|
92
Documentation/hwmon/mp9941.rst
Normal file
92
Documentation/hwmon/mp9941.rst
Normal 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**
|
63
Documentation/hwmon/spd5118.rst
Normal file
63
Documentation/hwmon/spd5118.rst
Normal 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.
|
21
MAINTAINERS
21
MAINTAINERS
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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");
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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),
|
||||
|
@ -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
@ -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",
|
||||
|
@ -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");
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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 = {
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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");
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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++) {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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))
|
||||
|
@ -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);
|
||||
|
@ -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 = {
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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",
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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");
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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 };
|
||||
|
||||
|
@ -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) */
|
||||
{},
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
|
600
drivers/hwmon/pmbus/mp2891.c
Normal file
600
drivers/hwmon/pmbus/mp2891.c
Normal 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);
|
261
drivers/hwmon/pmbus/mp2993.c
Normal file
261
drivers/hwmon/pmbus/mp2993.c
Normal 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);
|
90
drivers/hwmon/pmbus/mp5920.c
Normal file
90
drivers/hwmon/pmbus/mp5920.c
Normal 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);
|
319
drivers/hwmon/pmbus/mp9941.c
Normal file
319
drivers/hwmon/pmbus/mp9941.c
Normal 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);
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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
703
drivers/hwmon/spd5118.c
Normal 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, ®val);
|
||||
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, ®val);
|
||||
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, ®val);
|
||||
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, ®val);
|
||||
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");
|
@ -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);
|
||||
|
@ -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))
|
||||
|
@ -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++) {
|
||||
|
@ -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))
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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]);
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user