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:
Linus Torvalds 2021-04-26 14:59:21 -07:00
commit 47080f2286
93 changed files with 3148 additions and 1648 deletions

View File

@ -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

View File

@ -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,.*":

View File

@ -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.

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

View File

@ -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

View 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

View File

@ -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

View 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 dualloop digital multiphase 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
======================= ===========================

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

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

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

View File

@ -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.
-------------------------------------------------------------------------
======= ===========================================

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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");

View File

@ -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,

View File

@ -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);

View File

@ -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],
},
{ }
};

View File

@ -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)

View File

@ -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;

View File

@ -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;

View File

@ -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 */

View File

@ -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 */

View File

@ -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,

View File

@ -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,
},
{ }
};

View File

@ -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 */

View File

@ -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 */

View File

@ -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

View File

@ -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 */

View File

@ -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);

View File

@ -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);
}
/*

View File

@ -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));
}
/*

View File

@ -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 */

View File

@ -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 */

View File

@ -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);
}
/*

View File

@ -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)

View File

@ -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 */

View File

@ -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, &regval);
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, &regval);
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, &regval);
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;

View File

@ -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;

View 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");

View File

@ -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;

View File

@ -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);

View File

@ -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.

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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);

View 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);

View 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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View 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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View 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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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)
{

View File

@ -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);

View File

@ -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);

View 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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);
}
/**

View File

@ -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 = {

View File

@ -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[] = {

View File

@ -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) \

View File

@ -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,

View File

@ -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,

View File

@ -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);

View File

@ -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);