hwmon updates for v6.1

* New drivers
 
   - Driver for MAX31760 fan speed controller
 
   - Driver for TEXAS TPS546D24 Buck Converter
 
   - Driver for EMC2301/2/3/5 RPM-based PWM Fan Speed Controller
 
 * Removed drivers
 
   - Drop obsolete asus_wmi_ec_sensors driver
 
 * Cleanups, affecting various drivers
 
   - Use DEFINE_SIMPLE_DEV_PM_OPS where appropriate
 
   - Remove forward declarations
 
   - Move from strlcpy with unused retval to strscpy
 
   - Make use of devm_clk_get_enabled()
 
   - Drop devm_of_pwm_get()
 
 * Other notable cleanup and improvements
 
   - Support for additional USB devide ID and support for reporting
     of rail mode via debugfs added to corsair-psu driver
 
   - Support for aditional USB ID in nzxt-smart2 driver
 
   - Support for Aquacomputer High Flow Next in aquacomputer_d5next driver
 
   -  Major cleanup of pwm-fan driver
 
   -  Major cleanup of mr75203 driver, and added support for new device revision
 
 * Various other minor fixes and cleanups
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEiHPvMQj9QTOCiqgVyx8mb86fmYEFAmM6CSsACgkQyx8mb86f
 mYEwFw//SvJ5vW/bS0kynaH2TICzwHDHjH7nBjXyIlqN3ILrrTKyFQgkfqIlqYT9
 OYlXaKB6DqRBOZjLCYKNhDy13gdvOh0XBIsX/emqzFDmhGZ6yJtm0u+6CMp7UFGK
 3KDf4LSkEJ3QhgOOiROsz1u6CSXdZI8THnIBRCrzve3nlamRaR6n8tvvf6Fqspsa
 WQTDg1g2dOjpP6bi1EKre/KiBfjxrzy2/r9WGQr1J7LZb598/F7CPGztJU6P27TE
 gHltO4wcAgBgnL3ODBmLPDHyAa91fTmfHCvKRUfaAlZll8ucfXJnlfZVV8YW9ptC
 Fm00Wmy8aFX57ziXFlJ7LOFdjdCKyTRVp5ferPHCEuzmztWLdWauwiVV7GkeqzIB
 /seHcC59kX7yGKgP2sYV9SlD0GWa2Ax2rJT8gh78ozUgXMUlKkVPSly5ooOaKkEV
 nNRLJB40tAK43wwSWNVSw0wPXK0fWfxmyF1AMowNEoC12zDLO0JAlw/sVXj4GneG
 sq0ZN6vyAaDdcX3RKan6jTaus2AdHf3vZHqy/xW59Jo+JqoxF7zo1MLRDP2EekWn
 x0HbXT5ghWrmntN7mgRPvN1sgebK8L+yGttuJ/guFuYTTy8FrXOKtjhBCcvZHg7C
 tiMUAMTl7s6FujcuMEY84ZwwIDczLGpIwxXammIKJiHGBSeHsdk=
 =8u0w
 -----END PGP SIGNATURE-----

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

Pull hwmon updates from Guenter Roeck:
 "New drivers:

   - Driver for MAX31760 fan speed controller

   - Driver for TEXAS TPS546D24 Buck Converter

   - Driver for EMC2301/2/3/5 RPM-based PWM Fan Speed Controller

  Removed drivers:

   - Drop obsolete asus_wmi_ec_sensors driver

  Cleanups, affecting various drivers:

   - Use DEFINE_SIMPLE_DEV_PM_OPS where appropriate

   - Remove forward declarations

   - Move from strlcpy with unused retval to strscpy

   - Make use of devm_clk_get_enabled()

   - Drop devm_of_pwm_get()

  Other notable cleanup and improvements:

   - Support for additional USB devide ID and support for reporting of
     rail mode via debugfs added to corsair-psu driver

   - Support for aditional USB ID in nzxt-smart2 driver

   - Support for Aquacomputer High Flow Next in aquacomputer_d5next
     driver

   - Major cleanup of pwm-fan driver

   - Major cleanup of mr75203 driver, and added support for new device
     revision

  And various other minor fixes and cleanups"

* tag 'hwmon-for-v6.1' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (86 commits)
  hwmon: (corsair-psu) add USB id of new revision of the HX1000i psu
  hwmon: (pmbus/mp2888) Fix sensors readouts for MPS Multi-phase mp2888 controller
  dt-bindings: hwmon: sensirion,shtc1: Clean up spelling mistakes and grammar
  hwmon: (nct6683) remove unused variable in nct6683_create_attr_group
  hwmon: w83627hf: Reorder symbols to get rid of a few forward declarations
  hwmon: (ina3221) Use DEFINE_RUNTIME_DEV_PM_OPS() and pm_ptr()
  hwmon: (w83627ehf) Switch to DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr()
  hwmon: (tmp108) Switch to DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr()
  hwmon: (tmp103) Switch to DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr()
  hwmon: (tmp102) Switch to DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr()
  hwmon: (pwm-fan) Switch to DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr()
  hwmon: (nct6775) Switch to DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr()
  hwmon: (max6639) Switch to DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr()
  hwmon: (max31730) witch to DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr()
  hwmon: (max31722) Switch to DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr()
  hwmon: (ltc2947) Switch to EXPORT_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr()
  hwmon: (lm90) Switch to DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr()
  hwmon: (it87) Switch to DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr()
  hwmon: (gpio-fan) Switch to DEFINE_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr()
  hwmon: (adt7x10) Switch to EXPORT_SIMPLE_DEV_PM_OPS() and pm_sleep_ptr()
  ...
This commit is contained in:
Linus Torvalds 2022-10-04 19:02:54 -07:00
commit 0baf6dcc02
125 changed files with 6308 additions and 4945 deletions

View File

@ -0,0 +1,42 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/adi,max31760.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Analog Devices MAX31760 Fan-Speed Controller
maintainers:
- Ibrahim Tilki <Ibrahim.Tilki@analog.com>
description: |
Analog Devices MAX31760 Fan-Speed Controller
https://datasheets.maximintegrated.com/en/ds/MAX31760.pdf
properties:
compatible:
enum:
- adi,max31760
reg:
description: I2C address of slave device.
minimum: 0x50
maximum: 0x57
required:
- compatible
- reg
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
fan-controller@50 {
reg = <0x50>;
compatible = "adi,max31760";
};
};

View File

@ -22,7 +22,7 @@ properties:
clocks:
items:
- description: AHB reference clock
- description: System reference clock
'#thermal-sensor-cells':
const: 0
@ -40,5 +40,5 @@ examples:
compatible = "microchip,sparx5-temp";
reg = <0x10508110 0xc>;
#thermal-sensor-cells = <0>;
clocks = <&ahb_clk>;
clocks = <&sys_clk>;
};

View File

@ -9,6 +9,32 @@ title: Moortec Semiconductor MR75203 PVT Controller bindings
maintainers:
- Rahul Tanwar <rtanwar@maxlinear.com>
description: |
A Moortec PVT (Process, Voltage, Temperature) monitoring logic design can
include many different units.
Such a design will usually consists of several Moortec's embedded analog IPs,
and a single Moortec controller (mr75203) to configure and control the IPs.
Some of the Moortec's analog hard IPs that can be used in a design:
*) Temperature Sensor (TS) - used to monitor core temperature (e.g. mr74137).
*) Voltage Monitor (VM) - used to monitor voltage levels (e.g. mr74138).
*) Process Detector (PD) - used to assess silicon speed (e.g. mr74139).
*) Delay Chain - ring oscillator connected to the PD, used to measure IO
based transistors (e.g. mr76008 ring oscillator at 1.1V, mr76007 ring
oscillator at 1.8V).
*) Pre Scaler - provides divide-by-X scaling of input voltage, which can then
be presented for VM for measurement within its range (e.g. mr76006 -
divide by 2 pre-scaler).
TS, VM & PD also include a digital interface, which consists of configuration
inputs and measurement outputs.
Some of the units have number of series, each series can have slightly
different characteristics.
The mr75203 binding describes configuration for the controller unit, but also
for some of the analog IPs.
properties:
compatible:
const: moortec,mr75203
@ -44,12 +70,76 @@ properties:
"#thermal-sensor-cells":
const: 1
moortec,vm-active-channels:
description:
Defines the number of channels per VM that are actually used and are
connected to some input source.
Maximum number of items - number of VMs.
Maximum value of each item - number of channels.
Minimum value of each item - 0 (which means entire VM sensor is not used).
$ref: /schemas/types.yaml#/definitions/uint8-array
moortec,vm-pre-scaler-x2:
description:
Defines the channels that use a mr76006 pre-scaler to divide the input
source by 2.
The pre-scaler is used for input sources that exceed the VM input range.
The driver uses this information to present to the user with the actual
value of the voltage source.
For channels that are not listed, no pre-scaler is assumed.
Maximum number of items - total number of channels in all VMs.
Each channel should not appear more than once.
$ref: /schemas/types.yaml#/definitions/uint8-array
moortec,ts-series:
description:
Definition of the temperature equation and coefficients that shall be
used to convert the digital output to value in milli-Celsius.
minimum: 5
maximum: 6
default: 5
$ref: /schemas/types.yaml#/definitions/uint32
moortec,ts-coeff-g:
description:
G coefficient for temperature equation.
Default for series 5 = 60000
Default for series 6 = 57400
multipleOf: 1000
minimum: 1000
$ref: /schemas/types.yaml#/definitions/uint32
moortec,ts-coeff-h:
description:
H coefficient for temperature equation.
Default for series 5 = 200000
Default for series 6 = 249400
multipleOf: 1000
minimum: 1000
$ref: /schemas/types.yaml#/definitions/uint32
moortec,ts-coeff-cal5:
description:
cal5 coefficient for temperature equation.
Default for series 5 = 4094
Default for series 6 = 4096
minimum: 1
$ref: /schemas/types.yaml#/definitions/uint32
moortec,ts-coeff-j:
description:
J coefficient for temperature equation.
Default for series 5 = -100
Default for series 6 = 0
multipleOf: 1000
maximum: 0
$ref: /schemas/types.yaml#/definitions/int32
required:
- compatible
- reg
- reg-names
- clocks
- resets
- "#thermal-sensor-cells"
additionalProperties: false
@ -66,5 +156,9 @@ examples:
intel,vm-map = [03 01 04 ff ff];
clocks = <&osc0>;
resets = <&rcu0 0x40 7>;
moortec,vm-active-channels = /bits/ 8 <0x10 0x05>;
moortec,vm-pre-scaler-x2 = /bits/ 8 <5 6 20>;
moortec,ts-coeff-g = <61400>;
moortec,ts-coeff-h = <253700>;
#thermal-sensor-cells = <1>;
};

View File

@ -10,7 +10,7 @@ maintainers:
- Christopher Ruehl chris.ruehl@gtsys.com.hk
description: |
The SHTC1, SHTW1 and SHTC3 are digital humidity and temperature sensor
The SHTC1, SHTW1 and SHTC3 are digital humidity and temperature sensors
designed especially for battery-driven high-volume consumer electronics
applications.
For further information refere to Documentation/hwmon/shtc1.rst
@ -31,13 +31,13 @@ properties:
sensirion,blocking-io:
$ref: /schemas/types.yaml#/definitions/flag
description:
If set, the driver hold the i2c bus until measurement is finished.
If set, the driver holds the i2c bus until the measurement is finished.
sensirion,low-precision:
$ref: /schemas/types.yaml#/definitions/flag
description:
If set, the sensor aquire data with low precision (not recommended).
The driver aquire data with high precision by default.
If set, the sensor acquires data with low precision (not recommended).
The driver acquires data with high precision by default.
required:
- compatible

View File

@ -819,6 +819,8 @@ patternProperties:
description: MNT Research GmbH
"^modtronix,.*":
description: Modtronix Engineering
"^moortec,.*":
description: Moortec Semiconductor Ltd.
"^mosaixtech,.*":
description: Mosaix Technologies, Inc.
"^motorola,.*":

View File

@ -404,7 +404,6 @@ POWER
PWM
devm_pwm_get()
devm_of_pwm_get()
devm_fwnode_pwm_get()
REGULATOR

View File

@ -40,8 +40,7 @@ after usage with pwm_free().
New users should use the pwm_get() function and pass to it the consumer
device or a consumer name. pwm_put() is used to free the PWM device. Managed
variants of the getter, devm_pwm_get(), devm_of_pwm_get(),
devm_fwnode_pwm_get(), also exist.
variants of the getter, devm_pwm_get() and devm_fwnode_pwm_get(), also exist.
After being requested, a PWM has to be configured using::

View File

@ -10,6 +10,7 @@ Supported devices:
* Aquacomputer Farbwerk 360 RGB controller
* Aquacomputer Octo fan controller
* Aquacomputer Quadro fan controller
* Aquacomputer High Flow Next sensor
Author: Aleksa Savic
@ -20,10 +21,11 @@ This driver exposes hardware sensors of listed Aquacomputer devices, which
communicate through proprietary USB HID protocols.
For the D5 Next pump, available sensors are pump and fan speed, power, voltage
and current, as well as coolant temperature. Also available through debugfs are
the serial number, firmware version and power-on count. Attaching a fan to it is
optional and allows it to be controlled using temperature curves directly from the
pump. If it's not connected, the fan-related sensors will report zeroes.
and current, as well as coolant temperature and eight virtual temp sensors. Also
available through debugfs are the serial number, firmware version and power-on
count. Attaching a fan to it is optional and allows it to be controlled using
temperature curves directly from the pump. If it's not connected, the fan-related
sensors will report zeroes.
The pump can be configured either through software or via its physical
interface. Configuring the pump through this driver is not implemented, as it
@ -31,14 +33,23 @@ seems to require sending it a complete configuration. That includes addressable
RGB LEDs, for which there is no standard sysfs interface. Thus, that task is
better suited for userspace tools.
The Octo exposes four temperature sensors and eight PWM controllable fans, along
with their speed (in RPM), power, voltage and current.
The Octo exposes four physical and sixteen virtual temperature sensors, as well as
eight PWM controllable fans, along with their speed (in RPM), power, voltage and
current.
The Quadro exposes four temperature sensors, a flow sensor and four PWM controllable
fans, along with their speed (in RPM), power, voltage and current.
The Quadro exposes four physical and sixteen virtual temperature sensors, a flow
sensor and four PWM controllable fans, along with their speed (in RPM), power,
voltage and current.
The Farbwerk and Farbwerk 360 expose four temperature sensors. Depending on the device,
not all sysfs and debugfs entries will be available.
The Farbwerk and Farbwerk 360 expose four temperature sensors. Additionally,
sixteen virtual temperature sensors of the Farbwerk 360 are exposed.
The High Flow Next exposes +5V voltages, water quality, conductivity and flow readings.
A temperature sensor can be connected to it, in which case it provides its reading
and an estimation of the dissipated/absorbed power in the liquid cooling loop.
Depending on the device, not all sysfs and debugfs entries will be available.
Writing to virtual temperature sensors is not currently supported.
Usage notes
-----------
@ -49,14 +60,14 @@ the kernel and supports hotswapping.
Sysfs entries
-------------
================ ==============================================
temp[1-4]_input Temperature sensors (in millidegrees Celsius)
================ ==============================================================
temp[1-20]_input Physical/virtual temperature sensors (in millidegrees Celsius)
fan[1-8]_input Pump/fan speed (in RPM) / Flow speed (in dL/h)
power[1-8]_input Pump/fan power (in micro Watts)
in[0-7]_input Pump/fan voltage (in milli Volts)
curr[1-8]_input Pump/fan current (in milli Amperes)
pwm[1-8] Fan PWM (0 - 255)
================ ==============================================
================ ==============================================================
Debugfs entries
---------------

View File

@ -1,38 +0,0 @@
.. SPDX-License-Identifier: GPL-2.0-or-later
Kernel driver asus_wmi_ec_sensors
=================================
Supported boards:
* PRIME X570-PRO,
* Pro WS X570-ACE,
* ROG CROSSHAIR VIII DARK HERO,
* ROG CROSSHAIR VIII FORMULA,
* ROG CROSSHAIR VIII HERO,
* ROG STRIX B550-E GAMING,
* ROG STRIX B550-I GAMING,
* ROG STRIX X570-E GAMING.
Authors:
- Eugene Shalygin <eugene.shalygin@gmail.com>
Description:
------------
ASUS mainboards publish hardware monitoring information via Super I/O
chip and the ACPI embedded controller (EC) registers. Some of the sensors
are only available via the EC.
ASUS WMI interface provides a method (BREC) to read data from EC registers,
which is utilized by this driver to publish those sensor readings to the
HWMON system. The driver is aware of and reads the following sensors:
1. Chipset (PCH) temperature
2. CPU package temperature
3. Motherboard temperature
4. Readings from the T_Sensor header
5. VRM temperature
6. CPU_Opt fan RPM
7. Chipset fan RPM
8. Readings from the "Water flow meter" header (RPM)
9. Readings from the "Water In" and "Water Out" temperature headers
10. CPU current

View File

@ -15,7 +15,7 @@ Supported devices:
Corsair HX850i
Corsair HX1000i
Corsair HX1000i (revision 1 and 2)
Corsair HX1200i
@ -86,8 +86,9 @@ Debugfs entries
---------------
======================= ========================================================
uptime Current uptime of the psu
ocpmode Single or multi rail mode of the PCIe power connectors
product Product name of the psu
uptime Session uptime of the psu
uptime_total Total uptime of the psu
vendor Vendor name of the psu
product Product name of the psu
======================= ========================================================

View File

@ -0,0 +1,37 @@
.. SPDX-License-Identifier: GPL-2.0
Kernel driver emc2305
=====================
Supported chips:
Microchip EMC2305, EMC2303, EMC2302, EMC2301
Addresses scanned: I2C 0x27, 0x2c, 0x2d, 0x2e, 0x2f, 0x4c, 0x4d
Prefixes: 'emc2305'
Datasheet: Publicly available at the Microchip website :
https://www.microchip.com/en-us/product/EMC2305
Description:
------------
This driver implements support for Microchip EMC2301/2/3/5 RPM-based PWM Fan Controller.
The EMC2305 Fan Controller supports up to 5 independently controlled PWM fan drives.
Fan rotation speeds are reported in RPM.
The driver supports the RPM-based PWM control to keep a fan at the desired speed.
The driver provides the possibility to have one common PWM interface for all FANs
or up to the maximum available or configured independent PWMs.
The driver provides the following sysfs interfaces in hwmon subsystem:
================= == ===================================================
fan[1-5]_fault RO files for tachometers TACH1-TACH5 fault indication
fan[1-5]_input RO files for tachometers TACH1-TACH5 input (in RPM)
pwm[1-5] RW file for fan[1-5] target duty cycle (0..255)
================= == ===================================================
sysfs interfaces in thermal subsystem:
================= == ========================================================================
cur_state RW file for the current cooling state of the cooling device (0..max_state)
max_state RO file for the maximum cooling state of the cooling device
================= == ========================================================================

View File

@ -44,7 +44,6 @@ Hardware Monitoring Kernel Drivers
asc7621
aspeed-pwm-tacho
asus_ec_sensors
asus_wmi_ec_sensors
asus_wmi_sensors
bcm54140
bel-pfe
@ -63,6 +62,7 @@ Hardware Monitoring Kernel Drivers
ds620
emc1403
emc2103
emc2305
emc6w201
f71805f
f71882fg
@ -133,6 +133,7 @@ Hardware Monitoring Kernel Drivers
max20751
max31722
max31730
max31760
max31785
max31790
max34440
@ -205,6 +206,7 @@ Hardware Monitoring Kernel Drivers
tps23861
tps40422
tps53679
tps546d24
twl4030-madc-hwmon
ucd9000
ucd9200

View File

@ -0,0 +1,77 @@
.. SPDX-License-Identifier: GPL-2.0-or-later
Kernel driver max31760
======================
Supported chips:
* Analog Devices MAX31760
Prefix: 'max31760'
Addresses scanned: none
Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX31760.pdf
Author: Ibrahim Tilki <Ibrahim.Tilki@analog.com>
Description
-----------
The MAX31760 integrates temperature sensing along with precision PWM fan
control. It accurately measures its local die temperature and the remote
temperature of a discrete diode-connected transistor, such as a 2N3906,
or a thermal diode commonly found on CPUs, graphics processor units (GPUs),
and other ASICs. Multiple temperature thresholds, such as local
high/overtemperature (OT) and remote high/overtemperature, can be set by an
I2C-compatible interface. Fan speed is controlled based on the temperature
reading as an index to a 48-byte lookup table (LUT) containing
user-programmed PWM values. The flexible LUT-based architecture enables
the user to program a smooth nonlinear fan speed vs. temperature transfer
function to minimize acoustic fan noise. Two tachometer inputs allow
measuring the speeds of two fans independently. When the local or remote
OT threshold is exceeded, the SHDN pin is asserted low and can be used to
shut down the system. A dedicated ALERT pin reports that either a local or
remote high-temperature threshold has been exceeded.
Temperature measurement range: from -55°C to 125°C
Temperature Resolution: 11 Bits, ±0.125°C
Please refer how to instantiate this driver: Documentation/i2c/instantiating-devices.rst
Lookup table for auto fan control
---------------------------------
========= =================================
LUT Index Name
========= =================================
1 PWM value for T < +18°C
2 PWM value for +18°C ≤ T < +20°C
3 PWM value for +20°C ≤ T < +22°C
... ...
47 PWM value for +108°C ≤ T < +110°C
48 PWM value for T ≥ +110°C
========= =================================
Sysfs entries
-------------
=============================== =================================================================================
fan[1-2]_input Fan speed (in RPM)
fan[1-2]_enable Enable fan readings and fan fault alarms
fan[1-2]_fault Fan fault status
temp[1-2]_label "Remote" and "Local" temperature channel labels
temp[1-2]_input Temperature sensor readings (in millidegrees Celsius)
temp1_fault Remote temperature sensor fault status
temp[1-2]_max Temperature max value. Asserts "ALERT" pin when exceeded
temp[1-2]_max_alarm Temperature max alarm status
temp[1-2]_crit Temperature critical value. Asserts "SHDN" pin when exceeded
temp[1-2]_crit_alarm Temperature critical alarm status
pwm1 PWM value for direct fan control
pwm1_enable 1: direct fan control, 2: temperature based auto fan control
pwm1_freq PWM frequency in hertz
pwm1_auto_channels_temp Temperature source for auto fan control. 1: temp1, 2: temp2, 3: max(temp1, temp2)
pwm1_auto_point[1-48]_pwm PWM value for LUT point
pwm1_auto_point_temp_hyst Temperature hysteresis for auto fan control. Can be either 2000mC or 4000mC
=============================== =================================================================================

View File

@ -38,6 +38,7 @@ Sysfs entries
fan[1-12]_input RO fan tachometer speed in RPM
fan[1-12]_fault RO fan experienced fault
fan[1-6]_target RW desired fan speed in RPM
fan[1-6]_enable RW enable or disable the tachometer input
pwm[1-6]_enable RW regulator mode, 0=disabled (duty cycle=0%), 1=manual mode, 2=rpm mode
pwm[1-6] RW read: current pwm duty cycle,
write: target pwm duty cycle (0-255)

View File

@ -18,3 +18,15 @@ the hwmon's sysfs interface.
The fan rotation speed returned via the optional 'fan1_input' is extrapolated
from the sampled interrupts from the tachometer signal within 1 second.
The driver provides the following sensor accesses in sysfs:
=============== ======= =======================================================
fan1_input ro fan tachometer speed in RPM
pwm1_enable rw keep enable mode, defines behaviour when pwm1=0
0 -> disable pwm and regulator
1 -> enable pwm; if pwm==0, disable pwm, keep regulator enabled
2 -> enable pwm; if pwm==0, keep pwm and regulator enabled
3 -> enable pwm; if pwm==0, disable pwm and regulator
pwm1 rw relative speed (0-255), 255=max. speed.
=============== ======= =======================================================

View File

@ -0,0 +1,35 @@
.. SPDX-License-Identifier: GPL-2.0-only
Kernel driver tps546d24
=======================
Supported chips:
* TI TPS546D24
Prefix: 'tps546d24'
Addresses scanned: -
Datasheet: https://www.ti.com/lit/gpn/tps546d24
Author: Duke Du <dukedu83@gmail.com>
Description
-----------
The TPS546D24A is a highly integrated, non-isolated DC/DC converter capable
of high frequency operation and 40-A current output from a 7-mm x 5-mm
package.
Two, three, and four TPS546D24A devices can be interconnected
to provide up to 160 A on a single output. The device has an option to
overdrive the internal 5-V LDO with an external 5-V supply via the VDD5
pin to improve efficiency and reduce power dissipation of the converter.
Platform data support
---------------------
The driver supports standard PMBus driver platform data.

View File

@ -1339,6 +1339,15 @@ F: drivers/iio/amplifiers/hmc425a.c
F: drivers/staging/iio/*/ad*
X: drivers/iio/*/adjd*
ANALOG DEVICES INC MAX31760 DRIVER
M: Ibrahim Tilki <Ibrahim.Tilki@analog.com>
S: Maintained
W: http://wiki.analog.com/
W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/hwmon/adi,max31760.yaml
F: Documentation/hwmon/max31760.rst
F: drivers/hwmon/max31760.c
ANALOGBITS PLL LIBRARIES
M: Paul Walmsley <paul.walmsley@sifive.com>
S: Supported
@ -3238,13 +3247,6 @@ L: linux-hwmon@vger.kernel.org
S: Maintained
F: drivers/hwmon/asus_wmi_sensors.c
ASUS WMI EC HARDWARE MONITOR DRIVER
M: Eugene Shalygin <eugene.shalygin@gmail.com>
M: Denis Pauk <pauk.denis@gmail.com>
L: linux-hwmon@vger.kernel.org
S: Maintained
F: drivers/hwmon/asus_wmi_ec_sensors.c
ASUS EC HARDWARE MONITOR DRIVER
M: Eugene Shalygin <eugene.shalygin@gmail.com>
L: linux-hwmon@vger.kernel.org
@ -6181,7 +6183,7 @@ F: Documentation/devicetree/bindings/memory-controllers/samsung,exynos5422-dmc.y
F: drivers/memory/samsung/exynos5422-dmc.c
DME1737 HARDWARE MONITOR DRIVER
M: Juerg Haefliger <juergh@gmail.com>
M: Juerg Haefliger <juergh@proton.me>
L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/dme1737.rst
@ -20649,6 +20651,13 @@ Q: https://patchwork.kernel.org/project/linux-integrity/list/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/jarkko/linux-tpmdd.git
F: drivers/char/tpm/
TPS546D24 DRIVER
M: Duke Du <dukedu83@gmail.com>
L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/tps546d24.rst
F: drivers/hwmon/pmbus/tps546d24.c
TRACING
M: Steven Rostedt <rostedt@goodmis.org>
M: Ingo Molnar <mingo@redhat.com>
@ -21852,7 +21861,7 @@ F: lib/test_scanf.c
F: lib/vsprintf.c
VT1211 HARDWARE MONITOR DRIVER
M: Juerg Haefliger <juergh@gmail.com>
M: Juerg Haefliger <juergh@proton.me>
L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/vt1211.rst

View File

@ -257,14 +257,14 @@ config SENSORS_AHT10
will be called aht10.
config SENSORS_AQUACOMPUTER_D5NEXT
tristate "Aquacomputer D5 Next, Octo, Quadro, Farbwerk, and Farbwerk 360"
tristate "Aquacomputer D5 Next, Octo, Quadro, Farbwerk, Farbwerk 360, High Flow Next"
depends on USB_HID
select CRC16
help
If you say yes here you get support for sensors and fans of
the Aquacomputer D5 Next watercooling pump, Octo and Quadro fan
controllers, Farbwerk and Farbwerk 360 RGB controllers, where
available.
controllers, Farbwerk and Farbwerk 360 RGB controllers, High Flow
Next sensor, where available.
This driver can also be built as a module. If so, the module
will be called aquacomputer_d5next.
@ -393,6 +393,7 @@ config SENSORS_ASB100
config SENSORS_ASPEED
tristate "ASPEED AST2400/AST2500 PWM and Fan tach driver"
depends on ARCH_ASPEED || COMPILE_TEST
depends on THERMAL || THERMAL=n
select REGMAP
help
@ -1066,6 +1067,18 @@ config SENSORS_MAX31730
This driver can also be built as a module. If so, the module
will be called max31730.
config SENSORS_MAX31760
tristate "MAX31760 fan speed controller"
depends on I2C
select REGMAP_I2C
help
Support for the Analog Devices MAX31760 Precision Fan-Speed
Controller. MAX31760 integrates temperature sensing along with
precision PWM fan control.
This driver can also be built as a module. If so, the module
will be called max31760.
config SENSORS_MAX6620
tristate "Maxim MAX6620 fan controller"
depends on I2C
@ -1785,6 +1798,19 @@ config SENSORS_EMC2103
This driver can also be built as a module. If so, the module
will be called emc2103.
config SENSORS_EMC2305
tristate "Microchip EMC2305 and compatible EMC2301/2/3"
depends on I2C
imply THERMAL
help
If you say yes here you get support for the Microchip EMC2305
fan controller chips.
The Microchip EMC2305 is a fan controller for up to 5 fans.
Fan rotation speeds are reported in RPM.
This driver can also be built as a module. If so, the module
will be called emc2305.
config SENSORS_EMC6W201
tristate "SMSC EMC6W201"
depends on I2C
@ -2341,21 +2367,6 @@ config SENSORS_ASUS_WMI
This driver can also be built as a module. If so, the module
will be called asus_wmi_sensors.
config SENSORS_ASUS_WMI_EC
tristate "ASUS WMI B550/X570"
depends on ACPI_WMI && SENSORS_ASUS_EC=n
help
If you say yes here you get support for the ACPI embedded controller
hardware monitoring interface found in B550/X570 ASUS motherboards.
This driver will provide readings of fans, voltages and temperatures
through the system firmware.
This driver is deprecated in favor of the ASUS EC Sensors driver
which provides fully compatible output.
This driver can also be built as a module. If so, the module
will be called asus_wmi_sensors_ec.
config SENSORS_ASUS_EC
tristate "ASUS EC Sensors"
depends on X86

View File

@ -11,7 +11,6 @@ obj-$(CONFIG_SENSORS_ACPI_POWER) += acpi_power_meter.o
obj-$(CONFIG_SENSORS_ATK0110) += asus_atk0110.o
obj-$(CONFIG_SENSORS_ASUS_EC) += asus-ec-sensors.o
obj-$(CONFIG_SENSORS_ASUS_WMI) += asus_wmi_sensors.o
obj-$(CONFIG_SENSORS_ASUS_WMI_EC) += asus_wmi_ec_sensors.o
# Native drivers
# asb100, then w83781d go first, as they can override other drivers' addresses.
@ -70,6 +69,7 @@ obj-$(CONFIG_SENSORS_DS620) += ds620.o
obj-$(CONFIG_SENSORS_DS1621) += ds1621.o
obj-$(CONFIG_SENSORS_EMC1403) += emc1403.o
obj-$(CONFIG_SENSORS_EMC2103) += emc2103.o
obj-$(CONFIG_SENSORS_EMC2305) += emc2305.o
obj-$(CONFIG_SENSORS_EMC6W201) += emc6w201.o
obj-$(CONFIG_SENSORS_F71805F) += f71805f.o
obj-$(CONFIG_SENSORS_F71882FG) += f71882fg.o
@ -140,6 +140,7 @@ obj-$(CONFIG_SENSORS_MAX1668) += max1668.o
obj-$(CONFIG_SENSORS_MAX197) += max197.o
obj-$(CONFIG_SENSORS_MAX31722) += max31722.o
obj-$(CONFIG_SENSORS_MAX31730) += max31730.o
obj-$(CONFIG_SENSORS_MAX31760) += max31760.o
obj-$(CONFIG_SENSORS_MAX6620) += max6620.o
obj-$(CONFIG_SENSORS_MAX6621) += max6621.o
obj-$(CONFIG_SENSORS_MAX6639) += max6639.o

View File

@ -1504,7 +1504,6 @@ LEAVE_UPDATE:
return NULL;
}
#ifdef CONFIG_PM_SLEEP
static int abituguru_suspend(struct device *dev)
{
struct abituguru_data *data = dev_get_drvdata(dev);
@ -1526,16 +1525,12 @@ static int abituguru_resume(struct device *dev)
return 0;
}
static SIMPLE_DEV_PM_OPS(abituguru_pm, abituguru_suspend, abituguru_resume);
#define ABIT_UGURU_PM (&abituguru_pm)
#else
#define ABIT_UGURU_PM NULL
#endif /* CONFIG_PM */
static DEFINE_SIMPLE_DEV_PM_OPS(abituguru_pm, abituguru_suspend, abituguru_resume);
static struct platform_driver abituguru_driver = {
.driver = {
.name = ABIT_UGURU_NAME,
.pm = ABIT_UGURU_PM,
.pm = pm_sleep_ptr(&abituguru_pm),
},
.probe = abituguru_probe,
.remove = abituguru_remove,

View File

@ -1127,7 +1127,6 @@ LEAVE_UPDATE:
return NULL;
}
#ifdef CONFIG_PM_SLEEP
static int abituguru3_suspend(struct device *dev)
{
struct abituguru3_data *data = dev_get_drvdata(dev);
@ -1146,16 +1145,12 @@ static int abituguru3_resume(struct device *dev)
return 0;
}
static SIMPLE_DEV_PM_OPS(abituguru3_pm, abituguru3_suspend, abituguru3_resume);
#define ABIT_UGURU3_PM (&abituguru3_pm)
#else
#define ABIT_UGURU3_PM NULL
#endif /* CONFIG_PM */
static DEFINE_SIMPLE_DEV_PM_OPS(abituguru3_pm, abituguru3_suspend, abituguru3_resume);
static struct platform_driver abituguru3_driver = {
.driver = {
.name = ABIT_UGURU3_NAME,
.pm = ABIT_UGURU3_PM
.pm = pm_sleep_ptr(&abituguru3_pm),
},
.probe = abituguru3_probe,
.remove = abituguru3_remove,

View File

@ -927,8 +927,6 @@ static int acpi_power_meter_remove(struct acpi_device *device)
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int acpi_power_meter_resume(struct device *dev)
{
struct acpi_power_meter_resource *resource;
@ -946,9 +944,8 @@ static int acpi_power_meter_resume(struct device *dev)
return 0;
}
#endif /* CONFIG_PM_SLEEP */
static SIMPLE_DEV_PM_OPS(acpi_power_meter_pm, NULL, acpi_power_meter_resume);
static DEFINE_SIMPLE_DEV_PM_OPS(acpi_power_meter_pm, NULL,
acpi_power_meter_resume);
static struct acpi_driver acpi_power_meter_driver = {
.name = "power_meter",
@ -959,7 +956,7 @@ static struct acpi_driver acpi_power_meter_driver = {
.remove = acpi_power_meter_remove,
.notify = acpi_power_meter_notify,
},
.drv.pm = &acpi_power_meter_pm,
.drv.pm = pm_sleep_ptr(&acpi_power_meter_pm),
};
/* Module init/exit routines */

View File

@ -384,7 +384,7 @@ static int adc128_detect(struct i2c_client *client, struct i2c_board_info *info)
if (i2c_smbus_read_byte_data(client, ADC128_REG_BUSY_STATUS) & 0xfc)
return -ENODEV;
strlcpy(info->type, "adc128d818", I2C_NAME_SIZE);
strscpy(info->type, "adc128d818", I2C_NAME_SIZE);
return 0;
}

View File

@ -426,7 +426,7 @@ static int adm1021_detect(struct i2c_client *client,
pr_debug("Detected chip %s at adapter %d, address 0x%02x.\n",
type_name, i2c_adapter_id(adapter), client->addr);
strlcpy(info->type, type_name, I2C_NAME_SIZE);
strscpy(info->type, type_name, I2C_NAME_SIZE);
return 0;
}

View File

@ -470,7 +470,7 @@ static int adm1025_detect(struct i2c_client *client,
else
return -ENODEV;
strlcpy(info->type, name, I2C_NAME_SIZE);
strscpy(info->type, name, I2C_NAME_SIZE);
return 0;
}

View File

@ -1610,7 +1610,7 @@ static int adm1026_detect(struct i2c_client *client,
return -ENODEV;
}
strlcpy(info->type, "adm1026", I2C_NAME_SIZE);
strscpy(info->type, "adm1026", I2C_NAME_SIZE);
return 0;
}

View File

@ -329,7 +329,7 @@ static int adm1029_detect(struct i2c_client *client,
return -ENODEV;
}
strlcpy(info->type, "adm1029", I2C_NAME_SIZE);
strscpy(info->type, "adm1029", I2C_NAME_SIZE);
return 0;
}

View File

@ -985,7 +985,7 @@ static int adm1031_detect(struct i2c_client *client,
return -ENODEV;
name = (id == 0x30) ? "adm1030" : "adm1031";
strlcpy(info->type, name, I2C_NAME_SIZE);
strscpy(info->type, name, I2C_NAME_SIZE);
return 0;
}

View File

@ -501,17 +501,23 @@ static int adm9240_fan_read(struct device *dev, u32 attr, int channel, long *val
switch (attr) {
case hwmon_fan_input:
mutex_lock(&data->update_lock);
err = regmap_read(data->regmap, ADM9240_REG_FAN(channel), &regval);
if (err < 0)
if (err < 0) {
mutex_unlock(&data->update_lock);
return err;
}
if (regval == 255 && data->fan_div[channel] < 3) {
/* adjust fan clock divider on overflow */
err = adm9240_write_fan_div(data, channel,
++data->fan_div[channel]);
if (err)
if (err) {
mutex_unlock(&data->update_lock);
return err;
}
}
*val = FAN_FROM_REG(regval, BIT(data->fan_div[channel]));
mutex_unlock(&data->update_lock);
break;
case hwmon_fan_div:
*val = BIT(data->fan_div[channel]);

View File

@ -152,7 +152,7 @@ MODULE_DEVICE_TABLE(spi, adt7310_id);
static struct spi_driver adt7310_driver = {
.driver = {
.name = "adt7310",
.pm = ADT7X10_DEV_PM_OPS,
.pm = pm_sleep_ptr(&adt7x10_dev_pm_ops),
},
.probe = adt7310_spi_probe,
.id_table = adt7310_id,

View File

@ -98,7 +98,7 @@ static struct i2c_driver adt7410_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "adt7410",
.pm = ADT7X10_DEV_PM_OPS,
.pm = pm_sleep_ptr(&adt7x10_dev_pm_ops),
},
.probe_new = adt7410_i2c_probe,
.id_table = adt7410_ids,

View File

@ -590,7 +590,7 @@ static int adt7411_detect(struct i2c_client *client,
return -ENODEV;
}
strlcpy(info->type, "adt7411", I2C_NAME_SIZE);
strscpy(info->type, "adt7411", I2C_NAME_SIZE);
return 0;
}

View File

@ -1782,7 +1782,7 @@ static int adt7462_detect(struct i2c_client *client,
if (revision != ADT7462_REVISION)
return -ENODEV;
strlcpy(info->type, "adt7462", I2C_NAME_SIZE);
strscpy(info->type, "adt7462", I2C_NAME_SIZE);
return 0;
}

View File

@ -1342,7 +1342,7 @@ static int adt7475_detect(struct i2c_client *client,
return -ENODEV;
}
strlcpy(info->type, name, I2C_NAME_SIZE);
strscpy(info->type, name, I2C_NAME_SIZE);
return 0;
}

View File

@ -397,8 +397,6 @@ int adt7x10_probe(struct device *dev, const char *name, int irq,
}
EXPORT_SYMBOL_GPL(adt7x10_probe);
#ifdef CONFIG_PM_SLEEP
static int adt7x10_suspend(struct device *dev)
{
struct adt7x10_data *data = dev_get_drvdata(dev);
@ -414,10 +412,7 @@ static int adt7x10_resume(struct device *dev)
return regmap_write(data->regmap, ADT7X10_CONFIG, data->config);
}
SIMPLE_DEV_PM_OPS(adt7x10_dev_pm_ops, adt7x10_suspend, adt7x10_resume);
EXPORT_SYMBOL_GPL(adt7x10_dev_pm_ops);
#endif /* CONFIG_PM_SLEEP */
EXPORT_SIMPLE_DEV_PM_OPS(adt7x10_dev_pm_ops, adt7x10_suspend, adt7x10_resume);
MODULE_AUTHOR("Hartmut Knaack");
MODULE_DESCRIPTION("ADT7410/ADT7420, ADT7310/ADT7320 common code");

View File

@ -20,11 +20,6 @@ struct device;
int adt7x10_probe(struct device *dev, const char *name, int irq,
struct regmap *regmap);
#ifdef CONFIG_PM_SLEEP
extern const struct dev_pm_ops adt7x10_dev_pm_ops;
#define ADT7X10_DEV_PM_OPS (&adt7x10_dev_pm_ops)
#else
#define ADT7X10_DEV_PM_OPS NULL
#endif
#endif

View File

@ -809,7 +809,7 @@ static int amc6821_detect(
}
dev_info(&adapter->dev, "amc6821: chip found at 0x%02x.\n", address);
strlcpy(info->type, "amc6821", I2C_NAME_SIZE);
strscpy(info->type, "amc6821", I2C_NAME_SIZE);
return 0;
}

View File

@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* hwmon driver for Aquacomputer devices (D5 Next, Farbwerk, Farbwerk 360, Octo,
* Quadro)
* Quadro, High Flow Next)
*
* Aquacomputer devices send HID reports (with ID 0x01) every second to report
* sensor values.
@ -26,15 +26,17 @@
#define USB_PRODUCT_ID_D5NEXT 0xf00e
#define USB_PRODUCT_ID_FARBWERK360 0xf010
#define USB_PRODUCT_ID_OCTO 0xf011
#define USB_PRODUCT_ID_HIGHFLOWNEXT 0xf012
enum kinds { d5next, farbwerk, farbwerk360, octo, quadro };
enum kinds { d5next, farbwerk, farbwerk360, octo, quadro, highflownext };
static const char *const aqc_device_names[] = {
[d5next] = "d5next",
[farbwerk] = "farbwerk",
[farbwerk360] = "farbwerk360",
[octo] = "octo",
[quadro] = "quadro"
[quadro] = "quadro",
[highflownext] = "highflownext"
};
#define DRIVER_NAME "aquacomputer_d5next"
@ -71,6 +73,8 @@ static u8 secondary_ctrl_report[] = {
#define D5NEXT_COOLANT_TEMP 0x57
#define D5NEXT_NUM_FANS 2
#define D5NEXT_NUM_SENSORS 1
#define D5NEXT_NUM_VIRTUAL_SENSORS 8
#define D5NEXT_VIRTUAL_SENSORS_START 0x3f
#define D5NEXT_PUMP_OFFSET 0x6c
#define D5NEXT_FAN_OFFSET 0x5f
#define D5NEXT_5V_VOLTAGE 0x39
@ -86,14 +90,18 @@ static u16 d5next_ctrl_fan_offsets[] = { 0x97, 0x42 };
#define FARBWERK_SENSOR_START 0x2f
/* Register offsets for the Farbwerk 360 RGB controller */
#define FARBWERK360_NUM_SENSORS 4
#define FARBWERK360_SENSOR_START 0x32
#define FARBWERK360_NUM_SENSORS 4
#define FARBWERK360_SENSOR_START 0x32
#define FARBWERK360_NUM_VIRTUAL_SENSORS 16
#define FARBWERK360_VIRTUAL_SENSORS_START 0x3a
/* Register offsets for the Octo fan controller */
#define OCTO_POWER_CYCLES 0x18
#define OCTO_NUM_FANS 8
#define OCTO_NUM_SENSORS 4
#define OCTO_SENSOR_START 0x3D
#define OCTO_NUM_VIRTUAL_SENSORS 16
#define OCTO_VIRTUAL_SENSORS_START 0x45
#define OCTO_CTRL_REPORT_SIZE 0x65F
static u8 octo_sensor_fan_offsets[] = { 0x7D, 0x8A, 0x97, 0xA4, 0xB1, 0xBE, 0xCB, 0xD8 };
@ -105,12 +113,24 @@ static u16 octo_ctrl_fan_offsets[] = { 0x5B, 0xB0, 0x105, 0x15A, 0x1AF, 0x204, 0
#define QUADRO_NUM_FANS 4
#define QUADRO_NUM_SENSORS 4
#define QUADRO_SENSOR_START 0x34
#define QUADRO_NUM_VIRTUAL_SENSORS 16
#define QUADRO_VIRTUAL_SENSORS_START 0x3c
#define QUADRO_CTRL_REPORT_SIZE 0x3c1
#define QUADRO_FLOW_SENSOR_OFFSET 0x6e
static u8 quadro_sensor_fan_offsets[] = { 0x70, 0x7D, 0x8A, 0x97 };
/* Fan speed registers in Quadro control report (from 0-100%) */
static u16 quadro_ctrl_fan_offsets[] = { 0x36, 0x8b, 0xe0, 0x135 };
static u16 quadro_ctrl_fan_offsets[] = { 0x37, 0x8c, 0xe1, 0x136 };
/* Register offsets for the High Flow Next */
#define HIGHFLOWNEXT_NUM_SENSORS 2
#define HIGHFLOWNEXT_SENSOR_START 85
#define HIGHFLOWNEXT_FLOW 81
#define HIGHFLOWNEXT_WATER_QUALITY 89
#define HIGHFLOWNEXT_POWER 91
#define HIGHFLOWNEXT_CONDUCTIVITY 95
#define HIGHFLOWNEXT_5V_VOLTAGE 97
#define HIGHFLOWNEXT_5V_VOLTAGE_USB 99
/* Labels for D5 Next */
static const char *const label_d5next_temp[] = {
@ -147,6 +167,25 @@ static const char *const label_temp_sensors[] = {
"Sensor 4"
};
static const char *const label_virtual_temp_sensors[] = {
"Virtual sensor 1",
"Virtual sensor 2",
"Virtual sensor 3",
"Virtual sensor 4",
"Virtual sensor 5",
"Virtual sensor 6",
"Virtual sensor 7",
"Virtual sensor 8",
"Virtual sensor 9",
"Virtual sensor 10",
"Virtual sensor 11",
"Virtual sensor 12",
"Virtual sensor 13",
"Virtual sensor 14",
"Virtual sensor 15",
"Virtual sensor 16",
};
/* Labels for Octo and Quadro (except speed) */
static const char *const label_fan_speed[] = {
"Fan 1 speed",
@ -201,6 +240,27 @@ static const char *const label_quadro_speeds[] = {
"Flow speed [dL/h]"
};
/* Labels for High Flow Next */
static const char *const label_highflownext_temp_sensors[] = {
"Coolant temp",
"External sensor"
};
static const char *const label_highflownext_fan_speed[] = {
"Flow [dL/h]",
"Water quality [%]",
"Conductivity [nS/cm]",
};
static const char *const label_highflownext_power[] = {
"Dissipated power",
};
static const char *const label_highflownext_voltage[] = {
"+5V voltage",
"+5V USB voltage"
};
struct aqc_data {
struct hid_device *hdev;
struct device *hwmon_dev;
@ -220,6 +280,8 @@ struct aqc_data {
u16 *fan_ctrl_offsets;
int num_temp_sensors;
int temp_sensor_start_offset;
int num_virtual_temp_sensors;
int virtual_temp_sensor_start_offset;
u16 power_cycle_count_offset;
u8 flow_sensor_offset;
@ -231,7 +293,7 @@ struct aqc_data {
u32 power_cycles;
/* Sensor values */
s32 temp_input[4];
s32 temp_input[20]; /* Max 4 physical and 16 virtual */
u16 speed_input[8];
u32 power_input[8];
u16 voltage_input[8];
@ -239,6 +301,7 @@ struct aqc_data {
/* Label values */
const char *const *temp_label;
const char *const *virtual_temp_label;
const char *const *speed_label;
const char *const *power_label;
const char *const *voltage_label;
@ -345,7 +408,7 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3
switch (type) {
case hwmon_temp:
if (channel < priv->num_temp_sensors)
if (channel < priv->num_temp_sensors + priv->num_virtual_temp_sensors)
return 0444;
break;
case hwmon_pwm:
@ -360,6 +423,11 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3
break;
case hwmon_fan:
switch (priv->kind) {
case highflownext:
/* Special case to support flow sensor, water quality and conductivity */
if (channel < 3)
return 0444;
break;
case quadro:
/* Special case to support flow sensor */
if (channel < priv->num_fans + 1)
@ -372,6 +440,18 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3
}
break;
case hwmon_power:
switch (priv->kind) {
case highflownext:
/* Special case to support one power sensor */
if (channel == 0)
return 0444;
break;
default:
if (channel < priv->num_fans)
return 0444;
break;
}
break;
case hwmon_curr:
if (channel < priv->num_fans)
return 0444;
@ -383,6 +463,11 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3
if (channel < priv->num_fans + 2)
return 0444;
break;
case highflownext:
/* Special case to support two voltage sensors */
if (channel < 2)
return 0444;
break;
default:
if (channel < priv->num_fans)
return 0444;
@ -447,7 +532,10 @@ static int aqc_read_string(struct device *dev, enum hwmon_sensor_types type, u32
switch (type) {
case hwmon_temp:
*str = priv->temp_label[channel];
if (channel < priv->num_temp_sensors)
*str = priv->temp_label[channel];
else
*str = priv->virtual_temp_label[channel - priv->num_temp_sensors];
break;
case hwmon_fan:
*str = priv->speed_label[channel];
@ -509,6 +597,22 @@ static const struct hwmon_ops aqc_hwmon_ops = {
static const struct hwmon_channel_info *aqc_info[] = {
HWMON_CHANNEL_INFO(temp,
HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL,
@ -568,7 +672,7 @@ static const struct hwmon_chip_info aqc_chip_info = {
static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size)
{
int i, sensor_value;
int i, j, sensor_value;
struct aqc_data *priv;
if (report->id != STATUS_REPORT_ID)
@ -581,7 +685,7 @@ static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8
priv->serial_number[1] = get_unaligned_be16(data + SERIAL_SECOND_PART);
priv->firmware_version = get_unaligned_be16(data + FIRMWARE_VERSION);
/* Temperature sensor readings */
/* Physical temperature sensor readings */
for (i = 0; i < priv->num_temp_sensors; i++) {
sensor_value = get_unaligned_be16(data +
priv->temp_sensor_start_offset +
@ -592,6 +696,18 @@ static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8
priv->temp_input[i] = sensor_value * 10;
}
/* Virtual temperature sensor readings */
for (j = 0; j < priv->num_virtual_temp_sensors; j++) {
sensor_value = get_unaligned_be16(data +
priv->virtual_temp_sensor_start_offset +
j * AQC_TEMP_SENSOR_SIZE);
if (sensor_value == AQC_TEMP_SENSOR_DISCONNECTED)
priv->temp_input[i] = -ENODATA;
else
priv->temp_input[i] = sensor_value * 10;
i++;
}
/* Fan speed and related readings */
for (i = 0; i < priv->num_fans; i++) {
priv->speed_input[i] =
@ -618,6 +734,22 @@ static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8
case quadro:
priv->speed_input[4] = get_unaligned_be16(data + priv->flow_sensor_offset);
break;
case highflownext:
/* If external temp sensor is not connected, its power reading is also N/A */
if (priv->temp_input[1] == -ENODATA)
priv->power_input[0] = -ENODATA;
else
priv->power_input[0] =
get_unaligned_be16(data + HIGHFLOWNEXT_POWER) * 1000000;
priv->voltage_input[0] = get_unaligned_be16(data + HIGHFLOWNEXT_5V_VOLTAGE) * 10;
priv->voltage_input[1] =
get_unaligned_be16(data + HIGHFLOWNEXT_5V_VOLTAGE_USB) * 10;
priv->speed_input[0] = get_unaligned_be16(data + HIGHFLOWNEXT_FLOW);
priv->speed_input[1] = get_unaligned_be16(data + HIGHFLOWNEXT_WATER_QUALITY);
priv->speed_input[2] = get_unaligned_be16(data + HIGHFLOWNEXT_CONDUCTIVITY);
break;
default:
break;
}
@ -717,10 +849,13 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
priv->fan_ctrl_offsets = d5next_ctrl_fan_offsets;
priv->num_temp_sensors = D5NEXT_NUM_SENSORS;
priv->temp_sensor_start_offset = D5NEXT_COOLANT_TEMP;
priv->num_virtual_temp_sensors = D5NEXT_NUM_VIRTUAL_SENSORS;
priv->virtual_temp_sensor_start_offset = D5NEXT_VIRTUAL_SENSORS_START;
priv->power_cycle_count_offset = D5NEXT_POWER_CYCLES;
priv->buffer_size = D5NEXT_CTRL_REPORT_SIZE;
priv->temp_label = label_d5next_temp;
priv->virtual_temp_label = label_virtual_temp_sensors;
priv->speed_label = label_d5next_speeds;
priv->power_label = label_d5next_power;
priv->voltage_label = label_d5next_voltages;
@ -740,7 +875,11 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
priv->num_fans = 0;
priv->num_temp_sensors = FARBWERK360_NUM_SENSORS;
priv->temp_sensor_start_offset = FARBWERK360_SENSOR_START;
priv->num_virtual_temp_sensors = FARBWERK360_NUM_VIRTUAL_SENSORS;
priv->virtual_temp_sensor_start_offset = FARBWERK360_VIRTUAL_SENSORS_START;
priv->temp_label = label_temp_sensors;
priv->virtual_temp_label = label_virtual_temp_sensors;
break;
case USB_PRODUCT_ID_OCTO:
priv->kind = octo;
@ -750,10 +889,13 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
priv->fan_ctrl_offsets = octo_ctrl_fan_offsets;
priv->num_temp_sensors = OCTO_NUM_SENSORS;
priv->temp_sensor_start_offset = OCTO_SENSOR_START;
priv->num_virtual_temp_sensors = OCTO_NUM_VIRTUAL_SENSORS;
priv->virtual_temp_sensor_start_offset = OCTO_VIRTUAL_SENSORS_START;
priv->power_cycle_count_offset = OCTO_POWER_CYCLES;
priv->buffer_size = OCTO_CTRL_REPORT_SIZE;
priv->temp_label = label_temp_sensors;
priv->virtual_temp_label = label_virtual_temp_sensors;
priv->speed_label = label_fan_speed;
priv->power_label = label_fan_power;
priv->voltage_label = label_fan_voltage;
@ -767,16 +909,32 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
priv->fan_ctrl_offsets = quadro_ctrl_fan_offsets;
priv->num_temp_sensors = QUADRO_NUM_SENSORS;
priv->temp_sensor_start_offset = QUADRO_SENSOR_START;
priv->num_virtual_temp_sensors = QUADRO_NUM_VIRTUAL_SENSORS;
priv->virtual_temp_sensor_start_offset = QUADRO_VIRTUAL_SENSORS_START;
priv->power_cycle_count_offset = QUADRO_POWER_CYCLES;
priv->buffer_size = QUADRO_CTRL_REPORT_SIZE;
priv->flow_sensor_offset = QUADRO_FLOW_SENSOR_OFFSET;
priv->temp_label = label_temp_sensors;
priv->virtual_temp_label = label_virtual_temp_sensors;
priv->speed_label = label_quadro_speeds;
priv->power_label = label_fan_power;
priv->voltage_label = label_fan_voltage;
priv->current_label = label_fan_current;
break;
case USB_PRODUCT_ID_HIGHFLOWNEXT:
priv->kind = highflownext;
priv->num_fans = 0;
priv->num_temp_sensors = HIGHFLOWNEXT_NUM_SENSORS;
priv->temp_sensor_start_offset = HIGHFLOWNEXT_SENSOR_START;
priv->power_cycle_count_offset = QUADRO_POWER_CYCLES;
priv->temp_label = label_highflownext_temp_sensors;
priv->speed_label = label_highflownext_fan_speed;
priv->power_label = label_highflownext_power;
priv->voltage_label = label_highflownext_voltage;
break;
default:
break;
}
@ -833,6 +991,7 @@ static const struct hid_device_id aqc_table[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_FARBWERK360) },
{ HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_OCTO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_QUADRO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_HIGHFLOWNEXT) },
{ }
};

View File

@ -769,7 +769,7 @@ static int asb100_detect(struct i2c_client *client,
if (val1 != 0x31 || val2 != 0x06)
return -ENODEV;
strlcpy(info->type, "asb100", I2C_NAME_SIZE);
strscpy(info->type, "asb100", I2C_NAME_SIZE);
return 0;
}

View File

@ -1153,7 +1153,7 @@ static int asc7621_detect(struct i2c_client *client,
if (company == asc7621_chips[chip_index].company_id &&
verstep == asc7621_chips[chip_index].verstep_id) {
strlcpy(info->type, asc7621_chips[chip_index].name,
strscpy(info->type, asc7621_chips[chip_index].name,
I2C_NAME_SIZE);
dev_info(&adapter->dev, "Matched %s at 0x%02x\n",

View File

@ -1,622 +0,0 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* HWMON driver for ASUS B550/X570 motherboards that publish sensor
* values via the embedded controller registers.
*
* Copyright (C) 2021 Eugene Shalygin <eugene.shalygin@gmail.com>
* Copyright (C) 2018-2019 Ed Brindley <kernel@maidavale.org>
*
* EC provides:
* - Chipset temperature
* - CPU temperature
* - Motherboard temperature
* - T_Sensor temperature
* - VRM temperature
* - Water In temperature
* - Water Out temperature
* - CPU Optional Fan RPM
* - Chipset Fan RPM
* - Water Flow Fan RPM
* - CPU current
*/
#include <linux/acpi.h>
#include <linux/dmi.h>
#include <linux/hwmon.h>
#include <linux/init.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/nls.h>
#include <linux/units.h>
#include <linux/wmi.h>
#include <asm/unaligned.h>
#define ASUSWMI_MONITORING_GUID "466747A0-70EC-11DE-8A39-0800200C9A66"
#define ASUSWMI_METHODID_BLOCK_READ_EC 0x42524543 /* BREC */
/* From the ASUS DSDT source */
#define ASUSWMI_BREC_REGISTERS_MAX 16
#define ASUSWMI_MAX_BUF_LEN 128
#define SENSOR_LABEL_LEN 16
static u32 hwmon_attributes[hwmon_max] = {
[hwmon_chip] = HWMON_C_REGISTER_TZ,
[hwmon_temp] = HWMON_T_INPUT | HWMON_T_LABEL,
[hwmon_in] = HWMON_I_INPUT | HWMON_I_LABEL,
[hwmon_curr] = HWMON_C_INPUT | HWMON_C_LABEL,
[hwmon_fan] = HWMON_F_INPUT | HWMON_F_LABEL,
};
struct asus_wmi_ec_sensor_address {
u8 index;
u8 bank;
u8 size;
};
#define MAKE_SENSOR_ADDRESS(size_i, bank_i, index_i) { \
.size = size_i, \
.bank = bank_i, \
.index = index_i, \
}
struct ec_sensor_info {
struct asus_wmi_ec_sensor_address addr;
char label[SENSOR_LABEL_LEN];
enum hwmon_sensor_types type;
};
#define EC_SENSOR(sensor_label, sensor_type, size, bank, index) { \
.addr = MAKE_SENSOR_ADDRESS(size, bank, index), \
.label = sensor_label, \
.type = sensor_type, \
}
enum known_ec_sensor {
SENSOR_TEMP_CHIPSET,
SENSOR_TEMP_CPU,
SENSOR_TEMP_MB,
SENSOR_TEMP_T_SENSOR,
SENSOR_TEMP_VRM,
SENSOR_FAN_CPU_OPT,
SENSOR_FAN_CHIPSET,
SENSOR_FAN_VRM_HS,
SENSOR_FAN_WATER_FLOW,
SENSOR_CURR_CPU,
SENSOR_TEMP_WATER_IN,
SENSOR_TEMP_WATER_OUT,
SENSOR_MAX
};
/* All known sensors for ASUS EC controllers */
static const struct ec_sensor_info known_ec_sensors[] = {
[SENSOR_TEMP_CHIPSET] = EC_SENSOR("Chipset", hwmon_temp, 1, 0x00, 0x3a),
[SENSOR_TEMP_CPU] = EC_SENSOR("CPU", hwmon_temp, 1, 0x00, 0x3b),
[SENSOR_TEMP_MB] = EC_SENSOR("Motherboard", hwmon_temp, 1, 0x00, 0x3c),
[SENSOR_TEMP_T_SENSOR] = EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x3d),
[SENSOR_TEMP_VRM] = EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x3e),
[SENSOR_FAN_CPU_OPT] = EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xb0),
[SENSOR_FAN_VRM_HS] = EC_SENSOR("VRM HS", hwmon_fan, 2, 0x00, 0xb2),
[SENSOR_FAN_CHIPSET] = EC_SENSOR("Chipset", hwmon_fan, 2, 0x00, 0xb4),
[SENSOR_FAN_WATER_FLOW] = EC_SENSOR("Water_Flow", hwmon_fan, 2, 0x00, 0xbc),
[SENSOR_CURR_CPU] = EC_SENSOR("CPU", hwmon_curr, 1, 0x00, 0xf4),
[SENSOR_TEMP_WATER_IN] = EC_SENSOR("Water_In", hwmon_temp, 1, 0x01, 0x00),
[SENSOR_TEMP_WATER_OUT] = EC_SENSOR("Water_Out", hwmon_temp, 1, 0x01, 0x01),
};
struct asus_wmi_data {
const enum known_ec_sensor known_board_sensors[SENSOR_MAX + 1];
};
/* boards with EC support */
static struct asus_wmi_data sensors_board_PW_X570_P = {
.known_board_sensors = {
SENSOR_TEMP_CHIPSET, SENSOR_TEMP_CPU, SENSOR_TEMP_MB,
SENSOR_TEMP_T_SENSOR, SENSOR_TEMP_VRM,
SENSOR_FAN_CHIPSET,
SENSOR_MAX
},
};
static struct asus_wmi_data sensors_board_PW_X570_A = {
.known_board_sensors = {
SENSOR_TEMP_CHIPSET, SENSOR_TEMP_CPU, SENSOR_TEMP_MB, SENSOR_TEMP_VRM,
SENSOR_FAN_CHIPSET,
SENSOR_CURR_CPU,
SENSOR_MAX
},
};
static struct asus_wmi_data sensors_board_R_C8H = {
.known_board_sensors = {
SENSOR_TEMP_CHIPSET, SENSOR_TEMP_CPU, SENSOR_TEMP_MB,
SENSOR_TEMP_T_SENSOR, SENSOR_TEMP_VRM,
SENSOR_TEMP_WATER_IN, SENSOR_TEMP_WATER_OUT,
SENSOR_FAN_CPU_OPT, SENSOR_FAN_CHIPSET, SENSOR_FAN_WATER_FLOW,
SENSOR_CURR_CPU,
SENSOR_MAX
},
};
/* Same as Hero but without chipset fan */
static struct asus_wmi_data sensors_board_R_C8DH = {
.known_board_sensors = {
SENSOR_TEMP_CHIPSET, SENSOR_TEMP_CPU, SENSOR_TEMP_MB,
SENSOR_TEMP_T_SENSOR, SENSOR_TEMP_VRM,
SENSOR_TEMP_WATER_IN, SENSOR_TEMP_WATER_OUT,
SENSOR_FAN_CPU_OPT, SENSOR_FAN_WATER_FLOW,
SENSOR_CURR_CPU,
SENSOR_MAX
},
};
/* Same as Hero but without water */
static struct asus_wmi_data sensors_board_R_C8F = {
.known_board_sensors = {
SENSOR_TEMP_CHIPSET, SENSOR_TEMP_CPU, SENSOR_TEMP_MB,
SENSOR_TEMP_T_SENSOR, SENSOR_TEMP_VRM,
SENSOR_FAN_CPU_OPT, SENSOR_FAN_CHIPSET,
SENSOR_CURR_CPU,
SENSOR_MAX
},
};
static struct asus_wmi_data sensors_board_RS_B550_E_G = {
.known_board_sensors = {
SENSOR_TEMP_CHIPSET, SENSOR_TEMP_CPU, SENSOR_TEMP_MB,
SENSOR_TEMP_T_SENSOR, SENSOR_TEMP_VRM,
SENSOR_FAN_CPU_OPT,
SENSOR_MAX
},
};
static struct asus_wmi_data sensors_board_RS_B550_I_G = {
.known_board_sensors = {
SENSOR_TEMP_CHIPSET, SENSOR_TEMP_CPU, SENSOR_TEMP_MB,
SENSOR_TEMP_T_SENSOR, SENSOR_TEMP_VRM,
SENSOR_FAN_VRM_HS,
SENSOR_CURR_CPU,
SENSOR_MAX
},
};
static struct asus_wmi_data sensors_board_RS_X570_E_G = {
.known_board_sensors = {
SENSOR_TEMP_CHIPSET, SENSOR_TEMP_CPU, SENSOR_TEMP_MB,
SENSOR_TEMP_T_SENSOR, SENSOR_TEMP_VRM,
SENSOR_FAN_CHIPSET,
SENSOR_CURR_CPU,
SENSOR_MAX
},
};
#define DMI_EXACT_MATCH_ASUS_BOARD_NAME(name, sensors) { \
.matches = { \
DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."), \
DMI_EXACT_MATCH(DMI_BOARD_NAME, name), \
}, \
.driver_data = sensors, \
}
static const struct dmi_system_id asus_wmi_ec_dmi_table[] = {
DMI_EXACT_MATCH_ASUS_BOARD_NAME("PRIME X570-PRO", &sensors_board_PW_X570_P),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("Pro WS X570-ACE", &sensors_board_PW_X570_A),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VIII DARK HERO", &sensors_board_R_C8DH),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VIII FORMULA", &sensors_board_R_C8F),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VIII HERO", &sensors_board_R_C8H),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B550-E GAMING", &sensors_board_RS_B550_E_G),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B550-I GAMING", &sensors_board_RS_B550_I_G),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX X570-E GAMING", &sensors_board_RS_X570_E_G),
{}
};
MODULE_DEVICE_TABLE(dmi, asus_wmi_ec_dmi_table);
struct ec_sensor {
enum known_ec_sensor info_index;
long cached_value;
};
/**
* struct asus_wmi_ec_info - sensor info.
* @sensors: list of sensors.
* @read_arg: UTF-16LE string to pass to BRxx() WMI function.
* @read_buffer: decoded output from WMI result.
* @nr_sensors: number of board EC sensors.
* @nr_registers: number of EC registers to read (sensor might span more than 1 register).
* @last_updated: in jiffies.
*/
struct asus_wmi_ec_info {
struct ec_sensor sensors[SENSOR_MAX];
char read_arg[(ASUSWMI_BREC_REGISTERS_MAX * 4 + 1) * 2];
u8 read_buffer[ASUSWMI_BREC_REGISTERS_MAX];
unsigned int nr_sensors;
unsigned int nr_registers;
unsigned long last_updated;
};
struct asus_wmi_sensors {
struct asus_wmi_ec_info ec;
/* lock access to internal cache */
struct mutex lock;
};
static int asus_wmi_ec_fill_board_sensors(struct asus_wmi_ec_info *ec,
const enum known_ec_sensor *bsi)
{
struct ec_sensor *s = ec->sensors;
int i;
ec->nr_sensors = 0;
ec->nr_registers = 0;
for (i = 0; bsi[i] != SENSOR_MAX; i++) {
s[i].info_index = bsi[i];
ec->nr_sensors++;
ec->nr_registers += known_ec_sensors[bsi[i]].addr.size;
}
return 0;
}
/*
* The next four functions convert to or from BRxx string argument format.
* The format of the string is as follows:
* - The string consists of two-byte UTF-16LE characters.
* - The value of the very first byte in the string is equal to the total
* length of the next string in bytes, thus excluding the first two-byte
* character.
* - The rest of the string encodes the pairs of (bank, index) pairs, where
* both values are byte-long (0x00 to 0xFF).
* - Numbers are encoded as UTF-16LE hex values.
*/
static int asus_wmi_ec_decode_reply_buffer(const u8 *in, u32 length, u8 *out)
{
char buffer[ASUSWMI_MAX_BUF_LEN * 2];
u32 len = min_t(u32, get_unaligned_le16(in), length - 2);
utf16s_to_utf8s((wchar_t *)(in + 2), len / 2, UTF16_LITTLE_ENDIAN, buffer, sizeof(buffer));
return hex2bin(out, buffer, len / 4);
}
static void asus_wmi_ec_encode_registers(const u8 *in, u32 len, char *out)
{
char buffer[ASUSWMI_MAX_BUF_LEN * 2];
bin2hex(buffer, in, len);
utf8s_to_utf16s(buffer, len * 2, UTF16_LITTLE_ENDIAN, (wchar_t *)(out + 2), len * 2);
put_unaligned_le16(len * 4, out);
}
static void asus_wmi_ec_make_block_read_query(struct asus_wmi_ec_info *ec)
{
u8 registers[ASUSWMI_BREC_REGISTERS_MAX * 2];
const struct ec_sensor_info *si;
int i, j, offset;
offset = 0;
for (i = 0; i < ec->nr_sensors; i++) {
si = &known_ec_sensors[ec->sensors[i].info_index];
for (j = 0; j < si->addr.size; j++) {
registers[offset++] = si->addr.bank;
registers[offset++] = si->addr.index + j;
}
}
asus_wmi_ec_encode_registers(registers, offset, ec->read_arg);
}
static int asus_wmi_ec_block_read(u32 method_id, char *query, u8 *out)
{
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
struct acpi_buffer input;
union acpi_object *obj;
acpi_status status;
int ret;
/* The first byte of the BRxx() argument string has to be the string size. */
input.length = query[0] + 2;
input.pointer = query;
status = wmi_evaluate_method(ASUSWMI_MONITORING_GUID, 0, method_id, &input, &output);
if (ACPI_FAILURE(status))
return -EIO;
obj = output.pointer;
if (!obj)
return -EIO;
if (obj->type != ACPI_TYPE_BUFFER || obj->buffer.length < 2) {
ret = -EIO;
goto out_free_obj;
}
ret = asus_wmi_ec_decode_reply_buffer(obj->buffer.pointer, obj->buffer.length, out);
out_free_obj:
ACPI_FREE(obj);
return ret;
}
static inline long get_sensor_value(const struct ec_sensor_info *si, u8 *data)
{
switch (si->addr.size) {
case 1:
return *data;
case 2:
return get_unaligned_be16(data);
case 4:
return get_unaligned_be32(data);
default:
return 0;
}
}
static void asus_wmi_ec_update_ec_sensors(struct asus_wmi_ec_info *ec)
{
const struct ec_sensor_info *si;
struct ec_sensor *s;
u8 i_sensor;
u8 *data;
data = ec->read_buffer;
for (i_sensor = 0; i_sensor < ec->nr_sensors; i_sensor++) {
s = &ec->sensors[i_sensor];
si = &known_ec_sensors[s->info_index];
s->cached_value = get_sensor_value(si, data);
data += si->addr.size;
}
}
static long asus_wmi_ec_scale_sensor_value(long value, int data_type)
{
switch (data_type) {
case hwmon_curr:
case hwmon_temp:
case hwmon_in:
return value * MILLI;
default:
return value;
}
}
static int asus_wmi_ec_find_sensor_index(const struct asus_wmi_ec_info *ec,
enum hwmon_sensor_types type, int channel)
{
int i;
for (i = 0; i < ec->nr_sensors; i++) {
if (known_ec_sensors[ec->sensors[i].info_index].type == type) {
if (channel == 0)
return i;
channel--;
}
}
return -EINVAL;
}
static int asus_wmi_ec_get_cached_value_or_update(struct asus_wmi_sensors *sensor_data,
int sensor_index,
long *value)
{
struct asus_wmi_ec_info *ec = &sensor_data->ec;
int ret = 0;
mutex_lock(&sensor_data->lock);
if (time_after(jiffies, ec->last_updated + HZ)) {
ret = asus_wmi_ec_block_read(ASUSWMI_METHODID_BLOCK_READ_EC,
ec->read_arg, ec->read_buffer);
if (ret)
goto unlock;
asus_wmi_ec_update_ec_sensors(ec);
ec->last_updated = jiffies;
}
*value = ec->sensors[sensor_index].cached_value;
unlock:
mutex_unlock(&sensor_data->lock);
return ret;
}
/* Now follow the functions that implement the hwmon interface */
static int asus_wmi_ec_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
struct asus_wmi_sensors *sensor_data = dev_get_drvdata(dev);
struct asus_wmi_ec_info *ec = &sensor_data->ec;
int ret, sidx, info_index;
long value = 0;
sidx = asus_wmi_ec_find_sensor_index(ec, type, channel);
if (sidx < 0)
return sidx;
ret = asus_wmi_ec_get_cached_value_or_update(sensor_data, sidx, &value);
if (ret)
return ret;
info_index = ec->sensors[sidx].info_index;
*val = asus_wmi_ec_scale_sensor_value(value, known_ec_sensors[info_index].type);
return ret;
}
static int asus_wmi_ec_hwmon_read_string(struct device *dev,
enum hwmon_sensor_types type, u32 attr,
int channel, const char **str)
{
struct asus_wmi_sensors *sensor_data = dev_get_drvdata(dev);
struct asus_wmi_ec_info *ec = &sensor_data->ec;
int sensor_index;
sensor_index = asus_wmi_ec_find_sensor_index(ec, type, channel);
*str = known_ec_sensors[ec->sensors[sensor_index].info_index].label;
return 0;
}
static umode_t asus_wmi_ec_hwmon_is_visible(const void *drvdata,
enum hwmon_sensor_types type, u32 attr,
int channel)
{
const struct asus_wmi_sensors *sensor_data = drvdata;
const struct asus_wmi_ec_info *ec = &sensor_data->ec;
int index;
index = asus_wmi_ec_find_sensor_index(ec, type, channel);
return index < 0 ? 0 : 0444;
}
static int asus_wmi_hwmon_add_chan_info(struct hwmon_channel_info *asus_wmi_hwmon_chan,
struct device *dev, int num,
enum hwmon_sensor_types type, u32 config)
{
u32 *cfg;
cfg = devm_kcalloc(dev, num + 1, sizeof(*cfg), GFP_KERNEL);
if (!cfg)
return -ENOMEM;
asus_wmi_hwmon_chan->type = type;
asus_wmi_hwmon_chan->config = cfg;
memset32(cfg, config, num);
return 0;
}
static const struct hwmon_ops asus_wmi_ec_hwmon_ops = {
.is_visible = asus_wmi_ec_hwmon_is_visible,
.read = asus_wmi_ec_hwmon_read,
.read_string = asus_wmi_ec_hwmon_read_string,
};
static struct hwmon_chip_info asus_wmi_ec_chip_info = {
.ops = &asus_wmi_ec_hwmon_ops,
};
static int asus_wmi_ec_configure_sensor_setup(struct device *dev,
const enum known_ec_sensor *bsi)
{
struct asus_wmi_sensors *sensor_data = dev_get_drvdata(dev);
struct asus_wmi_ec_info *ec = &sensor_data->ec;
struct hwmon_channel_info *asus_wmi_hwmon_chan;
const struct hwmon_channel_info **asus_wmi_ci;
int nr_count[hwmon_max] = {}, nr_types = 0;
const struct hwmon_chip_info *chip_info;
const struct ec_sensor_info *si;
enum hwmon_sensor_types type;
struct device *hwdev;
int i, ret;
ret = asus_wmi_ec_fill_board_sensors(ec, bsi);
if (ret)
return ret;
if (!sensor_data->ec.nr_sensors)
return -ENODEV;
for (i = 0; i < ec->nr_sensors; i++) {
si = &known_ec_sensors[ec->sensors[i].info_index];
if (!nr_count[si->type])
nr_types++;
nr_count[si->type]++;
}
if (nr_count[hwmon_temp]) {
nr_count[hwmon_chip]++;
nr_types++;
}
/*
* If we can get values for all the registers in a single query,
* the query will not change from call to call.
*/
asus_wmi_ec_make_block_read_query(ec);
asus_wmi_hwmon_chan = devm_kcalloc(dev, nr_types, sizeof(*asus_wmi_hwmon_chan),
GFP_KERNEL);
if (!asus_wmi_hwmon_chan)
return -ENOMEM;
asus_wmi_ci = devm_kcalloc(dev, nr_types + 1, sizeof(*asus_wmi_ci), GFP_KERNEL);
if (!asus_wmi_ci)
return -ENOMEM;
asus_wmi_ec_chip_info.info = asus_wmi_ci;
chip_info = &asus_wmi_ec_chip_info;
for (type = 0; type < hwmon_max; type++) {
if (!nr_count[type])
continue;
ret = asus_wmi_hwmon_add_chan_info(asus_wmi_hwmon_chan, dev,
nr_count[type], type,
hwmon_attributes[type]);
if (ret)
return ret;
*asus_wmi_ci++ = asus_wmi_hwmon_chan++;
}
dev_dbg(dev, "board has %d EC sensors that span %d registers",
ec->nr_sensors, ec->nr_registers);
hwdev = devm_hwmon_device_register_with_info(dev, "asus_wmi_ec_sensors",
sensor_data, chip_info, NULL);
return PTR_ERR_OR_ZERO(hwdev);
}
static int asus_wmi_probe(struct wmi_device *wdev, const void *context)
{
struct asus_wmi_sensors *sensor_data;
struct asus_wmi_data *board_sensors;
const struct dmi_system_id *dmi_id;
const enum known_ec_sensor *bsi;
struct device *dev = &wdev->dev;
dmi_id = dmi_first_match(asus_wmi_ec_dmi_table);
if (!dmi_id)
return -ENODEV;
board_sensors = dmi_id->driver_data;
bsi = board_sensors->known_board_sensors;
sensor_data = devm_kzalloc(dev, sizeof(*sensor_data), GFP_KERNEL);
if (!sensor_data)
return -ENOMEM;
mutex_init(&sensor_data->lock);
dev_set_drvdata(dev, sensor_data);
return asus_wmi_ec_configure_sensor_setup(dev, bsi);
}
static const struct wmi_device_id asus_ec_wmi_id_table[] = {
{ ASUSWMI_MONITORING_GUID, NULL },
{ }
};
static struct wmi_driver asus_sensors_wmi_driver = {
.driver = {
.name = "asus_wmi_ec_sensors",
},
.id_table = asus_ec_wmi_id_table,
.probe = asus_wmi_probe,
};
module_wmi_driver(asus_sensors_wmi_driver);
MODULE_AUTHOR("Ed Brindley <kernel@maidavale.org>");
MODULE_AUTHOR("Eugene Shalygin <eugene.shalygin@gmail.com>");
MODULE_DESCRIPTION("Asus WMI Sensors Driver");
MODULE_LICENSE("GPL");

View File

@ -394,11 +394,6 @@ static int axi_fan_control_init(struct axi_fan_control_data *ctl,
return ret;
}
static void axi_fan_control_clk_disable(void *clk)
{
clk_disable_unprepare(clk);
}
static const struct hwmon_channel_info *axi_fan_control_info[] = {
HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT),
HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_FAULT | HWMON_F_LABEL),
@ -478,20 +473,12 @@ static int axi_fan_control_probe(struct platform_device *pdev)
if (IS_ERR(ctl->base))
return PTR_ERR(ctl->base);
clk = devm_clk_get(&pdev->dev, NULL);
clk = devm_clk_get_enabled(&pdev->dev, NULL);
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "clk_get failed with %ld\n", PTR_ERR(clk));
return PTR_ERR(clk);
}
ret = clk_prepare_enable(clk);
if (ret)
return ret;
ret = devm_add_action_or_reset(&pdev->dev, axi_fan_control_clk_disable, clk);
if (ret)
return ret;
ctl->clk_rate = clk_get_rate(clk);
if (!ctl->clk_rate)
return -EINVAL;

View File

@ -55,6 +55,7 @@
#define SECONDS_PER_DAY (SECONDS_PER_HOUR * 24)
#define RAIL_COUNT 3 /* 3v3 + 5v + 12v */
#define TEMP_COUNT 2
#define OCP_MULTI_RAIL 0x02
#define PSU_CMD_SELECT_RAIL 0x00 /* expects length 2 */
#define PSU_CMD_RAIL_VOLTS_HCRIT 0x40 /* the rest of the commands expect length 3 */
@ -71,9 +72,10 @@
#define PSU_CMD_RAIL_WATTS 0x96
#define PSU_CMD_VEND_STR 0x99
#define PSU_CMD_PROD_STR 0x9A
#define PSU_CMD_TOTAL_WATTS 0xEE
#define PSU_CMD_TOTAL_UPTIME 0xD1
#define PSU_CMD_UPTIME 0xD2
#define PSU_CMD_OCPMODE 0xD8
#define PSU_CMD_TOTAL_WATTS 0xEE
#define PSU_CMD_INIT 0xFE
#define L_IN_VOLTS "v_in"
@ -268,6 +270,7 @@ static int corsairpsu_get_value(struct corsairpsu_data *priv, u8 cmd, u8 rail, l
break;
case PSU_CMD_TOTAL_UPTIME:
case PSU_CMD_UPTIME:
case PSU_CMD_OCPMODE:
*val = tmp;
break;
default:
@ -660,6 +663,29 @@ static int product_show(struct seq_file *seqf, void *unused)
}
DEFINE_SHOW_ATTRIBUTE(product);
static int ocpmode_show(struct seq_file *seqf, void *unused)
{
struct corsairpsu_data *priv = seqf->private;
long val;
int ret;
/*
* The rail mode is switchable on the fly. The RAW interface can be used for this. But it
* will not be included here, because I consider it somewhat dangerous for the health of the
* PSU. The returned value can be a bogus one, if the PSU is in the process of switching and
* getting of the value itself can also fail during this. Because of this every other value
* than OCP_MULTI_RAIL can be considered as "single rail".
*/
ret = corsairpsu_get_value(priv, PSU_CMD_OCPMODE, 0, &val);
if (ret < 0)
seq_puts(seqf, "N/A\n");
else
seq_printf(seqf, "%s\n", (val == OCP_MULTI_RAIL) ? "multi rail" : "single rail");
return 0;
}
DEFINE_SHOW_ATTRIBUTE(ocpmode);
static void corsairpsu_debugfs_init(struct corsairpsu_data *priv)
{
char name[32];
@ -671,6 +697,7 @@ static void corsairpsu_debugfs_init(struct corsairpsu_data *priv)
debugfs_create_file("uptime_total", 0444, priv->debugfs, priv, &uptime_total_fops);
debugfs_create_file("vendor", 0444, priv->debugfs, priv, &vendor_fops);
debugfs_create_file("product", 0444, priv->debugfs, priv, &product_fops);
debugfs_create_file("ocpmode", 0444, priv->debugfs, priv, &ocpmode_fops);
}
#else
@ -786,13 +813,14 @@ static const struct hid_device_id corsairpsu_idtable[] = {
{ HID_USB_DEVICE(0x1b1c, 0x1c04) }, /* Corsair HX650i */
{ HID_USB_DEVICE(0x1b1c, 0x1c05) }, /* Corsair HX750i */
{ HID_USB_DEVICE(0x1b1c, 0x1c06) }, /* Corsair HX850i */
{ HID_USB_DEVICE(0x1b1c, 0x1c07) }, /* Corsair HX1000i */
{ HID_USB_DEVICE(0x1b1c, 0x1c07) }, /* Corsair HX1000i revision 1 */
{ HID_USB_DEVICE(0x1b1c, 0x1c08) }, /* Corsair HX1200i */
{ HID_USB_DEVICE(0x1b1c, 0x1c09) }, /* Corsair RM550i */
{ HID_USB_DEVICE(0x1b1c, 0x1c0a) }, /* Corsair RM650i */
{ HID_USB_DEVICE(0x1b1c, 0x1c0b) }, /* Corsair RM750i */
{ HID_USB_DEVICE(0x1b1c, 0x1c0c) }, /* Corsair RM850i */
{ HID_USB_DEVICE(0x1b1c, 0x1c0d) }, /* Corsair RM1000i */
{ HID_USB_DEVICE(0x1b1c, 0x1c1e) }, /* Corsaur HX1000i revision 2 */
{ },
};
MODULE_DEVICE_TABLE(hid, corsairpsu_idtable);

View File

@ -1355,15 +1355,21 @@ static int __init dell_smm_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, data);
if (dmi_check_system(i8k_blacklist_fan_support_dmi_table)) {
dev_warn(&pdev->dev, "broken Dell BIOS detected, disallow fan support\n");
if (!force)
if (!force) {
dev_notice(&pdev->dev, "Disabling fan support due to BIOS bugs\n");
data->disallow_fan_support = true;
} else {
dev_warn(&pdev->dev, "Enabling fan support despite BIOS bugs\n");
}
}
if (dmi_check_system(i8k_blacklist_fan_type_dmi_table)) {
dev_warn(&pdev->dev, "broken Dell BIOS detected, disallow fan type call\n");
if (!force)
if (!force) {
dev_notice(&pdev->dev, "Disabling fan type call due to BIOS bugs\n");
data->disallow_fan_type_call = true;
} else {
dev_warn(&pdev->dev, "Enabling fan type call despite BIOS bugs\n");
}
}
strscpy(data->bios_version, i8k_get_dmi_data(DMI_BIOS_VERSION),

View File

@ -2456,7 +2456,7 @@ static int dme1737_i2c_detect(struct i2c_client *client,
dev_info(dev, "Found a %s chip at 0x%02x (rev 0x%02x).\n",
verstep == SCH5027_VERSTEP ? "SCH5027" : "DME1737",
client->addr, verstep);
strlcpy(info->type, name, I2C_NAME_SIZE);
strscpy(info->type, name, I2C_NAME_SIZE);
return 0;
}

View File

@ -329,22 +329,22 @@ static int emc1403_detect(struct i2c_client *client,
id = i2c_smbus_read_byte_data(client, THERMAL_PID_REG);
switch (id) {
case 0x20:
strlcpy(info->type, "emc1402", I2C_NAME_SIZE);
strscpy(info->type, "emc1402", I2C_NAME_SIZE);
break;
case 0x21:
strlcpy(info->type, "emc1403", I2C_NAME_SIZE);
strscpy(info->type, "emc1403", I2C_NAME_SIZE);
break;
case 0x22:
strlcpy(info->type, "emc1422", I2C_NAME_SIZE);
strscpy(info->type, "emc1422", I2C_NAME_SIZE);
break;
case 0x23:
strlcpy(info->type, "emc1423", I2C_NAME_SIZE);
strscpy(info->type, "emc1423", I2C_NAME_SIZE);
break;
case 0x25:
strlcpy(info->type, "emc1404", I2C_NAME_SIZE);
strscpy(info->type, "emc1404", I2C_NAME_SIZE);
break;
case 0x27:
strlcpy(info->type, "emc1424", I2C_NAME_SIZE);
strscpy(info->type, "emc1424", I2C_NAME_SIZE);
break;
default:
return -ENODEV;

View File

@ -643,7 +643,7 @@ emc2103_detect(struct i2c_client *new_client, struct i2c_board_info *info)
if ((product != 0x24) && (product != 0x26))
return -ENODEV;
strlcpy(info->type, "emc2103", I2C_NAME_SIZE);
strscpy(info->type, "emc2103", I2C_NAME_SIZE);
return 0;
}

620
drivers/hwmon/emc2305.c Normal file
View File

@ -0,0 +1,620 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Hardware monitoring driver for EMC2305 fan controller
*
* Copyright (C) 2022 Nvidia Technologies Ltd.
*/
#include <linux/err.h>
#include <linux/hwmon.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/platform_data/emc2305.h>
#include <linux/thermal.h>
static const unsigned short
emc2305_normal_i2c[] = { 0x27, 0x2c, 0x2d, 0x2e, 0x2f, 0x4c, 0x4d, I2C_CLIENT_END };
#define EMC2305_REG_DRIVE_FAIL_STATUS 0x27
#define EMC2305_REG_DEVICE 0xfd
#define EMC2305_REG_VENDOR 0xfe
#define EMC2305_FAN_MAX 0xff
#define EMC2305_FAN_MIN 0x00
#define EMC2305_FAN_MAX_STATE 10
#define EMC2305_DEVICE 0x34
#define EMC2305_VENDOR 0x5d
#define EMC2305_REG_PRODUCT_ID 0xfd
#define EMC2305_TACH_REGS_UNUSE_BITS 3
#define EMC2305_TACH_CNT_MULTIPLIER 0x02
#define EMC2305_TACH_RANGE_MIN 480
#define EMC2305_PWM_DUTY2STATE(duty, max_state, pwm_max) \
DIV_ROUND_CLOSEST((duty) * (max_state), (pwm_max))
#define EMC2305_PWM_STATE2DUTY(state, max_state, pwm_max) \
DIV_ROUND_CLOSEST((state) * (pwm_max), (max_state))
/*
* Factor by equations [2] and [3] from data sheet; valid for fans where the number of edges
* equal (poles * 2 + 1).
*/
#define EMC2305_RPM_FACTOR 3932160
#define EMC2305_REG_FAN_DRIVE(n) (0x30 + 0x10 * (n))
#define EMC2305_REG_FAN_MIN_DRIVE(n) (0x38 + 0x10 * (n))
#define EMC2305_REG_FAN_TACH(n) (0x3e + 0x10 * (n))
enum emc230x_product_id {
EMC2305 = 0x34,
EMC2303 = 0x35,
EMC2302 = 0x36,
EMC2301 = 0x37,
};
static const struct i2c_device_id emc2305_ids[] = {
{ "emc2305", 0 },
{ "emc2303", 0 },
{ "emc2302", 0 },
{ "emc2301", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, emc2305_ids);
/**
* @cdev: cooling device;
* @curr_state: cooling current state;
* @last_hwmon_state: last cooling state updated by hwmon subsystem;
* @last_thermal_state: last cooling state updated by thermal subsystem;
*
* The 'last_hwmon_state' and 'last_thermal_state' fields are provided to support fan low limit
* speed feature. The purpose of this feature is to provides ability to limit fan speed
* according to some system wise considerations, like absence of some replaceable units (PSU or
* line cards), high system ambient temperature, unreliable transceivers temperature sensing or
* some other factors which indirectly impacts system's airflow
* Fan low limit feature is supported through 'hwmon' interface: 'hwmon' 'pwm' attribute is
* used for setting low limit for fan speed in case 'thermal' subsystem is configured in
* kernel. In this case setting fan speed through 'hwmon' will never let the 'thermal'
* subsystem to select a lower duty cycle than the duty cycle selected with the 'pwm'
* attribute.
* From other side, fan speed is to be updated in hardware through 'pwm' only in case the
* requested fan speed is above last speed set by 'thermal' subsystem, otherwise requested fan
* speed will be just stored with no PWM update.
*/
struct emc2305_cdev_data {
struct thermal_cooling_device *cdev;
unsigned int cur_state;
unsigned long last_hwmon_state;
unsigned long last_thermal_state;
};
/**
* @client: i2c client;
* @hwmon_dev: hwmon device;
* @max_state: maximum cooling state of the cooling device;
* @pwm_num: number of PWM channels;
* @pwm_separate: separate PWM settings for every channel;
* @pwm_min: array of minimum PWM per channel;
* @cdev_data: array of cooling devices data;
*/
struct emc2305_data {
struct i2c_client *client;
struct device *hwmon_dev;
u8 max_state;
u8 pwm_num;
bool pwm_separate;
u8 pwm_min[EMC2305_PWM_MAX];
struct emc2305_cdev_data cdev_data[EMC2305_PWM_MAX];
};
static char *emc2305_fan_name[] = {
"emc2305_fan",
"emc2305_fan1",
"emc2305_fan2",
"emc2305_fan3",
"emc2305_fan4",
"emc2305_fan5",
};
static void emc2305_unset_tz(struct device *dev);
static int emc2305_get_max_channel(const struct emc2305_data *data)
{
return data->pwm_num;
}
static int emc2305_get_cdev_idx(struct thermal_cooling_device *cdev)
{
struct emc2305_data *data = cdev->devdata;
size_t len = strlen(cdev->type);
int ret;
if (len <= 0)
return -EINVAL;
/*
* Returns index of cooling device 0..4 in case of separate PWM setting.
* Zero index is used in case of one common PWM setting.
* If the mode is not set as pwm_separate, all PWMs are to be bound
* to the common thermal zone and should work at the same speed
* to perform cooling for the same thermal junction.
* Otherwise, return specific channel that will be used in bound
* related PWM to the thermal zone.
*/
if (!data->pwm_separate)
return 0;
ret = cdev->type[len - 1];
switch (ret) {
case '1' ... '5':
return ret - '1';
default:
break;
}
return -EINVAL;
}
static int emc2305_get_cur_state(struct thermal_cooling_device *cdev, unsigned long *state)
{
int cdev_idx;
struct emc2305_data *data = cdev->devdata;
cdev_idx = emc2305_get_cdev_idx(cdev);
if (cdev_idx < 0)
return cdev_idx;
*state = data->cdev_data[cdev_idx].cur_state;
return 0;
}
static int emc2305_get_max_state(struct thermal_cooling_device *cdev, unsigned long *state)
{
struct emc2305_data *data = cdev->devdata;
*state = data->max_state;
return 0;
}
static int emc2305_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
{
int cdev_idx, ret;
struct emc2305_data *data = cdev->devdata;
struct i2c_client *client = data->client;
u8 val, i;
if (state > data->max_state)
return -EINVAL;
cdev_idx = emc2305_get_cdev_idx(cdev);
if (cdev_idx < 0)
return cdev_idx;
/* Save thermal state. */
data->cdev_data[cdev_idx].last_thermal_state = state;
state = max_t(unsigned long, state, data->cdev_data[cdev_idx].last_hwmon_state);
val = EMC2305_PWM_STATE2DUTY(state, data->max_state, EMC2305_FAN_MAX);
data->cdev_data[cdev_idx].cur_state = state;
if (data->pwm_separate) {
ret = i2c_smbus_write_byte_data(client, EMC2305_REG_FAN_DRIVE(cdev_idx), val);
if (ret < 0)
return ret;
} else {
/*
* Set the same PWM value in all channels
* if common PWM channel is used.
*/
for (i = 0; i < data->pwm_num; i++) {
ret = i2c_smbus_write_byte_data(client, EMC2305_REG_FAN_DRIVE(i), val);
if (ret < 0)
return ret;
}
}
return 0;
}
static const struct thermal_cooling_device_ops emc2305_cooling_ops = {
.get_max_state = emc2305_get_max_state,
.get_cur_state = emc2305_get_cur_state,
.set_cur_state = emc2305_set_cur_state,
};
static int emc2305_show_fault(struct device *dev, int channel)
{
struct emc2305_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
int status_reg;
status_reg = i2c_smbus_read_byte_data(client, EMC2305_REG_DRIVE_FAIL_STATUS);
if (status_reg < 0)
return status_reg;
return status_reg & (1 << channel) ? 1 : 0;
}
static int emc2305_show_fan(struct device *dev, int channel)
{
struct emc2305_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
int ret;
ret = i2c_smbus_read_word_swapped(client, EMC2305_REG_FAN_TACH(channel));
if (ret <= 0)
return ret;
ret = ret >> EMC2305_TACH_REGS_UNUSE_BITS;
ret = EMC2305_RPM_FACTOR / ret;
if (ret <= EMC2305_TACH_RANGE_MIN)
return 0;
return ret * EMC2305_TACH_CNT_MULTIPLIER;
}
static int emc2305_show_pwm(struct device *dev, int channel)
{
struct emc2305_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
return i2c_smbus_read_byte_data(client, EMC2305_REG_FAN_DRIVE(channel));
}
static int emc2305_set_pwm(struct device *dev, long val, int channel)
{
struct emc2305_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
int ret;
if (val < data->pwm_min[channel] || val > EMC2305_FAN_MAX)
return -EINVAL;
ret = i2c_smbus_write_byte_data(client, EMC2305_REG_FAN_DRIVE(channel), val);
if (ret < 0)
return ret;
data->cdev_data[channel].cur_state = EMC2305_PWM_DUTY2STATE(val, data->max_state,
EMC2305_FAN_MAX);
return 0;
}
static int emc2305_set_single_tz(struct device *dev, int idx)
{
struct emc2305_data *data = dev_get_drvdata(dev);
long pwm;
int i, cdev_idx, ret;
cdev_idx = (idx) ? idx - 1 : 0;
pwm = data->pwm_min[cdev_idx];
data->cdev_data[cdev_idx].cdev =
thermal_cooling_device_register(emc2305_fan_name[idx], data,
&emc2305_cooling_ops);
if (IS_ERR(data->cdev_data[cdev_idx].cdev)) {
dev_err(dev, "Failed to register cooling device %s\n", emc2305_fan_name[idx]);
return PTR_ERR(data->cdev_data[cdev_idx].cdev);
}
/* Set minimal PWM speed. */
if (data->pwm_separate) {
ret = emc2305_set_pwm(dev, pwm, cdev_idx);
if (ret < 0)
return ret;
} else {
for (i = 0; i < data->pwm_num; i++) {
ret = emc2305_set_pwm(dev, pwm, i);
if (ret < 0)
return ret;
}
}
data->cdev_data[cdev_idx].cur_state =
EMC2305_PWM_DUTY2STATE(data->pwm_min[cdev_idx], data->max_state,
EMC2305_FAN_MAX);
data->cdev_data[cdev_idx].last_hwmon_state =
EMC2305_PWM_DUTY2STATE(data->pwm_min[cdev_idx], data->max_state,
EMC2305_FAN_MAX);
return 0;
}
static int emc2305_set_tz(struct device *dev)
{
struct emc2305_data *data = dev_get_drvdata(dev);
int i, ret;
if (!data->pwm_separate)
return emc2305_set_single_tz(dev, 0);
for (i = 0; i < data->pwm_num; i++) {
ret = emc2305_set_single_tz(dev, i + 1);
if (ret)
goto thermal_cooling_device_register_fail;
}
return 0;
thermal_cooling_device_register_fail:
emc2305_unset_tz(dev);
return ret;
}
static void emc2305_unset_tz(struct device *dev)
{
struct emc2305_data *data = dev_get_drvdata(dev);
int i;
/* Unregister cooling device. */
for (i = 0; i < EMC2305_PWM_MAX; i++)
if (data->cdev_data[i].cdev)
thermal_cooling_device_unregister(data->cdev_data[i].cdev);
}
static umode_t
emc2305_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr, int channel)
{
int max_channel = emc2305_get_max_channel(data);
/* Don't show channels which are not physically connected. */
if (channel >= max_channel)
return 0;
switch (type) {
case hwmon_fan:
switch (attr) {
case hwmon_fan_input:
return 0444;
case hwmon_fan_fault:
return 0444;
default:
break;
}
break;
case hwmon_pwm:
switch (attr) {
case hwmon_pwm_input:
return 0644;
default:
break;
}
break;
default:
break;
}
return 0;
};
static int
emc2305_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long val)
{
struct emc2305_data *data = dev_get_drvdata(dev);
int cdev_idx;
switch (type) {
case hwmon_pwm:
switch (attr) {
case hwmon_pwm_input:
/* If thermal is configured - handle PWM limit setting. */
if (IS_REACHABLE(CONFIG_THERMAL)) {
if (data->pwm_separate)
cdev_idx = channel;
else
cdev_idx = 0;
data->cdev_data[cdev_idx].last_hwmon_state =
EMC2305_PWM_DUTY2STATE(val, data->max_state,
EMC2305_FAN_MAX);
/*
* Update PWM only in case requested state is not less than the
* last thermal state.
*/
if (data->cdev_data[cdev_idx].last_hwmon_state >=
data->cdev_data[cdev_idx].last_thermal_state)
return emc2305_set_cur_state(data->cdev_data[cdev_idx].cdev,
data->cdev_data[cdev_idx].last_hwmon_state);
return 0;
}
return emc2305_set_pwm(dev, val, channel);
default:
break;
}
break;
default:
break;
}
return -EOPNOTSUPP;
};
static int
emc2305_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val)
{
int ret;
switch (type) {
case hwmon_fan:
switch (attr) {
case hwmon_fan_input:
ret = emc2305_show_fan(dev, channel);
if (ret < 0)
return ret;
*val = ret;
return 0;
case hwmon_fan_fault:
ret = emc2305_show_fault(dev, channel);
if (ret < 0)
return ret;
*val = ret;
return 0;
default:
break;
}
break;
case hwmon_pwm:
switch (attr) {
case hwmon_pwm_input:
ret = emc2305_show_pwm(dev, channel);
if (ret < 0)
return ret;
*val = ret;
return 0;
default:
break;
}
break;
default:
break;
}
return -EOPNOTSUPP;
};
static const struct hwmon_ops emc2305_ops = {
.is_visible = emc2305_is_visible,
.read = emc2305_read,
.write = emc2305_write,
};
static const struct hwmon_channel_info *emc2305_info[] = {
HWMON_CHANNEL_INFO(fan,
HWMON_F_INPUT | HWMON_F_FAULT,
HWMON_F_INPUT | HWMON_F_FAULT,
HWMON_F_INPUT | HWMON_F_FAULT,
HWMON_F_INPUT | HWMON_F_FAULT,
HWMON_F_INPUT | HWMON_F_FAULT),
HWMON_CHANNEL_INFO(pwm,
HWMON_PWM_INPUT,
HWMON_PWM_INPUT,
HWMON_PWM_INPUT,
HWMON_PWM_INPUT,
HWMON_PWM_INPUT),
NULL
};
static const struct hwmon_chip_info emc2305_chip_info = {
.ops = &emc2305_ops,
.info = emc2305_info,
};
static int emc2305_identify(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct emc2305_data *data = i2c_get_clientdata(client);
int ret;
ret = i2c_smbus_read_byte_data(client, EMC2305_REG_PRODUCT_ID);
if (ret < 0)
return ret;
switch (ret) {
case EMC2305:
data->pwm_num = 5;
break;
case EMC2303:
data->pwm_num = 3;
break;
case EMC2302:
data->pwm_num = 2;
break;
case EMC2301:
data->pwm_num = 1;
break;
default:
return -ENODEV;
}
return 0;
}
static int emc2305_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct i2c_adapter *adapter = client->adapter;
struct device *dev = &client->dev;
struct emc2305_data *data;
struct emc2305_platform_data *pdata;
int vendor, device;
int ret;
int i;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA))
return -ENODEV;
vendor = i2c_smbus_read_byte_data(client, EMC2305_REG_VENDOR);
if (vendor != EMC2305_VENDOR)
return -ENODEV;
device = i2c_smbus_read_byte_data(client, EMC2305_REG_DEVICE);
if (device != EMC2305_DEVICE)
return -ENODEV;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
i2c_set_clientdata(client, data);
data->client = client;
ret = emc2305_identify(dev);
if (ret)
return ret;
pdata = dev_get_platdata(&client->dev);
if (pdata) {
if (!pdata->max_state || pdata->max_state > EMC2305_FAN_MAX_STATE)
return -EINVAL;
data->max_state = pdata->max_state;
/*
* Validate a number of active PWM channels. Note that
* configured number can be less than the actual maximum
* supported by the device.
*/
if (!pdata->pwm_num || pdata->pwm_num > EMC2305_PWM_MAX)
return -EINVAL;
data->pwm_num = pdata->pwm_num;
data->pwm_separate = pdata->pwm_separate;
for (i = 0; i < EMC2305_PWM_MAX; i++)
data->pwm_min[i] = pdata->pwm_min[i];
} else {
data->max_state = EMC2305_FAN_MAX_STATE;
data->pwm_separate = false;
for (i = 0; i < EMC2305_PWM_MAX; i++)
data->pwm_min[i] = EMC2305_FAN_MIN;
}
data->hwmon_dev = devm_hwmon_device_register_with_info(dev, "emc2305", data,
&emc2305_chip_info, NULL);
if (IS_ERR(data->hwmon_dev))
return PTR_ERR(data->hwmon_dev);
if (IS_REACHABLE(CONFIG_THERMAL)) {
ret = emc2305_set_tz(dev);
if (ret != 0)
return ret;
}
for (i = 0; i < data->pwm_num; i++) {
ret = i2c_smbus_write_byte_data(client, EMC2305_REG_FAN_MIN_DRIVE(i),
data->pwm_min[i]);
if (ret < 0)
return ret;
}
return 0;
}
static void emc2305_remove(struct i2c_client *client)
{
struct device *dev = &client->dev;
if (IS_REACHABLE(CONFIG_THERMAL))
emc2305_unset_tz(dev);
}
static struct i2c_driver emc2305_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "emc2305",
},
.probe = emc2305_probe,
.remove = emc2305_remove,
.id_table = emc2305_ids,
.address_list = emc2305_normal_i2c,
};
module_i2c_driver(emc2305_driver);
MODULE_AUTHOR("Nvidia");
MODULE_DESCRIPTION("Microchip EMC2305 fan controller driver");
MODULE_LICENSE("GPL");

View File

@ -439,7 +439,7 @@ static int emc6w201_detect(struct i2c_client *client,
return -ENODEV;
}
strlcpy(info->type, "emc6w201", I2C_NAME_SIZE);
strscpy(info->type, "emc6w201", I2C_NAME_SIZE);
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -896,7 +896,7 @@ static int f75375_detect(struct i2c_client *client,
version = f75375_read8(client, F75375_REG_VERSION);
dev_info(&adapter->dev, "found %s version: %02X\n", name, version);
strlcpy(info->type, name, I2C_NAME_SIZE);
strscpy(info->type, name, I2C_NAME_SIZE);
return 0;
}

View File

@ -1075,7 +1075,7 @@ static int fschmd_detect(struct i2c_client *client,
else
return -ENODEV;
strlcpy(info->type, fschmd_id[kind].name, I2C_NAME_SIZE);
strscpy(info->type, fschmd_id[kind].name, I2C_NAME_SIZE);
return 0;
}

View File

@ -739,7 +739,7 @@ static int fts_detect(struct i2c_client *client,
if (val != 0x11)
return -ENODEV;
strlcpy(info->type, fts_id[0].name, I2C_NAME_SIZE);
strscpy(info->type, fts_id[0].name, I2C_NAME_SIZE);
info->flags = 0;
return 0;
}

View File

@ -586,7 +586,7 @@ static int gl518_detect(struct i2c_client *client, struct i2c_board_info *info)
if (rev != 0x00 && rev != 0x80)
return -ENODEV;
strlcpy(info->type, "gl518sm", I2C_NAME_SIZE);
strscpy(info->type, "gl518sm", I2C_NAME_SIZE);
return 0;
}

View File

@ -811,7 +811,7 @@ static int gl520_detect(struct i2c_client *client, struct i2c_board_info *info)
return -ENODEV;
}
strlcpy(info->type, "gl520sm", I2C_NAME_SIZE);
strscpy(info->type, "gl520sm", I2C_NAME_SIZE);
return 0;
}

View File

@ -37,9 +37,7 @@ struct gpio_fan_data {
int num_speed;
struct gpio_fan_speed *speed;
int speed_index;
#ifdef CONFIG_PM_SLEEP
int resume_speed;
#endif
bool pwm_enable;
struct gpio_desc *alarm_gpio;
struct work_struct alarm_work;
@ -557,7 +555,6 @@ static void gpio_fan_shutdown(struct platform_device *pdev)
set_fan_speed(fan_data, 0);
}
#ifdef CONFIG_PM_SLEEP
static int gpio_fan_suspend(struct device *dev)
{
struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
@ -580,18 +577,14 @@ static int gpio_fan_resume(struct device *dev)
return 0;
}
static SIMPLE_DEV_PM_OPS(gpio_fan_pm, gpio_fan_suspend, gpio_fan_resume);
#define GPIO_FAN_PM (&gpio_fan_pm)
#else
#define GPIO_FAN_PM NULL
#endif
static DEFINE_SIMPLE_DEV_PM_OPS(gpio_fan_pm, gpio_fan_suspend, gpio_fan_resume);
static struct platform_driver gpio_fan_driver = {
.probe = gpio_fan_probe,
.shutdown = gpio_fan_shutdown,
.driver = {
.name = "gpio-fan",
.pm = GPIO_FAN_PM,
.pm = pm_sleep_ptr(&gpio_fan_pm),
.of_match_table = of_match_ptr(of_gpio_fan_match),
},
};

View File

@ -267,6 +267,7 @@ gsc_hwmon_get_devtree_pdata(struct device *dev)
pdata->nchannels = nchannels;
/* fan controller base address */
of_node_get(dev->parent->of_node);
fan = of_find_compatible_node(dev->parent->of_node, NULL, "gw,gsc-fan");
if (fan && of_property_read_u32(fan, "reg", &pdata->fan_base)) {
of_node_put(fan);

View File

@ -6,11 +6,13 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/hwmon.h>
#include <linux/of.h>
#include <linux/hwmon-sysfs.h>
#include <linux/iio/consumer.h>
#include <linux/iio/types.h>
@ -149,8 +151,8 @@ static int iio_hwmon_probe(struct platform_device *pdev)
st->attr_group.attrs = st->attrs;
st->groups[0] = &st->attr_group;
if (dev->of_node) {
sname = devm_kasprintf(dev, GFP_KERNEL, "%pOFn", dev->of_node);
if (dev_fwnode(dev)) {
sname = devm_kasprintf(dev, GFP_KERNEL, "%pfwP", dev_fwnode(dev));
if (!sname)
return -ENOMEM;
strreplace(sname, '-', '_');

View File

@ -928,7 +928,7 @@ static void ina3221_remove(struct i2c_client *client)
mutex_destroy(&ina->lock);
}
static int __maybe_unused ina3221_suspend(struct device *dev)
static int ina3221_suspend(struct device *dev)
{
struct ina3221_data *ina = dev_get_drvdata(dev);
int ret;
@ -951,7 +951,7 @@ static int __maybe_unused ina3221_suspend(struct device *dev)
return 0;
}
static int __maybe_unused ina3221_resume(struct device *dev)
static int ina3221_resume(struct device *dev)
{
struct ina3221_data *ina = dev_get_drvdata(dev);
int ret;
@ -994,11 +994,8 @@ static int __maybe_unused ina3221_resume(struct device *dev)
return 0;
}
static const struct dev_pm_ops ina3221_pm = {
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(ina3221_suspend, ina3221_resume, NULL)
};
static DEFINE_RUNTIME_DEV_PM_OPS(ina3221_pm, ina3221_suspend, ina3221_resume,
NULL);
static const struct of_device_id ina3221_of_match_table[] = {
{ .compatible = "ti,ina3221", },
@ -1018,7 +1015,7 @@ static struct i2c_driver ina3221_i2c_driver = {
.driver = {
.name = INA3221_DRIVER_NAME,
.of_match_table = ina3221_of_match_table,
.pm = &ina3221_pm,
.pm = pm_ptr(&ina3221_pm),
},
.id_table = ina3221_ids,
};

View File

@ -3179,7 +3179,7 @@ static int it87_probe(struct platform_device *pdev)
return PTR_ERR_OR_ZERO(hwmon_dev);
}
static void __maybe_unused it87_resume_sio(struct platform_device *pdev)
static void it87_resume_sio(struct platform_device *pdev)
{
struct it87_data *data = dev_get_drvdata(&pdev->dev);
int err;
@ -3211,7 +3211,7 @@ static void __maybe_unused it87_resume_sio(struct platform_device *pdev)
superio_exit(data->sioaddr);
}
static int __maybe_unused it87_resume(struct device *dev)
static int it87_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct it87_data *data = dev_get_drvdata(dev);
@ -3238,12 +3238,12 @@ static int __maybe_unused it87_resume(struct device *dev)
return 0;
}
static SIMPLE_DEV_PM_OPS(it87_dev_pm_ops, NULL, it87_resume);
static DEFINE_SIMPLE_DEV_PM_OPS(it87_dev_pm_ops, NULL, it87_resume);
static struct platform_driver it87_driver = {
.driver = {
.name = DRVNAME,
.pm = &it87_dev_pm_ops,
.pm = pm_sleep_ptr(&it87_dev_pm_ops),
},
.probe = it87_probe,
};

View File

@ -441,7 +441,7 @@ static int jc42_detect(struct i2c_client *client, struct i2c_board_info *info)
struct jc42_chips *chip = &jc42_chips[i];
if (manid == chip->manid &&
(devid & chip->devid_mask) == chip->devid) {
strlcpy(info->type, "jc42", I2C_NAME_SIZE);
strscpy(info->type, "jc42", I2C_NAME_SIZE);
return 0;
}
}

View File

@ -996,11 +996,11 @@ static int lm63_detect(struct i2c_client *client,
}
if (chip_id == 0x41 && address == 0x4c)
strlcpy(info->type, "lm63", I2C_NAME_SIZE);
strscpy(info->type, "lm63", I2C_NAME_SIZE);
else if (chip_id == 0x51 && (address == 0x18 || address == 0x4e))
strlcpy(info->type, "lm64", I2C_NAME_SIZE);
strscpy(info->type, "lm64", I2C_NAME_SIZE);
else if (chip_id == 0x49 && address == 0x4c)
strlcpy(info->type, "lm96163", I2C_NAME_SIZE);
strscpy(info->type, "lm96163", I2C_NAME_SIZE);
else
return -ENODEV;

View File

@ -257,7 +257,7 @@ static int lm73_detect(struct i2c_client *new_client,
if (id < 0 || id != LM73_ID)
return -ENODEV;
strlcpy(info->type, "lm73", I2C_NAME_SIZE);
strscpy(info->type, "lm73", I2C_NAME_SIZE);
return 0;
}

View File

@ -893,7 +893,7 @@ static int lm75_detect(struct i2c_client *new_client,
return -ENODEV;
}
strlcpy(info->type, is_lm75a ? "lm75a" : "lm75", I2C_NAME_SIZE);
strscpy(info->type, is_lm75a ? "lm75a" : "lm75", I2C_NAME_SIZE);
return 0;
}

View File

@ -302,7 +302,7 @@ static int lm77_detect(struct i2c_client *client, struct i2c_board_info *info)
|| i2c_smbus_read_word_data(client, 7) != min)
return -ENODEV;
strlcpy(info->type, "lm77", I2C_NAME_SIZE);
strscpy(info->type, "lm77", I2C_NAME_SIZE);
return 0;
}

View File

@ -617,7 +617,7 @@ static int lm78_i2c_detect(struct i2c_client *client,
if (isa)
mutex_unlock(&isa->update_lock);
strlcpy(info->type, client_name, I2C_NAME_SIZE);
strscpy(info->type, client_name, I2C_NAME_SIZE);
return 0;

View File

@ -586,7 +586,7 @@ static int lm80_detect(struct i2c_client *client, struct i2c_board_info *info)
name = "lm80";
}
strlcpy(info->type, name, I2C_NAME_SIZE);
strscpy(info->type, name, I2C_NAME_SIZE);
return 0;
}

View File

@ -412,7 +412,7 @@ static int lm83_detect(struct i2c_client *client,
return -ENODEV;
}
strlcpy(info->type, name, I2C_NAME_SIZE);
strscpy(info->type, name, I2C_NAME_SIZE);
return 0;
}

View File

@ -1539,7 +1539,7 @@ static int lm85_detect(struct i2c_client *client, struct i2c_board_info *info)
if (!type_name)
return -ENODEV;
strlcpy(info->type, type_name, I2C_NAME_SIZE);
strscpy(info->type, type_name, I2C_NAME_SIZE);
return 0;
}

View File

@ -833,7 +833,7 @@ static int lm87_detect(struct i2c_client *client, struct i2c_board_info *info)
return -ENODEV;
}
strlcpy(info->type, name, I2C_NAME_SIZE);
strscpy(info->type, name, I2C_NAME_SIZE);
return 0;
}

View File

@ -2547,7 +2547,7 @@ static int lm90_detect(struct i2c_client *client, struct i2c_board_info *info)
return -ENODEV;
}
strlcpy(info->type, name, I2C_NAME_SIZE);
strscpy(info->type, name, I2C_NAME_SIZE);
return 0;
}
@ -2956,7 +2956,7 @@ static void lm90_alert(struct i2c_client *client, enum i2c_alert_protocol type,
}
}
static int __maybe_unused lm90_suspend(struct device *dev)
static int lm90_suspend(struct device *dev)
{
struct lm90_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
@ -2967,7 +2967,7 @@ static int __maybe_unused lm90_suspend(struct device *dev)
return 0;
}
static int __maybe_unused lm90_resume(struct device *dev)
static int lm90_resume(struct device *dev)
{
struct lm90_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
@ -2978,14 +2978,14 @@ static int __maybe_unused lm90_resume(struct device *dev)
return 0;
}
static SIMPLE_DEV_PM_OPS(lm90_pm_ops, lm90_suspend, lm90_resume);
static DEFINE_SIMPLE_DEV_PM_OPS(lm90_pm_ops, lm90_suspend, lm90_resume);
static struct i2c_driver lm90_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "lm90",
.of_match_table = of_match_ptr(lm90_of_match),
.pm = &lm90_pm_ops,
.pm = pm_sleep_ptr(&lm90_pm_ops),
},
.probe_new = lm90_probe,
.alert = lm90_alert,

View File

@ -287,7 +287,7 @@ static int lm92_detect(struct i2c_client *new_client,
else
return -ENODEV;
strlcpy(info->type, "lm92", I2C_NAME_SIZE);
strscpy(info->type, "lm92", I2C_NAME_SIZE);
return 0;
}

View File

@ -2575,7 +2575,7 @@ static int lm93_detect(struct i2c_client *client, struct i2c_board_info *info)
return -ENODEV;
}
strlcpy(info->type, name, I2C_NAME_SIZE);
strscpy(info->type, name, I2C_NAME_SIZE);
dev_dbg(&adapter->dev, "loading %s at %d, 0x%02x\n",
client->name, i2c_adapter_id(client->adapter),
client->addr);

View File

@ -644,7 +644,7 @@ static int lm95234_detect(struct i2c_client *client,
if (val & model_mask)
return -ENODEV;
strlcpy(info->type, name, I2C_NAME_SIZE);
strscpy(info->type, name, I2C_NAME_SIZE);
return 0;
}

View File

@ -389,7 +389,7 @@ static int lm95241_detect(struct i2c_client *new_client,
}
/* Fill the i2c board info */
strlcpy(info->type, name, I2C_NAME_SIZE);
strscpy(info->type, name, I2C_NAME_SIZE);
return 0;
}

View File

@ -461,7 +461,7 @@ static int lm95245_detect(struct i2c_client *new_client,
return -ENODEV;
}
strlcpy(info->type, name, I2C_NAME_SIZE);
strscpy(info->type, name, I2C_NAME_SIZE);
return 0;
}

View File

@ -956,13 +956,6 @@ static struct attribute *ltc2947_attrs[] = {
};
ATTRIBUTE_GROUPS(ltc2947);
static void ltc2947_clk_disable(void *data)
{
struct clk *extclk = data;
clk_disable_unprepare(extclk);
}
static int ltc2947_setup(struct ltc2947_data *st)
{
int ret;
@ -989,7 +982,7 @@ static int ltc2947_setup(struct ltc2947_data *st)
return ret;
/* check external clock presence */
extclk = devm_clk_get_optional(st->dev, NULL);
extclk = devm_clk_get_optional_enabled(st->dev, NULL);
if (IS_ERR(extclk))
return dev_err_probe(st->dev, PTR_ERR(extclk),
"Failed to get external clock\n");
@ -1007,14 +1000,6 @@ static int ltc2947_setup(struct ltc2947_data *st)
return -EINVAL;
}
ret = clk_prepare_enable(extclk);
if (ret)
return ret;
ret = devm_add_action_or_reset(st->dev, ltc2947_clk_disable,
extclk);
if (ret)
return ret;
/* as in table 1 of the datasheet */
if (rate_hz >= LTC2947_CLK_MIN && rate_hz <= 1000000)
pre = 0;
@ -1135,7 +1120,7 @@ int ltc2947_core_probe(struct regmap *map, const char *name)
}
EXPORT_SYMBOL_GPL(ltc2947_core_probe);
static int __maybe_unused ltc2947_resume(struct device *dev)
static int ltc2947_resume(struct device *dev)
{
struct ltc2947_data *st = dev_get_drvdata(dev);
u32 ctrl = 0;
@ -1164,7 +1149,7 @@ static int __maybe_unused ltc2947_resume(struct device *dev)
LTC2947_CONT_MODE_MASK, LTC2947_CONT_MODE(1));
}
static int __maybe_unused ltc2947_suspend(struct device *dev)
static int ltc2947_suspend(struct device *dev)
{
struct ltc2947_data *st = dev_get_drvdata(dev);
@ -1172,8 +1157,7 @@ static int __maybe_unused ltc2947_suspend(struct device *dev)
LTC2947_SHUTDOWN_MASK, 1);
}
SIMPLE_DEV_PM_OPS(ltc2947_pm_ops, ltc2947_suspend, ltc2947_resume);
EXPORT_SYMBOL_GPL(ltc2947_pm_ops);
EXPORT_SIMPLE_DEV_PM_OPS(ltc2947_pm_ops, ltc2947_suspend, ltc2947_resume);
const struct of_device_id ltc2947_of_match[] = {
{ .compatible = "adi,ltc2947" },

View File

@ -36,7 +36,7 @@ static struct i2c_driver ltc2947_driver = {
.driver = {
.name = "ltc2947",
.of_match_table = ltc2947_of_match,
.pm = &ltc2947_pm_ops,
.pm = pm_sleep_ptr(&ltc2947_pm_ops),
},
.probe_new = ltc2947_probe,
.id_table = ltc2947_id,

View File

@ -38,7 +38,7 @@ static struct spi_driver ltc2947_driver = {
.driver = {
.name = "ltc2947",
.of_match_table = ltc2947_of_match,
.pm = &ltc2947_pm_ops,
.pm = pm_sleep_ptr(&ltc2947_pm_ops),
},
.probe = ltc2947_probe,
.id_table = ltc2947_id,

View File

@ -241,7 +241,7 @@ static int max1619_detect(struct i2c_client *client,
return -ENODEV;
}
strlcpy(info->type, "max1619", I2C_NAME_SIZE);
strscpy(info->type, "max1619", I2C_NAME_SIZE);
return 0;
}

View File

@ -386,7 +386,7 @@ static int max1668_detect(struct i2c_client *client,
if (!type_name)
return -ENODEV;
strlcpy(info->type, type_name, I2C_NAME_SIZE);
strscpy(info->type, type_name, I2C_NAME_SIZE);
return 0;
}

View File

@ -113,7 +113,7 @@ static void max31722_remove(struct spi_device *spi)
dev_warn(&spi->dev, "Failed to put device in stand-by mode\n");
}
static int __maybe_unused max31722_suspend(struct device *dev)
static int max31722_suspend(struct device *dev)
{
struct spi_device *spi_device = to_spi_device(dev);
struct max31722_data *data = spi_get_drvdata(spi_device);
@ -121,7 +121,7 @@ static int __maybe_unused max31722_suspend(struct device *dev)
return max31722_set_mode(data, MAX31722_MODE_STANDBY);
}
static int __maybe_unused max31722_resume(struct device *dev)
static int max31722_resume(struct device *dev)
{
struct spi_device *spi_device = to_spi_device(dev);
struct max31722_data *data = spi_get_drvdata(spi_device);
@ -129,7 +129,7 @@ static int __maybe_unused max31722_resume(struct device *dev)
return max31722_set_mode(data, MAX31722_MODE_CONTINUOUS);
}
static SIMPLE_DEV_PM_OPS(max31722_pm_ops, max31722_suspend, max31722_resume);
static DEFINE_SIMPLE_DEV_PM_OPS(max31722_pm_ops, max31722_suspend, max31722_resume);
static const struct spi_device_id max31722_spi_id[] = {
{"max31722", 0},
@ -141,7 +141,7 @@ MODULE_DEVICE_TABLE(spi, max31722_spi_id);
static struct spi_driver max31722_driver = {
.driver = {
.name = "max31722",
.pm = &max31722_pm_ops,
.pm = pm_sleep_ptr(&max31722_pm_ops),
},
.probe = max31722_probe,
.remove = max31722_remove,

View File

@ -399,33 +399,33 @@ static int max31730_detect(struct i2c_client *client,
return -ENODEV;
}
strlcpy(info->type, "max31730", I2C_NAME_SIZE);
strscpy(info->type, "max31730", I2C_NAME_SIZE);
return 0;
}
static int __maybe_unused max31730_suspend(struct device *dev)
static int max31730_suspend(struct device *dev)
{
struct max31730_data *data = dev_get_drvdata(dev);
return max31730_write_config(data, MAX31730_STOP, 0);
}
static int __maybe_unused max31730_resume(struct device *dev)
static int max31730_resume(struct device *dev)
{
struct max31730_data *data = dev_get_drvdata(dev);
return max31730_write_config(data, 0, MAX31730_STOP);
}
static SIMPLE_DEV_PM_OPS(max31730_pm_ops, max31730_suspend, max31730_resume);
static DEFINE_SIMPLE_DEV_PM_OPS(max31730_pm_ops, max31730_suspend, max31730_resume);
static struct i2c_driver max31730_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "max31730",
.of_match_table = of_match_ptr(max31730_of_match),
.pm = &max31730_pm_ops,
.pm = pm_sleep_ptr(&max31730_pm_ops),
},
.probe_new = max31730_probe,
.id_table = max31730_ids,

596
drivers/hwmon/max31760.c Normal file
View File

@ -0,0 +1,596 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/err.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/util_macros.h>
#define REG_CR1 0x00
#define CR1_HYST BIT(5)
#define CR1_DRV GENMASK(4, 3)
#define CR1_TEMP_SRC GENMASK(1, 0)
#define REG_CR2 0x01
#define CR2_STBY BIT(7)
#define CR2_ALERTS BIT(6)
#define CR2_DFC BIT(0)
#define REG_CR3 0x02
#define REG_PWMR 0x50
#define REG_PWMV 0x51
#define REG_STATUS 0x5A
#define STATUS_ALARM_CRIT(ch) BIT(2 + 2 * (ch))
#define STATUS_ALARM_MAX(ch) BIT(3 + 2 * (ch))
#define STATUS_RDFA BIT(6)
#define REG_TACH(ch) (0x52 + (ch) * 2)
#define REG_TEMP_INPUT(ch) (0x56 + (ch) * 2)
#define REG_TEMP_MAX(ch) (0x06 + (ch) * 2)
#define REG_TEMP_CRIT(ch) (0x0A + (ch) * 2)
#define TEMP11_FROM_REG(reg) ((reg) / 32 * 125)
#define TEMP11_TO_REG(val) (DIV_ROUND_CLOSEST(clamp_val((val), -128000, \
127875), 125) * 32)
#define LUT_SIZE 48
#define REG_LUT(index) (0x20 + (index))
struct max31760_state {
struct regmap *regmap;
struct lut_attribute {
char name[24];
struct sensor_device_attribute sda;
} lut[LUT_SIZE];
struct attribute *attrs[LUT_SIZE + 2];
struct attribute_group group;
const struct attribute_group *groups[2];
};
static bool max31760_volatile_reg(struct device *dev, unsigned int reg)
{
return reg > 0x50;
}
static const struct regmap_config regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0x5B,
.cache_type = REGCACHE_RBTREE,
.volatile_reg = max31760_volatile_reg,
};
static const int max31760_pwm_freq[] = {33, 150, 1500, 25000};
static int tach_to_rpm(u16 tach)
{
if (tach == 0)
tach = 1;
return 60 * 100000 / tach / 2;
}
static int max31760_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
struct max31760_state *state = dev_get_drvdata(dev);
unsigned int regval;
unsigned int reg_temp;
s16 temp;
u8 reg[2];
int ret;
switch (type) {
case hwmon_temp:
switch (attr) {
case hwmon_temp_fault:
ret = regmap_read(state->regmap, REG_STATUS, &regval);
if (ret)
return ret;
*val = FIELD_GET(STATUS_RDFA, regval);
return 0;
case hwmon_temp_max_alarm:
ret = regmap_read(state->regmap, REG_STATUS, &regval);
if (ret)
return ret;
if (channel)
*val = FIELD_GET(STATUS_ALARM_MAX(1), regval);
else
*val = FIELD_GET(STATUS_ALARM_MAX(0), regval);
return 0;
case hwmon_temp_crit_alarm:
ret = regmap_read(state->regmap, REG_STATUS, &regval);
if (ret)
return ret;
if (channel)
*val = FIELD_GET(STATUS_ALARM_CRIT(1), regval);
else
*val = FIELD_GET(STATUS_ALARM_CRIT(0), regval);
return 0;
case hwmon_temp_input:
reg_temp = REG_TEMP_INPUT(channel);
break;
case hwmon_temp_max:
reg_temp = REG_TEMP_MAX(channel);
break;
case hwmon_temp_crit:
reg_temp = REG_TEMP_CRIT(channel);
break;
default:
return -EOPNOTSUPP;
}
ret = regmap_bulk_read(state->regmap, reg_temp, reg, 2);
if (ret)
return ret;
temp = (reg[0] << 8) | reg[1];
*val = TEMP11_FROM_REG(temp);
return 0;
case hwmon_fan:
switch (attr) {
case hwmon_fan_input:
ret = regmap_bulk_read(state->regmap, REG_TACH(channel), reg, 2);
if (ret)
return ret;
*val = tach_to_rpm(reg[0] * 256 + reg[1]);
return 0;
case hwmon_fan_fault:
ret = regmap_read(state->regmap, REG_STATUS, &regval);
if (ret)
return ret;
if (channel)
*val = FIELD_GET(BIT(1), regval);
else
*val = FIELD_GET(BIT(0), regval);
return 0;
case hwmon_fan_enable:
ret = regmap_read(state->regmap, REG_CR3, &regval);
if (ret)
return ret;
if (channel)
*val = FIELD_GET(BIT(1), regval);
else
*val = FIELD_GET(BIT(0), regval);
return 0;
default:
return -EOPNOTSUPP;
}
case hwmon_pwm:
switch (attr) {
case hwmon_pwm_input:
ret = regmap_read(state->regmap, REG_PWMV, &regval);
if (ret)
return ret;
*val = regval;
return 0;
case hwmon_pwm_freq:
ret = regmap_read(state->regmap, REG_CR1, &regval);
if (ret)
return ret;
regval = FIELD_GET(CR1_DRV, regval);
if (regval >= ARRAY_SIZE(max31760_pwm_freq))
return -EINVAL;
*val = max31760_pwm_freq[regval];
return 0;
case hwmon_pwm_enable:
ret = regmap_read(state->regmap, REG_CR2, &regval);
if (ret)
return ret;
*val = 2 - FIELD_GET(CR2_DFC, regval);
return 0;
case hwmon_pwm_auto_channels_temp:
ret = regmap_read(state->regmap, REG_CR1, &regval);
if (ret)
return ret;
switch (FIELD_GET(CR1_TEMP_SRC, regval)) {
case 0:
*val = 2;
break;
case 1:
*val = 1;
break;
case 2:
case 3:
*val = 3;
break;
default:
return -EINVAL;
}
return 0;
default:
return -EOPNOTSUPP;
}
default:
return -EOPNOTSUPP;
}
}
static int max31760_write(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long val)
{
struct max31760_state *state = dev_get_drvdata(dev);
unsigned int pwm_index;
unsigned int reg_temp;
int temp;
u8 reg_val[2];
switch (type) {
case hwmon_temp:
switch (attr) {
case hwmon_temp_max:
reg_temp = REG_TEMP_MAX(channel);
break;
case hwmon_temp_crit:
reg_temp = REG_TEMP_CRIT(channel);
break;
default:
return -EOPNOTSUPP;
}
temp = TEMP11_TO_REG(val);
reg_val[0] = temp >> 8;
reg_val[1] = temp & 0xFF;
return regmap_bulk_write(state->regmap, reg_temp, reg_val, 2);
case hwmon_fan:
switch (attr) {
case hwmon_fan_enable:
if (val == 0)
return regmap_clear_bits(state->regmap, REG_CR3, BIT(channel));
if (val == 1)
return regmap_set_bits(state->regmap, REG_CR3, BIT(channel));
return -EINVAL;
default:
return -EOPNOTSUPP;
}
case hwmon_pwm:
switch (attr) {
case hwmon_pwm_input:
if (val < 0 || val > 255)
return -EINVAL;
return regmap_write(state->regmap, REG_PWMR, val);
case hwmon_pwm_enable:
if (val == 1)
return regmap_set_bits(state->regmap, REG_CR2, CR2_DFC);
if (val == 2)
return regmap_clear_bits(state->regmap, REG_CR2, CR2_DFC);
return -EINVAL;
case hwmon_pwm_freq:
pwm_index = find_closest(val, max31760_pwm_freq,
ARRAY_SIZE(max31760_pwm_freq));
return regmap_update_bits(state->regmap,
REG_CR1, CR1_DRV,
FIELD_PREP(CR1_DRV, pwm_index));
case hwmon_pwm_auto_channels_temp:
switch (val) {
case 1:
break;
case 2:
val = 0;
break;
case 3:
val = 2;
break;
default:
return -EINVAL;
}
return regmap_update_bits(state->regmap, REG_CR1, CR1_TEMP_SRC, val);
default:
return -EOPNOTSUPP;
}
default:
return -EOPNOTSUPP;
}
}
static const struct hwmon_channel_info *max31760_info[] = {
HWMON_CHANNEL_INFO(chip,
HWMON_C_REGISTER_TZ),
HWMON_CHANNEL_INFO(fan,
HWMON_F_INPUT | HWMON_F_FAULT | HWMON_F_ENABLE,
HWMON_F_INPUT | HWMON_F_FAULT | HWMON_F_ENABLE),
HWMON_CHANNEL_INFO(temp,
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT |
HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | HWMON_T_LABEL),
HWMON_CHANNEL_INFO(pwm,
HWMON_PWM_ENABLE | HWMON_PWM_FREQ | HWMON_PWM_INPUT |
HWMON_PWM_AUTO_CHANNELS_TEMP),
NULL
};
static umode_t max31760_is_visible(const void *data,
enum hwmon_sensor_types type,
u32 attr, int channel)
{
switch (type) {
case hwmon_temp:
switch (attr) {
case hwmon_temp_input:
case hwmon_temp_max_alarm:
case hwmon_temp_crit_alarm:
case hwmon_temp_fault:
case hwmon_temp_label:
return 0444;
case hwmon_temp_max:
case hwmon_temp_crit:
return 0644;
default:
return 0;
}
case hwmon_fan:
switch (attr) {
case hwmon_fan_input:
case hwmon_fan_fault:
return 0444;
case hwmon_fan_enable:
return 0644;
default:
return 0;
}
case hwmon_pwm:
switch (attr) {
case hwmon_pwm_enable:
case hwmon_pwm_input:
case hwmon_pwm_freq:
case hwmon_pwm_auto_channels_temp:
return 0644;
default:
return 0;
}
default:
return 0;
}
}
static int max31760_read_string(struct device *dev,
enum hwmon_sensor_types type,
u32 attr, int channel, const char **str)
{
switch (type) {
case hwmon_temp:
if (attr != hwmon_temp_label)
return -EOPNOTSUPP;
*str = channel ? "local" : "remote";
return 0;
default:
return -EOPNOTSUPP;
}
}
static const struct hwmon_ops max31760_hwmon_ops = {
.is_visible = max31760_is_visible,
.read = max31760_read,
.write = max31760_write,
.read_string = max31760_read_string
};
static const struct hwmon_chip_info max31760_chip_info = {
.ops = &max31760_hwmon_ops,
.info = max31760_info,
};
static ssize_t lut_show(struct device *dev,
struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr);
struct max31760_state *state = dev_get_drvdata(dev);
int ret;
unsigned int regval;
ret = regmap_read(state->regmap, REG_LUT(sda->index), &regval);
if (ret)
return ret;
return sysfs_emit(buf, "%d\n", regval);
}
static ssize_t lut_store(struct device *dev,
struct device_attribute *devattr,
const char *buf, size_t count)
{
struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr);
struct max31760_state *state = dev_get_drvdata(dev);
int ret;
u8 pwm;
ret = kstrtou8(buf, 10, &pwm);
if (ret)
return ret;
ret = regmap_write(state->regmap, REG_LUT(sda->index), pwm);
if (ret)
return ret;
return count;
}
static ssize_t pwm1_auto_point_temp_hyst_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct max31760_state *state = dev_get_drvdata(dev);
unsigned int regval;
int ret;
ret = regmap_read(state->regmap, REG_CR1, &regval);
if (ret)
return ret;
return sysfs_emit(buf, "%d\n", (1 + (int)FIELD_GET(CR1_HYST, regval)) * 2000);
}
static ssize_t pwm1_auto_point_temp_hyst_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
struct max31760_state *state = dev_get_drvdata(dev);
unsigned int hyst;
int ret;
ret = kstrtou32(buf, 10, &hyst);
if (ret)
return ret;
if (hyst < 3000)
ret = regmap_clear_bits(state->regmap, REG_CR1, CR1_HYST);
else
ret = regmap_set_bits(state->regmap, REG_CR1, CR1_HYST);
if (ret)
return ret;
return count;
}
static DEVICE_ATTR_RW(pwm1_auto_point_temp_hyst);
static void max31760_create_lut_nodes(struct max31760_state *state)
{
int i;
struct sensor_device_attribute *sda;
struct lut_attribute *lut;
for (i = 0; i < LUT_SIZE; ++i) {
lut = &state->lut[i];
sda = &lut->sda;
snprintf(lut->name, sizeof(lut->name),
"pwm1_auto_point%d_pwm", i + 1);
sda->dev_attr.attr.mode = 0644;
sda->index = i;
sda->dev_attr.show = lut_show;
sda->dev_attr.store = lut_store;
sda->dev_attr.attr.name = lut->name;
sysfs_attr_init(&sda->dev_attr.attr);
state->attrs[i] = &sda->dev_attr.attr;
}
state->attrs[i] = &dev_attr_pwm1_auto_point_temp_hyst.attr;
state->group.attrs = state->attrs;
state->groups[0] = &state->group;
}
static int max31760_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct max31760_state *state;
struct device *hwmon_dev;
int ret;
state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
if (!state)
return -ENOMEM;
state->regmap = devm_regmap_init_i2c(client, &regmap_config);
if (IS_ERR(state->regmap))
return dev_err_probe(dev,
PTR_ERR(state->regmap),
"regmap initialization failed\n");
dev_set_drvdata(dev, state);
/* Set alert output to comparator mode */
ret = regmap_set_bits(state->regmap, REG_CR2, CR2_ALERTS);
if (ret)
return dev_err_probe(dev, ret, "cannot write register\n");
max31760_create_lut_nodes(state);
hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
state,
&max31760_chip_info,
state->groups);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
static const struct of_device_id max31760_of_match[] = {
{.compatible = "adi,max31760"},
{ }
};
MODULE_DEVICE_TABLE(of, max31760_of_match);
static const struct i2c_device_id max31760_id[] = {
{"max31760"},
{ }
};
MODULE_DEVICE_TABLE(i2c, max31760_id);
static int max31760_suspend(struct device *dev)
{
struct max31760_state *state = dev_get_drvdata(dev);
return regmap_set_bits(state->regmap, REG_CR2, CR2_STBY);
}
static int max31760_resume(struct device *dev)
{
struct max31760_state *state = dev_get_drvdata(dev);
return regmap_clear_bits(state->regmap, REG_CR2, CR2_STBY);
}
static DEFINE_SIMPLE_DEV_PM_OPS(max31760_pm_ops, max31760_suspend,
max31760_resume);
static struct i2c_driver max31760_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "max31760",
.of_match_table = max31760_of_match,
.pm = pm_ptr(&max31760_pm_ops)
},
.probe_new = max31760_probe,
.id_table = max31760_id
};
module_i2c_driver(max31760_driver);
MODULE_AUTHOR("Ibrahim Tilki <Ibrahim.Tilki@analog.com>");
MODULE_DESCRIPTION("Analog Devices MAX31760 Fan Speed Controller");
MODULE_SOFTDEP("pre: regmap_i2c");
MODULE_VERSION("1.0");
MODULE_LICENSE("GPL");

View File

@ -202,6 +202,9 @@ static int max31790_read_fan(struct device *dev, u32 attr, int channel,
}
mutex_unlock(&data->update_lock);
return 0;
case hwmon_fan_enable:
*val = !!(data->fan_config[channel] & MAX31790_FAN_CFG_TACH_INPUT_EN);
return 0;
default:
return -EOPNOTSUPP;
}
@ -214,7 +217,7 @@ static int max31790_write_fan(struct device *dev, u32 attr, int channel,
struct i2c_client *client = data->client;
int target_count;
int err = 0;
u8 bits;
u8 bits, fan_config;
int sr;
mutex_lock(&data->update_lock);
@ -243,6 +246,23 @@ static int max31790_write_fan(struct device *dev, u32 attr, int channel,
MAX31790_REG_TARGET_COUNT(channel),
data->target_count[channel]);
break;
case hwmon_fan_enable:
fan_config = data->fan_config[channel];
if (val == 0) {
fan_config &= ~MAX31790_FAN_CFG_TACH_INPUT_EN;
} else if (val == 1) {
fan_config |= MAX31790_FAN_CFG_TACH_INPUT_EN;
} else {
err = -EINVAL;
break;
}
if (fan_config != data->fan_config[channel]) {
err = i2c_smbus_write_byte_data(client, MAX31790_REG_FAN_CONFIG(channel),
fan_config);
if (!err)
data->fan_config[channel] = fan_config;
}
break;
default:
err = -EOPNOTSUPP;
break;
@ -270,6 +290,10 @@ static umode_t max31790_fan_is_visible(const void *_data, u32 attr, int channel)
!(fan_config & MAX31790_FAN_CFG_TACH_INPUT))
return 0644;
return 0;
case hwmon_fan_enable:
if (channel < NR_CHANNEL)
return 0644;
return 0;
default:
return 0;
}
@ -423,12 +447,12 @@ static umode_t max31790_is_visible(const void *data,
static const struct hwmon_channel_info *max31790_info[] = {
HWMON_CHANNEL_INFO(fan,
HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT,
HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT | HWMON_F_ENABLE,
HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT | HWMON_F_ENABLE,
HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT | HWMON_F_ENABLE,
HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT | HWMON_F_ENABLE,
HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT | HWMON_F_ENABLE,
HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_FAULT | HWMON_F_ENABLE,
HWMON_F_INPUT | HWMON_F_FAULT,
HWMON_F_INPUT | HWMON_F_FAULT,
HWMON_F_INPUT | HWMON_F_FAULT,

View File

@ -514,7 +514,7 @@ static int max6639_detect(struct i2c_client *client,
if (dev_id != 0x58 || manu_id != 0x4D)
return -ENODEV;
strlcpy(info->type, "max6639", I2C_NAME_SIZE);
strscpy(info->type, "max6639", I2C_NAME_SIZE);
return 0;
}
@ -571,7 +571,6 @@ static int max6639_probe(struct i2c_client *client)
return PTR_ERR_OR_ZERO(hwmon_dev);
}
#ifdef CONFIG_PM_SLEEP
static int max6639_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
@ -609,7 +608,6 @@ static int max6639_resume(struct device *dev)
return i2c_smbus_write_byte_data(client,
MAX6639_REG_GCONFIG, ret & ~MAX6639_GCONFIG_STANDBY);
}
#endif /* CONFIG_PM_SLEEP */
static const struct i2c_device_id max6639_id[] = {
{"max6639", 0},
@ -618,13 +616,13 @@ static const struct i2c_device_id max6639_id[] = {
MODULE_DEVICE_TABLE(i2c, max6639_id);
static SIMPLE_DEV_PM_OPS(max6639_pm_ops, max6639_suspend, max6639_resume);
static DEFINE_SIMPLE_DEV_PM_OPS(max6639_pm_ops, max6639_suspend, max6639_resume);
static struct i2c_driver max6639_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "max6639",
.pm = &max6639_pm_ops,
.pm = pm_sleep_ptr(&max6639_pm_ops),
},
.probe_new = max6639_probe,
.id_table = max6639_id,

View File

@ -148,7 +148,7 @@ static int max6642_detect(struct i2c_client *client,
if ((reg_status & 0x2b) != 0x00)
return -ENODEV;
strlcpy(info->type, "max6642", I2C_NAME_SIZE);
strscpy(info->type, "max6642", I2C_NAME_SIZE);
return 0;
}

View File

@ -9,6 +9,7 @@
*/
#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/debugfs.h>
#include <linux/hwmon.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
@ -17,6 +18,7 @@
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/slab.h>
#include <linux/units.h>
/* PVT Common register */
@ -30,6 +32,8 @@
#define CH_NUM_MSK GENMASK(31, 24)
#define CH_NUM_SFT 24
#define VM_NUM_MAX (VM_NUM_MSK >> VM_NUM_SFT)
/* Macro Common Register */
#define CLK_SYNTH 0x00
#define CLK_SYNTH_LO_SFT 0
@ -99,13 +103,67 @@
#define PVT_POLL_DELAY_US 20
#define PVT_POLL_TIMEOUT_US 20000
#define PVT_H_CONST 100000
#define PVT_CAL5_CONST 2047
#define PVT_G_CONST 40000
#define PVT_CONV_BITS 10
#define PVT_N_CONST 90
#define PVT_R_CONST 245805
#define PVT_TEMP_MIN_mC -40000
#define PVT_TEMP_MAX_mC 125000
/* Temperature coefficients for series 5 */
#define PVT_SERIES5_H_CONST 200000
#define PVT_SERIES5_G_CONST 60000
#define PVT_SERIES5_J_CONST -100
#define PVT_SERIES5_CAL5_CONST 4094
/* Temperature coefficients for series 6 */
#define PVT_SERIES6_H_CONST 249400
#define PVT_SERIES6_G_CONST 57400
#define PVT_SERIES6_J_CONST 0
#define PVT_SERIES6_CAL5_CONST 4096
#define TEMPERATURE_SENSOR_SERIES_5 5
#define TEMPERATURE_SENSOR_SERIES_6 6
#define PRE_SCALER_X1 1
#define PRE_SCALER_X2 2
/**
* struct voltage_device - VM single input parameters.
* @vm_map: Map channel number to VM index.
* @ch_map: Map channel number to channel index.
* @pre_scaler: Pre scaler value (1 or 2) used to normalize the voltage output
* result.
*
* The structure provides mapping between channel-number (0..N-1) to VM-index
* (0..num_vm-1) and channel-index (0..ch_num-1) where N = num_vm * ch_num.
* It also provides normalization factor for the VM equation.
*/
struct voltage_device {
u32 vm_map;
u32 ch_map;
u32 pre_scaler;
};
/**
* struct voltage_channels - VM channel count.
* @total: Total number of channels in all VMs.
* @max: Maximum number of channels among all VMs.
*
* The structure provides channel count information across all VMs.
*/
struct voltage_channels {
u32 total;
u8 max;
};
struct temp_coeff {
u32 h;
u32 g;
u32 cal5;
s32 j;
};
struct pvt_device {
struct regmap *c_map;
struct regmap *t_map;
@ -113,14 +171,74 @@ struct pvt_device {
struct regmap *v_map;
struct clk *clk;
struct reset_control *rst;
struct dentry *dbgfs_dir;
struct voltage_device *vd;
struct voltage_channels vm_channels;
struct temp_coeff ts_coeff;
u32 t_num;
u32 p_num;
u32 v_num;
u32 c_num;
u32 ip_freq;
u8 *vm_idx;
};
static ssize_t pvt_ts_coeff_j_read(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
struct pvt_device *pvt = file->private_data;
unsigned int len;
char buf[13];
len = scnprintf(buf, sizeof(buf), "%d\n", pvt->ts_coeff.j);
return simple_read_from_buffer(user_buf, count, ppos, buf, len);
}
static ssize_t pvt_ts_coeff_j_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct pvt_device *pvt = file->private_data;
int ret;
ret = kstrtos32_from_user(user_buf, count, 0, &pvt->ts_coeff.j);
if (ret)
return ret;
return count;
}
static const struct file_operations pvt_ts_coeff_j_fops = {
.read = pvt_ts_coeff_j_read,
.write = pvt_ts_coeff_j_write,
.open = simple_open,
.owner = THIS_MODULE,
.llseek = default_llseek,
};
static void devm_pvt_ts_dbgfs_remove(void *data)
{
struct pvt_device *pvt = (struct pvt_device *)data;
debugfs_remove_recursive(pvt->dbgfs_dir);
pvt->dbgfs_dir = NULL;
}
static int pvt_ts_dbgfs_create(struct pvt_device *pvt, struct device *dev)
{
pvt->dbgfs_dir = debugfs_create_dir(dev_name(dev), NULL);
debugfs_create_u32("ts_coeff_h", 0644, pvt->dbgfs_dir,
&pvt->ts_coeff.h);
debugfs_create_u32("ts_coeff_g", 0644, pvt->dbgfs_dir,
&pvt->ts_coeff.g);
debugfs_create_u32("ts_coeff_cal5", 0644, pvt->dbgfs_dir,
&pvt->ts_coeff.cal5);
debugfs_create_file("ts_coeff_j", 0644, pvt->dbgfs_dir, pvt,
&pvt_ts_coeff_j_fops);
return devm_add_action_or_reset(dev, devm_pvt_ts_dbgfs_remove, pvt);
}
static umode_t pvt_is_visible(const void *data, enum hwmon_sensor_types type,
u32 attr, int channel)
{
@ -139,13 +257,28 @@ static umode_t pvt_is_visible(const void *data, enum hwmon_sensor_types type,
return 0;
}
static long pvt_calc_temp(struct pvt_device *pvt, u32 nbs)
{
/*
* Convert the register value to degrees centigrade temperature:
* T = G + H * (n / cal5 - 0.5) + J * F
*/
struct temp_coeff *ts_coeff = &pvt->ts_coeff;
s64 tmp = ts_coeff->g +
div_s64(ts_coeff->h * (s64)nbs, ts_coeff->cal5) -
ts_coeff->h / 2 +
div_s64(ts_coeff->j * (s64)pvt->ip_freq, HZ_PER_MHZ);
return clamp_val(tmp, PVT_TEMP_MIN_mC, PVT_TEMP_MAX_mC);
}
static int pvt_read_temp(struct device *dev, u32 attr, int channel, long *val)
{
struct pvt_device *pvt = dev_get_drvdata(dev);
struct regmap *t_map = pvt->t_map;
u32 stat, nbs;
int ret;
u64 tmp;
switch (attr) {
case hwmon_temp_input:
@ -157,7 +290,7 @@ static int pvt_read_temp(struct device *dev, u32 attr, int channel, long *val)
return ret;
ret = regmap_read(t_map, SDIF_DATA(channel), &nbs);
if(ret < 0)
if (ret < 0)
return ret;
nbs &= SAMPLE_DATA_MSK;
@ -166,9 +299,7 @@ static int pvt_read_temp(struct device *dev, u32 attr, int channel, long *val)
* Convert the register value to
* degrees centigrade temperature
*/
tmp = nbs * PVT_H_CONST;
do_div(tmp, PVT_CAL5_CONST);
*val = tmp - PVT_G_CONST - pvt->ip_freq;
*val = pvt_calc_temp(pvt, nbs);
return 0;
default:
@ -180,15 +311,15 @@ static int pvt_read_in(struct device *dev, u32 attr, int channel, long *val)
{
struct pvt_device *pvt = dev_get_drvdata(dev);
struct regmap *v_map = pvt->v_map;
u32 n, stat, pre_scaler;
u8 vm_idx, ch_idx;
u32 n, stat;
int ret;
if (channel >= pvt->v_num * pvt->c_num)
if (channel >= pvt->vm_channels.total)
return -EINVAL;
vm_idx = pvt->vm_idx[channel / pvt->c_num];
ch_idx = channel % pvt->c_num;
vm_idx = pvt->vd[channel].vm_map;
ch_idx = pvt->vd[channel].ch_map;
switch (attr) {
case hwmon_in_input:
@ -200,10 +331,11 @@ static int pvt_read_in(struct device *dev, u32 attr, int channel, long *val)
return ret;
ret = regmap_read(v_map, VM_SDIF_DATA(vm_idx, ch_idx), &n);
if(ret < 0)
if (ret < 0)
return ret;
n &= SAMPLE_DATA_MSK;
pre_scaler = pvt->vd[channel].pre_scaler;
/*
* Convert the N bitstream count into voltage.
* To support negative voltage calculation for 64bit machines
@ -215,7 +347,8 @@ static int pvt_read_in(struct device *dev, u32 attr, int channel, long *val)
* BIT(x) may not be used instead of (1 << x) because it's
* unsigned.
*/
*val = (PVT_N_CONST * (long)n - PVT_R_CONST) / (1 << PVT_CONV_BITS);
*val = pre_scaler * (PVT_N_CONST * (long)n - PVT_R_CONST) /
(1 << PVT_CONV_BITS);
return 0;
default:
@ -290,23 +423,23 @@ static int pvt_init(struct pvt_device *pvt)
(key >> 1) << CLK_SYNTH_HI_SFT |
(key >> 1) << CLK_SYNTH_HOLD_SFT | CLK_SYNTH_EN;
pvt->ip_freq = sys_freq * 100 / (key + 2);
pvt->ip_freq = clk_get_rate(pvt->clk) / (key + 2);
if (t_num) {
ret = regmap_write(t_map, SDIF_SMPL_CTRL, 0x0);
if(ret < 0)
if (ret < 0)
return ret;
ret = regmap_write(t_map, SDIF_HALT, 0x0);
if(ret < 0)
if (ret < 0)
return ret;
ret = regmap_write(t_map, CLK_SYNTH, clk_synth);
if(ret < 0)
if (ret < 0)
return ret;
ret = regmap_write(t_map, SDIF_DISABLE, 0x0);
if(ret < 0)
if (ret < 0)
return ret;
ret = regmap_read_poll_timeout(t_map, SDIF_STAT,
@ -319,7 +452,7 @@ static int pvt_init(struct pvt_device *pvt)
val = CFG0_MODE_2 | CFG0_PARALLEL_OUT | CFG0_12_BIT |
IP_CFG << SDIF_ADDR_SFT | SDIF_WRN_W | SDIF_PROG;
ret = regmap_write(t_map, SDIF_W, val);
if(ret < 0)
if (ret < 0)
return ret;
ret = regmap_read_poll_timeout(t_map, SDIF_STAT,
@ -332,7 +465,7 @@ static int pvt_init(struct pvt_device *pvt)
val = POWER_DELAY_CYCLE_256 | IP_TMR << SDIF_ADDR_SFT |
SDIF_WRN_W | SDIF_PROG;
ret = regmap_write(t_map, SDIF_W, val);
if(ret < 0)
if (ret < 0)
return ret;
ret = regmap_read_poll_timeout(t_map, SDIF_STAT,
@ -346,39 +479,39 @@ static int pvt_init(struct pvt_device *pvt)
IP_CTRL << SDIF_ADDR_SFT |
SDIF_WRN_W | SDIF_PROG;
ret = regmap_write(t_map, SDIF_W, val);
if(ret < 0)
if (ret < 0)
return ret;
}
if (p_num) {
ret = regmap_write(p_map, SDIF_HALT, 0x0);
if(ret < 0)
if (ret < 0)
return ret;
ret = regmap_write(p_map, SDIF_DISABLE, BIT(p_num) - 1);
if(ret < 0)
if (ret < 0)
return ret;
ret = regmap_write(p_map, CLK_SYNTH, clk_synth);
if(ret < 0)
if (ret < 0)
return ret;
}
if (v_num) {
ret = regmap_write(v_map, SDIF_SMPL_CTRL, 0x0);
if(ret < 0)
if (ret < 0)
return ret;
ret = regmap_write(v_map, SDIF_HALT, 0x0);
if(ret < 0)
if (ret < 0)
return ret;
ret = regmap_write(v_map, CLK_SYNTH, clk_synth);
if(ret < 0)
if (ret < 0)
return ret;
ret = regmap_write(v_map, SDIF_DISABLE, 0x0);
if(ret < 0)
if (ret < 0)
return ret;
ret = regmap_read_poll_timeout(v_map, SDIF_STAT,
@ -388,7 +521,7 @@ static int pvt_init(struct pvt_device *pvt)
if (ret)
return ret;
val = (BIT(pvt->c_num) - 1) | VM_CH_INIT |
val = (BIT(pvt->vm_channels.max) - 1) | VM_CH_INIT |
IP_POLL << SDIF_ADDR_SFT | SDIF_WRN_W | SDIF_PROG;
ret = regmap_write(v_map, SDIF_W, val);
if (ret < 0)
@ -405,7 +538,7 @@ static int pvt_init(struct pvt_device *pvt)
CFG1_14_BIT | IP_CFG << SDIF_ADDR_SFT |
SDIF_WRN_W | SDIF_PROG;
ret = regmap_write(v_map, SDIF_W, val);
if(ret < 0)
if (ret < 0)
return ret;
ret = regmap_read_poll_timeout(v_map, SDIF_STAT,
@ -418,7 +551,7 @@ static int pvt_init(struct pvt_device *pvt)
val = POWER_DELAY_CYCLE_64 | IP_TMR << SDIF_ADDR_SFT |
SDIF_WRN_W | SDIF_PROG;
ret = regmap_write(v_map, SDIF_W, val);
if(ret < 0)
if (ret < 0)
return ret;
ret = regmap_read_poll_timeout(v_map, SDIF_STAT,
@ -432,7 +565,7 @@ static int pvt_init(struct pvt_device *pvt)
IP_CTRL << SDIF_ADDR_SFT |
SDIF_WRN_W | SDIF_PROG;
ret = regmap_write(v_map, SDIF_W, val);
if(ret < 0)
if (ret < 0)
return ret;
}
@ -477,24 +610,6 @@ static int pvt_get_regmap(struct platform_device *pdev, char *reg_name,
return 0;
}
static void pvt_clk_disable(void *data)
{
struct pvt_device *pvt = data;
clk_disable_unprepare(pvt->clk);
}
static int pvt_clk_enable(struct device *dev, struct pvt_device *pvt)
{
int ret;
ret = clk_prepare_enable(pvt->clk);
if (ret)
return ret;
return devm_add_action_or_reset(dev, pvt_clk_disable, pvt);
}
static void pvt_reset_control_assert(void *data)
{
struct pvt_device *pvt = data;
@ -513,6 +628,141 @@ static int pvt_reset_control_deassert(struct device *dev, struct pvt_device *pvt
return devm_add_action_or_reset(dev, pvt_reset_control_assert, pvt);
}
static int pvt_get_active_channel(struct device *dev, struct pvt_device *pvt,
u32 vm_num, u32 ch_num, u8 *vm_idx)
{
u8 vm_active_ch[VM_NUM_MAX];
int ret, i, j, k;
ret = device_property_read_u8_array(dev, "moortec,vm-active-channels",
vm_active_ch, vm_num);
if (ret) {
/*
* Incase "moortec,vm-active-channels" property is not defined,
* we assume each VM sensor has all of its channels active.
*/
memset(vm_active_ch, ch_num, vm_num);
pvt->vm_channels.max = ch_num;
pvt->vm_channels.total = ch_num * vm_num;
} else {
for (i = 0; i < vm_num; i++) {
if (vm_active_ch[i] > ch_num) {
dev_err(dev, "invalid active channels: %u\n",
vm_active_ch[i]);
return -EINVAL;
}
pvt->vm_channels.total += vm_active_ch[i];
if (vm_active_ch[i] > pvt->vm_channels.max)
pvt->vm_channels.max = vm_active_ch[i];
}
}
/*
* Map between the channel-number to VM-index and channel-index.
* Example - 3 VMs, "moortec,vm_active_ch" = <5 2 4>:
* vm_map = [0 0 0 0 0 1 1 2 2 2 2]
* ch_map = [0 1 2 3 4 0 1 0 1 2 3]
*/
pvt->vd = devm_kcalloc(dev, pvt->vm_channels.total, sizeof(*pvt->vd),
GFP_KERNEL);
if (!pvt->vd)
return -ENOMEM;
k = 0;
for (i = 0; i < vm_num; i++) {
for (j = 0; j < vm_active_ch[i]; j++) {
pvt->vd[k].vm_map = vm_idx[i];
pvt->vd[k].ch_map = j;
k++;
}
}
return 0;
}
static int pvt_get_pre_scaler(struct device *dev, struct pvt_device *pvt)
{
u8 *pre_scaler_ch_list;
int i, ret, num_ch;
u32 channel;
/* Set default pre-scaler value to be 1. */
for (i = 0; i < pvt->vm_channels.total; i++)
pvt->vd[i].pre_scaler = PRE_SCALER_X1;
/* Get number of channels configured in "moortec,vm-pre-scaler-x2". */
num_ch = device_property_count_u8(dev, "moortec,vm-pre-scaler-x2");
if (num_ch <= 0)
return 0;
pre_scaler_ch_list = kcalloc(num_ch, sizeof(*pre_scaler_ch_list),
GFP_KERNEL);
if (!pre_scaler_ch_list)
return -ENOMEM;
/* Get list of all channels that have pre-scaler of 2. */
ret = device_property_read_u8_array(dev, "moortec,vm-pre-scaler-x2",
pre_scaler_ch_list, num_ch);
if (ret)
goto out;
for (i = 0; i < num_ch; i++) {
channel = pre_scaler_ch_list[i];
pvt->vd[channel].pre_scaler = PRE_SCALER_X2;
}
out:
kfree(pre_scaler_ch_list);
return ret;
}
static int pvt_set_temp_coeff(struct device *dev, struct pvt_device *pvt)
{
struct temp_coeff *ts_coeff = &pvt->ts_coeff;
u32 series;
int ret;
/* Incase ts-series property is not defined, use default 5. */
ret = device_property_read_u32(dev, "moortec,ts-series", &series);
if (ret)
series = TEMPERATURE_SENSOR_SERIES_5;
switch (series) {
case TEMPERATURE_SENSOR_SERIES_5:
ts_coeff->h = PVT_SERIES5_H_CONST;
ts_coeff->g = PVT_SERIES5_G_CONST;
ts_coeff->j = PVT_SERIES5_J_CONST;
ts_coeff->cal5 = PVT_SERIES5_CAL5_CONST;
break;
case TEMPERATURE_SENSOR_SERIES_6:
ts_coeff->h = PVT_SERIES6_H_CONST;
ts_coeff->g = PVT_SERIES6_G_CONST;
ts_coeff->j = PVT_SERIES6_J_CONST;
ts_coeff->cal5 = PVT_SERIES6_CAL5_CONST;
break;
default:
dev_err(dev, "invalid temperature sensor series (%u)\n",
series);
return -EINVAL;
}
dev_dbg(dev, "temperature sensor series = %u\n", series);
/* Override ts-coeff-h/g/j/cal5 if they are defined. */
device_property_read_u32(dev, "moortec,ts-coeff-h", &ts_coeff->h);
device_property_read_u32(dev, "moortec,ts-coeff-g", &ts_coeff->g);
device_property_read_u32(dev, "moortec,ts-coeff-j", &ts_coeff->j);
device_property_read_u32(dev, "moortec,ts-coeff-cal5", &ts_coeff->cal5);
dev_dbg(dev, "ts-coeff: h = %u, g = %u, j = %d, cal5 = %u\n",
ts_coeff->h, ts_coeff->g, ts_coeff->j, ts_coeff->cal5);
return 0;
}
static int mr75203_probe(struct platform_device *pdev)
{
u32 ts_num, vm_num, pd_num, ch_num, val, index, i;
@ -531,27 +781,24 @@ static int mr75203_probe(struct platform_device *pdev)
if (ret)
return ret;
pvt->clk = devm_clk_get(dev, NULL);
pvt->clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(pvt->clk))
return dev_err_probe(dev, PTR_ERR(pvt->clk), "failed to get clock\n");
ret = pvt_clk_enable(dev, pvt);
if (ret) {
dev_err(dev, "failed to enable clock\n");
return ret;
}
pvt->rst = devm_reset_control_get_exclusive(dev, NULL);
pvt->rst = devm_reset_control_get_optional_exclusive(dev, NULL);
if (IS_ERR(pvt->rst))
return dev_err_probe(dev, PTR_ERR(pvt->rst),
"failed to get reset control\n");
ret = pvt_reset_control_deassert(dev, pvt);
if (ret)
return dev_err_probe(dev, ret, "cannot deassert reset control\n");
if (pvt->rst) {
ret = pvt_reset_control_deassert(dev, pvt);
if (ret)
return dev_err_probe(dev, ret,
"cannot deassert reset control\n");
}
ret = regmap_read(pvt->c_map, PVT_IP_CONFIG, &val);
if(ret < 0)
if (ret < 0)
return ret;
ts_num = (val & TS_NUM_MSK) >> TS_NUM_SFT;
@ -561,7 +808,6 @@ static int mr75203_probe(struct platform_device *pdev)
pvt->t_num = ts_num;
pvt->p_num = pd_num;
pvt->v_num = vm_num;
pvt->c_num = ch_num;
val = 0;
if (ts_num)
val++;
@ -581,6 +827,10 @@ static int mr75203_probe(struct platform_device *pdev)
if (ret)
return ret;
ret = pvt_set_temp_coeff(dev, pvt);
if (ret)
return ret;
temp_config = devm_kcalloc(dev, ts_num + 1,
sizeof(*temp_config), GFP_KERNEL);
if (!temp_config)
@ -589,6 +839,8 @@ static int mr75203_probe(struct platform_device *pdev)
memset32(temp_config, HWMON_T_INPUT, ts_num);
pvt_temp.config = temp_config;
pvt_info[index++] = &pvt_temp;
pvt_ts_dbgfs_create(pvt, dev);
}
if (pd_num) {
@ -598,44 +850,45 @@ static int mr75203_probe(struct platform_device *pdev)
}
if (vm_num) {
u32 total_ch;
u8 vm_idx[VM_NUM_MAX];
ret = pvt_get_regmap(pdev, "vm", pvt);
if (ret)
return ret;
pvt->vm_idx = devm_kcalloc(dev, vm_num, sizeof(*pvt->vm_idx),
GFP_KERNEL);
if (!pvt->vm_idx)
return -ENOMEM;
ret = device_property_read_u8_array(dev, "intel,vm-map",
pvt->vm_idx, vm_num);
ret = device_property_read_u8_array(dev, "intel,vm-map", vm_idx,
vm_num);
if (ret) {
/*
* Incase intel,vm-map property is not defined, we
* assume incremental channel numbers.
*/
for (i = 0; i < vm_num; i++)
pvt->vm_idx[i] = i;
vm_idx[i] = i;
} else {
for (i = 0; i < vm_num; i++)
if (pvt->vm_idx[i] >= vm_num ||
pvt->vm_idx[i] == 0xff) {
if (vm_idx[i] >= vm_num || vm_idx[i] == 0xff) {
pvt->v_num = i;
vm_num = i;
break;
}
}
total_ch = ch_num * vm_num;
in_config = devm_kcalloc(dev, total_ch + 1,
ret = pvt_get_active_channel(dev, pvt, vm_num, ch_num, vm_idx);
if (ret)
return ret;
ret = pvt_get_pre_scaler(dev, pvt);
if (ret)
return ret;
in_config = devm_kcalloc(dev, pvt->vm_channels.total + 1,
sizeof(*in_config), GFP_KERNEL);
if (!in_config)
return -ENOMEM;
memset32(in_config, HWMON_I_INPUT, total_ch);
in_config[total_ch] = 0;
memset32(in_config, HWMON_I_INPUT, pvt->vm_channels.total);
in_config[pvt->vm_channels.total] = 0;
pvt_in.config = in_config;
pvt_info[index++] = &pvt_in;

View File

@ -412,7 +412,7 @@ nct6683_create_attr_group(struct device *dev,
struct sensor_device_attr_u *su;
struct attribute_group *group;
struct attribute **attrs;
int i, j, count;
int i, count;
if (repeat <= 0)
return ERR_PTR(-EINVAL);
@ -443,7 +443,7 @@ nct6683_create_attr_group(struct device *dev,
for (i = 0; i < repeat; i++) {
t = tg->templates;
for (j = 0; *t != NULL; j++) {
while (*t) {
snprintf(su->name, sizeof(su->name),
(*t)->dev_attr.attr.name, tg->base + i);
if ((*t)->s2) {

View File

@ -355,7 +355,7 @@ static void nct6791_enable_io_mapping(struct nct6775_sio_data *sio_data)
}
}
static int __maybe_unused nct6775_suspend(struct device *dev)
static int nct6775_suspend(struct device *dev)
{
int err;
u16 tmp;
@ -386,7 +386,7 @@ out:
return err;
}
static int __maybe_unused nct6775_resume(struct device *dev)
static int nct6775_resume(struct device *dev)
{
struct nct6775_data *data = dev_get_drvdata(dev);
struct nct6775_sio_data *sio_data = dev_get_platdata(dev);
@ -467,7 +467,7 @@ abort:
return err;
}
static SIMPLE_DEV_PM_OPS(nct6775_dev_pm_ops, nct6775_suspend, nct6775_resume);
static DEFINE_SIMPLE_DEV_PM_OPS(nct6775_dev_pm_ops, nct6775_suspend, nct6775_resume);
static void
nct6775_check_fan_inputs(struct nct6775_data *data, struct nct6775_sio_data *sio_data)
@ -934,7 +934,7 @@ static int nct6775_platform_probe(struct platform_device *pdev)
static struct platform_driver nct6775_driver = {
.driver = {
.name = DRVNAME,
.pm = &nct6775_dev_pm_ops,
.pm = pm_sleep_ptr(&nct6775_dev_pm_ops),
},
.probe = nct6775_platform_probe,
};

View File

@ -1038,7 +1038,7 @@ static int nct7802_detect(struct i2c_client *client,
if (reg < 0 || (reg & 0x3f))
return -ENODEV;
strlcpy(info->type, "nct7802", I2C_NAME_SIZE);
strscpy(info->type, "nct7802", I2C_NAME_SIZE);
return 0;
}

View File

@ -798,7 +798,7 @@ static int nct7904_detect(struct i2c_client *client,
(i2c_smbus_read_byte_data(client, BANK_SEL_REG) & 0xf8) != 0x00)
return -ENODEV;
strlcpy(info->type, "nct7904", I2C_NAME_SIZE);
strscpy(info->type, "nct7904", I2C_NAME_SIZE);
return 0;
}

View File

@ -787,6 +787,7 @@ static void nzxt_smart2_hid_remove(struct hid_device *hdev)
static const struct hid_device_id nzxt_smart2_hid_id_table[] = {
{ HID_USB_DEVICE(0x1e71, 0x2006) }, /* NZXT Smart Device V2 */
{ HID_USB_DEVICE(0x1e71, 0x200d) }, /* NZXT Smart Device V2 */
{ HID_USB_DEVICE(0x1e71, 0x200f) }, /* NZXT Smart Device V2 */
{ HID_USB_DEVICE(0x1e71, 0x2009) }, /* NZXT RGB & Fan Controller */
{ HID_USB_DEVICE(0x1e71, 0x200e) }, /* NZXT RGB & Fan Controller */
{ HID_USB_DEVICE(0x1e71, 0x2010) }, /* NZXT RGB & Fan Controller */

File diff suppressed because it is too large Load Diff

View File

@ -397,6 +397,15 @@ config SENSORS_TPS53679
This driver can also be built as a module. If so, the module will
be called tps53679.
config SENSORS_TPS546D24
tristate "TPS546D24"
help
If you say yes here you get hardware monitoring support for TEXAS
TPS546D24.
This driver can also be built as a module. If so, the module will
be called tps546d24
config SENSORS_UCD9000
tristate "TI UCD90120, UCD90124, UCD90160, UCD90320, UCD9090, UCD90910"
help

View File

@ -41,6 +41,7 @@ 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_TPS546D24) += tps546d24.o
obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o
obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o
obj-$(CONFIG_SENSORS_XDPE122) += xdpe12284.o

View File

@ -34,7 +34,7 @@ struct mp2888_data {
int curr_sense_gain;
};
#define to_mp2888_data(x) container_of(x, struct mp2888_data, info)
#define to_mp2888_data(x) container_of(x, struct mp2888_data, info)
static int mp2888_read_byte_data(struct i2c_client *client, int page, int reg)
{
@ -109,7 +109,7 @@ mp2888_read_phase(struct i2c_client *client, struct mp2888_data *data, int page,
* - Kcs is the DrMOS current sense gain of power stage, which is obtained from the
* register MP2888_MFR_VR_CONFIG1, bits 13-12 with the following selection of DrMOS
* (data->curr_sense_gain):
* 00b - 5µA/A, 01b - 8.5µA/A, 10b - 9.7µA/A, 11b - 10µA/A.
* 00b - 8.5µA/A, 01b - 9.7µA/A, 1b - 10µA/A, 11b - 5µA/A.
* - Rcs is the internal phase current sense resistor. This parameter depends on hardware
* assembly. By default it is set to 1kΩ. In case of different assembly, user should
* scale this parameter by dividing it by Rcs.
@ -118,10 +118,9 @@ mp2888_read_phase(struct i2c_client *client, struct mp2888_data *data, int page,
* because sampling of current occurrence of bit weight has a big deviation, especially for
* light load.
*/
ret = DIV_ROUND_CLOSEST(ret * 100 - 9800, data->curr_sense_gain);
ret = (data->phase_curr_resolution) ? ret * 2 : ret;
ret = DIV_ROUND_CLOSEST(ret * 200 - 19600, data->curr_sense_gain);
/* Scale according to total current resolution. */
ret = (data->total_curr_resolution) ? ret * 8 : ret * 4;
ret = (data->total_curr_resolution) ? ret * 2 : ret;
return ret;
}
@ -212,7 +211,7 @@ static int mp2888_read_word_data(struct i2c_client *client, int page, int phase,
ret = pmbus_read_word_data(client, page, phase, reg);
if (ret < 0)
return ret;
ret = data->total_curr_resolution ? ret * 2 : ret;
ret = data->total_curr_resolution ? ret : DIV_ROUND_CLOSEST(ret, 2);
break;
case PMBUS_POUT_OP_WARN_LIMIT:
ret = pmbus_read_word_data(client, page, phase, reg);
@ -223,7 +222,7 @@ static int mp2888_read_word_data(struct i2c_client *client, int page, int phase,
* set 1. Actual power is reported with 0.5W or 1W respectively resolution. Scaling
* is needed to match both.
*/
ret = data->total_curr_resolution ? ret * 4 : ret * 2;
ret = data->total_curr_resolution ? ret * 2 : ret;
break;
/*
* The below registers are not implemented by device or implemented not according to the

View File

@ -0,0 +1,71 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Hardware monitoring driver for TEXAS TPS546D24 buck converter
*/
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pmbus.h>
#include "pmbus.h"
static struct pmbus_driver_info tps546d24_info = {
.pages = 1,
.format[PSC_VOLTAGE_IN] = linear,
.format[PSC_VOLTAGE_OUT] = linear,
.format[PSC_TEMPERATURE] = linear,
.format[PSC_CURRENT_OUT] = linear,
.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN
| PMBUS_HAVE_IOUT | PMBUS_HAVE_VOUT
| PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_VOUT
| PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
};
static int tps546d24_probe(struct i2c_client *client)
{
int reg;
reg = i2c_smbus_read_byte_data(client, PMBUS_VOUT_MODE);
if (reg < 0)
return reg;
if (reg & 0x80) {
int err;
err = i2c_smbus_write_byte_data(client, PMBUS_VOUT_MODE, reg & 0x7f);
if (err < 0)
return err;
}
return pmbus_do_probe(client, &tps546d24_info);
}
static const struct i2c_device_id tps546d24_id[] = {
{"tps546d24", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, tps546d24_id);
static const struct of_device_id __maybe_unused tps546d24_of_match[] = {
{.compatible = "ti,tps546d24"},
{}
};
MODULE_DEVICE_TABLE(of, tps546d24_of_match);
/* This is the driver that will be inserted */
static struct i2c_driver tps546d24_driver = {
.driver = {
.name = "tps546d24",
.of_match_table = of_match_ptr(tps546d24_of_match),
},
.probe_new = tps546d24_probe,
.id_table = tps546d24_id,
};
module_i2c_driver(tps546d24_driver);
MODULE_AUTHOR("Duke Du <dukedu83@gmail.com>");
MODULE_DESCRIPTION("PMBus driver for TI tps546d24");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(PMBUS);

View File

@ -28,11 +28,23 @@ struct pwm_fan_tach {
u8 pulses_per_revolution;
};
enum pwm_fan_enable_mode {
pwm_off_reg_off,
pwm_disable_reg_enable,
pwm_enable_reg_enable,
pwm_disable_reg_disable,
};
struct pwm_fan_ctx {
struct device *dev;
struct mutex lock;
struct pwm_device *pwm;
struct pwm_state pwm_state;
struct regulator *reg_en;
enum pwm_fan_enable_mode enable_mode;
bool regulator_enabled;
bool enabled;
int tach_count;
struct pwm_fan_tach *tachs;
@ -82,25 +94,140 @@ static void sample_timer(struct timer_list *t)
mod_timer(&ctx->rpm_timer, jiffies + HZ);
}
static void pwm_fan_enable_mode_2_state(int enable_mode,
struct pwm_state *state,
bool *enable_regulator)
{
switch (enable_mode) {
case pwm_disable_reg_enable:
/* disable pwm, keep regulator enabled */
state->enabled = false;
*enable_regulator = true;
break;
case pwm_enable_reg_enable:
/* keep pwm and regulator enabled */
state->enabled = true;
*enable_regulator = true;
break;
case pwm_off_reg_off:
case pwm_disable_reg_disable:
/* disable pwm and regulator */
state->enabled = false;
*enable_regulator = false;
}
}
static int pwm_fan_switch_power(struct pwm_fan_ctx *ctx, bool on)
{
int ret = 0;
if (!ctx->reg_en)
return ret;
if (!ctx->regulator_enabled && on) {
ret = regulator_enable(ctx->reg_en);
if (ret == 0)
ctx->regulator_enabled = true;
} else if (ctx->regulator_enabled && !on) {
ret = regulator_disable(ctx->reg_en);
if (ret == 0)
ctx->regulator_enabled = false;
}
return ret;
}
static int pwm_fan_power_on(struct pwm_fan_ctx *ctx)
{
struct pwm_state *state = &ctx->pwm_state;
int ret;
if (ctx->enabled)
return 0;
ret = pwm_fan_switch_power(ctx, true);
if (ret < 0) {
dev_err(ctx->dev, "failed to enable power supply\n");
return ret;
}
state->enabled = true;
ret = pwm_apply_state(ctx->pwm, state);
if (ret) {
dev_err(ctx->dev, "failed to enable PWM\n");
goto disable_regulator;
}
ctx->enabled = true;
return 0;
disable_regulator:
pwm_fan_switch_power(ctx, false);
return ret;
}
static int pwm_fan_power_off(struct pwm_fan_ctx *ctx)
{
struct pwm_state *state = &ctx->pwm_state;
bool enable_regulator = false;
int ret;
if (!ctx->enabled)
return 0;
pwm_fan_enable_mode_2_state(ctx->enable_mode,
state,
&enable_regulator);
state->enabled = false;
state->duty_cycle = 0;
ret = pwm_apply_state(ctx->pwm, state);
if (ret) {
dev_err(ctx->dev, "failed to disable PWM\n");
return ret;
}
pwm_fan_switch_power(ctx, enable_regulator);
ctx->enabled = false;
return 0;
}
static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
{
struct pwm_state *state = &ctx->pwm_state;
unsigned long period;
int ret = 0;
struct pwm_state *state = &ctx->pwm_state;
mutex_lock(&ctx->lock);
if (ctx->pwm_value == pwm)
goto exit_set_pwm_err;
if (pwm > 0) {
if (ctx->enable_mode == pwm_off_reg_off)
/* pwm-fan hard disabled */
return 0;
period = state->period;
state->duty_cycle = DIV_ROUND_UP(pwm * (period - 1), MAX_PWM);
state->enabled = pwm ? true : false;
ret = pwm_apply_state(ctx->pwm, state);
period = state->period;
state->duty_cycle = DIV_ROUND_UP(pwm * (period - 1), MAX_PWM);
ret = pwm_apply_state(ctx->pwm, state);
if (ret)
return ret;
ret = pwm_fan_power_on(ctx);
} else {
ret = pwm_fan_power_off(ctx);
}
if (!ret)
ctx->pwm_value = pwm;
exit_set_pwm_err:
return ret;
}
static int set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
{
int ret;
mutex_lock(&ctx->lock);
ret = __set_pwm(ctx, pwm);
mutex_unlock(&ctx->lock);
return ret;
}
@ -115,20 +242,76 @@ static void pwm_fan_update_state(struct pwm_fan_ctx *ctx, unsigned long pwm)
ctx->pwm_fan_state = i;
}
static int pwm_fan_update_enable(struct pwm_fan_ctx *ctx, long val)
{
int ret = 0;
int old_val;
mutex_lock(&ctx->lock);
if (ctx->enable_mode == val)
goto out;
old_val = ctx->enable_mode;
ctx->enable_mode = val;
if (val == 0) {
/* Disable pwm-fan unconditionally */
ret = __set_pwm(ctx, 0);
if (ret)
ctx->enable_mode = old_val;
pwm_fan_update_state(ctx, 0);
} else {
/*
* Change PWM and/or regulator state if currently disabled
* Nothing to do if currently enabled
*/
if (!ctx->enabled) {
struct pwm_state *state = &ctx->pwm_state;
bool enable_regulator = false;
state->duty_cycle = 0;
pwm_fan_enable_mode_2_state(val,
state,
&enable_regulator);
pwm_apply_state(ctx->pwm, state);
pwm_fan_switch_power(ctx, enable_regulator);
pwm_fan_update_state(ctx, 0);
}
}
out:
mutex_unlock(&ctx->lock);
return ret;
}
static int pwm_fan_write(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long val)
{
struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
int ret;
if (val < 0 || val > MAX_PWM)
return -EINVAL;
switch (attr) {
case hwmon_pwm_input:
if (val < 0 || val > MAX_PWM)
return -EINVAL;
ret = set_pwm(ctx, val);
if (ret)
return ret;
pwm_fan_update_state(ctx, val);
break;
case hwmon_pwm_enable:
if (val < 0 || val > 3)
ret = -EINVAL;
else
ret = pwm_fan_update_enable(ctx, val);
ret = __set_pwm(ctx, val);
if (ret)
return ret;
default:
return -EOPNOTSUPP;
}
pwm_fan_update_state(ctx, val);
return 0;
}
@ -139,9 +322,15 @@ static int pwm_fan_read(struct device *dev, enum hwmon_sensor_types type,
switch (type) {
case hwmon_pwm:
*val = ctx->pwm_value;
return 0;
switch (attr) {
case hwmon_pwm_input:
*val = ctx->pwm_value;
return 0;
case hwmon_pwm_enable:
*val = ctx->enable_mode;
return 0;
}
return -EOPNOTSUPP;
case hwmon_fan:
*val = ctx->tachs[channel].rpm;
return 0;
@ -212,7 +401,7 @@ pwm_fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
if (state == ctx->pwm_fan_state)
return 0;
ret = __set_pwm(ctx, ctx->pwm_fan_cooling_levels[state]);
ret = set_pwm(ctx, ctx->pwm_fan_cooling_levels[state]);
if (ret) {
dev_err(&cdev->device, "Cannot set pwm!\n");
return ret;
@ -270,18 +459,14 @@ static int pwm_fan_of_get_cooling_data(struct device *dev,
return 0;
}
static void pwm_fan_regulator_disable(void *data)
{
regulator_disable(data);
}
static void pwm_fan_pwm_disable(void *__ctx)
static void pwm_fan_cleanup(void *__ctx)
{
struct pwm_fan_ctx *ctx = __ctx;
ctx->pwm_state.enabled = false;
pwm_apply_state(ctx->pwm, &ctx->pwm_state);
del_timer_sync(&ctx->rpm_timer);
/* Switch off everything */
ctx->enable_mode = pwm_disable_reg_disable;
pwm_fan_power_off(ctx);
}
static int pwm_fan_probe(struct platform_device *pdev)
@ -302,7 +487,8 @@ static int pwm_fan_probe(struct platform_device *pdev)
mutex_init(&ctx->lock);
ctx->pwm = devm_of_pwm_get(dev, dev->of_node, NULL);
ctx->dev = &pdev->dev;
ctx->pwm = devm_pwm_get(dev, NULL);
if (IS_ERR(ctx->pwm))
return dev_err_probe(dev, PTR_ERR(ctx->pwm), "Could not get PWM\n");
@ -314,22 +500,12 @@ static int pwm_fan_probe(struct platform_device *pdev)
return PTR_ERR(ctx->reg_en);
ctx->reg_en = NULL;
} else {
ret = regulator_enable(ctx->reg_en);
if (ret) {
dev_err(dev, "Failed to enable fan supply: %d\n", ret);
return ret;
}
ret = devm_add_action_or_reset(dev, pwm_fan_regulator_disable,
ctx->reg_en);
if (ret)
return ret;
}
pwm_init_state(ctx->pwm, &ctx->pwm_state);
/*
* __set_pwm assumes that MAX_PWM * (period - 1) fits into an unsigned
* set_pwm assumes that MAX_PWM * (period - 1) fits into an unsigned
* long. Check this here to prevent the fan running at a too low
* frequency.
*/
@ -338,14 +514,19 @@ static int pwm_fan_probe(struct platform_device *pdev)
return -EINVAL;
}
/* Set duty cycle to maximum allowed and enable PWM output */
ret = __set_pwm(ctx, MAX_PWM);
ctx->enable_mode = pwm_disable_reg_enable;
/*
* Set duty cycle to maximum allowed and enable PWM output as well as
* the regulator. In case of error nothing is changed
*/
ret = set_pwm(ctx, MAX_PWM);
if (ret) {
dev_err(dev, "Failed to configure PWM: %d\n", ret);
return ret;
}
timer_setup(&ctx->rpm_timer, sample_timer, 0);
ret = devm_add_action_or_reset(dev, pwm_fan_pwm_disable, ctx);
ret = devm_add_action_or_reset(dev, pwm_fan_cleanup, ctx);
if (ret)
return ret;
@ -377,7 +558,7 @@ static int pwm_fan_probe(struct platform_device *pdev)
if (!channels)
return -ENOMEM;
channels[0] = HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT);
channels[0] = HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT | HWMON_PWM_ENABLE);
for (i = 0; i < ctx->tach_count; i++) {
struct pwm_fan_tach *tach = &ctx->tachs[i];
@ -451,65 +632,28 @@ static int pwm_fan_probe(struct platform_device *pdev)
return 0;
}
static int pwm_fan_disable(struct device *dev)
{
struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
int ret;
if (ctx->pwm_value) {
/* keep ctx->pwm_state unmodified for pwm_fan_resume() */
struct pwm_state state = ctx->pwm_state;
state.duty_cycle = 0;
state.enabled = false;
ret = pwm_apply_state(ctx->pwm, &state);
if (ret < 0)
return ret;
}
if (ctx->reg_en) {
ret = regulator_disable(ctx->reg_en);
if (ret) {
dev_err(dev, "Failed to disable fan supply: %d\n", ret);
return ret;
}
}
return 0;
}
static void pwm_fan_shutdown(struct platform_device *pdev)
{
pwm_fan_disable(&pdev->dev);
struct pwm_fan_ctx *ctx = platform_get_drvdata(pdev);
pwm_fan_cleanup(ctx);
}
#ifdef CONFIG_PM_SLEEP
static int pwm_fan_suspend(struct device *dev)
{
return pwm_fan_disable(dev);
struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
return pwm_fan_power_off(ctx);
}
static int pwm_fan_resume(struct device *dev)
{
struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
int ret;
if (ctx->reg_en) {
ret = regulator_enable(ctx->reg_en);
if (ret) {
dev_err(dev, "Failed to enable fan supply: %d\n", ret);
return ret;
}
}
if (ctx->pwm_value == 0)
return 0;
return pwm_apply_state(ctx->pwm, &ctx->pwm_state);
return set_pwm(ctx, ctx->pwm_value);
}
#endif
static SIMPLE_DEV_PM_OPS(pwm_fan_pm, pwm_fan_suspend, pwm_fan_resume);
static DEFINE_SIMPLE_DEV_PM_OPS(pwm_fan_pm, pwm_fan_suspend, pwm_fan_resume);
static const struct of_device_id of_pwm_fan_match[] = {
{ .compatible = "pwm-fan", },
@ -522,7 +666,7 @@ static struct platform_driver pwm_fan_driver = {
.shutdown = pwm_fan_shutdown,
.driver = {
.name = "pwm-fan",
.pm = &pwm_fan_pm,
.pm = pm_sleep_ptr(&pwm_fan_pm),
.of_match_table = of_pwm_fan_match,
},
};

View File

@ -129,7 +129,7 @@ unlock:
static ssize_t sht4x_interval_write(struct sht4x_data *data, long val)
{
data->update_interval = clamp_val(val, SHT4X_MIN_POLL_INTERVAL, UINT_MAX);
data->update_interval = clamp_val(val, SHT4X_MIN_POLL_INTERVAL, INT_MAX);
return 0;
}

Some files were not shown because too many files have changed in this diff Show More