mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-06 02:50:49 +09:00
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:
@@ -938,3 +938,12 @@ Description:
|
|||||||
- 1: enable
|
- 1: enable
|
||||||
|
|
||||||
RW
|
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,adm1032
|
||||||
- adi,adt7461
|
- adi,adt7461
|
||||||
- adi,adt7461a
|
- adi,adt7461a
|
||||||
|
- adi,adt7481
|
||||||
- dallas,max6646
|
- dallas,max6646
|
||||||
- dallas,max6647
|
- dallas,max6647
|
||||||
- dallas,max6649
|
- dallas,max6649
|
||||||
@@ -50,6 +51,12 @@ properties:
|
|||||||
"#thermal-sensor-cells":
|
"#thermal-sensor-cells":
|
||||||
const: 1
|
const: 1
|
||||||
|
|
||||||
|
'#address-cells':
|
||||||
|
const: 1
|
||||||
|
|
||||||
|
'#size-cells':
|
||||||
|
const: 0
|
||||||
|
|
||||||
vcc-supply:
|
vcc-supply:
|
||||||
description: phandle to the regulator that provides the +VCC supply
|
description: phandle to the regulator that provides the +VCC supply
|
||||||
|
|
||||||
@@ -61,6 +68,29 @@ required:
|
|||||||
- compatible
|
- compatible
|
||||||
- reg
|
- 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:
|
allOf:
|
||||||
- if:
|
- if:
|
||||||
not:
|
not:
|
||||||
@@ -70,12 +100,84 @@ allOf:
|
|||||||
enum:
|
enum:
|
||||||
- adi,adt7461
|
- adi,adt7461
|
||||||
- adi,adt7461a
|
- adi,adt7461a
|
||||||
|
- adi,adt7481
|
||||||
- ti,tmp451
|
- ti,tmp451
|
||||||
- ti,tmp461
|
- ti,tmp461
|
||||||
then:
|
then:
|
||||||
properties:
|
properties:
|
||||||
ti,extended-range-enable: false
|
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
|
additionalProperties: false
|
||||||
|
|
||||||
examples:
|
examples:
|
||||||
@@ -94,3 +196,32 @@ examples:
|
|||||||
#thermal-sensor-cells = <1>;
|
#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
|
- adi,adp5585-02
|
||||||
# Analog Devices ADP5589 Keypad Decoder and I/O Expansion
|
# Analog Devices ADP5589 Keypad Decoder and I/O Expansion
|
||||||
- adi,adp5589
|
- adi,adp5589
|
||||||
|
# Analog Devices LT7182S Dual Channel 6A, 20V PolyPhase Step-Down Silent Switcher
|
||||||
|
- adi,lt7182s
|
||||||
# AMS iAQ-Core VOC Sensor
|
# AMS iAQ-Core VOC Sensor
|
||||||
- ams,iaq-core
|
- ams,iaq-core
|
||||||
# i2c serial eeprom (24cxx)
|
# i2c serial eeprom (24cxx)
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ Supported devices:
|
|||||||
* Aquacomputer Farbwerk RGB controller
|
* Aquacomputer Farbwerk RGB controller
|
||||||
* Aquacomputer Farbwerk 360 RGB controller
|
* Aquacomputer Farbwerk 360 RGB controller
|
||||||
* Aquacomputer Octo fan controller
|
* Aquacomputer Octo fan controller
|
||||||
|
* Aquacomputer Quadro fan controller
|
||||||
|
|
||||||
Author: Aleksa Savic
|
Author: Aleksa Savic
|
||||||
|
|
||||||
@@ -33,6 +34,9 @@ better suited for userspace tools.
|
|||||||
The Octo exposes four temperature sensors and eight PWM controllable fans, along
|
The Octo exposes four temperature sensors and eight PWM controllable fans, along
|
||||||
with their speed (in RPM), power, voltage and current.
|
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,
|
The Farbwerk and Farbwerk 360 expose four temperature sensors. Depending on the device,
|
||||||
not all sysfs and debugfs entries will be available.
|
not all sysfs and debugfs entries will be available.
|
||||||
|
|
||||||
@@ -45,13 +49,14 @@ the kernel and supports hotswapping.
|
|||||||
Sysfs entries
|
Sysfs entries
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
================ =============================================
|
================ ==============================================
|
||||||
temp[1-4]_input Temperature sensors (in millidegrees Celsius)
|
temp[1-4]_input Temperature sensors (in millidegrees Celsius)
|
||||||
fan[1-2]_input Pump/fan speed (in RPM)
|
fan[1-8]_input Pump/fan speed (in RPM) / Flow speed (in dL/h)
|
||||||
power[1-2]_input Pump/fan power (in micro Watts)
|
power[1-8]_input Pump/fan power (in micro Watts)
|
||||||
in[0-2]_input Pump/fan voltage (in milli Volts)
|
in[0-7]_input Pump/fan voltage (in milli Volts)
|
||||||
curr[1-2]_input Pump/fan current (in milli Amperes)
|
curr[1-8]_input Pump/fan current (in milli Amperes)
|
||||||
================ =============================================
|
pwm[1-8] Fan PWM (0 - 255)
|
||||||
|
================ ==============================================
|
||||||
|
|
||||||
Debugfs entries
|
Debugfs entries
|
||||||
---------------
|
---------------
|
||||||
|
|||||||
@@ -13,12 +13,16 @@ Supported boards:
|
|||||||
* ROG CROSSHAIR VIII FORMULA
|
* ROG CROSSHAIR VIII FORMULA
|
||||||
* ROG CROSSHAIR VIII HERO
|
* ROG CROSSHAIR VIII HERO
|
||||||
* ROG CROSSHAIR VIII IMPACT
|
* ROG CROSSHAIR VIII IMPACT
|
||||||
|
* ROG MAXIMUS XI HERO
|
||||||
|
* ROG MAXIMUS XI HERO (WI-FI)
|
||||||
* ROG STRIX B550-E GAMING
|
* ROG STRIX B550-E GAMING
|
||||||
* ROG STRIX B550-I GAMING
|
* ROG STRIX B550-I GAMING
|
||||||
* ROG STRIX X570-E GAMING
|
* ROG STRIX X570-E GAMING
|
||||||
* ROG STRIX X570-E GAMING WIFI II
|
* ROG STRIX X570-E GAMING WIFI II
|
||||||
* ROG STRIX X570-F GAMING
|
* ROG STRIX X570-F GAMING
|
||||||
* ROG STRIX X570-I GAMING
|
* ROG STRIX X570-I GAMING
|
||||||
|
* ROG STRIX Z690-A GAMING WIFI D4
|
||||||
|
* ROG ZENITH II EXTREME
|
||||||
|
|
||||||
Authors:
|
Authors:
|
||||||
- Eugene Shalygin <eugene.shalygin@gmail.com>
|
- 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.
|
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
|
Disabling automatic BIOS fan control
|
||||||
------------------------------------
|
------------------------------------
|
||||||
|
|
||||||
|
|||||||
@@ -109,6 +109,7 @@ Hardware Monitoring Kernel Drivers
|
|||||||
lm95234
|
lm95234
|
||||||
lm95245
|
lm95245
|
||||||
lochnagar
|
lochnagar
|
||||||
|
lt7182s
|
||||||
ltc2992
|
ltc2992
|
||||||
ltc2945
|
ltc2945
|
||||||
ltc2947
|
ltc2947
|
||||||
|
|||||||
@@ -3,6 +3,14 @@ Kernel driver lm90
|
|||||||
|
|
||||||
Supported chips:
|
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
|
* National Semiconductor LM90
|
||||||
|
|
||||||
Prefix: 'lm90'
|
Prefix: 'lm90'
|
||||||
@@ -43,6 +51,30 @@ Supported chips:
|
|||||||
|
|
||||||
http://www.national.com/mpf/LM/LM86.html
|
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
|
* Analog Devices ADM1032
|
||||||
|
|
||||||
Prefix: 'adm1032'
|
Prefix: 'adm1032'
|
||||||
@@ -73,6 +105,36 @@ Supported chips:
|
|||||||
|
|
||||||
https://www.onsemi.com/PowerSolutions/product.do?id=ADT7461A
|
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
|
* ON Semiconductor NCT1008
|
||||||
|
|
||||||
Prefix: 'nct1008'
|
Prefix: 'nct1008'
|
||||||
@@ -83,6 +145,72 @@ Supported chips:
|
|||||||
|
|
||||||
https://www.onsemi.com/PowerSolutions/product.do?id=NCT1008
|
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
|
* Maxim MAX6646
|
||||||
|
|
||||||
Prefix: 'max6646'
|
Prefix: 'max6646'
|
||||||
@@ -105,7 +233,7 @@ Supported chips:
|
|||||||
|
|
||||||
* Maxim MAX6648
|
* Maxim MAX6648
|
||||||
|
|
||||||
Prefix: 'max6646'
|
Prefix: 'max6648'
|
||||||
|
|
||||||
Addresses scanned: I2C 0x4c
|
Addresses scanned: I2C 0x4c
|
||||||
|
|
||||||
@@ -191,7 +319,7 @@ Supported chips:
|
|||||||
|
|
||||||
* Maxim MAX6692
|
* Maxim MAX6692
|
||||||
|
|
||||||
Prefix: 'max6646'
|
Prefix: 'max6648'
|
||||||
|
|
||||||
Addresses scanned: I2C 0x4c
|
Addresses scanned: I2C 0x4c
|
||||||
|
|
||||||
@@ -275,6 +403,46 @@ Supported chips:
|
|||||||
|
|
||||||
https://www.ti.com/lit/gpn/tmp461
|
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>
|
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
|
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.
|
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,
|
Note that there is no easy way to differentiate between the MAX6657,
|
||||||
MAX6658 and MAX6659 variants. The extra features of the MAX6659 are only
|
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,
|
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
|
The MAX6680 and MAX6681 only differ in their pinout, therefore they obviously
|
||||||
can't (and don't need to) be distinguished.
|
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
|
The different chipsets of the family are not strictly identical, although
|
||||||
very similar. For reference, here comes a non-exhaustive list of specific
|
very similar. For reference, here comes a non-exhaustive list of specific
|
||||||
features:
|
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:
|
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.
|
* Filter and alert configuration register at 0xBF.
|
||||||
* ALERT is triggered by temperatures over critical limits.
|
* ALERT is triggered by temperatures over critical limits.
|
||||||
|
|
||||||
@@ -322,8 +512,31 @@ ADM1032:
|
|||||||
ADT7461, ADT7461A, NCT1008:
|
ADT7461, ADT7461A, NCT1008:
|
||||||
* Extended temperature range (breaks compatibility)
|
* Extended temperature range (breaks compatibility)
|
||||||
* Lower resolution for remote temperature
|
* 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
|
* Better local resolution
|
||||||
* Selectable address
|
* Selectable address
|
||||||
* Remote sensor type selection
|
* 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.
|
transaction completion, effectively doubling the register cache refresh time.
|
||||||
I guess reliability comes at a price, but it's quite expensive this 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
|
So, as not everyone might enjoy the slowdown, PEC is disabled by default and
|
||||||
sysfs. Just write 0 to the "pec" file and PEC will be disabled. Write 1
|
can be enabled through sysfs. Just write 1 to the "pec" file and PEC will be
|
||||||
to that file to enable PEC again.
|
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
|
non-standard PMBus commands to standard commands, or to augment standard
|
||||||
command return values with device specific information.
|
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
|
API functions
|
||||||
=============
|
=============
|
||||||
|
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ config SENSORS_AD7418
|
|||||||
config SENSORS_ADM1021
|
config SENSORS_ADM1021
|
||||||
tristate "Analog Devices ADM1021 and compatibles"
|
tristate "Analog Devices ADM1021 and compatibles"
|
||||||
depends on I2C
|
depends on I2C
|
||||||
|
depends on SENSORS_LM90=n
|
||||||
help
|
help
|
||||||
If you say yes here you get support for Analog Devices ADM1021
|
If you say yes here you get support for Analog Devices ADM1021
|
||||||
and ADM1023 sensor chips and clones: Maxim MAX1617 and MAX1617A,
|
and ADM1023 sensor chips and clones: Maxim MAX1617 and MAX1617A,
|
||||||
@@ -256,13 +257,13 @@ config SENSORS_AHT10
|
|||||||
will be called aht10.
|
will be called aht10.
|
||||||
|
|
||||||
config SENSORS_AQUACOMPUTER_D5NEXT
|
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
|
depends on USB_HID
|
||||||
select CRC16
|
select CRC16
|
||||||
help
|
help
|
||||||
If you say yes here you get support for sensors and fans of
|
If you say yes here you get support for sensors and fans of
|
||||||
the Aquacomputer D5 Next watercooling pump, Octo fan
|
the Aquacomputer D5 Next watercooling pump, Octo and Quadro fan
|
||||||
controller, Farbwerk and Farbwerk 360 RGB controllers, where
|
controllers, Farbwerk and Farbwerk 360 RGB controllers, where
|
||||||
available.
|
available.
|
||||||
|
|
||||||
This driver can also be built as a module. If so, the module
|
This driver can also be built as a module. If so, the module
|
||||||
@@ -381,7 +382,7 @@ config SENSORS_ARM_SCPI
|
|||||||
|
|
||||||
config SENSORS_ASB100
|
config SENSORS_ASB100
|
||||||
tristate "Asus ASB100 Bach"
|
tristate "Asus ASB100 Bach"
|
||||||
depends on X86 && I2C
|
depends on (X86 || COMPILE_TEST) && I2C
|
||||||
select HWMON_VID
|
select HWMON_VID
|
||||||
help
|
help
|
||||||
If you say yes here you get support for the ASB100 Bach sensor
|
If you say yes here you get support for the ASB100 Bach sensor
|
||||||
@@ -626,7 +627,7 @@ config SENSORS_MC13783_ADC
|
|||||||
|
|
||||||
config SENSORS_FSCHMD
|
config SENSORS_FSCHMD
|
||||||
tristate "Fujitsu Siemens Computers sensor chips"
|
tristate "Fujitsu Siemens Computers sensor chips"
|
||||||
depends on X86 && I2C
|
depends on (X86 || COMPILE_TEST) && I2C
|
||||||
help
|
help
|
||||||
If you say yes here you get support for the following Fujitsu
|
If you say yes here you get support for the following Fujitsu
|
||||||
Siemens Computers (FSC) sensor chips: Poseidon, Scylla, Hermes,
|
Siemens Computers (FSC) sensor chips: Poseidon, Scylla, Hermes,
|
||||||
@@ -1102,6 +1103,7 @@ config SENSORS_MAX6639
|
|||||||
config SENSORS_MAX6642
|
config SENSORS_MAX6642
|
||||||
tristate "Maxim MAX6642 sensor chip"
|
tristate "Maxim MAX6642 sensor chip"
|
||||||
depends on I2C
|
depends on I2C
|
||||||
|
depends on SENSORS_LM90=n
|
||||||
help
|
help
|
||||||
If you say yes here you get support for MAX6642 sensor chip.
|
If you say yes here you get support for MAX6642 sensor chip.
|
||||||
MAX6642 is a SMBus-Compatible Remote/Local Temperature Sensor
|
MAX6642 is a SMBus-Compatible Remote/Local Temperature Sensor
|
||||||
@@ -1357,12 +1359,15 @@ config SENSORS_LM90
|
|||||||
tristate "National Semiconductor LM90 and compatibles"
|
tristate "National Semiconductor LM90 and compatibles"
|
||||||
depends on I2C
|
depends on I2C
|
||||||
help
|
help
|
||||||
If you say yes here you get support for National Semiconductor LM90,
|
If you say yes here you get support for National Semiconductor LM84,
|
||||||
LM86, LM89 and LM99, Analog Devices ADM1032, ADT7461, and ADT7461A,
|
LM90, LM86, LM89 and LM99, Analog Devices ADM1020, ADM2021, ADM1021A,
|
||||||
Maxim MAX6646, MAX6647, MAX6648, MAX6649, MAX6654, MAX6657, MAX6658,
|
ADM1023, ADM1032, ADT7461, ADT7461A, ADT7481, ADT7482, and ADT7483A,
|
||||||
MAX6659, MAX6680, MAX6681, MAX6692, MAX6695, MAX6696,
|
Maxim MAX1617, MAX6642, MAX6646, MAX6647, MAX6648, MAX6649, MAX6654,
|
||||||
ON Semiconductor NCT1008, Winbond/Nuvoton W83L771W/G/AWG/ASG,
|
MAX6657, MAX6658, MAX6659, MAX6680, MAX6681, MAX6692, MAX6695,
|
||||||
Philips SA56004, GMT G781, Texas Instruments TMP451 and TMP461
|
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.
|
sensor chips.
|
||||||
|
|
||||||
This driver can also be built as a module. If so, the module
|
This driver can also be built as a module. If so, the module
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0+
|
// 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
|
* Aquacomputer devices send HID reports (with ID 0x01) every second to report
|
||||||
* sensor values.
|
* sensor values.
|
||||||
@@ -21,17 +22,19 @@
|
|||||||
|
|
||||||
#define USB_VENDOR_ID_AQUACOMPUTER 0x0c70
|
#define USB_VENDOR_ID_AQUACOMPUTER 0x0c70
|
||||||
#define USB_PRODUCT_ID_FARBWERK 0xf00a
|
#define USB_PRODUCT_ID_FARBWERK 0xf00a
|
||||||
|
#define USB_PRODUCT_ID_QUADRO 0xf00d
|
||||||
#define USB_PRODUCT_ID_D5NEXT 0xf00e
|
#define USB_PRODUCT_ID_D5NEXT 0xf00e
|
||||||
#define USB_PRODUCT_ID_FARBWERK360 0xf010
|
#define USB_PRODUCT_ID_FARBWERK360 0xf010
|
||||||
#define USB_PRODUCT_ID_OCTO 0xf011
|
#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[] = {
|
static const char *const aqc_device_names[] = {
|
||||||
[d5next] = "d5next",
|
[d5next] = "d5next",
|
||||||
[farbwerk] = "farbwerk",
|
[farbwerk] = "farbwerk",
|
||||||
[farbwerk360] = "farbwerk360",
|
[farbwerk360] = "farbwerk360",
|
||||||
[octo] = "octo"
|
[octo] = "octo",
|
||||||
|
[quadro] = "quadro"
|
||||||
};
|
};
|
||||||
|
|
||||||
#define DRIVER_NAME "aquacomputer_d5next"
|
#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
|
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 */
|
/* 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
|
/* Pump and fan speed registers in D5 Next control report (from 0-100%) */
|
||||||
|
static u16 d5next_ctrl_fan_offsets[] = { 0x97, 0x42 };
|
||||||
#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
|
|
||||||
|
|
||||||
/* Register offsets for the Farbwerk RGB controller */
|
/* Register offsets for the Farbwerk RGB controller */
|
||||||
#define FARBWERK_NUM_SENSORS 4
|
#define FARBWERK_NUM_SENSORS 4
|
||||||
#define FARBWERK_SENSOR_START 0x2f
|
#define FARBWERK_SENSOR_START 0x2f
|
||||||
#define FARBWERK_SENSOR_SIZE 0x02
|
|
||||||
#define FARBWERK_SENSOR_DISCONNECTED 0x7FFF
|
|
||||||
|
|
||||||
/* Register offsets for the Farbwerk 360 RGB controller */
|
/* Register offsets for the Farbwerk 360 RGB controller */
|
||||||
#define FARBWERK360_NUM_SENSORS 4
|
#define FARBWERK360_NUM_SENSORS 4
|
||||||
#define FARBWERK360_SENSOR_START 0x32
|
#define FARBWERK360_SENSOR_START 0x32
|
||||||
#define FARBWERK360_SENSOR_SIZE 0x02
|
|
||||||
#define FARBWERK360_SENSOR_DISCONNECTED 0x7FFF
|
|
||||||
|
|
||||||
/* Register offsets for the Octo fan controller */
|
/* Register offsets for the Octo fan controller */
|
||||||
#define OCTO_POWER_CYCLES 0x18
|
#define OCTO_POWER_CYCLES 0x18
|
||||||
#define OCTO_NUM_FANS 8
|
#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_NUM_SENSORS 4
|
||||||
#define OCTO_SENSOR_START 0x3D
|
#define OCTO_SENSOR_START 0x3D
|
||||||
#define OCTO_SENSOR_SIZE 0x02
|
#define OCTO_CTRL_REPORT_SIZE 0x65F
|
||||||
#define OCTO_SENSOR_DISCONNECTED 0x7FFF
|
static u8 octo_sensor_fan_offsets[] = { 0x7D, 0x8A, 0x97, 0xA4, 0xB1, 0xBE, 0xCB, 0xD8 };
|
||||||
|
|
||||||
#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
|
|
||||||
|
|
||||||
/* Fan speed registers in Octo control report (from 0-100%) */
|
/* Fan speed registers in Octo control report (from 0-100%) */
|
||||||
static u16 octo_ctrl_fan_offsets[] = { 0x5B, 0xB0, 0x105, 0x15A, 0x1AF, 0x204, 0x259, 0x2AE };
|
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 */
|
/* Labels for D5 Next */
|
||||||
static const char *const label_d5next_temp[] = {
|
static const char *const label_d5next_temp[] = {
|
||||||
"Coolant temp"
|
"Coolant temp"
|
||||||
@@ -126,7 +130,8 @@ static const char *const label_d5next_power[] = {
|
|||||||
static const char *const label_d5next_voltages[] = {
|
static const char *const label_d5next_voltages[] = {
|
||||||
"Pump voltage",
|
"Pump voltage",
|
||||||
"Fan voltage",
|
"Fan voltage",
|
||||||
"+5V voltage"
|
"+5V voltage",
|
||||||
|
"+12V voltage"
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *const label_d5next_current[] = {
|
static const char *const label_d5next_current[] = {
|
||||||
@@ -134,7 +139,7 @@ static const char *const label_d5next_current[] = {
|
|||||||
"Fan 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[] = {
|
static const char *const label_temp_sensors[] = {
|
||||||
"Sensor 1",
|
"Sensor 1",
|
||||||
"Sensor 2",
|
"Sensor 2",
|
||||||
@@ -142,7 +147,7 @@ static const char *const label_temp_sensors[] = {
|
|||||||
"Sensor 4"
|
"Sensor 4"
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Labels for Octo */
|
/* Labels for Octo and Quadro (except speed) */
|
||||||
static const char *const label_fan_speed[] = {
|
static const char *const label_fan_speed[] = {
|
||||||
"Fan 1 speed",
|
"Fan 1 speed",
|
||||||
"Fan 2 speed",
|
"Fan 2 speed",
|
||||||
@@ -187,6 +192,15 @@ static const char *const label_fan_current[] = {
|
|||||||
"Fan 8 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 aqc_data {
|
||||||
struct hid_device *hdev;
|
struct hid_device *hdev;
|
||||||
struct device *hwmon_dev;
|
struct device *hwmon_dev;
|
||||||
@@ -201,11 +215,19 @@ struct aqc_data {
|
|||||||
int checksum_length;
|
int checksum_length;
|
||||||
int checksum_offset;
|
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 */
|
/* General info, same across all devices */
|
||||||
u32 serial_number[2];
|
u32 serial_number[2];
|
||||||
u16 firmware_version;
|
u16 firmware_version;
|
||||||
|
|
||||||
/* How many times the device was powered on */
|
/* How many times the device was powered on, if available */
|
||||||
u32 power_cycles;
|
u32 power_cycles;
|
||||||
|
|
||||||
/* Sensor values */
|
/* Sensor values */
|
||||||
@@ -323,56 +345,47 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3
|
|||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case hwmon_temp:
|
case hwmon_temp:
|
||||||
switch (priv->kind) {
|
if (channel < priv->num_temp_sensors)
|
||||||
case d5next:
|
|
||||||
if (channel == 0)
|
|
||||||
return 0444;
|
|
||||||
break;
|
|
||||||
case farbwerk:
|
|
||||||
case farbwerk360:
|
|
||||||
case octo:
|
|
||||||
return 0444;
|
return 0444;
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case hwmon_pwm:
|
case hwmon_pwm:
|
||||||
switch (priv->kind) {
|
if (priv->fan_ctrl_offsets && channel < priv->num_fans) {
|
||||||
case octo:
|
|
||||||
switch (attr) {
|
switch (attr) {
|
||||||
case hwmon_pwm_input:
|
case hwmon_pwm_input:
|
||||||
return 0644;
|
return 0644;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case hwmon_fan:
|
case hwmon_fan:
|
||||||
case hwmon_power:
|
|
||||||
case hwmon_curr:
|
|
||||||
switch (priv->kind) {
|
switch (priv->kind) {
|
||||||
case d5next:
|
case quadro:
|
||||||
if (channel < 2)
|
/* Special case to support flow sensor */
|
||||||
|
if (channel < priv->num_fans + 1)
|
||||||
return 0444;
|
return 0444;
|
||||||
break;
|
break;
|
||||||
case octo:
|
|
||||||
return 0444;
|
|
||||||
default:
|
default:
|
||||||
|
if (channel < priv->num_fans)
|
||||||
|
return 0444;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case hwmon_power:
|
||||||
|
case hwmon_curr:
|
||||||
|
if (channel < priv->num_fans)
|
||||||
|
return 0444;
|
||||||
|
break;
|
||||||
case hwmon_in:
|
case hwmon_in:
|
||||||
switch (priv->kind) {
|
switch (priv->kind) {
|
||||||
case d5next:
|
case d5next:
|
||||||
if (channel < 3)
|
/* Special case to support +5V and +12V voltage sensors */
|
||||||
|
if (channel < priv->num_fans + 2)
|
||||||
return 0444;
|
return 0444;
|
||||||
break;
|
break;
|
||||||
case octo:
|
|
||||||
return 0444;
|
|
||||||
default:
|
default:
|
||||||
|
if (channel < priv->num_fans)
|
||||||
|
return 0444;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
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];
|
*val = priv->power_input[channel];
|
||||||
break;
|
break;
|
||||||
case hwmon_pwm:
|
case hwmon_pwm:
|
||||||
switch (priv->kind) {
|
if (priv->fan_ctrl_offsets) {
|
||||||
case octo:
|
ret = aqc_get_ctrl_val(priv, priv->fan_ctrl_offsets[channel]);
|
||||||
ret = aqc_get_ctrl_val(priv, octo_ctrl_fan_offsets[channel]);
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
*val = aqc_percent_to_pwm(ret);
|
*val = aqc_percent_to_pwm(ret);
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case hwmon_in:
|
case hwmon_in:
|
||||||
@@ -469,19 +478,15 @@ static int aqc_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
|
|||||||
case hwmon_pwm:
|
case hwmon_pwm:
|
||||||
switch (attr) {
|
switch (attr) {
|
||||||
case hwmon_pwm_input:
|
case hwmon_pwm_input:
|
||||||
switch (priv->kind) {
|
if (priv->fan_ctrl_offsets) {
|
||||||
case octo:
|
|
||||||
pwm_value = aqc_pwm_to_percent(val);
|
pwm_value = aqc_pwm_to_percent(val);
|
||||||
if (pwm_value < 0)
|
if (pwm_value < 0)
|
||||||
return pwm_value;
|
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);
|
pwm_value);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
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->serial_number[1] = get_unaligned_be16(data + SERIAL_SECOND_PART);
|
||||||
priv->firmware_version = get_unaligned_be16(data + FIRMWARE_VERSION);
|
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) {
|
switch (priv->kind) {
|
||||||
case d5next:
|
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->voltage_input[2] = get_unaligned_be16(data + D5NEXT_5V_VOLTAGE) * 10;
|
||||||
|
priv->voltage_input[3] = get_unaligned_be16(data + D5NEXT_12V_VOLTAGE) * 10;
|
||||||
priv->current_input[0] = get_unaligned_be16(data + D5NEXT_PUMP_CURRENT);
|
|
||||||
priv->current_input[1] = get_unaligned_be16(data + D5NEXT_FAN_CURRENT);
|
|
||||||
break;
|
break;
|
||||||
case farbwerk:
|
case quadro:
|
||||||
/* Temperature sensor readings */
|
priv->speed_input[4] = get_unaligned_be16(data + priv->flow_sensor_offset);
|
||||||
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;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
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("serial_number", 0444, priv->debugfs, priv, &serial_number_fops);
|
||||||
debugfs_create_file("firmware_version", 0444, priv->debugfs, priv, &firmware_version_fops);
|
debugfs_create_file("firmware_version", 0444, priv->debugfs, priv, &firmware_version_fops);
|
||||||
|
|
||||||
switch (priv->kind) {
|
if (priv->power_cycle_count_offset != 0)
|
||||||
case d5next:
|
|
||||||
case octo:
|
|
||||||
debugfs_create_file("power_cycles", 0444, priv->debugfs, priv, &power_cycles_fops);
|
debugfs_create_file("power_cycles", 0444, priv->debugfs, priv, &power_cycles_fops);
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
@@ -747,6 +712,14 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|||||||
case USB_PRODUCT_ID_D5NEXT:
|
case USB_PRODUCT_ID_D5NEXT:
|
||||||
priv->kind = 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->temp_label = label_d5next_temp;
|
||||||
priv->speed_label = label_d5next_speeds;
|
priv->speed_label = label_d5next_speeds;
|
||||||
priv->power_label = label_d5next_power;
|
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:
|
case USB_PRODUCT_ID_FARBWERK:
|
||||||
priv->kind = 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;
|
priv->temp_label = label_temp_sensors;
|
||||||
break;
|
break;
|
||||||
case USB_PRODUCT_ID_FARBWERK360:
|
case USB_PRODUCT_ID_FARBWERK360:
|
||||||
priv->kind = 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;
|
priv->temp_label = label_temp_sensors;
|
||||||
break;
|
break;
|
||||||
case USB_PRODUCT_ID_OCTO:
|
case USB_PRODUCT_ID_OCTO:
|
||||||
priv->kind = 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->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->temp_label = label_temp_sensors;
|
||||||
priv->speed_label = label_fan_speed;
|
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->voltage_label = label_fan_voltage;
|
||||||
priv->current_label = label_fan_current;
|
priv->current_label = label_fan_current;
|
||||||
break;
|
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:
|
default:
|
||||||
break;
|
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->name = aqc_device_names[priv->kind];
|
||||||
|
|
||||||
priv->buffer = devm_kzalloc(&hdev->dev, priv->buffer_size, GFP_KERNEL);
|
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_FARBWERK) },
|
||||||
{ HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_FARBWERK360) },
|
{ 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_OCTO) },
|
||||||
|
{ HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_QUADRO) },
|
||||||
{ }
|
{ }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -159,7 +159,7 @@
|
|||||||
* 11: reserved.
|
* 11: reserved.
|
||||||
*/
|
*/
|
||||||
#define M_TACH_MODE 0x02 /* 10b */
|
#define M_TACH_MODE 0x02 /* 10b */
|
||||||
#define M_TACH_UNIT 0x0210
|
#define M_TACH_UNIT 0x0420
|
||||||
#define INIT_FAN_CTRL 0xFF
|
#define INIT_FAN_CTRL 0xFF
|
||||||
|
|
||||||
/* How long we sleep in us while waiting for an RPM result. */
|
/* 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 */
|
/* ACPI mutex for locking access to the EC for the firmware */
|
||||||
#define ASUS_HW_ACCESS_MUTEX_ASMX "\\AMW0.ASMX"
|
#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
|
#define MAX_IDENTICAL_BOARD_VARIATIONS 3
|
||||||
|
|
||||||
/* Moniker for the ACPI global lock (':' is not allowed in ASL identifiers) */
|
/* Moniker for the ACPI global lock (':' is not allowed in ASL identifiers) */
|
||||||
@@ -119,6 +123,18 @@ enum ec_sensors {
|
|||||||
ec_sensor_temp_water_in,
|
ec_sensor_temp_water_in,
|
||||||
/* "Water_Out" temperature sensor reading [℃] */
|
/* "Water_Out" temperature sensor reading [℃] */
|
||||||
ec_sensor_temp_water_out,
|
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)
|
#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_CURR_CPU BIT(ec_sensor_curr_cpu)
|
||||||
#define SENSOR_TEMP_WATER_IN BIT(ec_sensor_temp_water_in)
|
#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_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 {
|
enum board_family {
|
||||||
family_unknown,
|
family_unknown,
|
||||||
family_amd_400_series,
|
family_amd_400_series,
|
||||||
family_amd_500_series,
|
family_amd_500_series,
|
||||||
|
family_intel_300_series,
|
||||||
|
family_intel_600_series
|
||||||
};
|
};
|
||||||
|
|
||||||
/* All the known sensors for ASUS EC controllers */
|
/* 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("Water_In", hwmon_temp, 1, 0x01, 0x00),
|
||||||
[ec_sensor_temp_water_out] =
|
[ec_sensor_temp_water_out] =
|
||||||
EC_SENSOR("Water_Out", hwmon_temp, 1, 0x01, 0x01),
|
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 */
|
/* Shortcuts for common combinations */
|
||||||
#define SENSOR_SET_TEMP_CHIPSET_CPU_MB \
|
#define SENSOR_SET_TEMP_CHIPSET_CPU_MB \
|
||||||
(SENSOR_TEMP_CHIPSET | SENSOR_TEMP_CPU | SENSOR_TEMP_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_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 {
|
struct ec_board_info {
|
||||||
const char *board_names[MAX_IDENTICAL_BOARD_VARIATIONS];
|
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,
|
.mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
|
||||||
.family = family_amd_500_series,
|
.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"},
|
.board_names = {"ROG CROSSHAIR VIII IMPACT"},
|
||||||
.sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
|
.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"},
|
.board_names = {"ROG STRIX X570-I GAMING"},
|
||||||
.sensors = SENSOR_TEMP_T_SENSOR | SENSOR_FAN_VRM_HS |
|
.sensors = SENSOR_TEMP_CHIPSET | SENSOR_TEMP_VRM |
|
||||||
SENSOR_FAN_CHIPSET | SENSOR_CURR_CPU |
|
SENSOR_TEMP_T_SENSOR |
|
||||||
SENSOR_IN_CPU_CORE,
|
SENSOR_FAN_VRM_HS | SENSOR_FAN_CHIPSET |
|
||||||
|
SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE,
|
||||||
.mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
|
.mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
|
||||||
.family = family_amd_500_series,
|
.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:
|
case family_amd_500_series:
|
||||||
ec_data->sensors_info = sensors_family_amd_500;
|
ec_data->sensors_info = sensors_family_amd_500;
|
||||||
break;
|
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:
|
default:
|
||||||
dev_err(dev, "Unknown board family: %d",
|
dev_err(dev, "Unknown board family: %d",
|
||||||
ec_data->board_info->family);
|
ec_data->board_info->family);
|
||||||
|
|||||||
@@ -514,22 +514,20 @@ static int asus_wmi_configure_sensor_setup(struct device *dev,
|
|||||||
int i, idx;
|
int i, idx;
|
||||||
int err;
|
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++) {
|
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)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
switch (temp_sensor->data_type) {
|
switch (sensor.data_type) {
|
||||||
case TEMPERATURE_C:
|
case TEMPERATURE_C:
|
||||||
case VOLTAGE:
|
case VOLTAGE:
|
||||||
case CURRENT:
|
case CURRENT:
|
||||||
case FAN_RPM:
|
case FAN_RPM:
|
||||||
case WATER_FLOW:
|
case WATER_FLOW:
|
||||||
type = asus_data_types[temp_sensor->data_type];
|
type = asus_data_types[sensor.data_type];
|
||||||
if (!nr_count[type])
|
if (!nr_count[type])
|
||||||
nr_types++;
|
nr_types++;
|
||||||
nr_count[type]++;
|
nr_count[type]++;
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ struct smm_regs {
|
|||||||
unsigned int edx;
|
unsigned int edx;
|
||||||
unsigned int esi;
|
unsigned int esi;
|
||||||
unsigned int edi;
|
unsigned int edi;
|
||||||
} __packed;
|
};
|
||||||
|
|
||||||
static const char * const temp_labels[] = {
|
static const char * const temp_labels[] = {
|
||||||
"CPU",
|
"CPU",
|
||||||
@@ -175,77 +175,35 @@ static int i8k_smm_func(void *par)
|
|||||||
struct smm_regs *regs = par;
|
struct smm_regs *regs = par;
|
||||||
int eax = regs->eax;
|
int eax = regs->eax;
|
||||||
int ebx = regs->ebx;
|
int ebx = regs->ebx;
|
||||||
|
unsigned char carry;
|
||||||
long long duration;
|
long long duration;
|
||||||
int rc;
|
|
||||||
|
|
||||||
/* SMM requires CPU 0 */
|
/* SMM requires CPU 0 */
|
||||||
if (smp_processor_id() != 0)
|
if (smp_processor_id() != 0)
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
|
|
||||||
#if defined(CONFIG_X86_64)
|
asm volatile("out %%al,$0xb2\n\t"
|
||||||
asm volatile("pushq %%rax\n\t"
|
"out %%al,$0x84\n\t"
|
||||||
"movl 0(%%rax),%%edx\n\t"
|
"setc %0\n"
|
||||||
"pushq %%rdx\n\t"
|
: "=mr" (carry),
|
||||||
"movl 4(%%rax),%%ebx\n\t"
|
"+a" (regs->eax),
|
||||||
"movl 8(%%rax),%%ecx\n\t"
|
"+b" (regs->ebx),
|
||||||
"movl 12(%%rax),%%edx\n\t"
|
"+c" (regs->ecx),
|
||||||
"movl 16(%%rax),%%esi\n\t"
|
"+d" (regs->edx),
|
||||||
"movl 20(%%rax),%%edi\n\t"
|
"+S" (regs->esi),
|
||||||
"popq %%rax\n\t"
|
"+D" (regs->edi));
|
||||||
"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;
|
|
||||||
|
|
||||||
duration = ktime_us_delta(ktime_get(), calltime);
|
duration = ktime_us_delta(ktime_get(), calltime);
|
||||||
pr_debug("smm(0x%.4x 0x%.4x) = 0x%.4x (took %7lld usecs)\n", eax, ebx,
|
pr_debug("smm(0x%.4x 0x%.4x) = 0x%.4x carry: %d (took %7lld usecs)\n",
|
||||||
(rc ? 0xffff : regs->eax & 0xffff), duration);
|
eax, ebx, regs->eax & 0xffff, carry, duration);
|
||||||
|
|
||||||
if (duration > DELL_SMM_MAX_DURATION)
|
if (duration > DELL_SMM_MAX_DURATION)
|
||||||
pr_warn_once("SMM call took %lld usecs!\n", 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 = {
|
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",
|
.ident = "Dell Inspiron",
|
||||||
.matches = {
|
.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],
|
.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_AUTHOR("Guenter Roeck <linus@roeck-us.net>");
|
||||||
MODULE_DESCRIPTION("Hard drive temperature monitor");
|
MODULE_DESCRIPTION("Hard drive temperature monitor");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_ALIAS("platform:drivetemp");
|
||||||
|
|||||||
@@ -49,6 +49,7 @@
|
|||||||
#define SIO_F81768D_ID 0x1210 /* Chipset ID */
|
#define SIO_F81768D_ID 0x1210 /* Chipset ID */
|
||||||
#define SIO_F81865_ID 0x0704 /* Chipset ID */
|
#define SIO_F81865_ID 0x0704 /* Chipset ID */
|
||||||
#define SIO_F81866_ID 0x1010 /* Chipset ID */
|
#define SIO_F81866_ID 0x1010 /* Chipset ID */
|
||||||
|
#define SIO_F71858AD_ID 0x0903 /* Chipset ID */
|
||||||
#define SIO_F81966_ID 0x1502 /* Chipset ID */
|
#define SIO_F81966_ID 0x1502 /* Chipset ID */
|
||||||
|
|
||||||
#define REGION_LENGTH 8
|
#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;
|
sio_data->type = f71808a;
|
||||||
break;
|
break;
|
||||||
case SIO_F71858_ID:
|
case SIO_F71858_ID:
|
||||||
|
case SIO_F71858AD_ID:
|
||||||
sio_data->type = f71858fg;
|
sio_data->type = f71858fg;
|
||||||
break;
|
break;
|
||||||
case SIO_F71862_ID:
|
case SIO_F71862_ID:
|
||||||
|
|||||||
@@ -269,10 +269,13 @@ gsc_hwmon_get_devtree_pdata(struct device *dev)
|
|||||||
/* fan controller base address */
|
/* fan controller base address */
|
||||||
fan = of_find_compatible_node(dev->parent->of_node, NULL, "gw,gsc-fan");
|
fan = of_find_compatible_node(dev->parent->of_node, NULL, "gw,gsc-fan");
|
||||||
if (fan && of_property_read_u32(fan, "reg", &pdata->fan_base)) {
|
if (fan && of_property_read_u32(fan, "reg", &pdata->fan_base)) {
|
||||||
|
of_node_put(fan);
|
||||||
dev_err(dev, "fan node without base\n");
|
dev_err(dev, "fan node without base\n");
|
||||||
return ERR_PTR(-EINVAL);
|
return ERR_PTR(-EINVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
of_node_put(fan);
|
||||||
|
|
||||||
/* allocate structures for channels and count instances of each type */
|
/* allocate structures for channels and count instances of each type */
|
||||||
device_for_each_child_node(dev, child) {
|
device_for_each_child_node(dev, child) {
|
||||||
if (fwnode_property_read_string(child, "label", &ch->name)) {
|
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.
|
* 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 */
|
/* straight from the datasheet */
|
||||||
#define LM75_TEMP_MIN (-55000)
|
#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>
|
* Reworked by Sven Schuchmann <schuchmann@schleissheimer.de>
|
||||||
* DT support added by Clemens Gruber <clemens.gruber@pqgruber.com>
|
* 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
|
* voltage unit is mV. Through the sysfs interface, lm-sensors tool
|
||||||
* can also display the input voltage.
|
* can also display the input voltage.
|
||||||
*/
|
*/
|
||||||
@@ -45,19 +45,29 @@ enum chips {
|
|||||||
* Client data (each client gets its own)
|
* Client data (each client gets its own)
|
||||||
*/
|
*/
|
||||||
struct mcp3021_data {
|
struct mcp3021_data {
|
||||||
struct device *hwmon_dev;
|
struct i2c_client *client;
|
||||||
u32 vdd; /* supply and reference voltage in millivolt */
|
u32 vdd; /* supply and reference voltage in millivolt */
|
||||||
u16 sar_shift;
|
u16 sar_shift;
|
||||||
u16 sar_mask;
|
u16 sar_mask;
|
||||||
u8 output_res;
|
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);
|
return DIV_ROUND_CLOSEST(data->vdd * val, 1 << data->output_res);
|
||||||
int ret;
|
}
|
||||||
u16 reg;
|
|
||||||
|
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;
|
__be16 buf;
|
||||||
|
u16 reg;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (type != hwmon_in)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
ret = i2c_master_recv(client, (char *)&buf, 2);
|
ret = i2c_master_recv(client, (char *)&buf, 2);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
@@ -74,39 +84,46 @@ static int mcp3021_read16(struct i2c_client *client)
|
|||||||
*/
|
*/
|
||||||
reg = (reg >> data->sar_shift) & data->sar_mask;
|
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,
|
static const struct hwmon_channel_info *mcp3021_info[] = {
|
||||||
struct device_attribute *attr, char *buf)
|
HWMON_CHANNEL_INFO(in, HWMON_I_INPUT),
|
||||||
{
|
NULL
|
||||||
struct i2c_client *client = to_i2c_client(dev);
|
};
|
||||||
struct mcp3021_data *data = i2c_get_clientdata(client);
|
|
||||||
int reg, in_input;
|
|
||||||
|
|
||||||
reg = mcp3021_read16(client);
|
static const struct hwmon_ops mcp3021_hwmon_ops = {
|
||||||
if (reg < 0)
|
.is_visible = mcp3021_is_visible,
|
||||||
return reg;
|
.read = mcp3021_read,
|
||||||
|
};
|
||||||
|
|
||||||
in_input = volts_from_reg(data, reg);
|
static const struct hwmon_chip_info mcp3021_chip_info = {
|
||||||
|
.ops = &mcp3021_hwmon_ops,
|
||||||
return sprintf(buf, "%d\n", in_input);
|
.info = mcp3021_info,
|
||||||
}
|
};
|
||||||
|
|
||||||
static DEVICE_ATTR_RO(in0_input);
|
|
||||||
|
|
||||||
static const struct i2c_device_id mcp3021_id[];
|
static const struct i2c_device_id mcp3021_id[];
|
||||||
|
|
||||||
static int mcp3021_probe(struct i2c_client *client)
|
static int mcp3021_probe(struct i2c_client *client)
|
||||||
{
|
{
|
||||||
int err;
|
|
||||||
struct mcp3021_data *data = NULL;
|
struct mcp3021_data *data = NULL;
|
||||||
struct device_node *np = client->dev.of_node;
|
struct device_node *np = client->dev.of_node;
|
||||||
|
struct device *hwmon_dev;
|
||||||
|
|
||||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
|
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
@@ -147,34 +164,17 @@ static int mcp3021_probe(struct i2c_client *client)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data->client = client;
|
||||||
|
|
||||||
if (data->vdd > MCP3021_VDD_REF_MAX || data->vdd < MCP3021_VDD_REF_MIN)
|
if (data->vdd > MCP3021_VDD_REF_MAX || data->vdd < MCP3021_VDD_REF_MIN)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
err = sysfs_create_file(&client->dev.kobj, &dev_attr_in0_input.attr);
|
hwmon_dev = devm_hwmon_device_register_with_info(&client->dev,
|
||||||
if (err)
|
client->name,
|
||||||
return err;
|
data,
|
||||||
|
&mcp3021_chip_info,
|
||||||
data->hwmon_dev = hwmon_device_register(&client->dev);
|
NULL);
|
||||||
if (IS_ERR(data->hwmon_dev)) {
|
return PTR_ERR_OR_ZERO(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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct i2c_device_id mcp3021_id[] = {
|
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),
|
.of_match_table = of_match_ptr(of_mcp3021_match),
|
||||||
},
|
},
|
||||||
.probe_new = mcp3021_probe,
|
.probe_new = mcp3021_probe,
|
||||||
.remove = mcp3021_remove,
|
|
||||||
.id_table = mcp3021_id,
|
.id_table = mcp3021_id,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1083,6 +1083,7 @@ static const char * const asus_wmi_boards[] = {
|
|||||||
"TUF GAMING B550M-PLUS",
|
"TUF GAMING B550M-PLUS",
|
||||||
"TUF GAMING B550M-PLUS (WI-FI)",
|
"TUF GAMING B550M-PLUS (WI-FI)",
|
||||||
"TUF GAMING B550-PLUS",
|
"TUF GAMING B550-PLUS",
|
||||||
|
"TUF GAMING B550-PLUS WIFI II",
|
||||||
"TUF GAMING B550-PRO",
|
"TUF GAMING B550-PRO",
|
||||||
"TUF GAMING X570-PLUS",
|
"TUF GAMING X570-PLUS",
|
||||||
"TUF GAMING X570-PLUS (WI-FI)",
|
"TUF GAMING X570-PLUS (WI-FI)",
|
||||||
@@ -1200,10 +1201,8 @@ static int __init sensors_nct6775_platform_init(void)
|
|||||||
exit_device_put:
|
exit_device_put:
|
||||||
platform_device_put(pdev[i]);
|
platform_device_put(pdev[i]);
|
||||||
exit_device_unregister:
|
exit_device_unregister:
|
||||||
while (--i >= 0) {
|
while (i--)
|
||||||
if (pdev[i])
|
platform_device_unregister(pdev[i]);
|
||||||
platform_device_unregister(pdev[i]);
|
|
||||||
}
|
|
||||||
exit_unregister:
|
exit_unregister:
|
||||||
platform_driver_unregister(&nct6775_driver);
|
platform_driver_unregister(&nct6775_driver);
|
||||||
return err;
|
return err;
|
||||||
@@ -1213,10 +1212,8 @@ static void __exit sensors_nct6775_platform_exit(void)
|
|||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(pdev); i++) {
|
for (i = 0; i < ARRAY_SIZE(pdev); i++)
|
||||||
if (pdev[i])
|
platform_device_unregister(pdev[i]);
|
||||||
platform_device_unregister(pdev[i]);
|
|
||||||
}
|
|
||||||
platform_driver_unregister(&nct6775_driver);
|
platform_driver_unregister(&nct6775_driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -729,18 +729,14 @@ static ssize_t occ_show_extended(struct device *dev,
|
|||||||
rc = sysfs_emit(buf, "%u",
|
rc = sysfs_emit(buf, "%u",
|
||||||
get_unaligned_be32(&extn->sensor_id));
|
get_unaligned_be32(&extn->sensor_id));
|
||||||
} else {
|
} else {
|
||||||
rc = sysfs_emit(buf, "%02x%02x%02x%02x\n",
|
rc = sysfs_emit(buf, "%4phN\n", extn->name);
|
||||||
extn->name[0], extn->name[1],
|
|
||||||
extn->name[2], extn->name[3]);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
rc = sysfs_emit(buf, "%02x\n", extn->flags);
|
rc = sysfs_emit(buf, "%02x\n", extn->flags);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
rc = sysfs_emit(buf, "%02x%02x%02x%02x%02x%02x\n",
|
rc = sysfs_emit(buf, "%6phN\n", extn->data);
|
||||||
extn->data[0], extn->data[1], extn->data[2],
|
|
||||||
extn->data[3], extn->data[4], extn->data[5]);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return -EINVAL;
|
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);
|
mutex_lock(&ctx->sbe_error_lock);
|
||||||
if (!ctx->sbe_error) {
|
if (!ctx->sbe_error) {
|
||||||
if (resp_len > ctx->ffdc_size) {
|
if (resp_len > ctx->ffdc_size) {
|
||||||
if (ctx->ffdc)
|
kvfree(ctx->ffdc);
|
||||||
kvfree(ctx->ffdc);
|
|
||||||
ctx->ffdc = kvmalloc(resp_len, GFP_KERNEL);
|
ctx->ffdc = kvmalloc(resp_len, GFP_KERNEL);
|
||||||
if (!ctx->ffdc) {
|
if (!ctx->ffdc) {
|
||||||
ctx->ffdc_len = 0;
|
ctx->ffdc_len = 0;
|
||||||
@@ -170,8 +169,7 @@ static int p9_sbe_occ_remove(struct platform_device *pdev)
|
|||||||
ctx->sbe = NULL;
|
ctx->sbe = NULL;
|
||||||
occ_shutdown(occ);
|
occ_shutdown(occ);
|
||||||
|
|
||||||
if (ctx->ffdc)
|
kvfree(ctx->ffdc);
|
||||||
kvfree(ctx->ffdc);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -181,6 +181,15 @@ config SENSORS_LM25066_REGULATOR
|
|||||||
If you say yes here you get regulator support for National
|
If you say yes here you get regulator support for National
|
||||||
Semiconductor LM25066, LM5064, and LM5066.
|
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
|
config SENSORS_LTC2978
|
||||||
tristate "Linear Technologies LTC2978 and compatibles"
|
tristate "Linear Technologies LTC2978 and compatibles"
|
||||||
help
|
help
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ obj-$(CONFIG_SENSORS_IR38064) += ir38064.o
|
|||||||
obj-$(CONFIG_SENSORS_IRPS5401) += irps5401.o
|
obj-$(CONFIG_SENSORS_IRPS5401) += irps5401.o
|
||||||
obj-$(CONFIG_SENSORS_ISL68137) += isl68137.o
|
obj-$(CONFIG_SENSORS_ISL68137) += isl68137.o
|
||||||
obj-$(CONFIG_SENSORS_LM25066) += lm25066.o
|
obj-$(CONFIG_SENSORS_LM25066) += lm25066.o
|
||||||
|
obj-$(CONFIG_SENSORS_LT7182S) += lt7182s.o
|
||||||
obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o
|
obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o
|
||||||
obj-$(CONFIG_SENSORS_LTC3815) += ltc3815.o
|
obj-$(CONFIG_SENSORS_LTC3815) += ltc3815.o
|
||||||
obj-$(CONFIG_SENSORS_MAX15301) += max15301.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);
|
MODULE_DEVICE_TABLE(i2c, ltc2978_id);
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_SENSORS_LTC2978_REGULATOR)
|
#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[] = {
|
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", 0),
|
||||||
PMBUS_REGULATOR("vout", 1),
|
PMBUS_REGULATOR("vout", 1),
|
||||||
PMBUS_REGULATOR("vout", 2),
|
PMBUS_REGULATOR("vout", 2),
|
||||||
@@ -839,10 +856,29 @@ static int ltc2978_probe(struct i2c_client *client)
|
|||||||
|
|
||||||
#if IS_ENABLED(CONFIG_SENSORS_LTC2978_REGULATOR)
|
#if IS_ENABLED(CONFIG_SENSORS_LTC2978_REGULATOR)
|
||||||
info->num_regulators = info->pages;
|
info->num_regulators = info->pages;
|
||||||
info->reg_desc = ltc2978_reg_desc;
|
switch (data->id) {
|
||||||
if (info->num_regulators > ARRAY_SIZE(ltc2978_reg_desc)) {
|
case ltc2972:
|
||||||
dev_err(&client->dev, "num_regulators too large!");
|
case ltc2974:
|
||||||
info->num_regulators = ARRAY_SIZE(ltc2978_reg_desc);
|
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
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -406,7 +406,7 @@ enum pmbus_sensor_classes {
|
|||||||
#define PMBUS_PHASE_VIRTUAL BIT(30) /* Phases on this page are virtual */
|
#define PMBUS_PHASE_VIRTUAL BIT(30) /* Phases on this page are virtual */
|
||||||
#define PMBUS_PAGE_VIRTUAL BIT(31) /* Page is 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 };
|
enum vrm_version { vr11 = 0, vr12, vr13, imvp9, amd625mv };
|
||||||
|
|
||||||
struct pmbus_driver_info {
|
struct pmbus_driver_info {
|
||||||
@@ -463,8 +463,8 @@ struct pmbus_driver_info {
|
|||||||
|
|
||||||
extern const struct regulator_ops pmbus_regulator_ops;
|
extern const struct regulator_ops pmbus_regulator_ops;
|
||||||
|
|
||||||
/* Macro for filling in array of struct regulator_desc */
|
/* Macros for filling in array of struct regulator_desc */
|
||||||
#define PMBUS_REGULATOR(_name, _id) \
|
#define PMBUS_REGULATOR_STEP(_name, _id, _voltages, _step) \
|
||||||
[_id] = { \
|
[_id] = { \
|
||||||
.name = (_name # _id), \
|
.name = (_name # _id), \
|
||||||
.supply_name = "vin", \
|
.supply_name = "vin", \
|
||||||
@@ -474,8 +474,12 @@ extern const struct regulator_ops pmbus_regulator_ops;
|
|||||||
.ops = &pmbus_regulator_ops, \
|
.ops = &pmbus_regulator_ops, \
|
||||||
.type = REGULATOR_VOLTAGE, \
|
.type = REGULATOR_VOLTAGE, \
|
||||||
.owner = THIS_MODULE, \
|
.owner = THIS_MODULE, \
|
||||||
|
.n_voltages = _voltages, \
|
||||||
|
.uV_step = _step, \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define PMBUS_REGULATOR(_name, _id) PMBUS_REGULATOR_STEP(_name, _id, 0, 0)
|
||||||
|
|
||||||
/* Function declarations */
|
/* Function declarations */
|
||||||
|
|
||||||
void pmbus_clear_cache(struct i2c_client *client);
|
void pmbus_clear_cache(struct i2c_client *client);
|
||||||
|
|||||||
@@ -104,6 +104,9 @@ struct pmbus_data {
|
|||||||
|
|
||||||
s16 currpage; /* current page, -1 for unknown/unset */
|
s16 currpage; /* current page, -1 for unknown/unset */
|
||||||
s16 currphase; /* current phase, 0xff for all, -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 {
|
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);
|
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,
|
static struct pmbus_sensor *pmbus_find_sensor(struct pmbus_data *data, int page,
|
||||||
int reg)
|
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);
|
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)
|
const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client *client)
|
||||||
{
|
{
|
||||||
struct pmbus_data *data = i2c_get_clientdata(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);
|
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
|
* Convert linear sensor values to milli- or micro-units
|
||||||
* depending on sensor type.
|
* depending on sensor type.
|
||||||
@@ -741,6 +832,9 @@ static s64 pmbus_reg2data(struct pmbus_data *data, struct pmbus_sensor *sensor)
|
|||||||
case vid:
|
case vid:
|
||||||
val = pmbus_reg2data_vid(data, sensor);
|
val = pmbus_reg2data_vid(data, sensor);
|
||||||
break;
|
break;
|
||||||
|
case ieee754:
|
||||||
|
val = pmbus_reg2data_ieee754(data, sensor);
|
||||||
|
break;
|
||||||
case linear:
|
case linear:
|
||||||
default:
|
default:
|
||||||
val = pmbus_reg2data_linear(data, sensor);
|
val = pmbus_reg2data_linear(data, sensor);
|
||||||
@@ -749,8 +843,72 @@ static s64 pmbus_reg2data(struct pmbus_data *data, struct pmbus_sensor *sensor)
|
|||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define MAX_MANTISSA (1023 * 1000)
|
#define MAX_IEEE_MANTISSA (0x7ff * 1000)
|
||||||
#define MIN_MANTISSA (511 * 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,
|
static u16 pmbus_data2reg_linear(struct pmbus_data *data,
|
||||||
struct pmbus_sensor *sensor, s64 val)
|
struct pmbus_sensor *sensor, s64 val)
|
||||||
@@ -796,12 +954,12 @@ static u16 pmbus_data2reg_linear(struct pmbus_data *data,
|
|||||||
val = val * 1000LL;
|
val = val * 1000LL;
|
||||||
|
|
||||||
/* Reduce large mantissa until it fits into 10 bit */
|
/* Reduce large mantissa until it fits into 10 bit */
|
||||||
while (val >= MAX_MANTISSA && exponent < 15) {
|
while (val >= MAX_LIN_MANTISSA && exponent < 15) {
|
||||||
exponent++;
|
exponent++;
|
||||||
val >>= 1;
|
val >>= 1;
|
||||||
}
|
}
|
||||||
/* Increase small mantissa to improve precision */
|
/* Increase small mantissa to improve precision */
|
||||||
while (val < MIN_MANTISSA && exponent > -15) {
|
while (val < MIN_LIN_MANTISSA && exponent > -15) {
|
||||||
exponent--;
|
exponent--;
|
||||||
val <<= 1;
|
val <<= 1;
|
||||||
}
|
}
|
||||||
@@ -875,6 +1033,9 @@ static u16 pmbus_data2reg(struct pmbus_data *data,
|
|||||||
case vid:
|
case vid:
|
||||||
regval = pmbus_data2reg_vid(data, sensor, val);
|
regval = pmbus_data2reg_vid(data, sensor, val);
|
||||||
break;
|
break;
|
||||||
|
case ieee754:
|
||||||
|
regval = pmbus_data2reg_ieee754(data, sensor, val);
|
||||||
|
break;
|
||||||
case linear:
|
case linear:
|
||||||
default:
|
default:
|
||||||
regval = pmbus_data2reg_linear(data, sensor, val);
|
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)
|
if (data->info->format[PSC_VOLTAGE_OUT] != direct)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
break;
|
break;
|
||||||
|
case 3: /* ieee 754 half precision */
|
||||||
|
if (data->info->format[PSC_VOLTAGE_OUT] != ieee754)
|
||||||
|
return -ENODEV;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return -ENODEV;
|
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);
|
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,
|
static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data,
|
||||||
struct pmbus_driver_info *info)
|
struct pmbus_driver_info *info)
|
||||||
{
|
{
|
||||||
@@ -2474,6 +2675,20 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data,
|
|||||||
return ret;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2636,6 +2851,58 @@ static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned
|
|||||||
return 0;
|
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)
|
static int pmbus_regulator_get_voltage(struct regulator_dev *rdev)
|
||||||
{
|
{
|
||||||
struct device *dev = rdev_get_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;
|
*selector = 0;
|
||||||
|
|
||||||
if (pmbus_check_word_register(client, s.page, PMBUS_MFR_VOUT_MIN))
|
low = pmbus_regulator_get_low_margin(client, s.page);
|
||||||
s.data = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_MFR_VOUT_MIN);
|
if (low < 0)
|
||||||
if (s.data < 0) {
|
return low;
|
||||||
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);
|
|
||||||
|
|
||||||
s.data = -1;
|
high = pmbus_regulator_get_high_margin(client, s.page);
|
||||||
if (pmbus_check_word_register(client, s.page, PMBUS_MFR_VOUT_MAX))
|
if (high < 0)
|
||||||
s.data = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_MFR_VOUT_MAX);
|
return high;
|
||||||
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);
|
|
||||||
|
|
||||||
/* Make sure we are within margins */
|
/* Make sure we are within margins */
|
||||||
if (low > val)
|
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);
|
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 = {
|
const struct regulator_ops pmbus_regulator_ops = {
|
||||||
.enable = pmbus_regulator_enable,
|
.enable = pmbus_regulator_enable,
|
||||||
.disable = pmbus_regulator_disable,
|
.disable = pmbus_regulator_disable,
|
||||||
@@ -2708,6 +2993,7 @@ const struct regulator_ops pmbus_regulator_ops = {
|
|||||||
.get_error_flags = pmbus_regulator_get_error_flags,
|
.get_error_flags = pmbus_regulator_get_error_flags,
|
||||||
.get_voltage = pmbus_regulator_get_voltage,
|
.get_voltage = pmbus_regulator_get_voltage,
|
||||||
.set_voltage = pmbus_regulator_set_voltage,
|
.set_voltage = pmbus_regulator_set_voltage,
|
||||||
|
.list_voltage = pmbus_regulator_list_voltage,
|
||||||
};
|
};
|
||||||
EXPORT_SYMBOL_NS_GPL(pmbus_regulator_ops, PMBUS);
|
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,
|
DEFINE_DEBUGFS_ATTRIBUTE(pmbus_debugfs_ops_status, pmbus_debugfs_get_status,
|
||||||
NULL, "0x%04llx\n");
|
NULL, "0x%04llx\n");
|
||||||
|
|
||||||
static int pmbus_debugfs_get_pec(void *data, u64 *val)
|
static ssize_t pmbus_debugfs_mfr_read(struct file *file, char __user *buf,
|
||||||
{
|
size_t count, loff_t *ppos)
|
||||||
struct i2c_client *client = data;
|
|
||||||
|
|
||||||
*val = !!(client->flags & I2C_CLIENT_PEC);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pmbus_debugfs_set_pec(void *data, u64 val)
|
|
||||||
{
|
{
|
||||||
int rc;
|
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) {
|
rc = pmbus_read_block_data(entry->client, entry->page, entry->reg,
|
||||||
client->flags &= ~I2C_CLIENT_PEC;
|
data);
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (val != 1)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
rc = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY);
|
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
if (!(rc & PB_CAPABILITY_ERROR_CHECK))
|
/* Add newline at the end of a read data */
|
||||||
return -EOPNOTSUPP;
|
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)
|
static void pmbus_remove_debugfs(void *data)
|
||||||
{
|
{
|
||||||
@@ -2846,16 +3124,80 @@ static int pmbus_init_debugfs(struct i2c_client *client,
|
|||||||
return -ENODEV;
|
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,
|
entries = devm_kcalloc(data->dev,
|
||||||
data->info->pages * 10, sizeof(*entries),
|
6 + data->info->pages * 10, sizeof(*entries),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (!entries)
|
if (!entries)
|
||||||
return -ENOMEM;
|
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) {
|
for (i = 0; i < data->info->pages; ++i) {
|
||||||
/* Check accessibility of status register if it's not page 0 */
|
/* Check accessibility of status register if it's not page 0 */
|
||||||
if (!i || pmbus_check_status_register(client, i)) {
|
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);
|
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 */
|
/* For autoloading only */
|
||||||
static const struct dmi_system_id sch56xx_dmi_table[] __initconst = {
|
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))
|
if (!dmi_check_system(sch56xx_dmi_table))
|
||||||
return -ENODEV;
|
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
|
* Some machines like the Esprimo P720 and Esprimo C700 have
|
||||||
* "Antiope"/"Theseus", so we need to check for both.
|
* 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) &&
|
if (!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, " Antiope", NULL) &&
|
||||||
!dmi_find_device(DMI_DEV_TYPE_OTHER, " Theseus", NULL))
|
!dmi_find_device(DMI_DEV_TYPE_OTHER, "Theseus", NULL) &&
|
||||||
return -ENODEV;
|
!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)
|
static int sht15_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct sht15_data *data = platform_get_drvdata(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);
|
hwmon_device_unregister(data->hwmon_dev);
|
||||||
sysfs_remove_group(&pdev->dev.kobj, &sht15_attr_group);
|
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)) {
|
if (!IS_ERR(data->reg)) {
|
||||||
regulator_unregister_notifier(data->reg, &data->nb);
|
regulator_unregister_notifier(data->reg, &data->nb);
|
||||||
regulator_disable(data->reg);
|
regulator_disable(data->reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_unlock(&data->read_lock);
|
|
||||||
|
|
||||||
return 0;
|
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,
|
static int tps23861_read_voltage(struct tps23861_data *data, int channel,
|
||||||
long *val)
|
long *val)
|
||||||
{
|
{
|
||||||
unsigned int regval;
|
__le16 regval;
|
||||||
|
long raw_val;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (channel < TPS23861_NUM_PORTS) {
|
if (channel < TPS23861_NUM_PORTS) {
|
||||||
@@ -155,7 +156,8 @@ static int tps23861_read_voltage(struct tps23861_data *data, int channel,
|
|||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
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;
|
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,
|
static int tps23861_read_current(struct tps23861_data *data, int channel,
|
||||||
long *val)
|
long *val)
|
||||||
{
|
{
|
||||||
unsigned int current_lsb;
|
long raw_val, current_lsb;
|
||||||
unsigned int regval;
|
__le16 regval;
|
||||||
|
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (data->shunt_resistor == SHUNT_RESISTOR_DEFAULT)
|
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)
|
if (err < 0)
|
||||||
return err;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user