mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-24 12:44:11 +08:00
hwmon updates for v5.13
The most notable change is the removal of the amd_energy driver. It was rendered all but unusable by making its attributes privileged-only to work around a security issue. A suggested remedy was rejected by AMD, so the only real solution was to remove the driver. For the future, we'll have to make sure that no privileged-access-only drivers are accepted into the hwmon subsystem in the first place. The hwmon ABI document was updated accordingly. Other changes: PMBus drivers: - Added driver for MAX15301 - Added driver for BluTek BPA-RS600 - Added driver for fsp-3y PSUs and PDUs - Added driver for Infineon IR36021 - Added driver for ST STPDDC60 - Added support for TI TPS53676 to tps53679 driver - Introduced PMBUS symbol namespace This was made necessary by a suggestion to use its exported functions from outside the hwmon subsystem. - Minor improvements and bug fixes New drivers: - Driver for NZXT Kraken X42/X52/X62/X72 Driver enhancements: - Added support for Intel D5005 to intel-m10-bmc-hwmon driver - Added support for NCT6686D to nct6683 driver Other: - Converted sch5627 and amd9240 drivers to hwmon_device_register_with_info() - Added support for fan drawers capability and present registers to mlxreg-fan driver - Added Dell Latitude E7440 to fan control whitelist in dell-smm driver - Replaced snprintf in show functions with sysfs_emit Done with coccinelle script for all drivers to preempt endless per-driver submissions of the same change. - Use kobj_to_dev() Another coccinelle based change to preempt endless per-driver submissions of the same change. - Various minor fixes and improvements -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEiHPvMQj9QTOCiqgVyx8mb86fmYEFAmCG+0QACgkQyx8mb86f mYEpXw/8Dv4d+oQAPpxt1MrcGIORnUPWuby92UBtgJWnf7x1nE3NTN7Z1DYDjO6F +oLSHtHZCUa9x7i7dbnEy5W+HOIiR8RQMOCZB6dDsZUFdUwbS683X/DrWHzJByPL Vm9m2KKWyarmiE6Ke5oKko9KICx/Q4r640sPju/exhu2qd9RA1A8yKmSmOb6+33m Yhu+nZjh2Qpc1KoDSI/lXTmgzGpw3FdVUBMiGXMuFhihjWnnSiZbm1LmpsChkMCl HGTOt2yZJorKbtyeclzgbVgrXbcP8/jwvSMfbc/0AvMHB/rsMSJWXqCLngvzYO9t 0pcsxNDqzHSPMq0aBBTSylxpnWxCUwic1luXcA5zwzOhtsndg6OzGnYAyCKEx4Fu TbJClzKFE89qIOkGmd20lGp8FR0ZtpJsCGOPk0epyRzfHQYYtGPw65wssPDeYZXD RvY7YQGNh5fnRmjlSc6lngp+HLWTuirjJkgD8tYJyv5lUixBNAY4YRl+/wb8lju0 5Q5lZWSfhu/Gn2qLW0iqBeevgxP6PIUMmUnYQ4BnqYlbI8ox3KrZvATPf9QZGrV/ FpcsST0bi4wjOKZKvdKJidieK+J3KpA26PeOlnyD3nhRHA4kEoU92KwsccPZg7hz XY8MFXqy3+scOVXAb0ul1XvLn+PkuLazh05x5zfg2CQcw1gc0+E= =alEZ -----END PGP SIGNATURE----- Merge tag 'hwmon-for-v5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging Pull hwmon updates from Guenter Roeck: "The most notable change is the removal of the amd_energy driver. It was rendered all but unusable by making its attributes privileged-only to work around a security issue. A suggested remedy was rejected by AMD, so the only real solution was to remove the driver. For the future, we'll have to make sure that no privileged-access-only drivers are accepted into the hwmon subsystem in the first place. The hwmon ABI document was updated accordingly. Other changes: PMBus drivers: - Added driver for MAX15301 - Added driver for BluTek BPA-RS600 - Added driver for fsp-3y PSUs and PDUs - Added driver for Infineon IR36021 - Added driver for ST STPDDC60 - Added support for TI TPS53676 to tps53679 driver - Introduced PMBUS symbol namespace. This was made necessary by a suggestion to use its exported functions from outside the hwmon subsystem. - Minor improvements and bug fixes New drivers: - Driver for NZXT Kraken X42/X52/X62/X72 Driver enhancements: - Added support for Intel D5005 to intel-m10-bmc-hwmon driver - Added support for NCT6686D to nct6683 driver Other: - Converted sch5627 and amd9240 drivers to hwmon_device_register_with_info() - Added support for fan drawers capability and present registers to mlxreg-fan driver - Added Dell Latitude E7440 to fan control whitelist in dell-smm driver - Replaced snprintf in show functions with sysfs_emit. Done with coccinelle script for all drivers to preempt endless per-driver submissions of the same change. - Use kobj_to_dev(). Another coccinelle based change to preempt endless per-driver submissions of the same change. - Various minor fixes and improvements" * tag 'hwmon-for-v5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (38 commits) hwmon: Remove amd_energy driver hwmon: Clarify scope of attribute access hwmon: (pmbus) Introduce PMBUS symbol namespace hwmon: (pmbus) Add pmbus driver for MAX15301 hwmon: (sch5627) Remove unnecessary error path hwmon: (sch5627) Use devres function hwmon: (pmbus/pxe1610) don't bail out when not all pages are active hwmon: Add driver for fsp-3y PSUs and PDUs hwmon: (intel-m10-bmc-hwmon) add sensor support of Intel D5005 card hwmon: (sch5627) Split sch5627_update_device() hwmon: (sch5627) Convert to hwmon_device_register_with_info() hwmon: (nct6683) remove useless function hwmon: (dell-smm) Add Dell Latitude E7440 to fan control whitelist MAINTAINERS: Add keyword pattern for hwmon registration functions hwmon: (mlxreg-fan) Add support for fan drawers capability and present registers hwmon: (pmbus/tps53679) Add support for TI TPS53676 dt-bindings: Add trivial device entry for TPS53676 hwmon: (ftsteutates) Rudimentary typo fixes hwmon: (pmbus) Add driver for BluTek BPA-RS600 dt-bindings: Add vendor prefix and trivial device for BluTek BPA-RS600 ...
This commit is contained in:
commit
47080f2286
@ -50,6 +50,8 @@ properties:
|
||||
- atmel,atsha204a
|
||||
# i2c h/w elliptic curve crypto module
|
||||
- atmel,atecc508a
|
||||
# BPA-RS600: Power Supply
|
||||
- blutek,bpa-rs600
|
||||
# Bosch Sensortec pressure, temperature, humididty and VOC sensor
|
||||
- bosch,bme680
|
||||
# CM32181: Ambient Light Sensor
|
||||
@ -102,6 +104,8 @@ properties:
|
||||
- mps,mp2975
|
||||
# G751: Digital Temperature Sensor and Thermal Watchdog with Two-Wire Interface
|
||||
- gmt,g751
|
||||
# Infineon IR36021 digital POL buck controller
|
||||
- infineon,ir36021
|
||||
# Infineon IR38064 Voltage Regulator
|
||||
- infineon,ir38064
|
||||
# Infineon SLB9635 (Soft-) I2C TPM (old protocol, max 100khz)
|
||||
@ -288,6 +292,8 @@ properties:
|
||||
- ti,tmp103
|
||||
# Digital Temperature Sensor
|
||||
- ti,tmp275
|
||||
# TI Dual channel DCAP+ multiphase controller TPS53676 with AVSBus
|
||||
- ti,tps53676
|
||||
# TI Dual channel DCAP+ multiphase controller TPS53679
|
||||
- ti,tps53679
|
||||
# TI Dual channel DCAP+ multiphase controller TPS53688
|
||||
|
@ -171,6 +171,8 @@ patternProperties:
|
||||
description: Beckhoff Automation GmbH & Co. KG
|
||||
"^bitmain,.*":
|
||||
description: Bitmain Technologies
|
||||
"^blutek,.*":
|
||||
description: BluTek Power
|
||||
"^boe,.*":
|
||||
description: BOE Technology Group Co., Ltd.
|
||||
"^bosch,.*":
|
||||
|
@ -1,119 +0,0 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
Kernel driver amd_energy
|
||||
==========================
|
||||
|
||||
Supported chips:
|
||||
|
||||
* AMD Family 17h Processors: Model 30h
|
||||
|
||||
* AMD Family 19h Processors: Model 01h
|
||||
|
||||
Prefix: 'amd_energy'
|
||||
|
||||
Addresses used: RAPL MSRs
|
||||
|
||||
Datasheets:
|
||||
|
||||
- Processor Programming Reference (PPR) for AMD Family 17h Model 01h, Revision B1 Processors
|
||||
|
||||
https://developer.amd.com/wp-content/resources/55570-B1_PUB.zip
|
||||
|
||||
- Preliminary Processor Programming Reference (PPR) for AMD Family 17h Model 31h, Revision B0 Processors
|
||||
|
||||
https://developer.amd.com/wp-content/resources/56176_ppr_Family_17h_Model_71h_B0_pub_Rev_3.06.zip
|
||||
|
||||
Author: Naveen Krishna Chatradhi <nchatrad@amd.com>
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The Energy driver exposes the energy counters that are
|
||||
reported via the Running Average Power Limit (RAPL)
|
||||
Model-specific Registers (MSRs) via the hardware monitor
|
||||
(HWMON) sysfs interface.
|
||||
|
||||
1. Power, Energy and Time Units
|
||||
MSR_RAPL_POWER_UNIT/ C001_0299:
|
||||
shared with all cores in the socket
|
||||
|
||||
2. Energy consumed by each Core
|
||||
MSR_CORE_ENERGY_STATUS/ C001_029A:
|
||||
32-bitRO, Accumulator, core-level power reporting
|
||||
|
||||
3. Energy consumed by Socket
|
||||
MSR_PACKAGE_ENERGY_STATUS/ C001_029B:
|
||||
32-bitRO, Accumulator, socket-level power reporting,
|
||||
shared with all cores in socket
|
||||
|
||||
These registers are updated every 1ms and cleared on
|
||||
reset of the system.
|
||||
|
||||
Note: If SMT is enabled, Linux enumerates all threads as cpus.
|
||||
Since, the energy status registers are accessed at core level,
|
||||
reading those registers from the sibling threads would result
|
||||
in duplicate values. Hence, energy counter entries are not
|
||||
populated for the siblings.
|
||||
|
||||
Energy Caluclation
|
||||
------------------
|
||||
|
||||
Energy information (in Joules) is based on the multiplier,
|
||||
1/2^ESU; where ESU is an unsigned integer read from
|
||||
MSR_RAPL_POWER_UNIT register. Default value is 10000b,
|
||||
indicating energy status unit is 15.3 micro-Joules increment.
|
||||
|
||||
Reported values are scaled as per the formula
|
||||
|
||||
scaled value = ((1/2^ESU) * (Raw value) * 1000000UL) in uJoules
|
||||
|
||||
Users calculate power for a given domain by calculating
|
||||
dEnergy/dTime for that domain.
|
||||
|
||||
Energy accumulation
|
||||
--------------------------
|
||||
|
||||
Current, Socket energy status register is 32bit, assuming a 240W
|
||||
2P system, the register would wrap around in
|
||||
|
||||
2^32*15.3 e-6/240 * 2 = 547.60833024 secs to wrap(~9 mins)
|
||||
|
||||
The Core energy register may wrap around after several days.
|
||||
|
||||
To improve the wrap around time, a kernel thread is implemented
|
||||
to accumulate the socket energy counters and one core energy counter
|
||||
per run to a respective 64-bit counter. The kernel thread starts
|
||||
running during probe, wakes up every 100secs and stops running
|
||||
when driver is removed.
|
||||
|
||||
Frequency of the accumulator thread is set during the probe
|
||||
based on the chosen energy unit resolution. For example
|
||||
A. fine grain (1.625 micro J)
|
||||
B. course grain (0.125 milli J)
|
||||
|
||||
A socket and core energy read would return the current register
|
||||
value added to the respective energy accumulator.
|
||||
|
||||
Sysfs attributes
|
||||
----------------
|
||||
|
||||
=============== ======== =====================================
|
||||
Attribute Label Description
|
||||
=============== ======== =====================================
|
||||
|
||||
* For index N between [1] and [nr_cpus]
|
||||
|
||||
=============== ======== ======================================
|
||||
energy[N]_input EcoreX Core Energy X = [0] to [nr_cpus - 1]
|
||||
Measured input core energy
|
||||
=============== ======== ======================================
|
||||
|
||||
* For N between [nr_cpus] and [nr_cpus + nr_socks]
|
||||
|
||||
=============== ======== ======================================
|
||||
energy[N]_input EsocketX Socket Energy X = [0] to [nr_socks -1]
|
||||
Measured input socket energy
|
||||
=============== ======== ======================================
|
||||
|
||||
Note: To address CVE-2020-12912, the visibility of the energy[N]_input
|
||||
attributes is restricted to owner and groups only.
|
74
Documentation/hwmon/bpa-rs600.rst
Normal file
74
Documentation/hwmon/bpa-rs600.rst
Normal file
@ -0,0 +1,74 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
Kernel driver bpa-rs600
|
||||
=======================
|
||||
|
||||
Supported chips:
|
||||
|
||||
* BPA-RS600-120
|
||||
|
||||
Datasheet: Publicly available at the BluTek website
|
||||
http://blutekpower.com/wp-content/uploads/2019/01/BPA-RS600-120-07-19-2018.pdf
|
||||
|
||||
Authors:
|
||||
- Chris Packham <chris.packham@alliedtelesis.co.nz>
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The BPA-RS600 is a compact 600W removable power supply module.
|
||||
|
||||
Usage Notes
|
||||
-----------
|
||||
|
||||
This driver does not probe for PMBus devices. You will have to instantiate
|
||||
devices explicitly.
|
||||
|
||||
Sysfs attributes
|
||||
----------------
|
||||
|
||||
======================= ============================================
|
||||
curr1_label "iin"
|
||||
curr1_input Measured input current
|
||||
curr1_max Maximum input current
|
||||
curr1_max_alarm Input current high alarm
|
||||
|
||||
curr2_label "iout1"
|
||||
curr2_input Measured output current
|
||||
curr2_max Maximum output current
|
||||
curr2_max_alarm Output current high alarm
|
||||
|
||||
fan1_input Measured fan speed
|
||||
fan1_alarm Fan warning
|
||||
fan1_fault Fan fault
|
||||
|
||||
in1_label "vin"
|
||||
in1_input Measured input voltage
|
||||
in1_max Maximum input voltage
|
||||
in1_max_alarm Input voltage high alarm
|
||||
in1_min Minimum input voltage
|
||||
in1_min_alarm Input voltage low alarm
|
||||
|
||||
in2_label "vout1"
|
||||
in2_input Measured output voltage
|
||||
in2_max Maximum output voltage
|
||||
in2_max_alarm Output voltage high alarm
|
||||
in2_min Maximum output voltage
|
||||
in2_min_alarm Output voltage low alarm
|
||||
|
||||
power1_label "pin"
|
||||
power1_input Measured input power
|
||||
power1_alarm Input power alarm
|
||||
power1_max Maximum input power
|
||||
|
||||
power2_label "pout1"
|
||||
power2_input Measured output power
|
||||
power2_max Maximum output power
|
||||
power2_max_alarm Output power high alarm
|
||||
|
||||
temp1_input Measured temperature around input connector
|
||||
temp1_alarm Temperature alarm
|
||||
|
||||
temp2_input Measured temperature around output connector
|
||||
temp2_alarm Temperature alarm
|
||||
======================= ============================================
|
@ -47,19 +47,30 @@ Sysfs entries
|
||||
======================= ========================================================
|
||||
curr1_input Total current usage
|
||||
curr2_input Current on the 12v psu rail
|
||||
curr2_crit Current max critical value on the 12v psu rail
|
||||
curr3_input Current on the 5v psu rail
|
||||
curr3_crit Current max critical value on the 5v psu rail
|
||||
curr4_input Current on the 3.3v psu rail
|
||||
curr4_crit Current max critical value on the 3.3v psu rail
|
||||
fan1_input RPM of psu fan
|
||||
in0_input Voltage of the psu ac input
|
||||
in1_input Voltage of the 12v psu rail
|
||||
in1_crit Voltage max critical value on the 12v psu rail
|
||||
in1_lcrit Voltage min critical value on the 12v psu rail
|
||||
in2_input Voltage of the 5v psu rail
|
||||
in3_input Voltage of the 3.3 psu rail
|
||||
in2_crit Voltage max critical value on the 5v psu rail
|
||||
in2_lcrit Voltage min critical value on the 5v psu rail
|
||||
in3_input Voltage of the 3.3v psu rail
|
||||
in3_crit Voltage max critical value on the 3.3v psu rail
|
||||
in3_lcrit Voltage min critical value on the 3.3v psu rail
|
||||
power1_input Total power usage
|
||||
power2_input Power usage of the 12v psu rail
|
||||
power3_input Power usage of the 5v psu rail
|
||||
power4_input Power usage of the 3.3v psu rail
|
||||
temp1_input Temperature of the psu vrm component
|
||||
temp1_crit Temperature max cirtical value of the psu vrm component
|
||||
temp2_input Temperature of the psu case
|
||||
temp2_crit Temperature max critical value of psu case
|
||||
======================= ========================================================
|
||||
|
||||
Usage Notes
|
||||
|
28
Documentation/hwmon/fsp-3y.rst
Normal file
28
Documentation/hwmon/fsp-3y.rst
Normal file
@ -0,0 +1,28 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
Kernel driver fsp3y
|
||||
======================
|
||||
Supported devices:
|
||||
* 3Y POWER YH-5151E
|
||||
* 3Y POWER YM-2151E
|
||||
|
||||
Author: Václav Kubernát <kubernat@cesnet.cz>
|
||||
|
||||
Description
|
||||
-----------
|
||||
This driver implements limited support for two 3Y POWER devices.
|
||||
|
||||
Sysfs entries
|
||||
-------------
|
||||
* in1_input input voltage
|
||||
* in2_input 12V output voltage
|
||||
* in3_input 5V output voltage
|
||||
* curr1_input input current
|
||||
* curr2_input 12V output current
|
||||
* curr3_input 5V output current
|
||||
* fan1_input fan rpm
|
||||
* temp1_input temperature 1
|
||||
* temp2_input temperature 2
|
||||
* temp3_input temperature 3
|
||||
* power1_input input power
|
||||
* power2_input output power
|
@ -39,12 +39,12 @@ Hardware Monitoring Kernel Drivers
|
||||
adt7475
|
||||
aht10
|
||||
amc6821
|
||||
amd_energy
|
||||
asb100
|
||||
asc7621
|
||||
aspeed-pwm-tacho
|
||||
bcm54140
|
||||
bel-pfe
|
||||
bpa-rs600
|
||||
bt1-pvt
|
||||
coretemp
|
||||
corsair-cpro
|
||||
@ -62,6 +62,7 @@ Hardware Monitoring Kernel Drivers
|
||||
f71805f
|
||||
f71882fg
|
||||
fam15h_power
|
||||
fsp-3y
|
||||
ftsteutates
|
||||
g760a
|
||||
g762
|
||||
@ -77,6 +78,7 @@ Hardware Monitoring Kernel Drivers
|
||||
intel-m10-bmc-hwmon
|
||||
ir35221
|
||||
ir38064
|
||||
ir36021
|
||||
isl68137
|
||||
it87
|
||||
jc42
|
||||
@ -112,6 +114,7 @@ Hardware Monitoring Kernel Drivers
|
||||
ltc4260
|
||||
ltc4261
|
||||
max127
|
||||
max15301
|
||||
max16064
|
||||
max16065
|
||||
max1619
|
||||
@ -142,6 +145,7 @@ Hardware Monitoring Kernel Drivers
|
||||
npcm750-pwm-fan
|
||||
nsa320
|
||||
ntc_thermistor
|
||||
nzxt-kraken2
|
||||
occ
|
||||
pc87360
|
||||
pc87427
|
||||
@ -168,6 +172,7 @@ Hardware Monitoring Kernel Drivers
|
||||
smsc47m192
|
||||
smsc47m1
|
||||
sparx5-temp
|
||||
stpddc60
|
||||
tc654
|
||||
tc74
|
||||
thmc50
|
||||
|
63
Documentation/hwmon/ir36021.rst
Normal file
63
Documentation/hwmon/ir36021.rst
Normal file
@ -0,0 +1,63 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
Kernel driver ir36021
|
||||
=====================
|
||||
|
||||
Supported chips:
|
||||
|
||||
* Infineon IR36021
|
||||
|
||||
Prefix: ir36021
|
||||
Addresses scanned: -
|
||||
|
||||
Datasheet: Publicly available at the Infineon website
|
||||
https://www.infineon.com/dgdl/ir36021.pdf?fileId=5546d462533600a4015355d0aa2d1775
|
||||
|
||||
Authors:
|
||||
- Chris Packham <chris.packham@alliedtelesis.co.nz>
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
The IR36021 is a dual‐loop digital multi‐phase buck controller designed for
|
||||
point of load applications.
|
||||
|
||||
Usage Notes
|
||||
-----------
|
||||
|
||||
This driver does not probe for PMBus devices. You will have to instantiate
|
||||
devices explicitly.
|
||||
|
||||
Sysfs attributes
|
||||
----------------
|
||||
|
||||
======================= ===========================
|
||||
curr1_label "iin"
|
||||
curr1_input Measured input current
|
||||
curr1_alarm Input fault alarm
|
||||
|
||||
curr2_label "iout1"
|
||||
curr2_input Measured output current
|
||||
curr2_alarm Output over-current alarm
|
||||
|
||||
in1_label "vin"
|
||||
in1_input Measured input voltage
|
||||
in1_alarm Input under-voltage alarm
|
||||
|
||||
in2_label "vout1"
|
||||
in2_input Measured output voltage
|
||||
in2_alarm Output over-voltage alarm
|
||||
|
||||
power1_label "pin"
|
||||
power1_input Measured input power
|
||||
power1_alarm Input under-voltage alarm
|
||||
|
||||
power2_label "pout1"
|
||||
power2_input Measured output power
|
||||
|
||||
temp1_input Measured temperature
|
||||
temp1_alarm Temperature alarm
|
||||
|
||||
temp2_input Measured other loop temperature
|
||||
temp2_alarm Temperature alarm
|
||||
======================= ===========================
|
87
Documentation/hwmon/max15301.rst
Normal file
87
Documentation/hwmon/max15301.rst
Normal file
@ -0,0 +1,87 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
Kernel driver max15301
|
||||
======================
|
||||
|
||||
Supported chips:
|
||||
|
||||
* Maxim MAX15301
|
||||
|
||||
Prefix: 'max15301', 'bmr461'
|
||||
|
||||
Addresses scanned: -
|
||||
|
||||
Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX15301.pdf
|
||||
|
||||
Author: Erik Rosen <erik.rosen@metormote.com>
|
||||
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
This driver supports hardware monitoring for Maxim MAX15301 controller chip and
|
||||
compatible modules.
|
||||
|
||||
The driver is a client driver to the core PMBus driver. Please see
|
||||
Documentation/hwmon/pmbus.rst and Documentation.hwmon/pmbus-core for details
|
||||
on PMBus client drivers.
|
||||
|
||||
|
||||
Usage Notes
|
||||
-----------
|
||||
|
||||
This driver does not auto-detect devices. You will have to instantiate the
|
||||
devices explicitly. Please see Documentation/i2c/instantiating-devices.rst for
|
||||
details.
|
||||
|
||||
|
||||
Platform data support
|
||||
---------------------
|
||||
|
||||
The driver supports standard PMBus driver platform data.
|
||||
|
||||
|
||||
Module parameters
|
||||
-----------------
|
||||
|
||||
delay
|
||||
-----
|
||||
|
||||
The controller requires a minimum interval between I2C bus accesses.
|
||||
The default interval is set to 100 us. For manual override, the driver
|
||||
provides a writeable module parameter, 'delay', which can be used to
|
||||
set the interval to a value between 0 and 65,535 microseconds.
|
||||
|
||||
|
||||
Sysfs entries
|
||||
-------------
|
||||
|
||||
The following attributes are supported. Limits are read-write; all other
|
||||
attributes are read-only.
|
||||
|
||||
======================= ========================================================
|
||||
in1_label "vin"
|
||||
in1_input Measured input voltage.
|
||||
in1_lcrit Critical minimum input voltage.
|
||||
in1_crit Critical maximum input voltage.
|
||||
in1_lcrit_alarm Input voltage critical low alarm.
|
||||
in1_crit_alarm Input voltage critical high alarm.
|
||||
|
||||
in2_label "vout1"
|
||||
in2_input Measured output voltage.
|
||||
in2_lcrit Critical minimum output Voltage.
|
||||
in2_crit Critical maximum output voltage.
|
||||
in2_lcrit_alarm Critical output voltage critical low alarm.
|
||||
in2_crit_alarm Critical output voltage critical high alarm.
|
||||
|
||||
curr1_label "iout1"
|
||||
curr1_input Measured output current.
|
||||
curr1_crit Critical maximum output current.
|
||||
curr1_crit_alarm Output current critical high alarm.
|
||||
|
||||
temp1_input Measured maximum temperature of all phases.
|
||||
temp1_max Maximum temperature limit.
|
||||
temp1_max_alarm High temperature alarm.
|
||||
temp1_crit Critical maximum temperature limit.
|
||||
temp1_crit_alarm Critical maximum temperature alarm.
|
||||
======================= ========================================================
|
42
Documentation/hwmon/nzxt-kraken2.rst
Normal file
42
Documentation/hwmon/nzxt-kraken2.rst
Normal file
@ -0,0 +1,42 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
Kernel driver nzxt-kraken2
|
||||
==========================
|
||||
|
||||
Supported devices:
|
||||
|
||||
* NZXT Kraken X42
|
||||
* NZXT Kraken X52
|
||||
* NZXT Kraken X62
|
||||
* NZXT Kraken X72
|
||||
|
||||
Author: Jonas Malaco
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
This driver enables hardware monitoring support for NZXT Kraken X42/X52/X62/X72
|
||||
all-in-one CPU liquid coolers. Three sensors are available: fan speed, pump
|
||||
speed and coolant temperature.
|
||||
|
||||
Fan and pump control, while supported by the firmware, are not currently
|
||||
exposed. The addressable RGB LEDs, present in the integrated CPU water block
|
||||
and pump head, are not supported either. But both features can be found in
|
||||
existing user-space tools (e.g. `liquidctl`_).
|
||||
|
||||
.. _liquidctl: https://github.com/liquidctl/liquidctl
|
||||
|
||||
Usage Notes
|
||||
-----------
|
||||
|
||||
As these are USB HIDs, the driver can be loaded automatically by the kernel and
|
||||
supports hot swapping.
|
||||
|
||||
Sysfs entries
|
||||
-------------
|
||||
|
||||
======================= ========================================================
|
||||
fan1_input Fan speed (in rpm)
|
||||
fan2_input Pump speed (in rpm)
|
||||
temp1_input Coolant temperature (in millidegrees Celsius)
|
||||
======================= ========================================================
|
90
Documentation/hwmon/stpddc60.rst
Normal file
90
Documentation/hwmon/stpddc60.rst
Normal file
@ -0,0 +1,90 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
Kernel driver stpddc60
|
||||
======================
|
||||
|
||||
Supported chips:
|
||||
|
||||
* ST STPDDC60
|
||||
|
||||
Prefix: 'stpddc60', 'bmr481'
|
||||
|
||||
Addresses scanned: -
|
||||
|
||||
Datasheet: https://flexpowermodules.com/documents/fpm-techspec-bmr481
|
||||
|
||||
Author: Erik Rosen <erik.rosen@metormote.com>
|
||||
|
||||
|
||||
Description
|
||||
-----------
|
||||
|
||||
This driver supports hardware monitoring for ST STPDDC60 controller chip and
|
||||
compatible modules.
|
||||
|
||||
The driver is a client driver to the core PMBus driver. Please see
|
||||
Documentation/hwmon/pmbus.rst and Documentation.hwmon/pmbus-core for details
|
||||
on PMBus client drivers.
|
||||
|
||||
|
||||
Usage Notes
|
||||
-----------
|
||||
|
||||
This driver does not auto-detect devices. You will have to instantiate the
|
||||
devices explicitly. Please see Documentation/i2c/instantiating-devices.rst for
|
||||
details.
|
||||
|
||||
The vout under- and over-voltage limits are set in relation to the commanded
|
||||
output voltage as a positive or negative offset in the interval 50mV to 400mV
|
||||
in 50mV steps. This means that the absolute values of the limits will change
|
||||
when the commanded output voltage changes. Also, care should be taken when
|
||||
writing to those limits since in the worst case the commanded output voltage
|
||||
could change at the same time as the limit is written to, wich will lead to
|
||||
unpredictable results.
|
||||
|
||||
|
||||
Platform data support
|
||||
---------------------
|
||||
|
||||
The driver supports standard PMBus driver platform data.
|
||||
|
||||
|
||||
Sysfs entries
|
||||
-------------
|
||||
|
||||
The following attributes are supported. Vin, iout, pout and temp limits
|
||||
are read-write; all other attributes are read-only.
|
||||
|
||||
======================= ========================================================
|
||||
in1_label "vin"
|
||||
in1_input Measured input voltage.
|
||||
in1_lcrit Critical minimum input voltage.
|
||||
in1_crit Critical maximum input voltage.
|
||||
in1_lcrit_alarm Input voltage critical low alarm.
|
||||
in1_crit_alarm Input voltage critical high alarm.
|
||||
|
||||
in2_label "vout1"
|
||||
in2_input Measured output voltage.
|
||||
in2_lcrit Critical minimum output voltage.
|
||||
in2_crit Critical maximum output voltage.
|
||||
in2_lcrit_alarm Critical output voltage critical low alarm.
|
||||
in2_crit_alarm Critical output voltage critical high alarm.
|
||||
|
||||
curr1_label "iout1"
|
||||
curr1_input Measured output current.
|
||||
curr1_max Maximum output current.
|
||||
curr1_max_alarm Output current high alarm.
|
||||
curr1_crit Critical maximum output current.
|
||||
curr1_crit_alarm Output current critical high alarm.
|
||||
|
||||
power1_label "pout1"
|
||||
power1_input Measured output power.
|
||||
power1_crit Critical maximum output power.
|
||||
power1_crit_alarm Output power critical high alarm.
|
||||
|
||||
temp1_input Measured maximum temperature of all phases.
|
||||
temp1_max Maximum temperature limit.
|
||||
temp1_max_alarm High temperature alarm.
|
||||
temp1_crit Critical maximum temperature limit.
|
||||
temp1_crit_alarm Critical maximum temperature alarm.
|
||||
======================= ========================================================
|
@ -65,6 +65,14 @@ the desired value must be written, note that strings which are not a number
|
||||
are interpreted as 0! For more on how written strings are interpreted see the
|
||||
"sysfs attribute writes interpretation" section at the end of this file.
|
||||
|
||||
Attribute access
|
||||
----------------
|
||||
|
||||
Hardware monitoring sysfs attributes are displayed by unrestricted userspace
|
||||
applications. For this reason, all standard ABI attributes shall be world
|
||||
readable. Writeable standard ABI attributes shall be writeable only for
|
||||
privileged users.
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
======= ===========================================
|
||||
|
@ -19,6 +19,14 @@ Supported chips:
|
||||
|
||||
Datasheet: https://www.ti.com/lit/gpn/TPS53667
|
||||
|
||||
* Texas Instruments TPS53676
|
||||
|
||||
Prefix: 'tps53676'
|
||||
|
||||
Addresses scanned: -
|
||||
|
||||
Datasheet: https://www.ti.com/lit/gpn/TPS53676
|
||||
|
||||
* Texas Instruments TPS53679
|
||||
|
||||
Prefix: 'tps53679'
|
||||
@ -136,7 +144,7 @@ power1_input Measured input power.
|
||||
power[N]_label "pout[1-2]".
|
||||
|
||||
- TPS53647, TPS53667: N=2
|
||||
- TPS53679, TPS53681, TPS53588: N=2,3
|
||||
- TPS53676, TPS53679, TPS53681, TPS53588: N=2,3
|
||||
|
||||
power[N]_input Measured output power.
|
||||
|
||||
@ -156,10 +164,11 @@ curr[N]_label "iout[1-2]" or "iout1.[0-5]".
|
||||
|
||||
The first digit is the output channel, the second
|
||||
digit is the phase within the channel. Per-phase
|
||||
telemetry supported on TPS53681 only.
|
||||
telemetry supported on TPS53676 and TPS53681 only.
|
||||
|
||||
- TPS53647, TPS53667: N=2
|
||||
- TPS53679, TPS53588: N=2,3
|
||||
- TPS53676: N=2-8
|
||||
- TPS53681: N=2-9
|
||||
|
||||
curr[N]_input Measured output current.
|
||||
|
29
MAINTAINERS
29
MAINTAINERS
@ -880,13 +880,6 @@ S: Supported
|
||||
T: git git://people.freedesktop.org/~agd5f/linux
|
||||
F: drivers/gpu/drm/amd/display/
|
||||
|
||||
AMD ENERGY DRIVER
|
||||
M: Naveen Krishna Chatradhi <nchatrad@amd.com>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/hwmon/amd_energy.rst
|
||||
F: drivers/hwmon/amd_energy.c
|
||||
|
||||
AMD FAM15H PROCESSOR POWER MONITORING DRIVER
|
||||
M: Huang Rui <ray.huang@amd.com>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
@ -7927,6 +7920,7 @@ F: Documentation/hwmon/
|
||||
F: drivers/hwmon/
|
||||
F: include/linux/hwmon*.h
|
||||
F: include/trace/events/hwmon*.h
|
||||
K: (devm_)?hwmon_device_(un)?register(|_with_groups|_with_info)
|
||||
|
||||
HARDWARE RANDOM NUMBER GENERATOR CORE
|
||||
M: Matt Mackall <mpm@selenic.com>
|
||||
@ -10898,6 +10892,13 @@ S: Orphan
|
||||
F: drivers/video/fbdev/matrox/matroxfb_*
|
||||
F: include/uapi/linux/matroxfb.h
|
||||
|
||||
MAX15301 DRIVER
|
||||
M: Daniel Nilsson <daniel.nilsson@flex.com>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/hwmon/max15301.rst
|
||||
F: drivers/hwmon/pmbus/max15301.c
|
||||
|
||||
MAX16065 HARDWARE MONITOR DRIVER
|
||||
M: Guenter Roeck <linux@roeck-us.net>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
@ -13027,6 +13028,13 @@ L: linux-nfc@lists.01.org (moderated for non-subscribers)
|
||||
S: Supported
|
||||
F: drivers/nfc/nxp-nci
|
||||
|
||||
NZXT-KRAKEN2 HARDWARE MONITORING DRIVER
|
||||
M: Jonas Malaco <jonas@protocubo.io>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/hwmon/nzxt-kraken2.rst
|
||||
F: drivers/hwmon/nzxt-kraken2.c
|
||||
|
||||
OBJAGG
|
||||
M: Jiri Pirko <jiri@nvidia.com>
|
||||
L: netdev@vger.kernel.org
|
||||
@ -17068,6 +17076,13 @@ L: linux-i2c@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/i2c/busses/i2c-stm32*
|
||||
|
||||
ST STPDDC60 DRIVER
|
||||
M: Daniel Nilsson <daniel.nilsson@flex.com>
|
||||
L: linux-hwmon@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/hwmon/stpddc60.rst
|
||||
F: drivers/hwmon/pmbus/stpddc60.c
|
||||
|
||||
ST VL53L0X ToF RANGER(I2C) IIO DRIVER
|
||||
M: Song Qiang <songqiang1304521@gmail.com>
|
||||
L: linux-iio@vger.kernel.org
|
||||
|
@ -321,16 +321,6 @@ config SENSORS_FAM15H_POWER
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called fam15h_power.
|
||||
|
||||
config SENSORS_AMD_ENERGY
|
||||
tristate "AMD RAPL MSR based Energy driver"
|
||||
depends on X86
|
||||
help
|
||||
If you say yes here you get support for core and package energy
|
||||
sensors, based on RAPL MSR for AMD family 17h and above CPUs.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called as amd_energy.
|
||||
|
||||
config SENSORS_APPLESMC
|
||||
tristate "Apple SMC (Motion sensor, light sensor, keyboard backlight)"
|
||||
depends on INPUT && X86
|
||||
@ -1492,6 +1482,16 @@ config SENSORS_NSA320
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called nsa320-hwmon.
|
||||
|
||||
config SENSORS_NZXT_KRAKEN2
|
||||
tristate "NZXT Kraken X42/X51/X62/X72 liquid coolers"
|
||||
depends on USB_HID
|
||||
help
|
||||
If you say yes here you get support for hardware monitoring for the
|
||||
NZXT Kraken X42/X52/X62/X72 all-in-one CPU liquid coolers.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called nzxt-kraken2.
|
||||
|
||||
source "drivers/hwmon/occ/Kconfig"
|
||||
|
||||
config SENSORS_PCF8591
|
||||
|
@ -155,6 +155,7 @@ obj-$(CONFIG_SENSORS_NCT7904) += nct7904.o
|
||||
obj-$(CONFIG_SENSORS_NPCM7XX) += npcm750-pwm-fan.o
|
||||
obj-$(CONFIG_SENSORS_NSA320) += nsa320-hwmon.o
|
||||
obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o
|
||||
obj-$(CONFIG_SENSORS_NZXT_KRAKEN2) += nzxt-kraken2.o
|
||||
obj-$(CONFIG_SENSORS_PC87360) += pc87360.o
|
||||
obj-$(CONFIG_SENSORS_PC87427) += pc87427.o
|
||||
obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
|
||||
|
@ -248,7 +248,7 @@ static ssize_t adc128_alarm_show(struct device *dev,
|
||||
static umode_t adc128_is_visible(struct kobject *kobj,
|
||||
struct attribute *attr, int index)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct adc128_data *data = dev_get_drvdata(dev);
|
||||
|
||||
if (index < ADC128_ATTR_NUM_VOLT) {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,379 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
/*
|
||||
* Copyright (C) 2020 Advanced Micro Devices, Inc.
|
||||
*/
|
||||
#include <asm/cpu_device_id.h>
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/processor.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/topology.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#define DRVNAME "amd_energy"
|
||||
|
||||
#define ENERGY_PWR_UNIT_MSR 0xC0010299
|
||||
#define ENERGY_CORE_MSR 0xC001029A
|
||||
#define ENERGY_PKG_MSR 0xC001029B
|
||||
|
||||
#define AMD_ENERGY_UNIT_MASK 0x01F00
|
||||
#define AMD_ENERGY_MASK 0xFFFFFFFF
|
||||
|
||||
struct sensor_accumulator {
|
||||
u64 energy_ctr;
|
||||
u64 prev_value;
|
||||
};
|
||||
|
||||
struct amd_energy_data {
|
||||
struct hwmon_channel_info energy_info;
|
||||
const struct hwmon_channel_info *info[2];
|
||||
struct hwmon_chip_info chip;
|
||||
struct task_struct *wrap_accumulate;
|
||||
/* Lock around the accumulator */
|
||||
struct mutex lock;
|
||||
/* An accumulator for each core and socket */
|
||||
struct sensor_accumulator *accums;
|
||||
unsigned int timeout_ms;
|
||||
/* Energy Status Units */
|
||||
int energy_units;
|
||||
int nr_cpus;
|
||||
int nr_socks;
|
||||
int core_id;
|
||||
char (*label)[10];
|
||||
};
|
||||
|
||||
static int amd_energy_read_labels(struct device *dev,
|
||||
enum hwmon_sensor_types type,
|
||||
u32 attr, int channel,
|
||||
const char **str)
|
||||
{
|
||||
struct amd_energy_data *data = dev_get_drvdata(dev);
|
||||
|
||||
*str = data->label[channel];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void get_energy_units(struct amd_energy_data *data)
|
||||
{
|
||||
u64 rapl_units;
|
||||
|
||||
rdmsrl_safe(ENERGY_PWR_UNIT_MSR, &rapl_units);
|
||||
data->energy_units = (rapl_units & AMD_ENERGY_UNIT_MASK) >> 8;
|
||||
}
|
||||
|
||||
static void accumulate_delta(struct amd_energy_data *data,
|
||||
int channel, int cpu, u32 reg)
|
||||
{
|
||||
struct sensor_accumulator *accum;
|
||||
u64 input;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
rdmsrl_safe_on_cpu(cpu, reg, &input);
|
||||
input &= AMD_ENERGY_MASK;
|
||||
|
||||
accum = &data->accums[channel];
|
||||
if (input >= accum->prev_value)
|
||||
accum->energy_ctr +=
|
||||
input - accum->prev_value;
|
||||
else
|
||||
accum->energy_ctr += UINT_MAX -
|
||||
accum->prev_value + input;
|
||||
|
||||
accum->prev_value = input;
|
||||
mutex_unlock(&data->lock);
|
||||
}
|
||||
|
||||
static void read_accumulate(struct amd_energy_data *data)
|
||||
{
|
||||
int sock, scpu, cpu;
|
||||
|
||||
for (sock = 0; sock < data->nr_socks; sock++) {
|
||||
scpu = cpumask_first_and(cpu_online_mask,
|
||||
cpumask_of_node(sock));
|
||||
|
||||
accumulate_delta(data, data->nr_cpus + sock,
|
||||
scpu, ENERGY_PKG_MSR);
|
||||
}
|
||||
|
||||
if (data->core_id >= data->nr_cpus)
|
||||
data->core_id = 0;
|
||||
|
||||
cpu = data->core_id;
|
||||
if (cpu_online(cpu))
|
||||
accumulate_delta(data, cpu, cpu, ENERGY_CORE_MSR);
|
||||
|
||||
data->core_id++;
|
||||
}
|
||||
|
||||
static void amd_add_delta(struct amd_energy_data *data, int ch,
|
||||
int cpu, long *val, u32 reg)
|
||||
{
|
||||
struct sensor_accumulator *accum;
|
||||
u64 input;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
rdmsrl_safe_on_cpu(cpu, reg, &input);
|
||||
input &= AMD_ENERGY_MASK;
|
||||
|
||||
accum = &data->accums[ch];
|
||||
if (input >= accum->prev_value)
|
||||
input += accum->energy_ctr -
|
||||
accum->prev_value;
|
||||
else
|
||||
input += UINT_MAX - accum->prev_value +
|
||||
accum->energy_ctr;
|
||||
|
||||
/* Energy consumed = (1/(2^ESU) * RAW * 1000000UL) μJoules */
|
||||
*val = div64_ul(input * 1000000UL, BIT(data->energy_units));
|
||||
|
||||
mutex_unlock(&data->lock);
|
||||
}
|
||||
|
||||
static int amd_energy_read(struct device *dev,
|
||||
enum hwmon_sensor_types type,
|
||||
u32 attr, int channel, long *val)
|
||||
{
|
||||
struct amd_energy_data *data = dev_get_drvdata(dev);
|
||||
u32 reg;
|
||||
int cpu;
|
||||
|
||||
if (channel >= data->nr_cpus) {
|
||||
cpu = cpumask_first_and(cpu_online_mask,
|
||||
cpumask_of_node
|
||||
(channel - data->nr_cpus));
|
||||
reg = ENERGY_PKG_MSR;
|
||||
} else {
|
||||
cpu = channel;
|
||||
if (!cpu_online(cpu))
|
||||
return -ENODEV;
|
||||
|
||||
reg = ENERGY_CORE_MSR;
|
||||
}
|
||||
amd_add_delta(data, channel, cpu, val, reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static umode_t amd_energy_is_visible(const void *_data,
|
||||
enum hwmon_sensor_types type,
|
||||
u32 attr, int channel)
|
||||
{
|
||||
return 0440;
|
||||
}
|
||||
|
||||
static int energy_accumulator(void *p)
|
||||
{
|
||||
struct amd_energy_data *data = (struct amd_energy_data *)p;
|
||||
unsigned int timeout = data->timeout_ms;
|
||||
|
||||
while (!kthread_should_stop()) {
|
||||
/*
|
||||
* Ignoring the conditions such as
|
||||
* cpu being offline or rdmsr failure
|
||||
*/
|
||||
read_accumulate(data);
|
||||
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
if (kthread_should_stop())
|
||||
break;
|
||||
|
||||
schedule_timeout(msecs_to_jiffies(timeout));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct hwmon_ops amd_energy_ops = {
|
||||
.is_visible = amd_energy_is_visible,
|
||||
.read = amd_energy_read,
|
||||
.read_string = amd_energy_read_labels,
|
||||
};
|
||||
|
||||
static int amd_create_sensor(struct device *dev,
|
||||
struct amd_energy_data *data,
|
||||
enum hwmon_sensor_types type, u32 config)
|
||||
{
|
||||
struct hwmon_channel_info *info = &data->energy_info;
|
||||
struct sensor_accumulator *accums;
|
||||
int i, num_siblings, cpus, sockets;
|
||||
u32 *s_config;
|
||||
char (*label_l)[10];
|
||||
|
||||
/* Identify the number of siblings per core */
|
||||
num_siblings = ((cpuid_ebx(0x8000001e) >> 8) & 0xff) + 1;
|
||||
|
||||
sockets = num_possible_nodes();
|
||||
|
||||
/*
|
||||
* Energy counter register is accessed at core level.
|
||||
* Hence, filterout the siblings.
|
||||
*/
|
||||
cpus = num_present_cpus() / num_siblings;
|
||||
|
||||
s_config = devm_kcalloc(dev, cpus + sockets + 1,
|
||||
sizeof(u32), GFP_KERNEL);
|
||||
if (!s_config)
|
||||
return -ENOMEM;
|
||||
|
||||
accums = devm_kcalloc(dev, cpus + sockets,
|
||||
sizeof(struct sensor_accumulator),
|
||||
GFP_KERNEL);
|
||||
if (!accums)
|
||||
return -ENOMEM;
|
||||
|
||||
label_l = devm_kcalloc(dev, cpus + sockets,
|
||||
sizeof(*label_l), GFP_KERNEL);
|
||||
if (!label_l)
|
||||
return -ENOMEM;
|
||||
|
||||
info->type = type;
|
||||
info->config = s_config;
|
||||
|
||||
data->nr_cpus = cpus;
|
||||
data->nr_socks = sockets;
|
||||
data->accums = accums;
|
||||
data->label = label_l;
|
||||
|
||||
for (i = 0; i < cpus + sockets; i++) {
|
||||
s_config[i] = config;
|
||||
if (i < cpus)
|
||||
scnprintf(label_l[i], 10, "Ecore%03u", i);
|
||||
else
|
||||
scnprintf(label_l[i], 10, "Esocket%u", (i - cpus));
|
||||
}
|
||||
|
||||
s_config[i] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amd_energy_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *hwmon_dev;
|
||||
struct amd_energy_data *data;
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret;
|
||||
|
||||
data = devm_kzalloc(dev,
|
||||
sizeof(struct amd_energy_data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->chip.ops = &amd_energy_ops;
|
||||
data->chip.info = data->info;
|
||||
|
||||
dev_set_drvdata(dev, data);
|
||||
/* Populate per-core energy reporting */
|
||||
data->info[0] = &data->energy_info;
|
||||
ret = amd_create_sensor(dev, data, hwmon_energy,
|
||||
HWMON_E_INPUT | HWMON_E_LABEL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_init(&data->lock);
|
||||
get_energy_units(data);
|
||||
|
||||
hwmon_dev = devm_hwmon_device_register_with_info(dev, DRVNAME,
|
||||
data,
|
||||
&data->chip,
|
||||
NULL);
|
||||
if (IS_ERR(hwmon_dev))
|
||||
return PTR_ERR(hwmon_dev);
|
||||
|
||||
/*
|
||||
* On a system with peak wattage of 250W
|
||||
* timeout = 2 ^ 32 / 2 ^ energy_units / 250 secs
|
||||
*/
|
||||
data->timeout_ms = 1000 *
|
||||
BIT(min(28, 31 - data->energy_units)) / 250;
|
||||
|
||||
data->wrap_accumulate = kthread_run(energy_accumulator, data,
|
||||
"%s", dev_name(hwmon_dev));
|
||||
return PTR_ERR_OR_ZERO(data->wrap_accumulate);
|
||||
}
|
||||
|
||||
static int amd_energy_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct amd_energy_data *data = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
if (data && data->wrap_accumulate)
|
||||
kthread_stop(data->wrap_accumulate);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct platform_device_id amd_energy_ids[] = {
|
||||
{ .name = DRVNAME, },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, amd_energy_ids);
|
||||
|
||||
static struct platform_driver amd_energy_driver = {
|
||||
.probe = amd_energy_probe,
|
||||
.remove = amd_energy_remove,
|
||||
.id_table = amd_energy_ids,
|
||||
.driver = {
|
||||
.name = DRVNAME,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device *amd_energy_platdev;
|
||||
|
||||
static const struct x86_cpu_id cpu_ids[] __initconst = {
|
||||
X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x17, 0x31, NULL),
|
||||
X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x19, 0x01, NULL),
|
||||
X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x19, 0x30, NULL),
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(x86cpu, cpu_ids);
|
||||
|
||||
static int __init amd_energy_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!x86_match_cpu(cpu_ids))
|
||||
return -ENODEV;
|
||||
|
||||
ret = platform_driver_register(&amd_energy_driver);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
amd_energy_platdev = platform_device_alloc(DRVNAME, 0);
|
||||
if (!amd_energy_platdev) {
|
||||
platform_driver_unregister(&amd_energy_driver);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = platform_device_add(amd_energy_platdev);
|
||||
if (ret) {
|
||||
platform_device_put(amd_energy_platdev);
|
||||
platform_driver_unregister(&amd_energy_driver);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit amd_energy_exit(void)
|
||||
{
|
||||
platform_device_unregister(amd_energy_platdev);
|
||||
platform_driver_unregister(&amd_energy_driver);
|
||||
}
|
||||
|
||||
module_init(amd_energy_init);
|
||||
module_exit(amd_energy_exit);
|
||||
|
||||
MODULE_DESCRIPTION("Driver for AMD Energy reporting from RAPL MSR via HWMON interface");
|
||||
MODULE_AUTHOR("Naveen Krishna Chatradhi <nchatrad@amd.com>");
|
||||
MODULE_LICENSE("GPL");
|
@ -741,7 +741,7 @@ static void applesmc_idev_poll(struct input_dev *idev)
|
||||
static ssize_t applesmc_name_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "applesmc\n");
|
||||
return sysfs_emit(buf, "applesmc\n");
|
||||
}
|
||||
|
||||
static ssize_t applesmc_position_show(struct device *dev,
|
||||
@ -763,8 +763,8 @@ static ssize_t applesmc_position_show(struct device *dev,
|
||||
out:
|
||||
if (ret)
|
||||
return ret;
|
||||
else
|
||||
return snprintf(buf, PAGE_SIZE, "(%d,%d,%d)\n", x, y, z);
|
||||
|
||||
return sysfs_emit(buf, "(%d,%d,%d)\n", x, y, z);
|
||||
}
|
||||
|
||||
static ssize_t applesmc_light_show(struct device *dev,
|
||||
@ -804,8 +804,8 @@ static ssize_t applesmc_light_show(struct device *dev,
|
||||
out:
|
||||
if (ret)
|
||||
return ret;
|
||||
else
|
||||
return snprintf(sysfsbuf, PAGE_SIZE, "(%d,%d)\n", left, right);
|
||||
|
||||
return sysfs_emit(sysfsbuf, "(%d,%d)\n", left, right);
|
||||
}
|
||||
|
||||
/* Displays sensor key as label */
|
||||
@ -814,7 +814,7 @@ static ssize_t applesmc_show_sensor_label(struct device *dev,
|
||||
{
|
||||
const char *key = smcreg.index[to_index(devattr)];
|
||||
|
||||
return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", key);
|
||||
return sysfs_emit(sysfsbuf, "%s\n", key);
|
||||
}
|
||||
|
||||
/* Displays degree Celsius * 1000 */
|
||||
@ -832,7 +832,7 @@ static ssize_t applesmc_show_temperature(struct device *dev,
|
||||
|
||||
temp = 250 * (value >> 6);
|
||||
|
||||
return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", temp);
|
||||
return sysfs_emit(sysfsbuf, "%d\n", temp);
|
||||
}
|
||||
|
||||
static ssize_t applesmc_show_fan_speed(struct device *dev,
|
||||
@ -851,7 +851,7 @@ static ssize_t applesmc_show_fan_speed(struct device *dev,
|
||||
return ret;
|
||||
|
||||
speed = ((buffer[0] << 8 | buffer[1]) >> 2);
|
||||
return snprintf(sysfsbuf, PAGE_SIZE, "%u\n", speed);
|
||||
return sysfs_emit(sysfsbuf, "%u\n", speed);
|
||||
}
|
||||
|
||||
static ssize_t applesmc_store_fan_speed(struct device *dev,
|
||||
@ -891,7 +891,7 @@ static ssize_t applesmc_show_fan_manual(struct device *dev,
|
||||
return ret;
|
||||
|
||||
manual = ((buffer[0] << 8 | buffer[1]) >> to_index(attr)) & 0x01;
|
||||
return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", manual);
|
||||
return sysfs_emit(sysfsbuf, "%d\n", manual);
|
||||
}
|
||||
|
||||
static ssize_t applesmc_store_fan_manual(struct device *dev,
|
||||
@ -943,14 +943,14 @@ static ssize_t applesmc_show_fan_position(struct device *dev,
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
else
|
||||
return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", buffer+4);
|
||||
|
||||
return sysfs_emit(sysfsbuf, "%s\n", buffer + 4);
|
||||
}
|
||||
|
||||
static ssize_t applesmc_calibrate_show(struct device *dev,
|
||||
struct device_attribute *attr, char *sysfsbuf)
|
||||
{
|
||||
return snprintf(sysfsbuf, PAGE_SIZE, "(%d,%d)\n", rest_x, rest_y);
|
||||
return sysfs_emit(sysfsbuf, "(%d,%d)\n", rest_x, rest_y);
|
||||
}
|
||||
|
||||
static ssize_t applesmc_calibrate_store(struct device *dev,
|
||||
@ -992,7 +992,7 @@ static ssize_t applesmc_key_count_show(struct device *dev,
|
||||
|
||||
count = ((u32)buffer[0]<<24) + ((u32)buffer[1]<<16) +
|
||||
((u32)buffer[2]<<8) + buffer[3];
|
||||
return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", count);
|
||||
return sysfs_emit(sysfsbuf, "%d\n", count);
|
||||
}
|
||||
|
||||
static ssize_t applesmc_key_at_index_read_show(struct device *dev,
|
||||
@ -1020,7 +1020,7 @@ static ssize_t applesmc_key_at_index_data_length_show(struct device *dev,
|
||||
if (IS_ERR(entry))
|
||||
return PTR_ERR(entry);
|
||||
|
||||
return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", entry->len);
|
||||
return sysfs_emit(sysfsbuf, "%d\n", entry->len);
|
||||
}
|
||||
|
||||
static ssize_t applesmc_key_at_index_type_show(struct device *dev,
|
||||
@ -1032,7 +1032,7 @@ static ssize_t applesmc_key_at_index_type_show(struct device *dev,
|
||||
if (IS_ERR(entry))
|
||||
return PTR_ERR(entry);
|
||||
|
||||
return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", entry->type);
|
||||
return sysfs_emit(sysfsbuf, "%s\n", entry->type);
|
||||
}
|
||||
|
||||
static ssize_t applesmc_key_at_index_name_show(struct device *dev,
|
||||
@ -1044,13 +1044,13 @@ static ssize_t applesmc_key_at_index_name_show(struct device *dev,
|
||||
if (IS_ERR(entry))
|
||||
return PTR_ERR(entry);
|
||||
|
||||
return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", entry->key);
|
||||
return sysfs_emit(sysfsbuf, "%s\n", entry->key);
|
||||
}
|
||||
|
||||
static ssize_t applesmc_key_at_index_show(struct device *dev,
|
||||
struct device_attribute *attr, char *sysfsbuf)
|
||||
{
|
||||
return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", key_at_index);
|
||||
return sysfs_emit(sysfsbuf, "%d\n", key_at_index);
|
||||
}
|
||||
|
||||
static ssize_t applesmc_key_at_index_store(struct device *dev,
|
||||
|
@ -53,11 +53,17 @@
|
||||
#define CMD_TIMEOUT_MS 250
|
||||
#define SECONDS_PER_HOUR (60 * 60)
|
||||
#define SECONDS_PER_DAY (SECONDS_PER_HOUR * 24)
|
||||
#define RAIL_COUNT 3 /* 3v3 + 5v + 12v */
|
||||
#define TEMP_COUNT 2
|
||||
|
||||
#define PSU_CMD_SELECT_RAIL 0x00 /* expects length 2 */
|
||||
#define PSU_CMD_IN_VOLTS 0x88 /* the rest of the commands expect length 3 */
|
||||
#define PSU_CMD_RAIL_VOLTS_HCRIT 0x40 /* the rest of the commands expect length 3 */
|
||||
#define PSU_CMD_RAIL_VOLTS_LCRIT 0x44
|
||||
#define PSU_CMD_RAIL_AMPS_HCRIT 0x46
|
||||
#define PSU_CMD_TEMP_HCRIT 0x4F
|
||||
#define PSU_CMD_IN_VOLTS 0x88
|
||||
#define PSU_CMD_IN_AMPS 0x89
|
||||
#define PSU_CMD_RAIL_OUT_VOLTS 0x8B
|
||||
#define PSU_CMD_RAIL_VOLTS 0x8B
|
||||
#define PSU_CMD_RAIL_AMPS 0x8C
|
||||
#define PSU_CMD_TEMP0 0x8D
|
||||
#define PSU_CMD_TEMP1 0x8E
|
||||
@ -116,30 +122,25 @@ struct corsairpsu_data {
|
||||
u8 *cmd_buffer;
|
||||
char vendor[REPLY_SIZE];
|
||||
char product[REPLY_SIZE];
|
||||
long temp_crit[TEMP_COUNT];
|
||||
long in_crit[RAIL_COUNT];
|
||||
long in_lcrit[RAIL_COUNT];
|
||||
long curr_crit[RAIL_COUNT];
|
||||
u8 temp_crit_support;
|
||||
u8 in_crit_support;
|
||||
u8 in_lcrit_support;
|
||||
u8 curr_crit_support;
|
||||
bool in_curr_cmd_support; /* not all commands are supported on every PSU */
|
||||
};
|
||||
|
||||
/* some values are SMBus LINEAR11 data which need a conversion */
|
||||
static int corsairpsu_linear11_to_int(const int val)
|
||||
static int corsairpsu_linear11_to_int(const u16 val, const int scale)
|
||||
{
|
||||
int exp = (val & 0xFFFF) >> 0x0B;
|
||||
int mant = val & 0x7FF;
|
||||
int i;
|
||||
const int exp = ((s16)val) >> 11;
|
||||
const int mant = (((s16)(val & 0x7ff)) << 5) >> 5;
|
||||
const int result = mant * scale;
|
||||
|
||||
if (exp > 0x0F)
|
||||
exp -= 0x20;
|
||||
if (mant > 0x3FF)
|
||||
mant -= 0x800;
|
||||
if ((mant & 0x01) == 1)
|
||||
++mant;
|
||||
if (exp < 0) {
|
||||
for (i = 0; i < -exp; ++i)
|
||||
mant /= 2;
|
||||
} else {
|
||||
for (i = 0; i < exp; ++i)
|
||||
mant *= 2;
|
||||
}
|
||||
|
||||
return mant;
|
||||
return (exp >= 0) ? (result << exp) : (result >> -exp);
|
||||
}
|
||||
|
||||
static int corsairpsu_usb_cmd(struct corsairpsu_data *priv, u8 p0, u8 p1, u8 p2, void *data)
|
||||
@ -207,7 +208,10 @@ static int corsairpsu_request(struct corsairpsu_data *priv, u8 cmd, u8 rail, voi
|
||||
|
||||
mutex_lock(&priv->lock);
|
||||
switch (cmd) {
|
||||
case PSU_CMD_RAIL_OUT_VOLTS:
|
||||
case PSU_CMD_RAIL_VOLTS_HCRIT:
|
||||
case PSU_CMD_RAIL_VOLTS_LCRIT:
|
||||
case PSU_CMD_RAIL_AMPS_HCRIT:
|
||||
case PSU_CMD_RAIL_VOLTS:
|
||||
case PSU_CMD_RAIL_AMPS:
|
||||
case PSU_CMD_RAIL_WATTS:
|
||||
ret = corsairpsu_usb_cmd(priv, 2, PSU_CMD_SELECT_RAIL, rail, NULL);
|
||||
@ -243,20 +247,24 @@ static int corsairpsu_get_value(struct corsairpsu_data *priv, u8 cmd, u8 rail, l
|
||||
*/
|
||||
tmp = ((long)data[3] << 24) + (data[2] << 16) + (data[1] << 8) + data[0];
|
||||
switch (cmd) {
|
||||
case PSU_CMD_RAIL_VOLTS_HCRIT:
|
||||
case PSU_CMD_RAIL_VOLTS_LCRIT:
|
||||
case PSU_CMD_RAIL_AMPS_HCRIT:
|
||||
case PSU_CMD_TEMP_HCRIT:
|
||||
case PSU_CMD_IN_VOLTS:
|
||||
case PSU_CMD_IN_AMPS:
|
||||
case PSU_CMD_RAIL_OUT_VOLTS:
|
||||
case PSU_CMD_RAIL_VOLTS:
|
||||
case PSU_CMD_RAIL_AMPS:
|
||||
case PSU_CMD_TEMP0:
|
||||
case PSU_CMD_TEMP1:
|
||||
*val = corsairpsu_linear11_to_int(tmp & 0xFFFF) * 1000;
|
||||
*val = corsairpsu_linear11_to_int(tmp & 0xFFFF, 1000);
|
||||
break;
|
||||
case PSU_CMD_FAN:
|
||||
*val = corsairpsu_linear11_to_int(tmp & 0xFFFF);
|
||||
*val = corsairpsu_linear11_to_int(tmp & 0xFFFF, 1);
|
||||
break;
|
||||
case PSU_CMD_RAIL_WATTS:
|
||||
case PSU_CMD_TOTAL_WATTS:
|
||||
*val = corsairpsu_linear11_to_int(tmp & 0xFFFF) * 1000000;
|
||||
*val = corsairpsu_linear11_to_int(tmp & 0xFFFF, 1000000);
|
||||
break;
|
||||
case PSU_CMD_TOTAL_UPTIME:
|
||||
case PSU_CMD_UPTIME:
|
||||
@ -270,75 +278,265 @@ static int corsairpsu_get_value(struct corsairpsu_data *priv, u8 cmd, u8 rail, l
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void corsairpsu_get_criticals(struct corsairpsu_data *priv)
|
||||
{
|
||||
long tmp;
|
||||
int rail;
|
||||
|
||||
for (rail = 0; rail < TEMP_COUNT; ++rail) {
|
||||
if (!corsairpsu_get_value(priv, PSU_CMD_TEMP_HCRIT, rail, &tmp)) {
|
||||
priv->temp_crit_support |= BIT(rail);
|
||||
priv->temp_crit[rail] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
for (rail = 0; rail < RAIL_COUNT; ++rail) {
|
||||
if (!corsairpsu_get_value(priv, PSU_CMD_RAIL_VOLTS_HCRIT, rail, &tmp)) {
|
||||
priv->in_crit_support |= BIT(rail);
|
||||
priv->in_crit[rail] = tmp;
|
||||
}
|
||||
|
||||
if (!corsairpsu_get_value(priv, PSU_CMD_RAIL_VOLTS_LCRIT, rail, &tmp)) {
|
||||
priv->in_lcrit_support |= BIT(rail);
|
||||
priv->in_lcrit[rail] = tmp;
|
||||
}
|
||||
|
||||
if (!corsairpsu_get_value(priv, PSU_CMD_RAIL_AMPS_HCRIT, rail, &tmp)) {
|
||||
priv->curr_crit_support |= BIT(rail);
|
||||
priv->curr_crit[rail] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void corsairpsu_check_cmd_support(struct corsairpsu_data *priv)
|
||||
{
|
||||
long tmp;
|
||||
|
||||
priv->in_curr_cmd_support = !corsairpsu_get_value(priv, PSU_CMD_IN_AMPS, 0, &tmp);
|
||||
}
|
||||
|
||||
static umode_t corsairpsu_hwmon_temp_is_visible(const struct corsairpsu_data *priv, u32 attr,
|
||||
int channel)
|
||||
{
|
||||
umode_t res = 0444;
|
||||
|
||||
switch (attr) {
|
||||
case hwmon_temp_input:
|
||||
case hwmon_temp_label:
|
||||
case hwmon_temp_crit:
|
||||
if (channel > 0 && !(priv->temp_crit_support & BIT(channel - 1)))
|
||||
res = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static umode_t corsairpsu_hwmon_fan_is_visible(const struct corsairpsu_data *priv, u32 attr,
|
||||
int channel)
|
||||
{
|
||||
switch (attr) {
|
||||
case hwmon_fan_input:
|
||||
case hwmon_fan_label:
|
||||
return 0444;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static umode_t corsairpsu_hwmon_power_is_visible(const struct corsairpsu_data *priv, u32 attr,
|
||||
int channel)
|
||||
{
|
||||
switch (attr) {
|
||||
case hwmon_power_input:
|
||||
case hwmon_power_label:
|
||||
return 0444;
|
||||
default:
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
||||
static umode_t corsairpsu_hwmon_in_is_visible(const struct corsairpsu_data *priv, u32 attr,
|
||||
int channel)
|
||||
{
|
||||
umode_t res = 0444;
|
||||
|
||||
switch (attr) {
|
||||
case hwmon_in_input:
|
||||
case hwmon_in_label:
|
||||
case hwmon_in_crit:
|
||||
if (channel > 0 && !(priv->in_crit_support & BIT(channel - 1)))
|
||||
res = 0;
|
||||
break;
|
||||
case hwmon_in_lcrit:
|
||||
if (channel > 0 && !(priv->in_lcrit_support & BIT(channel - 1)))
|
||||
res = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
};
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static umode_t corsairpsu_hwmon_curr_is_visible(const struct corsairpsu_data *priv, u32 attr,
|
||||
int channel)
|
||||
{
|
||||
umode_t res = 0444;
|
||||
|
||||
switch (attr) {
|
||||
case hwmon_curr_input:
|
||||
if (channel == 0 && !priv->in_curr_cmd_support)
|
||||
res = 0;
|
||||
break;
|
||||
case hwmon_curr_label:
|
||||
case hwmon_curr_crit:
|
||||
if (channel > 0 && !(priv->curr_crit_support & BIT(channel - 1)))
|
||||
res = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static umode_t corsairpsu_hwmon_ops_is_visible(const void *data, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel)
|
||||
{
|
||||
if (type == hwmon_temp && (attr == hwmon_temp_input || attr == hwmon_temp_label))
|
||||
return 0444;
|
||||
else if (type == hwmon_fan && (attr == hwmon_fan_input || attr == hwmon_fan_label))
|
||||
return 0444;
|
||||
else if (type == hwmon_power && (attr == hwmon_power_input || attr == hwmon_power_label))
|
||||
return 0444;
|
||||
else if (type == hwmon_in && (attr == hwmon_in_input || attr == hwmon_in_label))
|
||||
return 0444;
|
||||
else if (type == hwmon_curr && (attr == hwmon_curr_input || attr == hwmon_curr_label))
|
||||
return 0444;
|
||||
const struct corsairpsu_data *priv = data;
|
||||
|
||||
return 0;
|
||||
switch (type) {
|
||||
case hwmon_temp:
|
||||
return corsairpsu_hwmon_temp_is_visible(priv, attr, channel);
|
||||
case hwmon_fan:
|
||||
return corsairpsu_hwmon_fan_is_visible(priv, attr, channel);
|
||||
case hwmon_power:
|
||||
return corsairpsu_hwmon_power_is_visible(priv, attr, channel);
|
||||
case hwmon_in:
|
||||
return corsairpsu_hwmon_in_is_visible(priv, attr, channel);
|
||||
case hwmon_curr:
|
||||
return corsairpsu_hwmon_curr_is_visible(priv, attr, channel);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int corsairpsu_hwmon_temp_read(struct corsairpsu_data *priv, u32 attr, int channel,
|
||||
long *val)
|
||||
{
|
||||
int err = -EOPNOTSUPP;
|
||||
|
||||
switch (attr) {
|
||||
case hwmon_temp_input:
|
||||
return corsairpsu_get_value(priv, channel ? PSU_CMD_TEMP1 : PSU_CMD_TEMP0,
|
||||
channel, val);
|
||||
case hwmon_temp_crit:
|
||||
*val = priv->temp_crit[channel];
|
||||
err = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int corsairpsu_hwmon_power_read(struct corsairpsu_data *priv, u32 attr, int channel,
|
||||
long *val)
|
||||
{
|
||||
if (attr == hwmon_power_input) {
|
||||
switch (channel) {
|
||||
case 0:
|
||||
return corsairpsu_get_value(priv, PSU_CMD_TOTAL_WATTS, 0, val);
|
||||
case 1 ... 3:
|
||||
return corsairpsu_get_value(priv, PSU_CMD_RAIL_WATTS, channel - 1, val);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static int corsairpsu_hwmon_in_read(struct corsairpsu_data *priv, u32 attr, int channel, long *val)
|
||||
{
|
||||
int err = -EOPNOTSUPP;
|
||||
|
||||
switch (attr) {
|
||||
case hwmon_in_input:
|
||||
switch (channel) {
|
||||
case 0:
|
||||
return corsairpsu_get_value(priv, PSU_CMD_IN_VOLTS, 0, val);
|
||||
case 1 ... 3:
|
||||
return corsairpsu_get_value(priv, PSU_CMD_RAIL_VOLTS, channel - 1, val);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case hwmon_in_crit:
|
||||
*val = priv->in_crit[channel - 1];
|
||||
err = 0;
|
||||
break;
|
||||
case hwmon_in_lcrit:
|
||||
*val = priv->in_lcrit[channel - 1];
|
||||
err = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int corsairpsu_hwmon_curr_read(struct corsairpsu_data *priv, u32 attr, int channel,
|
||||
long *val)
|
||||
{
|
||||
int err = -EOPNOTSUPP;
|
||||
|
||||
switch (attr) {
|
||||
case hwmon_curr_input:
|
||||
switch (channel) {
|
||||
case 0:
|
||||
return corsairpsu_get_value(priv, PSU_CMD_IN_AMPS, 0, val);
|
||||
case 1 ... 3:
|
||||
return corsairpsu_get_value(priv, PSU_CMD_RAIL_AMPS, channel - 1, val);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case hwmon_curr_crit:
|
||||
*val = priv->curr_crit[channel - 1];
|
||||
err = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int corsairpsu_hwmon_ops_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
|
||||
int channel, long *val)
|
||||
{
|
||||
struct corsairpsu_data *priv = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
if (type == hwmon_temp && attr == hwmon_temp_input && channel < 2) {
|
||||
ret = corsairpsu_get_value(priv, channel ? PSU_CMD_TEMP1 : PSU_CMD_TEMP0, channel,
|
||||
val);
|
||||
} else if (type == hwmon_fan && attr == hwmon_fan_input) {
|
||||
ret = corsairpsu_get_value(priv, PSU_CMD_FAN, 0, val);
|
||||
} else if (type == hwmon_power && attr == hwmon_power_input) {
|
||||
switch (channel) {
|
||||
case 0:
|
||||
ret = corsairpsu_get_value(priv, PSU_CMD_TOTAL_WATTS, 0, val);
|
||||
break;
|
||||
case 1 ... 3:
|
||||
ret = corsairpsu_get_value(priv, PSU_CMD_RAIL_WATTS, channel - 1, val);
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
} else if (type == hwmon_in && attr == hwmon_in_input) {
|
||||
switch (channel) {
|
||||
case 0:
|
||||
ret = corsairpsu_get_value(priv, PSU_CMD_IN_VOLTS, 0, val);
|
||||
break;
|
||||
case 1 ... 3:
|
||||
ret = corsairpsu_get_value(priv, PSU_CMD_RAIL_OUT_VOLTS, channel - 1, val);
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
} else if (type == hwmon_curr && attr == hwmon_curr_input) {
|
||||
switch (channel) {
|
||||
case 0:
|
||||
ret = corsairpsu_get_value(priv, PSU_CMD_IN_AMPS, 0, val);
|
||||
break;
|
||||
case 1 ... 3:
|
||||
ret = corsairpsu_get_value(priv, PSU_CMD_RAIL_AMPS, channel - 1, val);
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
} else {
|
||||
switch (type) {
|
||||
case hwmon_temp:
|
||||
return corsairpsu_hwmon_temp_read(priv, attr, channel, val);
|
||||
case hwmon_fan:
|
||||
if (attr == hwmon_fan_input)
|
||||
return corsairpsu_get_value(priv, PSU_CMD_FAN, 0, val);
|
||||
return -EOPNOTSUPP;
|
||||
case hwmon_power:
|
||||
return corsairpsu_hwmon_power_read(priv, attr, channel, val);
|
||||
case hwmon_in:
|
||||
return corsairpsu_hwmon_in_read(priv, attr, channel, val);
|
||||
case hwmon_curr:
|
||||
return corsairpsu_hwmon_curr_read(priv, attr, channel, val);
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int corsairpsu_hwmon_ops_read_string(struct device *dev, enum hwmon_sensor_types type,
|
||||
@ -374,8 +572,8 @@ static const struct hwmon_channel_info *corsairpsu_info[] = {
|
||||
HWMON_CHANNEL_INFO(chip,
|
||||
HWMON_C_REGISTER_TZ),
|
||||
HWMON_CHANNEL_INFO(temp,
|
||||
HWMON_T_INPUT | HWMON_T_LABEL,
|
||||
HWMON_T_INPUT | HWMON_T_LABEL),
|
||||
HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_CRIT,
|
||||
HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_CRIT),
|
||||
HWMON_CHANNEL_INFO(fan,
|
||||
HWMON_F_INPUT | HWMON_F_LABEL),
|
||||
HWMON_CHANNEL_INFO(power,
|
||||
@ -385,14 +583,14 @@ static const struct hwmon_channel_info *corsairpsu_info[] = {
|
||||
HWMON_P_INPUT | HWMON_P_LABEL),
|
||||
HWMON_CHANNEL_INFO(in,
|
||||
HWMON_I_INPUT | HWMON_I_LABEL,
|
||||
HWMON_I_INPUT | HWMON_I_LABEL,
|
||||
HWMON_I_INPUT | HWMON_I_LABEL,
|
||||
HWMON_I_INPUT | HWMON_I_LABEL),
|
||||
HWMON_I_INPUT | HWMON_I_LABEL | HWMON_I_LCRIT | HWMON_I_CRIT,
|
||||
HWMON_I_INPUT | HWMON_I_LABEL | HWMON_I_LCRIT | HWMON_I_CRIT,
|
||||
HWMON_I_INPUT | HWMON_I_LABEL | HWMON_I_LCRIT | HWMON_I_CRIT),
|
||||
HWMON_CHANNEL_INFO(curr,
|
||||
HWMON_C_INPUT | HWMON_C_LABEL,
|
||||
HWMON_C_INPUT | HWMON_C_LABEL,
|
||||
HWMON_C_INPUT | HWMON_C_LABEL,
|
||||
HWMON_C_INPUT | HWMON_C_LABEL),
|
||||
HWMON_C_INPUT | HWMON_C_LABEL | HWMON_C_CRIT,
|
||||
HWMON_C_INPUT | HWMON_C_LABEL | HWMON_C_CRIT,
|
||||
HWMON_C_INPUT | HWMON_C_LABEL | HWMON_C_CRIT),
|
||||
NULL
|
||||
};
|
||||
|
||||
@ -527,6 +725,9 @@ static int corsairpsu_probe(struct hid_device *hdev, const struct hid_device_id
|
||||
goto fail_and_stop;
|
||||
}
|
||||
|
||||
corsairpsu_get_criticals(priv);
|
||||
corsairpsu_check_cmd_support(priv);
|
||||
|
||||
priv->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "corsairpsu", priv,
|
||||
&corsairpsu_chip_info, 0);
|
||||
|
||||
|
@ -1210,6 +1210,14 @@ static struct dmi_system_id i8k_whitelist_fan_control[] __initdata = {
|
||||
},
|
||||
.driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3],
|
||||
},
|
||||
{
|
||||
.ident = "Dell Latitude E7440",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
||||
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Latitude E7440"),
|
||||
},
|
||||
.driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3],
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
|
@ -326,7 +326,7 @@ static struct attribute *ds1621_attributes[] = {
|
||||
static umode_t ds1621_attribute_visible(struct kobject *kobj,
|
||||
struct attribute *attr, int index)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct ds1621_data *data = dev_get_drvdata(dev);
|
||||
|
||||
if (attr == &dev_attr_update_interval.attr)
|
||||
|
@ -509,7 +509,7 @@ error:
|
||||
/* SysFS structs */
|
||||
/*****************************************************************************/
|
||||
|
||||
/* Temprature sensors */
|
||||
/* Temperature sensors */
|
||||
static SENSOR_DEVICE_ATTR_RO(temp1_input, temp_value, 0);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp2_input, temp_value, 1);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp3_input, temp_value, 2);
|
||||
@ -713,7 +713,7 @@ static int fts_detect(struct i2c_client *client,
|
||||
{
|
||||
int val;
|
||||
|
||||
/* detection works with revsion greater or equal to 0x2b */
|
||||
/* detection works with revision greater or equal to 0x2b */
|
||||
val = i2c_smbus_read_byte_data(client, FTS_DEVICE_REVISION_REG);
|
||||
if (val < 0x2b)
|
||||
return -ENODEV;
|
||||
|
@ -79,7 +79,7 @@ static struct attribute *hwmon_dev_attrs[] = {
|
||||
static umode_t hwmon_dev_name_is_visible(struct kobject *kobj,
|
||||
struct attribute *attr, int n)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
|
||||
if (to_hwmon_device(dev)->name == NULL)
|
||||
return 0;
|
||||
|
@ -259,7 +259,7 @@ static ssize_t ina209_interval_show(struct device *dev,
|
||||
{
|
||||
struct ina209_data *data = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", data->update_interval);
|
||||
return sysfs_emit(buf, "%d\n", data->update_interval);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -343,7 +343,7 @@ static ssize_t ina209_value_show(struct device *dev,
|
||||
return PTR_ERR(data);
|
||||
|
||||
val = ina209_from_reg(attr->index, data->regs[attr->index]);
|
||||
return snprintf(buf, PAGE_SIZE, "%ld\n", val);
|
||||
return sysfs_emit(buf, "%ld\n", val);
|
||||
}
|
||||
|
||||
static ssize_t ina209_alarm_show(struct device *dev,
|
||||
@ -363,7 +363,7 @@ static ssize_t ina209_alarm_show(struct device *dev,
|
||||
* All alarms are in the INA209_STATUS register. To avoid a long
|
||||
* switch statement, the mask is passed in attr->index
|
||||
*/
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", !!(status & mask));
|
||||
return sysfs_emit(buf, "%u\n", !!(status & mask));
|
||||
}
|
||||
|
||||
/* Shunt voltage, history, limits, alarms */
|
||||
|
@ -310,8 +310,7 @@ static ssize_t ina2xx_value_show(struct device *dev,
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n",
|
||||
ina2xx_get_value(data, attr->index, regval));
|
||||
return sysfs_emit(buf, "%d\n", ina2xx_get_value(data, attr->index, regval));
|
||||
}
|
||||
|
||||
static int ina226_reg_to_alert(struct ina2xx_data *data, u8 bit, u16 regval)
|
||||
@ -386,7 +385,7 @@ static ssize_t ina226_alert_show(struct device *dev,
|
||||
val = ina226_reg_to_alert(data, attr->index, regval);
|
||||
}
|
||||
|
||||
ret = snprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
ret = sysfs_emit(buf, "%d\n", val);
|
||||
abort:
|
||||
mutex_unlock(&data->config_lock);
|
||||
return ret;
|
||||
@ -450,7 +449,7 @@ static ssize_t ina226_alarm_show(struct device *dev,
|
||||
|
||||
alarm = (regval & BIT(attr->index)) &&
|
||||
(regval & INA226_ALERT_FUNCTION_FLAG);
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", alarm);
|
||||
return sysfs_emit(buf, "%d\n", alarm);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -481,7 +480,7 @@ static ssize_t ina2xx_shunt_show(struct device *dev,
|
||||
{
|
||||
struct ina2xx_data *data = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%li\n", data->rshunt);
|
||||
return sysfs_emit(buf, "%li\n", data->rshunt);
|
||||
}
|
||||
|
||||
static ssize_t ina2xx_shunt_store(struct device *dev,
|
||||
@ -537,7 +536,7 @@ static ssize_t ina226_interval_show(struct device *dev,
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", ina226_reg_to_interval(regval));
|
||||
return sysfs_emit(buf, "%d\n", ina226_reg_to_interval(regval));
|
||||
}
|
||||
|
||||
/* shunt voltage */
|
||||
|
@ -698,7 +698,7 @@ static ssize_t ina3221_shunt_show(struct device *dev,
|
||||
unsigned int channel = sd_attr->index;
|
||||
struct ina3221_input *input = &ina->inputs[channel];
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", input->shunt_resistor);
|
||||
return sysfs_emit(buf, "%d\n", input->shunt_resistor);
|
||||
}
|
||||
|
||||
static ssize_t ina3221_shunt_store(struct device *dev,
|
||||
|
@ -99,6 +99,50 @@ static const struct hwmon_channel_info *n3000bmc_hinfo[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct m10bmc_sdata d5005bmc_temp_tbl[] = {
|
||||
{ 0x100, 0x104, 0x108, 0x10c, 0x0, 500, "Board Inlet Air Temperature" },
|
||||
{ 0x110, 0x114, 0x118, 0x0, 0x0, 500, "FPGA Core Temperature" },
|
||||
{ 0x11c, 0x120, 0x124, 0x128, 0x0, 500, "Board Exhaust Air Temperature" },
|
||||
{ 0x12c, 0x130, 0x134, 0x0, 0x0, 500, "FPGA Transceiver Temperature" },
|
||||
{ 0x138, 0x13c, 0x140, 0x144, 0x0, 500, "RDIMM0 Temperature" },
|
||||
{ 0x148, 0x14c, 0x150, 0x154, 0x0, 500, "RDIMM1 Temperature" },
|
||||
{ 0x158, 0x15c, 0x160, 0x164, 0x0, 500, "RDIMM2 Temperature" },
|
||||
{ 0x168, 0x16c, 0x170, 0x174, 0x0, 500, "RDIMM3 Temperature" },
|
||||
{ 0x178, 0x17c, 0x180, 0x0, 0x0, 500, "QSFP0 Temperature" },
|
||||
{ 0x188, 0x18c, 0x190, 0x0, 0x0, 500, "QSFP1 Temperature" },
|
||||
{ 0x1a0, 0x1a4, 0x1a8, 0x0, 0x0, 500, "3.3v Temperature" },
|
||||
{ 0x1bc, 0x1c0, 0x1c4, 0x0, 0x0, 500, "VCCERAM Temperature" },
|
||||
{ 0x1d8, 0x1dc, 0x1e0, 0x0, 0x0, 500, "VCCR Temperature" },
|
||||
{ 0x1f4, 0x1f8, 0x1fc, 0x0, 0x0, 500, "VCCT Temperature" },
|
||||
{ 0x210, 0x214, 0x218, 0x0, 0x0, 500, "1.8v Temperature" },
|
||||
{ 0x22c, 0x230, 0x234, 0x0, 0x0, 500, "12v Backplane Temperature" },
|
||||
{ 0x248, 0x24c, 0x250, 0x0, 0x0, 500, "12v AUX Temperature" },
|
||||
};
|
||||
|
||||
static const struct m10bmc_sdata d5005bmc_in_tbl[] = {
|
||||
{ 0x184, 0x0, 0x0, 0x0, 0x0, 1, "QSFP0 Supply Voltage" },
|
||||
{ 0x194, 0x0, 0x0, 0x0, 0x0, 1, "QSFP1 Supply Voltage" },
|
||||
{ 0x198, 0x0, 0x0, 0x0, 0x0, 1, "FPGA Core Voltage" },
|
||||
{ 0x1ac, 0x1b0, 0x1b4, 0x0, 0x0, 1, "3.3v Voltage" },
|
||||
{ 0x1c8, 0x1cc, 0x1d0, 0x0, 0x0, 1, "VCCERAM Voltage" },
|
||||
{ 0x1e4, 0x1e8, 0x1ec, 0x0, 0x0, 1, "VCCR Voltage" },
|
||||
{ 0x200, 0x204, 0x208, 0x0, 0x0, 1, "VCCT Voltage" },
|
||||
{ 0x21c, 0x220, 0x224, 0x0, 0x0, 1, "1.8v Voltage" },
|
||||
{ 0x238, 0x0, 0x0, 0x0, 0x23c, 1, "12v Backplane Voltage" },
|
||||
{ 0x254, 0x0, 0x0, 0x0, 0x258, 1, "12v AUX Voltage" },
|
||||
};
|
||||
|
||||
static const struct m10bmc_sdata d5005bmc_curr_tbl[] = {
|
||||
{ 0x19c, 0x0, 0x0, 0x0, 0x0, 1, "FPGA Core Current" },
|
||||
{ 0x1b8, 0x0, 0x0, 0x0, 0x0, 1, "3.3v Current" },
|
||||
{ 0x1d4, 0x0, 0x0, 0x0, 0x0, 1, "VCCERAM Current" },
|
||||
{ 0x1f0, 0x0, 0x0, 0x0, 0x0, 1, "VCCR Current" },
|
||||
{ 0x20c, 0x0, 0x0, 0x0, 0x0, 1, "VCCT Current" },
|
||||
{ 0x228, 0x0, 0x0, 0x0, 0x0, 1, "1.8v Current" },
|
||||
{ 0x240, 0x244, 0x0, 0x0, 0x0, 1, "12v Backplane Current" },
|
||||
{ 0x25c, 0x260, 0x0, 0x0, 0x0, 1, "12v AUX Current" },
|
||||
};
|
||||
|
||||
static const struct m10bmc_hwmon_board_data n3000bmc_hwmon_bdata = {
|
||||
.tables = {
|
||||
[hwmon_temp] = n3000bmc_temp_tbl,
|
||||
@ -110,6 +154,80 @@ static const struct m10bmc_hwmon_board_data n3000bmc_hwmon_bdata = {
|
||||
.hinfo = n3000bmc_hinfo,
|
||||
};
|
||||
|
||||
static const struct hwmon_channel_info *d5005bmc_hinfo[] = {
|
||||
HWMON_CHANNEL_INFO(temp,
|
||||
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
|
||||
HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL,
|
||||
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
|
||||
HWMON_T_LABEL,
|
||||
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
|
||||
HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL,
|
||||
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
|
||||
HWMON_T_LABEL,
|
||||
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
|
||||
HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL,
|
||||
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
|
||||
HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL,
|
||||
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
|
||||
HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL,
|
||||
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
|
||||
HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL,
|
||||
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
|
||||
HWMON_T_LABEL,
|
||||
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
|
||||
HWMON_T_LABEL,
|
||||
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
|
||||
HWMON_T_LABEL,
|
||||
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
|
||||
HWMON_T_LABEL,
|
||||
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
|
||||
HWMON_T_LABEL,
|
||||
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
|
||||
HWMON_T_LABEL,
|
||||
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
|
||||
HWMON_T_LABEL,
|
||||
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
|
||||
HWMON_T_LABEL,
|
||||
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
|
||||
HWMON_T_LABEL),
|
||||
HWMON_CHANNEL_INFO(in,
|
||||
HWMON_I_INPUT | HWMON_I_LABEL,
|
||||
HWMON_I_INPUT | HWMON_I_LABEL,
|
||||
HWMON_I_INPUT | HWMON_I_LABEL,
|
||||
HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_CRIT |
|
||||
HWMON_I_LABEL,
|
||||
HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_CRIT |
|
||||
HWMON_I_LABEL,
|
||||
HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_CRIT |
|
||||
HWMON_I_LABEL,
|
||||
HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_CRIT |
|
||||
HWMON_I_LABEL,
|
||||
HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_CRIT |
|
||||
HWMON_I_LABEL,
|
||||
HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_LABEL,
|
||||
HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_LABEL),
|
||||
HWMON_CHANNEL_INFO(curr,
|
||||
HWMON_C_INPUT | HWMON_C_LABEL,
|
||||
HWMON_C_INPUT | HWMON_C_LABEL,
|
||||
HWMON_C_INPUT | HWMON_C_LABEL,
|
||||
HWMON_C_INPUT | HWMON_C_LABEL,
|
||||
HWMON_C_INPUT | HWMON_C_LABEL,
|
||||
HWMON_C_INPUT | HWMON_C_LABEL,
|
||||
HWMON_C_INPUT | HWMON_C_MAX | HWMON_C_LABEL,
|
||||
HWMON_C_INPUT | HWMON_C_MAX | HWMON_C_LABEL),
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct m10bmc_hwmon_board_data d5005bmc_hwmon_bdata = {
|
||||
.tables = {
|
||||
[hwmon_temp] = d5005bmc_temp_tbl,
|
||||
[hwmon_in] = d5005bmc_in_tbl,
|
||||
[hwmon_curr] = d5005bmc_curr_tbl,
|
||||
},
|
||||
|
||||
.hinfo = d5005bmc_hinfo,
|
||||
};
|
||||
|
||||
static umode_t
|
||||
m10bmc_hwmon_is_visible(const void *data, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel)
|
||||
@ -316,6 +434,10 @@ static const struct platform_device_id intel_m10bmc_hwmon_ids[] = {
|
||||
.name = "n3000bmc-hwmon",
|
||||
.driver_data = (unsigned long)&n3000bmc_hwmon_bdata,
|
||||
},
|
||||
{
|
||||
.name = "d5005bmc-hwmon",
|
||||
.driver_data = (unsigned long)&d5005bmc_hwmon_bdata,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
|
@ -1981,7 +1981,7 @@ static SENSOR_DEVICE_ATTR(in9_label, S_IRUGO, show_label, NULL, 3);
|
||||
static umode_t it87_in_is_visible(struct kobject *kobj,
|
||||
struct attribute *attr, int index)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct it87_data *data = dev_get_drvdata(dev);
|
||||
int i = index / 5; /* voltage index */
|
||||
int a = index % 5; /* attribute index */
|
||||
@ -2065,7 +2065,7 @@ static const struct attribute_group it87_group_in = {
|
||||
static umode_t it87_temp_is_visible(struct kobject *kobj,
|
||||
struct attribute *attr, int index)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct it87_data *data = dev_get_drvdata(dev);
|
||||
int i = index / 7; /* temperature index */
|
||||
int a = index % 7; /* attribute index */
|
||||
@ -2126,7 +2126,7 @@ static const struct attribute_group it87_group_temp = {
|
||||
static umode_t it87_is_visible(struct kobject *kobj,
|
||||
struct attribute *attr, int index)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct it87_data *data = dev_get_drvdata(dev);
|
||||
|
||||
if ((index == 2 || index == 3) && !data->has_vid)
|
||||
@ -2158,7 +2158,7 @@ static const struct attribute_group it87_group = {
|
||||
static umode_t it87_fan_is_visible(struct kobject *kobj,
|
||||
struct attribute *attr, int index)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct it87_data *data = dev_get_drvdata(dev);
|
||||
int i = index / 5; /* fan index */
|
||||
int a = index % 5; /* attribute index */
|
||||
@ -2229,7 +2229,7 @@ static const struct attribute_group it87_group_fan = {
|
||||
static umode_t it87_pwm_is_visible(struct kobject *kobj,
|
||||
struct attribute *attr, int index)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct it87_data *data = dev_get_drvdata(dev);
|
||||
int i = index / 4; /* pwm index */
|
||||
int a = index % 4; /* attribute index */
|
||||
@ -2290,7 +2290,7 @@ static const struct attribute_group it87_group_pwm = {
|
||||
static umode_t it87_auto_pwm_is_visible(struct kobject *kobj,
|
||||
struct attribute *attr, int index)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct it87_data *data = dev_get_drvdata(dev);
|
||||
int i = index / 11; /* pwm index */
|
||||
int a = index % 11; /* attribute index */
|
||||
|
@ -280,7 +280,7 @@ static ssize_t pem_bool_show(struct device *dev, struct device_attribute *da,
|
||||
return PTR_ERR(data);
|
||||
|
||||
status = data->data_string[attr->nr] & attr->index;
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", !!status);
|
||||
return sysfs_emit(buf, "%d\n", !!status);
|
||||
}
|
||||
|
||||
static ssize_t pem_data_show(struct device *dev, struct device_attribute *da,
|
||||
@ -296,7 +296,7 @@ static ssize_t pem_data_show(struct device *dev, struct device_attribute *da,
|
||||
value = pem_get_data(data->data_string, sizeof(data->data_string),
|
||||
attr->index);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%ld\n", value);
|
||||
return sysfs_emit(buf, "%ld\n", value);
|
||||
}
|
||||
|
||||
static ssize_t pem_input_show(struct device *dev, struct device_attribute *da,
|
||||
@ -312,7 +312,7 @@ static ssize_t pem_input_show(struct device *dev, struct device_attribute *da,
|
||||
value = pem_get_input(data->input_string, sizeof(data->input_string),
|
||||
attr->index);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%ld\n", value);
|
||||
return sysfs_emit(buf, "%ld\n", value);
|
||||
}
|
||||
|
||||
static ssize_t pem_fan_show(struct device *dev, struct device_attribute *da,
|
||||
@ -328,7 +328,7 @@ static ssize_t pem_fan_show(struct device *dev, struct device_attribute *da,
|
||||
value = pem_get_fan(data->fan_speed, sizeof(data->fan_speed),
|
||||
attr->index);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%ld\n", value);
|
||||
return sysfs_emit(buf, "%ld\n", value);
|
||||
}
|
||||
|
||||
/* Voltages */
|
||||
|
@ -931,7 +931,7 @@ static const struct attribute_group lm63_group_extra_lut = {
|
||||
static umode_t lm63_attribute_mode(struct kobject *kobj,
|
||||
struct attribute *attr, int index)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct lm63_data *data = dev_get_drvdata(dev);
|
||||
|
||||
if (attr == &sensor_dev_attr_temp2_crit.dev_attr.attr
|
||||
|
@ -226,7 +226,7 @@ static ssize_t ltc2945_value_show(struct device *dev,
|
||||
value = ltc2945_reg_to_val(dev, attr->index);
|
||||
if (value < 0)
|
||||
return value;
|
||||
return snprintf(buf, PAGE_SIZE, "%lld\n", value);
|
||||
return sysfs_emit(buf, "%lld\n", value);
|
||||
}
|
||||
|
||||
static ssize_t ltc2945_value_store(struct device *dev,
|
||||
@ -333,7 +333,7 @@ static ssize_t ltc2945_bool_show(struct device *dev,
|
||||
if (fault) /* Clear reported faults in chip register */
|
||||
regmap_update_bits(regmap, LTC2945_FAULT, attr->index, 0);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", !!fault);
|
||||
return sysfs_emit(buf, "%d\n", !!fault);
|
||||
}
|
||||
|
||||
/* Input voltages */
|
||||
|
@ -147,13 +147,13 @@ static ssize_t ltc2990_value_show(struct device *dev,
|
||||
if (unlikely(ret < 0))
|
||||
return ret;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", value);
|
||||
return sysfs_emit(buf, "%d\n", value);
|
||||
}
|
||||
|
||||
static umode_t ltc2990_attrs_visible(struct kobject *kobj,
|
||||
struct attribute *a, int n)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct ltc2990_data *data = dev_get_drvdata(dev);
|
||||
struct device_attribute *da =
|
||||
container_of(a, struct device_attribute, attr);
|
||||
|
@ -128,7 +128,7 @@ static ssize_t ltc4151_value_show(struct device *dev,
|
||||
return PTR_ERR(data);
|
||||
|
||||
value = ltc4151_get_value(data, attr->index);
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", value);
|
||||
return sysfs_emit(buf, "%d\n", value);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -139,7 +139,7 @@ static ssize_t ltc4215_voltage_show(struct device *dev,
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
|
||||
const int voltage = ltc4215_get_voltage(dev, attr->index);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", voltage);
|
||||
return sysfs_emit(buf, "%d\n", voltage);
|
||||
}
|
||||
|
||||
static ssize_t ltc4215_current_show(struct device *dev,
|
||||
@ -147,7 +147,7 @@ static ssize_t ltc4215_current_show(struct device *dev,
|
||||
{
|
||||
const unsigned int curr = ltc4215_get_current(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", curr);
|
||||
return sysfs_emit(buf, "%u\n", curr);
|
||||
}
|
||||
|
||||
static ssize_t ltc4215_power_show(struct device *dev,
|
||||
@ -159,7 +159,7 @@ static ssize_t ltc4215_power_show(struct device *dev,
|
||||
/* current in mA * voltage in mV == power in uW */
|
||||
const unsigned int power = abs(output_voltage * curr);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", power);
|
||||
return sysfs_emit(buf, "%u\n", power);
|
||||
}
|
||||
|
||||
static ssize_t ltc4215_alarm_show(struct device *dev,
|
||||
@ -170,7 +170,7 @@ static ssize_t ltc4215_alarm_show(struct device *dev,
|
||||
const u8 reg = data->regs[LTC4215_STATUS];
|
||||
const u32 mask = attr->index;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", !!(reg & mask));
|
||||
return sysfs_emit(buf, "%u\n", !!(reg & mask));
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -94,7 +94,7 @@ static ssize_t ltc4222_value_show(struct device *dev,
|
||||
value = ltc4222_get_value(dev, attr->index);
|
||||
if (value < 0)
|
||||
return value;
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", value);
|
||||
return sysfs_emit(buf, "%d\n", value);
|
||||
}
|
||||
|
||||
static ssize_t ltc4222_bool_show(struct device *dev,
|
||||
@ -112,7 +112,7 @@ static ssize_t ltc4222_bool_show(struct device *dev,
|
||||
if (fault) /* Clear reported faults in chip register */
|
||||
regmap_update_bits(regmap, attr->nr, attr->index, 0);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", !!fault);
|
||||
return sysfs_emit(buf, "%d\n", !!fault);
|
||||
}
|
||||
|
||||
/* Voltages */
|
||||
|
@ -79,7 +79,7 @@ static ssize_t ltc4260_value_show(struct device *dev,
|
||||
value = ltc4260_get_value(dev, attr->index);
|
||||
if (value < 0)
|
||||
return value;
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", value);
|
||||
return sysfs_emit(buf, "%d\n", value);
|
||||
}
|
||||
|
||||
static ssize_t ltc4260_bool_show(struct device *dev,
|
||||
@ -98,7 +98,7 @@ static ssize_t ltc4260_bool_show(struct device *dev,
|
||||
if (fault) /* Clear reported faults in chip register */
|
||||
regmap_update_bits(regmap, LTC4260_FAULT, attr->index, 0);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", !!fault);
|
||||
return sysfs_emit(buf, "%d\n", !!fault);
|
||||
}
|
||||
|
||||
/* Voltages */
|
||||
|
@ -130,7 +130,7 @@ static ssize_t ltc4261_value_show(struct device *dev,
|
||||
return PTR_ERR(data);
|
||||
|
||||
value = ltc4261_get_value(data, attr->index);
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", value);
|
||||
return sysfs_emit(buf, "%d\n", value);
|
||||
}
|
||||
|
||||
static ssize_t ltc4261_bool_show(struct device *dev,
|
||||
@ -147,7 +147,7 @@ static ssize_t ltc4261_bool_show(struct device *dev,
|
||||
if (fault) /* Clear reported faults in chip register */
|
||||
i2c_smbus_write_byte_data(data->client, LTC4261_FAULT, ~fault);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", fault ? 1 : 0);
|
||||
return sysfs_emit(buf, "%d\n", fault ? 1 : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -187,7 +187,7 @@ static ssize_t max16065_alarm_show(struct device *dev,
|
||||
i2c_smbus_write_byte_data(data->client,
|
||||
MAX16065_FAULT(attr2->nr), val);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", !!val);
|
||||
return sysfs_emit(buf, "%d\n", !!val);
|
||||
}
|
||||
|
||||
static ssize_t max16065_input_show(struct device *dev,
|
||||
@ -200,8 +200,8 @@ static ssize_t max16065_input_show(struct device *dev,
|
||||
if (unlikely(adc < 0))
|
||||
return adc;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n",
|
||||
ADC_TO_MV(adc, data->range[attr->index]));
|
||||
return sysfs_emit(buf, "%d\n",
|
||||
ADC_TO_MV(adc, data->range[attr->index]));
|
||||
}
|
||||
|
||||
static ssize_t max16065_current_show(struct device *dev,
|
||||
@ -212,8 +212,8 @@ static ssize_t max16065_current_show(struct device *dev,
|
||||
if (unlikely(data->curr_sense < 0))
|
||||
return data->curr_sense;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n",
|
||||
ADC_TO_CURR(data->curr_sense, data->curr_gain));
|
||||
return sysfs_emit(buf, "%d\n",
|
||||
ADC_TO_CURR(data->curr_sense, data->curr_gain));
|
||||
}
|
||||
|
||||
static ssize_t max16065_limit_store(struct device *dev,
|
||||
@ -249,8 +249,8 @@ static ssize_t max16065_limit_show(struct device *dev,
|
||||
struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(da);
|
||||
struct max16065_data *data = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n",
|
||||
data->limit[attr2->nr][attr2->index]);
|
||||
return sysfs_emit(buf, "%d\n",
|
||||
data->limit[attr2->nr][attr2->index]);
|
||||
}
|
||||
|
||||
/* Construct a sensor_device_attribute structure for each register */
|
||||
@ -454,7 +454,7 @@ static struct attribute *max16065_max_attributes[] = {
|
||||
static umode_t max16065_basic_is_visible(struct kobject *kobj,
|
||||
struct attribute *a, int n)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct max16065_data *data = dev_get_drvdata(dev);
|
||||
int index = n / 4;
|
||||
|
||||
@ -466,7 +466,7 @@ static umode_t max16065_basic_is_visible(struct kobject *kobj,
|
||||
static umode_t max16065_secondary_is_visible(struct kobject *kobj,
|
||||
struct attribute *a, int index)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct max16065_data *data = dev_get_drvdata(dev);
|
||||
|
||||
if (index >= data->num_adc)
|
||||
|
@ -460,7 +460,7 @@ static DEVICE_ATTR(dummy, 0, NULL, NULL);
|
||||
static umode_t max6697_is_visible(struct kobject *kobj, struct attribute *attr,
|
||||
int index)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct max6697_data *data = dev_get_drvdata(dev);
|
||||
const struct max6697_chip_data *chip = data->chip;
|
||||
int channel = index / 7; /* channel number */
|
||||
|
@ -67,11 +67,13 @@
|
||||
* @connected: indicates if tachometer is connected;
|
||||
* @reg: register offset;
|
||||
* @mask: fault mask;
|
||||
* @prsnt: present register offset;
|
||||
*/
|
||||
struct mlxreg_fan_tacho {
|
||||
bool connected;
|
||||
u32 reg;
|
||||
u32 mask;
|
||||
u32 prsnt;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -92,6 +94,7 @@ struct mlxreg_fan_pwm {
|
||||
* @regmap: register map of parent device;
|
||||
* @tacho: tachometer data;
|
||||
* @pwm: PWM data;
|
||||
* @tachos_per_drwr - number of tachometers per drawer;
|
||||
* @samples: minimum allowed samples per pulse;
|
||||
* @divider: divider value for tachometer RPM calculation;
|
||||
* @cooling: cooling device levels;
|
||||
@ -103,6 +106,7 @@ struct mlxreg_fan {
|
||||
struct mlxreg_core_platform_data *pdata;
|
||||
struct mlxreg_fan_tacho tacho[MLXREG_FAN_MAX_TACHO];
|
||||
struct mlxreg_fan_pwm pwm;
|
||||
int tachos_per_drwr;
|
||||
int samples;
|
||||
int divider;
|
||||
u8 cooling_levels[MLXREG_FAN_MAX_STATE + 1];
|
||||
@ -123,6 +127,26 @@ mlxreg_fan_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
|
||||
tacho = &fan->tacho[channel];
|
||||
switch (attr) {
|
||||
case hwmon_fan_input:
|
||||
/*
|
||||
* Check FAN presence: FAN related bit in presence register is one,
|
||||
* if FAN is physically connected, zero - otherwise.
|
||||
*/
|
||||
if (tacho->prsnt && fan->tachos_per_drwr) {
|
||||
err = regmap_read(fan->regmap, tacho->prsnt, ®val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/*
|
||||
* Map channel to presence bit - drawer can be equipped with
|
||||
* one or few FANs, while presence is indicated per drawer.
|
||||
*/
|
||||
if (BIT(channel / fan->tachos_per_drwr) & regval) {
|
||||
/* FAN is not connected - return zero for FAN speed. */
|
||||
*val = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
err = regmap_read(fan->regmap, tacho->reg, ®val);
|
||||
if (err)
|
||||
return err;
|
||||
@ -389,8 +413,8 @@ static int mlxreg_fan_config(struct mlxreg_fan *fan,
|
||||
struct mlxreg_core_platform_data *pdata)
|
||||
{
|
||||
struct mlxreg_core_data *data = pdata->data;
|
||||
int tacho_num = 0, tacho_avail = 0, i;
|
||||
bool configured = false;
|
||||
int tacho_num = 0, i;
|
||||
int err;
|
||||
|
||||
fan->samples = MLXREG_FAN_TACHO_SAMPLES_PER_PULSE_DEF;
|
||||
@ -415,7 +439,9 @@ static int mlxreg_fan_config(struct mlxreg_fan *fan,
|
||||
|
||||
fan->tacho[tacho_num].reg = data->reg;
|
||||
fan->tacho[tacho_num].mask = data->mask;
|
||||
fan->tacho[tacho_num].prsnt = data->reg_prsnt;
|
||||
fan->tacho[tacho_num++].connected = true;
|
||||
tacho_avail++;
|
||||
} else if (strnstr(data->label, "pwm", sizeof(data->label))) {
|
||||
if (fan->pwm.connected) {
|
||||
dev_err(fan->dev, "duplicate pwm entry: %s\n",
|
||||
@ -453,6 +479,29 @@ static int mlxreg_fan_config(struct mlxreg_fan *fan,
|
||||
}
|
||||
}
|
||||
|
||||
if (pdata->capability) {
|
||||
int drwr_avail;
|
||||
u32 regval;
|
||||
|
||||
/* Obtain the number of FAN drawers, supported by system. */
|
||||
err = regmap_read(fan->regmap, pdata->capability, ®val);
|
||||
if (err) {
|
||||
dev_err(fan->dev, "Failed to query capability register 0x%08x\n",
|
||||
pdata->capability);
|
||||
return err;
|
||||
}
|
||||
|
||||
drwr_avail = hweight32(regval);
|
||||
if (!tacho_avail || !drwr_avail || tacho_avail < drwr_avail) {
|
||||
dev_err(fan->dev, "Configuration is invalid: drawers num %d tachos num %d\n",
|
||||
drwr_avail, tacho_avail);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Set the number of tachometers per one drawer. */
|
||||
fan->tachos_per_drwr = tacho_avail / drwr_avail;
|
||||
}
|
||||
|
||||
/* Init cooling levels per PWM state. */
|
||||
for (i = 0; i < MLXREG_FAN_SPEED_MIN_LEVEL; i++)
|
||||
fan->cooling_levels[i] = MLXREG_FAN_SPEED_MIN_LEVEL;
|
||||
|
@ -1,7 +1,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* nct6683 - Driver for the hardware monitoring functionality of
|
||||
* Nuvoton NCT6683D/NCT6687D eSIO
|
||||
* Nuvoton NCT6683D/NCT6686D/NCT6687D eSIO
|
||||
*
|
||||
* Copyright (C) 2013 Guenter Roeck <linux@roeck-us.net>
|
||||
*
|
||||
@ -12,6 +12,7 @@
|
||||
*
|
||||
* Chip #vin #fan #pwm #temp chip ID
|
||||
* nct6683d 21(1) 16 8 32(1) 0xc730
|
||||
* nct6686d 21(1) 16 8 32(1) 0xd440
|
||||
* nct6687d 21(1) 16 8 32(1) 0xd590
|
||||
*
|
||||
* Notes:
|
||||
@ -33,7 +34,7 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
enum kinds { nct6683, nct6687 };
|
||||
enum kinds { nct6683, nct6686, nct6687 };
|
||||
|
||||
static bool force;
|
||||
module_param(force, bool, 0);
|
||||
@ -41,11 +42,13 @@ MODULE_PARM_DESC(force, "Set to one to enable support for unknown vendors");
|
||||
|
||||
static const char * const nct6683_device_names[] = {
|
||||
"nct6683",
|
||||
"nct6686",
|
||||
"nct6687",
|
||||
};
|
||||
|
||||
static const char * const nct6683_chip_names[] = {
|
||||
"NCT6683D",
|
||||
"NCT6686D",
|
||||
"NCT6687D",
|
||||
};
|
||||
|
||||
@ -66,6 +69,7 @@ static const char * const nct6683_chip_names[] = {
|
||||
|
||||
#define SIO_NCT6681_ID 0xb270 /* for later */
|
||||
#define SIO_NCT6683_ID 0xc730
|
||||
#define SIO_NCT6686_ID 0xd440
|
||||
#define SIO_NCT6687_ID 0xd590
|
||||
#define SIO_ID_MASK 0xFFF0
|
||||
|
||||
@ -488,17 +492,6 @@ static inline long in_from_reg(u16 reg, u8 src)
|
||||
return reg * scale;
|
||||
}
|
||||
|
||||
static inline u16 in_to_reg(u32 val, u8 src)
|
||||
{
|
||||
int scale = 16;
|
||||
|
||||
if (src == MON_SRC_VCC || src == MON_SRC_VSB || src == MON_SRC_AVSB ||
|
||||
src == MON_SRC_VBAT)
|
||||
scale <<= 1;
|
||||
|
||||
return clamp_val(DIV_ROUND_CLOSEST(val, scale), 0, 127);
|
||||
}
|
||||
|
||||
static u16 nct6683_read(struct nct6683_data *data, u16 reg)
|
||||
{
|
||||
int res;
|
||||
@ -1362,6 +1355,9 @@ static int __init nct6683_find(int sioaddr, struct nct6683_sio_data *sio_data)
|
||||
case SIO_NCT6683_ID:
|
||||
sio_data->kind = nct6683;
|
||||
break;
|
||||
case SIO_NCT6686_ID:
|
||||
sio_data->kind = nct6686;
|
||||
break;
|
||||
case SIO_NCT6687_ID:
|
||||
sio_data->kind = nct6687;
|
||||
break;
|
||||
|
234
drivers/hwmon/nzxt-kraken2.c
Normal file
234
drivers/hwmon/nzxt-kraken2.c
Normal file
@ -0,0 +1,234 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* nzxt-kraken2.c - hwmon driver for NZXT Kraken X42/X52/X62/X72 coolers
|
||||
*
|
||||
* The device asynchronously sends HID reports (with id 0x04) twice a second to
|
||||
* communicate current fan speed, pump speed and coolant temperature. The
|
||||
* device does not respond to Get_Report requests for this status report.
|
||||
*
|
||||
* Copyright 2019-2021 Jonas Malaco <jonas@protocubo.io>
|
||||
*/
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/hid.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#define STATUS_REPORT_ID 0x04
|
||||
#define STATUS_VALIDITY 2 /* seconds; equivalent to 4 missed updates */
|
||||
|
||||
static const char *const kraken2_temp_label[] = {
|
||||
"Coolant",
|
||||
};
|
||||
|
||||
static const char *const kraken2_fan_label[] = {
|
||||
"Fan",
|
||||
"Pump",
|
||||
};
|
||||
|
||||
struct kraken2_priv_data {
|
||||
struct hid_device *hid_dev;
|
||||
struct device *hwmon_dev;
|
||||
s32 temp_input[1];
|
||||
u16 fan_input[2];
|
||||
unsigned long updated; /* jiffies */
|
||||
};
|
||||
|
||||
static umode_t kraken2_is_visible(const void *data,
|
||||
enum hwmon_sensor_types type,
|
||||
u32 attr, int channel)
|
||||
{
|
||||
return 0444;
|
||||
}
|
||||
|
||||
static int kraken2_read(struct device *dev, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel, long *val)
|
||||
{
|
||||
struct kraken2_priv_data *priv = dev_get_drvdata(dev);
|
||||
|
||||
if (time_after(jiffies, priv->updated + STATUS_VALIDITY * HZ))
|
||||
return -ENODATA;
|
||||
|
||||
switch (type) {
|
||||
case hwmon_temp:
|
||||
*val = priv->temp_input[channel];
|
||||
break;
|
||||
case hwmon_fan:
|
||||
*val = priv->fan_input[channel];
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP; /* unreachable */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kraken2_read_string(struct device *dev, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel, const char **str)
|
||||
{
|
||||
switch (type) {
|
||||
case hwmon_temp:
|
||||
*str = kraken2_temp_label[channel];
|
||||
break;
|
||||
case hwmon_fan:
|
||||
*str = kraken2_fan_label[channel];
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP; /* unreachable */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct hwmon_ops kraken2_hwmon_ops = {
|
||||
.is_visible = kraken2_is_visible,
|
||||
.read = kraken2_read,
|
||||
.read_string = kraken2_read_string,
|
||||
};
|
||||
|
||||
static const struct hwmon_channel_info *kraken2_info[] = {
|
||||
HWMON_CHANNEL_INFO(temp,
|
||||
HWMON_T_INPUT | HWMON_T_LABEL),
|
||||
HWMON_CHANNEL_INFO(fan,
|
||||
HWMON_F_INPUT | HWMON_F_LABEL,
|
||||
HWMON_F_INPUT | HWMON_F_LABEL),
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct hwmon_chip_info kraken2_chip_info = {
|
||||
.ops = &kraken2_hwmon_ops,
|
||||
.info = kraken2_info,
|
||||
};
|
||||
|
||||
static int kraken2_raw_event(struct hid_device *hdev,
|
||||
struct hid_report *report, u8 *data, int size)
|
||||
{
|
||||
struct kraken2_priv_data *priv;
|
||||
|
||||
if (size < 7 || report->id != STATUS_REPORT_ID)
|
||||
return 0;
|
||||
|
||||
priv = hid_get_drvdata(hdev);
|
||||
|
||||
/*
|
||||
* The fractional byte of the coolant temperature has been observed to
|
||||
* be in the interval [1,9], but some of these steps are also
|
||||
* consistently skipped for certain integer parts.
|
||||
*
|
||||
* For the lack of a better idea, assume that the resolution is 0.1°C,
|
||||
* and that the missing steps are artifacts of how the firmware
|
||||
* processes the raw sensor data.
|
||||
*/
|
||||
priv->temp_input[0] = data[1] * 1000 + data[2] * 100;
|
||||
|
||||
priv->fan_input[0] = get_unaligned_be16(data + 3);
|
||||
priv->fan_input[1] = get_unaligned_be16(data + 5);
|
||||
|
||||
priv->updated = jiffies;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int kraken2_probe(struct hid_device *hdev,
|
||||
const struct hid_device_id *id)
|
||||
{
|
||||
struct kraken2_priv_data *priv;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(&hdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->hid_dev = hdev;
|
||||
hid_set_drvdata(hdev, priv);
|
||||
|
||||
/*
|
||||
* Initialize ->updated to STATUS_VALIDITY seconds in the past, making
|
||||
* the initial empty data invalid for kraken2_read without the need for
|
||||
* a special case there.
|
||||
*/
|
||||
priv->updated = jiffies - STATUS_VALIDITY * HZ;
|
||||
|
||||
ret = hid_parse(hdev);
|
||||
if (ret) {
|
||||
hid_err(hdev, "hid parse failed with %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable hidraw so existing user-space tools can continue to work.
|
||||
*/
|
||||
ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
|
||||
if (ret) {
|
||||
hid_err(hdev, "hid hw start failed with %d\n", ret);
|
||||
goto fail_and_stop;
|
||||
}
|
||||
|
||||
ret = hid_hw_open(hdev);
|
||||
if (ret) {
|
||||
hid_err(hdev, "hid hw open failed with %d\n", ret);
|
||||
goto fail_and_close;
|
||||
}
|
||||
|
||||
priv->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "kraken2",
|
||||
priv, &kraken2_chip_info,
|
||||
NULL);
|
||||
if (IS_ERR(priv->hwmon_dev)) {
|
||||
ret = PTR_ERR(priv->hwmon_dev);
|
||||
hid_err(hdev, "hwmon registration failed with %d\n", ret);
|
||||
goto fail_and_close;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail_and_close:
|
||||
hid_hw_close(hdev);
|
||||
fail_and_stop:
|
||||
hid_hw_stop(hdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void kraken2_remove(struct hid_device *hdev)
|
||||
{
|
||||
struct kraken2_priv_data *priv = hid_get_drvdata(hdev);
|
||||
|
||||
hwmon_device_unregister(priv->hwmon_dev);
|
||||
|
||||
hid_hw_close(hdev);
|
||||
hid_hw_stop(hdev);
|
||||
}
|
||||
|
||||
static const struct hid_device_id kraken2_table[] = {
|
||||
{ HID_USB_DEVICE(0x1e71, 0x170e) }, /* NZXT Kraken X42/X52/X62/X72 */
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(hid, kraken2_table);
|
||||
|
||||
static struct hid_driver kraken2_driver = {
|
||||
.name = "nzxt-kraken2",
|
||||
.id_table = kraken2_table,
|
||||
.probe = kraken2_probe,
|
||||
.remove = kraken2_remove,
|
||||
.raw_event = kraken2_raw_event,
|
||||
};
|
||||
|
||||
static int __init kraken2_init(void)
|
||||
{
|
||||
return hid_register_driver(&kraken2_driver);
|
||||
}
|
||||
|
||||
static void __exit kraken2_exit(void)
|
||||
{
|
||||
hid_unregister_driver(&kraken2_driver);
|
||||
}
|
||||
|
||||
/*
|
||||
* When compiled into the kernel, initialize after the hid bus.
|
||||
*/
|
||||
late_initcall(kraken2_init);
|
||||
module_exit(kraken2_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Jonas Malaco <jonas@protocubo.io>");
|
||||
MODULE_DESCRIPTION("Hwmon driver for NZXT Kraken X42/X52/X62/X72 coolers");
|
@ -261,7 +261,7 @@ static ssize_t occ_show_temp_1(struct device *dev,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return snprintf(buf, PAGE_SIZE - 1, "%u\n", val);
|
||||
return sysfs_emit(buf, "%u\n", val);
|
||||
}
|
||||
|
||||
static ssize_t occ_show_temp_2(struct device *dev,
|
||||
@ -312,7 +312,7 @@ static ssize_t occ_show_temp_2(struct device *dev,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return snprintf(buf, PAGE_SIZE - 1, "%u\n", val);
|
||||
return sysfs_emit(buf, "%u\n", val);
|
||||
}
|
||||
|
||||
static ssize_t occ_show_temp_10(struct device *dev,
|
||||
@ -366,7 +366,7 @@ static ssize_t occ_show_temp_10(struct device *dev,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return snprintf(buf, PAGE_SIZE - 1, "%u\n", val);
|
||||
return sysfs_emit(buf, "%u\n", val);
|
||||
}
|
||||
|
||||
static ssize_t occ_show_freq_1(struct device *dev,
|
||||
@ -396,7 +396,7 @@ static ssize_t occ_show_freq_1(struct device *dev,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return snprintf(buf, PAGE_SIZE - 1, "%u\n", val);
|
||||
return sysfs_emit(buf, "%u\n", val);
|
||||
}
|
||||
|
||||
static ssize_t occ_show_freq_2(struct device *dev,
|
||||
@ -426,7 +426,7 @@ static ssize_t occ_show_freq_2(struct device *dev,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return snprintf(buf, PAGE_SIZE - 1, "%u\n", val);
|
||||
return sysfs_emit(buf, "%u\n", val);
|
||||
}
|
||||
|
||||
static ssize_t occ_show_power_1(struct device *dev,
|
||||
@ -465,7 +465,7 @@ static ssize_t occ_show_power_1(struct device *dev,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return snprintf(buf, PAGE_SIZE - 1, "%llu\n", val);
|
||||
return sysfs_emit(buf, "%llu\n", val);
|
||||
}
|
||||
|
||||
static u64 occ_get_powr_avg(u64 *accum, u32 *samples)
|
||||
@ -494,9 +494,9 @@ static ssize_t occ_show_power_2(struct device *dev,
|
||||
|
||||
switch (sattr->nr) {
|
||||
case 0:
|
||||
return snprintf(buf, PAGE_SIZE - 1, "%u_%u_%u\n",
|
||||
get_unaligned_be32(&power->sensor_id),
|
||||
power->function_id, power->apss_channel);
|
||||
return sysfs_emit(buf, "%u_%u_%u\n",
|
||||
get_unaligned_be32(&power->sensor_id),
|
||||
power->function_id, power->apss_channel);
|
||||
case 1:
|
||||
val = occ_get_powr_avg(&power->accumulator,
|
||||
&power->update_tag);
|
||||
@ -512,7 +512,7 @@ static ssize_t occ_show_power_2(struct device *dev,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return snprintf(buf, PAGE_SIZE - 1, "%llu\n", val);
|
||||
return sysfs_emit(buf, "%llu\n", val);
|
||||
}
|
||||
|
||||
static ssize_t occ_show_power_a0(struct device *dev,
|
||||
@ -533,8 +533,8 @@ static ssize_t occ_show_power_a0(struct device *dev,
|
||||
|
||||
switch (sattr->nr) {
|
||||
case 0:
|
||||
return snprintf(buf, PAGE_SIZE - 1, "%u_system\n",
|
||||
get_unaligned_be32(&power->sensor_id));
|
||||
return sysfs_emit(buf, "%u_system\n",
|
||||
get_unaligned_be32(&power->sensor_id));
|
||||
case 1:
|
||||
val = occ_get_powr_avg(&power->system.accumulator,
|
||||
&power->system.update_tag);
|
||||
@ -547,8 +547,8 @@ static ssize_t occ_show_power_a0(struct device *dev,
|
||||
val = get_unaligned_be16(&power->system.value) * 1000000ULL;
|
||||
break;
|
||||
case 4:
|
||||
return snprintf(buf, PAGE_SIZE - 1, "%u_proc\n",
|
||||
get_unaligned_be32(&power->sensor_id));
|
||||
return sysfs_emit(buf, "%u_proc\n",
|
||||
get_unaligned_be32(&power->sensor_id));
|
||||
case 5:
|
||||
val = occ_get_powr_avg(&power->proc.accumulator,
|
||||
&power->proc.update_tag);
|
||||
@ -561,8 +561,8 @@ static ssize_t occ_show_power_a0(struct device *dev,
|
||||
val = get_unaligned_be16(&power->proc.value) * 1000000ULL;
|
||||
break;
|
||||
case 8:
|
||||
return snprintf(buf, PAGE_SIZE - 1, "%u_vdd\n",
|
||||
get_unaligned_be32(&power->sensor_id));
|
||||
return sysfs_emit(buf, "%u_vdd\n",
|
||||
get_unaligned_be32(&power->sensor_id));
|
||||
case 9:
|
||||
val = occ_get_powr_avg(&power->vdd.accumulator,
|
||||
&power->vdd.update_tag);
|
||||
@ -575,8 +575,8 @@ static ssize_t occ_show_power_a0(struct device *dev,
|
||||
val = get_unaligned_be16(&power->vdd.value) * 1000000ULL;
|
||||
break;
|
||||
case 12:
|
||||
return snprintf(buf, PAGE_SIZE - 1, "%u_vdn\n",
|
||||
get_unaligned_be32(&power->sensor_id));
|
||||
return sysfs_emit(buf, "%u_vdn\n",
|
||||
get_unaligned_be32(&power->sensor_id));
|
||||
case 13:
|
||||
val = occ_get_powr_avg(&power->vdn.accumulator,
|
||||
&power->vdn.update_tag);
|
||||
@ -592,7 +592,7 @@ static ssize_t occ_show_power_a0(struct device *dev,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return snprintf(buf, PAGE_SIZE - 1, "%llu\n", val);
|
||||
return sysfs_emit(buf, "%llu\n", val);
|
||||
}
|
||||
|
||||
static ssize_t occ_show_caps_1_2(struct device *dev,
|
||||
@ -613,7 +613,7 @@ static ssize_t occ_show_caps_1_2(struct device *dev,
|
||||
|
||||
switch (sattr->nr) {
|
||||
case 0:
|
||||
return snprintf(buf, PAGE_SIZE - 1, "system\n");
|
||||
return sysfs_emit(buf, "system\n");
|
||||
case 1:
|
||||
val = get_unaligned_be16(&caps->cap) * 1000000ULL;
|
||||
break;
|
||||
@ -642,7 +642,7 @@ static ssize_t occ_show_caps_1_2(struct device *dev,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return snprintf(buf, PAGE_SIZE - 1, "%llu\n", val);
|
||||
return sysfs_emit(buf, "%llu\n", val);
|
||||
}
|
||||
|
||||
static ssize_t occ_show_caps_3(struct device *dev,
|
||||
@ -663,7 +663,7 @@ static ssize_t occ_show_caps_3(struct device *dev,
|
||||
|
||||
switch (sattr->nr) {
|
||||
case 0:
|
||||
return snprintf(buf, PAGE_SIZE - 1, "system\n");
|
||||
return sysfs_emit(buf, "system\n");
|
||||
case 1:
|
||||
val = get_unaligned_be16(&caps->cap) * 1000000ULL;
|
||||
break;
|
||||
@ -689,7 +689,7 @@ static ssize_t occ_show_caps_3(struct device *dev,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return snprintf(buf, PAGE_SIZE - 1, "%llu\n", val);
|
||||
return sysfs_emit(buf, "%llu\n", val);
|
||||
}
|
||||
|
||||
static ssize_t occ_store_caps_user(struct device *dev,
|
||||
@ -732,21 +732,22 @@ static ssize_t occ_show_extended(struct device *dev,
|
||||
|
||||
switch (sattr->nr) {
|
||||
case 0:
|
||||
if (extn->flags & EXTN_FLAG_SENSOR_ID)
|
||||
rc = snprintf(buf, PAGE_SIZE - 1, "%u",
|
||||
get_unaligned_be32(&extn->sensor_id));
|
||||
else
|
||||
rc = snprintf(buf, PAGE_SIZE - 1, "%02x%02x%02x%02x\n",
|
||||
extn->name[0], extn->name[1],
|
||||
extn->name[2], extn->name[3]);
|
||||
if (extn->flags & EXTN_FLAG_SENSOR_ID) {
|
||||
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]);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
rc = snprintf(buf, PAGE_SIZE - 1, "%02x\n", extn->flags);
|
||||
rc = sysfs_emit(buf, "%02x\n", extn->flags);
|
||||
break;
|
||||
case 2:
|
||||
rc = snprintf(buf, PAGE_SIZE - 1, "%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, "%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]);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
@ -67,7 +67,7 @@ static ssize_t occ_sysfs_show(struct device *dev,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return snprintf(buf, PAGE_SIZE - 1, "%d\n", val);
|
||||
return sysfs_emit(buf, "%d\n", val);
|
||||
}
|
||||
|
||||
static ssize_t occ_error_show(struct device *dev,
|
||||
@ -77,7 +77,7 @@ static ssize_t occ_error_show(struct device *dev,
|
||||
|
||||
occ_update_response(occ);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE - 1, "%d\n", occ->error);
|
||||
return sysfs_emit(buf, "%d\n", occ->error);
|
||||
}
|
||||
|
||||
static SENSOR_DEVICE_ATTR(occ_master, 0444, occ_sysfs_show, NULL, 0);
|
||||
|
@ -56,6 +56,25 @@ config SENSORS_BEL_PFE
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called bel-pfe.
|
||||
|
||||
config SENSORS_BPA_RS600
|
||||
tristate "BluTek BPA-RS600 Power Supplies"
|
||||
help
|
||||
If you say yes here you get hardware monitoring support for BluTek
|
||||
BPA-RS600 Power Supplies.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called bpa-rs600.
|
||||
|
||||
config SENSORS_FSP_3Y
|
||||
tristate "FSP/3Y-Power power supplies"
|
||||
help
|
||||
If you say yes here you get hardware monitoring support for
|
||||
FSP/3Y-Power hot-swap power supplies.
|
||||
Supported models: YH-5151E, YM-2151E
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called fsp-3y.
|
||||
|
||||
config SENSORS_IBM_CFFPS
|
||||
tristate "IBM Common Form Factor Power Supply"
|
||||
depends on LEDS_CLASS
|
||||
@ -84,6 +103,15 @@ config SENSORS_IR35221
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called ir35221.
|
||||
|
||||
config SENSORS_IR36021
|
||||
tristate "Infineon IR36021"
|
||||
help
|
||||
If you say yes here you get hardware monitoring support for Infineon
|
||||
IR36021.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called ir36021.
|
||||
|
||||
config SENSORS_IR38064
|
||||
tristate "Infineon IR38064"
|
||||
help
|
||||
@ -148,6 +176,15 @@ config SENSORS_LTC3815
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called ltc3815.
|
||||
|
||||
config SENSORS_MAX15301
|
||||
tristate "Maxim MAX15301"
|
||||
help
|
||||
If you say yes here you get hardware monitoring support for Maxim
|
||||
MAX15301, as well as for Flex BMR461.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called max15301.
|
||||
|
||||
config SENSORS_MAX16064
|
||||
tristate "Maxim MAX16064"
|
||||
help
|
||||
@ -247,6 +284,16 @@ config SENSORS_Q54SJ108A2
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called q54sj108a2.
|
||||
|
||||
config SENSORS_STPDDC60
|
||||
tristate "ST STPDDC60"
|
||||
help
|
||||
If you say yes here you get hardware monitoring support for ST
|
||||
STPDDC60 Universal Digital Multicell Controller, as well as for
|
||||
Flex BMR481.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called stpddc60.
|
||||
|
||||
config SENSORS_TPS40422
|
||||
tristate "TI TPS40422"
|
||||
help
|
||||
@ -257,10 +304,10 @@ config SENSORS_TPS40422
|
||||
be called tps40422.
|
||||
|
||||
config SENSORS_TPS53679
|
||||
tristate "TI TPS53647, TPS53667, TPS53679, TPS53681, TPS53688"
|
||||
tristate "TI TPS53647, TPS53667, TPS53676, TPS53679, TPS53681, TPS53688"
|
||||
help
|
||||
If you say yes here you get hardware monitoring support for TI
|
||||
TPS53647, TPS53667, TPS53679, TPS53681, and TPS53688.
|
||||
TPS53647, TPS53667, TPS53676, TPS53679, TPS53681, and TPS53688.
|
||||
|
||||
This driver can also be built as a module. If so, the module will
|
||||
be called tps53679.
|
||||
|
@ -8,15 +8,19 @@ obj-$(CONFIG_SENSORS_PMBUS) += pmbus.o
|
||||
obj-$(CONFIG_SENSORS_ADM1266) += adm1266.o
|
||||
obj-$(CONFIG_SENSORS_ADM1275) += adm1275.o
|
||||
obj-$(CONFIG_SENSORS_BEL_PFE) += bel-pfe.o
|
||||
obj-$(CONFIG_SENSORS_BPA_RS600) += bpa-rs600.o
|
||||
obj-$(CONFIG_SENSORS_FSP_3Y) += fsp-3y.o
|
||||
obj-$(CONFIG_SENSORS_IBM_CFFPS) += ibm-cffps.o
|
||||
obj-$(CONFIG_SENSORS_INSPUR_IPSPS) += inspur-ipsps.o
|
||||
obj-$(CONFIG_SENSORS_IR35221) += ir35221.o
|
||||
obj-$(CONFIG_SENSORS_IR36021) += ir36021.o
|
||||
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_LTC2978) += ltc2978.o
|
||||
obj-$(CONFIG_SENSORS_LTC3815) += ltc3815.o
|
||||
obj-$(CONFIG_SENSORS_MAX15301) += max15301.o
|
||||
obj-$(CONFIG_SENSORS_MAX16064) += max16064.o
|
||||
obj-$(CONFIG_SENSORS_MAX16601) += max16601.o
|
||||
obj-$(CONFIG_SENSORS_MAX20730) += max20730.o
|
||||
@ -28,6 +32,7 @@ obj-$(CONFIG_SENSORS_MP2975) += mp2975.o
|
||||
obj-$(CONFIG_SENSORS_PM6764TR) += pm6764tr.o
|
||||
obj-$(CONFIG_SENSORS_PXE1610) += pxe1610.o
|
||||
obj-$(CONFIG_SENSORS_Q54SJ108A2) += q54sj108a2.o
|
||||
obj-$(CONFIG_SENSORS_STPDDC60) += stpddc60.o
|
||||
obj-$(CONFIG_SENSORS_TPS40422) += tps40422.o
|
||||
obj-$(CONFIG_SENSORS_TPS53679) += tps53679.o
|
||||
obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o
|
||||
|
@ -510,3 +510,4 @@ module_i2c_driver(adm1266_driver);
|
||||
MODULE_AUTHOR("Alexandru Tachici <alexandru.tachici@analog.com>");
|
||||
MODULE_DESCRIPTION("PMBus driver for Analog Devices ADM1266");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_IMPORT_NS(PMBUS);
|
||||
|
@ -805,3 +805,4 @@ module_i2c_driver(adm1275_driver);
|
||||
MODULE_AUTHOR("Guenter Roeck");
|
||||
MODULE_DESCRIPTION("PMBus driver for Analog Devices ADM1275 and compatibles");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(PMBUS);
|
||||
|
@ -129,3 +129,4 @@ module_i2c_driver(pfe_pmbus_driver);
|
||||
MODULE_AUTHOR("Tao Ren <rentao.bupt@gmail.com>");
|
||||
MODULE_DESCRIPTION("PMBus driver for BEL PFE Family Power Supplies");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(PMBUS);
|
||||
|
173
drivers/hwmon/pmbus/bpa-rs600.c
Normal file
173
drivers/hwmon/pmbus/bpa-rs600.c
Normal file
@ -0,0 +1,173 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Hardware monitoring driver for BluTek BPA-RS600 Power Supplies
|
||||
*
|
||||
* Copyright 2021 Allied Telesis Labs
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pmbus.h>
|
||||
#include "pmbus.h"
|
||||
|
||||
#define BPARS600_MFR_VIN_MIN 0xa0
|
||||
#define BPARS600_MFR_VIN_MAX 0xa1
|
||||
#define BPARS600_MFR_IIN_MAX 0xa2
|
||||
#define BPARS600_MFR_PIN_MAX 0xa3
|
||||
#define BPARS600_MFR_VOUT_MIN 0xa4
|
||||
#define BPARS600_MFR_VOUT_MAX 0xa5
|
||||
#define BPARS600_MFR_IOUT_MAX 0xa6
|
||||
#define BPARS600_MFR_POUT_MAX 0xa7
|
||||
|
||||
static int bpa_rs600_read_byte_data(struct i2c_client *client, int page, int reg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (page > 0)
|
||||
return -ENXIO;
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_FAN_CONFIG_12:
|
||||
/*
|
||||
* Two fans are reported in PMBUS_FAN_CONFIG_12 but there is
|
||||
* only one fan in the module. Mask out the FAN2 bits.
|
||||
*/
|
||||
ret = pmbus_read_byte_data(client, 0, PMBUS_FAN_CONFIG_12);
|
||||
if (ret >= 0)
|
||||
ret &= ~(PB_FAN_2_INSTALLED | PB_FAN_2_PULSE_MASK);
|
||||
break;
|
||||
default:
|
||||
ret = -ENODATA;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bpa_rs600_read_word_data(struct i2c_client *client, int page, int phase, int reg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (page > 0)
|
||||
return -ENXIO;
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_VIN_UV_WARN_LIMIT:
|
||||
ret = pmbus_read_word_data(client, 0, 0xff, BPARS600_MFR_VIN_MIN);
|
||||
break;
|
||||
case PMBUS_VIN_OV_WARN_LIMIT:
|
||||
ret = pmbus_read_word_data(client, 0, 0xff, BPARS600_MFR_VIN_MAX);
|
||||
break;
|
||||
case PMBUS_VOUT_UV_WARN_LIMIT:
|
||||
ret = pmbus_read_word_data(client, 0, 0xff, BPARS600_MFR_VOUT_MIN);
|
||||
break;
|
||||
case PMBUS_VOUT_OV_WARN_LIMIT:
|
||||
ret = pmbus_read_word_data(client, 0, 0xff, BPARS600_MFR_VOUT_MAX);
|
||||
break;
|
||||
case PMBUS_IIN_OC_WARN_LIMIT:
|
||||
ret = pmbus_read_word_data(client, 0, 0xff, BPARS600_MFR_IIN_MAX);
|
||||
break;
|
||||
case PMBUS_IOUT_OC_WARN_LIMIT:
|
||||
ret = pmbus_read_word_data(client, 0, 0xff, BPARS600_MFR_IOUT_MAX);
|
||||
break;
|
||||
case PMBUS_PIN_OP_WARN_LIMIT:
|
||||
ret = pmbus_read_word_data(client, 0, 0xff, BPARS600_MFR_PIN_MAX);
|
||||
break;
|
||||
case PMBUS_POUT_OP_WARN_LIMIT:
|
||||
ret = pmbus_read_word_data(client, 0, 0xff, BPARS600_MFR_POUT_MAX);
|
||||
break;
|
||||
case PMBUS_VIN_UV_FAULT_LIMIT:
|
||||
case PMBUS_VIN_OV_FAULT_LIMIT:
|
||||
case PMBUS_VOUT_UV_FAULT_LIMIT:
|
||||
case PMBUS_VOUT_OV_FAULT_LIMIT:
|
||||
/* These commands return data but it is invalid/un-documented */
|
||||
ret = -ENXIO;
|
||||
break;
|
||||
default:
|
||||
if (reg >= PMBUS_VIRT_BASE)
|
||||
ret = -ENXIO;
|
||||
else
|
||||
ret = -ENODATA;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct pmbus_driver_info bpa_rs600_info = {
|
||||
.pages = 1,
|
||||
.format[PSC_VOLTAGE_IN] = linear,
|
||||
.format[PSC_VOLTAGE_OUT] = linear,
|
||||
.format[PSC_CURRENT_IN] = linear,
|
||||
.format[PSC_CURRENT_OUT] = linear,
|
||||
.format[PSC_POWER] = linear,
|
||||
.format[PSC_TEMPERATURE] = linear,
|
||||
.format[PSC_FAN] = linear,
|
||||
.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT |
|
||||
PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT |
|
||||
PMBUS_HAVE_PIN | PMBUS_HAVE_POUT |
|
||||
PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 |
|
||||
PMBUS_HAVE_FAN12 |
|
||||
PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT |
|
||||
PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP |
|
||||
PMBUS_HAVE_STATUS_FAN12,
|
||||
.read_byte_data = bpa_rs600_read_byte_data,
|
||||
.read_word_data = bpa_rs600_read_word_data,
|
||||
};
|
||||
|
||||
static int bpa_rs600_probe(struct i2c_client *client)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
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_MODEL, buf);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to read Manufacturer Model\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (strncmp(buf, "BPA-RS600", 8)) {
|
||||
buf[ret] = '\0';
|
||||
dev_err(dev, "Unsupported Manufacturer Model '%s'\n", buf);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return pmbus_do_probe(client, &bpa_rs600_info);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id bpa_rs600_id[] = {
|
||||
{ "bpars600", 0 },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, bpa_rs600_id);
|
||||
|
||||
static const struct of_device_id __maybe_unused bpa_rs600_of_match[] = {
|
||||
{ .compatible = "blutek,bpa-rs600" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bpa_rs600_of_match);
|
||||
|
||||
static struct i2c_driver bpa_rs600_driver = {
|
||||
.driver = {
|
||||
.name = "bpa-rs600",
|
||||
.of_match_table = of_match_ptr(bpa_rs600_of_match),
|
||||
},
|
||||
.probe_new = bpa_rs600_probe,
|
||||
.id_table = bpa_rs600_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(bpa_rs600_driver);
|
||||
|
||||
MODULE_AUTHOR("Chris Packham");
|
||||
MODULE_DESCRIPTION("PMBus driver for BluTek BPA-RS600");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(PMBUS);
|
254
drivers/hwmon/pmbus/fsp-3y.c
Normal file
254
drivers/hwmon/pmbus/fsp-3y.c
Normal file
@ -0,0 +1,254 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Hardware monitoring driver for FSP 3Y-Power PSUs
|
||||
*
|
||||
* Copyright (c) 2021 Václav Kubernát, CESNET
|
||||
*
|
||||
* This driver is mostly reverse engineered with the help of a tool called pmbus_peek written by
|
||||
* David Brownell (and later adopted by Jan Kundrát). The device has some sort of a timing issue
|
||||
* when switching pages, details are explained in the code. The driver support is limited. It
|
||||
* exposes only the values, that have been tested to work correctly. Unsupported values either
|
||||
* aren't supported by the devices or their encondings are unknown.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include "pmbus.h"
|
||||
|
||||
#define YM2151_PAGE_12V_LOG 0x00
|
||||
#define YM2151_PAGE_12V_REAL 0x00
|
||||
#define YM2151_PAGE_5VSB_LOG 0x01
|
||||
#define YM2151_PAGE_5VSB_REAL 0x20
|
||||
#define YH5151E_PAGE_12V_LOG 0x00
|
||||
#define YH5151E_PAGE_12V_REAL 0x00
|
||||
#define YH5151E_PAGE_5V_LOG 0x01
|
||||
#define YH5151E_PAGE_5V_REAL 0x10
|
||||
#define YH5151E_PAGE_3V3_LOG 0x02
|
||||
#define YH5151E_PAGE_3V3_REAL 0x11
|
||||
|
||||
enum chips {
|
||||
ym2151e,
|
||||
yh5151e
|
||||
};
|
||||
|
||||
struct fsp3y_data {
|
||||
struct pmbus_driver_info info;
|
||||
int chip;
|
||||
int page;
|
||||
};
|
||||
|
||||
#define to_fsp3y_data(x) container_of(x, struct fsp3y_data, info)
|
||||
|
||||
static int page_log_to_page_real(int page_log, enum chips chip)
|
||||
{
|
||||
switch (chip) {
|
||||
case ym2151e:
|
||||
switch (page_log) {
|
||||
case YM2151_PAGE_12V_LOG:
|
||||
return YM2151_PAGE_12V_REAL;
|
||||
case YM2151_PAGE_5VSB_LOG:
|
||||
return YM2151_PAGE_5VSB_REAL;
|
||||
}
|
||||
return -EINVAL;
|
||||
case yh5151e:
|
||||
switch (page_log) {
|
||||
case YH5151E_PAGE_12V_LOG:
|
||||
return YH5151E_PAGE_12V_REAL;
|
||||
case YH5151E_PAGE_5V_LOG:
|
||||
return YH5151E_PAGE_5V_LOG;
|
||||
case YH5151E_PAGE_3V3_LOG:
|
||||
return YH5151E_PAGE_3V3_REAL;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int set_page(struct i2c_client *client, int page_log)
|
||||
{
|
||||
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
||||
struct fsp3y_data *data = to_fsp3y_data(info);
|
||||
int rv;
|
||||
int page_real;
|
||||
|
||||
if (page_log < 0)
|
||||
return 0;
|
||||
|
||||
page_real = page_log_to_page_real(page_log, data->chip);
|
||||
if (page_real < 0)
|
||||
return page_real;
|
||||
|
||||
if (data->page != page_real) {
|
||||
rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page_real);
|
||||
if (rv < 0)
|
||||
return rv;
|
||||
|
||||
data->page = page_real;
|
||||
|
||||
/*
|
||||
* Testing showed that the device has a timing issue. After
|
||||
* setting a page, it takes a while, before the device actually
|
||||
* gives the correct values from the correct page. 20 ms was
|
||||
* tested to be enough to not give wrong values (15 ms wasn't
|
||||
* enough).
|
||||
*/
|
||||
usleep_range(20000, 30000);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsp3y_read_byte_data(struct i2c_client *client, int page, int reg)
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv = set_page(client, page);
|
||||
if (rv < 0)
|
||||
return rv;
|
||||
|
||||
return i2c_smbus_read_byte_data(client, reg);
|
||||
}
|
||||
|
||||
static int fsp3y_read_word_data(struct i2c_client *client, int page, int phase, int reg)
|
||||
{
|
||||
int rv;
|
||||
|
||||
/*
|
||||
* This masks commands which weren't tested to work correctly. Some of
|
||||
* the masked commands return 0xFFFF. These would probably get tagged as
|
||||
* invalid by pmbus_core. Other ones do return values which might be
|
||||
* useful (that is, they are not 0xFFFF), but their encoding is unknown,
|
||||
* and so they are unsupported.
|
||||
*/
|
||||
switch (reg) {
|
||||
case PMBUS_READ_FAN_SPEED_1:
|
||||
case PMBUS_READ_IIN:
|
||||
case PMBUS_READ_IOUT:
|
||||
case PMBUS_READ_PIN:
|
||||
case PMBUS_READ_POUT:
|
||||
case PMBUS_READ_TEMPERATURE_1:
|
||||
case PMBUS_READ_TEMPERATURE_2:
|
||||
case PMBUS_READ_TEMPERATURE_3:
|
||||
case PMBUS_READ_VIN:
|
||||
case PMBUS_READ_VOUT:
|
||||
case PMBUS_STATUS_WORD:
|
||||
break;
|
||||
default:
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
rv = set_page(client, page);
|
||||
if (rv < 0)
|
||||
return rv;
|
||||
|
||||
return i2c_smbus_read_word_data(client, reg);
|
||||
}
|
||||
|
||||
static struct pmbus_driver_info fsp3y_info[] = {
|
||||
[ym2151e] = {
|
||||
.pages = 2,
|
||||
.func[YM2151_PAGE_12V_LOG] =
|
||||
PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
|
||||
PMBUS_HAVE_PIN | PMBUS_HAVE_POUT |
|
||||
PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 |
|
||||
PMBUS_HAVE_VIN | PMBUS_HAVE_IIN |
|
||||
PMBUS_HAVE_FAN12,
|
||||
.func[YM2151_PAGE_5VSB_LOG] =
|
||||
PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT,
|
||||
PMBUS_HAVE_IIN,
|
||||
.read_word_data = fsp3y_read_word_data,
|
||||
.read_byte_data = fsp3y_read_byte_data,
|
||||
},
|
||||
[yh5151e] = {
|
||||
.pages = 3,
|
||||
.func[YH5151E_PAGE_12V_LOG] =
|
||||
PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
|
||||
PMBUS_HAVE_POUT |
|
||||
PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3,
|
||||
.func[YH5151E_PAGE_5V_LOG] =
|
||||
PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
|
||||
PMBUS_HAVE_POUT,
|
||||
.func[YH5151E_PAGE_3V3_LOG] =
|
||||
PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
|
||||
PMBUS_HAVE_POUT,
|
||||
.read_word_data = fsp3y_read_word_data,
|
||||
.read_byte_data = fsp3y_read_byte_data,
|
||||
}
|
||||
};
|
||||
|
||||
static int fsp3y_detect(struct i2c_client *client)
|
||||
{
|
||||
int rv;
|
||||
u8 buf[I2C_SMBUS_BLOCK_MAX + 1];
|
||||
|
||||
rv = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf);
|
||||
if (rv < 0)
|
||||
return rv;
|
||||
|
||||
buf[rv] = '\0';
|
||||
|
||||
if (rv == 8) {
|
||||
if (!strcmp(buf, "YM-2151E"))
|
||||
return ym2151e;
|
||||
else if (!strcmp(buf, "YH-5151E"))
|
||||
return yh5151e;
|
||||
}
|
||||
|
||||
dev_err(&client->dev, "Unsupported model %.*s\n", rv, buf);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id fsp3y_id[] = {
|
||||
{"ym2151e", ym2151e},
|
||||
{"yh5151e", yh5151e},
|
||||
{ }
|
||||
};
|
||||
|
||||
static int fsp3y_probe(struct i2c_client *client)
|
||||
{
|
||||
struct fsp3y_data *data;
|
||||
const struct i2c_device_id *id;
|
||||
int rv;
|
||||
|
||||
data = devm_kzalloc(&client->dev, sizeof(struct fsp3y_data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->chip = fsp3y_detect(client);
|
||||
if (data->chip < 0)
|
||||
return data->chip;
|
||||
|
||||
id = i2c_match_id(fsp3y_id, client);
|
||||
if (data->chip != id->driver_data)
|
||||
dev_warn(&client->dev, "Device mismatch: Configured %s (%d), detected %d\n",
|
||||
id->name, (int)id->driver_data, data->chip);
|
||||
|
||||
rv = i2c_smbus_read_byte_data(client, PMBUS_PAGE);
|
||||
if (rv < 0)
|
||||
return rv;
|
||||
data->page = rv;
|
||||
|
||||
data->info = fsp3y_info[data->chip];
|
||||
|
||||
return pmbus_do_probe(client, &data->info);
|
||||
}
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, fsp3y_id);
|
||||
|
||||
static struct i2c_driver fsp3y_driver = {
|
||||
.driver = {
|
||||
.name = "fsp3y",
|
||||
},
|
||||
.probe_new = fsp3y_probe,
|
||||
.id_table = fsp3y_id
|
||||
};
|
||||
|
||||
module_i2c_driver(fsp3y_driver);
|
||||
|
||||
MODULE_AUTHOR("Václav Kubernát");
|
||||
MODULE_DESCRIPTION("PMBus driver for FSP/3Y-Power power supplies");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(PMBUS);
|
@ -625,3 +625,4 @@ module_i2c_driver(ibm_cffps_driver);
|
||||
MODULE_AUTHOR("Eddie James");
|
||||
MODULE_DESCRIPTION("PMBus driver for IBM Common Form Factor power supplies");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(PMBUS);
|
||||
|
@ -70,7 +70,7 @@ static ssize_t ipsps_string_show(struct device *dev,
|
||||
p = memscan(data, '#', rc);
|
||||
*p = '\0';
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", data);
|
||||
return sysfs_emit(buf, "%s\n", data);
|
||||
}
|
||||
|
||||
static ssize_t ipsps_fw_version_show(struct device *dev,
|
||||
@ -91,9 +91,9 @@ static ssize_t ipsps_fw_version_show(struct device *dev,
|
||||
if (rc != 6)
|
||||
return -EPROTO;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u.%02u%u-%u.%02u\n",
|
||||
data[1], data[2]/* < 100 */, data[3]/*< 10*/,
|
||||
data[4], data[5]/* < 100 */);
|
||||
return sysfs_emit(buf, "%u.%02u%u-%u.%02u\n",
|
||||
data[1], data[2]/* < 100 */, data[3]/*< 10*/,
|
||||
data[4], data[5]/* < 100 */);
|
||||
}
|
||||
|
||||
static ssize_t ipsps_mode_show(struct device *dev,
|
||||
@ -111,19 +111,19 @@ static ssize_t ipsps_mode_show(struct device *dev,
|
||||
|
||||
switch (rc) {
|
||||
case MODE_ACTIVE:
|
||||
return snprintf(buf, PAGE_SIZE, "[%s] %s %s\n",
|
||||
MODE_ACTIVE_STRING,
|
||||
MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING);
|
||||
return sysfs_emit(buf, "[%s] %s %s\n",
|
||||
MODE_ACTIVE_STRING,
|
||||
MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING);
|
||||
case MODE_STANDBY:
|
||||
return snprintf(buf, PAGE_SIZE, "%s [%s] %s\n",
|
||||
MODE_ACTIVE_STRING,
|
||||
MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING);
|
||||
return sysfs_emit(buf, "%s [%s] %s\n",
|
||||
MODE_ACTIVE_STRING,
|
||||
MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING);
|
||||
case MODE_REDUNDANCY:
|
||||
return snprintf(buf, PAGE_SIZE, "%s %s [%s]\n",
|
||||
MODE_ACTIVE_STRING,
|
||||
MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING);
|
||||
return sysfs_emit(buf, "%s %s [%s]\n",
|
||||
MODE_ACTIVE_STRING,
|
||||
MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING);
|
||||
default:
|
||||
return snprintf(buf, PAGE_SIZE, "unspecified\n");
|
||||
return sysfs_emit(buf, "unspecified\n");
|
||||
}
|
||||
}
|
||||
|
||||
@ -224,3 +224,4 @@ module_i2c_driver(ipsps_driver);
|
||||
MODULE_AUTHOR("John Wang");
|
||||
MODULE_DESCRIPTION("PMBus driver for Inspur Power System power supplies");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(PMBUS);
|
||||
|
@ -145,3 +145,4 @@ module_i2c_driver(ir35221_driver);
|
||||
MODULE_AUTHOR("Samuel Mendoza-Jonas <sam@mendozajonas.com");
|
||||
MODULE_DESCRIPTION("PMBus driver for IR35221");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(PMBUS);
|
||||
|
80
drivers/hwmon/pmbus/ir36021.c
Normal file
80
drivers/hwmon/pmbus/ir36021.c
Normal file
@ -0,0 +1,80 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Hardware monitoring driver for Infineon IR36021
|
||||
*
|
||||
* Copyright (c) 2021 Allied Telesis
|
||||
*/
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include "pmbus.h"
|
||||
|
||||
static struct pmbus_driver_info ir36021_info = {
|
||||
.pages = 1,
|
||||
.format[PSC_VOLTAGE_IN] = linear,
|
||||
.format[PSC_VOLTAGE_OUT] = linear,
|
||||
.format[PSC_CURRENT_IN] = linear,
|
||||
.format[PSC_CURRENT_OUT] = linear,
|
||||
.format[PSC_POWER] = linear,
|
||||
.format[PSC_TEMPERATURE] = linear,
|
||||
.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT
|
||||
| PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT
|
||||
| PMBUS_HAVE_PIN | PMBUS_HAVE_POUT
|
||||
| PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2
|
||||
| PMBUS_HAVE_STATUS_TEMP,
|
||||
};
|
||||
|
||||
static int ir36021_probe(struct i2c_client *client)
|
||||
{
|
||||
u8 buf[I2C_SMBUS_BLOCK_MAX];
|
||||
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_i2c_block_data(client, PMBUS_MFR_MODEL, 2, buf);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Failed to read PMBUS_MFR_MODEL\n");
|
||||
return ret;
|
||||
}
|
||||
if (ret != 2 || buf[0] != 0x01 || buf[1] != 0x2d) {
|
||||
dev_err(&client->dev, "MFR_MODEL unrecognised\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return pmbus_do_probe(client, &ir36021_info);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ir36021_id[] = {
|
||||
{ "ir36021", 0 },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ir36021_id);
|
||||
|
||||
static const struct of_device_id __maybe_unused ir36021_of_id[] = {
|
||||
{ .compatible = "infineon,ir36021" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ir36021_of_id);
|
||||
|
||||
static struct i2c_driver ir36021_driver = {
|
||||
.class = I2C_CLASS_HWMON,
|
||||
.driver = {
|
||||
.name = "ir36021",
|
||||
.of_match_table = of_match_ptr(ir36021_of_id),
|
||||
},
|
||||
.probe_new = ir36021_probe,
|
||||
.id_table = ir36021_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(ir36021_driver);
|
||||
|
||||
MODULE_AUTHOR("Chris Packham <chris.packham@alliedtelesis.co.nz>");
|
||||
MODULE_DESCRIPTION("PMBus driver for Infineon IR36021");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(PMBUS);
|
@ -61,3 +61,4 @@ module_i2c_driver(ir38064_driver);
|
||||
MODULE_AUTHOR("Maxim Sloyko <maxims@google.com>");
|
||||
MODULE_DESCRIPTION("PMBus driver for Infineon IR38064");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(PMBUS);
|
||||
|
@ -63,3 +63,4 @@ module_i2c_driver(irps5401_driver);
|
||||
MODULE_AUTHOR("Robert Hancock");
|
||||
MODULE_DESCRIPTION("PMBus driver for Infineon IRPS5401");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(PMBUS);
|
||||
|
@ -332,3 +332,4 @@ module_i2c_driver(isl68137_driver);
|
||||
MODULE_AUTHOR("Maxim Sloyko <maxims@google.com>");
|
||||
MODULE_DESCRIPTION("PMBus driver for Renesas digital multiphase voltage regulators");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(PMBUS);
|
||||
|
@ -511,3 +511,4 @@ module_i2c_driver(lm25066_driver);
|
||||
MODULE_AUTHOR("Guenter Roeck");
|
||||
MODULE_DESCRIPTION("PMBus driver for LM25066 and compatible chips");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(PMBUS);
|
||||
|
@ -883,3 +883,4 @@ module_i2c_driver(ltc2978_driver);
|
||||
MODULE_AUTHOR("Guenter Roeck");
|
||||
MODULE_DESCRIPTION("PMBus driver for LTC2978 and compatible chips");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(PMBUS);
|
||||
|
@ -208,3 +208,4 @@ module_i2c_driver(ltc3815_driver);
|
||||
MODULE_AUTHOR("Guenter Roeck");
|
||||
MODULE_DESCRIPTION("PMBus driver for LTC3815");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(PMBUS);
|
||||
|
190
drivers/hwmon/pmbus/max15301.c
Normal file
190
drivers/hwmon/pmbus/max15301.c
Normal file
@ -0,0 +1,190 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Hardware monitoring driver for Maxim MAX15301
|
||||
*
|
||||
* Copyright (c) 2021 Flextronics International Sweden AB
|
||||
*
|
||||
* Even though the specification does not specifically mention it,
|
||||
* extensive empirical testing has revealed that auto-detection of
|
||||
* limit-registers will fail in a random fashion unless the delay
|
||||
* parameter is set to above about 80us. The default delay is set
|
||||
* to 100us to include some safety margin.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pmbus.h>
|
||||
#include "pmbus.h"
|
||||
|
||||
static const struct i2c_device_id max15301_id[] = {
|
||||
{"bmr461", 0},
|
||||
{"max15301", 0},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, max15301_id);
|
||||
|
||||
struct max15301_data {
|
||||
int id;
|
||||
ktime_t access; /* Chip access time */
|
||||
int delay; /* Delay between chip accesses in us */
|
||||
struct pmbus_driver_info info;
|
||||
};
|
||||
|
||||
#define to_max15301_data(x) container_of(x, struct max15301_data, info)
|
||||
|
||||
#define MAX15301_WAIT_TIME 100 /* us */
|
||||
|
||||
static ushort delay = MAX15301_WAIT_TIME;
|
||||
module_param(delay, ushort, 0644);
|
||||
MODULE_PARM_DESC(delay, "Delay between chip accesses in us");
|
||||
|
||||
static struct max15301_data max15301_data = {
|
||||
.info = {
|
||||
.pages = 1,
|
||||
.func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
|
||||
| PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT
|
||||
| PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2
|
||||
| PMBUS_HAVE_STATUS_TEMP
|
||||
| PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
|
||||
}
|
||||
};
|
||||
|
||||
/* This chip needs a delay between accesses */
|
||||
static inline void max15301_wait(const struct max15301_data *data)
|
||||
{
|
||||
if (data->delay) {
|
||||
s64 delta = ktime_us_delta(ktime_get(), data->access);
|
||||
|
||||
if (delta < data->delay)
|
||||
udelay(data->delay - delta);
|
||||
}
|
||||
}
|
||||
|
||||
static int max15301_read_word_data(struct i2c_client *client, int page,
|
||||
int phase, int reg)
|
||||
{
|
||||
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
||||
struct max15301_data *data = to_max15301_data(info);
|
||||
int ret;
|
||||
|
||||
if (page > 0)
|
||||
return -ENXIO;
|
||||
|
||||
if (reg >= PMBUS_VIRT_BASE)
|
||||
return -ENXIO;
|
||||
|
||||
max15301_wait(data);
|
||||
ret = pmbus_read_word_data(client, page, phase, reg);
|
||||
data->access = ktime_get();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max15301_read_byte_data(struct i2c_client *client, int page, int reg)
|
||||
{
|
||||
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
||||
struct max15301_data *data = to_max15301_data(info);
|
||||
int ret;
|
||||
|
||||
if (page > 0)
|
||||
return -ENXIO;
|
||||
|
||||
max15301_wait(data);
|
||||
ret = pmbus_read_byte_data(client, page, reg);
|
||||
data->access = ktime_get();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max15301_write_word_data(struct i2c_client *client, int page, int reg,
|
||||
u16 word)
|
||||
{
|
||||
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
||||
struct max15301_data *data = to_max15301_data(info);
|
||||
int ret;
|
||||
|
||||
if (page > 0)
|
||||
return -ENXIO;
|
||||
|
||||
if (reg >= PMBUS_VIRT_BASE)
|
||||
return -ENXIO;
|
||||
|
||||
max15301_wait(data);
|
||||
ret = pmbus_write_word_data(client, page, reg, word);
|
||||
data->access = ktime_get();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max15301_write_byte(struct i2c_client *client, int page, u8 value)
|
||||
{
|
||||
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
||||
struct max15301_data *data = to_max15301_data(info);
|
||||
int ret;
|
||||
|
||||
if (page > 0)
|
||||
return -ENXIO;
|
||||
|
||||
max15301_wait(data);
|
||||
ret = pmbus_write_byte(client, page, value);
|
||||
data->access = ktime_get();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max15301_probe(struct i2c_client *client)
|
||||
{
|
||||
int status;
|
||||
u8 device_id[I2C_SMBUS_BLOCK_MAX + 1];
|
||||
const struct i2c_device_id *mid;
|
||||
struct pmbus_driver_info *info = &max15301_data.info;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_READ_BYTE_DATA
|
||||
| I2C_FUNC_SMBUS_BLOCK_DATA))
|
||||
return -ENODEV;
|
||||
|
||||
status = i2c_smbus_read_block_data(client, PMBUS_IC_DEVICE_ID, device_id);
|
||||
if (status < 0) {
|
||||
dev_err(&client->dev, "Failed to read Device Id\n");
|
||||
return status;
|
||||
}
|
||||
for (mid = max15301_id; mid->name[0]; mid++) {
|
||||
if (!strncasecmp(mid->name, device_id, strlen(mid->name)))
|
||||
break;
|
||||
}
|
||||
if (!mid->name[0]) {
|
||||
dev_err(&client->dev, "Unsupported device\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
max15301_data.delay = delay;
|
||||
|
||||
info->read_byte_data = max15301_read_byte_data;
|
||||
info->read_word_data = max15301_read_word_data;
|
||||
info->write_byte = max15301_write_byte;
|
||||
info->write_word_data = max15301_write_word_data;
|
||||
|
||||
return pmbus_do_probe(client, info);
|
||||
}
|
||||
|
||||
static struct i2c_driver max15301_driver = {
|
||||
.driver = {
|
||||
.name = "max15301",
|
||||
},
|
||||
.probe_new = max15301_probe,
|
||||
.id_table = max15301_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(max15301_driver);
|
||||
|
||||
MODULE_AUTHOR("Erik Rosen <erik.rosen@metormote.com>");
|
||||
MODULE_DESCRIPTION("PMBus driver for Maxim MAX15301");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(PMBUS);
|
@ -111,3 +111,4 @@ module_i2c_driver(max16064_driver);
|
||||
MODULE_AUTHOR("Guenter Roeck");
|
||||
MODULE_DESCRIPTION("PMBus driver for Maxim MAX16064");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(PMBUS);
|
||||
|
@ -359,3 +359,4 @@ module_i2c_driver(max16601_driver);
|
||||
MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
|
||||
MODULE_DESCRIPTION("PMBus driver for Maxim MAX16601");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_IMPORT_NS(PMBUS);
|
||||
|
@ -785,3 +785,4 @@ module_i2c_driver(max20730_driver);
|
||||
MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
|
||||
MODULE_DESCRIPTION("PMBus driver for Maxim MAX20710 / MAX20730 / MAX20734 / MAX20743");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(PMBUS);
|
||||
|
@ -51,3 +51,4 @@ module_i2c_driver(max20751_driver);
|
||||
MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
|
||||
MODULE_DESCRIPTION("PMBus driver for Maxim MAX20751");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(PMBUS);
|
||||
|
@ -403,3 +403,4 @@ module_i2c_driver(max31785_driver);
|
||||
MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>");
|
||||
MODULE_DESCRIPTION("PMBus driver for the Maxim MAX31785");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(PMBUS);
|
||||
|
@ -529,3 +529,4 @@ module_i2c_driver(max34440_driver);
|
||||
MODULE_AUTHOR("Guenter Roeck");
|
||||
MODULE_DESCRIPTION("PMBus driver for Maxim MAX34440/MAX34441");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(PMBUS);
|
||||
|
@ -191,3 +191,4 @@ module_i2c_driver(max8688_driver);
|
||||
MODULE_AUTHOR("Guenter Roeck");
|
||||
MODULE_DESCRIPTION("PMBus driver for Maxim MAX8688");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(PMBUS);
|
||||
|
@ -766,3 +766,4 @@ module_i2c_driver(mp2975_driver);
|
||||
MODULE_AUTHOR("Vadim Pasternak <vadimp@nvidia.com>");
|
||||
MODULE_DESCRIPTION("PMBus driver for MPS MP2975 device");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(PMBUS);
|
||||
|
@ -73,3 +73,4 @@ module_i2c_driver(pm6764tr_driver);
|
||||
MODULE_AUTHOR("Charles Hsu");
|
||||
MODULE_DESCRIPTION("PMBus driver for ST PM6764TR");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(PMBUS);
|
||||
|
@ -246,3 +246,4 @@ module_i2c_driver(pmbus_driver);
|
||||
MODULE_AUTHOR("Guenter Roeck");
|
||||
MODULE_DESCRIPTION("Generic PMBus driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(PMBUS);
|
||||
|
@ -475,6 +475,7 @@ extern const struct regulator_ops pmbus_regulator_ops;
|
||||
/* Function declarations */
|
||||
|
||||
void pmbus_clear_cache(struct i2c_client *client);
|
||||
void pmbus_set_update(struct i2c_client *client, u8 reg, bool update);
|
||||
int pmbus_set_page(struct i2c_client *client, int page, int phase);
|
||||
int pmbus_read_word_data(struct i2c_client *client, int page, int phase,
|
||||
u8 reg);
|
||||
|
@ -139,7 +139,18 @@ void pmbus_clear_cache(struct i2c_client *client)
|
||||
for (sensor = data->sensors; sensor; sensor = sensor->next)
|
||||
sensor->data = -ENODATA;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pmbus_clear_cache);
|
||||
EXPORT_SYMBOL_NS_GPL(pmbus_clear_cache, PMBUS);
|
||||
|
||||
void pmbus_set_update(struct i2c_client *client, u8 reg, bool update)
|
||||
{
|
||||
struct pmbus_data *data = i2c_get_clientdata(client);
|
||||
struct pmbus_sensor *sensor;
|
||||
|
||||
for (sensor = data->sensors; sensor; sensor = sensor->next)
|
||||
if (sensor->reg == reg)
|
||||
sensor->update = update;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(pmbus_set_update, PMBUS);
|
||||
|
||||
int pmbus_set_page(struct i2c_client *client, int page, int phase)
|
||||
{
|
||||
@ -175,7 +186,7 @@ int pmbus_set_page(struct i2c_client *client, int page, int phase)
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pmbus_set_page);
|
||||
EXPORT_SYMBOL_NS_GPL(pmbus_set_page, PMBUS);
|
||||
|
||||
int pmbus_write_byte(struct i2c_client *client, int page, u8 value)
|
||||
{
|
||||
@ -187,7 +198,7 @@ int pmbus_write_byte(struct i2c_client *client, int page, u8 value)
|
||||
|
||||
return i2c_smbus_write_byte(client, value);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pmbus_write_byte);
|
||||
EXPORT_SYMBOL_NS_GPL(pmbus_write_byte, PMBUS);
|
||||
|
||||
/*
|
||||
* _pmbus_write_byte() is similar to pmbus_write_byte(), but checks if
|
||||
@ -218,7 +229,7 @@ int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg,
|
||||
|
||||
return i2c_smbus_write_word_data(client, reg, word);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pmbus_write_word_data);
|
||||
EXPORT_SYMBOL_NS_GPL(pmbus_write_word_data, PMBUS);
|
||||
|
||||
|
||||
static int pmbus_write_virt_reg(struct i2c_client *client, int page, int reg,
|
||||
@ -288,7 +299,7 @@ int pmbus_update_fan(struct i2c_client *client, int page, int id,
|
||||
return _pmbus_write_word_data(client, page,
|
||||
pmbus_fan_command_registers[id], command);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pmbus_update_fan);
|
||||
EXPORT_SYMBOL_NS_GPL(pmbus_update_fan, PMBUS);
|
||||
|
||||
int pmbus_read_word_data(struct i2c_client *client, int page, int phase, u8 reg)
|
||||
{
|
||||
@ -300,7 +311,7 @@ int pmbus_read_word_data(struct i2c_client *client, int page, int phase, u8 reg)
|
||||
|
||||
return i2c_smbus_read_word_data(client, reg);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pmbus_read_word_data);
|
||||
EXPORT_SYMBOL_NS_GPL(pmbus_read_word_data, PMBUS);
|
||||
|
||||
static int pmbus_read_virt_reg(struct i2c_client *client, int page, int reg)
|
||||
{
|
||||
@ -359,7 +370,7 @@ int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg)
|
||||
|
||||
return i2c_smbus_read_byte_data(client, reg);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pmbus_read_byte_data);
|
||||
EXPORT_SYMBOL_NS_GPL(pmbus_read_byte_data, PMBUS);
|
||||
|
||||
int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, u8 value)
|
||||
{
|
||||
@ -371,7 +382,7 @@ int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, u8 value)
|
||||
|
||||
return i2c_smbus_write_byte_data(client, reg, value);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pmbus_write_byte_data);
|
||||
EXPORT_SYMBOL_NS_GPL(pmbus_write_byte_data, PMBUS);
|
||||
|
||||
int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg,
|
||||
u8 mask, u8 value)
|
||||
@ -390,7 +401,7 @@ int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg,
|
||||
|
||||
return rv;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pmbus_update_byte_data);
|
||||
EXPORT_SYMBOL_NS_GPL(pmbus_update_byte_data, PMBUS);
|
||||
|
||||
/*
|
||||
* _pmbus_read_byte_data() is similar to pmbus_read_byte_data(), but checks if
|
||||
@ -463,14 +474,14 @@ int pmbus_get_fan_rate_device(struct i2c_client *client, int page, int id,
|
||||
{
|
||||
return pmbus_get_fan_rate(client, page, id, mode, false);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pmbus_get_fan_rate_device);
|
||||
EXPORT_SYMBOL_NS_GPL(pmbus_get_fan_rate_device, PMBUS);
|
||||
|
||||
int pmbus_get_fan_rate_cached(struct i2c_client *client, int page, int id,
|
||||
enum pmbus_fan_mode mode)
|
||||
{
|
||||
return pmbus_get_fan_rate(client, page, id, mode, true);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pmbus_get_fan_rate_cached);
|
||||
EXPORT_SYMBOL_NS_GPL(pmbus_get_fan_rate_cached, PMBUS);
|
||||
|
||||
static void pmbus_clear_fault_page(struct i2c_client *client, int page)
|
||||
{
|
||||
@ -485,7 +496,7 @@ void pmbus_clear_faults(struct i2c_client *client)
|
||||
for (i = 0; i < data->info->pages; i++)
|
||||
pmbus_clear_fault_page(client, i);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pmbus_clear_faults);
|
||||
EXPORT_SYMBOL_NS_GPL(pmbus_clear_faults, PMBUS);
|
||||
|
||||
static int pmbus_check_status_cml(struct i2c_client *client)
|
||||
{
|
||||
@ -537,13 +548,13 @@ bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg)
|
||||
{
|
||||
return pmbus_check_register(client, _pmbus_read_byte_data, page, reg);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pmbus_check_byte_register);
|
||||
EXPORT_SYMBOL_NS_GPL(pmbus_check_byte_register, PMBUS);
|
||||
|
||||
bool pmbus_check_word_register(struct i2c_client *client, int page, int reg)
|
||||
{
|
||||
return pmbus_check_register(client, __pmbus_read_word_data, page, reg);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pmbus_check_word_register);
|
||||
EXPORT_SYMBOL_NS_GPL(pmbus_check_word_register, PMBUS);
|
||||
|
||||
const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client *client)
|
||||
{
|
||||
@ -551,7 +562,7 @@ const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client *client)
|
||||
|
||||
return data->info;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pmbus_get_driver_info);
|
||||
EXPORT_SYMBOL_NS_GPL(pmbus_get_driver_info, PMBUS);
|
||||
|
||||
static int pmbus_get_status(struct i2c_client *client, int page, int reg)
|
||||
{
|
||||
@ -932,7 +943,7 @@ static ssize_t pmbus_show_boolean(struct device *dev,
|
||||
val = pmbus_get_boolean(client, boolean, attr->index);
|
||||
if (val < 0)
|
||||
return val;
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
return sysfs_emit(buf, "%d\n", val);
|
||||
}
|
||||
|
||||
static ssize_t pmbus_show_sensor(struct device *dev,
|
||||
@ -948,7 +959,7 @@ static ssize_t pmbus_show_sensor(struct device *dev,
|
||||
if (sensor->data < 0)
|
||||
ret = sensor->data;
|
||||
else
|
||||
ret = snprintf(buf, PAGE_SIZE, "%lld\n", pmbus_reg2data(data, sensor));
|
||||
ret = sysfs_emit(buf, "%lld\n", pmbus_reg2data(data, sensor));
|
||||
mutex_unlock(&data->update_lock);
|
||||
return ret;
|
||||
}
|
||||
@ -984,7 +995,7 @@ static ssize_t pmbus_show_label(struct device *dev,
|
||||
{
|
||||
struct pmbus_label *label = to_pmbus_label(da);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", label->label);
|
||||
return sysfs_emit(buf, "%s\n", label->label);
|
||||
}
|
||||
|
||||
static int pmbus_add_attribute(struct pmbus_data *data, struct attribute *attr)
|
||||
@ -2024,7 +2035,7 @@ static ssize_t pmbus_show_samples(struct device *dev,
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
return sysfs_emit(buf, "%d\n", val);
|
||||
}
|
||||
|
||||
static ssize_t pmbus_set_samples(struct device *dev,
|
||||
@ -2288,7 +2299,7 @@ const struct regulator_ops pmbus_regulator_ops = {
|
||||
.disable = pmbus_regulator_disable,
|
||||
.is_enabled = pmbus_regulator_is_enabled,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(pmbus_regulator_ops);
|
||||
EXPORT_SYMBOL_NS_GPL(pmbus_regulator_ops, PMBUS);
|
||||
|
||||
static int pmbus_regulator_register(struct pmbus_data *data)
|
||||
{
|
||||
@ -2557,6 +2568,7 @@ int pmbus_do_probe(struct i2c_client *client, struct pmbus_driver_info *info)
|
||||
struct pmbus_data *data;
|
||||
size_t groups_num = 0;
|
||||
int ret;
|
||||
char *name;
|
||||
|
||||
if (!info)
|
||||
return -ENODEV;
|
||||
@ -2606,10 +2618,15 @@ int pmbus_do_probe(struct i2c_client *client, struct pmbus_driver_info *info)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
name = devm_kstrdup(dev, client->name, GFP_KERNEL);
|
||||
if (!name)
|
||||
return -ENOMEM;
|
||||
strreplace(name, '-', '_');
|
||||
|
||||
data->groups[0] = &data->group;
|
||||
memcpy(data->groups + 1, info->groups, sizeof(void *) * groups_num);
|
||||
data->hwmon_dev = devm_hwmon_device_register_with_groups(dev,
|
||||
client->name, data, data->groups);
|
||||
name, data, data->groups);
|
||||
if (IS_ERR(data->hwmon_dev)) {
|
||||
dev_err(dev, "Failed to register hwmon device\n");
|
||||
return PTR_ERR(data->hwmon_dev);
|
||||
@ -2625,7 +2642,7 @@ int pmbus_do_probe(struct i2c_client *client, struct pmbus_driver_info *info)
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pmbus_do_probe);
|
||||
EXPORT_SYMBOL_NS_GPL(pmbus_do_probe, PMBUS);
|
||||
|
||||
struct dentry *pmbus_get_debugfs_dir(struct i2c_client *client)
|
||||
{
|
||||
@ -2633,7 +2650,7 @@ struct dentry *pmbus_get_debugfs_dir(struct i2c_client *client)
|
||||
|
||||
return data->debugfs;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(pmbus_get_debugfs_dir);
|
||||
EXPORT_SYMBOL_NS_GPL(pmbus_get_debugfs_dir, PMBUS);
|
||||
|
||||
static int __init pmbus_core_init(void)
|
||||
{
|
||||
|
@ -41,6 +41,15 @@ static int pxe1610_identify(struct i2c_client *client,
|
||||
info->vrm_version[i] = vr13;
|
||||
break;
|
||||
default:
|
||||
/*
|
||||
* If prior pages are available limit operation
|
||||
* to them
|
||||
*/
|
||||
if (i != 0) {
|
||||
info->pages = i;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
@ -139,3 +148,4 @@ module_i2c_driver(pxe1610_driver);
|
||||
MODULE_AUTHOR("Vijay Khemka <vijaykhemka@fb.com>");
|
||||
MODULE_DESCRIPTION("PMBus driver for Infineon PXE1610, PXE1110 and PXM1310");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(PMBUS);
|
||||
|
@ -420,3 +420,4 @@ module_i2c_driver(q54sj108a2_driver);
|
||||
MODULE_AUTHOR("Xiao.Ma <xiao.mx.ma@deltaww.com>");
|
||||
MODULE_DESCRIPTION("PMBus driver for Delta Q54SJ108A2 series modules");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(PMBUS);
|
||||
|
249
drivers/hwmon/pmbus/stpddc60.c
Normal file
249
drivers/hwmon/pmbus/stpddc60.c
Normal file
@ -0,0 +1,249 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Hardware monitoring driver for the STPDDC60 controller
|
||||
*
|
||||
* Copyright (c) 2021 Flextronics International Sweden AB.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/pmbus.h>
|
||||
#include "pmbus.h"
|
||||
|
||||
#define STPDDC60_MFR_READ_VOUT 0xd2
|
||||
#define STPDDC60_MFR_OV_LIMIT_OFFSET 0xe5
|
||||
#define STPDDC60_MFR_UV_LIMIT_OFFSET 0xe6
|
||||
|
||||
static const struct i2c_device_id stpddc60_id[] = {
|
||||
{"stpddc60", 0},
|
||||
{"bmr481", 0},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, stpddc60_id);
|
||||
|
||||
static struct pmbus_driver_info stpddc60_info = {
|
||||
.pages = 1,
|
||||
.func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
|
||||
| PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT
|
||||
| PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP
|
||||
| PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
|
||||
| PMBUS_HAVE_POUT,
|
||||
};
|
||||
|
||||
/*
|
||||
* Calculate the closest absolute offset between commanded vout value
|
||||
* and limit value in steps of 50mv in the range 0 (50mv) to 7 (400mv).
|
||||
* Return 0 if the upper limit is lower than vout or if the lower limit
|
||||
* is higher than vout.
|
||||
*/
|
||||
static u8 stpddc60_get_offset(int vout, u16 limit, bool over)
|
||||
{
|
||||
int offset;
|
||||
long v, l;
|
||||
|
||||
v = 250 + (vout - 1) * 5; /* Convert VID to mv */
|
||||
l = (limit * 1000L) >> 8; /* Convert LINEAR to mv */
|
||||
|
||||
if (over == (l < v))
|
||||
return 0;
|
||||
|
||||
offset = DIV_ROUND_CLOSEST(abs(l - v), 50);
|
||||
|
||||
if (offset > 0)
|
||||
offset--;
|
||||
|
||||
return clamp_val(offset, 0, 7);
|
||||
}
|
||||
|
||||
/*
|
||||
* Adjust the linear format word to use the given fixed exponent.
|
||||
*/
|
||||
static u16 stpddc60_adjust_linear(u16 word, s16 fixed)
|
||||
{
|
||||
s16 e, m, d;
|
||||
|
||||
e = ((s16)word) >> 11;
|
||||
m = ((s16)((word & 0x7ff) << 5)) >> 5;
|
||||
d = e - fixed;
|
||||
|
||||
if (d >= 0)
|
||||
m <<= d;
|
||||
else
|
||||
m >>= -d;
|
||||
|
||||
return clamp_val(m, 0, 0x3ff) | ((fixed << 11) & 0xf800);
|
||||
}
|
||||
|
||||
/*
|
||||
* The VOUT_COMMAND register uses the VID format but the vout alarm limit
|
||||
* registers use the LINEAR format so we override VOUT_MODE here to force
|
||||
* LINEAR format for all registers.
|
||||
*/
|
||||
static int stpddc60_read_byte_data(struct i2c_client *client, int page, int reg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (page > 0)
|
||||
return -ENXIO;
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_VOUT_MODE:
|
||||
ret = 0x18;
|
||||
break;
|
||||
default:
|
||||
ret = -ENODATA;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* The vout related registers return values in LINEAR11 format when LINEAR16
|
||||
* is expected. Clear the top 5 bits to set the exponent part to zero to
|
||||
* convert the value to LINEAR16 format.
|
||||
*/
|
||||
static int stpddc60_read_word_data(struct i2c_client *client, int page,
|
||||
int phase, int reg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (page > 0)
|
||||
return -ENXIO;
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_READ_VOUT:
|
||||
ret = pmbus_read_word_data(client, page, phase,
|
||||
STPDDC60_MFR_READ_VOUT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret &= 0x7ff;
|
||||
break;
|
||||
case PMBUS_VOUT_OV_FAULT_LIMIT:
|
||||
case PMBUS_VOUT_UV_FAULT_LIMIT:
|
||||
ret = pmbus_read_word_data(client, page, phase, reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret &= 0x7ff;
|
||||
break;
|
||||
default:
|
||||
ret = -ENODATA;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* The vout under- and over-voltage limits are set as an offset relative to
|
||||
* the commanded vout voltage. The vin, iout, pout and temp limits must use
|
||||
* the same fixed exponent the chip uses to encode the data when read.
|
||||
*/
|
||||
static int stpddc60_write_word_data(struct i2c_client *client, int page,
|
||||
int reg, u16 word)
|
||||
{
|
||||
int ret;
|
||||
u8 offset;
|
||||
|
||||
if (page > 0)
|
||||
return -ENXIO;
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_VOUT_OV_FAULT_LIMIT:
|
||||
ret = pmbus_read_word_data(client, page, 0xff,
|
||||
PMBUS_VOUT_COMMAND);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
offset = stpddc60_get_offset(ret, word, true);
|
||||
ret = pmbus_write_byte_data(client, page,
|
||||
STPDDC60_MFR_OV_LIMIT_OFFSET,
|
||||
offset);
|
||||
break;
|
||||
case PMBUS_VOUT_UV_FAULT_LIMIT:
|
||||
ret = pmbus_read_word_data(client, page, 0xff,
|
||||
PMBUS_VOUT_COMMAND);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
offset = stpddc60_get_offset(ret, word, false);
|
||||
ret = pmbus_write_byte_data(client, page,
|
||||
STPDDC60_MFR_UV_LIMIT_OFFSET,
|
||||
offset);
|
||||
break;
|
||||
case PMBUS_VIN_OV_FAULT_LIMIT:
|
||||
case PMBUS_VIN_UV_FAULT_LIMIT:
|
||||
case PMBUS_OT_FAULT_LIMIT:
|
||||
case PMBUS_OT_WARN_LIMIT:
|
||||
case PMBUS_IOUT_OC_FAULT_LIMIT:
|
||||
case PMBUS_IOUT_OC_WARN_LIMIT:
|
||||
case PMBUS_POUT_OP_FAULT_LIMIT:
|
||||
ret = pmbus_read_word_data(client, page, 0xff, reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
word = stpddc60_adjust_linear(word, ret >> 11);
|
||||
ret = pmbus_write_word_data(client, page, reg, word);
|
||||
break;
|
||||
default:
|
||||
ret = -ENODATA;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int stpddc60_probe(struct i2c_client *client)
|
||||
{
|
||||
int status;
|
||||
u8 device_id[I2C_SMBUS_BLOCK_MAX + 1];
|
||||
const struct i2c_device_id *mid;
|
||||
struct pmbus_driver_info *info = &stpddc60_info;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_READ_BYTE_DATA
|
||||
| I2C_FUNC_SMBUS_BLOCK_DATA))
|
||||
return -ENODEV;
|
||||
|
||||
status = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, device_id);
|
||||
if (status < 0) {
|
||||
dev_err(&client->dev, "Failed to read Manufacturer Model\n");
|
||||
return status;
|
||||
}
|
||||
for (mid = stpddc60_id; mid->name[0]; mid++) {
|
||||
if (!strncasecmp(mid->name, device_id, strlen(mid->name)))
|
||||
break;
|
||||
}
|
||||
if (!mid->name[0]) {
|
||||
dev_err(&client->dev, "Unsupported device\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
info->read_byte_data = stpddc60_read_byte_data;
|
||||
info->read_word_data = stpddc60_read_word_data;
|
||||
info->write_word_data = stpddc60_write_word_data;
|
||||
|
||||
status = pmbus_do_probe(client, info);
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
pmbus_set_update(client, PMBUS_VOUT_OV_FAULT_LIMIT, true);
|
||||
pmbus_set_update(client, PMBUS_VOUT_UV_FAULT_LIMIT, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct i2c_driver stpddc60_driver = {
|
||||
.driver = {
|
||||
.name = "stpddc60",
|
||||
},
|
||||
.probe_new = stpddc60_probe,
|
||||
.id_table = stpddc60_id,
|
||||
};
|
||||
|
||||
module_i2c_driver(stpddc60_driver);
|
||||
|
||||
MODULE_AUTHOR("Erik Rosen <erik.rosen@metormote.com>");
|
||||
MODULE_DESCRIPTION("PMBus driver for ST STPDDC60");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(PMBUS);
|
@ -51,3 +51,4 @@ module_i2c_driver(tps40422_driver);
|
||||
MODULE_AUTHOR("Zhu Laiwen <richard.zhu@nsn.com>");
|
||||
MODULE_DESCRIPTION("PMBus driver for TI TPS40422");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(PMBUS);
|
||||
|
@ -16,11 +16,14 @@
|
||||
#include "pmbus.h"
|
||||
|
||||
enum chips {
|
||||
tps53647, tps53667, tps53679, tps53681, tps53688
|
||||
tps53647, tps53667, tps53676, tps53679, tps53681, tps53688
|
||||
};
|
||||
|
||||
#define TPS53647_PAGE_NUM 1
|
||||
|
||||
#define TPS53676_USER_DATA_03 0xb3
|
||||
#define TPS53676_MAX_PHASES 7
|
||||
|
||||
#define TPS53679_PROT_VR12_5MV 0x01 /* VR12.0 mode, 5-mV DAC */
|
||||
#define TPS53679_PROT_VR12_5_10MV 0x02 /* VR12.5 mode, 10-mV DAC */
|
||||
#define TPS53679_PROT_VR13_10MV 0x04 /* VR13.0 mode, 10-mV DAC */
|
||||
@ -143,6 +146,45 @@ static int tps53681_identify(struct i2c_client *client,
|
||||
TPS53681_DEVICE_ID);
|
||||
}
|
||||
|
||||
static int tps53676_identify(struct i2c_client *client,
|
||||
struct pmbus_driver_info *info)
|
||||
{
|
||||
u8 buf[I2C_SMBUS_BLOCK_MAX];
|
||||
int phases_a = 0, phases_b = 0;
|
||||
int i, ret;
|
||||
|
||||
ret = i2c_smbus_read_block_data(client, PMBUS_IC_DEVICE_ID, buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (strncmp("TI\x53\x67\x60", buf, 5)) {
|
||||
dev_err(&client->dev, "Unexpected device ID: %s\n", buf);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_read_block_data(client, TPS53676_USER_DATA_03, buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret != 24)
|
||||
return -EIO;
|
||||
for (i = 0; i < 2 * TPS53676_MAX_PHASES; i += 2) {
|
||||
if (buf[i + 1] & 0x80) {
|
||||
if (buf[i] & 0x08)
|
||||
phases_b++;
|
||||
else
|
||||
phases_a++;
|
||||
}
|
||||
}
|
||||
|
||||
info->format[PSC_VOLTAGE_OUT] = linear;
|
||||
info->pages = 1;
|
||||
info->phases[0] = phases_a;
|
||||
if (phases_b > 0) {
|
||||
info->pages = 2;
|
||||
info->phases[1] = phases_b;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tps53681_read_word_data(struct i2c_client *client, int page,
|
||||
int phase, int reg)
|
||||
{
|
||||
@ -183,6 +225,7 @@ static struct pmbus_driver_info tps53679_info = {
|
||||
.pfunc[3] = PMBUS_HAVE_IOUT,
|
||||
.pfunc[4] = PMBUS_HAVE_IOUT,
|
||||
.pfunc[5] = PMBUS_HAVE_IOUT,
|
||||
.pfunc[6] = PMBUS_HAVE_IOUT,
|
||||
};
|
||||
|
||||
static int tps53679_probe(struct i2c_client *client)
|
||||
@ -206,6 +249,9 @@ static int tps53679_probe(struct i2c_client *client)
|
||||
info->pages = TPS53647_PAGE_NUM;
|
||||
info->identify = tps53679_identify;
|
||||
break;
|
||||
case tps53676:
|
||||
info->identify = tps53676_identify;
|
||||
break;
|
||||
case tps53679:
|
||||
case tps53688:
|
||||
info->pages = TPS53679_PAGE_NUM;
|
||||
@ -225,8 +271,10 @@ static int tps53679_probe(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id tps53679_id[] = {
|
||||
{"bmr474", tps53676},
|
||||
{"tps53647", tps53647},
|
||||
{"tps53667", tps53667},
|
||||
{"tps53676", tps53676},
|
||||
{"tps53679", tps53679},
|
||||
{"tps53681", tps53681},
|
||||
{"tps53688", tps53688},
|
||||
@ -238,6 +286,7 @@ MODULE_DEVICE_TABLE(i2c, tps53679_id);
|
||||
static const struct of_device_id __maybe_unused tps53679_of_match[] = {
|
||||
{.compatible = "ti,tps53647", .data = (void *)tps53647},
|
||||
{.compatible = "ti,tps53667", .data = (void *)tps53667},
|
||||
{.compatible = "ti,tps53676", .data = (void *)tps53676},
|
||||
{.compatible = "ti,tps53679", .data = (void *)tps53679},
|
||||
{.compatible = "ti,tps53681", .data = (void *)tps53681},
|
||||
{.compatible = "ti,tps53688", .data = (void *)tps53688},
|
||||
@ -259,3 +308,4 @@ module_i2c_driver(tps53679_driver);
|
||||
MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>");
|
||||
MODULE_DESCRIPTION("PMBus driver for Texas Instruments TPS53679");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(PMBUS);
|
||||
|
@ -629,3 +629,4 @@ module_i2c_driver(ucd9000_driver);
|
||||
MODULE_AUTHOR("Guenter Roeck");
|
||||
MODULE_DESCRIPTION("PMBus driver for TI UCD90xxx");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(PMBUS);
|
||||
|
@ -209,3 +209,4 @@ module_i2c_driver(ucd9200_driver);
|
||||
MODULE_AUTHOR("Guenter Roeck");
|
||||
MODULE_DESCRIPTION("PMBus driver for TI UCD922x, UCD924x");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(PMBUS);
|
||||
|
@ -168,3 +168,4 @@ module_i2c_driver(xdpe122_driver);
|
||||
MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>");
|
||||
MODULE_DESCRIPTION("PMBus driver for Infineon XDPE122 family");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(PMBUS);
|
||||
|
@ -404,3 +404,4 @@ module_i2c_driver(zl6100_driver);
|
||||
MODULE_AUTHOR("Guenter Roeck");
|
||||
MODULE_DESCRIPTION("PMBus driver for ZL6100 and compatibles");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(PMBUS);
|
||||
|
@ -166,7 +166,7 @@ static ssize_t s3c_hwmon_ch_show(struct device *dev,
|
||||
ret *= cfg->mult;
|
||||
ret = DIV_ROUND_CLOSEST(ret, cfg->div);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", ret);
|
||||
return sysfs_emit(buf, "%d\n", ret);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -187,7 +187,7 @@ static ssize_t s3c_hwmon_label_show(struct device *dev,
|
||||
|
||||
cfg = pdata->in[sen_attr->index];
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", cfg->name);
|
||||
return sysfs_emit(buf, "%s\n", cfg->name);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mutex.h>
|
||||
#include "sch56xx-common.h"
|
||||
@ -65,7 +64,6 @@ static const char * const SCH5627_IN_LABELS[SCH5627_NO_IN] = {
|
||||
|
||||
struct sch5627_data {
|
||||
unsigned short addr;
|
||||
struct device *hwmon_dev;
|
||||
struct sch56xx_watchdog_data *watchdog;
|
||||
u8 control;
|
||||
u8 temp_max[SCH5627_NO_TEMPS];
|
||||
@ -74,66 +72,96 @@ struct sch5627_data {
|
||||
|
||||
struct mutex update_lock;
|
||||
unsigned long last_battery; /* In jiffies */
|
||||
char valid; /* !=0 if following fields are valid */
|
||||
unsigned long last_updated; /* In jiffies */
|
||||
char temp_valid; /* !=0 if following fields are valid */
|
||||
char fan_valid;
|
||||
char in_valid;
|
||||
unsigned long temp_last_updated; /* In jiffies */
|
||||
unsigned long fan_last_updated;
|
||||
unsigned long in_last_updated;
|
||||
u16 temp[SCH5627_NO_TEMPS];
|
||||
u16 fan[SCH5627_NO_FANS];
|
||||
u16 in[SCH5627_NO_IN];
|
||||
};
|
||||
|
||||
static struct sch5627_data *sch5627_update_device(struct device *dev)
|
||||
static int sch5627_update_temp(struct sch5627_data *data)
|
||||
{
|
||||
struct sch5627_data *data = dev_get_drvdata(dev);
|
||||
struct sch5627_data *ret = data;
|
||||
int ret = 0;
|
||||
int i, val;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
/* Cache the values for 1 second */
|
||||
if (time_after(jiffies, data->temp_last_updated + HZ) || !data->temp_valid) {
|
||||
for (i = 0; i < SCH5627_NO_TEMPS; i++) {
|
||||
val = sch56xx_read_virtual_reg12(data->addr, SCH5627_REG_TEMP_MSB[i],
|
||||
SCH5627_REG_TEMP_LSN[i],
|
||||
SCH5627_REG_TEMP_HIGH_NIBBLE[i]);
|
||||
if (unlikely(val < 0)) {
|
||||
ret = val;
|
||||
goto abort;
|
||||
}
|
||||
data->temp[i] = val;
|
||||
}
|
||||
data->temp_last_updated = jiffies;
|
||||
data->temp_valid = 1;
|
||||
}
|
||||
abort:
|
||||
mutex_unlock(&data->update_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sch5627_update_fan(struct sch5627_data *data)
|
||||
{
|
||||
int ret = 0;
|
||||
int i, val;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
/* Cache the values for 1 second */
|
||||
if (time_after(jiffies, data->fan_last_updated + HZ) || !data->fan_valid) {
|
||||
for (i = 0; i < SCH5627_NO_FANS; i++) {
|
||||
val = sch56xx_read_virtual_reg16(data->addr, SCH5627_REG_FAN[i]);
|
||||
if (unlikely(val < 0)) {
|
||||
ret = val;
|
||||
goto abort;
|
||||
}
|
||||
data->fan[i] = val;
|
||||
}
|
||||
data->fan_last_updated = jiffies;
|
||||
data->fan_valid = 1;
|
||||
}
|
||||
abort:
|
||||
mutex_unlock(&data->update_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sch5627_update_in(struct sch5627_data *data)
|
||||
{
|
||||
int ret = 0;
|
||||
int i, val;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
/* Trigger a Vbat voltage measurement every 5 minutes */
|
||||
if (time_after(jiffies, data->last_battery + 300 * HZ)) {
|
||||
sch56xx_write_virtual_reg(data->addr, SCH5627_REG_CTRL,
|
||||
data->control | 0x10);
|
||||
sch56xx_write_virtual_reg(data->addr, SCH5627_REG_CTRL, data->control | 0x10);
|
||||
data->last_battery = jiffies;
|
||||
}
|
||||
|
||||
/* Cache the values for 1 second */
|
||||
if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
|
||||
for (i = 0; i < SCH5627_NO_TEMPS; i++) {
|
||||
val = sch56xx_read_virtual_reg12(data->addr,
|
||||
SCH5627_REG_TEMP_MSB[i],
|
||||
SCH5627_REG_TEMP_LSN[i],
|
||||
SCH5627_REG_TEMP_HIGH_NIBBLE[i]);
|
||||
if (unlikely(val < 0)) {
|
||||
ret = ERR_PTR(val);
|
||||
goto abort;
|
||||
}
|
||||
data->temp[i] = val;
|
||||
}
|
||||
|
||||
for (i = 0; i < SCH5627_NO_FANS; i++) {
|
||||
val = sch56xx_read_virtual_reg16(data->addr,
|
||||
SCH5627_REG_FAN[i]);
|
||||
if (unlikely(val < 0)) {
|
||||
ret = ERR_PTR(val);
|
||||
goto abort;
|
||||
}
|
||||
data->fan[i] = val;
|
||||
}
|
||||
|
||||
if (time_after(jiffies, data->in_last_updated + HZ) || !data->in_valid) {
|
||||
for (i = 0; i < SCH5627_NO_IN; i++) {
|
||||
val = sch56xx_read_virtual_reg12(data->addr,
|
||||
SCH5627_REG_IN_MSB[i],
|
||||
SCH5627_REG_IN_LSN[i],
|
||||
SCH5627_REG_IN_HIGH_NIBBLE[i]);
|
||||
val = sch56xx_read_virtual_reg12(data->addr, SCH5627_REG_IN_MSB[i],
|
||||
SCH5627_REG_IN_LSN[i],
|
||||
SCH5627_REG_IN_HIGH_NIBBLE[i]);
|
||||
if (unlikely(val < 0)) {
|
||||
ret = ERR_PTR(val);
|
||||
ret = val;
|
||||
goto abort;
|
||||
}
|
||||
data->in[i] = val;
|
||||
}
|
||||
|
||||
data->last_updated = jiffies;
|
||||
data->valid = 1;
|
||||
data->in_last_updated = jiffies;
|
||||
data->in_valid = 1;
|
||||
}
|
||||
abort:
|
||||
mutex_unlock(&data->update_lock);
|
||||
@ -192,249 +220,141 @@ static int reg_to_rpm(u16 reg)
|
||||
return 5400540 / reg;
|
||||
}
|
||||
|
||||
static ssize_t name_show(struct device *dev, struct device_attribute *devattr,
|
||||
char *buf)
|
||||
static umode_t sch5627_is_visible(const void *drvdata, enum hwmon_sensor_types type, u32 attr,
|
||||
int channel)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", DEVNAME);
|
||||
return 0444;
|
||||
}
|
||||
|
||||
static ssize_t temp_show(struct device *dev, struct device_attribute *devattr,
|
||||
char *buf)
|
||||
static int sch5627_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
|
||||
long *val)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct sch5627_data *data = sch5627_update_device(dev);
|
||||
int val;
|
||||
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
|
||||
val = reg_to_temp(data->temp[attr->index]);
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
}
|
||||
|
||||
static ssize_t temp_fault_show(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct sch5627_data *data = sch5627_update_device(dev);
|
||||
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", data->temp[attr->index] == 0);
|
||||
}
|
||||
|
||||
static ssize_t temp_max_show(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct sch5627_data *data = dev_get_drvdata(dev);
|
||||
int val;
|
||||
int ret;
|
||||
|
||||
val = reg_to_temp_limit(data->temp_max[attr->index]);
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
switch (type) {
|
||||
case hwmon_temp:
|
||||
ret = sch5627_update_temp(data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
switch (attr) {
|
||||
case hwmon_temp_input:
|
||||
*val = reg_to_temp(data->temp[channel]);
|
||||
return 0;
|
||||
case hwmon_temp_max:
|
||||
*val = reg_to_temp_limit(data->temp_max[channel]);
|
||||
return 0;
|
||||
case hwmon_temp_crit:
|
||||
*val = reg_to_temp_limit(data->temp_crit[channel]);
|
||||
return 0;
|
||||
case hwmon_temp_fault:
|
||||
*val = (data->temp[channel] == 0);
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case hwmon_fan:
|
||||
ret = sch5627_update_fan(data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
switch (attr) {
|
||||
case hwmon_fan_input:
|
||||
ret = reg_to_rpm(data->fan[channel]);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
return 0;
|
||||
case hwmon_fan_min:
|
||||
ret = reg_to_rpm(data->fan_min[channel]);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
return 0;
|
||||
case hwmon_fan_fault:
|
||||
*val = (data->fan[channel] == 0xffff);
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case hwmon_in:
|
||||
ret = sch5627_update_in(data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
switch (attr) {
|
||||
case hwmon_in_input:
|
||||
*val = DIV_ROUND_CLOSEST(data->in[channel] * SCH5627_REG_IN_FACTOR[channel],
|
||||
10000);
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static ssize_t temp_crit_show(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
static int sch5627_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr,
|
||||
int channel, const char **str)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct sch5627_data *data = dev_get_drvdata(dev);
|
||||
int val;
|
||||
switch (type) {
|
||||
case hwmon_in:
|
||||
switch (attr) {
|
||||
case hwmon_in_label:
|
||||
*str = SCH5627_IN_LABELS[channel];
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
val = reg_to_temp_limit(data->temp_crit[attr->index]);
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static ssize_t fan_show(struct device *dev, struct device_attribute *devattr,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct sch5627_data *data = sch5627_update_device(dev);
|
||||
int val;
|
||||
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
|
||||
val = reg_to_rpm(data->fan[attr->index]);
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
}
|
||||
|
||||
static ssize_t fan_fault_show(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct sch5627_data *data = sch5627_update_device(dev);
|
||||
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n",
|
||||
data->fan[attr->index] == 0xffff);
|
||||
}
|
||||
|
||||
static ssize_t fan_min_show(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct sch5627_data *data = dev_get_drvdata(dev);
|
||||
int val = reg_to_rpm(data->fan_min[attr->index]);
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
}
|
||||
|
||||
static ssize_t in_show(struct device *dev, struct device_attribute *devattr,
|
||||
char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct sch5627_data *data = sch5627_update_device(dev);
|
||||
int val;
|
||||
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
|
||||
val = DIV_ROUND_CLOSEST(
|
||||
data->in[attr->index] * SCH5627_REG_IN_FACTOR[attr->index],
|
||||
10000);
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
}
|
||||
|
||||
static ssize_t in_label_show(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n",
|
||||
SCH5627_IN_LABELS[attr->index]);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RO(name);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, 0);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp2_input, temp, 1);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp3_input, temp, 2);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp4_input, temp, 3);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp5_input, temp, 4);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp6_input, temp, 5);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp7_input, temp, 6);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp8_input, temp, 7);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp1_fault, temp_fault, 0);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp2_fault, temp_fault, 1);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp3_fault, temp_fault, 2);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp4_fault, temp_fault, 3);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp5_fault, temp_fault, 4);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp6_fault, temp_fault, 5);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp7_fault, temp_fault, 6);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp8_fault, temp_fault, 7);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp1_max, temp_max, 0);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp2_max, temp_max, 1);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp3_max, temp_max, 2);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp4_max, temp_max, 3);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp5_max, temp_max, 4);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp6_max, temp_max, 5);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp7_max, temp_max, 6);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp8_max, temp_max, 7);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp1_crit, temp_crit, 0);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp2_crit, temp_crit, 1);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp3_crit, temp_crit, 2);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp4_crit, temp_crit, 3);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp5_crit, temp_crit, 4);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp6_crit, temp_crit, 5);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp7_crit, temp_crit, 6);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp8_crit, temp_crit, 7);
|
||||
|
||||
static SENSOR_DEVICE_ATTR_RO(fan1_input, fan, 0);
|
||||
static SENSOR_DEVICE_ATTR_RO(fan2_input, fan, 1);
|
||||
static SENSOR_DEVICE_ATTR_RO(fan3_input, fan, 2);
|
||||
static SENSOR_DEVICE_ATTR_RO(fan4_input, fan, 3);
|
||||
static SENSOR_DEVICE_ATTR_RO(fan1_fault, fan_fault, 0);
|
||||
static SENSOR_DEVICE_ATTR_RO(fan2_fault, fan_fault, 1);
|
||||
static SENSOR_DEVICE_ATTR_RO(fan3_fault, fan_fault, 2);
|
||||
static SENSOR_DEVICE_ATTR_RO(fan4_fault, fan_fault, 3);
|
||||
static SENSOR_DEVICE_ATTR_RO(fan1_min, fan_min, 0);
|
||||
static SENSOR_DEVICE_ATTR_RO(fan2_min, fan_min, 1);
|
||||
static SENSOR_DEVICE_ATTR_RO(fan3_min, fan_min, 2);
|
||||
static SENSOR_DEVICE_ATTR_RO(fan4_min, fan_min, 3);
|
||||
|
||||
static SENSOR_DEVICE_ATTR_RO(in0_input, in, 0);
|
||||
static SENSOR_DEVICE_ATTR_RO(in1_input, in, 1);
|
||||
static SENSOR_DEVICE_ATTR_RO(in2_input, in, 2);
|
||||
static SENSOR_DEVICE_ATTR_RO(in3_input, in, 3);
|
||||
static SENSOR_DEVICE_ATTR_RO(in4_input, in, 4);
|
||||
static SENSOR_DEVICE_ATTR_RO(in0_label, in_label, 0);
|
||||
static SENSOR_DEVICE_ATTR_RO(in1_label, in_label, 1);
|
||||
static SENSOR_DEVICE_ATTR_RO(in2_label, in_label, 2);
|
||||
static SENSOR_DEVICE_ATTR_RO(in3_label, in_label, 3);
|
||||
|
||||
static struct attribute *sch5627_attributes[] = {
|
||||
&dev_attr_name.attr,
|
||||
|
||||
&sensor_dev_attr_temp1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp5_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp6_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp7_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp8_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_temp5_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_temp6_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_temp7_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_temp8_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp5_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp6_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp7_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp8_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_crit.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_crit.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_crit.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_crit.dev_attr.attr,
|
||||
&sensor_dev_attr_temp5_crit.dev_attr.attr,
|
||||
&sensor_dev_attr_temp6_crit.dev_attr.attr,
|
||||
&sensor_dev_attr_temp7_crit.dev_attr.attr,
|
||||
&sensor_dev_attr_temp8_crit.dev_attr.attr,
|
||||
|
||||
&sensor_dev_attr_fan1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan3_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan4_input.dev_attr.attr,
|
||||
&sensor_dev_attr_fan1_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_fan2_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_fan3_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_fan4_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_fan1_min.dev_attr.attr,
|
||||
&sensor_dev_attr_fan2_min.dev_attr.attr,
|
||||
&sensor_dev_attr_fan3_min.dev_attr.attr,
|
||||
&sensor_dev_attr_fan4_min.dev_attr.attr,
|
||||
|
||||
&sensor_dev_attr_in0_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in3_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in4_input.dev_attr.attr,
|
||||
&sensor_dev_attr_in0_label.dev_attr.attr,
|
||||
&sensor_dev_attr_in1_label.dev_attr.attr,
|
||||
&sensor_dev_attr_in2_label.dev_attr.attr,
|
||||
&sensor_dev_attr_in3_label.dev_attr.attr,
|
||||
/* No in4_label as in4 is a generic input pin */
|
||||
static const struct hwmon_ops sch5627_ops = {
|
||||
.is_visible = sch5627_is_visible,
|
||||
.read = sch5627_read,
|
||||
.read_string = sch5627_read_string,
|
||||
};
|
||||
|
||||
static const struct hwmon_channel_info *sch5627_info[] = {
|
||||
HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ),
|
||||
HWMON_CHANNEL_INFO(temp,
|
||||
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT,
|
||||
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT,
|
||||
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT,
|
||||
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT,
|
||||
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT,
|
||||
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT,
|
||||
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT,
|
||||
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT
|
||||
),
|
||||
HWMON_CHANNEL_INFO(fan,
|
||||
HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_FAULT,
|
||||
HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_FAULT,
|
||||
HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_FAULT,
|
||||
HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_FAULT
|
||||
),
|
||||
HWMON_CHANNEL_INFO(in,
|
||||
HWMON_I_INPUT | HWMON_I_LABEL,
|
||||
HWMON_I_INPUT | HWMON_I_LABEL,
|
||||
HWMON_I_INPUT | HWMON_I_LABEL,
|
||||
HWMON_I_INPUT | HWMON_I_LABEL,
|
||||
HWMON_I_INPUT
|
||||
),
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group sch5627_group = {
|
||||
.attrs = sch5627_attributes,
|
||||
static const struct hwmon_chip_info sch5627_chip_info = {
|
||||
.ops = &sch5627_ops,
|
||||
.info = sch5627_info,
|
||||
};
|
||||
|
||||
static int sch5627_remove(struct platform_device *pdev)
|
||||
@ -444,17 +364,13 @@ static int sch5627_remove(struct platform_device *pdev)
|
||||
if (data->watchdog)
|
||||
sch56xx_watchdog_unregister(data->watchdog);
|
||||
|
||||
if (data->hwmon_dev)
|
||||
hwmon_device_unregister(data->hwmon_dev);
|
||||
|
||||
sysfs_remove_group(&pdev->dev.kobj, &sch5627_group);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sch5627_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct sch5627_data *data;
|
||||
struct device *hwmon_dev;
|
||||
int err, build_code, build_id, hwmon_rev, val;
|
||||
|
||||
data = devm_kzalloc(&pdev->dev, sizeof(struct sch5627_data),
|
||||
@ -467,72 +383,58 @@ static int sch5627_probe(struct platform_device *pdev)
|
||||
platform_set_drvdata(pdev, data);
|
||||
|
||||
val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_HWMON_ID);
|
||||
if (val < 0) {
|
||||
err = val;
|
||||
goto error;
|
||||
}
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
if (val != SCH5627_HWMON_ID) {
|
||||
pr_err("invalid %s id: 0x%02X (expected 0x%02X)\n", "hwmon",
|
||||
val, SCH5627_HWMON_ID);
|
||||
err = -ENODEV;
|
||||
goto error;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_COMPANY_ID);
|
||||
if (val < 0) {
|
||||
err = val;
|
||||
goto error;
|
||||
}
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
if (val != SCH5627_COMPANY_ID) {
|
||||
pr_err("invalid %s id: 0x%02X (expected 0x%02X)\n", "company",
|
||||
val, SCH5627_COMPANY_ID);
|
||||
err = -ENODEV;
|
||||
goto error;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_PRIMARY_ID);
|
||||
if (val < 0) {
|
||||
err = val;
|
||||
goto error;
|
||||
}
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
if (val != SCH5627_PRIMARY_ID) {
|
||||
pr_err("invalid %s id: 0x%02X (expected 0x%02X)\n", "primary",
|
||||
val, SCH5627_PRIMARY_ID);
|
||||
err = -ENODEV;
|
||||
goto error;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
build_code = sch56xx_read_virtual_reg(data->addr,
|
||||
SCH5627_REG_BUILD_CODE);
|
||||
if (build_code < 0) {
|
||||
err = build_code;
|
||||
goto error;
|
||||
}
|
||||
if (build_code < 0)
|
||||
return build_code;
|
||||
|
||||
build_id = sch56xx_read_virtual_reg16(data->addr,
|
||||
SCH5627_REG_BUILD_ID);
|
||||
if (build_id < 0) {
|
||||
err = build_id;
|
||||
goto error;
|
||||
}
|
||||
if (build_id < 0)
|
||||
return build_id;
|
||||
|
||||
hwmon_rev = sch56xx_read_virtual_reg(data->addr,
|
||||
SCH5627_REG_HWMON_REV);
|
||||
if (hwmon_rev < 0) {
|
||||
err = hwmon_rev;
|
||||
goto error;
|
||||
}
|
||||
if (hwmon_rev < 0)
|
||||
return hwmon_rev;
|
||||
|
||||
val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_CTRL);
|
||||
if (val < 0) {
|
||||
err = val;
|
||||
goto error;
|
||||
}
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
data->control = val;
|
||||
if (!(data->control & 0x01)) {
|
||||
pr_err("hardware monitoring not enabled\n");
|
||||
err = -ENODEV;
|
||||
goto error;
|
||||
return -ENODEV;
|
||||
}
|
||||
/* Trigger a Vbat voltage measurement, so that we get a valid reading
|
||||
the first time we read Vbat */
|
||||
@ -546,23 +448,16 @@ static int sch5627_probe(struct platform_device *pdev)
|
||||
*/
|
||||
err = sch5627_read_limits(data);
|
||||
if (err)
|
||||
goto error;
|
||||
return err;
|
||||
|
||||
pr_info("found %s chip at %#hx\n", DEVNAME, data->addr);
|
||||
pr_info("firmware build: code 0x%02X, id 0x%04X, hwmon: rev 0x%02X\n",
|
||||
build_code, build_id, hwmon_rev);
|
||||
|
||||
/* Register sysfs interface files */
|
||||
err = sysfs_create_group(&pdev->dev.kobj, &sch5627_group);
|
||||
if (err)
|
||||
goto error;
|
||||
|
||||
data->hwmon_dev = hwmon_device_register(&pdev->dev);
|
||||
if (IS_ERR(data->hwmon_dev)) {
|
||||
err = PTR_ERR(data->hwmon_dev);
|
||||
data->hwmon_dev = NULL;
|
||||
goto error;
|
||||
}
|
||||
hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, DEVNAME, data,
|
||||
&sch5627_chip_info, NULL);
|
||||
if (IS_ERR(hwmon_dev))
|
||||
return PTR_ERR(hwmon_dev);
|
||||
|
||||
/* Note failing to register the watchdog is not a fatal error */
|
||||
data->watchdog = sch56xx_watchdog_register(&pdev->dev, data->addr,
|
||||
@ -570,10 +465,6 @@ static int sch5627_probe(struct platform_device *pdev)
|
||||
&data->update_lock, 1);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
sch5627_remove(pdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct platform_driver sch5627_driver = {
|
||||
|
@ -160,7 +160,7 @@ static int reg_to_rpm(u16 reg)
|
||||
static ssize_t name_show(struct device *dev, struct device_attribute *devattr,
|
||||
char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", DEVNAME);
|
||||
return sysfs_emit(buf, "%s\n", DEVNAME);
|
||||
}
|
||||
|
||||
static ssize_t in_value_show(struct device *dev,
|
||||
@ -176,7 +176,7 @@ static ssize_t in_value_show(struct device *dev,
|
||||
val = DIV_ROUND_CLOSEST(
|
||||
data->in[attr->index] * SCH5636_REG_IN_FACTORS[attr->index],
|
||||
255);
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
return sysfs_emit(buf, "%d\n", val);
|
||||
}
|
||||
|
||||
static ssize_t in_label_show(struct device *dev,
|
||||
@ -184,8 +184,8 @@ static ssize_t in_label_show(struct device *dev,
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n",
|
||||
SCH5636_IN_LABELS[attr->index]);
|
||||
return sysfs_emit(buf, "%s\n",
|
||||
SCH5636_IN_LABELS[attr->index]);
|
||||
}
|
||||
|
||||
static ssize_t temp_value_show(struct device *dev,
|
||||
@ -199,7 +199,7 @@ static ssize_t temp_value_show(struct device *dev,
|
||||
return PTR_ERR(data);
|
||||
|
||||
val = (data->temp_val[attr->index] - 64) * 1000;
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
return sysfs_emit(buf, "%d\n", val);
|
||||
}
|
||||
|
||||
static ssize_t temp_fault_show(struct device *dev,
|
||||
@ -213,7 +213,7 @@ static ssize_t temp_fault_show(struct device *dev,
|
||||
return PTR_ERR(data);
|
||||
|
||||
val = (data->temp_ctrl[attr->index] & SCH5636_TEMP_WORKING) ? 0 : 1;
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
return sysfs_emit(buf, "%d\n", val);
|
||||
}
|
||||
|
||||
static ssize_t temp_alarm_show(struct device *dev,
|
||||
@ -227,7 +227,7 @@ static ssize_t temp_alarm_show(struct device *dev,
|
||||
return PTR_ERR(data);
|
||||
|
||||
val = (data->temp_ctrl[attr->index] & SCH5636_TEMP_ALARM) ? 1 : 0;
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
return sysfs_emit(buf, "%d\n", val);
|
||||
}
|
||||
|
||||
static ssize_t fan_value_show(struct device *dev,
|
||||
@ -244,7 +244,7 @@ static ssize_t fan_value_show(struct device *dev,
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
return sysfs_emit(buf, "%d\n", val);
|
||||
}
|
||||
|
||||
static ssize_t fan_fault_show(struct device *dev,
|
||||
@ -258,7 +258,7 @@ static ssize_t fan_fault_show(struct device *dev,
|
||||
return PTR_ERR(data);
|
||||
|
||||
val = (data->fan_ctrl[attr->index] & SCH5636_FAN_NOT_PRESENT) ? 1 : 0;
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
return sysfs_emit(buf, "%d\n", val);
|
||||
}
|
||||
|
||||
static ssize_t fan_alarm_show(struct device *dev,
|
||||
@ -272,7 +272,7 @@ static ssize_t fan_alarm_show(struct device *dev,
|
||||
return PTR_ERR(data);
|
||||
|
||||
val = (data->fan_ctrl[attr->index] & SCH5636_FAN_ALARM) ? 1 : 0;
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
return sysfs_emit(buf, "%d\n", val);
|
||||
}
|
||||
|
||||
static struct sensor_device_attribute sch5636_attr[] = {
|
||||
|
@ -351,7 +351,7 @@ static ssize_t smm665_show_crit_alarm(struct device *dev,
|
||||
if (data->faults & (1 << attr->index))
|
||||
val = 1;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
return sysfs_emit(buf, "%d\n", val);
|
||||
}
|
||||
|
||||
static ssize_t smm665_show_input(struct device *dev,
|
||||
@ -366,7 +366,7 @@ static ssize_t smm665_show_input(struct device *dev,
|
||||
return PTR_ERR(data);
|
||||
|
||||
val = smm665_convert(data->adc[adc], adc);
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", val);
|
||||
return sysfs_emit(buf, "%d\n", val);
|
||||
}
|
||||
|
||||
#define SMM665_SHOW(what) \
|
||||
|
@ -387,7 +387,7 @@ static ssize_t max_alarm_show(struct device *dev,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", priv->max_alert);
|
||||
return sysfs_emit(buf, "%d\n", priv->max_alert);
|
||||
}
|
||||
|
||||
static ssize_t min_alarm_show(struct device *dev,
|
||||
@ -404,7 +404,7 @@ static ssize_t min_alarm_show(struct device *dev,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", priv->min_alert);
|
||||
return sysfs_emit(buf, "%d\n", priv->min_alert);
|
||||
}
|
||||
|
||||
static ssize_t input_show(struct device *dev, struct device_attribute *attr,
|
||||
@ -419,7 +419,7 @@ static ssize_t input_show(struct device *dev, struct device_attribute *attr,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", priv->temp);
|
||||
return sysfs_emit(buf, "%d\n", priv->temp);
|
||||
}
|
||||
|
||||
static ssize_t therm_show(struct device *dev, struct device_attribute *attr,
|
||||
@ -427,7 +427,7 @@ static ssize_t therm_show(struct device *dev, struct device_attribute *attr,
|
||||
{
|
||||
struct stts751_priv *priv = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", priv->therm);
|
||||
return sysfs_emit(buf, "%d\n", priv->therm);
|
||||
}
|
||||
|
||||
static ssize_t therm_store(struct device *dev, struct device_attribute *attr,
|
||||
@ -469,7 +469,7 @@ static ssize_t hyst_show(struct device *dev, struct device_attribute *attr,
|
||||
{
|
||||
struct stts751_priv *priv = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", priv->hyst);
|
||||
return sysfs_emit(buf, "%d\n", priv->hyst);
|
||||
}
|
||||
|
||||
static ssize_t hyst_store(struct device *dev, struct device_attribute *attr,
|
||||
@ -509,7 +509,7 @@ static ssize_t therm_trip_show(struct device *dev,
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", priv->therm_trip);
|
||||
return sysfs_emit(buf, "%d\n", priv->therm_trip);
|
||||
}
|
||||
|
||||
static ssize_t max_show(struct device *dev, struct device_attribute *attr,
|
||||
@ -517,7 +517,7 @@ static ssize_t max_show(struct device *dev, struct device_attribute *attr,
|
||||
{
|
||||
struct stts751_priv *priv = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", priv->event_max);
|
||||
return sysfs_emit(buf, "%d\n", priv->event_max);
|
||||
}
|
||||
|
||||
static ssize_t max_store(struct device *dev, struct device_attribute *attr,
|
||||
@ -551,7 +551,7 @@ static ssize_t min_show(struct device *dev, struct device_attribute *attr,
|
||||
{
|
||||
struct stts751_priv *priv = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", priv->event_min);
|
||||
return sysfs_emit(buf, "%d\n", priv->event_min);
|
||||
}
|
||||
|
||||
static ssize_t min_store(struct device *dev, struct device_attribute *attr,
|
||||
@ -585,8 +585,8 @@ static ssize_t interval_show(struct device *dev,
|
||||
{
|
||||
struct stts751_priv *priv = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n",
|
||||
stts751_intervals[priv->interval]);
|
||||
return sysfs_emit(buf, "%d\n",
|
||||
stts751_intervals[priv->interval]);
|
||||
}
|
||||
|
||||
static ssize_t interval_store(struct device *dev,
|
||||
|
@ -27,7 +27,7 @@ static ssize_t vexpress_hwmon_label_show(struct device *dev,
|
||||
{
|
||||
const char *label = of_get_property(dev->of_node, "label", NULL);
|
||||
|
||||
return snprintf(buffer, PAGE_SIZE, "%s\n", label);
|
||||
return sysfs_emit(buffer, "%s\n", label);
|
||||
}
|
||||
|
||||
static ssize_t vexpress_hwmon_u32_show(struct device *dev,
|
||||
@ -41,8 +41,8 @@ static ssize_t vexpress_hwmon_u32_show(struct device *dev,
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return snprintf(buffer, PAGE_SIZE, "%u\n", value /
|
||||
to_sensor_dev_attr(dev_attr)->index);
|
||||
return sysfs_emit(buffer, "%u\n", value /
|
||||
to_sensor_dev_attr(dev_attr)->index);
|
||||
}
|
||||
|
||||
static ssize_t vexpress_hwmon_u64_show(struct device *dev,
|
||||
@ -60,9 +60,9 @@ static ssize_t vexpress_hwmon_u64_show(struct device *dev,
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return snprintf(buffer, PAGE_SIZE, "%llu\n",
|
||||
div_u64(((u64)value_hi << 32) | value_lo,
|
||||
to_sensor_dev_attr(dev_attr)->index));
|
||||
return sysfs_emit(buffer, "%llu\n",
|
||||
div_u64(((u64)value_hi << 32) | value_lo,
|
||||
to_sensor_dev_attr(dev_attr)->index));
|
||||
}
|
||||
|
||||
static umode_t vexpress_hwmon_attr_is_visible(struct kobject *kobj,
|
||||
|
@ -329,14 +329,14 @@ static ssize_t temp1_input_show(struct device *dev,
|
||||
|
||||
temp = sign_extend32(val, TEMP_NEGATIVE_BIT);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", CELSIUS_TO_mCELSIUS(temp));
|
||||
return sysfs_emit(buf, "%d\n", CELSIUS_TO_mCELSIUS(temp));
|
||||
}
|
||||
|
||||
static ssize_t temp1_label_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "SoC Temperature\n");
|
||||
return sysfs_emit(buf, "SoC Temperature\n");
|
||||
}
|
||||
|
||||
static ssize_t temp1_critical_alarm_show(struct device *dev,
|
||||
@ -345,21 +345,21 @@ static ssize_t temp1_critical_alarm_show(struct device *dev,
|
||||
{
|
||||
struct xgene_hwmon_dev *ctx = dev_get_drvdata(dev);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", ctx->temp_critical_alarm);
|
||||
return sysfs_emit(buf, "%d\n", ctx->temp_critical_alarm);
|
||||
}
|
||||
|
||||
static ssize_t power1_label_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "CPU power\n");
|
||||
return sysfs_emit(buf, "CPU power\n");
|
||||
}
|
||||
|
||||
static ssize_t power2_label_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "IO power\n");
|
||||
return sysfs_emit(buf, "IO power\n");
|
||||
}
|
||||
|
||||
static ssize_t power1_input_show(struct device *dev,
|
||||
@ -374,7 +374,7 @@ static ssize_t power1_input_show(struct device *dev,
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", mWATT_TO_uWATT(val));
|
||||
return sysfs_emit(buf, "%u\n", mWATT_TO_uWATT(val));
|
||||
}
|
||||
|
||||
static ssize_t power2_input_show(struct device *dev,
|
||||
@ -389,7 +389,7 @@ static ssize_t power2_input_show(struct device *dev,
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%u\n", mWATT_TO_uWATT(val));
|
||||
return sysfs_emit(buf, "%u\n", mWATT_TO_uWATT(val));
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RO(temp1_label);
|
||||
|
@ -15,6 +15,11 @@
|
||||
|
||||
enum m10bmc_type {
|
||||
M10_N3000,
|
||||
M10_D5005
|
||||
};
|
||||
|
||||
static struct mfd_cell m10bmc_d5005_subdevs[] = {
|
||||
{ .name = "d5005bmc-hwmon" },
|
||||
};
|
||||
|
||||
static struct mfd_cell m10bmc_pacn3000_subdevs[] = {
|
||||
@ -173,6 +178,10 @@ static int intel_m10_bmc_spi_probe(struct spi_device *spi)
|
||||
cells = m10bmc_pacn3000_subdevs;
|
||||
n_cell = ARRAY_SIZE(m10bmc_pacn3000_subdevs);
|
||||
break;
|
||||
case M10_D5005:
|
||||
cells = m10bmc_d5005_subdevs;
|
||||
n_cell = ARRAY_SIZE(m10bmc_d5005_subdevs);
|
||||
break;
|
||||
default:
|
||||
return -ENODEV;
|
||||
}
|
||||
@ -187,6 +196,7 @@ static int intel_m10_bmc_spi_probe(struct spi_device *spi)
|
||||
|
||||
static const struct spi_device_id m10bmc_spi_id[] = {
|
||||
{ "m10-n3000", M10_N3000 },
|
||||
{ "m10-d5005", M10_D5005 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, m10bmc_spi_id);
|
||||
|
Loading…
Reference in New Issue
Block a user