hwmon updates for v5.20

- Substantial rewrite of lm90 driver to support several
   additional chips and improve support for existing chips.
 
 - Add support of ROG ZENITH II EXTREME, Maximus XI Hero,
   Strix Z690-a D4 to asus-ec-sensors driver
 
 - Add support of F71858AD to f71882fg driver
 
 - Add support of Aquacomputer Quadro to aquacomputer_d5next
   driver
 
 - Improved assembler code and add support for Dell G5 5590
   as well as XPS 13 7390 in dell-smm driver
 
 - Add support for ASUS TUF GAMING B550-PLUS WIFI II to nct775 driver
 
 - Add support for IEEE 754 half precision to PMBus core.
   Also support for Analog Devices LT7182S, improve
   regulator support, and report various MFR register
   values in debugfs.
 
 - Various other minor improvements and fixes
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEiHPvMQj9QTOCiqgVyx8mb86fmYEFAmLkUzwACgkQyx8mb86f
 mYFxXg/9HuCcW14zARhroIplYrqqKlk6/oeTFc5g3t4Mm5q7lcvpHx3PJM0GOUH1
 xRSqIZRhzf42L83rTtrwcKyoL9e0YPTJ7F9Ja+lYcAR3RqRTspaz3f2rHkO+/7BK
 2Lf0KTaPFzGFEBYZPDr/ekY6ihRtJeVuZ0qOfqCLaF7+z/J/IOsJHX3WaE+UASHy
 JnN2Uvq8Q5Op/6kejqfAwpO3Us4og1eh0vzGH2hfGo47bq8C8OtNyzbFHl2cgDdX
 eYj1qPzBGLMt/zvbhdyrPwAHgttTQ4EINhNj815WDueFCCxkQsJqIkgZ8N01N3Gc
 maezQiN65A4wCNov0mlFYWHatjyNo+uV3Bngf6rWDQwsvueTS6j5EKHcMmNoXr/3
 HlmenpKHggF87WXVSVA9NYB455XG2LS8F0BpVdHzUl/z0PMcVI3ew0IMoIe08f7m
 SQ7OZ/jX2/2QoBxZy/A6juKSTOaIorp4jJQiWrejeYr8SmTccRC/5mzNDga1v+xZ
 DMwtzPIUC73xyo/i696qV5Lu+DdiQrA0SAzPPxaXZoDd4AkeylxpJxte4vqCp9lF
 x+4Bc9LXyv/jkIC5t/rTJmICkMkd9Z8Z60laakZl4gKvoWeMdah/3y0vq2VicCg/
 0ol4hhGRjgAH3KsWrSFKtNgF7ccvQ9nF0XolwLaHWYW1C8fetlY=
 =M9iY
 -----END PGP SIGNATURE-----

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

Pull hwmon updates from Guenter Roeck:

 - Substantial rewrite of lm90 driver to support several additional
   chips and improve support for existing chips.

 - Add support of ROG ZENITH II EXTREME, Maximus XI Hero, and
   Strix Z690-a D4 to asus-ec-sensors driver

 - Add support of F71858AD to f71882fg driver

 - Add support of Aquacomputer Quadro to aquacomputer_d5next driver

 - Improved assembler code and add support for Dell G5 5590 as well as
   XPS 13 7390 in dell-smm driver

 - Add support for ASUS TUF GAMING B550-PLUS WIFI II to nct775 driver

 - Add support for IEEE 754 half precision to PMBus core. Also support
   for Analog Devices LT7182S, improve regulator support, and report
   various MFR register values in debugfs.

 - Various other minor improvements and fixes

* tag 'hwmon-for-v5.20' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (85 commits)
  hwmon: (aquacomputer_d5next) Add support for Aquacomputer Quadro fan controller
  hwmon: (dell-smm) Improve documentation
  hwmon: (nct6775) add ASUS TUF GAMING B550-PLUS WIFI II
  hwmon: (occ) Replace open-coded variant of %*phN specifier
  hwmon: (sht15) Fix wrong assumptions in device remove callback
  hwmon: (aquacomputer_d5next) Add support for reading the +12V voltage sensor on D5 Next
  hwmon: (tps23861) fix byte order in current and voltage registers
  hwmon: (aspeed-pwm-tacho) increase fan tach period (again)
  hwmon: (aquacomputer_d5next) Add D5 Next fan control support
  hwmon: (mcp3021) improve driver support for newer hwmon interface
  hwmon: (asus-ec-sensors) add definitions for ROG ZENITH II EXTREME
  hwmon: (aquacomputer_d5next) Move device-specific data into struct aqc_data
  hwmon: (asus-ec-sensors) add missing sensors for X570-I GAMING
  hwmon: (drivetemp) Add module alias
  hwmon: (asus_wmi_sensors) Save a few bytes of memory
  hwmon: (lm90) Use worker for alarm notifications
  hwmon: (asus-ec-sensors) add support for Maximus XI Hero
  hwmon: (dell-smm) Improve assembly code
  hwmon: (pmbus/ltc2978) Set voltage resolution
  hwmon: (pmbus) Add list_voltage to pmbus ops
  ...
This commit is contained in:
Linus Torvalds 2022-08-02 11:07:04 -07:00
commit 64ae88ff48
34 changed files with 3306 additions and 1218 deletions

View File

@ -938,3 +938,12 @@ Description:
- 1: enable
RW
What: /sys/class/hwmon/hwmonX/device/pec
Description:
PEC support on I2C devices
- 0, off, n: disable
- 1, on, y: enable
RW

View File

@ -16,6 +16,7 @@ properties:
- adi,adm1032
- adi,adt7461
- adi,adt7461a
- adi,adt7481
- dallas,max6646
- dallas,max6647
- dallas,max6649
@ -50,6 +51,12 @@ properties:
"#thermal-sensor-cells":
const: 1
'#address-cells':
const: 1
'#size-cells':
const: 0
vcc-supply:
description: phandle to the regulator that provides the +VCC supply
@ -61,6 +68,29 @@ required:
- compatible
- reg
patternProperties:
"^channel@([0-2])$":
type: object
description: Represents channels of the device and their specific configuration.
properties:
reg:
description: The channel number. 0 is local channel, 1-2 are remote channels.
items:
minimum: 0
maximum: 2
label:
description: A descriptive name for this channel, like "ambient" or "psu".
temperature-offset-millicelsius:
description: Temperature offset to be added to or subtracted from remote temperature measurements.
required:
- reg
additionalProperties: false
allOf:
- if:
not:
@ -70,12 +100,84 @@ allOf:
enum:
- adi,adt7461
- adi,adt7461a
- adi,adt7481
- ti,tmp451
- ti,tmp461
then:
properties:
ti,extended-range-enable: false
- if:
properties:
compatible:
contains:
enum:
- dallas,max6646
- dallas,max6647
- dallas,max6649
- dallas,max6657
- dallas,max6658
- dallas,max6659
- dallas,max6695
- dallas,max6696
then:
patternProperties:
"^channel@([0-2])$":
properties:
temperature-offset-millicelsius: false
- if:
properties:
compatible:
contains:
enum:
- adi,adt7461
- adi,adt7461a
- adi,adt7481
- onnn,nct1008
then:
patternProperties:
"^channel@([0-2])$":
properties:
temperature-offset-millicelsius:
maximum: 127750
- if:
properties:
compatible:
contains:
enum:
- adi,adm1032
- dallas,max6680
- dallas,max6681
- gmt,g781
- national,lm86
- national,lm89
- national,lm90
- national,lm99
- nxp,sa56004
- winbond,w83l771
then:
patternProperties:
"^channel@([0-2])$":
properties:
temperature-offset-millicelsius:
maximum: 127875
- if:
properties:
compatible:
contains:
enum:
- ti,tmp451
- ti,tmp461
then:
patternProperties:
"^channel@([0-2])$":
properties:
temperature-offset-millicelsius:
maximum: 127937
additionalProperties: false
examples:
@ -94,3 +196,32 @@ examples:
#thermal-sensor-cells = <1>;
};
};
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
sensor@4c {
compatible = "adi,adt7481";
reg = <0x4c>;
#address-cells = <1>;
#size-cells = <0>;
channel@0 {
reg = <0x0>;
label = "local";
};
channel@1 {
reg = <0x1>;
label = "front";
temperature-offset-millicelsius = <4000>;
};
channel@2 {
reg = <0x2>;
label = "back";
temperature-offset-millicelsius = <750>;
};
};
};

View File

@ -41,6 +41,8 @@ properties:
- adi,adp5585-02
# Analog Devices ADP5589 Keypad Decoder and I/O Expansion
- adi,adp5589
# Analog Devices LT7182S Dual Channel 6A, 20V PolyPhase Step-Down Silent Switcher
- adi,lt7182s
# AMS iAQ-Core VOC Sensor
- ams,iaq-core
# i2c serial eeprom (24cxx)

View File

@ -9,6 +9,7 @@ Supported devices:
* Aquacomputer Farbwerk RGB controller
* Aquacomputer Farbwerk 360 RGB controller
* Aquacomputer Octo fan controller
* Aquacomputer Quadro fan controller
Author: Aleksa Savic
@ -33,6 +34,9 @@ better suited for userspace tools.
The Octo exposes four temperature sensors and eight PWM controllable fans, along
with their speed (in RPM), power, voltage and current.
The Quadro exposes four temperature sensors, a flow sensor and four PWM controllable
fans, along with their speed (in RPM), power, voltage and current.
The Farbwerk and Farbwerk 360 expose four temperature sensors. Depending on the device,
not all sysfs and debugfs entries will be available.
@ -45,13 +49,14 @@ the kernel and supports hotswapping.
Sysfs entries
-------------
================ =============================================
================ ==============================================
temp[1-4]_input Temperature sensors (in millidegrees Celsius)
fan[1-2]_input Pump/fan speed (in RPM)
power[1-2]_input Pump/fan power (in micro Watts)
in[0-2]_input Pump/fan voltage (in milli Volts)
curr[1-2]_input Pump/fan current (in milli Amperes)
================ =============================================
fan[1-8]_input Pump/fan speed (in RPM) / Flow speed (in dL/h)
power[1-8]_input Pump/fan power (in micro Watts)
in[0-7]_input Pump/fan voltage (in milli Volts)
curr[1-8]_input Pump/fan current (in milli Amperes)
pwm[1-8] Fan PWM (0 - 255)
================ ==============================================
Debugfs entries
---------------

View File

@ -13,12 +13,16 @@ Supported boards:
* ROG CROSSHAIR VIII FORMULA
* ROG CROSSHAIR VIII HERO
* ROG CROSSHAIR VIII IMPACT
* ROG MAXIMUS XI HERO
* ROG MAXIMUS XI HERO (WI-FI)
* ROG STRIX B550-E GAMING
* ROG STRIX B550-I GAMING
* ROG STRIX X570-E GAMING
* ROG STRIX X570-E GAMING WIFI II
* ROG STRIX X570-F GAMING
* ROG STRIX X570-I GAMING
* ROG STRIX Z690-A GAMING WIFI D4
* ROG ZENITH II EXTREME
Authors:
- Eugene Shalygin <eugene.shalygin@gmail.com>

View File

@ -46,6 +46,9 @@ temp[1-10]_input RO Temperature reading in milli-degrees
temp[1-10]_label RO Temperature sensor label.
=============================== ======= =======================================
Due to the nature of the SMM interface, each pwmX attribute controls
fan number X.
Disabling automatic BIOS fan control
------------------------------------

View File

@ -109,6 +109,7 @@ Hardware Monitoring Kernel Drivers
lm95234
lm95245
lochnagar
lt7182s
ltc2992
ltc2945
ltc2947

View File

@ -3,6 +3,14 @@ Kernel driver lm90
Supported chips:
* National Semiconductor LM84
Prefix: 'lm84'
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
Datasheet: Publicly available at the National Semiconductor website
* National Semiconductor LM90
Prefix: 'lm90'
@ -43,6 +51,30 @@ Supported chips:
http://www.national.com/mpf/LM/LM86.html
* Analog Devices ADM1020
Prefix: 'adm1020'
Addresses scanned: I2C 0x4c - 0x4e
Datasheet: Publicly available at the Analog Devices website
* Analog Devices ADM1021
Prefix: 'adm1021'
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
Datasheet: Publicly available at the Analog Devices website
* Analog Devices ADM1021A/ADM1023
Prefix: 'adm1023'
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
Datasheet: Publicly available at the Analog Devices website
* Analog Devices ADM1032
Prefix: 'adm1032'
@ -73,6 +105,36 @@ Supported chips:
https://www.onsemi.com/PowerSolutions/product.do?id=ADT7461A
* Analog Devices ADT7481
Prefix: 'adt7481'
Addresses scanned: I2C 0x4b and 0x4c
Datasheet: Publicly available at the ON Semiconductor website
https://www.onsemi.com/PowerSolutions/product.do?id=ADT7481
* Analog Devices ADT7482
Prefix: 'adt7482'
Addresses scanned: I2C 0x4c
Datasheet: Publicly available at the ON Semiconductor website
https://www.onsemi.com/PowerSolutions/product.do?id=ADT7482
* Analog Devices ADT7483A
Prefix: 'adt7483a'
Addresses scanned: I2C 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e
Datasheet: Publicly available at the ON Semiconductor website
https://www.onsemi.com/PowerSolutions/product.do?id=ADT7483A
* ON Semiconductor NCT1008
Prefix: 'nct1008'
@ -83,6 +145,72 @@ Supported chips:
https://www.onsemi.com/PowerSolutions/product.do?id=NCT1008
* ON Semiconductor NCT210
Prefix: 'adm1021'
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
Datasheet: Publicly available at the ON Semiconductor website
https://www.onsemi.com/PowerSolutions/product.do?id=NCT210
* ON Semiconductor NCT214
Prefix: 'nct214'
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
Datasheet: Publicly available at the ON Semiconductor website
https://www.onsemi.com/PowerSolutions/product.do?id=NCT214
* ON Semiconductor NCT218
Prefix: 'nct218'
Addresses scanned: I2C 0x4c - 0x4d
Datasheet: Publicly available at the ON Semiconductor website
https://www.onsemi.com/PowerSolutions/product.do?id=NCT218
* ON Semiconductor NCT72
Prefix: 'nct72'
Addresses scanned: I2C 0x4c - 0x4d
Datasheet: Publicly available at the ON Semiconductor website
https://www.onsemi.com/PowerSolutions/product.do?id=NCT72
* Maxim MAX1617
Prefix: 'max1617'
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
Datasheet: Publicly available at the Maxim website
* Maxim MAX1617A
Prefix: 'max1617a'
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
Datasheet: Publicly available at the Maxim website
* Maxim MAX6642
Prefix: 'max6642'
Addresses scanned: I2C 0x48-0x4f
Datasheet: Publicly available at the Maxim website
http://datasheets.maxim-ic.com/en/ds/MAX6642.pdf
* Maxim MAX6646
Prefix: 'max6646'
@ -105,7 +233,7 @@ Supported chips:
* Maxim MAX6648
Prefix: 'max6646'
Prefix: 'max6648'
Addresses scanned: I2C 0x4c
@ -191,7 +319,7 @@ Supported chips:
* Maxim MAX6692
Prefix: 'max6646'
Prefix: 'max6648'
Addresses scanned: I2C 0x4c
@ -275,6 +403,46 @@ Supported chips:
https://www.ti.com/lit/gpn/tmp461
* Philips NE1617, NE1617A
Prefix: 'max1617' (probably detected as a max1617)
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
Datasheets: Publicly available at the Philips website
* Philips NE1618
Prefix: 'ne1618'
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
Datasheets: Publicly available at the Philips website
* Genesys Logic GL523SM
Prefix: 'gl523sm'
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
Datasheet:
* TI THMC10
Prefix: 'thmc10'
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
Datasheet: Publicly available at the TI website
* Onsemi MC1066
Prefix: 'mc1066'
Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e
Datasheet: Publicly available at the Onsemi website
Author: Jean Delvare <jdelvare@suse.de>
@ -285,6 +453,12 @@ The LM90 is a digital temperature sensor. It senses its own temperature as
well as the temperature of up to one external diode. It is compatible
with many other devices, many of which are supported by this driver.
The family of chips supported by this driver is derived from MAX1617.
This chip as well as various compatible chips support a local and a remote
temperature sensor with 8 bit accuracy. Later chips provide improved accuracy
and other additional features such as hysteresis and temperature offset
registers.
Note that there is no easy way to differentiate between the MAX6657,
MAX6658 and MAX6659 variants. The extra features of the MAX6659 are only
supported by this driver if the chip is located at address 0x4d or 0x4e,
@ -292,15 +466,31 @@ or if the chip type is explicitly selected as max6659.
The MAX6680 and MAX6681 only differ in their pinout, therefore they obviously
can't (and don't need to) be distinguished.
The specificity of this family of chipsets over the ADM1021/LM84
family is that it features critical limits with hysteresis, and an
increased resolution of the remote temperature measurement.
The different chipsets of the family are not strictly identical, although
very similar. For reference, here comes a non-exhaustive list of specific
features:
LM84:
* 8 bit sensor resolution
ADM1020, ADM1021, GL523SM, MAX1617, NE1617, NE1617A, THMC10:
* 8 bit sensor resolution
* Low temperature limits
NCT210, NE1618:
* 11 bit sensor resolution for remote temperature sensor
* Low temperature limits
ADM1021A, ADM1023:
* Temperature offset register for remote temperature sensor
* 11 bit resolution for remote temperature sensor
* Low temperature limits
LM90:
* 11 bit resolution for remote temperature sensor
* Temperature offset register for remote temperature sensor
* Low and critical temperature limits
* Configurable conversion rate
* Filter and alert configuration register at 0xBF.
* ALERT is triggered by temperatures over critical limits.
@ -322,8 +512,31 @@ ADM1032:
ADT7461, ADT7461A, NCT1008:
* Extended temperature range (breaks compatibility)
* Lower resolution for remote temperature
* SMBus PEC support for Write Byte and Receive Byte transactions.
* 10 bit temperature resolution
MAX6654:
ADT7481, ADT7482, ADT7483:
* Temperature offset register
* SMBus PEC support
* 10 bit temperature resolution for external sensors
* Two remote sensors
* Selectable address (ADT7483)
MAX6642:
* No critical limit register
* Conversion rate not configurable
* Better local resolution (10 bit)
* 10 bit external sensor resolution
MAX6646, MAX6647, MAX6649:
* Better local resolution
* Extended range unsigned external temperature
MAX6648, MAX6692:
* Better local resolution
* Unsigned temperature
MAX6654, MAX6690:
* Better local resolution
* Selectable address
* Remote sensor type selection
@ -423,6 +636,6 @@ two transactions will typically mean twice as much delay waiting for
transaction completion, effectively doubling the register cache refresh time.
I guess reliability comes at a price, but it's quite expensive this time.
So, as not everyone might enjoy the slowdown, PEC can be disabled through
sysfs. Just write 0 to the "pec" file and PEC will be disabled. Write 1
to that file to enable PEC again.
So, as not everyone might enjoy the slowdown, PEC is disabled by default and
can be enabled through sysfs. Just write 1 to the "pec" file and PEC will be
enabled. Write 0 to that file to disable PEC again.

View 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
======================= ====================================

View File

@ -121,6 +121,15 @@ Specifically, it provides the following information.
non-standard PMBus commands to standard commands, or to augment standard
command return values with device specific information.
PEC Support
===========
Many PMBus devices support SMBus PEC (Packet Error Checking). If supported
by both the I2C adapter and by the PMBus chip, it is by default enabled.
If PEC is supported, the PMBus core driver adds an attribute named 'pec' to
the I2C device. This attribute can be used to control PEC support in the
communication with the PMBus chip.
API functions
=============

View File

@ -100,6 +100,7 @@ config SENSORS_AD7418
config SENSORS_ADM1021
tristate "Analog Devices ADM1021 and compatibles"
depends on I2C
depends on SENSORS_LM90=n
help
If you say yes here you get support for Analog Devices ADM1021
and ADM1023 sensor chips and clones: Maxim MAX1617 and MAX1617A,
@ -256,13 +257,13 @@ config SENSORS_AHT10
will be called aht10.
config SENSORS_AQUACOMPUTER_D5NEXT
tristate "Aquacomputer D5 Next, Octo, Farbwerk, and Farbwerk 360"
tristate "Aquacomputer D5 Next, Octo, Quadro, Farbwerk, and Farbwerk 360"
depends on USB_HID
select CRC16
help
If you say yes here you get support for sensors and fans of
the Aquacomputer D5 Next watercooling pump, Octo fan
controller, Farbwerk and Farbwerk 360 RGB controllers, where
the Aquacomputer D5 Next watercooling pump, Octo and Quadro fan
controllers, Farbwerk and Farbwerk 360 RGB controllers, where
available.
This driver can also be built as a module. If so, the module
@ -381,7 +382,7 @@ config SENSORS_ARM_SCPI
config SENSORS_ASB100
tristate "Asus ASB100 Bach"
depends on X86 && I2C
depends on (X86 || COMPILE_TEST) && I2C
select HWMON_VID
help
If you say yes here you get support for the ASB100 Bach sensor
@ -626,7 +627,7 @@ config SENSORS_MC13783_ADC
config SENSORS_FSCHMD
tristate "Fujitsu Siemens Computers sensor chips"
depends on X86 && I2C
depends on (X86 || COMPILE_TEST) && I2C
help
If you say yes here you get support for the following Fujitsu
Siemens Computers (FSC) sensor chips: Poseidon, Scylla, Hermes,
@ -1102,6 +1103,7 @@ config SENSORS_MAX6639
config SENSORS_MAX6642
tristate "Maxim MAX6642 sensor chip"
depends on I2C
depends on SENSORS_LM90=n
help
If you say yes here you get support for MAX6642 sensor chip.
MAX6642 is a SMBus-Compatible Remote/Local Temperature Sensor
@ -1357,12 +1359,15 @@ config SENSORS_LM90
tristate "National Semiconductor LM90 and compatibles"
depends on I2C
help
If you say yes here you get support for National Semiconductor LM90,
LM86, LM89 and LM99, Analog Devices ADM1032, ADT7461, and ADT7461A,
Maxim MAX6646, MAX6647, MAX6648, MAX6649, MAX6654, MAX6657, MAX6658,
MAX6659, MAX6680, MAX6681, MAX6692, MAX6695, MAX6696,
ON Semiconductor NCT1008, Winbond/Nuvoton W83L771W/G/AWG/ASG,
Philips SA56004, GMT G781, Texas Instruments TMP451 and TMP461
If you say yes here you get support for National Semiconductor LM84,
LM90, LM86, LM89 and LM99, Analog Devices ADM1020, ADM2021, ADM1021A,
ADM1023, ADM1032, ADT7461, ADT7461A, ADT7481, ADT7482, and ADT7483A,
Maxim MAX1617, MAX6642, MAX6646, MAX6647, MAX6648, MAX6649, MAX6654,
MAX6657, MAX6658, MAX6659, MAX6680, MAX6681, MAX6692, MAX6695,
MAX6696,
ON Semiconductor NCT1008, NCT210, NCT72, NCT214, NCT218,
Winbond/Nuvoton W83L771W/G/AWG/ASG,
Philips NE1618, SA56004, GMT G781, Texas Instruments TMP451 and TMP461
sensor chips.
This driver can also be built as a module. If so, the module

View File

@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* hwmon driver for Aquacomputer devices (D5 Next, Farbwerk, Farbwerk 360, Octo)
* hwmon driver for Aquacomputer devices (D5 Next, Farbwerk, Farbwerk 360, Octo,
* Quadro)
*
* Aquacomputer devices send HID reports (with ID 0x01) every second to report
* sensor values.
@ -21,17 +22,19 @@
#define USB_VENDOR_ID_AQUACOMPUTER 0x0c70
#define USB_PRODUCT_ID_FARBWERK 0xf00a
#define USB_PRODUCT_ID_QUADRO 0xf00d
#define USB_PRODUCT_ID_D5NEXT 0xf00e
#define USB_PRODUCT_ID_FARBWERK360 0xf010
#define USB_PRODUCT_ID_OCTO 0xf011
enum kinds { d5next, farbwerk, farbwerk360, octo };
enum kinds { d5next, farbwerk, farbwerk360, octo, quadro };
static const char *const aqc_device_names[] = {
[d5next] = "d5next",
[farbwerk] = "farbwerk",
[farbwerk360] = "farbwerk360",
[octo] = "octo"
[octo] = "octo",
[quadro] = "quadro"
};
#define DRIVER_NAME "aquacomputer_d5next"
@ -54,60 +57,61 @@ static u8 secondary_ctrl_report[] = {
0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x34, 0xC6
};
/* Register offsets for all Aquacomputer devices */
#define AQC_TEMP_SENSOR_SIZE 0x02
#define AQC_TEMP_SENSOR_DISCONNECTED 0x7FFF
#define AQC_FAN_PERCENT_OFFSET 0x00
#define AQC_FAN_VOLTAGE_OFFSET 0x02
#define AQC_FAN_CURRENT_OFFSET 0x04
#define AQC_FAN_POWER_OFFSET 0x06
#define AQC_FAN_SPEED_OFFSET 0x08
/* Register offsets for the D5 Next pump */
#define D5NEXT_POWER_CYCLES 24
#define D5NEXT_POWER_CYCLES 0x18
#define D5NEXT_COOLANT_TEMP 0x57
#define D5NEXT_NUM_FANS 2
#define D5NEXT_NUM_SENSORS 1
#define D5NEXT_PUMP_OFFSET 0x6c
#define D5NEXT_FAN_OFFSET 0x5f
#define D5NEXT_5V_VOLTAGE 0x39
#define D5NEXT_12V_VOLTAGE 0x37
#define D5NEXT_CTRL_REPORT_SIZE 0x329
static u8 d5next_sensor_fan_offsets[] = { D5NEXT_PUMP_OFFSET, D5NEXT_FAN_OFFSET };
#define D5NEXT_COOLANT_TEMP 87
#define D5NEXT_PUMP_SPEED 116
#define D5NEXT_FAN_SPEED 103
#define D5NEXT_PUMP_POWER 114
#define D5NEXT_FAN_POWER 101
#define D5NEXT_PUMP_VOLTAGE 110
#define D5NEXT_FAN_VOLTAGE 97
#define D5NEXT_5V_VOLTAGE 57
#define D5NEXT_PUMP_CURRENT 112
#define D5NEXT_FAN_CURRENT 99
/* Pump and fan speed registers in D5 Next control report (from 0-100%) */
static u16 d5next_ctrl_fan_offsets[] = { 0x97, 0x42 };
/* Register offsets for the Farbwerk RGB controller */
#define FARBWERK_NUM_SENSORS 4
#define FARBWERK_SENSOR_START 0x2f
#define FARBWERK_SENSOR_SIZE 0x02
#define FARBWERK_SENSOR_DISCONNECTED 0x7FFF
/* Register offsets for the Farbwerk 360 RGB controller */
#define FARBWERK360_NUM_SENSORS 4
#define FARBWERK360_SENSOR_START 0x32
#define FARBWERK360_SENSOR_SIZE 0x02
#define FARBWERK360_SENSOR_DISCONNECTED 0x7FFF
/* Register offsets for the Octo fan controller */
#define OCTO_POWER_CYCLES 0x18
#define OCTO_NUM_FANS 8
#define OCTO_FAN_PERCENT_OFFSET 0x00
#define OCTO_FAN_VOLTAGE_OFFSET 0x02
#define OCTO_FAN_CURRENT_OFFSET 0x04
#define OCTO_FAN_POWER_OFFSET 0x06
#define OCTO_FAN_SPEED_OFFSET 0x08
static u8 octo_sensor_fan_offsets[] = { 0x7D, 0x8A, 0x97, 0xA4, 0xB1, 0xBE, 0xCB, 0xD8 };
#define OCTO_NUM_SENSORS 4
#define OCTO_SENSOR_START 0x3D
#define OCTO_SENSOR_SIZE 0x02
#define OCTO_SENSOR_DISCONNECTED 0x7FFF
#define OCTO_CTRL_REPORT_SIZE 0x65F
#define OCTO_CTRL_REPORT_CHECKSUM_OFFSET 0x65D
#define OCTO_CTRL_REPORT_CHECKSUM_START 0x01
#define OCTO_CTRL_REPORT_CHECKSUM_LENGTH 0x65C
#define OCTO_CTRL_REPORT_SIZE 0x65F
static u8 octo_sensor_fan_offsets[] = { 0x7D, 0x8A, 0x97, 0xA4, 0xB1, 0xBE, 0xCB, 0xD8 };
/* Fan speed registers in Octo control report (from 0-100%) */
static u16 octo_ctrl_fan_offsets[] = { 0x5B, 0xB0, 0x105, 0x15A, 0x1AF, 0x204, 0x259, 0x2AE };
/* Register offsets for the Quadro fan controller */
#define QUADRO_POWER_CYCLES 0x18
#define QUADRO_NUM_FANS 4
#define QUADRO_NUM_SENSORS 4
#define QUADRO_SENSOR_START 0x34
#define QUADRO_CTRL_REPORT_SIZE 0x3c1
#define QUADRO_FLOW_SENSOR_OFFSET 0x6e
static u8 quadro_sensor_fan_offsets[] = { 0x70, 0x7D, 0x8A, 0x97 };
/* Fan speed registers in Quadro control report (from 0-100%) */
static u16 quadro_ctrl_fan_offsets[] = { 0x36, 0x8b, 0xe0, 0x135 };
/* Labels for D5 Next */
static const char *const label_d5next_temp[] = {
"Coolant temp"
@ -126,7 +130,8 @@ static const char *const label_d5next_power[] = {
static const char *const label_d5next_voltages[] = {
"Pump voltage",
"Fan voltage",
"+5V voltage"
"+5V voltage",
"+12V voltage"
};
static const char *const label_d5next_current[] = {
@ -134,7 +139,7 @@ static const char *const label_d5next_current[] = {
"Fan current"
};
/* Labels for Farbwerk, Farbwerk 360 and Octo temperature sensors */
/* Labels for Farbwerk, Farbwerk 360 and Octo and Quadro temperature sensors */
static const char *const label_temp_sensors[] = {
"Sensor 1",
"Sensor 2",
@ -142,7 +147,7 @@ static const char *const label_temp_sensors[] = {
"Sensor 4"
};
/* Labels for Octo */
/* Labels for Octo and Quadro (except speed) */
static const char *const label_fan_speed[] = {
"Fan 1 speed",
"Fan 2 speed",
@ -187,6 +192,15 @@ static const char *const label_fan_current[] = {
"Fan 8 current"
};
/* Labels for Quadro fan speeds */
static const char *const label_quadro_speeds[] = {
"Fan 1 speed",
"Fan 2 speed",
"Fan 3 speed",
"Fan 4 speed",
"Flow speed [dL/h]"
};
struct aqc_data {
struct hid_device *hdev;
struct device *hwmon_dev;
@ -201,11 +215,19 @@ struct aqc_data {
int checksum_length;
int checksum_offset;
int num_fans;
u8 *fan_sensor_offsets;
u16 *fan_ctrl_offsets;
int num_temp_sensors;
int temp_sensor_start_offset;
u16 power_cycle_count_offset;
u8 flow_sensor_offset;
/* General info, same across all devices */
u32 serial_number[2];
u16 firmware_version;
/* How many times the device was powered on */
/* How many times the device was powered on, if available */
u32 power_cycles;
/* Sensor values */
@ -323,56 +345,47 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3
switch (type) {
case hwmon_temp:
switch (priv->kind) {
case d5next:
if (channel == 0)
return 0444;
break;
case farbwerk:
case farbwerk360:
case octo:
if (channel < priv->num_temp_sensors)
return 0444;
default:
break;
}
break;
case hwmon_pwm:
switch (priv->kind) {
case octo:
if (priv->fan_ctrl_offsets && channel < priv->num_fans) {
switch (attr) {
case hwmon_pwm_input:
return 0644;
default:
break;
}
break;
default:
break;
}
break;
case hwmon_fan:
case hwmon_power:
case hwmon_curr:
switch (priv->kind) {
case d5next:
if (channel < 2)
case quadro:
/* Special case to support flow sensor */
if (channel < priv->num_fans + 1)
return 0444;
break;
case octo:
return 0444;
default:
if (channel < priv->num_fans)
return 0444;
break;
}
break;
case hwmon_power:
case hwmon_curr:
if (channel < priv->num_fans)
return 0444;
break;
case hwmon_in:
switch (priv->kind) {
case d5next:
if (channel < 3)
/* Special case to support +5V and +12V voltage sensors */
if (channel < priv->num_fans + 2)
return 0444;
break;
case octo:
return 0444;
default:
if (channel < priv->num_fans)
return 0444;
break;
}
break;
@ -406,16 +419,12 @@ static int aqc_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
*val = priv->power_input[channel];
break;
case hwmon_pwm:
switch (priv->kind) {
case octo:
ret = aqc_get_ctrl_val(priv, octo_ctrl_fan_offsets[channel]);
if (priv->fan_ctrl_offsets) {
ret = aqc_get_ctrl_val(priv, priv->fan_ctrl_offsets[channel]);
if (ret < 0)
return ret;
*val = aqc_percent_to_pwm(ret);
break;
default:
break;
}
break;
case hwmon_in:
@ -469,19 +478,15 @@ static int aqc_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
case hwmon_pwm:
switch (attr) {
case hwmon_pwm_input:
switch (priv->kind) {
case octo:
if (priv->fan_ctrl_offsets) {
pwm_value = aqc_pwm_to_percent(val);
if (pwm_value < 0)
return pwm_value;
ret = aqc_set_ctrl_val(priv, octo_ctrl_fan_offsets[channel],
ret = aqc_set_ctrl_val(priv, priv->fan_ctrl_offsets[channel],
pwm_value);
if (ret < 0)
return ret;
break;
default:
break;
}
break;
default:
@ -576,76 +581,42 @@ static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8
priv->serial_number[1] = get_unaligned_be16(data + SERIAL_SECOND_PART);
priv->firmware_version = get_unaligned_be16(data + FIRMWARE_VERSION);
/* Sensor readings */
/* Temperature sensor readings */
for (i = 0; i < priv->num_temp_sensors; i++) {
sensor_value = get_unaligned_be16(data +
priv->temp_sensor_start_offset +
i * AQC_TEMP_SENSOR_SIZE);
if (sensor_value == AQC_TEMP_SENSOR_DISCONNECTED)
priv->temp_input[i] = -ENODATA;
else
priv->temp_input[i] = sensor_value * 10;
}
/* Fan speed and related readings */
for (i = 0; i < priv->num_fans; i++) {
priv->speed_input[i] =
get_unaligned_be16(data + priv->fan_sensor_offsets[i] + AQC_FAN_SPEED_OFFSET);
priv->power_input[i] =
get_unaligned_be16(data + priv->fan_sensor_offsets[i] +
AQC_FAN_POWER_OFFSET) * 10000;
priv->voltage_input[i] =
get_unaligned_be16(data + priv->fan_sensor_offsets[i] +
AQC_FAN_VOLTAGE_OFFSET) * 10;
priv->current_input[i] =
get_unaligned_be16(data + priv->fan_sensor_offsets[i] + AQC_FAN_CURRENT_OFFSET);
}
if (priv->power_cycle_count_offset != 0)
priv->power_cycles = get_unaligned_be32(data + priv->power_cycle_count_offset);
/* Special-case sensor readings */
switch (priv->kind) {
case d5next:
priv->power_cycles = get_unaligned_be32(data + D5NEXT_POWER_CYCLES);
priv->temp_input[0] = get_unaligned_be16(data + D5NEXT_COOLANT_TEMP) * 10;
priv->speed_input[0] = get_unaligned_be16(data + D5NEXT_PUMP_SPEED);
priv->speed_input[1] = get_unaligned_be16(data + D5NEXT_FAN_SPEED);
priv->power_input[0] = get_unaligned_be16(data + D5NEXT_PUMP_POWER) * 10000;
priv->power_input[1] = get_unaligned_be16(data + D5NEXT_FAN_POWER) * 10000;
priv->voltage_input[0] = get_unaligned_be16(data + D5NEXT_PUMP_VOLTAGE) * 10;
priv->voltage_input[1] = get_unaligned_be16(data + D5NEXT_FAN_VOLTAGE) * 10;
priv->voltage_input[2] = get_unaligned_be16(data + D5NEXT_5V_VOLTAGE) * 10;
priv->current_input[0] = get_unaligned_be16(data + D5NEXT_PUMP_CURRENT);
priv->current_input[1] = get_unaligned_be16(data + D5NEXT_FAN_CURRENT);
priv->voltage_input[3] = get_unaligned_be16(data + D5NEXT_12V_VOLTAGE) * 10;
break;
case farbwerk:
/* Temperature sensor readings */
for (i = 0; i < FARBWERK_NUM_SENSORS; i++) {
sensor_value = get_unaligned_be16(data + FARBWERK_SENSOR_START +
i * FARBWERK_SENSOR_SIZE);
if (sensor_value == FARBWERK_SENSOR_DISCONNECTED)
priv->temp_input[i] = -ENODATA;
else
priv->temp_input[i] = sensor_value * 10;
}
break;
case farbwerk360:
/* Temperature sensor readings */
for (i = 0; i < FARBWERK360_NUM_SENSORS; i++) {
sensor_value = get_unaligned_be16(data + FARBWERK360_SENSOR_START +
i * FARBWERK360_SENSOR_SIZE);
if (sensor_value == FARBWERK360_SENSOR_DISCONNECTED)
priv->temp_input[i] = -ENODATA;
else
priv->temp_input[i] = sensor_value * 10;
}
break;
case octo:
priv->power_cycles = get_unaligned_be32(data + OCTO_POWER_CYCLES);
/* Fan speed and related readings */
for (i = 0; i < OCTO_NUM_FANS; i++) {
priv->speed_input[i] =
get_unaligned_be16(data + octo_sensor_fan_offsets[i] +
OCTO_FAN_SPEED_OFFSET);
priv->power_input[i] =
get_unaligned_be16(data + octo_sensor_fan_offsets[i] +
OCTO_FAN_POWER_OFFSET) * 10000;
priv->voltage_input[i] =
get_unaligned_be16(data + octo_sensor_fan_offsets[i] +
OCTO_FAN_VOLTAGE_OFFSET) * 10;
priv->current_input[i] =
get_unaligned_be16(data + octo_sensor_fan_offsets[i] +
OCTO_FAN_CURRENT_OFFSET);
}
/* Temperature sensor readings */
for (i = 0; i < OCTO_NUM_SENSORS; i++) {
sensor_value = get_unaligned_be16(data + OCTO_SENSOR_START +
i * OCTO_SENSOR_SIZE);
if (sensor_value == OCTO_SENSOR_DISCONNECTED)
priv->temp_input[i] = -ENODATA;
else
priv->temp_input[i] = sensor_value * 10;
}
case quadro:
priv->speed_input[4] = get_unaligned_be16(data + priv->flow_sensor_offset);
break;
default:
break;
@ -699,14 +670,8 @@ static void aqc_debugfs_init(struct aqc_data *priv)
debugfs_create_file("serial_number", 0444, priv->debugfs, priv, &serial_number_fops);
debugfs_create_file("firmware_version", 0444, priv->debugfs, priv, &firmware_version_fops);
switch (priv->kind) {
case d5next:
case octo:
if (priv->power_cycle_count_offset != 0)
debugfs_create_file("power_cycles", 0444, priv->debugfs, priv, &power_cycles_fops);
break;
default:
break;
}
}
#else
@ -747,6 +712,14 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
case USB_PRODUCT_ID_D5NEXT:
priv->kind = d5next;
priv->num_fans = D5NEXT_NUM_FANS;
priv->fan_sensor_offsets = d5next_sensor_fan_offsets;
priv->fan_ctrl_offsets = d5next_ctrl_fan_offsets;
priv->num_temp_sensors = D5NEXT_NUM_SENSORS;
priv->temp_sensor_start_offset = D5NEXT_COOLANT_TEMP;
priv->power_cycle_count_offset = D5NEXT_POWER_CYCLES;
priv->buffer_size = D5NEXT_CTRL_REPORT_SIZE;
priv->temp_label = label_d5next_temp;
priv->speed_label = label_d5next_speeds;
priv->power_label = label_d5next_power;
@ -756,19 +729,29 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
case USB_PRODUCT_ID_FARBWERK:
priv->kind = farbwerk;
priv->num_fans = 0;
priv->num_temp_sensors = FARBWERK_NUM_SENSORS;
priv->temp_sensor_start_offset = FARBWERK_SENSOR_START;
priv->temp_label = label_temp_sensors;
break;
case USB_PRODUCT_ID_FARBWERK360:
priv->kind = farbwerk360;
priv->num_fans = 0;
priv->num_temp_sensors = FARBWERK360_NUM_SENSORS;
priv->temp_sensor_start_offset = FARBWERK360_SENSOR_START;
priv->temp_label = label_temp_sensors;
break;
case USB_PRODUCT_ID_OCTO:
priv->kind = octo;
priv->num_fans = OCTO_NUM_FANS;
priv->fan_sensor_offsets = octo_sensor_fan_offsets;
priv->fan_ctrl_offsets = octo_ctrl_fan_offsets;
priv->num_temp_sensors = OCTO_NUM_SENSORS;
priv->temp_sensor_start_offset = OCTO_SENSOR_START;
priv->power_cycle_count_offset = OCTO_POWER_CYCLES;
priv->buffer_size = OCTO_CTRL_REPORT_SIZE;
priv->checksum_start = OCTO_CTRL_REPORT_CHECKSUM_START;
priv->checksum_length = OCTO_CTRL_REPORT_CHECKSUM_LENGTH;
priv->checksum_offset = OCTO_CTRL_REPORT_CHECKSUM_OFFSET;
priv->temp_label = label_temp_sensors;
priv->speed_label = label_fan_speed;
@ -776,10 +759,34 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
priv->voltage_label = label_fan_voltage;
priv->current_label = label_fan_current;
break;
case USB_PRODUCT_ID_QUADRO:
priv->kind = quadro;
priv->num_fans = QUADRO_NUM_FANS;
priv->fan_sensor_offsets = quadro_sensor_fan_offsets;
priv->fan_ctrl_offsets = quadro_ctrl_fan_offsets;
priv->num_temp_sensors = QUADRO_NUM_SENSORS;
priv->temp_sensor_start_offset = QUADRO_SENSOR_START;
priv->power_cycle_count_offset = QUADRO_POWER_CYCLES;
priv->buffer_size = QUADRO_CTRL_REPORT_SIZE;
priv->flow_sensor_offset = QUADRO_FLOW_SENSOR_OFFSET;
priv->temp_label = label_temp_sensors;
priv->speed_label = label_quadro_speeds;
priv->power_label = label_fan_power;
priv->voltage_label = label_fan_voltage;
priv->current_label = label_fan_current;
break;
default:
break;
}
if (priv->buffer_size != 0) {
priv->checksum_start = 0x01;
priv->checksum_length = priv->buffer_size - 3;
priv->checksum_offset = priv->buffer_size - 2;
}
priv->name = aqc_device_names[priv->kind];
priv->buffer = devm_kzalloc(&hdev->dev, priv->buffer_size, GFP_KERNEL);
@ -825,6 +832,7 @@ static const struct hid_device_id aqc_table[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_FARBWERK) },
{ HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_FARBWERK360) },
{ HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_OCTO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_QUADRO) },
{ }
};

View File

@ -159,7 +159,7 @@
* 11: reserved.
*/
#define M_TACH_MODE 0x02 /* 10b */
#define M_TACH_UNIT 0x0210
#define M_TACH_UNIT 0x0420
#define INIT_FAN_CTRL 0xFF
/* How long we sleep in us while waiting for an RPM result. */

View File

@ -54,6 +54,10 @@ static char *mutex_path_override;
/* ACPI mutex for locking access to the EC for the firmware */
#define ASUS_HW_ACCESS_MUTEX_ASMX "\\AMW0.ASMX"
#define ASUS_HW_ACCESS_MUTEX_RMTW_ASMX "\\RMTW.ASMX"
#define ASUS_HW_ACCESS_MUTEX_SB_PCI0_SBRG_SIO1_MUT0 "\\_SB_.PCI0.SBRG.SIO1.MUT0"
#define MAX_IDENTICAL_BOARD_VARIATIONS 3
/* Moniker for the ACPI global lock (':' is not allowed in ASL identifiers) */
@ -119,6 +123,18 @@ enum ec_sensors {
ec_sensor_temp_water_in,
/* "Water_Out" temperature sensor reading [℃] */
ec_sensor_temp_water_out,
/* "Water_Block_In" temperature sensor reading [℃] */
ec_sensor_temp_water_block_in,
/* "Water_Block_Out" temperature sensor reading [℃] */
ec_sensor_temp_water_block_out,
/* "T_sensor_2" temperature sensor reading [℃] */
ec_sensor_temp_t_sensor_2,
/* "Extra_1" temperature sensor reading [℃] */
ec_sensor_temp_sensor_extra_1,
/* "Extra_2" temperature sensor reading [℃] */
ec_sensor_temp_sensor_extra_2,
/* "Extra_3" temperature sensor reading [℃] */
ec_sensor_temp_sensor_extra_3,
};
#define SENSOR_TEMP_CHIPSET BIT(ec_sensor_temp_chipset)
@ -134,11 +150,19 @@ enum ec_sensors {
#define SENSOR_CURR_CPU BIT(ec_sensor_curr_cpu)
#define SENSOR_TEMP_WATER_IN BIT(ec_sensor_temp_water_in)
#define SENSOR_TEMP_WATER_OUT BIT(ec_sensor_temp_water_out)
#define SENSOR_TEMP_WATER_BLOCK_IN BIT(ec_sensor_temp_water_block_in)
#define SENSOR_TEMP_WATER_BLOCK_OUT BIT(ec_sensor_temp_water_block_out)
#define SENSOR_TEMP_T_SENSOR_2 BIT(ec_sensor_temp_t_sensor_2)
#define SENSOR_TEMP_SENSOR_EXTRA_1 BIT(ec_sensor_temp_sensor_extra_1)
#define SENSOR_TEMP_SENSOR_EXTRA_2 BIT(ec_sensor_temp_sensor_extra_2)
#define SENSOR_TEMP_SENSOR_EXTRA_3 BIT(ec_sensor_temp_sensor_extra_3)
enum board_family {
family_unknown,
family_amd_400_series,
family_amd_500_series,
family_intel_300_series,
family_intel_600_series
};
/* All the known sensors for ASUS EC controllers */
@ -195,12 +219,53 @@ static const struct ec_sensor_info sensors_family_amd_500[] = {
EC_SENSOR("Water_In", hwmon_temp, 1, 0x01, 0x00),
[ec_sensor_temp_water_out] =
EC_SENSOR("Water_Out", hwmon_temp, 1, 0x01, 0x01),
[ec_sensor_temp_water_block_in] =
EC_SENSOR("Water_Block_In", hwmon_temp, 1, 0x01, 0x02),
[ec_sensor_temp_water_block_out] =
EC_SENSOR("Water_Block_Out", hwmon_temp, 1, 0x01, 0x03),
[ec_sensor_temp_sensor_extra_1] =
EC_SENSOR("Extra_1", hwmon_temp, 1, 0x01, 0x09),
[ec_sensor_temp_t_sensor_2] =
EC_SENSOR("T_sensor_2", hwmon_temp, 1, 0x01, 0x0a),
[ec_sensor_temp_sensor_extra_2] =
EC_SENSOR("Extra_2", hwmon_temp, 1, 0x01, 0x0b),
[ec_sensor_temp_sensor_extra_3] =
EC_SENSOR("Extra_3", hwmon_temp, 1, 0x01, 0x0c),
};
static const struct ec_sensor_info sensors_family_intel_300[] = {
[ec_sensor_temp_chipset] =
EC_SENSOR("Chipset", hwmon_temp, 1, 0x00, 0x3a),
[ec_sensor_temp_cpu] = EC_SENSOR("CPU", hwmon_temp, 1, 0x00, 0x3b),
[ec_sensor_temp_mb] =
EC_SENSOR("Motherboard", hwmon_temp, 1, 0x00, 0x3c),
[ec_sensor_temp_t_sensor] =
EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x3d),
[ec_sensor_temp_vrm] = EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x3e),
[ec_sensor_fan_cpu_opt] =
EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xb0),
[ec_sensor_fan_vrm_hs] = EC_SENSOR("VRM HS", hwmon_fan, 2, 0x00, 0xb2),
[ec_sensor_fan_water_flow] =
EC_SENSOR("Water_Flow", hwmon_fan, 2, 0x00, 0xbc),
[ec_sensor_temp_water_in] =
EC_SENSOR("Water_In", hwmon_temp, 1, 0x01, 0x00),
[ec_sensor_temp_water_out] =
EC_SENSOR("Water_Out", hwmon_temp, 1, 0x01, 0x01),
};
static const struct ec_sensor_info sensors_family_intel_600[] = {
[ec_sensor_temp_t_sensor] =
EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x3d),
[ec_sensor_temp_vrm] = EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x3e),
};
/* Shortcuts for common combinations */
#define SENSOR_SET_TEMP_CHIPSET_CPU_MB \
(SENSOR_TEMP_CHIPSET | SENSOR_TEMP_CPU | SENSOR_TEMP_MB)
#define SENSOR_SET_TEMP_WATER (SENSOR_TEMP_WATER_IN | SENSOR_TEMP_WATER_OUT)
#define SENSOR_SET_WATER_BLOCK \
(SENSOR_TEMP_WATER_BLOCK_IN | SENSOR_TEMP_WATER_BLOCK_OUT)
struct ec_board_info {
const char *board_names[MAX_IDENTICAL_BOARD_VARIATIONS];
@ -272,6 +337,18 @@ static const struct ec_board_info board_info[] = {
.mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
.family = family_amd_500_series,
},
{
.board_names = {
"ROG MAXIMUS XI HERO",
"ROG MAXIMUS XI HERO (WI-FI)",
},
.sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
SENSOR_TEMP_T_SENSOR |
SENSOR_TEMP_VRM | SENSOR_SET_TEMP_WATER |
SENSOR_FAN_CPU_OPT | SENSOR_FAN_WATER_FLOW,
.mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
.family = family_intel_300_series,
},
{
.board_names = {"ROG CROSSHAIR VIII IMPACT"},
.sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
@ -324,12 +401,31 @@ static const struct ec_board_info board_info[] = {
},
{
.board_names = {"ROG STRIX X570-I GAMING"},
.sensors = SENSOR_TEMP_T_SENSOR | SENSOR_FAN_VRM_HS |
SENSOR_FAN_CHIPSET | SENSOR_CURR_CPU |
SENSOR_IN_CPU_CORE,
.sensors = SENSOR_TEMP_CHIPSET | SENSOR_TEMP_VRM |
SENSOR_TEMP_T_SENSOR |
SENSOR_FAN_VRM_HS | SENSOR_FAN_CHIPSET |
SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE,
.mutex_path = ASUS_HW_ACCESS_MUTEX_ASMX,
.family = family_amd_500_series,
},
{
.board_names = {"ROG STRIX Z690-A GAMING WIFI D4"},
.sensors = SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM,
.mutex_path = ASUS_HW_ACCESS_MUTEX_RMTW_ASMX,
.family = family_intel_600_series,
},
{
.board_names = {"ROG ZENITH II EXTREME"},
.sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB | SENSOR_TEMP_T_SENSOR |
SENSOR_TEMP_VRM | SENSOR_SET_TEMP_WATER |
SENSOR_FAN_CPU_OPT | SENSOR_FAN_CHIPSET | SENSOR_FAN_VRM_HS |
SENSOR_FAN_WATER_FLOW | SENSOR_CURR_CPU | SENSOR_IN_CPU_CORE |
SENSOR_SET_WATER_BLOCK |
SENSOR_TEMP_T_SENSOR_2 | SENSOR_TEMP_SENSOR_EXTRA_1 |
SENSOR_TEMP_SENSOR_EXTRA_2 | SENSOR_TEMP_SENSOR_EXTRA_3,
.mutex_path = ASUS_HW_ACCESS_MUTEX_SB_PCI0_SBRG_SIO1_MUT0,
.family = family_amd_500_series,
},
{}
};
@ -799,6 +895,12 @@ static int __init asus_ec_probe(struct platform_device *pdev)
case family_amd_500_series:
ec_data->sensors_info = sensors_family_amd_500;
break;
case family_intel_300_series:
ec_data->sensors_info = sensors_family_intel_300;
break;
case family_intel_600_series:
ec_data->sensors_info = sensors_family_intel_600;
break;
default:
dev_err(dev, "Unknown board family: %d",
ec_data->board_info->family);

View File

@ -514,22 +514,20 @@ static int asus_wmi_configure_sensor_setup(struct device *dev,
int i, idx;
int err;
temp_sensor = devm_kcalloc(dev, 1, sizeof(*temp_sensor), GFP_KERNEL);
if (!temp_sensor)
return -ENOMEM;
for (i = 0; i < sensor_data->wmi.sensor_count; i++) {
err = asus_wmi_sensor_info(i, temp_sensor);
struct asus_wmi_sensor_info sensor;
err = asus_wmi_sensor_info(i, &sensor);
if (err)
return err;
switch (temp_sensor->data_type) {
switch (sensor.data_type) {
case TEMPERATURE_C:
case VOLTAGE:
case CURRENT:
case FAN_RPM:
case WATER_FLOW:
type = asus_data_types[temp_sensor->data_type];
type = asus_data_types[sensor.data_type];
if (!nr_count[type])
nr_types++;
nr_count[type]++;

View File

@ -130,7 +130,7 @@ struct smm_regs {
unsigned int edx;
unsigned int esi;
unsigned int edi;
} __packed;
};
static const char * const temp_labels[] = {
"CPU",
@ -175,77 +175,35 @@ static int i8k_smm_func(void *par)
struct smm_regs *regs = par;
int eax = regs->eax;
int ebx = regs->ebx;
unsigned char carry;
long long duration;
int rc;
/* SMM requires CPU 0 */
if (smp_processor_id() != 0)
return -EBUSY;
#if defined(CONFIG_X86_64)
asm volatile("pushq %%rax\n\t"
"movl 0(%%rax),%%edx\n\t"
"pushq %%rdx\n\t"
"movl 4(%%rax),%%ebx\n\t"
"movl 8(%%rax),%%ecx\n\t"
"movl 12(%%rax),%%edx\n\t"
"movl 16(%%rax),%%esi\n\t"
"movl 20(%%rax),%%edi\n\t"
"popq %%rax\n\t"
"out %%al,$0xb2\n\t"
"out %%al,$0x84\n\t"
"xchgq %%rax,(%%rsp)\n\t"
"movl %%ebx,4(%%rax)\n\t"
"movl %%ecx,8(%%rax)\n\t"
"movl %%edx,12(%%rax)\n\t"
"movl %%esi,16(%%rax)\n\t"
"movl %%edi,20(%%rax)\n\t"
"popq %%rdx\n\t"
"movl %%edx,0(%%rax)\n\t"
"pushfq\n\t"
"popq %%rax\n\t"
"andl $1,%%eax\n"
: "=a"(rc)
: "a"(regs)
: "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory");
#else
asm volatile("pushl %%eax\n\t"
"movl 0(%%eax),%%edx\n\t"
"push %%edx\n\t"
"movl 4(%%eax),%%ebx\n\t"
"movl 8(%%eax),%%ecx\n\t"
"movl 12(%%eax),%%edx\n\t"
"movl 16(%%eax),%%esi\n\t"
"movl 20(%%eax),%%edi\n\t"
"popl %%eax\n\t"
"out %%al,$0xb2\n\t"
"out %%al,$0x84\n\t"
"xchgl %%eax,(%%esp)\n\t"
"movl %%ebx,4(%%eax)\n\t"
"movl %%ecx,8(%%eax)\n\t"
"movl %%edx,12(%%eax)\n\t"
"movl %%esi,16(%%eax)\n\t"
"movl %%edi,20(%%eax)\n\t"
"popl %%edx\n\t"
"movl %%edx,0(%%eax)\n\t"
"lahf\n\t"
"shrl $8,%%eax\n\t"
"andl $1,%%eax\n"
: "=a"(rc)
: "a"(regs)
: "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory");
#endif
if (rc != 0 || (regs->eax & 0xffff) == 0xffff || regs->eax == eax)
rc = -EINVAL;
asm volatile("out %%al,$0xb2\n\t"
"out %%al,$0x84\n\t"
"setc %0\n"
: "=mr" (carry),
"+a" (regs->eax),
"+b" (regs->ebx),
"+c" (regs->ecx),
"+d" (regs->edx),
"+S" (regs->esi),
"+D" (regs->edi));
duration = ktime_us_delta(ktime_get(), calltime);
pr_debug("smm(0x%.4x 0x%.4x) = 0x%.4x (took %7lld usecs)\n", eax, ebx,
(rc ? 0xffff : regs->eax & 0xffff), duration);
pr_debug("smm(0x%.4x 0x%.4x) = 0x%.4x carry: %d (took %7lld usecs)\n",
eax, ebx, regs->eax & 0xffff, carry, duration);
if (duration > DELL_SMM_MAX_DURATION)
pr_warn_once("SMM call took %lld usecs!\n", duration);
return rc;
if (carry || (regs->eax & 0xffff) == 0xffff || regs->eax == eax)
return -EINVAL;
return 0;
}
/*
@ -1131,6 +1089,13 @@ static const struct i8k_config_data i8k_config_data[] __initconst = {
};
static const struct dmi_system_id i8k_dmi_table[] __initconst = {
{
.ident = "Dell G5 5590",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "G5 5590"),
},
},
{
.ident = "Dell Inspiron",
.matches = {
@ -1365,6 +1330,14 @@ static const struct dmi_system_id i8k_whitelist_fan_control[] __initconst = {
},
.driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3],
},
{
.ident = "Dell XPS 13 7390",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "XPS 13 7390"),
},
.driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3],
},
{ }
};

View File

@ -621,3 +621,4 @@ module_exit(drivetemp_exit);
MODULE_AUTHOR("Guenter Roeck <linus@roeck-us.net>");
MODULE_DESCRIPTION("Hard drive temperature monitor");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:drivetemp");

View File

@ -49,6 +49,7 @@
#define SIO_F81768D_ID 0x1210 /* Chipset ID */
#define SIO_F81865_ID 0x0704 /* Chipset ID */
#define SIO_F81866_ID 0x1010 /* Chipset ID */
#define SIO_F71858AD_ID 0x0903 /* Chipset ID */
#define SIO_F81966_ID 0x1502 /* Chipset ID */
#define REGION_LENGTH 8
@ -2638,6 +2639,7 @@ static int __init f71882fg_find(int sioaddr, struct f71882fg_sio_data *sio_data)
sio_data->type = f71808a;
break;
case SIO_F71858_ID:
case SIO_F71858AD_ID:
sio_data->type = f71858fg;
break;
case SIO_F71862_ID:

View File

@ -269,10 +269,13 @@ gsc_hwmon_get_devtree_pdata(struct device *dev)
/* fan controller base address */
fan = of_find_compatible_node(dev->parent->of_node, NULL, "gw,gsc-fan");
if (fan && of_property_read_u32(fan, "reg", &pdata->fan_base)) {
of_node_put(fan);
dev_err(dev, "fan node without base\n");
return ERR_PTR(-EINVAL);
}
of_node_put(fan);
/* allocate structures for channels and count instances of each type */
device_for_each_child_node(dev, child) {
if (fwnode_property_read_string(child, "label", &ch->name)) {

View File

@ -11,7 +11,8 @@
* which contains this code, we don't worry about the wasted space.
*/
#include <linux/kernel.h>
#include <linux/minmax.h>
#include <linux/types.h>
/* straight from the datasheet */
#define LM75_TEMP_MIN (-55000)

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@
* Reworked by Sven Schuchmann <schuchmann@schleissheimer.de>
* DT support added by Clemens Gruber <clemens.gruber@pqgruber.com>
*
* This driver export the value of analog input voltage to sysfs, the
* This driver exports the value of analog input voltage to sysfs, the
* voltage unit is mV. Through the sysfs interface, lm-sensors tool
* can also display the input voltage.
*/
@ -45,19 +45,29 @@ enum chips {
* Client data (each client gets its own)
*/
struct mcp3021_data {
struct device *hwmon_dev;
struct i2c_client *client;
u32 vdd; /* supply and reference voltage in millivolt */
u16 sar_shift;
u16 sar_mask;
u8 output_res;
};
static int mcp3021_read16(struct i2c_client *client)
static inline u16 volts_from_reg(struct mcp3021_data *data, u16 val)
{
struct mcp3021_data *data = i2c_get_clientdata(client);
int ret;
u16 reg;
return DIV_ROUND_CLOSEST(data->vdd * val, 1 << data->output_res);
}
static int mcp3021_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
struct mcp3021_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
__be16 buf;
u16 reg;
int ret;
if (type != hwmon_in)
return -EOPNOTSUPP;
ret = i2c_master_recv(client, (char *)&buf, 2);
if (ret < 0)
@ -74,39 +84,46 @@ static int mcp3021_read16(struct i2c_client *client)
*/
reg = (reg >> data->sar_shift) & data->sar_mask;
return reg;
*val = volts_from_reg(data, reg);
return 0;
}
static inline u16 volts_from_reg(struct mcp3021_data *data, u16 val)
static umode_t mcp3021_is_visible(const void *_data,
enum hwmon_sensor_types type,
u32 attr, int channel)
{
return DIV_ROUND_CLOSEST(data->vdd * val, 1 << data->output_res);
if (type != hwmon_in)
return 0;
if (attr != hwmon_in_input)
return 0;
return 0444;
}
static ssize_t in0_input_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
struct mcp3021_data *data = i2c_get_clientdata(client);
int reg, in_input;
static const struct hwmon_channel_info *mcp3021_info[] = {
HWMON_CHANNEL_INFO(in, HWMON_I_INPUT),
NULL
};
reg = mcp3021_read16(client);
if (reg < 0)
return reg;
static const struct hwmon_ops mcp3021_hwmon_ops = {
.is_visible = mcp3021_is_visible,
.read = mcp3021_read,
};
in_input = volts_from_reg(data, reg);
return sprintf(buf, "%d\n", in_input);
}
static DEVICE_ATTR_RO(in0_input);
static const struct hwmon_chip_info mcp3021_chip_info = {
.ops = &mcp3021_hwmon_ops,
.info = mcp3021_info,
};
static const struct i2c_device_id mcp3021_id[];
static int mcp3021_probe(struct i2c_client *client)
{
int err;
struct mcp3021_data *data = NULL;
struct device_node *np = client->dev.of_node;
struct device *hwmon_dev;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
return -ENODEV;
@ -147,34 +164,17 @@ static int mcp3021_probe(struct i2c_client *client)
break;
}
data->client = client;
if (data->vdd > MCP3021_VDD_REF_MAX || data->vdd < MCP3021_VDD_REF_MIN)
return -EINVAL;
err = sysfs_create_file(&client->dev.kobj, &dev_attr_in0_input.attr);
if (err)
return err;
data->hwmon_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->hwmon_dev)) {
err = PTR_ERR(data->hwmon_dev);
goto exit_remove;
}
return 0;
exit_remove:
sysfs_remove_file(&client->dev.kobj, &dev_attr_in0_input.attr);
return err;
}
static int mcp3021_remove(struct i2c_client *client)
{
struct mcp3021_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_file(&client->dev.kobj, &dev_attr_in0_input.attr);
return 0;
hwmon_dev = devm_hwmon_device_register_with_info(&client->dev,
client->name,
data,
&mcp3021_chip_info,
NULL);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
static const struct i2c_device_id mcp3021_id[] = {
@ -199,7 +199,6 @@ static struct i2c_driver mcp3021_driver = {
.of_match_table = of_match_ptr(of_mcp3021_match),
},
.probe_new = mcp3021_probe,
.remove = mcp3021_remove,
.id_table = mcp3021_id,
};

View File

@ -1083,6 +1083,7 @@ static const char * const asus_wmi_boards[] = {
"TUF GAMING B550M-PLUS",
"TUF GAMING B550M-PLUS (WI-FI)",
"TUF GAMING B550-PLUS",
"TUF GAMING B550-PLUS WIFI II",
"TUF GAMING B550-PRO",
"TUF GAMING X570-PLUS",
"TUF GAMING X570-PLUS (WI-FI)",
@ -1200,10 +1201,8 @@ static int __init sensors_nct6775_platform_init(void)
exit_device_put:
platform_device_put(pdev[i]);
exit_device_unregister:
while (--i >= 0) {
if (pdev[i])
platform_device_unregister(pdev[i]);
}
while (i--)
platform_device_unregister(pdev[i]);
exit_unregister:
platform_driver_unregister(&nct6775_driver);
return err;
@ -1213,10 +1212,8 @@ static void __exit sensors_nct6775_platform_exit(void)
{
int i;
for (i = 0; i < ARRAY_SIZE(pdev); i++) {
if (pdev[i])
platform_device_unregister(pdev[i]);
}
for (i = 0; i < ARRAY_SIZE(pdev); i++)
platform_device_unregister(pdev[i]);
platform_driver_unregister(&nct6775_driver);
}

View File

@ -729,18 +729,14 @@ static ssize_t occ_show_extended(struct device *dev,
rc = sysfs_emit(buf, "%u",
get_unaligned_be32(&extn->sensor_id));
} else {
rc = sysfs_emit(buf, "%02x%02x%02x%02x\n",
extn->name[0], extn->name[1],
extn->name[2], extn->name[3]);
rc = sysfs_emit(buf, "%4phN\n", extn->name);
}
break;
case 1:
rc = sysfs_emit(buf, "%02x\n", extn->flags);
break;
case 2:
rc = sysfs_emit(buf, "%02x%02x%02x%02x%02x%02x\n",
extn->data[0], extn->data[1], extn->data[2],
extn->data[3], extn->data[4], extn->data[5]);
rc = sysfs_emit(buf, "%6phN\n", extn->data);
break;
default:
return -EINVAL;

View File

@ -55,8 +55,7 @@ static bool p9_sbe_occ_save_ffdc(struct p9_sbe_occ *ctx, const void *resp,
mutex_lock(&ctx->sbe_error_lock);
if (!ctx->sbe_error) {
if (resp_len > ctx->ffdc_size) {
if (ctx->ffdc)
kvfree(ctx->ffdc);
kvfree(ctx->ffdc);
ctx->ffdc = kvmalloc(resp_len, GFP_KERNEL);
if (!ctx->ffdc) {
ctx->ffdc_len = 0;
@ -170,8 +169,7 @@ static int p9_sbe_occ_remove(struct platform_device *pdev)
ctx->sbe = NULL;
occ_shutdown(occ);
if (ctx->ffdc)
kvfree(ctx->ffdc);
kvfree(ctx->ffdc);
return 0;
}

View File

@ -181,6 +181,15 @@ config SENSORS_LM25066_REGULATOR
If you say yes here you get regulator support for National
Semiconductor LM25066, LM5064, and LM5066.
config SENSORS_LT7182S
tristate "Analog Devices LT7182S"
help
If you say yes here you get hardware monitoring support for Analog
Devices LT7182S.
This driver can also be built as a module. If so, the module will
be called lt7182s.
config SENSORS_LTC2978
tristate "Linear Technologies LTC2978 and compatibles"
help

View File

@ -20,6 +20,7 @@ obj-$(CONFIG_SENSORS_IR38064) += ir38064.o
obj-$(CONFIG_SENSORS_IRPS5401) += irps5401.o
obj-$(CONFIG_SENSORS_ISL68137) += isl68137.o
obj-$(CONFIG_SENSORS_LM25066) += lm25066.o
obj-$(CONFIG_SENSORS_LT7182S) += lt7182s.o
obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o
obj-$(CONFIG_SENSORS_LTC3815) += ltc3815.o
obj-$(CONFIG_SENSORS_MAX15301) += max15301.o

View 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, &lt7182s_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);

View File

@ -562,7 +562,24 @@ static const struct i2c_device_id ltc2978_id[] = {
MODULE_DEVICE_TABLE(i2c, ltc2978_id);
#if IS_ENABLED(CONFIG_SENSORS_LTC2978_REGULATOR)
#define LTC2978_ADC_RES 0xFFFF
#define LTC2978_N_ADC 122
#define LTC2978_MAX_UV (LTC2978_ADC_RES * LTC2978_N_ADC)
#define LTC2978_UV_STEP 1000
#define LTC2978_N_VOLTAGES ((LTC2978_MAX_UV / LTC2978_UV_STEP) + 1)
static const struct regulator_desc ltc2978_reg_desc[] = {
PMBUS_REGULATOR_STEP("vout", 0, LTC2978_N_VOLTAGES, LTC2978_UV_STEP),
PMBUS_REGULATOR_STEP("vout", 1, LTC2978_N_VOLTAGES, LTC2978_UV_STEP),
PMBUS_REGULATOR_STEP("vout", 2, LTC2978_N_VOLTAGES, LTC2978_UV_STEP),
PMBUS_REGULATOR_STEP("vout", 3, LTC2978_N_VOLTAGES, LTC2978_UV_STEP),
PMBUS_REGULATOR_STEP("vout", 4, LTC2978_N_VOLTAGES, LTC2978_UV_STEP),
PMBUS_REGULATOR_STEP("vout", 5, LTC2978_N_VOLTAGES, LTC2978_UV_STEP),
PMBUS_REGULATOR_STEP("vout", 6, LTC2978_N_VOLTAGES, LTC2978_UV_STEP),
PMBUS_REGULATOR_STEP("vout", 7, LTC2978_N_VOLTAGES, LTC2978_UV_STEP),
};
static const struct regulator_desc ltc2978_reg_desc_default[] = {
PMBUS_REGULATOR("vout", 0),
PMBUS_REGULATOR("vout", 1),
PMBUS_REGULATOR("vout", 2),
@ -839,10 +856,29 @@ static int ltc2978_probe(struct i2c_client *client)
#if IS_ENABLED(CONFIG_SENSORS_LTC2978_REGULATOR)
info->num_regulators = info->pages;
info->reg_desc = ltc2978_reg_desc;
if (info->num_regulators > ARRAY_SIZE(ltc2978_reg_desc)) {
dev_err(&client->dev, "num_regulators too large!");
info->num_regulators = ARRAY_SIZE(ltc2978_reg_desc);
switch (data->id) {
case ltc2972:
case ltc2974:
case ltc2975:
case ltc2977:
case ltc2978:
case ltc2979:
case ltc2980:
case ltm2987:
info->reg_desc = ltc2978_reg_desc;
if (info->num_regulators > ARRAY_SIZE(ltc2978_reg_desc)) {
dev_warn(&client->dev, "num_regulators too large!");
info->num_regulators = ARRAY_SIZE(ltc2978_reg_desc);
}
break;
default:
info->reg_desc = ltc2978_reg_desc_default;
if (info->num_regulators > ARRAY_SIZE(ltc2978_reg_desc_default)) {
dev_warn(&client->dev, "num_regulators too large!");
info->num_regulators =
ARRAY_SIZE(ltc2978_reg_desc_default);
}
break;
}
#endif

View File

@ -406,7 +406,7 @@ enum pmbus_sensor_classes {
#define PMBUS_PHASE_VIRTUAL BIT(30) /* Phases on this page are virtual */
#define PMBUS_PAGE_VIRTUAL BIT(31) /* Page is virtual */
enum pmbus_data_format { linear = 0, direct, vid };
enum pmbus_data_format { linear = 0, ieee754, direct, vid };
enum vrm_version { vr11 = 0, vr12, vr13, imvp9, amd625mv };
struct pmbus_driver_info {
@ -463,8 +463,8 @@ struct pmbus_driver_info {
extern const struct regulator_ops pmbus_regulator_ops;
/* Macro for filling in array of struct regulator_desc */
#define PMBUS_REGULATOR(_name, _id) \
/* Macros for filling in array of struct regulator_desc */
#define PMBUS_REGULATOR_STEP(_name, _id, _voltages, _step) \
[_id] = { \
.name = (_name # _id), \
.supply_name = "vin", \
@ -474,8 +474,12 @@ extern const struct regulator_ops pmbus_regulator_ops;
.ops = &pmbus_regulator_ops, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
.n_voltages = _voltages, \
.uV_step = _step, \
}
#define PMBUS_REGULATOR(_name, _id) PMBUS_REGULATOR_STEP(_name, _id, 0, 0)
/* Function declarations */
void pmbus_clear_cache(struct i2c_client *client);

View File

@ -104,6 +104,9 @@ struct pmbus_data {
s16 currpage; /* current page, -1 for unknown/unset */
s16 currphase; /* current phase, 0xff for all, -1 for unknown/unset */
int vout_low[PMBUS_PAGES]; /* voltage low margin */
int vout_high[PMBUS_PAGES]; /* voltage high margin */
};
struct pmbus_debugfs_entry {
@ -441,6 +444,18 @@ int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg,
}
EXPORT_SYMBOL_NS_GPL(pmbus_update_byte_data, PMBUS);
static int pmbus_read_block_data(struct i2c_client *client, int page, u8 reg,
char *data_buf)
{
int rv;
rv = pmbus_set_page(client, page, 0xff);
if (rv < 0)
return rv;
return i2c_smbus_read_block_data(client, reg, data_buf);
}
static struct pmbus_sensor *pmbus_find_sensor(struct pmbus_data *data, int page,
int reg)
{
@ -578,6 +593,22 @@ bool pmbus_check_word_register(struct i2c_client *client, int page, int reg)
}
EXPORT_SYMBOL_NS_GPL(pmbus_check_word_register, PMBUS);
static bool __maybe_unused pmbus_check_block_register(struct i2c_client *client,
int page, int reg)
{
int rv;
struct pmbus_data *data = i2c_get_clientdata(client);
char data_buf[I2C_SMBUS_BLOCK_MAX + 2];
rv = pmbus_read_block_data(client, page, reg, data_buf);
if (rv >= 0 && !(data->flags & PMBUS_SKIP_STATUS_CHECK))
rv = pmbus_check_status_cml(client);
if (rv < 0 && (data->flags & PMBUS_READ_STATUS_AFTER_FAILED_CHECK))
data->read_status(client, -1);
pmbus_clear_fault_page(client, -1);
return rv >= 0;
}
const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client *client)
{
struct pmbus_data *data = i2c_get_clientdata(client);
@ -611,6 +642,66 @@ static void pmbus_update_sensor_data(struct i2c_client *client, struct pmbus_sen
sensor->phase, sensor->reg);
}
/*
* Convert ieee754 sensor values to milli- or micro-units
* depending on sensor type.
*
* ieee754 data format:
* bit 15: sign
* bit 10..14: exponent
* bit 0..9: mantissa
* exponent=0:
* v=(1)^signbit * 2^(14) * 0.significantbits
* exponent=1..30:
* v=(1)^signbit * 2^(exponent - 15) * 1.significantbits
* exponent=31:
* v=NaN
*
* Add the number mantissa bits into the calculations for simplicity.
* To do that, add '10' to the exponent. By doing that, we can just add
* 0x400 to normal values and get the expected result.
*/
static long pmbus_reg2data_ieee754(struct pmbus_data *data,
struct pmbus_sensor *sensor)
{
int exponent;
bool sign;
long val;
/* only support half precision for now */
sign = sensor->data & 0x8000;
exponent = (sensor->data >> 10) & 0x1f;
val = sensor->data & 0x3ff;
if (exponent == 0) { /* subnormal */
exponent = -(14 + 10);
} else if (exponent == 0x1f) { /* NaN, convert to min/max */
exponent = 0;
val = 65504;
} else {
exponent -= (15 + 10); /* normal */
val |= 0x400;
}
/* scale result to milli-units for all sensors except fans */
if (sensor->class != PSC_FAN)
val = val * 1000L;
/* scale result to micro-units for power sensors */
if (sensor->class == PSC_POWER)
val = val * 1000L;
if (exponent >= 0)
val <<= exponent;
else
val >>= -exponent;
if (sign)
val = -val;
return val;
}
/*
* Convert linear sensor values to milli- or micro-units
* depending on sensor type.
@ -741,6 +832,9 @@ static s64 pmbus_reg2data(struct pmbus_data *data, struct pmbus_sensor *sensor)
case vid:
val = pmbus_reg2data_vid(data, sensor);
break;
case ieee754:
val = pmbus_reg2data_ieee754(data, sensor);
break;
case linear:
default:
val = pmbus_reg2data_linear(data, sensor);
@ -749,8 +843,72 @@ static s64 pmbus_reg2data(struct pmbus_data *data, struct pmbus_sensor *sensor)
return val;
}
#define MAX_MANTISSA (1023 * 1000)
#define MIN_MANTISSA (511 * 1000)
#define MAX_IEEE_MANTISSA (0x7ff * 1000)
#define MIN_IEEE_MANTISSA (0x400 * 1000)
static u16 pmbus_data2reg_ieee754(struct pmbus_data *data,
struct pmbus_sensor *sensor, long val)
{
u16 exponent = (15 + 10);
long mantissa;
u16 sign = 0;
/* simple case */
if (val == 0)
return 0;
if (val < 0) {
sign = 0x8000;
val = -val;
}
/* Power is in uW. Convert to mW before converting. */
if (sensor->class == PSC_POWER)
val = DIV_ROUND_CLOSEST(val, 1000L);
/*
* For simplicity, convert fan data to milli-units
* before calculating the exponent.
*/
if (sensor->class == PSC_FAN)
val = val * 1000;
/* Reduce large mantissa until it fits into 10 bit */
while (val > MAX_IEEE_MANTISSA && exponent < 30) {
exponent++;
val >>= 1;
}
/*
* Increase small mantissa to generate valid 'normal'
* number
*/
while (val < MIN_IEEE_MANTISSA && exponent > 1) {
exponent--;
val <<= 1;
}
/* Convert mantissa from milli-units to units */
mantissa = DIV_ROUND_CLOSEST(val, 1000);
/*
* Ensure that the resulting number is within range.
* Valid range is 0x400..0x7ff, where bit 10 reflects
* the implied high bit in normalized ieee754 numbers.
* Set the range to 0x400..0x7ff to reflect this.
* The upper bit is then removed by the mask against
* 0x3ff in the final assignment.
*/
if (mantissa > 0x7ff)
mantissa = 0x7ff;
else if (mantissa < 0x400)
mantissa = 0x400;
/* Convert to sign, 5 bit exponent, 10 bit mantissa */
return sign | (mantissa & 0x3ff) | ((exponent << 10) & 0x7c00);
}
#define MAX_LIN_MANTISSA (1023 * 1000)
#define MIN_LIN_MANTISSA (511 * 1000)
static u16 pmbus_data2reg_linear(struct pmbus_data *data,
struct pmbus_sensor *sensor, s64 val)
@ -796,12 +954,12 @@ static u16 pmbus_data2reg_linear(struct pmbus_data *data,
val = val * 1000LL;
/* Reduce large mantissa until it fits into 10 bit */
while (val >= MAX_MANTISSA && exponent < 15) {
while (val >= MAX_LIN_MANTISSA && exponent < 15) {
exponent++;
val >>= 1;
}
/* Increase small mantissa to improve precision */
while (val < MIN_MANTISSA && exponent > -15) {
while (val < MIN_LIN_MANTISSA && exponent > -15) {
exponent--;
val <<= 1;
}
@ -875,6 +1033,9 @@ static u16 pmbus_data2reg(struct pmbus_data *data,
case vid:
regval = pmbus_data2reg_vid(data, sensor, val);
break;
case ieee754:
regval = pmbus_data2reg_ieee754(data, sensor, val);
break;
case linear:
default:
regval = pmbus_data2reg_linear(data, sensor, val);
@ -2369,6 +2530,10 @@ static int pmbus_identify_common(struct i2c_client *client,
if (data->info->format[PSC_VOLTAGE_OUT] != direct)
return -ENODEV;
break;
case 3: /* ieee 754 half precision */
if (data->info->format[PSC_VOLTAGE_OUT] != ieee754)
return -ENODEV;
break;
default:
return -ENODEV;
}
@ -2388,6 +2553,42 @@ static int pmbus_read_status_word(struct i2c_client *client, int page)
return _pmbus_read_word_data(client, page, 0xff, PMBUS_STATUS_WORD);
}
/* PEC attribute support */
static ssize_t pec_show(struct device *dev, struct device_attribute *dummy,
char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
return sysfs_emit(buf, "%d\n", !!(client->flags & I2C_CLIENT_PEC));
}
static ssize_t pec_store(struct device *dev, struct device_attribute *dummy,
const char *buf, size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
bool enable;
int err;
err = kstrtobool(buf, &enable);
if (err < 0)
return err;
if (enable)
client->flags |= I2C_CLIENT_PEC;
else
client->flags &= ~I2C_CLIENT_PEC;
return count;
}
static DEVICE_ATTR_RW(pec);
static void pmbus_remove_pec(void *dev)
{
device_remove_file(dev, &dev_attr_pec);
}
static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data,
struct pmbus_driver_info *info)
{
@ -2474,6 +2675,20 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data,
return ret;
}
if (client->flags & I2C_CLIENT_PEC) {
/*
* If I2C_CLIENT_PEC is set here, both the I2C adapter and the
* chip support PEC. Add 'pec' attribute to client device to let
* the user control it.
*/
ret = device_create_file(dev, &dev_attr_pec);
if (ret)
return ret;
ret = devm_add_action_or_reset(dev, pmbus_remove_pec, dev);
if (ret)
return ret;
}
return 0;
}
@ -2636,6 +2851,58 @@ static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned
return 0;
}
static int pmbus_regulator_get_low_margin(struct i2c_client *client, int page)
{
struct pmbus_data *data = i2c_get_clientdata(client);
struct pmbus_sensor s = {
.page = page,
.class = PSC_VOLTAGE_OUT,
.convert = true,
.data = -1,
};
if (!data->vout_low[page]) {
if (pmbus_check_word_register(client, page, PMBUS_MFR_VOUT_MIN))
s.data = _pmbus_read_word_data(client, page, 0xff,
PMBUS_MFR_VOUT_MIN);
if (s.data < 0) {
s.data = _pmbus_read_word_data(client, page, 0xff,
PMBUS_VOUT_MARGIN_LOW);
if (s.data < 0)
return s.data;
}
data->vout_low[page] = pmbus_reg2data(data, &s);
}
return data->vout_low[page];
}
static int pmbus_regulator_get_high_margin(struct i2c_client *client, int page)
{
struct pmbus_data *data = i2c_get_clientdata(client);
struct pmbus_sensor s = {
.page = page,
.class = PSC_VOLTAGE_OUT,
.convert = true,
.data = -1,
};
if (!data->vout_high[page]) {
if (pmbus_check_word_register(client, page, PMBUS_MFR_VOUT_MAX))
s.data = _pmbus_read_word_data(client, page, 0xff,
PMBUS_MFR_VOUT_MAX);
if (s.data < 0) {
s.data = _pmbus_read_word_data(client, page, 0xff,
PMBUS_VOUT_MARGIN_HIGH);
if (s.data < 0)
return s.data;
}
data->vout_high[page] = pmbus_reg2data(data, &s);
}
return data->vout_high[page];
}
static int pmbus_regulator_get_voltage(struct regulator_dev *rdev)
{
struct device *dev = rdev_get_dev(rdev);
@ -2671,24 +2938,13 @@ static int pmbus_regulator_set_voltage(struct regulator_dev *rdev, int min_uv,
*selector = 0;
if (pmbus_check_word_register(client, s.page, PMBUS_MFR_VOUT_MIN))
s.data = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_MFR_VOUT_MIN);
if (s.data < 0) {
s.data = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_VOUT_MARGIN_LOW);
if (s.data < 0)
return s.data;
}
low = pmbus_reg2data(data, &s);
low = pmbus_regulator_get_low_margin(client, s.page);
if (low < 0)
return low;
s.data = -1;
if (pmbus_check_word_register(client, s.page, PMBUS_MFR_VOUT_MAX))
s.data = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_MFR_VOUT_MAX);
if (s.data < 0) {
s.data = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_VOUT_MARGIN_HIGH);
if (s.data < 0)
return s.data;
}
high = pmbus_reg2data(data, &s);
high = pmbus_regulator_get_high_margin(client, s.page);
if (high < 0)
return high;
/* Make sure we are within margins */
if (low > val)
@ -2701,6 +2957,35 @@ static int pmbus_regulator_set_voltage(struct regulator_dev *rdev, int min_uv,
return _pmbus_write_word_data(client, s.page, PMBUS_VOUT_COMMAND, (u16)val);
}
static int pmbus_regulator_list_voltage(struct regulator_dev *rdev,
unsigned int selector)
{
struct device *dev = rdev_get_dev(rdev);
struct i2c_client *client = to_i2c_client(dev->parent);
int val, low, high;
if (selector >= rdev->desc->n_voltages ||
selector < rdev->desc->linear_min_sel)
return -EINVAL;
selector -= rdev->desc->linear_min_sel;
val = DIV_ROUND_CLOSEST(rdev->desc->min_uV +
(rdev->desc->uV_step * selector), 1000); /* convert to mV */
low = pmbus_regulator_get_low_margin(client, rdev_get_id(rdev));
if (low < 0)
return low;
high = pmbus_regulator_get_high_margin(client, rdev_get_id(rdev));
if (high < 0)
return high;
if (val >= low && val <= high)
return val * 1000; /* unit is uV */
return 0;
}
const struct regulator_ops pmbus_regulator_ops = {
.enable = pmbus_regulator_enable,
.disable = pmbus_regulator_disable,
@ -2708,6 +2993,7 @@ const struct regulator_ops pmbus_regulator_ops = {
.get_error_flags = pmbus_regulator_get_error_flags,
.get_voltage = pmbus_regulator_get_voltage,
.set_voltage = pmbus_regulator_set_voltage,
.list_voltage = pmbus_regulator_list_voltage,
};
EXPORT_SYMBOL_NS_GPL(pmbus_regulator_ops, PMBUS);
@ -2782,41 +3068,33 @@ static int pmbus_debugfs_get_status(void *data, u64 *val)
DEFINE_DEBUGFS_ATTRIBUTE(pmbus_debugfs_ops_status, pmbus_debugfs_get_status,
NULL, "0x%04llx\n");
static int pmbus_debugfs_get_pec(void *data, u64 *val)
{
struct i2c_client *client = data;
*val = !!(client->flags & I2C_CLIENT_PEC);
return 0;
}
static int pmbus_debugfs_set_pec(void *data, u64 val)
static ssize_t pmbus_debugfs_mfr_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
int rc;
struct i2c_client *client = data;
struct pmbus_debugfs_entry *entry = file->private_data;
char data[I2C_SMBUS_BLOCK_MAX + 2] = { 0 };
if (!val) {
client->flags &= ~I2C_CLIENT_PEC;
return 0;
}
if (val != 1)
return -EINVAL;
rc = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY);
rc = pmbus_read_block_data(entry->client, entry->page, entry->reg,
data);
if (rc < 0)
return rc;
if (!(rc & PB_CAPABILITY_ERROR_CHECK))
return -EOPNOTSUPP;
/* Add newline at the end of a read data */
data[rc] = '\n';
client->flags |= I2C_CLIENT_PEC;
/* Include newline into the length */
rc += 1;
return 0;
return simple_read_from_buffer(buf, count, ppos, data, rc);
}
DEFINE_DEBUGFS_ATTRIBUTE(pmbus_debugfs_ops_pec, pmbus_debugfs_get_pec,
pmbus_debugfs_set_pec, "%llu\n");
static const struct file_operations pmbus_debugfs_ops_mfr = {
.llseek = noop_llseek,
.read = pmbus_debugfs_mfr_read,
.write = NULL,
.open = simple_open,
};
static void pmbus_remove_debugfs(void *data)
{
@ -2846,16 +3124,80 @@ static int pmbus_init_debugfs(struct i2c_client *client,
return -ENODEV;
}
/* Allocate the max possible entries we need. */
/*
* Allocate the max possible entries we need.
* 6 entries device-specific
* 10 entries page-specific
*/
entries = devm_kcalloc(data->dev,
data->info->pages * 10, sizeof(*entries),
6 + data->info->pages * 10, sizeof(*entries),
GFP_KERNEL);
if (!entries)
return -ENOMEM;
debugfs_create_file("pec", 0664, data->debugfs, client,
&pmbus_debugfs_ops_pec);
/*
* Add device-specific entries.
* Please note that the PMBUS standard allows all registers to be
* page-specific.
* To reduce the number of debugfs entries for devices with many pages
* assume that values of the following registers are the same for all
* pages and report values only for page 0.
*/
if (pmbus_check_block_register(client, 0, PMBUS_MFR_ID)) {
entries[idx].client = client;
entries[idx].page = 0;
entries[idx].reg = PMBUS_MFR_ID;
debugfs_create_file("mfr_id", 0444, data->debugfs,
&entries[idx++],
&pmbus_debugfs_ops_mfr);
}
if (pmbus_check_block_register(client, 0, PMBUS_MFR_MODEL)) {
entries[idx].client = client;
entries[idx].page = 0;
entries[idx].reg = PMBUS_MFR_MODEL;
debugfs_create_file("mfr_model", 0444, data->debugfs,
&entries[idx++],
&pmbus_debugfs_ops_mfr);
}
if (pmbus_check_block_register(client, 0, PMBUS_MFR_REVISION)) {
entries[idx].client = client;
entries[idx].page = 0;
entries[idx].reg = PMBUS_MFR_REVISION;
debugfs_create_file("mfr_revision", 0444, data->debugfs,
&entries[idx++],
&pmbus_debugfs_ops_mfr);
}
if (pmbus_check_block_register(client, 0, PMBUS_MFR_LOCATION)) {
entries[idx].client = client;
entries[idx].page = 0;
entries[idx].reg = PMBUS_MFR_LOCATION;
debugfs_create_file("mfr_location", 0444, data->debugfs,
&entries[idx++],
&pmbus_debugfs_ops_mfr);
}
if (pmbus_check_block_register(client, 0, PMBUS_MFR_DATE)) {
entries[idx].client = client;
entries[idx].page = 0;
entries[idx].reg = PMBUS_MFR_DATE;
debugfs_create_file("mfr_date", 0444, data->debugfs,
&entries[idx++],
&pmbus_debugfs_ops_mfr);
}
if (pmbus_check_block_register(client, 0, PMBUS_MFR_SERIAL)) {
entries[idx].client = client;
entries[idx].page = 0;
entries[idx].reg = PMBUS_MFR_SERIAL;
debugfs_create_file("mfr_serial", 0444, data->debugfs,
&entries[idx++],
&pmbus_debugfs_ops_mfr);
}
/* Add page specific entries */
for (i = 0; i < data->info->pages; ++i) {
/* Check accessibility of status register if it's not page 0 */
if (!i || pmbus_check_status_register(client, i)) {

View File

@ -523,6 +523,28 @@ static int __init sch56xx_device_add(int address, const char *name)
return PTR_ERR_OR_ZERO(sch56xx_pdev);
}
static const struct dmi_system_id sch56xx_dmi_override_table[] __initconst = {
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
DMI_MATCH(DMI_PRODUCT_NAME, "CELSIUS W380"),
},
},
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO P710"),
},
},
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO E9900"),
},
},
{ }
};
/* For autoloading only */
static const struct dmi_system_id sch56xx_dmi_table[] __initconst = {
{
@ -543,16 +565,18 @@ static int __init sch56xx_init(void)
if (!dmi_check_system(sch56xx_dmi_table))
return -ENODEV;
/*
* Some machines like the Esprimo P720 and Esprimo C700 have
* onboard devices named " Antiope"/" Theseus" instead of
* "Antiope"/"Theseus", so we need to check for both.
*/
if (!dmi_find_device(DMI_DEV_TYPE_OTHER, "Antiope", NULL) &&
!dmi_find_device(DMI_DEV_TYPE_OTHER, " Antiope", NULL) &&
!dmi_find_device(DMI_DEV_TYPE_OTHER, "Theseus", NULL) &&
!dmi_find_device(DMI_DEV_TYPE_OTHER, " Theseus", NULL))
return -ENODEV;
if (!dmi_check_system(sch56xx_dmi_override_table)) {
/*
* Some machines like the Esprimo P720 and Esprimo C700 have
* onboard devices named " Antiope"/" Theseus" instead of
* "Antiope"/"Theseus", so we need to check for both.
*/
if (!dmi_find_device(DMI_DEV_TYPE_OTHER, "Antiope", NULL) &&
!dmi_find_device(DMI_DEV_TYPE_OTHER, " Antiope", NULL) &&
!dmi_find_device(DMI_DEV_TYPE_OTHER, "Theseus", NULL) &&
!dmi_find_device(DMI_DEV_TYPE_OTHER, " Theseus", NULL))
return -ENODEV;
}
}
/*

View File

@ -1020,25 +1020,20 @@ err_release_reg:
static int sht15_remove(struct platform_device *pdev)
{
struct sht15_data *data = platform_get_drvdata(pdev);
int ret;
/*
* Make sure any reads from the device are done and
* prevent new ones beginning
*/
mutex_lock(&data->read_lock);
if (sht15_soft_reset(data)) {
mutex_unlock(&data->read_lock);
return -EFAULT;
}
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&pdev->dev.kobj, &sht15_attr_group);
ret = sht15_soft_reset(data);
if (ret)
dev_err(&pdev->dev, "Failed to reset device (%pe)\n", ERR_PTR(ret));
if (!IS_ERR(data->reg)) {
regulator_unregister_notifier(data->reg, &data->nb);
regulator_disable(data->reg);
}
mutex_unlock(&data->read_lock);
return 0;
}

View File

@ -140,7 +140,8 @@ static int tps23861_read_temp(struct tps23861_data *data, long *val)
static int tps23861_read_voltage(struct tps23861_data *data, int channel,
long *val)
{
unsigned int regval;
__le16 regval;
long raw_val;
int err;
if (channel < TPS23861_NUM_PORTS) {
@ -155,7 +156,8 @@ static int tps23861_read_voltage(struct tps23861_data *data, int channel,
if (err < 0)
return err;
*val = (FIELD_GET(VOLTAGE_CURRENT_MASK, regval) * VOLTAGE_LSB) / 1000;
raw_val = le16_to_cpu(regval);
*val = (FIELD_GET(VOLTAGE_CURRENT_MASK, raw_val) * VOLTAGE_LSB) / 1000;
return 0;
}
@ -163,8 +165,9 @@ static int tps23861_read_voltage(struct tps23861_data *data, int channel,
static int tps23861_read_current(struct tps23861_data *data, int channel,
long *val)
{
unsigned int current_lsb;
unsigned int regval;
long raw_val, current_lsb;
__le16 regval;
int err;
if (data->shunt_resistor == SHUNT_RESISTOR_DEFAULT)
@ -178,7 +181,8 @@ static int tps23861_read_current(struct tps23861_data *data, int channel,
if (err < 0)
return err;
*val = (FIELD_GET(VOLTAGE_CURRENT_MASK, regval) * current_lsb) / 1000000;
raw_val = le16_to_cpu(regval);
*val = (FIELD_GET(VOLTAGE_CURRENT_MASK, raw_val) * current_lsb) / 1000000;
return 0;
}