mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-26 05:34:13 +08:00
hwmon updates for v5.20
- Substantial rewrite of lm90 driver to support several additional chips and improve support for existing chips. - Add support of ROG ZENITH II EXTREME, Maximus XI Hero, Strix Z690-a D4 to asus-ec-sensors driver - Add support of F71858AD to f71882fg driver - Add support of Aquacomputer Quadro to aquacomputer_d5next driver - Improved assembler code and add support for Dell G5 5590 as well as XPS 13 7390 in dell-smm driver - Add support for ASUS TUF GAMING B550-PLUS WIFI II to nct775 driver - Add support for IEEE 754 half precision to PMBus core. Also support for Analog Devices LT7182S, improve regulator support, and report various MFR register values in debugfs. - Various other minor improvements and fixes -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEiHPvMQj9QTOCiqgVyx8mb86fmYEFAmLkUzwACgkQyx8mb86f mYFxXg/9HuCcW14zARhroIplYrqqKlk6/oeTFc5g3t4Mm5q7lcvpHx3PJM0GOUH1 xRSqIZRhzf42L83rTtrwcKyoL9e0YPTJ7F9Ja+lYcAR3RqRTspaz3f2rHkO+/7BK 2Lf0KTaPFzGFEBYZPDr/ekY6ihRtJeVuZ0qOfqCLaF7+z/J/IOsJHX3WaE+UASHy JnN2Uvq8Q5Op/6kejqfAwpO3Us4og1eh0vzGH2hfGo47bq8C8OtNyzbFHl2cgDdX eYj1qPzBGLMt/zvbhdyrPwAHgttTQ4EINhNj815WDueFCCxkQsJqIkgZ8N01N3Gc maezQiN65A4wCNov0mlFYWHatjyNo+uV3Bngf6rWDQwsvueTS6j5EKHcMmNoXr/3 HlmenpKHggF87WXVSVA9NYB455XG2LS8F0BpVdHzUl/z0PMcVI3ew0IMoIe08f7m SQ7OZ/jX2/2QoBxZy/A6juKSTOaIorp4jJQiWrejeYr8SmTccRC/5mzNDga1v+xZ DMwtzPIUC73xyo/i696qV5Lu+DdiQrA0SAzPPxaXZoDd4AkeylxpJxte4vqCp9lF x+4Bc9LXyv/jkIC5t/rTJmICkMkd9Z8Z60laakZl4gKvoWeMdah/3y0vq2VicCg/ 0ol4hhGRjgAH3KsWrSFKtNgF7ccvQ9nF0XolwLaHWYW1C8fetlY= =M9iY -----END PGP SIGNATURE----- Merge tag 'hwmon-for-v5.20' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging Pull hwmon updates from Guenter Roeck: - Substantial rewrite of lm90 driver to support several additional chips and improve support for existing chips. - Add support of ROG ZENITH II EXTREME, Maximus XI Hero, and Strix Z690-a D4 to asus-ec-sensors driver - Add support of F71858AD to f71882fg driver - Add support of Aquacomputer Quadro to aquacomputer_d5next driver - Improved assembler code and add support for Dell G5 5590 as well as XPS 13 7390 in dell-smm driver - Add support for ASUS TUF GAMING B550-PLUS WIFI II to nct775 driver - Add support for IEEE 754 half precision to PMBus core. Also support for Analog Devices LT7182S, improve regulator support, and report various MFR register values in debugfs. - Various other minor improvements and fixes * tag 'hwmon-for-v5.20' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (85 commits) hwmon: (aquacomputer_d5next) Add support for Aquacomputer Quadro fan controller hwmon: (dell-smm) Improve documentation hwmon: (nct6775) add ASUS TUF GAMING B550-PLUS WIFI II hwmon: (occ) Replace open-coded variant of %*phN specifier hwmon: (sht15) Fix wrong assumptions in device remove callback hwmon: (aquacomputer_d5next) Add support for reading the +12V voltage sensor on D5 Next hwmon: (tps23861) fix byte order in current and voltage registers hwmon: (aspeed-pwm-tacho) increase fan tach period (again) hwmon: (aquacomputer_d5next) Add D5 Next fan control support hwmon: (mcp3021) improve driver support for newer hwmon interface hwmon: (asus-ec-sensors) add definitions for ROG ZENITH II EXTREME hwmon: (aquacomputer_d5next) Move device-specific data into struct aqc_data hwmon: (asus-ec-sensors) add missing sensors for X570-I GAMING hwmon: (drivetemp) Add module alias hwmon: (asus_wmi_sensors) Save a few bytes of memory hwmon: (lm90) Use worker for alarm notifications hwmon: (asus-ec-sensors) add support for Maximus XI Hero hwmon: (dell-smm) Improve assembly code hwmon: (pmbus/ltc2978) Set voltage resolution hwmon: (pmbus) Add list_voltage to pmbus ops ...
This commit is contained in:
commit
64ae88ff48
@ -938,3 +938,12 @@ Description:
|
||||
- 1: enable
|
||||
|
||||
RW
|
||||
|
||||
What: /sys/class/hwmon/hwmonX/device/pec
|
||||
Description:
|
||||
PEC support on I2C devices
|
||||
|
||||
- 0, off, n: disable
|
||||
- 1, on, y: enable
|
||||
|
||||
RW
|
||||
|
@ -16,6 +16,7 @@ properties:
|
||||
- adi,adm1032
|
||||
- adi,adt7461
|
||||
- adi,adt7461a
|
||||
- adi,adt7481
|
||||
- dallas,max6646
|
||||
- dallas,max6647
|
||||
- dallas,max6649
|
||||
@ -50,6 +51,12 @@ properties:
|
||||
"#thermal-sensor-cells":
|
||||
const: 1
|
||||
|
||||
'#address-cells':
|
||||
const: 1
|
||||
|
||||
'#size-cells':
|
||||
const: 0
|
||||
|
||||
vcc-supply:
|
||||
description: phandle to the regulator that provides the +VCC supply
|
||||
|
||||
@ -61,6 +68,29 @@ required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
patternProperties:
|
||||
"^channel@([0-2])$":
|
||||
type: object
|
||||
description: Represents channels of the device and their specific configuration.
|
||||
|
||||
properties:
|
||||
reg:
|
||||
description: The channel number. 0 is local channel, 1-2 are remote channels.
|
||||
items:
|
||||
minimum: 0
|
||||
maximum: 2
|
||||
|
||||
label:
|
||||
description: A descriptive name for this channel, like "ambient" or "psu".
|
||||
|
||||
temperature-offset-millicelsius:
|
||||
description: Temperature offset to be added to or subtracted from remote temperature measurements.
|
||||
|
||||
required:
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
allOf:
|
||||
- if:
|
||||
not:
|
||||
@ -70,12 +100,84 @@ allOf:
|
||||
enum:
|
||||
- adi,adt7461
|
||||
- adi,adt7461a
|
||||
- adi,adt7481
|
||||
- ti,tmp451
|
||||
- ti,tmp461
|
||||
then:
|
||||
properties:
|
||||
ti,extended-range-enable: false
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- dallas,max6646
|
||||
- dallas,max6647
|
||||
- dallas,max6649
|
||||
- dallas,max6657
|
||||
- dallas,max6658
|
||||
- dallas,max6659
|
||||
- dallas,max6695
|
||||
- dallas,max6696
|
||||
then:
|
||||
patternProperties:
|
||||
"^channel@([0-2])$":
|
||||
properties:
|
||||
temperature-offset-millicelsius: false
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- adi,adt7461
|
||||
- adi,adt7461a
|
||||
- adi,adt7481
|
||||
- onnn,nct1008
|
||||
then:
|
||||
patternProperties:
|
||||
"^channel@([0-2])$":
|
||||
properties:
|
||||
temperature-offset-millicelsius:
|
||||
maximum: 127750
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- adi,adm1032
|
||||
- dallas,max6680
|
||||
- dallas,max6681
|
||||
- gmt,g781
|
||||
- national,lm86
|
||||
- national,lm89
|
||||
- national,lm90
|
||||
- national,lm99
|
||||
- nxp,sa56004
|
||||
- winbond,w83l771
|
||||
then:
|
||||
patternProperties:
|
||||
"^channel@([0-2])$":
|
||||
properties:
|
||||
temperature-offset-millicelsius:
|
||||
maximum: 127875
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- ti,tmp451
|
||||
- ti,tmp461
|
||||
then:
|
||||
patternProperties:
|
||||
"^channel@([0-2])$":
|
||||
properties:
|
||||
temperature-offset-millicelsius:
|
||||
maximum: 127937
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
@ -94,3 +196,32 @@ examples:
|
||||
#thermal-sensor-cells = <1>;
|
||||
};
|
||||
};
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
sensor@4c {
|
||||
compatible = "adi,adt7481";
|
||||
reg = <0x4c>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
channel@0 {
|
||||
reg = <0x0>;
|
||||
label = "local";
|
||||
};
|
||||
|
||||
channel@1 {
|
||||
reg = <0x1>;
|
||||
label = "front";
|
||||
temperature-offset-millicelsius = <4000>;
|
||||
};
|
||||
|
||||
channel@2 {
|
||||
reg = <0x2>;
|
||||
label = "back";
|
||||
temperature-offset-millicelsius = <750>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -41,6 +41,8 @@ properties:
|
||||
- adi,adp5585-02
|
||||
# Analog Devices ADP5589 Keypad Decoder and I/O Expansion
|
||||
- adi,adp5589
|
||||
# Analog Devices LT7182S Dual Channel 6A, 20V PolyPhase Step-Down Silent Switcher
|
||||
- adi,lt7182s
|
||||
# AMS iAQ-Core VOC Sensor
|
||||
- ams,iaq-core
|
||||
# i2c serial eeprom (24cxx)
|
||||
|
@ -9,6 +9,7 @@ Supported devices:
|
||||
* Aquacomputer Farbwerk RGB controller
|
||||
* Aquacomputer Farbwerk 360 RGB controller
|
||||
* Aquacomputer Octo fan controller
|
||||
* Aquacomputer Quadro fan controller
|
||||
|
||||
Author: Aleksa Savic
|
||||
|
||||
@ -33,6 +34,9 @@ better suited for userspace tools.
|
||||
The Octo exposes four temperature sensors and eight PWM controllable fans, along
|
||||
with their speed (in RPM), power, voltage and current.
|
||||
|
||||
The Quadro exposes four temperature sensors, a flow sensor and four PWM controllable
|
||||
fans, along with their speed (in RPM), power, voltage and current.
|
||||
|
||||
The Farbwerk and Farbwerk 360 expose four temperature sensors. Depending on the device,
|
||||
not all sysfs and debugfs entries will be available.
|
||||
|
||||
@ -45,13 +49,14 @@ the kernel and supports hotswapping.
|
||||
Sysfs entries
|
||||
-------------
|
||||
|
||||
================ =============================================
|
||||
================ ==============================================
|
||||
temp[1-4]_input Temperature sensors (in millidegrees Celsius)
|
||||
fan[1-2]_input Pump/fan speed (in RPM)
|
||||
power[1-2]_input Pump/fan power (in micro Watts)
|
||||
in[0-2]_input Pump/fan voltage (in milli Volts)
|
||||
curr[1-2]_input Pump/fan current (in milli Amperes)
|
||||
================ =============================================
|
||||
fan[1-8]_input Pump/fan speed (in RPM) / Flow speed (in dL/h)
|
||||
power[1-8]_input Pump/fan power (in micro Watts)
|
||||
in[0-7]_input Pump/fan voltage (in milli Volts)
|
||||
curr[1-8]_input Pump/fan current (in milli Amperes)
|
||||
pwm[1-8] Fan PWM (0 - 255)
|
||||
================ ==============================================
|
||||
|
||||
Debugfs entries
|
||||
---------------
|
||||
|
@ -13,12 +13,16 @@ Supported boards:
|
||||
* ROG CROSSHAIR VIII FORMULA
|
||||
* ROG CROSSHAIR VIII HERO
|
||||
* ROG CROSSHAIR VIII IMPACT
|
||||
* ROG MAXIMUS XI HERO
|
||||
* ROG MAXIMUS XI HERO (WI-FI)
|
||||
* ROG STRIX B550-E GAMING
|
||||
* ROG STRIX B550-I GAMING
|
||||
* ROG STRIX X570-E GAMING
|
||||
* ROG STRIX X570-E GAMING WIFI II
|
||||
* ROG STRIX X570-F GAMING
|
||||
* ROG STRIX X570-I GAMING
|
||||
* ROG STRIX Z690-A GAMING WIFI D4
|
||||
* ROG ZENITH II EXTREME
|
||||
|
||||
Authors:
|
||||
- Eugene Shalygin <eugene.shalygin@gmail.com>
|
||||
|
@ -46,6 +46,9 @@ temp[1-10]_input RO Temperature reading in milli-degrees
|
||||
temp[1-10]_label RO Temperature sensor label.
|
||||
=============================== ======= =======================================
|
||||
|
||||
Due to the nature of the SMM interface, each pwmX attribute controls
|
||||
fan number X.
|
||||
|
||||
Disabling automatic BIOS fan control
|
||||
------------------------------------
|
||||
|
||||
|
@ -109,6 +109,7 @@ Hardware Monitoring Kernel Drivers
|
||||
lm95234
|
||||
lm95245
|
||||
lochnagar
|
||||
lt7182s
|
||||
ltc2992
|
||||
ltc2945
|
||||
ltc2947
|
||||
|
@ -3,6 +3,14 @@ Kernel driver lm90
|
||||
|
||||
Supported chips:
|
||||
|
||||
* National Semiconductor LM84
|
||||
|
||||
Prefix: 'lm84'
|
||||
|
||||
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
|
||||
|
||||
Datasheet: Publicly available at the National Semiconductor website
|
||||
|
||||
* National Semiconductor LM90
|
||||
|
||||
Prefix: 'lm90'
|
||||
@ -43,6 +51,30 @@ Supported chips:
|
||||
|
||||
http://www.national.com/mpf/LM/LM86.html
|
||||
|
||||
* Analog Devices ADM1020
|
||||
|
||||
Prefix: 'adm1020'
|
||||
|
||||
Addresses scanned: I2C 0x4c - 0x4e
|
||||
|
||||
Datasheet: Publicly available at the Analog Devices website
|
||||
|
||||
* 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
|
||||
|
||||
* Analog Devices ADM1032
|
||||
|
||||
Prefix: 'adm1032'
|
||||
@ -73,6 +105,36 @@ Supported chips:
|
||||
|
||||
https://www.onsemi.com/PowerSolutions/product.do?id=ADT7461A
|
||||
|
||||
* Analog Devices ADT7481
|
||||
|
||||
Prefix: 'adt7481'
|
||||
|
||||
Addresses scanned: I2C 0x4b and 0x4c
|
||||
|
||||
Datasheet: Publicly available at the ON Semiconductor website
|
||||
|
||||
https://www.onsemi.com/PowerSolutions/product.do?id=ADT7481
|
||||
|
||||
* Analog Devices ADT7482
|
||||
|
||||
Prefix: 'adt7482'
|
||||
|
||||
Addresses scanned: I2C 0x4c
|
||||
|
||||
Datasheet: Publicly available at the ON Semiconductor website
|
||||
|
||||
https://www.onsemi.com/PowerSolutions/product.do?id=ADT7482
|
||||
|
||||
* Analog Devices ADT7483A
|
||||
|
||||
Prefix: 'adt7483a'
|
||||
|
||||
Addresses scanned: I2C 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e
|
||||
|
||||
Datasheet: Publicly available at the ON Semiconductor website
|
||||
|
||||
https://www.onsemi.com/PowerSolutions/product.do?id=ADT7483A
|
||||
|
||||
* ON Semiconductor NCT1008
|
||||
|
||||
Prefix: 'nct1008'
|
||||
@ -83,6 +145,72 @@ Supported chips:
|
||||
|
||||
https://www.onsemi.com/PowerSolutions/product.do?id=NCT1008
|
||||
|
||||
* ON Semiconductor NCT210
|
||||
|
||||
Prefix: 'adm1021'
|
||||
|
||||
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
|
||||
|
||||
Datasheet: Publicly available at the ON Semiconductor website
|
||||
|
||||
https://www.onsemi.com/PowerSolutions/product.do?id=NCT210
|
||||
|
||||
* ON Semiconductor NCT214
|
||||
|
||||
Prefix: 'nct214'
|
||||
|
||||
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
|
||||
|
||||
Datasheet: Publicly available at the ON Semiconductor website
|
||||
|
||||
https://www.onsemi.com/PowerSolutions/product.do?id=NCT214
|
||||
|
||||
* ON Semiconductor NCT218
|
||||
|
||||
Prefix: 'nct218'
|
||||
|
||||
Addresses scanned: I2C 0x4c - 0x4d
|
||||
|
||||
Datasheet: Publicly available at the ON Semiconductor website
|
||||
|
||||
https://www.onsemi.com/PowerSolutions/product.do?id=NCT218
|
||||
|
||||
* ON Semiconductor NCT72
|
||||
|
||||
Prefix: 'nct72'
|
||||
|
||||
Addresses scanned: I2C 0x4c - 0x4d
|
||||
|
||||
Datasheet: Publicly available at the ON Semiconductor website
|
||||
|
||||
https://www.onsemi.com/PowerSolutions/product.do?id=NCT72
|
||||
|
||||
* 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
|
||||
|
||||
* 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
|
||||
|
||||
* Maxim MAX6646
|
||||
|
||||
Prefix: 'max6646'
|
||||
@ -105,7 +233,7 @@ Supported chips:
|
||||
|
||||
* Maxim MAX6648
|
||||
|
||||
Prefix: 'max6646'
|
||||
Prefix: 'max6648'
|
||||
|
||||
Addresses scanned: I2C 0x4c
|
||||
|
||||
@ -191,7 +319,7 @@ Supported chips:
|
||||
|
||||
* Maxim MAX6692
|
||||
|
||||
Prefix: 'max6646'
|
||||
Prefix: 'max6648'
|
||||
|
||||
Addresses scanned: I2C 0x4c
|
||||
|
||||
@ -275,6 +403,46 @@ Supported chips:
|
||||
|
||||
https://www.ti.com/lit/gpn/tmp461
|
||||
|
||||
* Philips NE1617, NE1617A
|
||||
|
||||
Prefix: 'max1617' (probably detected as a max1617)
|
||||
|
||||
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
|
||||
|
||||
Datasheets: Publicly available at the Philips website
|
||||
|
||||
* Philips NE1618
|
||||
|
||||
Prefix: 'ne1618'
|
||||
|
||||
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
|
||||
|
||||
Datasheets: Publicly available at the Philips website
|
||||
|
||||
* Genesys Logic GL523SM
|
||||
|
||||
Prefix: 'gl523sm'
|
||||
|
||||
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
|
||||
|
||||
Datasheet:
|
||||
|
||||
* 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
|
||||
|
||||
Author: Jean Delvare <jdelvare@suse.de>
|
||||
|
||||
|
||||
@ -285,6 +453,12 @@ The LM90 is a digital temperature sensor. It senses its own temperature as
|
||||
well as the temperature of up to one external diode. It is compatible
|
||||
with many other devices, many of which are supported by this driver.
|
||||
|
||||
The family of chips supported by this driver is derived from MAX1617.
|
||||
This chip as well as various compatible chips support a local and a remote
|
||||
temperature sensor with 8 bit accuracy. Later chips provide improved accuracy
|
||||
and other additional features such as hysteresis and temperature offset
|
||||
registers.
|
||||
|
||||
Note that there is no easy way to differentiate between the MAX6657,
|
||||
MAX6658 and MAX6659 variants. The extra features of the MAX6659 are only
|
||||
supported by this driver if the chip is located at address 0x4d or 0x4e,
|
||||
@ -292,15 +466,31 @@ or if the chip type is explicitly selected as max6659.
|
||||
The MAX6680 and MAX6681 only differ in their pinout, therefore they obviously
|
||||
can't (and don't need to) be distinguished.
|
||||
|
||||
The specificity of this family of chipsets over the ADM1021/LM84
|
||||
family is that it features critical limits with hysteresis, and an
|
||||
increased resolution of the remote temperature measurement.
|
||||
|
||||
The different chipsets of the family are not strictly identical, although
|
||||
very similar. For reference, here comes a non-exhaustive list of specific
|
||||
features:
|
||||
|
||||
LM84:
|
||||
* 8 bit sensor resolution
|
||||
|
||||
ADM1020, ADM1021, GL523SM, MAX1617, NE1617, NE1617A, THMC10:
|
||||
* 8 bit sensor resolution
|
||||
* Low temperature limits
|
||||
|
||||
NCT210, NE1618:
|
||||
* 11 bit sensor resolution for remote temperature sensor
|
||||
* Low temperature limits
|
||||
|
||||
ADM1021A, ADM1023:
|
||||
* Temperature offset register for remote temperature sensor
|
||||
* 11 bit resolution for remote temperature sensor
|
||||
* Low temperature limits
|
||||
|
||||
LM90:
|
||||
* 11 bit resolution for remote temperature sensor
|
||||
* Temperature offset register for remote temperature sensor
|
||||
* Low and critical temperature limits
|
||||
* Configurable conversion rate
|
||||
* Filter and alert configuration register at 0xBF.
|
||||
* ALERT is triggered by temperatures over critical limits.
|
||||
|
||||
@ -322,8 +512,31 @@ ADM1032:
|
||||
ADT7461, ADT7461A, NCT1008:
|
||||
* Extended temperature range (breaks compatibility)
|
||||
* Lower resolution for remote temperature
|
||||
* SMBus PEC support for Write Byte and Receive Byte transactions.
|
||||
* 10 bit temperature resolution
|
||||
|
||||
MAX6654:
|
||||
ADT7481, ADT7482, ADT7483:
|
||||
* Temperature offset register
|
||||
* SMBus PEC support
|
||||
* 10 bit temperature resolution for external sensors
|
||||
* Two remote sensors
|
||||
* Selectable address (ADT7483)
|
||||
|
||||
MAX6642:
|
||||
* No critical limit register
|
||||
* Conversion rate not configurable
|
||||
* Better local resolution (10 bit)
|
||||
* 10 bit external sensor resolution
|
||||
|
||||
MAX6646, MAX6647, MAX6649:
|
||||
* Better local resolution
|
||||
* Extended range unsigned external temperature
|
||||
|
||||
MAX6648, MAX6692:
|
||||
* Better local resolution
|
||||
* Unsigned temperature
|
||||
|
||||
MAX6654, MAX6690:
|
||||
* Better local resolution
|
||||
* Selectable address
|
||||
* Remote sensor type selection
|
||||
@ -423,6 +636,6 @@ two transactions will typically mean twice as much delay waiting for
|
||||
transaction completion, effectively doubling the register cache refresh time.
|
||||
I guess reliability comes at a price, but it's quite expensive this time.
|
||||
|
||||
So, as not everyone might enjoy the slowdown, PEC can be disabled through
|
||||
sysfs. Just write 0 to the "pec" file and PEC will be disabled. Write 1
|
||||
to that file to enable PEC again.
|
||||
So, as not everyone might enjoy the slowdown, PEC is disabled by default and
|
||||
can be enabled through sysfs. Just write 1 to the "pec" file and PEC will be
|
||||
enabled. Write 0 to that file to disable PEC again.
|
||||
|
92
Documentation/hwmon/lt7182s.rst
Normal file
92
Documentation/hwmon/lt7182s.rst
Normal file
@ -0,0 +1,92 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
Kernel driver lt7182s
|
||||
=====================
|
||||
|
||||
Supported chips:
|
||||
|
||||
* ADI LT7182S
|
||||
|
||||
Prefix: 'lt7182s'
|
||||
|
||||
Addresses scanned: -
|
||||
|
||||
Datasheet: https://www.analog.com/en/products/lt7182s.html
|
||||
|
||||
Author: Guenter Roeck <linux@roeck-us.net>
|
||||
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
LT7182S is a Dual Channel 6A, 20V PolyPhase Step-Down Silent Switcher with
|
||||
Digital Power System Management support.
|
||||
|
||||
|
||||
Usage Notes
|
||||
-----------
|
||||
|
||||
This driver does not probe for PMBus devices. You will have to instantiate
|
||||
devices explicitly.
|
||||
|
||||
Example: the following commands will load the driver for a LT7182S
|
||||
at address 0x4f on I2C bus #4::
|
||||
|
||||
# modprobe lt7182s
|
||||
# echo lt7182s 0x4f > /sys/bus/i2c/devices/i2c-4/new_device
|
||||
|
||||
It can also be instantiated by declaring an entry in device tree.
|
||||
|
||||
|
||||
Sysfs attributes
|
||||
----------------
|
||||
|
||||
======================= ====================================
|
||||
curr[1-2]_label "iin[12]"
|
||||
curr[1-2]_input Measured input current
|
||||
curr[1-2]_max Maximum input current
|
||||
curr[1-2]_max_alarm Current high alarm
|
||||
|
||||
curr[3-4]_label "iout[1-2]"
|
||||
curr[3-4]_input Measured output current
|
||||
curr[3-4]_highest Highest measured output current
|
||||
curr[3-4]_max Maximum output current
|
||||
curr[3-4]_max_alarm Output current high alarm
|
||||
|
||||
in[1-2]_label "vin[12]"
|
||||
in[1-2]_input Measured input voltage
|
||||
in[1-2]_highest Highest measured input voltage
|
||||
in[1-2]_crit Critical maximum input voltage
|
||||
in[1-2]_crit_alarm Input voltage critical high alarm
|
||||
in[1-2]_min Minimum input voltage
|
||||
in[1-2]_min_alarm Input voltage low alarm
|
||||
in[1-2]_rated_min Rated minimum input voltage
|
||||
in[1-2]_rated_max Rated maximum input voltage
|
||||
in1_reset_history Write to reset history for all attributes
|
||||
|
||||
in[3-5]_label "vmon[1-3]"
|
||||
in[3-5]_input Measured voltage on ITH1/ITH2/EXTVCC pins
|
||||
Only available if enabled with MFR_ADC_CONTROL_LT7182S
|
||||
command.
|
||||
|
||||
in[3-4|6-7]_label "vout[1-2]"
|
||||
in[3-4|6-7]_input Measured output voltage
|
||||
in[3-4|6-7]_highest Highest measured output voltage
|
||||
in[3-4|6-7]_lcrit Critical minimum output voltage
|
||||
in[3-4|6-7]_lcrit_alarm Output voltage critical low alarm
|
||||
in[3-4|6-7]_min Minimum output voltage
|
||||
in[3-4|6-7]_max_alarm Output voltage low alarm
|
||||
in[3-4|6-7]_max Maximum output voltage
|
||||
in[3-4|6-7]_max_alarm Output voltage high alarm
|
||||
in[3-4|6-7]_crit Critical maximum output voltage
|
||||
in[3-4|6-7]_crit_alarm Output voltage critical high alarm
|
||||
|
||||
power[1-2]_label "pout[1-2]"
|
||||
power[1-2]_input Measured output power
|
||||
|
||||
temp1_input Measured temperature
|
||||
temp1_crit Critical high temperature
|
||||
temp1_crit_alarm Chip temperature critical high alarm
|
||||
temp1_max Maximum temperature
|
||||
temp1_max_alarm Chip temperature high alarm
|
||||
======================= ====================================
|
@ -121,6 +121,15 @@ Specifically, it provides the following information.
|
||||
non-standard PMBus commands to standard commands, or to augment standard
|
||||
command return values with device specific information.
|
||||
|
||||
PEC Support
|
||||
===========
|
||||
|
||||
Many PMBus devices support SMBus PEC (Packet Error Checking). If supported
|
||||
by both the I2C adapter and by the PMBus chip, it is by default enabled.
|
||||
If PEC is supported, the PMBus core driver adds an attribute named 'pec' to
|
||||
the I2C device. This attribute can be used to control PEC support in the
|
||||
communication with the PMBus chip.
|
||||
|
||||
API functions
|
||||
=============
|
||||
|
||||
|
@ -100,6 +100,7 @@ config SENSORS_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,
|
||||
@ -256,13 +257,13 @@ config SENSORS_AHT10
|
||||
will be called aht10.
|
||||
|
||||
config SENSORS_AQUACOMPUTER_D5NEXT
|
||||
tristate "Aquacomputer D5 Next, Octo, Farbwerk, and Farbwerk 360"
|
||||
tristate "Aquacomputer D5 Next, Octo, Quadro, Farbwerk, and Farbwerk 360"
|
||||
depends on USB_HID
|
||||
select CRC16
|
||||
help
|
||||
If you say yes here you get support for sensors and fans of
|
||||
the Aquacomputer D5 Next watercooling pump, Octo fan
|
||||
controller, Farbwerk and Farbwerk 360 RGB controllers, where
|
||||
the Aquacomputer D5 Next watercooling pump, Octo and Quadro fan
|
||||
controllers, Farbwerk and Farbwerk 360 RGB controllers, where
|
||||
available.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
@ -381,7 +382,7 @@ config SENSORS_ARM_SCPI
|
||||
|
||||
config SENSORS_ASB100
|
||||
tristate "Asus ASB100 Bach"
|
||||
depends on X86 && I2C
|
||||
depends on (X86 || COMPILE_TEST) && I2C
|
||||
select HWMON_VID
|
||||
help
|
||||
If you say yes here you get support for the ASB100 Bach sensor
|
||||
@ -626,7 +627,7 @@ config SENSORS_MC13783_ADC
|
||||
|
||||
config SENSORS_FSCHMD
|
||||
tristate "Fujitsu Siemens Computers sensor chips"
|
||||
depends on X86 && I2C
|
||||
depends on (X86 || COMPILE_TEST) && I2C
|
||||
help
|
||||
If you say yes here you get support for the following Fujitsu
|
||||
Siemens Computers (FSC) sensor chips: Poseidon, Scylla, Hermes,
|
||||
@ -1102,6 +1103,7 @@ config SENSORS_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
|
||||
@ -1357,12 +1359,15 @@ config SENSORS_LM90
|
||||
tristate "National Semiconductor LM90 and compatibles"
|
||||
depends on I2C
|
||||
help
|
||||
If you say yes here you get support for National Semiconductor LM90,
|
||||
LM86, LM89 and LM99, Analog Devices ADM1032, ADT7461, and ADT7461A,
|
||||
Maxim MAX6646, MAX6647, MAX6648, MAX6649, MAX6654, MAX6657, MAX6658,
|
||||
MAX6659, MAX6680, MAX6681, MAX6692, MAX6695, MAX6696,
|
||||
ON Semiconductor NCT1008, Winbond/Nuvoton W83L771W/G/AWG/ASG,
|
||||
Philips SA56004, GMT G781, Texas Instruments TMP451 and TMP461
|
||||
If you say yes here you get support for National Semiconductor LM84,
|
||||
LM90, LM86, LM89 and LM99, Analog Devices ADM1020, ADM2021, ADM1021A,
|
||||
ADM1023, ADM1032, ADT7461, ADT7461A, ADT7481, ADT7482, and ADT7483A,
|
||||
Maxim MAX1617, MAX6642, MAX6646, MAX6647, MAX6648, MAX6649, MAX6654,
|
||||
MAX6657, MAX6658, MAX6659, MAX6680, MAX6681, MAX6692, MAX6695,
|
||||
MAX6696,
|
||||
ON Semiconductor NCT1008, NCT210, NCT72, NCT214, NCT218,
|
||||
Winbond/Nuvoton W83L771W/G/AWG/ASG,
|
||||
Philips NE1618, SA56004, GMT G781, Texas Instruments TMP451 and TMP461
|
||||
sensor chips.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
|
@ -1,6 +1,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* hwmon driver for Aquacomputer devices (D5 Next, Farbwerk, Farbwerk 360, Octo)
|
||||
* hwmon driver for Aquacomputer devices (D5 Next, Farbwerk, Farbwerk 360, Octo,
|
||||
* Quadro)
|
||||
*
|
||||
* Aquacomputer devices send HID reports (with ID 0x01) every second to report
|
||||
* sensor values.
|
||||
@ -21,17 +22,19 @@
|
||||
|
||||
#define USB_VENDOR_ID_AQUACOMPUTER 0x0c70
|
||||
#define USB_PRODUCT_ID_FARBWERK 0xf00a
|
||||
#define USB_PRODUCT_ID_QUADRO 0xf00d
|
||||
#define USB_PRODUCT_ID_D5NEXT 0xf00e
|
||||
#define USB_PRODUCT_ID_FARBWERK360 0xf010
|
||||
#define USB_PRODUCT_ID_OCTO 0xf011
|
||||
|
||||
enum kinds { d5next, farbwerk, farbwerk360, octo };
|
||||
enum kinds { d5next, farbwerk, farbwerk360, octo, quadro };
|
||||
|
||||
static const char *const aqc_device_names[] = {
|
||||
[d5next] = "d5next",
|
||||
[farbwerk] = "farbwerk",
|
||||
[farbwerk360] = "farbwerk360",
|
||||
[octo] = "octo"
|
||||
[octo] = "octo",
|
||||
[quadro] = "quadro"
|
||||
};
|
||||
|
||||
#define DRIVER_NAME "aquacomputer_d5next"
|
||||
@ -54,60 +57,61 @@ static u8 secondary_ctrl_report[] = {
|
||||
0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x34, 0xC6
|
||||
};
|
||||
|
||||
/* Register offsets for all Aquacomputer devices */
|
||||
#define AQC_TEMP_SENSOR_SIZE 0x02
|
||||
#define AQC_TEMP_SENSOR_DISCONNECTED 0x7FFF
|
||||
#define AQC_FAN_PERCENT_OFFSET 0x00
|
||||
#define AQC_FAN_VOLTAGE_OFFSET 0x02
|
||||
#define AQC_FAN_CURRENT_OFFSET 0x04
|
||||
#define AQC_FAN_POWER_OFFSET 0x06
|
||||
#define AQC_FAN_SPEED_OFFSET 0x08
|
||||
|
||||
/* Register offsets for the D5 Next pump */
|
||||
#define D5NEXT_POWER_CYCLES 24
|
||||
#define D5NEXT_POWER_CYCLES 0x18
|
||||
#define D5NEXT_COOLANT_TEMP 0x57
|
||||
#define D5NEXT_NUM_FANS 2
|
||||
#define D5NEXT_NUM_SENSORS 1
|
||||
#define D5NEXT_PUMP_OFFSET 0x6c
|
||||
#define D5NEXT_FAN_OFFSET 0x5f
|
||||
#define D5NEXT_5V_VOLTAGE 0x39
|
||||
#define D5NEXT_12V_VOLTAGE 0x37
|
||||
#define D5NEXT_CTRL_REPORT_SIZE 0x329
|
||||
static u8 d5next_sensor_fan_offsets[] = { D5NEXT_PUMP_OFFSET, D5NEXT_FAN_OFFSET };
|
||||
|
||||
#define D5NEXT_COOLANT_TEMP 87
|
||||
|
||||
#define D5NEXT_PUMP_SPEED 116
|
||||
#define D5NEXT_FAN_SPEED 103
|
||||
|
||||
#define D5NEXT_PUMP_POWER 114
|
||||
#define D5NEXT_FAN_POWER 101
|
||||
|
||||
#define D5NEXT_PUMP_VOLTAGE 110
|
||||
#define D5NEXT_FAN_VOLTAGE 97
|
||||
#define D5NEXT_5V_VOLTAGE 57
|
||||
|
||||
#define D5NEXT_PUMP_CURRENT 112
|
||||
#define D5NEXT_FAN_CURRENT 99
|
||||
/* Pump and fan speed registers in D5 Next control report (from 0-100%) */
|
||||
static u16 d5next_ctrl_fan_offsets[] = { 0x97, 0x42 };
|
||||
|
||||
/* Register offsets for the Farbwerk RGB controller */
|
||||
#define FARBWERK_NUM_SENSORS 4
|
||||
#define FARBWERK_SENSOR_START 0x2f
|
||||
#define FARBWERK_SENSOR_SIZE 0x02
|
||||
#define FARBWERK_SENSOR_DISCONNECTED 0x7FFF
|
||||
|
||||
/* Register offsets for the Farbwerk 360 RGB controller */
|
||||
#define FARBWERK360_NUM_SENSORS 4
|
||||
#define FARBWERK360_SENSOR_START 0x32
|
||||
#define FARBWERK360_SENSOR_SIZE 0x02
|
||||
#define FARBWERK360_SENSOR_DISCONNECTED 0x7FFF
|
||||
|
||||
/* Register offsets for the Octo fan controller */
|
||||
#define OCTO_POWER_CYCLES 0x18
|
||||
#define OCTO_NUM_FANS 8
|
||||
#define OCTO_FAN_PERCENT_OFFSET 0x00
|
||||
#define OCTO_FAN_VOLTAGE_OFFSET 0x02
|
||||
#define OCTO_FAN_CURRENT_OFFSET 0x04
|
||||
#define OCTO_FAN_POWER_OFFSET 0x06
|
||||
#define OCTO_FAN_SPEED_OFFSET 0x08
|
||||
|
||||
static u8 octo_sensor_fan_offsets[] = { 0x7D, 0x8A, 0x97, 0xA4, 0xB1, 0xBE, 0xCB, 0xD8 };
|
||||
|
||||
#define OCTO_NUM_SENSORS 4
|
||||
#define OCTO_SENSOR_START 0x3D
|
||||
#define OCTO_SENSOR_SIZE 0x02
|
||||
#define OCTO_SENSOR_DISCONNECTED 0x7FFF
|
||||
|
||||
#define OCTO_CTRL_REPORT_SIZE 0x65F
|
||||
#define OCTO_CTRL_REPORT_CHECKSUM_OFFSET 0x65D
|
||||
#define OCTO_CTRL_REPORT_CHECKSUM_START 0x01
|
||||
#define OCTO_CTRL_REPORT_CHECKSUM_LENGTH 0x65C
|
||||
#define OCTO_CTRL_REPORT_SIZE 0x65F
|
||||
static u8 octo_sensor_fan_offsets[] = { 0x7D, 0x8A, 0x97, 0xA4, 0xB1, 0xBE, 0xCB, 0xD8 };
|
||||
|
||||
/* Fan speed registers in Octo control report (from 0-100%) */
|
||||
static u16 octo_ctrl_fan_offsets[] = { 0x5B, 0xB0, 0x105, 0x15A, 0x1AF, 0x204, 0x259, 0x2AE };
|
||||
|
||||
/* Register offsets for the Quadro fan controller */
|
||||
#define QUADRO_POWER_CYCLES 0x18
|
||||
#define QUADRO_NUM_FANS 4
|
||||
#define QUADRO_NUM_SENSORS 4
|
||||
#define QUADRO_SENSOR_START 0x34
|
||||
#define QUADRO_CTRL_REPORT_SIZE 0x3c1
|
||||
#define QUADRO_FLOW_SENSOR_OFFSET 0x6e
|
||||
static u8 quadro_sensor_fan_offsets[] = { 0x70, 0x7D, 0x8A, 0x97 };
|
||||
|
||||
/* Fan speed registers in Quadro control report (from 0-100%) */
|
||||
static u16 quadro_ctrl_fan_offsets[] = { 0x36, 0x8b, 0xe0, 0x135 };
|
||||
|
||||
/* Labels for D5 Next */
|
||||
static const char *const label_d5next_temp[] = {
|
||||
"Coolant temp"
|
||||
@ -126,7 +130,8 @@ static const char *const label_d5next_power[] = {
|
||||
static const char *const label_d5next_voltages[] = {
|
||||
"Pump voltage",
|
||||
"Fan voltage",
|
||||
"+5V voltage"
|
||||
"+5V voltage",
|
||||
"+12V voltage"
|
||||
};
|
||||
|
||||
static const char *const label_d5next_current[] = {
|
||||
@ -134,7 +139,7 @@ static const char *const label_d5next_current[] = {
|
||||
"Fan current"
|
||||
};
|
||||
|
||||
/* Labels for Farbwerk, Farbwerk 360 and Octo temperature sensors */
|
||||
/* Labels for Farbwerk, Farbwerk 360 and Octo and Quadro temperature sensors */
|
||||
static const char *const label_temp_sensors[] = {
|
||||
"Sensor 1",
|
||||
"Sensor 2",
|
||||
@ -142,7 +147,7 @@ static const char *const label_temp_sensors[] = {
|
||||
"Sensor 4"
|
||||
};
|
||||
|
||||
/* Labels for Octo */
|
||||
/* Labels for Octo and Quadro (except speed) */
|
||||
static const char *const label_fan_speed[] = {
|
||||
"Fan 1 speed",
|
||||
"Fan 2 speed",
|
||||
@ -187,6 +192,15 @@ static const char *const label_fan_current[] = {
|
||||
"Fan 8 current"
|
||||
};
|
||||
|
||||
/* Labels for Quadro fan speeds */
|
||||
static const char *const label_quadro_speeds[] = {
|
||||
"Fan 1 speed",
|
||||
"Fan 2 speed",
|
||||
"Fan 3 speed",
|
||||
"Fan 4 speed",
|
||||
"Flow speed [dL/h]"
|
||||
};
|
||||
|
||||
struct aqc_data {
|
||||
struct hid_device *hdev;
|
||||
struct device *hwmon_dev;
|
||||
@ -201,11 +215,19 @@ struct aqc_data {
|
||||
int checksum_length;
|
||||
int checksum_offset;
|
||||
|
||||
int num_fans;
|
||||
u8 *fan_sensor_offsets;
|
||||
u16 *fan_ctrl_offsets;
|
||||
int num_temp_sensors;
|
||||
int temp_sensor_start_offset;
|
||||
u16 power_cycle_count_offset;
|
||||
u8 flow_sensor_offset;
|
||||
|
||||
/* General info, same across all devices */
|
||||
u32 serial_number[2];
|
||||
u16 firmware_version;
|
||||
|
||||
/* How many times the device was powered on */
|
||||
/* How many times the device was powered on, if available */
|
||||
u32 power_cycles;
|
||||
|
||||
/* Sensor values */
|
||||
@ -323,56 +345,47 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3
|
||||
|
||||
switch (type) {
|
||||
case hwmon_temp:
|
||||
switch (priv->kind) {
|
||||
case d5next:
|
||||
if (channel == 0)
|
||||
return 0444;
|
||||
break;
|
||||
case farbwerk:
|
||||
case farbwerk360:
|
||||
case octo:
|
||||
if (channel < priv->num_temp_sensors)
|
||||
return 0444;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case hwmon_pwm:
|
||||
switch (priv->kind) {
|
||||
case octo:
|
||||
if (priv->fan_ctrl_offsets && channel < priv->num_fans) {
|
||||
switch (attr) {
|
||||
case hwmon_pwm_input:
|
||||
return 0644;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case hwmon_fan:
|
||||
case hwmon_power:
|
||||
case hwmon_curr:
|
||||
switch (priv->kind) {
|
||||
case d5next:
|
||||
if (channel < 2)
|
||||
case quadro:
|
||||
/* Special case to support flow sensor */
|
||||
if (channel < priv->num_fans + 1)
|
||||
return 0444;
|
||||
break;
|
||||
case octo:
|
||||
return 0444;
|
||||
default:
|
||||
if (channel < priv->num_fans)
|
||||
return 0444;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case hwmon_power:
|
||||
case hwmon_curr:
|
||||
if (channel < priv->num_fans)
|
||||
return 0444;
|
||||
break;
|
||||
case hwmon_in:
|
||||
switch (priv->kind) {
|
||||
case d5next:
|
||||
if (channel < 3)
|
||||
/* Special case to support +5V and +12V voltage sensors */
|
||||
if (channel < priv->num_fans + 2)
|
||||
return 0444;
|
||||
break;
|
||||
case octo:
|
||||
return 0444;
|
||||
default:
|
||||
if (channel < priv->num_fans)
|
||||
return 0444;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@ -406,16 +419,12 @@ static int aqc_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
|
||||
*val = priv->power_input[channel];
|
||||
break;
|
||||
case hwmon_pwm:
|
||||
switch (priv->kind) {
|
||||
case octo:
|
||||
ret = aqc_get_ctrl_val(priv, octo_ctrl_fan_offsets[channel]);
|
||||
if (priv->fan_ctrl_offsets) {
|
||||
ret = aqc_get_ctrl_val(priv, priv->fan_ctrl_offsets[channel]);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = aqc_percent_to_pwm(ret);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case hwmon_in:
|
||||
@ -469,19 +478,15 @@ static int aqc_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
|
||||
case hwmon_pwm:
|
||||
switch (attr) {
|
||||
case hwmon_pwm_input:
|
||||
switch (priv->kind) {
|
||||
case octo:
|
||||
if (priv->fan_ctrl_offsets) {
|
||||
pwm_value = aqc_pwm_to_percent(val);
|
||||
if (pwm_value < 0)
|
||||
return pwm_value;
|
||||
|
||||
ret = aqc_set_ctrl_val(priv, octo_ctrl_fan_offsets[channel],
|
||||
ret = aqc_set_ctrl_val(priv, priv->fan_ctrl_offsets[channel],
|
||||
pwm_value);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@ -576,76 +581,42 @@ static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8
|
||||
priv->serial_number[1] = get_unaligned_be16(data + SERIAL_SECOND_PART);
|
||||
priv->firmware_version = get_unaligned_be16(data + FIRMWARE_VERSION);
|
||||
|
||||
/* Sensor readings */
|
||||
/* Temperature sensor readings */
|
||||
for (i = 0; i < priv->num_temp_sensors; i++) {
|
||||
sensor_value = get_unaligned_be16(data +
|
||||
priv->temp_sensor_start_offset +
|
||||
i * AQC_TEMP_SENSOR_SIZE);
|
||||
if (sensor_value == AQC_TEMP_SENSOR_DISCONNECTED)
|
||||
priv->temp_input[i] = -ENODATA;
|
||||
else
|
||||
priv->temp_input[i] = sensor_value * 10;
|
||||
}
|
||||
|
||||
/* Fan speed and related readings */
|
||||
for (i = 0; i < priv->num_fans; i++) {
|
||||
priv->speed_input[i] =
|
||||
get_unaligned_be16(data + priv->fan_sensor_offsets[i] + AQC_FAN_SPEED_OFFSET);
|
||||
priv->power_input[i] =
|
||||
get_unaligned_be16(data + priv->fan_sensor_offsets[i] +
|
||||
AQC_FAN_POWER_OFFSET) * 10000;
|
||||
priv->voltage_input[i] =
|
||||
get_unaligned_be16(data + priv->fan_sensor_offsets[i] +
|
||||
AQC_FAN_VOLTAGE_OFFSET) * 10;
|
||||
priv->current_input[i] =
|
||||
get_unaligned_be16(data + priv->fan_sensor_offsets[i] + AQC_FAN_CURRENT_OFFSET);
|
||||
}
|
||||
|
||||
if (priv->power_cycle_count_offset != 0)
|
||||
priv->power_cycles = get_unaligned_be32(data + priv->power_cycle_count_offset);
|
||||
|
||||
/* Special-case sensor readings */
|
||||
switch (priv->kind) {
|
||||
case d5next:
|
||||
priv->power_cycles = get_unaligned_be32(data + D5NEXT_POWER_CYCLES);
|
||||
|
||||
priv->temp_input[0] = get_unaligned_be16(data + D5NEXT_COOLANT_TEMP) * 10;
|
||||
|
||||
priv->speed_input[0] = get_unaligned_be16(data + D5NEXT_PUMP_SPEED);
|
||||
priv->speed_input[1] = get_unaligned_be16(data + D5NEXT_FAN_SPEED);
|
||||
|
||||
priv->power_input[0] = get_unaligned_be16(data + D5NEXT_PUMP_POWER) * 10000;
|
||||
priv->power_input[1] = get_unaligned_be16(data + D5NEXT_FAN_POWER) * 10000;
|
||||
|
||||
priv->voltage_input[0] = get_unaligned_be16(data + D5NEXT_PUMP_VOLTAGE) * 10;
|
||||
priv->voltage_input[1] = get_unaligned_be16(data + D5NEXT_FAN_VOLTAGE) * 10;
|
||||
priv->voltage_input[2] = get_unaligned_be16(data + D5NEXT_5V_VOLTAGE) * 10;
|
||||
|
||||
priv->current_input[0] = get_unaligned_be16(data + D5NEXT_PUMP_CURRENT);
|
||||
priv->current_input[1] = get_unaligned_be16(data + D5NEXT_FAN_CURRENT);
|
||||
priv->voltage_input[3] = get_unaligned_be16(data + D5NEXT_12V_VOLTAGE) * 10;
|
||||
break;
|
||||
case farbwerk:
|
||||
/* Temperature sensor readings */
|
||||
for (i = 0; i < FARBWERK_NUM_SENSORS; i++) {
|
||||
sensor_value = get_unaligned_be16(data + FARBWERK_SENSOR_START +
|
||||
i * FARBWERK_SENSOR_SIZE);
|
||||
if (sensor_value == FARBWERK_SENSOR_DISCONNECTED)
|
||||
priv->temp_input[i] = -ENODATA;
|
||||
else
|
||||
priv->temp_input[i] = sensor_value * 10;
|
||||
}
|
||||
break;
|
||||
case farbwerk360:
|
||||
/* Temperature sensor readings */
|
||||
for (i = 0; i < FARBWERK360_NUM_SENSORS; i++) {
|
||||
sensor_value = get_unaligned_be16(data + FARBWERK360_SENSOR_START +
|
||||
i * FARBWERK360_SENSOR_SIZE);
|
||||
if (sensor_value == FARBWERK360_SENSOR_DISCONNECTED)
|
||||
priv->temp_input[i] = -ENODATA;
|
||||
else
|
||||
priv->temp_input[i] = sensor_value * 10;
|
||||
}
|
||||
break;
|
||||
case octo:
|
||||
priv->power_cycles = get_unaligned_be32(data + OCTO_POWER_CYCLES);
|
||||
|
||||
/* Fan speed and related readings */
|
||||
for (i = 0; i < OCTO_NUM_FANS; i++) {
|
||||
priv->speed_input[i] =
|
||||
get_unaligned_be16(data + octo_sensor_fan_offsets[i] +
|
||||
OCTO_FAN_SPEED_OFFSET);
|
||||
priv->power_input[i] =
|
||||
get_unaligned_be16(data + octo_sensor_fan_offsets[i] +
|
||||
OCTO_FAN_POWER_OFFSET) * 10000;
|
||||
priv->voltage_input[i] =
|
||||
get_unaligned_be16(data + octo_sensor_fan_offsets[i] +
|
||||
OCTO_FAN_VOLTAGE_OFFSET) * 10;
|
||||
priv->current_input[i] =
|
||||
get_unaligned_be16(data + octo_sensor_fan_offsets[i] +
|
||||
OCTO_FAN_CURRENT_OFFSET);
|
||||
}
|
||||
|
||||
/* Temperature sensor readings */
|
||||
for (i = 0; i < OCTO_NUM_SENSORS; i++) {
|
||||
sensor_value = get_unaligned_be16(data + OCTO_SENSOR_START +
|
||||
i * OCTO_SENSOR_SIZE);
|
||||
if (sensor_value == OCTO_SENSOR_DISCONNECTED)
|
||||
priv->temp_input[i] = -ENODATA;
|
||||
else
|
||||
priv->temp_input[i] = sensor_value * 10;
|
||||
}
|
||||
case quadro:
|
||||
priv->speed_input[4] = get_unaligned_be16(data + priv->flow_sensor_offset);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -699,14 +670,8 @@ static void aqc_debugfs_init(struct aqc_data *priv)
|
||||
debugfs_create_file("serial_number", 0444, priv->debugfs, priv, &serial_number_fops);
|
||||
debugfs_create_file("firmware_version", 0444, priv->debugfs, priv, &firmware_version_fops);
|
||||
|
||||
switch (priv->kind) {
|
||||
case d5next:
|
||||
case octo:
|
||||
if (priv->power_cycle_count_offset != 0)
|
||||
debugfs_create_file("power_cycles", 0444, priv->debugfs, priv, &power_cycles_fops);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
@ -747,6 +712,14 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
case USB_PRODUCT_ID_D5NEXT:
|
||||
priv->kind = d5next;
|
||||
|
||||
priv->num_fans = D5NEXT_NUM_FANS;
|
||||
priv->fan_sensor_offsets = d5next_sensor_fan_offsets;
|
||||
priv->fan_ctrl_offsets = d5next_ctrl_fan_offsets;
|
||||
priv->num_temp_sensors = D5NEXT_NUM_SENSORS;
|
||||
priv->temp_sensor_start_offset = D5NEXT_COOLANT_TEMP;
|
||||
priv->power_cycle_count_offset = D5NEXT_POWER_CYCLES;
|
||||
priv->buffer_size = D5NEXT_CTRL_REPORT_SIZE;
|
||||
|
||||
priv->temp_label = label_d5next_temp;
|
||||
priv->speed_label = label_d5next_speeds;
|
||||
priv->power_label = label_d5next_power;
|
||||
@ -756,19 +729,29 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
case USB_PRODUCT_ID_FARBWERK:
|
||||
priv->kind = farbwerk;
|
||||
|
||||
priv->num_fans = 0;
|
||||
priv->num_temp_sensors = FARBWERK_NUM_SENSORS;
|
||||
priv->temp_sensor_start_offset = FARBWERK_SENSOR_START;
|
||||
priv->temp_label = label_temp_sensors;
|
||||
break;
|
||||
case USB_PRODUCT_ID_FARBWERK360:
|
||||
priv->kind = farbwerk360;
|
||||
|
||||
priv->num_fans = 0;
|
||||
priv->num_temp_sensors = FARBWERK360_NUM_SENSORS;
|
||||
priv->temp_sensor_start_offset = FARBWERK360_SENSOR_START;
|
||||
priv->temp_label = label_temp_sensors;
|
||||
break;
|
||||
case USB_PRODUCT_ID_OCTO:
|
||||
priv->kind = octo;
|
||||
|
||||
priv->num_fans = OCTO_NUM_FANS;
|
||||
priv->fan_sensor_offsets = octo_sensor_fan_offsets;
|
||||
priv->fan_ctrl_offsets = octo_ctrl_fan_offsets;
|
||||
priv->num_temp_sensors = OCTO_NUM_SENSORS;
|
||||
priv->temp_sensor_start_offset = OCTO_SENSOR_START;
|
||||
priv->power_cycle_count_offset = OCTO_POWER_CYCLES;
|
||||
priv->buffer_size = OCTO_CTRL_REPORT_SIZE;
|
||||
priv->checksum_start = OCTO_CTRL_REPORT_CHECKSUM_START;
|
||||
priv->checksum_length = OCTO_CTRL_REPORT_CHECKSUM_LENGTH;
|
||||
priv->checksum_offset = OCTO_CTRL_REPORT_CHECKSUM_OFFSET;
|
||||
|
||||
priv->temp_label = label_temp_sensors;
|
||||
priv->speed_label = label_fan_speed;
|
||||
@ -776,10 +759,34 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
||||
priv->voltage_label = label_fan_voltage;
|
||||
priv->current_label = label_fan_current;
|
||||
break;
|
||||
case USB_PRODUCT_ID_QUADRO:
|
||||
priv->kind = quadro;
|
||||
|
||||
priv->num_fans = QUADRO_NUM_FANS;
|
||||
priv->fan_sensor_offsets = quadro_sensor_fan_offsets;
|
||||
priv->fan_ctrl_offsets = quadro_ctrl_fan_offsets;
|
||||
priv->num_temp_sensors = QUADRO_NUM_SENSORS;
|
||||
priv->temp_sensor_start_offset = QUADRO_SENSOR_START;
|
||||
priv->power_cycle_count_offset = QUADRO_POWER_CYCLES;
|
||||
priv->buffer_size = QUADRO_CTRL_REPORT_SIZE;
|
||||
priv->flow_sensor_offset = QUADRO_FLOW_SENSOR_OFFSET;
|
||||
|
||||
priv->temp_label = label_temp_sensors;
|
||||
priv->speed_label = label_quadro_speeds;
|
||||
priv->power_label = label_fan_power;
|
||||
priv->voltage_label = label_fan_voltage;
|
||||
priv->current_label = label_fan_current;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (priv->buffer_size != 0) {
|
||||
priv->checksum_start = 0x01;
|
||||
priv->checksum_length = priv->buffer_size - 3;
|
||||
priv->checksum_offset = priv->buffer_size - 2;
|
||||
}
|
||||
|
||||
priv->name = aqc_device_names[priv->kind];
|
||||
|
||||
priv->buffer = devm_kzalloc(&hdev->dev, priv->buffer_size, GFP_KERNEL);
|
||||
@ -825,6 +832,7 @@ static const struct hid_device_id aqc_table[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_FARBWERK) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_FARBWERK360) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_OCTO) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_QUADRO) },
|
||||
{ }
|
||||
};
|
||||
|
||||
|
@ -159,7 +159,7 @@
|
||||
* 11: reserved.
|
||||
*/
|
||||
#define M_TACH_MODE 0x02 /* 10b */
|
||||
#define M_TACH_UNIT 0x0210
|
||||
#define M_TACH_UNIT 0x0420
|
||||
#define INIT_FAN_CTRL 0xFF
|
||||
|
||||
/* How long we sleep in us while waiting for an RPM result. */
|
||||
|
@ -54,6 +54,10 @@ static char *mutex_path_override;
|
||||
/* ACPI mutex for locking access to the EC for the firmware */
|
||||
#define ASUS_HW_ACCESS_MUTEX_ASMX "\\AMW0.ASMX"
|
||||
|
||||
#define ASUS_HW_ACCESS_MUTEX_RMTW_ASMX "\\RMTW.ASMX"
|
||||
|
||||
#define ASUS_HW_ACCESS_MUTEX_SB_PCI0_SBRG_SIO1_MUT0 "\\_SB_.PCI0.SBRG.SIO1.MUT0"
|
||||
|
||||
#define MAX_IDENTICAL_BOARD_VARIATIONS 3
|
||||
|
||||
/* Moniker for the ACPI global lock (':' is not allowed in ASL identifiers) */
|
||||
@ -119,6 +123,18 @@ enum ec_sensors {
|
||||
ec_sensor_temp_water_in,
|
||||
/* "Water_Out" temperature sensor reading [℃] */
|
||||
ec_sensor_temp_water_out,
|
||||
/* "Water_Block_In" temperature sensor reading [℃] */
|
||||
ec_sensor_temp_water_block_in,
|
||||
/* "Water_Block_Out" temperature sensor reading [℃] */
|
||||
ec_sensor_temp_water_block_out,
|
||||
/* "T_sensor_2" temperature sensor reading [℃] */
|
||||
ec_sensor_temp_t_sensor_2,
|
||||
/* "Extra_1" temperature sensor reading [℃] */
|
||||
ec_sensor_temp_sensor_extra_1,
|
||||
/* "Extra_2" temperature sensor reading [℃] */
|
||||
ec_sensor_temp_sensor_extra_2,
|
||||
/* "Extra_3" temperature sensor reading [℃] */
|
||||
ec_sensor_temp_sensor_extra_3,
|
||||
};
|
||||
|
||||
#define SENSOR_TEMP_CHIPSET BIT(ec_sensor_temp_chipset)
|
||||
@ -134,11 +150,19 @@ enum ec_sensors {
|
||||
#define SENSOR_CURR_CPU BIT(ec_sensor_curr_cpu)
|
||||
#define SENSOR_TEMP_WATER_IN BIT(ec_sensor_temp_water_in)
|
||||
#define SENSOR_TEMP_WATER_OUT BIT(ec_sensor_temp_water_out)
|
||||
#define SENSOR_TEMP_WATER_BLOCK_IN BIT(ec_sensor_temp_water_block_in)
|
||||
#define SENSOR_TEMP_WATER_BLOCK_OUT BIT(ec_sensor_temp_water_block_out)
|
||||
#define SENSOR_TEMP_T_SENSOR_2 BIT(ec_sensor_temp_t_sensor_2)
|
||||
#define SENSOR_TEMP_SENSOR_EXTRA_1 BIT(ec_sensor_temp_sensor_extra_1)
|
||||
#define SENSOR_TEMP_SENSOR_EXTRA_2 BIT(ec_sensor_temp_sensor_extra_2)
|
||||
#define SENSOR_TEMP_SENSOR_EXTRA_3 BIT(ec_sensor_temp_sensor_extra_3)
|
||||
|
||||
enum board_family {
|
||||
family_unknown,
|
||||
family_amd_400_series,
|
||||
family_amd_500_series,
|
||||
family_intel_300_series,
|
||||
family_intel_600_series
|
||||
};
|
||||
|
||||
/* All the known sensors for ASUS EC controllers */
|
||||
@ -195,12 +219,53 @@ static const struct ec_sensor_info sensors_family_amd_500[] = {
|
||||
EC_SENSOR("Water_In", hwmon_temp, 1, 0x01, 0x00),
|
||||
[ec_sensor_temp_water_out] =
|
||||
EC_SENSOR("Water_Out", hwmon_temp, 1, 0x01, 0x01),
|
||||
[ec_sensor_temp_water_block_in] =
|
||||
EC_SENSOR("Water_Block_In", hwmon_temp, 1, 0x01, 0x02),
|
||||
[ec_sensor_temp_water_block_out] =
|
||||
EC_SENSOR("Water_Block_Out", hwmon_temp, 1, 0x01, 0x03),
|
||||
[ec_sensor_temp_sensor_extra_1] =
|
||||
EC_SENSOR("Extra_1", hwmon_temp, 1, 0x01, 0x09),
|
||||
[ec_sensor_temp_t_sensor_2] =
|
||||
EC_SENSOR("T_sensor_2", hwmon_temp, 1, 0x01, 0x0a),
|
||||
[ec_sensor_temp_sensor_extra_2] =
|
||||
EC_SENSOR("Extra_2", hwmon_temp, 1, 0x01, 0x0b),
|
||||
[ec_sensor_temp_sensor_extra_3] =
|
||||
EC_SENSOR("Extra_3", hwmon_temp, 1, 0x01, 0x0c),
|
||||
};
|
||||
|
||||
static const struct ec_sensor_info sensors_family_intel_300[] = {
|
||||
[ec_sensor_temp_chipset] =
|
||||
EC_SENSOR("Chipset", hwmon_temp, 1, 0x00, 0x3a),
|
||||
[ec_sensor_temp_cpu] = EC_SENSOR("CPU", hwmon_temp, 1, 0x00, 0x3b),
|
||||
[ec_sensor_temp_mb] =
|
||||
EC_SENSOR("Motherboard", hwmon_temp, 1, 0x00, 0x3c),
|
||||
[ec_sensor_temp_t_sensor] =
|
||||
EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x3d),
|
||||
[ec_sensor_temp_vrm] = EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x3e),
|
||||
[ec_sensor_fan_cpu_opt] =
|
||||
EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xb0),
|
||||
[ec_sensor_fan_vrm_hs] = EC_SENSOR("VRM HS", hwmon_fan, 2, 0x00, 0xb2),
|
||||
[ec_sensor_fan_water_flow] =
|
||||
EC_SENSOR("Water_Flow", hwmon_fan, 2, 0x00, 0xbc),
|
||||
[ec_sensor_temp_water_in] =
|
||||
EC_SENSOR("Water_In", hwmon_temp, 1, 0x01, 0x00),
|
||||
[ec_sensor_temp_water_out] =
|
||||
EC_SENSOR("Water_Out", hwmon_temp, 1, 0x01, 0x01),
|
||||
};
|
||||
|
||||
static const struct ec_sensor_info sensors_family_intel_600[] = {
|
||||
[ec_sensor_temp_t_sensor] =
|
||||
EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x3d),
|
||||
[ec_sensor_temp_vrm] = EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x3e),
|
||||
};
|
||||
|
||||
/* Shortcuts for common combinations */
|
||||
#define SENSOR_SET_TEMP_CHIPSET_CPU_MB \
|
||||
(SENSOR_TEMP_CHIPSET | SENSOR_TEMP_CPU | SENSOR_TEMP_MB)
|
||||
#define SENSOR_SET_TEMP_WATER (SENSOR_TEMP_WATER_IN | SENSOR_TEMP_WATER_OUT)
|
||||
#define SENSOR_SET_WATER_BLOCK \
|
||||
(SENSOR_TEMP_WATER_BLOCK_IN | SENSOR_TEMP_WATER_BLOCK_OUT)
|
||||
|
||||
|
||||
struct ec_board_info {
|
||||
const char *board_names[MAX_IDENTICAL_BOARD_VARIATIONS];
|
||||
@ -272,6 +337,18 @@ static const struct ec_board_info board_info[] = {
|
||||
.mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
|
||||
.family = family_amd_500_series,
|
||||
},
|
||||
{
|
||||
.board_names = {
|
||||
"ROG MAXIMUS XI HERO",
|
||||
"ROG MAXIMUS XI HERO (WI-FI)",
|
||||
},
|
||||
.sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
|
||||
SENSOR_TEMP_T_SENSOR |
|
||||
SENSOR_TEMP_VRM | SENSOR_SET_TEMP_WATER |
|
||||
SENSOR_FAN_CPU_OPT | SENSOR_FAN_WATER_FLOW,
|
||||
.mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
|
||||
.family = family_intel_300_series,
|
||||
},
|
||||
{
|
||||
.board_names = {"ROG CROSSHAIR VIII IMPACT"},
|
||||
.sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
|
||||
@ -324,12 +401,31 @@ static const struct ec_board_info board_info[] = {
|
||||
},
|
||||
{
|
||||
.board_names = {"ROG STRIX X570-I GAMING"},
|
||||
.sensors = SENSOR_TEMP_T_SENSOR | SENSOR_FAN_VRM_HS |
|
||||
SENSOR_FAN_CHIPSET | SENSOR_CURR_CPU |
|
||||
SENSOR_IN_CPU_CORE,
|
||||
.sensors = SENSOR_TEMP_CHIPSET | SENSOR_TEMP_VRM |
|
||||
SENSOR_TEMP_T_SENSOR |
|
||||
SENSOR_FAN_VRM_HS | SENSOR_FAN_CHIPSET |
|
||||
SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE,
|
||||
.mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
|
||||
.family = family_amd_500_series,
|
||||
},
|
||||
{
|
||||
.board_names = {"ROG STRIX Z690-A GAMING WIFI D4"},
|
||||
.sensors = SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM,
|
||||
.mutex_path = ASUS_HW_ACCESS_MUTEX_RMTW_ASMX,
|
||||
.family = family_intel_600_series,
|
||||
},
|
||||
{
|
||||
.board_names = {"ROG ZENITH II EXTREME"},
|
||||
.sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_T_SENSOR |
|
||||
SENSOR_TEMP_VRM | SENSOR_SET_TEMP_WATER |
|
||||
SENSOR_FAN_CPU_OPT | SENSOR_FAN_CHIPSET | SENSOR_FAN_VRM_HS |
|
||||
SENSOR_FAN_WATER_FLOW | SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE |
|
||||
SENSOR_SET_WATER_BLOCK |
|
||||
SENSOR_TEMP_T_SENSOR_2 | SENSOR_TEMP_SENSOR_EXTRA_1 |
|
||||
SENSOR_TEMP_SENSOR_EXTRA_2 | SENSOR_TEMP_SENSOR_EXTRA_3,
|
||||
.mutex_path = ASUS_HW_ACCESS_MUTEX_SB_PCI0_SBRG_SIO1_MUT0,
|
||||
.family = family_amd_500_series,
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
@ -799,6 +895,12 @@ static int __init asus_ec_probe(struct platform_device *pdev)
|
||||
case family_amd_500_series:
|
||||
ec_data->sensors_info = sensors_family_amd_500;
|
||||
break;
|
||||
case family_intel_300_series:
|
||||
ec_data->sensors_info = sensors_family_intel_300;
|
||||
break;
|
||||
case family_intel_600_series:
|
||||
ec_data->sensors_info = sensors_family_intel_600;
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "Unknown board family: %d",
|
||||
ec_data->board_info->family);
|
||||
|
@ -514,22 +514,20 @@ static int asus_wmi_configure_sensor_setup(struct device *dev,
|
||||
int i, idx;
|
||||
int err;
|
||||
|
||||
temp_sensor = devm_kcalloc(dev, 1, sizeof(*temp_sensor), GFP_KERNEL);
|
||||
if (!temp_sensor)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < sensor_data->wmi.sensor_count; i++) {
|
||||
err = asus_wmi_sensor_info(i, temp_sensor);
|
||||
struct asus_wmi_sensor_info sensor;
|
||||
|
||||
err = asus_wmi_sensor_info(i, &sensor);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
switch (temp_sensor->data_type) {
|
||||
switch (sensor.data_type) {
|
||||
case TEMPERATURE_C:
|
||||
case VOLTAGE:
|
||||
case CURRENT:
|
||||
case FAN_RPM:
|
||||
case WATER_FLOW:
|
||||
type = asus_data_types[temp_sensor->data_type];
|
||||
type = asus_data_types[sensor.data_type];
|
||||
if (!nr_count[type])
|
||||
nr_types++;
|
||||
nr_count[type]++;
|
||||
|
@ -130,7 +130,7 @@ struct smm_regs {
|
||||
unsigned int edx;
|
||||
unsigned int esi;
|
||||
unsigned int edi;
|
||||
} __packed;
|
||||
};
|
||||
|
||||
static const char * const temp_labels[] = {
|
||||
"CPU",
|
||||
@ -175,77 +175,35 @@ static int i8k_smm_func(void *par)
|
||||
struct smm_regs *regs = par;
|
||||
int eax = regs->eax;
|
||||
int ebx = regs->ebx;
|
||||
unsigned char carry;
|
||||
long long duration;
|
||||
int rc;
|
||||
|
||||
/* SMM requires CPU 0 */
|
||||
if (smp_processor_id() != 0)
|
||||
return -EBUSY;
|
||||
|
||||
#if defined(CONFIG_X86_64)
|
||||
asm volatile("pushq %%rax\n\t"
|
||||
"movl 0(%%rax),%%edx\n\t"
|
||||
"pushq %%rdx\n\t"
|
||||
"movl 4(%%rax),%%ebx\n\t"
|
||||
"movl 8(%%rax),%%ecx\n\t"
|
||||
"movl 12(%%rax),%%edx\n\t"
|
||||
"movl 16(%%rax),%%esi\n\t"
|
||||
"movl 20(%%rax),%%edi\n\t"
|
||||
"popq %%rax\n\t"
|
||||
"out %%al,$0xb2\n\t"
|
||||
"out %%al,$0x84\n\t"
|
||||
"xchgq %%rax,(%%rsp)\n\t"
|
||||
"movl %%ebx,4(%%rax)\n\t"
|
||||
"movl %%ecx,8(%%rax)\n\t"
|
||||
"movl %%edx,12(%%rax)\n\t"
|
||||
"movl %%esi,16(%%rax)\n\t"
|
||||
"movl %%edi,20(%%rax)\n\t"
|
||||
"popq %%rdx\n\t"
|
||||
"movl %%edx,0(%%rax)\n\t"
|
||||
"pushfq\n\t"
|
||||
"popq %%rax\n\t"
|
||||
"andl $1,%%eax\n"
|
||||
: "=a"(rc)
|
||||
: "a"(regs)
|
||||
: "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory");
|
||||
#else
|
||||
asm volatile("pushl %%eax\n\t"
|
||||
"movl 0(%%eax),%%edx\n\t"
|
||||
"push %%edx\n\t"
|
||||
"movl 4(%%eax),%%ebx\n\t"
|
||||
"movl 8(%%eax),%%ecx\n\t"
|
||||
"movl 12(%%eax),%%edx\n\t"
|
||||
"movl 16(%%eax),%%esi\n\t"
|
||||
"movl 20(%%eax),%%edi\n\t"
|
||||
"popl %%eax\n\t"
|
||||
"out %%al,$0xb2\n\t"
|
||||
"out %%al,$0x84\n\t"
|
||||
"xchgl %%eax,(%%esp)\n\t"
|
||||
"movl %%ebx,4(%%eax)\n\t"
|
||||
"movl %%ecx,8(%%eax)\n\t"
|
||||
"movl %%edx,12(%%eax)\n\t"
|
||||
"movl %%esi,16(%%eax)\n\t"
|
||||
"movl %%edi,20(%%eax)\n\t"
|
||||
"popl %%edx\n\t"
|
||||
"movl %%edx,0(%%eax)\n\t"
|
||||
"lahf\n\t"
|
||||
"shrl $8,%%eax\n\t"
|
||||
"andl $1,%%eax\n"
|
||||
: "=a"(rc)
|
||||
: "a"(regs)
|
||||
: "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory");
|
||||
#endif
|
||||
if (rc != 0 || (regs->eax & 0xffff) == 0xffff || regs->eax == eax)
|
||||
rc = -EINVAL;
|
||||
asm volatile("out %%al,$0xb2\n\t"
|
||||
"out %%al,$0x84\n\t"
|
||||
"setc %0\n"
|
||||
: "=mr" (carry),
|
||||
"+a" (regs->eax),
|
||||
"+b" (regs->ebx),
|
||||
"+c" (regs->ecx),
|
||||
"+d" (regs->edx),
|
||||
"+S" (regs->esi),
|
||||
"+D" (regs->edi));
|
||||
|
||||
duration = ktime_us_delta(ktime_get(), calltime);
|
||||
pr_debug("smm(0x%.4x 0x%.4x) = 0x%.4x (took %7lld usecs)\n", eax, ebx,
|
||||
(rc ? 0xffff : regs->eax & 0xffff), duration);
|
||||
pr_debug("smm(0x%.4x 0x%.4x) = 0x%.4x carry: %d (took %7lld usecs)\n",
|
||||
eax, ebx, regs->eax & 0xffff, carry, duration);
|
||||
|
||||
if (duration > DELL_SMM_MAX_DURATION)
|
||||
pr_warn_once("SMM call took %lld usecs!\n", duration);
|
||||
|
||||
return rc;
|
||||
if (carry || (regs->eax & 0xffff) == 0xffff || regs->eax == eax)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1131,6 +1089,13 @@ static const struct i8k_config_data i8k_config_data[] __initconst = {
|
||||
};
|
||||
|
||||
static const struct dmi_system_id i8k_dmi_table[] __initconst = {
|
||||
{
|
||||
.ident = "Dell G5 5590",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "G5 5590"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "Dell Inspiron",
|
||||
.matches = {
|
||||
@ -1365,6 +1330,14 @@ static const struct dmi_system_id i8k_whitelist_fan_control[] __initconst = {
|
||||
},
|
||||
.driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3],
|
||||
},
|
||||
{
|
||||
.ident = "Dell XPS 13 7390",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "XPS 13 7390"),
|
||||
},
|
||||
.driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3],
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
|
@ -621,3 +621,4 @@ module_exit(drivetemp_exit);
|
||||
MODULE_AUTHOR("Guenter Roeck <linus@roeck-us.net>");
|
||||
MODULE_DESCRIPTION("Hard drive temperature monitor");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:drivetemp");
|
||||
|
@ -49,6 +49,7 @@
|
||||
#define SIO_F81768D_ID 0x1210 /* Chipset ID */
|
||||
#define SIO_F81865_ID 0x0704 /* Chipset ID */
|
||||
#define SIO_F81866_ID 0x1010 /* Chipset ID */
|
||||
#define SIO_F71858AD_ID 0x0903 /* Chipset ID */
|
||||
#define SIO_F81966_ID 0x1502 /* Chipset ID */
|
||||
|
||||
#define REGION_LENGTH 8
|
||||
@ -2638,6 +2639,7 @@ static int __init f71882fg_find(int sioaddr, struct f71882fg_sio_data *sio_data)
|
||||
sio_data->type = f71808a;
|
||||
break;
|
||||
case SIO_F71858_ID:
|
||||
case SIO_F71858AD_ID:
|
||||
sio_data->type = f71858fg;
|
||||
break;
|
||||
case SIO_F71862_ID:
|
||||
|
@ -269,10 +269,13 @@ gsc_hwmon_get_devtree_pdata(struct device *dev)
|
||||
/* fan controller base address */
|
||||
fan = of_find_compatible_node(dev->parent->of_node, NULL, "gw,gsc-fan");
|
||||
if (fan && of_property_read_u32(fan, "reg", &pdata->fan_base)) {
|
||||
of_node_put(fan);
|
||||
dev_err(dev, "fan node without base\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
of_node_put(fan);
|
||||
|
||||
/* allocate structures for channels and count instances of each type */
|
||||
device_for_each_child_node(dev, child) {
|
||||
if (fwnode_property_read_string(child, "label", &ch->name)) {
|
||||
|
@ -11,7 +11,8 @@
|
||||
* which contains this code, we don't worry about the wasted space.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/minmax.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/* straight from the datasheet */
|
||||
#define LM75_TEMP_MIN (-55000)
|
||||
|
2550
drivers/hwmon/lm90.c
2550
drivers/hwmon/lm90.c
File diff suppressed because it is too large
Load Diff
@ -7,7 +7,7 @@
|
||||
* Reworked by Sven Schuchmann <schuchmann@schleissheimer.de>
|
||||
* DT support added by Clemens Gruber <clemens.gruber@pqgruber.com>
|
||||
*
|
||||
* This driver export the value of analog input voltage to sysfs, the
|
||||
* This driver exports the value of analog input voltage to sysfs, the
|
||||
* voltage unit is mV. Through the sysfs interface, lm-sensors tool
|
||||
* can also display the input voltage.
|
||||
*/
|
||||
@ -45,19 +45,29 @@ enum chips {
|
||||
* Client data (each client gets its own)
|
||||
*/
|
||||
struct mcp3021_data {
|
||||
struct device *hwmon_dev;
|
||||
struct i2c_client *client;
|
||||
u32 vdd; /* supply and reference voltage in millivolt */
|
||||
u16 sar_shift;
|
||||
u16 sar_mask;
|
||||
u8 output_res;
|
||||
};
|
||||
|
||||
static int mcp3021_read16(struct i2c_client *client)
|
||||
static inline u16 volts_from_reg(struct mcp3021_data *data, u16 val)
|
||||
{
|
||||
struct mcp3021_data *data = i2c_get_clientdata(client);
|
||||
int ret;
|
||||
u16 reg;
|
||||
return DIV_ROUND_CLOSEST(data->vdd * val, 1 << data->output_res);
|
||||
}
|
||||
|
||||
static int mcp3021_read(struct device *dev, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel, long *val)
|
||||
{
|
||||
struct mcp3021_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
__be16 buf;
|
||||
u16 reg;
|
||||
int ret;
|
||||
|
||||
if (type != hwmon_in)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
ret = i2c_master_recv(client, (char *)&buf, 2);
|
||||
if (ret < 0)
|
||||
@ -74,39 +84,46 @@ static int mcp3021_read16(struct i2c_client *client)
|
||||
*/
|
||||
reg = (reg >> data->sar_shift) & data->sar_mask;
|
||||
|
||||
return reg;
|
||||
*val = volts_from_reg(data, reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline u16 volts_from_reg(struct mcp3021_data *data, u16 val)
|
||||
static umode_t mcp3021_is_visible(const void *_data,
|
||||
enum hwmon_sensor_types type,
|
||||
u32 attr, int channel)
|
||||
{
|
||||
return DIV_ROUND_CLOSEST(data->vdd * val, 1 << data->output_res);
|
||||
if (type != hwmon_in)
|
||||
return 0;
|
||||
|
||||
if (attr != hwmon_in_input)
|
||||
return 0;
|
||||
|
||||
return 0444;
|
||||
}
|
||||
|
||||
static ssize_t in0_input_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct mcp3021_data *data = i2c_get_clientdata(client);
|
||||
int reg, in_input;
|
||||
static const struct hwmon_channel_info *mcp3021_info[] = {
|
||||
HWMON_CHANNEL_INFO(in, HWMON_I_INPUT),
|
||||
NULL
|
||||
};
|
||||
|
||||
reg = mcp3021_read16(client);
|
||||
if (reg < 0)
|
||||
return reg;
|
||||
static const struct hwmon_ops mcp3021_hwmon_ops = {
|
||||
.is_visible = mcp3021_is_visible,
|
||||
.read = mcp3021_read,
|
||||
};
|
||||
|
||||
in_input = volts_from_reg(data, reg);
|
||||
|
||||
return sprintf(buf, "%d\n", in_input);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RO(in0_input);
|
||||
static const struct hwmon_chip_info mcp3021_chip_info = {
|
||||
.ops = &mcp3021_hwmon_ops,
|
||||
.info = mcp3021_info,
|
||||
};
|
||||
|
||||
static const struct i2c_device_id mcp3021_id[];
|
||||
|
||||
static int mcp3021_probe(struct i2c_client *client)
|
||||
{
|
||||
int err;
|
||||
struct mcp3021_data *data = NULL;
|
||||
struct device_node *np = client->dev.of_node;
|
||||
struct device *hwmon_dev;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
|
||||
return -ENODEV;
|
||||
@ -147,34 +164,17 @@ static int mcp3021_probe(struct i2c_client *client)
|
||||
break;
|
||||
}
|
||||
|
||||
data->client = client;
|
||||
|
||||
if (data->vdd > MCP3021_VDD_REF_MAX || data->vdd < MCP3021_VDD_REF_MIN)
|
||||
return -EINVAL;
|
||||
|
||||
err = sysfs_create_file(&client->dev.kobj, &dev_attr_in0_input.attr);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
data->hwmon_dev = hwmon_device_register(&client->dev);
|
||||
if (IS_ERR(data->hwmon_dev)) {
|
||||
err = PTR_ERR(data->hwmon_dev);
|
||||
goto exit_remove;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
exit_remove:
|
||||
sysfs_remove_file(&client->dev.kobj, &dev_attr_in0_input.attr);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int mcp3021_remove(struct i2c_client *client)
|
||||
{
|
||||
struct mcp3021_data *data = i2c_get_clientdata(client);
|
||||
|
||||
hwmon_device_unregister(data->hwmon_dev);
|
||||
sysfs_remove_file(&client->dev.kobj, &dev_attr_in0_input.attr);
|
||||
|
||||
return 0;
|
||||
hwmon_dev = devm_hwmon_device_register_with_info(&client->dev,
|
||||
client->name,
|
||||
data,
|
||||
&mcp3021_chip_info,
|
||||
NULL);
|
||||
return PTR_ERR_OR_ZERO(hwmon_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id mcp3021_id[] = {
|
||||
@ -199,7 +199,6 @@ static struct i2c_driver mcp3021_driver = {
|
||||
.of_match_table = of_match_ptr(of_mcp3021_match),
|
||||
},
|
||||
.probe_new = mcp3021_probe,
|
||||
.remove = mcp3021_remove,
|
||||
.id_table = mcp3021_id,
|
||||
};
|
||||
|
||||
|
@ -1083,6 +1083,7 @@ static const char * const asus_wmi_boards[] = {
|
||||
"TUF GAMING B550M-PLUS",
|
||||
"TUF GAMING B550M-PLUS (WI-FI)",
|
||||
"TUF GAMING B550-PLUS",
|
||||
"TUF GAMING B550-PLUS WIFI II",
|
||||
"TUF GAMING B550-PRO",
|
||||
"TUF GAMING X570-PLUS",
|
||||
"TUF GAMING X570-PLUS (WI-FI)",
|
||||
@ -1200,10 +1201,8 @@ static int __init sensors_nct6775_platform_init(void)
|
||||
exit_device_put:
|
||||
platform_device_put(pdev[i]);
|
||||
exit_device_unregister:
|
||||
while (--i >= 0) {
|
||||
if (pdev[i])
|
||||
platform_device_unregister(pdev[i]);
|
||||
}
|
||||
while (i--)
|
||||
platform_device_unregister(pdev[i]);
|
||||
exit_unregister:
|
||||
platform_driver_unregister(&nct6775_driver);
|
||||
return err;
|
||||
@ -1213,10 +1212,8 @@ static void __exit sensors_nct6775_platform_exit(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pdev); i++) {
|
||||
if (pdev[i])
|
||||
platform_device_unregister(pdev[i]);
|
||||
}
|
||||
for (i = 0; i < ARRAY_SIZE(pdev); i++)
|
||||
platform_device_unregister(pdev[i]);
|
||||
platform_driver_unregister(&nct6775_driver);
|
||||
}
|
||||
|
||||
|
@ -729,18 +729,14 @@ static ssize_t occ_show_extended(struct device *dev,
|
||||
rc = sysfs_emit(buf, "%u",
|
||||
get_unaligned_be32(&extn->sensor_id));
|
||||
} else {
|
||||
rc = sysfs_emit(buf, "%02x%02x%02x%02x\n",
|
||||
extn->name[0], extn->name[1],
|
||||
extn->name[2], extn->name[3]);
|
||||
rc = sysfs_emit(buf, "%4phN\n", extn->name);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
rc = sysfs_emit(buf, "%02x\n", extn->flags);
|
||||
break;
|
||||
case 2:
|
||||
rc = sysfs_emit(buf, "%02x%02x%02x%02x%02x%02x\n",
|
||||
extn->data[0], extn->data[1], extn->data[2],
|
||||
extn->data[3], extn->data[4], extn->data[5]);
|
||||
rc = sysfs_emit(buf, "%6phN\n", extn->data);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
@ -55,8 +55,7 @@ static bool p9_sbe_occ_save_ffdc(struct p9_sbe_occ *ctx, const void *resp,
|
||||
mutex_lock(&ctx->sbe_error_lock);
|
||||
if (!ctx->sbe_error) {
|
||||
if (resp_len > ctx->ffdc_size) {
|
||||
if (ctx->ffdc)
|
||||
kvfree(ctx->ffdc);
|
||||
kvfree(ctx->ffdc);
|
||||
ctx->ffdc = kvmalloc(resp_len, GFP_KERNEL);
|
||||
if (!ctx->ffdc) {
|
||||
ctx->ffdc_len = 0;
|
||||
@ -170,8 +169,7 @@ static int p9_sbe_occ_remove(struct platform_device *pdev)
|
||||
ctx->sbe = NULL;
|
||||
occ_shutdown(occ);
|
||||
|
||||
if (ctx->ffdc)
|
||||
kvfree(ctx->ffdc);
|
||||
kvfree(ctx->ffdc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -181,6 +181,15 @@ config SENSORS_LM25066_REGULATOR
|
||||
If you say yes here you get regulator support for National
|
||||
Semiconductor LM25066, LM5064, and LM5066.
|
||||
|
||||
config SENSORS_LT7182S
|
||||
tristate "Analog Devices LT7182S"
|
||||
help
|
||||
If you say yes here you get hardware monitoring support for Analog
|
||||
Devices LT7182S.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called lt7182s.
|
||||
|
||||
config SENSORS_LTC2978
|
||||
tristate "Linear Technologies LTC2978 and compatibles"
|
||||
help
|
||||
|
@ -20,6 +20,7 @@ obj-$(CONFIG_SENSORS_IR38064) += ir38064.o
|
||||
obj-$(CONFIG_SENSORS_IRPS5401) += irps5401.o
|
||||
obj-$(CONFIG_SENSORS_ISL68137) += isl68137.o
|
||||
obj-$(CONFIG_SENSORS_LM25066) += lm25066.o
|
||||
obj-$(CONFIG_SENSORS_LT7182S) += lt7182s.o
|
||||
obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o
|
||||
obj-$(CONFIG_SENSORS_LTC3815) += ltc3815.o
|
||||
obj-$(CONFIG_SENSORS_MAX15301) += max15301.o
|
||||
|
195
drivers/hwmon/pmbus/lt7182s.c
Normal file
195
drivers/hwmon/pmbus/lt7182s.c
Normal file
@ -0,0 +1,195 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Hardware monitoring driver for Analog Devices LT7182S
|
||||
*
|
||||
* Copyright (c) 2022 Guenter Roeck
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include "pmbus.h"
|
||||
|
||||
#define LT7182S_NUM_PAGES 2
|
||||
|
||||
#define MFR_READ_EXTVCC 0xcd
|
||||
#define MFR_READ_ITH 0xce
|
||||
#define MFR_CONFIG_ALL_LT7182S 0xd1
|
||||
#define MFR_IOUT_PEAK 0xd7
|
||||
#define MFR_ADC_CONTROL_LT7182S 0xd8
|
||||
|
||||
#define MFR_DEBUG_TELEMETRY BIT(0)
|
||||
|
||||
#define MFR_VOUT_PEAK 0xdd
|
||||
#define MFR_VIN_PEAK 0xde
|
||||
#define MFR_TEMPERATURE_1_PEAK 0xdf
|
||||
#define MFR_CLEAR_PEAKS 0xe3
|
||||
|
||||
#define MFR_CONFIG_IEEE BIT(8)
|
||||
|
||||
static int lt7182s_read_word_data(struct i2c_client *client, int page, int phase, int reg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_VIRT_READ_VMON:
|
||||
if (page == 0 || page == 1)
|
||||
ret = pmbus_read_word_data(client, page, phase, MFR_READ_ITH);
|
||||
else
|
||||
ret = pmbus_read_word_data(client, 0, phase, MFR_READ_EXTVCC);
|
||||
break;
|
||||
case PMBUS_VIRT_READ_IOUT_MAX:
|
||||
ret = pmbus_read_word_data(client, page, phase, MFR_IOUT_PEAK);
|
||||
break;
|
||||
case PMBUS_VIRT_READ_VOUT_MAX:
|
||||
ret = pmbus_read_word_data(client, page, phase, MFR_VOUT_PEAK);
|
||||
break;
|
||||
case PMBUS_VIRT_READ_VIN_MAX:
|
||||
ret = pmbus_read_word_data(client, page, phase, MFR_VIN_PEAK);
|
||||
break;
|
||||
case PMBUS_VIRT_READ_TEMP_MAX:
|
||||
ret = pmbus_read_word_data(client, page, phase, MFR_TEMPERATURE_1_PEAK);
|
||||
break;
|
||||
case PMBUS_VIRT_RESET_VIN_HISTORY:
|
||||
ret = (page == 0) ? 0 : -ENODATA;
|
||||
break;
|
||||
default:
|
||||
ret = -ENODATA;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lt7182s_write_word_data(struct i2c_client *client, int page, int reg, u16 word)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_VIRT_RESET_VIN_HISTORY:
|
||||
ret = pmbus_write_byte(client, 0, MFR_CLEAR_PEAKS);
|
||||
break;
|
||||
default:
|
||||
ret = -ENODATA;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct pmbus_driver_info lt7182s_info = {
|
||||
.pages = LT7182S_NUM_PAGES,
|
||||
.format[PSC_VOLTAGE_IN] = linear,
|
||||
.format[PSC_VOLTAGE_OUT] = linear,
|
||||
.format[PSC_CURRENT_IN] = linear,
|
||||
.format[PSC_CURRENT_OUT] = linear,
|
||||
.format[PSC_TEMPERATURE] = linear,
|
||||
.format[PSC_POWER] = linear,
|
||||
.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT |
|
||||
PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_POUT |
|
||||
PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT |
|
||||
PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP,
|
||||
.func[1] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT |
|
||||
PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT | PMBUS_HAVE_POUT |
|
||||
PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT |
|
||||
PMBUS_HAVE_STATUS_INPUT,
|
||||
.read_word_data = lt7182s_read_word_data,
|
||||
.write_word_data = lt7182s_write_word_data,
|
||||
};
|
||||
|
||||
static int lt7182s_probe(struct i2c_client *client)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct pmbus_driver_info *info;
|
||||
u8 buf[I2C_SMBUS_BLOCK_MAX + 1];
|
||||
int ret;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_READ_BYTE_DATA |
|
||||
I2C_FUNC_SMBUS_READ_WORD_DATA |
|
||||
I2C_FUNC_SMBUS_READ_BLOCK_DATA))
|
||||
return -ENODEV;
|
||||
|
||||
ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to read PMBUS_MFR_ID\n");
|
||||
return ret;
|
||||
}
|
||||
if (ret != 3 || strncmp(buf, "ADI", 3)) {
|
||||
buf[ret] = '\0';
|
||||
dev_err(dev, "Manufacturer '%s' not supported\n", buf);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to read PMBUS_MFR_MODEL\n");
|
||||
return ret;
|
||||
}
|
||||
if (ret != 7 || strncmp(buf, "LT7182S", 7)) {
|
||||
buf[ret] = '\0';
|
||||
dev_err(dev, "Model '%s' not supported\n", buf);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
info = devm_kmemdup(dev, <7182s_info,
|
||||
sizeof(struct pmbus_driver_info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Set data format to IEEE754 if configured */
|
||||
ret = i2c_smbus_read_word_data(client, MFR_CONFIG_ALL_LT7182S);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret & MFR_CONFIG_IEEE) {
|
||||
info->format[PSC_VOLTAGE_IN] = ieee754;
|
||||
info->format[PSC_VOLTAGE_OUT] = ieee754;
|
||||
info->format[PSC_CURRENT_IN] = ieee754;
|
||||
info->format[PSC_CURRENT_OUT] = ieee754;
|
||||
info->format[PSC_TEMPERATURE] = ieee754;
|
||||
info->format[PSC_POWER] = ieee754;
|
||||
}
|
||||
|
||||
/* Enable VMON output if configured */
|
||||
ret = i2c_smbus_read_byte_data(client, MFR_ADC_CONTROL_LT7182S);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret & MFR_DEBUG_TELEMETRY) {
|
||||
info->pages = 3;
|
||||
info->func[0] |= PMBUS_HAVE_VMON;
|
||||
info->func[1] |= PMBUS_HAVE_VMON;
|
||||
info->func[2] = PMBUS_HAVE_VMON;
|
||||
}
|
||||
|
||||
return pmbus_do_probe(client, info);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id lt7182s_id[] = {
|
||||
{ "lt7182s", 0 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, lt7182s_id);
|
||||
|
||||
static const struct of_device_id __maybe_unused lt7182s_of_match[] = {
|
||||
{ .compatible = "adi,lt7182s" },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct i2c_driver lt7182s_driver = {
|
||||
.driver = {
|
||||
.name = "lt7182s",
|
||||
.of_match_table = of_match_ptr(lt7182s_of_match),
|
||||
},
|
||||
.probe_new = lt7182s_probe,
|
||||
.id_table = lt7182s_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(lt7182s_driver);
|
||||
|
||||
MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
|
||||
MODULE_DESCRIPTION("PMBus driver for Analog Devices LT7182S");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(PMBUS);
|
@ -562,7 +562,24 @@ static const struct i2c_device_id ltc2978_id[] = {
|
||||
MODULE_DEVICE_TABLE(i2c, ltc2978_id);
|
||||
|
||||
#if IS_ENABLED(CONFIG_SENSORS_LTC2978_REGULATOR)
|
||||
#define LTC2978_ADC_RES 0xFFFF
|
||||
#define LTC2978_N_ADC 122
|
||||
#define LTC2978_MAX_UV (LTC2978_ADC_RES * LTC2978_N_ADC)
|
||||
#define LTC2978_UV_STEP 1000
|
||||
#define LTC2978_N_VOLTAGES ((LTC2978_MAX_UV / LTC2978_UV_STEP) + 1)
|
||||
|
||||
static const struct regulator_desc ltc2978_reg_desc[] = {
|
||||
PMBUS_REGULATOR_STEP("vout", 0, LTC2978_N_VOLTAGES, LTC2978_UV_STEP),
|
||||
PMBUS_REGULATOR_STEP("vout", 1, LTC2978_N_VOLTAGES, LTC2978_UV_STEP),
|
||||
PMBUS_REGULATOR_STEP("vout", 2, LTC2978_N_VOLTAGES, LTC2978_UV_STEP),
|
||||
PMBUS_REGULATOR_STEP("vout", 3, LTC2978_N_VOLTAGES, LTC2978_UV_STEP),
|
||||
PMBUS_REGULATOR_STEP("vout", 4, LTC2978_N_VOLTAGES, LTC2978_UV_STEP),
|
||||
PMBUS_REGULATOR_STEP("vout", 5, LTC2978_N_VOLTAGES, LTC2978_UV_STEP),
|
||||
PMBUS_REGULATOR_STEP("vout", 6, LTC2978_N_VOLTAGES, LTC2978_UV_STEP),
|
||||
PMBUS_REGULATOR_STEP("vout", 7, LTC2978_N_VOLTAGES, LTC2978_UV_STEP),
|
||||
};
|
||||
|
||||
static const struct regulator_desc ltc2978_reg_desc_default[] = {
|
||||
PMBUS_REGULATOR("vout", 0),
|
||||
PMBUS_REGULATOR("vout", 1),
|
||||
PMBUS_REGULATOR("vout", 2),
|
||||
@ -839,10 +856,29 @@ static int ltc2978_probe(struct i2c_client *client)
|
||||
|
||||
#if IS_ENABLED(CONFIG_SENSORS_LTC2978_REGULATOR)
|
||||
info->num_regulators = info->pages;
|
||||
info->reg_desc = ltc2978_reg_desc;
|
||||
if (info->num_regulators > ARRAY_SIZE(ltc2978_reg_desc)) {
|
||||
dev_err(&client->dev, "num_regulators too large!");
|
||||
info->num_regulators = ARRAY_SIZE(ltc2978_reg_desc);
|
||||
switch (data->id) {
|
||||
case ltc2972:
|
||||
case ltc2974:
|
||||
case ltc2975:
|
||||
case ltc2977:
|
||||
case ltc2978:
|
||||
case ltc2979:
|
||||
case ltc2980:
|
||||
case ltm2987:
|
||||
info->reg_desc = ltc2978_reg_desc;
|
||||
if (info->num_regulators > ARRAY_SIZE(ltc2978_reg_desc)) {
|
||||
dev_warn(&client->dev, "num_regulators too large!");
|
||||
info->num_regulators = ARRAY_SIZE(ltc2978_reg_desc);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
info->reg_desc = ltc2978_reg_desc_default;
|
||||
if (info->num_regulators > ARRAY_SIZE(ltc2978_reg_desc_default)) {
|
||||
dev_warn(&client->dev, "num_regulators too large!");
|
||||
info->num_regulators =
|
||||
ARRAY_SIZE(ltc2978_reg_desc_default);
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -406,7 +406,7 @@ enum pmbus_sensor_classes {
|
||||
#define PMBUS_PHASE_VIRTUAL BIT(30) /* Phases on this page are virtual */
|
||||
#define PMBUS_PAGE_VIRTUAL BIT(31) /* Page is virtual */
|
||||
|
||||
enum pmbus_data_format { linear = 0, direct, vid };
|
||||
enum pmbus_data_format { linear = 0, ieee754, direct, vid };
|
||||
enum vrm_version { vr11 = 0, vr12, vr13, imvp9, amd625mv };
|
||||
|
||||
struct pmbus_driver_info {
|
||||
@ -463,8 +463,8 @@ struct pmbus_driver_info {
|
||||
|
||||
extern const struct regulator_ops pmbus_regulator_ops;
|
||||
|
||||
/* Macro for filling in array of struct regulator_desc */
|
||||
#define PMBUS_REGULATOR(_name, _id) \
|
||||
/* Macros for filling in array of struct regulator_desc */
|
||||
#define PMBUS_REGULATOR_STEP(_name, _id, _voltages, _step) \
|
||||
[_id] = { \
|
||||
.name = (_name # _id), \
|
||||
.supply_name = "vin", \
|
||||
@ -474,8 +474,12 @@ extern const struct regulator_ops pmbus_regulator_ops;
|
||||
.ops = &pmbus_regulator_ops, \
|
||||
.type = REGULATOR_VOLTAGE, \
|
||||
.owner = THIS_MODULE, \
|
||||
.n_voltages = _voltages, \
|
||||
.uV_step = _step, \
|
||||
}
|
||||
|
||||
#define PMBUS_REGULATOR(_name, _id) PMBUS_REGULATOR_STEP(_name, _id, 0, 0)
|
||||
|
||||
/* Function declarations */
|
||||
|
||||
void pmbus_clear_cache(struct i2c_client *client);
|
||||
|
@ -104,6 +104,9 @@ struct pmbus_data {
|
||||
|
||||
s16 currpage; /* current page, -1 for unknown/unset */
|
||||
s16 currphase; /* current phase, 0xff for all, -1 for unknown/unset */
|
||||
|
||||
int vout_low[PMBUS_PAGES]; /* voltage low margin */
|
||||
int vout_high[PMBUS_PAGES]; /* voltage high margin */
|
||||
};
|
||||
|
||||
struct pmbus_debugfs_entry {
|
||||
@ -441,6 +444,18 @@ int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg,
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(pmbus_update_byte_data, PMBUS);
|
||||
|
||||
static int pmbus_read_block_data(struct i2c_client *client, int page, u8 reg,
|
||||
char *data_buf)
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv = pmbus_set_page(client, page, 0xff);
|
||||
if (rv < 0)
|
||||
return rv;
|
||||
|
||||
return i2c_smbus_read_block_data(client, reg, data_buf);
|
||||
}
|
||||
|
||||
static struct pmbus_sensor *pmbus_find_sensor(struct pmbus_data *data, int page,
|
||||
int reg)
|
||||
{
|
||||
@ -578,6 +593,22 @@ bool pmbus_check_word_register(struct i2c_client *client, int page, int reg)
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(pmbus_check_word_register, PMBUS);
|
||||
|
||||
static bool __maybe_unused pmbus_check_block_register(struct i2c_client *client,
|
||||
int page, int reg)
|
||||
{
|
||||
int rv;
|
||||
struct pmbus_data *data = i2c_get_clientdata(client);
|
||||
char data_buf[I2C_SMBUS_BLOCK_MAX + 2];
|
||||
|
||||
rv = pmbus_read_block_data(client, page, reg, data_buf);
|
||||
if (rv >= 0 && !(data->flags & PMBUS_SKIP_STATUS_CHECK))
|
||||
rv = pmbus_check_status_cml(client);
|
||||
if (rv < 0 && (data->flags & PMBUS_READ_STATUS_AFTER_FAILED_CHECK))
|
||||
data->read_status(client, -1);
|
||||
pmbus_clear_fault_page(client, -1);
|
||||
return rv >= 0;
|
||||
}
|
||||
|
||||
const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client *client)
|
||||
{
|
||||
struct pmbus_data *data = i2c_get_clientdata(client);
|
||||
@ -611,6 +642,66 @@ static void pmbus_update_sensor_data(struct i2c_client *client, struct pmbus_sen
|
||||
sensor->phase, sensor->reg);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert ieee754 sensor values to milli- or micro-units
|
||||
* depending on sensor type.
|
||||
*
|
||||
* ieee754 data format:
|
||||
* bit 15: sign
|
||||
* bit 10..14: exponent
|
||||
* bit 0..9: mantissa
|
||||
* exponent=0:
|
||||
* v=(−1)^signbit * 2^(−14) * 0.significantbits
|
||||
* exponent=1..30:
|
||||
* v=(−1)^signbit * 2^(exponent - 15) * 1.significantbits
|
||||
* exponent=31:
|
||||
* v=NaN
|
||||
*
|
||||
* Add the number mantissa bits into the calculations for simplicity.
|
||||
* To do that, add '10' to the exponent. By doing that, we can just add
|
||||
* 0x400 to normal values and get the expected result.
|
||||
*/
|
||||
static long pmbus_reg2data_ieee754(struct pmbus_data *data,
|
||||
struct pmbus_sensor *sensor)
|
||||
{
|
||||
int exponent;
|
||||
bool sign;
|
||||
long val;
|
||||
|
||||
/* only support half precision for now */
|
||||
sign = sensor->data & 0x8000;
|
||||
exponent = (sensor->data >> 10) & 0x1f;
|
||||
val = sensor->data & 0x3ff;
|
||||
|
||||
if (exponent == 0) { /* subnormal */
|
||||
exponent = -(14 + 10);
|
||||
} else if (exponent == 0x1f) { /* NaN, convert to min/max */
|
||||
exponent = 0;
|
||||
val = 65504;
|
||||
} else {
|
||||
exponent -= (15 + 10); /* normal */
|
||||
val |= 0x400;
|
||||
}
|
||||
|
||||
/* scale result to milli-units for all sensors except fans */
|
||||
if (sensor->class != PSC_FAN)
|
||||
val = val * 1000L;
|
||||
|
||||
/* scale result to micro-units for power sensors */
|
||||
if (sensor->class == PSC_POWER)
|
||||
val = val * 1000L;
|
||||
|
||||
if (exponent >= 0)
|
||||
val <<= exponent;
|
||||
else
|
||||
val >>= -exponent;
|
||||
|
||||
if (sign)
|
||||
val = -val;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert linear sensor values to milli- or micro-units
|
||||
* depending on sensor type.
|
||||
@ -741,6 +832,9 @@ static s64 pmbus_reg2data(struct pmbus_data *data, struct pmbus_sensor *sensor)
|
||||
case vid:
|
||||
val = pmbus_reg2data_vid(data, sensor);
|
||||
break;
|
||||
case ieee754:
|
||||
val = pmbus_reg2data_ieee754(data, sensor);
|
||||
break;
|
||||
case linear:
|
||||
default:
|
||||
val = pmbus_reg2data_linear(data, sensor);
|
||||
@ -749,8 +843,72 @@ static s64 pmbus_reg2data(struct pmbus_data *data, struct pmbus_sensor *sensor)
|
||||
return val;
|
||||
}
|
||||
|
||||
#define MAX_MANTISSA (1023 * 1000)
|
||||
#define MIN_MANTISSA (511 * 1000)
|
||||
#define MAX_IEEE_MANTISSA (0x7ff * 1000)
|
||||
#define MIN_IEEE_MANTISSA (0x400 * 1000)
|
||||
|
||||
static u16 pmbus_data2reg_ieee754(struct pmbus_data *data,
|
||||
struct pmbus_sensor *sensor, long val)
|
||||
{
|
||||
u16 exponent = (15 + 10);
|
||||
long mantissa;
|
||||
u16 sign = 0;
|
||||
|
||||
/* simple case */
|
||||
if (val == 0)
|
||||
return 0;
|
||||
|
||||
if (val < 0) {
|
||||
sign = 0x8000;
|
||||
val = -val;
|
||||
}
|
||||
|
||||
/* Power is in uW. Convert to mW before converting. */
|
||||
if (sensor->class == PSC_POWER)
|
||||
val = DIV_ROUND_CLOSEST(val, 1000L);
|
||||
|
||||
/*
|
||||
* For simplicity, convert fan data to milli-units
|
||||
* before calculating the exponent.
|
||||
*/
|
||||
if (sensor->class == PSC_FAN)
|
||||
val = val * 1000;
|
||||
|
||||
/* Reduce large mantissa until it fits into 10 bit */
|
||||
while (val > MAX_IEEE_MANTISSA && exponent < 30) {
|
||||
exponent++;
|
||||
val >>= 1;
|
||||
}
|
||||
/*
|
||||
* Increase small mantissa to generate valid 'normal'
|
||||
* number
|
||||
*/
|
||||
while (val < MIN_IEEE_MANTISSA && exponent > 1) {
|
||||
exponent--;
|
||||
val <<= 1;
|
||||
}
|
||||
|
||||
/* Convert mantissa from milli-units to units */
|
||||
mantissa = DIV_ROUND_CLOSEST(val, 1000);
|
||||
|
||||
/*
|
||||
* Ensure that the resulting number is within range.
|
||||
* Valid range is 0x400..0x7ff, where bit 10 reflects
|
||||
* the implied high bit in normalized ieee754 numbers.
|
||||
* Set the range to 0x400..0x7ff to reflect this.
|
||||
* The upper bit is then removed by the mask against
|
||||
* 0x3ff in the final assignment.
|
||||
*/
|
||||
if (mantissa > 0x7ff)
|
||||
mantissa = 0x7ff;
|
||||
else if (mantissa < 0x400)
|
||||
mantissa = 0x400;
|
||||
|
||||
/* Convert to sign, 5 bit exponent, 10 bit mantissa */
|
||||
return sign | (mantissa & 0x3ff) | ((exponent << 10) & 0x7c00);
|
||||
}
|
||||
|
||||
#define MAX_LIN_MANTISSA (1023 * 1000)
|
||||
#define MIN_LIN_MANTISSA (511 * 1000)
|
||||
|
||||
static u16 pmbus_data2reg_linear(struct pmbus_data *data,
|
||||
struct pmbus_sensor *sensor, s64 val)
|
||||
@ -796,12 +954,12 @@ static u16 pmbus_data2reg_linear(struct pmbus_data *data,
|
||||
val = val * 1000LL;
|
||||
|
||||
/* Reduce large mantissa until it fits into 10 bit */
|
||||
while (val >= MAX_MANTISSA && exponent < 15) {
|
||||
while (val >= MAX_LIN_MANTISSA && exponent < 15) {
|
||||
exponent++;
|
||||
val >>= 1;
|
||||
}
|
||||
/* Increase small mantissa to improve precision */
|
||||
while (val < MIN_MANTISSA && exponent > -15) {
|
||||
while (val < MIN_LIN_MANTISSA && exponent > -15) {
|
||||
exponent--;
|
||||
val <<= 1;
|
||||
}
|
||||
@ -875,6 +1033,9 @@ static u16 pmbus_data2reg(struct pmbus_data *data,
|
||||
case vid:
|
||||
regval = pmbus_data2reg_vid(data, sensor, val);
|
||||
break;
|
||||
case ieee754:
|
||||
regval = pmbus_data2reg_ieee754(data, sensor, val);
|
||||
break;
|
||||
case linear:
|
||||
default:
|
||||
regval = pmbus_data2reg_linear(data, sensor, val);
|
||||
@ -2369,6 +2530,10 @@ static int pmbus_identify_common(struct i2c_client *client,
|
||||
if (data->info->format[PSC_VOLTAGE_OUT] != direct)
|
||||
return -ENODEV;
|
||||
break;
|
||||
case 3: /* ieee 754 half precision */
|
||||
if (data->info->format[PSC_VOLTAGE_OUT] != ieee754)
|
||||
return -ENODEV;
|
||||
break;
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
@ -2388,6 +2553,42 @@ static int pmbus_read_status_word(struct i2c_client *client, int page)
|
||||
return _pmbus_read_word_data(client, page, 0xff, PMBUS_STATUS_WORD);
|
||||
}
|
||||
|
||||
/* PEC attribute support */
|
||||
|
||||
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 *dummy,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
bool enable;
|
||||
int err;
|
||||
|
||||
err = kstrtobool(buf, &enable);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (enable)
|
||||
client->flags |= I2C_CLIENT_PEC;
|
||||
else
|
||||
client->flags &= ~I2C_CLIENT_PEC;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RW(pec);
|
||||
|
||||
static void pmbus_remove_pec(void *dev)
|
||||
{
|
||||
device_remove_file(dev, &dev_attr_pec);
|
||||
}
|
||||
|
||||
static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data,
|
||||
struct pmbus_driver_info *info)
|
||||
{
|
||||
@ -2474,6 +2675,20 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data,
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (client->flags & I2C_CLIENT_PEC) {
|
||||
/*
|
||||
* If I2C_CLIENT_PEC is set here, both the I2C adapter and the
|
||||
* chip support PEC. Add 'pec' attribute to client device to let
|
||||
* the user control it.
|
||||
*/
|
||||
ret = device_create_file(dev, &dev_attr_pec);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = devm_add_action_or_reset(dev, pmbus_remove_pec, dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2636,6 +2851,58 @@ static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pmbus_regulator_get_low_margin(struct i2c_client *client, int page)
|
||||
{
|
||||
struct pmbus_data *data = i2c_get_clientdata(client);
|
||||
struct pmbus_sensor s = {
|
||||
.page = page,
|
||||
.class = PSC_VOLTAGE_OUT,
|
||||
.convert = true,
|
||||
.data = -1,
|
||||
};
|
||||
|
||||
if (!data->vout_low[page]) {
|
||||
if (pmbus_check_word_register(client, page, PMBUS_MFR_VOUT_MIN))
|
||||
s.data = _pmbus_read_word_data(client, page, 0xff,
|
||||
PMBUS_MFR_VOUT_MIN);
|
||||
if (s.data < 0) {
|
||||
s.data = _pmbus_read_word_data(client, page, 0xff,
|
||||
PMBUS_VOUT_MARGIN_LOW);
|
||||
if (s.data < 0)
|
||||
return s.data;
|
||||
}
|
||||
data->vout_low[page] = pmbus_reg2data(data, &s);
|
||||
}
|
||||
|
||||
return data->vout_low[page];
|
||||
}
|
||||
|
||||
static int pmbus_regulator_get_high_margin(struct i2c_client *client, int page)
|
||||
{
|
||||
struct pmbus_data *data = i2c_get_clientdata(client);
|
||||
struct pmbus_sensor s = {
|
||||
.page = page,
|
||||
.class = PSC_VOLTAGE_OUT,
|
||||
.convert = true,
|
||||
.data = -1,
|
||||
};
|
||||
|
||||
if (!data->vout_high[page]) {
|
||||
if (pmbus_check_word_register(client, page, PMBUS_MFR_VOUT_MAX))
|
||||
s.data = _pmbus_read_word_data(client, page, 0xff,
|
||||
PMBUS_MFR_VOUT_MAX);
|
||||
if (s.data < 0) {
|
||||
s.data = _pmbus_read_word_data(client, page, 0xff,
|
||||
PMBUS_VOUT_MARGIN_HIGH);
|
||||
if (s.data < 0)
|
||||
return s.data;
|
||||
}
|
||||
data->vout_high[page] = pmbus_reg2data(data, &s);
|
||||
}
|
||||
|
||||
return data->vout_high[page];
|
||||
}
|
||||
|
||||
static int pmbus_regulator_get_voltage(struct regulator_dev *rdev)
|
||||
{
|
||||
struct device *dev = rdev_get_dev(rdev);
|
||||
@ -2671,24 +2938,13 @@ static int pmbus_regulator_set_voltage(struct regulator_dev *rdev, int min_uv,
|
||||
|
||||
*selector = 0;
|
||||
|
||||
if (pmbus_check_word_register(client, s.page, PMBUS_MFR_VOUT_MIN))
|
||||
s.data = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_MFR_VOUT_MIN);
|
||||
if (s.data < 0) {
|
||||
s.data = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_VOUT_MARGIN_LOW);
|
||||
if (s.data < 0)
|
||||
return s.data;
|
||||
}
|
||||
low = pmbus_reg2data(data, &s);
|
||||
low = pmbus_regulator_get_low_margin(client, s.page);
|
||||
if (low < 0)
|
||||
return low;
|
||||
|
||||
s.data = -1;
|
||||
if (pmbus_check_word_register(client, s.page, PMBUS_MFR_VOUT_MAX))
|
||||
s.data = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_MFR_VOUT_MAX);
|
||||
if (s.data < 0) {
|
||||
s.data = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_VOUT_MARGIN_HIGH);
|
||||
if (s.data < 0)
|
||||
return s.data;
|
||||
}
|
||||
high = pmbus_reg2data(data, &s);
|
||||
high = pmbus_regulator_get_high_margin(client, s.page);
|
||||
if (high < 0)
|
||||
return high;
|
||||
|
||||
/* Make sure we are within margins */
|
||||
if (low > val)
|
||||
@ -2701,6 +2957,35 @@ static int pmbus_regulator_set_voltage(struct regulator_dev *rdev, int min_uv,
|
||||
return _pmbus_write_word_data(client, s.page, PMBUS_VOUT_COMMAND, (u16)val);
|
||||
}
|
||||
|
||||
static int pmbus_regulator_list_voltage(struct regulator_dev *rdev,
|
||||
unsigned int selector)
|
||||
{
|
||||
struct device *dev = rdev_get_dev(rdev);
|
||||
struct i2c_client *client = to_i2c_client(dev->parent);
|
||||
int val, low, high;
|
||||
|
||||
if (selector >= rdev->desc->n_voltages ||
|
||||
selector < rdev->desc->linear_min_sel)
|
||||
return -EINVAL;
|
||||
|
||||
selector -= rdev->desc->linear_min_sel;
|
||||
val = DIV_ROUND_CLOSEST(rdev->desc->min_uV +
|
||||
(rdev->desc->uV_step * selector), 1000); /* convert to mV */
|
||||
|
||||
low = pmbus_regulator_get_low_margin(client, rdev_get_id(rdev));
|
||||
if (low < 0)
|
||||
return low;
|
||||
|
||||
high = pmbus_regulator_get_high_margin(client, rdev_get_id(rdev));
|
||||
if (high < 0)
|
||||
return high;
|
||||
|
||||
if (val >= low && val <= high)
|
||||
return val * 1000; /* unit is uV */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct regulator_ops pmbus_regulator_ops = {
|
||||
.enable = pmbus_regulator_enable,
|
||||
.disable = pmbus_regulator_disable,
|
||||
@ -2708,6 +2993,7 @@ const struct regulator_ops pmbus_regulator_ops = {
|
||||
.get_error_flags = pmbus_regulator_get_error_flags,
|
||||
.get_voltage = pmbus_regulator_get_voltage,
|
||||
.set_voltage = pmbus_regulator_set_voltage,
|
||||
.list_voltage = pmbus_regulator_list_voltage,
|
||||
};
|
||||
EXPORT_SYMBOL_NS_GPL(pmbus_regulator_ops, PMBUS);
|
||||
|
||||
@ -2782,41 +3068,33 @@ static int pmbus_debugfs_get_status(void *data, u64 *val)
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(pmbus_debugfs_ops_status, pmbus_debugfs_get_status,
|
||||
NULL, "0x%04llx\n");
|
||||
|
||||
static int pmbus_debugfs_get_pec(void *data, u64 *val)
|
||||
{
|
||||
struct i2c_client *client = data;
|
||||
|
||||
*val = !!(client->flags & I2C_CLIENT_PEC);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pmbus_debugfs_set_pec(void *data, u64 val)
|
||||
static ssize_t pmbus_debugfs_mfr_read(struct file *file, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
int rc;
|
||||
struct i2c_client *client = data;
|
||||
struct pmbus_debugfs_entry *entry = file->private_data;
|
||||
char data[I2C_SMBUS_BLOCK_MAX + 2] = { 0 };
|
||||
|
||||
if (!val) {
|
||||
client->flags &= ~I2C_CLIENT_PEC;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (val != 1)
|
||||
return -EINVAL;
|
||||
|
||||
rc = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY);
|
||||
rc = pmbus_read_block_data(entry->client, entry->page, entry->reg,
|
||||
data);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
if (!(rc & PB_CAPABILITY_ERROR_CHECK))
|
||||
return -EOPNOTSUPP;
|
||||
/* Add newline at the end of a read data */
|
||||
data[rc] = '\n';
|
||||
|
||||
client->flags |= I2C_CLIENT_PEC;
|
||||
/* Include newline into the length */
|
||||
rc += 1;
|
||||
|
||||
return 0;
|
||||
return simple_read_from_buffer(buf, count, ppos, data, rc);
|
||||
}
|
||||
DEFINE_DEBUGFS_ATTRIBUTE(pmbus_debugfs_ops_pec, pmbus_debugfs_get_pec,
|
||||
pmbus_debugfs_set_pec, "%llu\n");
|
||||
|
||||
static const struct file_operations pmbus_debugfs_ops_mfr = {
|
||||
.llseek = noop_llseek,
|
||||
.read = pmbus_debugfs_mfr_read,
|
||||
.write = NULL,
|
||||
.open = simple_open,
|
||||
};
|
||||
|
||||
static void pmbus_remove_debugfs(void *data)
|
||||
{
|
||||
@ -2846,16 +3124,80 @@ static int pmbus_init_debugfs(struct i2c_client *client,
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Allocate the max possible entries we need. */
|
||||
/*
|
||||
* Allocate the max possible entries we need.
|
||||
* 6 entries device-specific
|
||||
* 10 entries page-specific
|
||||
*/
|
||||
entries = devm_kcalloc(data->dev,
|
||||
data->info->pages * 10, sizeof(*entries),
|
||||
6 + data->info->pages * 10, sizeof(*entries),
|
||||
GFP_KERNEL);
|
||||
if (!entries)
|
||||
return -ENOMEM;
|
||||
|
||||
debugfs_create_file("pec", 0664, data->debugfs, client,
|
||||
&pmbus_debugfs_ops_pec);
|
||||
/*
|
||||
* Add device-specific entries.
|
||||
* Please note that the PMBUS standard allows all registers to be
|
||||
* page-specific.
|
||||
* To reduce the number of debugfs entries for devices with many pages
|
||||
* assume that values of the following registers are the same for all
|
||||
* pages and report values only for page 0.
|
||||
*/
|
||||
if (pmbus_check_block_register(client, 0, PMBUS_MFR_ID)) {
|
||||
entries[idx].client = client;
|
||||
entries[idx].page = 0;
|
||||
entries[idx].reg = PMBUS_MFR_ID;
|
||||
debugfs_create_file("mfr_id", 0444, data->debugfs,
|
||||
&entries[idx++],
|
||||
&pmbus_debugfs_ops_mfr);
|
||||
}
|
||||
|
||||
if (pmbus_check_block_register(client, 0, PMBUS_MFR_MODEL)) {
|
||||
entries[idx].client = client;
|
||||
entries[idx].page = 0;
|
||||
entries[idx].reg = PMBUS_MFR_MODEL;
|
||||
debugfs_create_file("mfr_model", 0444, data->debugfs,
|
||||
&entries[idx++],
|
||||
&pmbus_debugfs_ops_mfr);
|
||||
}
|
||||
|
||||
if (pmbus_check_block_register(client, 0, PMBUS_MFR_REVISION)) {
|
||||
entries[idx].client = client;
|
||||
entries[idx].page = 0;
|
||||
entries[idx].reg = PMBUS_MFR_REVISION;
|
||||
debugfs_create_file("mfr_revision", 0444, data->debugfs,
|
||||
&entries[idx++],
|
||||
&pmbus_debugfs_ops_mfr);
|
||||
}
|
||||
|
||||
if (pmbus_check_block_register(client, 0, PMBUS_MFR_LOCATION)) {
|
||||
entries[idx].client = client;
|
||||
entries[idx].page = 0;
|
||||
entries[idx].reg = PMBUS_MFR_LOCATION;
|
||||
debugfs_create_file("mfr_location", 0444, data->debugfs,
|
||||
&entries[idx++],
|
||||
&pmbus_debugfs_ops_mfr);
|
||||
}
|
||||
|
||||
if (pmbus_check_block_register(client, 0, PMBUS_MFR_DATE)) {
|
||||
entries[idx].client = client;
|
||||
entries[idx].page = 0;
|
||||
entries[idx].reg = PMBUS_MFR_DATE;
|
||||
debugfs_create_file("mfr_date", 0444, data->debugfs,
|
||||
&entries[idx++],
|
||||
&pmbus_debugfs_ops_mfr);
|
||||
}
|
||||
|
||||
if (pmbus_check_block_register(client, 0, PMBUS_MFR_SERIAL)) {
|
||||
entries[idx].client = client;
|
||||
entries[idx].page = 0;
|
||||
entries[idx].reg = PMBUS_MFR_SERIAL;
|
||||
debugfs_create_file("mfr_serial", 0444, data->debugfs,
|
||||
&entries[idx++],
|
||||
&pmbus_debugfs_ops_mfr);
|
||||
}
|
||||
|
||||
/* Add page specific entries */
|
||||
for (i = 0; i < data->info->pages; ++i) {
|
||||
/* Check accessibility of status register if it's not page 0 */
|
||||
if (!i || pmbus_check_status_register(client, i)) {
|
||||
|
@ -523,6 +523,28 @@ static int __init sch56xx_device_add(int address, const char *name)
|
||||
return PTR_ERR_OR_ZERO(sch56xx_pdev);
|
||||
}
|
||||
|
||||
static const struct dmi_system_id sch56xx_dmi_override_table[] __initconst = {
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS W380"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO P710"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO E9900"),
|
||||
},
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
/* For autoloading only */
|
||||
static const struct dmi_system_id sch56xx_dmi_table[] __initconst = {
|
||||
{
|
||||
@ -543,16 +565,18 @@ static int __init sch56xx_init(void)
|
||||
if (!dmi_check_system(sch56xx_dmi_table))
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* Some machines like the Esprimo P720 and Esprimo C700 have
|
||||
* onboard devices named " Antiope"/" Theseus" instead of
|
||||
* "Antiope"/"Theseus", so we need to check for both.
|
||||
*/
|
||||
if (!dmi_find_device(DMI_DEV_TYPE_OTHER, "Antiope", NULL) &&
|
||||
!dmi_find_device(DMI_DEV_TYPE_OTHER, " Antiope", NULL) &&
|
||||
!dmi_find_device(DMI_DEV_TYPE_OTHER, "Theseus", NULL) &&
|
||||
!dmi_find_device(DMI_DEV_TYPE_OTHER, " Theseus", NULL))
|
||||
return -ENODEV;
|
||||
if (!dmi_check_system(sch56xx_dmi_override_table)) {
|
||||
/*
|
||||
* Some machines like the Esprimo P720 and Esprimo C700 have
|
||||
* onboard devices named " Antiope"/" Theseus" instead of
|
||||
* "Antiope"/"Theseus", so we need to check for both.
|
||||
*/
|
||||
if (!dmi_find_device(DMI_DEV_TYPE_OTHER, "Antiope", NULL) &&
|
||||
!dmi_find_device(DMI_DEV_TYPE_OTHER, " Antiope", NULL) &&
|
||||
!dmi_find_device(DMI_DEV_TYPE_OTHER, "Theseus", NULL) &&
|
||||
!dmi_find_device(DMI_DEV_TYPE_OTHER, " Theseus", NULL))
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1020,25 +1020,20 @@ err_release_reg:
|
||||
static int sht15_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sht15_data *data = platform_get_drvdata(pdev);
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Make sure any reads from the device are done and
|
||||
* prevent new ones beginning
|
||||
*/
|
||||
mutex_lock(&data->read_lock);
|
||||
if (sht15_soft_reset(data)) {
|
||||
mutex_unlock(&data->read_lock);
|
||||
return -EFAULT;
|
||||
}
|
||||
hwmon_device_unregister(data->hwmon_dev);
|
||||
sysfs_remove_group(&pdev->dev.kobj, &sht15_attr_group);
|
||||
|
||||
ret = sht15_soft_reset(data);
|
||||
if (ret)
|
||||
dev_err(&pdev->dev, "Failed to reset device (%pe)\n", ERR_PTR(ret));
|
||||
|
||||
if (!IS_ERR(data->reg)) {
|
||||
regulator_unregister_notifier(data->reg, &data->nb);
|
||||
regulator_disable(data->reg);
|
||||
}
|
||||
|
||||
mutex_unlock(&data->read_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -140,7 +140,8 @@ static int tps23861_read_temp(struct tps23861_data *data, long *val)
|
||||
static int tps23861_read_voltage(struct tps23861_data *data, int channel,
|
||||
long *val)
|
||||
{
|
||||
unsigned int regval;
|
||||
__le16 regval;
|
||||
long raw_val;
|
||||
int err;
|
||||
|
||||
if (channel < TPS23861_NUM_PORTS) {
|
||||
@ -155,7 +156,8 @@ static int tps23861_read_voltage(struct tps23861_data *data, int channel,
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
*val = (FIELD_GET(VOLTAGE_CURRENT_MASK, regval) * VOLTAGE_LSB) / 1000;
|
||||
raw_val = le16_to_cpu(regval);
|
||||
*val = (FIELD_GET(VOLTAGE_CURRENT_MASK, raw_val) * VOLTAGE_LSB) / 1000;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -163,8 +165,9 @@ static int tps23861_read_voltage(struct tps23861_data *data, int channel,
|
||||
static int tps23861_read_current(struct tps23861_data *data, int channel,
|
||||
long *val)
|
||||
{
|
||||
unsigned int current_lsb;
|
||||
unsigned int regval;
|
||||
long raw_val, current_lsb;
|
||||
__le16 regval;
|
||||
|
||||
int err;
|
||||
|
||||
if (data->shunt_resistor == SHUNT_RESISTOR_DEFAULT)
|
||||
@ -178,7 +181,8 @@ static int tps23861_read_current(struct tps23861_data *data, int channel,
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
*val = (FIELD_GET(VOLTAGE_CURRENT_MASK, regval) * current_lsb) / 1000000;
|
||||
raw_val = le16_to_cpu(regval);
|
||||
*val = (FIELD_GET(VOLTAGE_CURRENT_MASK, raw_val) * current_lsb) / 1000000;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user