hwmon updates for v6.12

* New drivers
 
   - Driver for Sophgo SG2042 external hardware monitor
 
   - Thermal sensor driver for Surface Aggregator Module
 
 * Added support to existing drivers
 
   - oxp-sensors: Support for multiple new devices.
 
   - nct6775: Added G15CF to ASUS WMI monitoring list
 
 * Modernizations
 
   - ina2xx: Driver cleanup and update to use with_info API
 
   - lm92: Driver cleanup and update to use regmap and with_info API
 
   - lm95234: Driver cleanup and update to use regmap and with_info API
 
   - max1619: Driver cleanup and update to use regmap and with_info API
 
   - max1668: Driver cleanup and update to use regmap and with_info API
 
   - max6697: Driver cleanup and update to use regmap and with_info API
 
 * API updates
 
   - Removed unused devm_hwmon_device_unregister() API function
 
 * Other notable changes
 
   - Implement and use generic bus access delay for pmbus drivers
 
   - Use with scoped for each OF child loop in several drivers
 
   - Module unloading fixes for gsc-hwmon and ntc_thermistor drivers
 
   - Converted various drivers to use multi-byte regmap operations
 
   - adt7475: Improved devicetree based configuration
 
   - ltc2947: Move to firmware agnostic API
 
   - ltc2978: Converted devicetree description to yaml
 
   - max16065: Addressed overflows when writing limit attributes
 
 * Various other minor cleanups, fixes and improvements
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEiHPvMQj9QTOCiqgVyx8mb86fmYEFAmboX+AACgkQyx8mb86f
 mYHRGA/7BVlHa3sxDTNLPvL3VMZ1/SgmQqC0xUx57bAOcpkLX0taXD/t+Nm93HaW
 vCKGYPe0jII9tVz1YvZ1VSNwJXZo4X+jfdL3t6RpdKpn/op6vASzhKhh0sGeiyw1
 aQNXzrm4dthFDRfmscZM1+CBQv4aTf6ApyTbRFH2dnViXu9idMZYcxoxz87uody5
 AxUAgNDPb/mQww3x6r+rVv3VQaJZ+yrJxbYvaxgzbm8TqIFCpHgNtRJTVBhGjbOi
 o9rushlUpOjBQE2/jKOajtfV9fWX/kpJu9dUfSbVMCvZgEPU985UX6dpg9Oc0t0o
 oUhID2dHLUVNmn4dTQCtvzuLTEBDi87TcML6VDlMIn3dFi5QG3tJZkaWtbPymHz9
 4Qf3TJ2TV0E/jIh8UueFd2SlRlkCE3HooM04Kbes7H8ftSbddMM3fTah8yzdOJJE
 Dwv6eO3T9REHPaBauFq0Y9hzkx46rqF6Mli0tFUumh7oM1b68ILZ+oJxfpapatzO
 Pa6UPFfaHU63VFmDCzNWc0IiI1beF7i5fzzWwj37HgIdaw1+cS6kNtvbv/6t5/41
 5TVitpP1SLzDtjK6f+VDjroE/Qg4OeodamBOEopMPjYM/nipyWxPoOK1VEtbo1P5
 vAJBmgPn45M02miB7l3ERCCkRVQdeL69ZM5vcRmLB8NQRKML830=
 =z6jf
 -----END PGP SIGNATURE-----

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

Pull hwmon updates from Guenter Roeck:
 "New drivers:
   - driver for Sophgo SG2042 external hardware monitor
   - thermal sensor driver for Surface Aggregator Module

  Added support to existing drivers:
   - oxp-sensors: Support for multiple new devices.
   - nct6775: Added G15CF to ASUS WMI monitoring list

  Modernizations:
   - driver cleanup and update to use with_info API: ina2xx, lm92,
     lm95234, max1619, max1668, and max6697.

  API updates:
   - removed unused devm_hwmon_device_unregister() API function

  Other notable changes
   - implement and use generic bus access delay for pmbus drivers
   - use with scoped for each OF child loop in several drivers
   - module unloading fixes for gsc-hwmon and ntc_thermistor drivers
   - converted various drivers to use multi-byte regmap operations
   - adt7475: Improved devicetree based configuration
   - ltc2947: Move to firmware agnostic API
   - ltc2978: Converted devicetree description to yaml
   - max16065: Addressed overflows when writing limit attributes

  Various other minor cleanups, fixes and improvements"

* tag 'hwmon-for-v6.12' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (96 commits)
  hwmon: Remove devm_hwmon_device_unregister() API function
  hwmon: (sch5636) Print unknown ID in error string via %*pE
  hwmon: (sht21) Use %*ph to print small buffer
  hwmon: (pmbus/mpq7932) Constify struct regulator_desc
  hwmon: pmbus: pli12096bc: Add write delay
  hwmon: pmbus: zl6100: Use generic code
  hwmon: pmbus: ucd9000: Use generic code
  hwmon: pmbus: max15301: Use generic code
  hwmon: pmbus: Implement generic bus access delay
  hwmon: (ina2xx) Use shunt voltage to calculate current
  hwmon: (ina2xx) Add support for current limits
  hwmon: (ina2xx) Pass register to alert limit write functions
  hwmon: (ina2xx) Convert to use with_info hwmon API
  hwmon: (ina2xx) Move ina2xx_get_value()
  hwmon: (ina2xx) Set alert latch
  hwmon: (ina2xx) Consolidate chip initialization code
  hwmon: (ina2xx) Fix various overflow issues
  hwmon: (ina2xx) Re-initialize chip using regmap functions
  hwmon: (ina2xx) Use local regmap pointer if used more than once
  hwmon: (ina2xx) Mark regmap_config as const
  ...
This commit is contained in:
Linus Torvalds 2024-09-18 12:40:48 +02:00
commit c27ea952c6
65 changed files with 3740 additions and 2880 deletions

View File

@ -45,12 +45,31 @@ properties:
the pwm uses a logic low output for 100% duty cycle. If set to 1 the pwm the pwm uses a logic low output for 100% duty cycle. If set to 1 the pwm
uses a logic high output for 100% duty cycle. uses a logic high output for 100% duty cycle.
$ref: /schemas/types.yaml#/definitions/uint32-array $ref: /schemas/types.yaml#/definitions/uint32-array
deprecated: true
minItems: 3 minItems: 3
maxItems: 3 maxItems: 3
items: items:
enum: [0, 1] enum: [0, 1]
default: 1 default: 1
"#pwm-cells":
const: 4
description: |
Number of cells in a PWM specifier.
- 0: The PWM channel
- 1: The PWM period in nanoseconds
- 90909091 (11 Hz)
- 71428571 (14 Hz)
- 45454545 (22 Hz)
- 34482759 (29 Hz)
- 28571429 (35 Hz)
- 22727273 (44 Hz)
- 17241379 (58 Hz)
- 11363636 (88 Hz)
- 44444 (22 kHz)
- 2: PWM flags 0 or PWM_POLARITY_INVERTED
- 3: The default PWM duty cycle in nanoseconds
patternProperties: patternProperties:
"^adi,bypass-attenuator-in[0-4]$": "^adi,bypass-attenuator-in[0-4]$":
description: | description: |
@ -81,6 +100,10 @@ patternProperties:
- smbalert# - smbalert#
- gpio - gpio
"^fan-[0-9]+$":
$ref: fan-common.yaml#
unevaluatedProperties: false
required: required:
- compatible - compatible
- reg - reg
@ -89,17 +112,27 @@ additionalProperties: false
examples: examples:
- | - |
#include <dt-bindings/pwm/pwm.h>
i2c { i2c {
#address-cells = <1>; #address-cells = <1>;
#size-cells = <0>; #size-cells = <0>;
hwmon@2e { pwm: hwmon@2e {
compatible = "adi,adt7476"; compatible = "adi,adt7476";
reg = <0x2e>; reg = <0x2e>;
adi,bypass-attenuator-in0 = <1>; adi,bypass-attenuator-in0 = <1>;
adi,bypass-attenuator-in1 = <0>; adi,bypass-attenuator-in1 = <0>;
adi,pwm-active-state = <1 0 1>;
adi,pin10-function = "smbalert#"; adi,pin10-function = "smbalert#";
adi,pin14-function = "tach4"; adi,pin14-function = "tach4";
#pwm-cells = <4>;
/* PWMs at 22.5 kHz frequency, 50% duty*/
fan-0 {
pwms = <&pwm 0 44444 0 22222>;
};
fan-1 {
pwms = <&pwm 2 44444 0 22222>;
};
}; };
}; };

View File

@ -0,0 +1,94 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/lltc,ltc2978.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Octal Digital Power-supply monitor/supervisor/sequencer/margin controller.
maintainers:
- Frank Li <Frank.Li@nxp.com>
properties:
compatible:
enum:
- lltc,ltc2972
- lltc,ltc2974
- lltc,ltc2975
- lltc,ltc2977
- lltc,ltc2978
- lltc,ltc2979
- lltc,ltc2980
- lltc,ltc3880
- lltc,ltc3882
- lltc,ltc3883
- lltc,ltc3884
- lltc,ltc3886
- lltc,ltc3887
- lltc,ltc3889
- lltc,ltc7880
- lltc,ltm2987
- lltc,ltm4664
- lltc,ltm4675
- lltc,ltm4676
- lltc,ltm4677
- lltc,ltm4678
- lltc,ltm4680
- lltc,ltm4686
- lltc,ltm4700
reg:
maxItems: 1
regulators:
type: object
description: |
list of regulators provided by this controller.
Valid names of regulators depend on number of supplies supported per device:
* ltc2972 vout0 - vout1
* ltc2974, ltc2975 : vout0 - vout3
* ltc2977, ltc2979, ltc2980, ltm2987 : vout0 - vout7
* ltc2978 : vout0 - vout7
* ltc3880, ltc3882, ltc3884, ltc3886, ltc3887, ltc3889 : vout0 - vout1
* ltc7880 : vout0 - vout1
* ltc3883 : vout0
* ltm4664 : vout0 - vout1
* ltm4675, ltm4676, ltm4677, ltm4678 : vout0 - vout1
* ltm4680, ltm4686 : vout0 - vout1
* ltm4700 : vout0 - vout1
patternProperties:
"^vout[0-7]$":
$ref: /schemas/regulator/regulator.yaml#
type: object
unevaluatedProperties: false
additionalProperties: false
required:
- compatible
- reg
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
regulator@5e {
compatible = "lltc,ltc2978";
reg = <0x5e>;
regulators {
vout0 {
regulator-name = "FPGA-2.5V";
};
vout2 {
regulator-name = "FPGA-1.5V";
};
};
};
};

View File

@ -1,62 +0,0 @@
ltc2978
Required properties:
- compatible: should contain one of:
* "lltc,ltc2972"
* "lltc,ltc2974"
* "lltc,ltc2975"
* "lltc,ltc2977"
* "lltc,ltc2978"
* "lltc,ltc2979"
* "lltc,ltc2980"
* "lltc,ltc3880"
* "lltc,ltc3882"
* "lltc,ltc3883"
* "lltc,ltc3884"
* "lltc,ltc3886"
* "lltc,ltc3887"
* "lltc,ltc3889"
* "lltc,ltc7880"
* "lltc,ltm2987"
* "lltc,ltm4664"
* "lltc,ltm4675"
* "lltc,ltm4676"
* "lltc,ltm4677"
* "lltc,ltm4678"
* "lltc,ltm4680"
* "lltc,ltm4686"
* "lltc,ltm4700"
- reg: I2C slave address
Optional properties:
- regulators: A node that houses a sub-node for each regulator controlled by
the device. Each sub-node is identified using the node's name, with valid
values listed below. The content of each sub-node is defined by the
standard binding for regulators; see regulator.txt.
Valid names of regulators depend on number of supplies supported per device:
* ltc2972 vout0 - vout1
* ltc2974, ltc2975 : vout0 - vout3
* ltc2977, ltc2979, ltc2980, ltm2987 : vout0 - vout7
* ltc2978 : vout0 - vout7
* ltc3880, ltc3882, ltc3884, ltc3886, ltc3887, ltc3889 : vout0 - vout1
* ltc7880 : vout0 - vout1
* ltc3883 : vout0
* ltm4664 : vout0 - vout1
* ltm4675, ltm4676, ltm4677, ltm4678 : vout0 - vout1
* ltm4680, ltm4686 : vout0 - vout1
* ltm4700 : vout0 - vout1
Example:
ltc2978@5e {
compatible = "lltc,ltc2978";
reg = <0x5e>;
regulators {
vout0 {
regulator-name = "FPGA-2.5V";
};
vout2 {
regulator-name = "FPGA-1.5V";
};
};
};

View File

@ -0,0 +1,70 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/maxim,max31790.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: The Maxim MAX31790 Fan Controller
maintainers:
- Guenter Roeck <linux@roeck-us.net>
- Chanh Nguyen <chanh@os.amperecomputing.com>
description: >
The MAX31790 controls the speeds of up to six fans using six
independent PWM outputs. The desired fan speeds (or PWM duty cycles)
are written through the I2C interface.
Datasheets:
https://datasheets.maximintegrated.com/en/ds/MAX31790.pdf
properties:
compatible:
const: maxim,max31790
reg:
maxItems: 1
clocks:
maxItems: 1
resets:
maxItems: 1
"#pwm-cells":
const: 1
patternProperties:
"^fan-[0-9]+$":
$ref: fan-common.yaml#
unevaluatedProperties: false
required:
- compatible
- reg
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
pwm_provider: fan-controller@20 {
compatible = "maxim,max31790";
reg = <0x20>;
clocks = <&sys_clk>;
resets = <&reset 0>;
#pwm-cells = <1>;
fan-0 {
pwms = <&pwm_provider 1>;
};
fan-1 {
pwms = <&pwm_provider 2>;
};
};
};

View File

@ -0,0 +1,43 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/sophgo,sg2042-hwmon-mcu.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Sophgo SG2042 onboard MCU support
maintainers:
- Inochi Amaoto <inochiama@outlook.com>
properties:
compatible:
const: sophgo,sg2042-hwmon-mcu
reg:
maxItems: 1
"#thermal-sensor-cells":
const: 1
required:
- compatible
- reg
- "#thermal-sensor-cells"
allOf:
- $ref: /schemas/thermal/thermal-sensor.yaml#
unevaluatedProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
hwmon@17 {
compatible = "sophgo,sg2042-hwmon-mcu";
reg = <0x17>;
#thermal-sensor-cells = <1>;
};
};

View File

@ -38,8 +38,6 @@ register/unregister functions::
void hwmon_device_unregister(struct device *dev); void hwmon_device_unregister(struct device *dev);
void devm_hwmon_device_unregister(struct device *dev);
char *hwmon_sanitize_name(const char *name); char *hwmon_sanitize_name(const char *name);
char *devm_hwmon_sanitize_name(struct device *dev, const char *name); char *devm_hwmon_sanitize_name(struct device *dev, const char *name);
@ -64,11 +62,6 @@ monitoring device structure. This function must be called from the driver
remove function if the hardware monitoring device was registered with remove function if the hardware monitoring device was registered with
hwmon_device_register_with_info. hwmon_device_register_with_info.
devm_hwmon_device_unregister does not normally have to be called. It is only
needed for error handling, and only needed if the driver probe fails after
the call to devm_hwmon_device_register_with_info and if the automatic (device
managed) removal would be too late.
All supported hwmon device registration functions only accept valid device All supported hwmon device registration functions only accept valid device
names. Device names including invalid characters (whitespace, '*', or '-') names. Device names including invalid characters (whitespace, '*', or '-')
will be rejected. The 'name' parameter is mandatory. will be rejected. The 'name' parameter is mandatory.

View File

@ -99,6 +99,10 @@ Sysfs entries for ina226, ina230 and ina231 only
------------------------------------------------ ------------------------------------------------
======================= ==================================================== ======================= ====================================================
curr1_lcrit Critical low current
curr1_crit Critical high current
curr1_lcrit_alarm Current critical low alarm
curr1_crit_alarm Current critical high alarm
in0_lcrit Critical low shunt voltage in0_lcrit Critical low shunt voltage
in0_crit Critical high shunt voltage in0_crit Critical high shunt voltage
in0_lcrit_alarm Shunt voltage critical low alarm in0_lcrit_alarm Shunt voltage critical low alarm

View File

@ -206,6 +206,7 @@ Hardware Monitoring Kernel Drivers
sch5636 sch5636
scpi-hwmon scpi-hwmon
sfctemp sfctemp
sg2042-mcu
sht15 sht15
sht21 sht21
sht3x sht3x

View File

@ -3,29 +3,29 @@ Kernel driver lm92
Supported chips: Supported chips:
* National Semiconductor LM92 * National Semiconductor / Texas Instruments LM92
Prefix: 'lm92' Prefix: 'lm92'
Addresses scanned: I2C 0x48 - 0x4b Addresses scanned: I2C 0x48 - 0x4b
Datasheet: http://www.national.com/pf/LM/LM92.html Datasheet: https://www.ti.com/lit/gpn/LM92
* National Semiconductor LM76 * National Semiconductor / Texas Instruments LM76
Prefix: 'lm92' Prefix: 'lm92'
Addresses scanned: none, force parameter needed Addresses scanned: none, must be instantiated explicitly
Datasheet: http://www.national.com/pf/LM/LM76.html Datasheet: https://www.ti.com/lit/gpn/LM76
* Maxim MAX6633/MAX6634/MAX6635 * Maxim /Analog Devices MAX6633/MAX6634/MAX6635
Prefix: 'max6635' Prefix: 'max6635'
Addresses scanned: none, force parameter needed Addresses scanned: none, must be instantiated explicitly
Datasheet: http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3074 Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/max6633-max6635.pdf
Authors: Authors:
@ -36,13 +36,13 @@ Authors:
Description Description
----------- -----------
This driver implements support for the National Semiconductor LM92 This driver implements support for the National Semiconductor / Texas
temperature sensor. Instruments LM92 temperature sensor.
Each LM92 temperature sensor supports a single temperature sensor. There are Each LM92 temperature sensor supports a single temperature sensor. There are
alarms for high, low, and critical thresholds. There's also an hysteresis to alarms for high, low, and critical thresholds. There's also an hysteresis to
control the thresholds for resetting alarms. control the thresholds for resetting alarms.
Support was added later for the LM76 and Maxim MAX6633/MAX6634/MAX6635, The driver also supports LM76 and Maxim MAX6633/MAX6634/MAX6635, which are
which are mostly compatible. They have not all been tested, so you mostly compatible but do not have a vendor ID register and therefore must be
may need to use the force parameter. instantiated explicitly.

View File

@ -27,7 +27,3 @@ All temperature values are given in degrees Celsius. Resolution
is 1.0 degree for the local temperature and for the remote temperature. is 1.0 degree for the local temperature and for the remote temperature.
Only the external sensor has high and low limits. Only the external sensor has high and low limits.
The max1619 driver will not update its values more frequently than every
other second; reading them more often will do no harm, but will return
'old' values.

View File

@ -10,41 +10,59 @@ Authors:
Description: Description:
------------ ------------
Handheld devices from One Netbook and Aya Neo provide fan readings and fan Handheld devices from OneNetbook, AOKZOE, AYANEO, And OrangePi provide fan
control through their embedded controllers. readings and fan control through their embedded controllers.
Currently only supports AMD boards from One X Player, AOK ZOE, and some Aya Currently supports OneXPlayer devices, AOKZOE, AYANEO, and OrangePi
Neo devices. One X Player Intel boards could be supported if we could figure handheld devices. AYANEO devices preceding the AIR and OneXPlayer devices
out the EC registers and values to write to since the EC layout and model is preceding the Mini A07 are not supportable as the EC model is different
different. Aya Neo devices preceding the AIR may not be supportable as the EC and do not have manual control capabilities.
model is different and do not appear to have manual control capabilities.
Some models have a toggle for changing the behaviour of the "Turbo/Silent" Some OneXPlayer and AOKZOE models have a toggle for changing the behaviour
button of the device. It will change the key event that it triggers with of the "Turbo/Silent" button of the device. It will change the key event
a flip of the `tt_toggle` attribute. See below for boards that support this that it triggers with a flip of the `tt_toggle` attribute. See below for
function. boards that support this function.
Supported devices Supported devices
----------------- -----------------
Currently the driver supports the following handhelds: Currently the driver supports the following handhelds:
- AOK ZOE A1 - AOKZOE A1
- AOK ZOE A1 PRO - AOKZOE A1 PRO
- Aya Neo 2 - AYANEO 2
- Aya Neo AIR - AYANEO 2S
- Aya Neo AIR Plus (Mendocino) - AYANEO AIR
- Aya Neo AIR Pro - AYANEO AIR 1S
- Aya Neo Geek - AYANEO AIR Plus (Mendocino)
- AYANEO AIR Pro
- AYANEO Flip DS
- AYANEO Flip KB
- AYANEO Geek
- AYANEO Geek 1S
- AYANEO KUN
- OneXPlayer 2
- OneXPlayer 2 Pro
- OneXPlayer AMD - OneXPlayer AMD
- OneXPlayer mini AMD - OneXPlayer mini AMD
- OneXPlayer mini AMD PRO - OneXPlayer mini AMD PRO
- OneXPlayer OneXFly
- OneXPlayer X1 A
- OneXPlayer X1 i
- OneXPlayer X1 mini
- OrangePi NEO-01
"Turbo/Silent" button behaviour toggle is only supported on: "Turbo/Silent" button behaviour toggle is only supported on:
- AOK ZOE A1 - AOK ZOE A1
- AOK ZOE A1 PRO - AOK ZOE A1 PRO
- OneXPlayer 2
- OneXPlayer 2 Pro
- OneXPlayer mini AMD (only with updated alpha BIOS) - OneXPlayer mini AMD (only with updated alpha BIOS)
- OneXPlayer mini AMD PRO - OneXPlayer mini AMD PRO
- OneXPlayer OneXFly
- OneXPlayer X1 A
- OneXPlayer X1 i
- OneXPlayer X1 mini
Sysfs entries Sysfs entries
------------- -------------
@ -52,7 +70,7 @@ Sysfs entries
The following attributes are supported: The following attributes are supported:
fan1_input fan1_input
Read Only. Reads current fan RMP. Read Only. Reads current fan RPM.
pwm1_enable pwm1_enable
Read Write. Enable manual fan control. Write "1" to set to manual, write "0" Read Write. Enable manual fan control. Write "1" to set to manual, write "0"

View File

@ -0,0 +1,78 @@
.. SPDX-License-Identifier: GPL-2.0
Kernel driver sg2042-mcu
========================
Supported chips:
* Onboard MCU for sg2042
Addresses scanned: -
Prefix: 'sg2042-mcu'
Authors:
- Inochi Amaoto <inochiama@outlook.com>
Description
-----------
This driver supprts hardware monitoring for onboard MCU with
i2c interface.
Usage Notes
-----------
This driver does not auto-detect devices. You will have to instantiate
the devices explicitly.
Please see Documentation/i2c/instantiating-devices.rst for details.
Sysfs Attributes
----------------
The following table shows the standard entries support by the driver:
================= =====================================================
Name Description
================= =====================================================
temp1_input Measured temperature of SoC
temp1_crit Critical high temperature
temp1_crit_hyst hysteresis temperature restore from Critical
temp2_input Measured temperature of the base board
================= =====================================================
The following table shows the extra entries support by the driver
(the MCU device is in i2c subsystem):
================= ======= =============================================
Name Perm Description
================= ======= =============================================
reset_count RO Reset count of the SoC
uptime RO Seconds after the MCU is powered
reset_reason RO Reset reason for the last reset
repower_policy RW Execution policy when triggering repower
================= ======= =============================================
``repower_policy``
The repower is triggered when the temperature of the SoC falls below
the hysteresis temperature after triggering a shutdown due to
reaching the critical temperature.
The valid values for this entry are "repower" and "keep". "keep" will
leave the SoC down when the triggering repower, and "repower" will
boot the SoC.
Debugfs Interfaces
------------------
If debugfs is available, this driver exposes some hardware specific
data in ``/sys/kernel/debug/sg2042-mcu/*/``.
================= ======= =============================================
Name Format Description
================= ======= =============================================
firmware_version 0x%02x firmware version of the MCU
pcb_version 0x%02x version number of the base board
board_type 0x%02x identifiers for the base board
mcu_type %d type of the MCU: 0 is STM32, 1 is GD32
================= ======= =============================================

View File

@ -15295,6 +15295,12 @@ S: Maintained
F: Documentation/hwmon/surface_fan.rst F: Documentation/hwmon/surface_fan.rst
F: drivers/hwmon/surface_fan.c F: drivers/hwmon/surface_fan.c
MICROSOFT SURFACE SENSOR THERMAL DRIVER
M: Maximilian Luz <luzmaximilian@gmail.com>
L: linux-hwmon@vger.kernel.org
S: Maintained
F: drivers/hwmon/surface_temp.c
MICROSOFT SURFACE GPE LID SUPPORT DRIVER MICROSOFT SURFACE GPE LID SUPPORT DRIVER
M: Maximilian Luz <luzmaximilian@gmail.com> M: Maximilian Luz <luzmaximilian@gmail.com>
L: platform-driver-x86@vger.kernel.org L: platform-driver-x86@vger.kernel.org

View File

@ -1511,9 +1511,10 @@ config SENSORS_LM90
config SENSORS_LM92 config SENSORS_LM92
tristate "National Semiconductor LM92 and compatibles" tristate "National Semiconductor LM92 and compatibles"
depends on I2C depends on I2C
select REGMAP_I2C
help help
If you say yes here you get support for National Semiconductor LM92 If you say yes here you get support for National Semiconductor LM92
and Maxim MAX6635 sensor chips. and LM76 as well as Maxim MAX6633/6634/6635 sensor chips.
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called lm92. will be called lm92.
@ -1532,6 +1533,7 @@ config SENSORS_LM93
config SENSORS_LM95234 config SENSORS_LM95234
tristate "National Semiconductor LM95234 and compatibles" tristate "National Semiconductor LM95234 and compatibles"
depends on I2C depends on I2C
select REGMAP_I2C
help help
If you say yes here you get support for the LM95233 and LM95234 If you say yes here you get support for the LM95233 and LM95234
temperature sensor chips. temperature sensor chips.
@ -2066,6 +2068,17 @@ config SENSORS_SFCTEMP
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called sfctemp. will be called sfctemp.
config SENSORS_SG2042_MCU
tristate "Sophgo onboard MCU support"
depends on I2C
depends on ARCH_SOPHGO || COMPILE_TEST
help
Support for onboard MCU of Sophgo SG2042 SoCs. This mcu provides
power control and some basic information.
This driver can be built as a module. If so, the module
will be called sg2042-mcu.
config SENSORS_SURFACE_FAN config SENSORS_SURFACE_FAN
tristate "Surface Fan Driver" tristate "Surface Fan Driver"
depends on SURFACE_AGGREGATOR depends on SURFACE_AGGREGATOR
@ -2080,6 +2093,17 @@ config SENSORS_SURFACE_FAN
Select M or Y here, if you want to be able to read the fan's speed. Select M or Y here, if you want to be able to read the fan's speed.
config SENSORS_SURFACE_TEMP
tristate "Microsoft Surface Thermal Sensor Driver"
depends on SURFACE_AGGREGATOR
depends on SURFACE_AGGREGATOR_BUS
help
Driver for monitoring thermal sensors connected via the Surface
Aggregator Module (embedded controller) on Microsoft Surface devices.
This driver can also be built as a module. If so, the module
will be called surface_temp.
config SENSORS_ADC128D818 config SENSORS_ADC128D818
tristate "Texas Instruments ADC128D818" tristate "Texas Instruments ADC128D818"
depends on I2C depends on I2C

View File

@ -194,6 +194,7 @@ obj-$(CONFIG_SENSORS_SCH56XX_COMMON)+= sch56xx-common.o
obj-$(CONFIG_SENSORS_SCH5627) += sch5627.o obj-$(CONFIG_SENSORS_SCH5627) += sch5627.o
obj-$(CONFIG_SENSORS_SCH5636) += sch5636.o obj-$(CONFIG_SENSORS_SCH5636) += sch5636.o
obj-$(CONFIG_SENSORS_SFCTEMP) += sfctemp.o obj-$(CONFIG_SENSORS_SFCTEMP) += sfctemp.o
obj-$(CONFIG_SENSORS_SG2042_MCU) += sg2042-mcu.o
obj-$(CONFIG_SENSORS_SL28CPLD) += sl28cpld-hwmon.o obj-$(CONFIG_SENSORS_SL28CPLD) += sl28cpld-hwmon.o
obj-$(CONFIG_SENSORS_SHT15) += sht15.o obj-$(CONFIG_SENSORS_SHT15) += sht15.o
obj-$(CONFIG_SENSORS_SHT21) += sht21.o obj-$(CONFIG_SENSORS_SHT21) += sht21.o
@ -209,6 +210,7 @@ obj-$(CONFIG_SENSORS_SPARX5) += sparx5-temp.o
obj-$(CONFIG_SENSORS_SPD5118) += spd5118.o obj-$(CONFIG_SENSORS_SPD5118) += spd5118.o
obj-$(CONFIG_SENSORS_STTS751) += stts751.o obj-$(CONFIG_SENSORS_STTS751) += stts751.o
obj-$(CONFIG_SENSORS_SURFACE_FAN)+= surface_fan.o obj-$(CONFIG_SENSORS_SURFACE_FAN)+= surface_fan.o
obj-$(CONFIG_SENSORS_SURFACE_TEMP)+= surface_temp.o
obj-$(CONFIG_SENSORS_SY7636A) += sy7636a-hwmon.o obj-$(CONFIG_SENSORS_SY7636A) += sy7636a-hwmon.o
obj-$(CONFIG_SENSORS_AMC6821) += amc6821.o obj-$(CONFIG_SENSORS_AMC6821) += amc6821.o
obj-$(CONFIG_SENSORS_TC74) += tc74.o obj-$(CONFIG_SENSORS_TC74) += tc74.o

View File

@ -728,30 +728,22 @@ static const int adt7470_freq_map[] = {
static int pwm1_freq_get(struct device *dev) static int pwm1_freq_get(struct device *dev)
{ {
struct adt7470_data *data = dev_get_drvdata(dev); struct adt7470_data *data = dev_get_drvdata(dev);
unsigned int cfg_reg_1, cfg_reg_2; unsigned int regs[2] = {ADT7470_REG_CFG, ADT7470_REG_CFG_2};
u8 cfg_reg[2];
int index; int index;
int err; int err;
mutex_lock(&data->lock); err = regmap_multi_reg_read(data->regmap, regs, cfg_reg, 2);
err = regmap_read(data->regmap, ADT7470_REG_CFG, &cfg_reg_1); if (err)
if (err < 0) return err;
goto out;
err = regmap_read(data->regmap, ADT7470_REG_CFG_2, &cfg_reg_2);
if (err < 0)
goto out;
mutex_unlock(&data->lock);
index = (cfg_reg_2 & ADT7470_FREQ_MASK) >> ADT7470_FREQ_SHIFT; index = (cfg_reg[1] & ADT7470_FREQ_MASK) >> ADT7470_FREQ_SHIFT;
if (!(cfg_reg_1 & ADT7470_CFG_LF)) if (!(cfg_reg[0] & ADT7470_CFG_LF))
index += 8; index += 8;
if (index >= ARRAY_SIZE(adt7470_freq_map)) if (index >= ARRAY_SIZE(adt7470_freq_map))
index = ARRAY_SIZE(adt7470_freq_map) - 1; index = ARRAY_SIZE(adt7470_freq_map) - 1;
return adt7470_freq_map[index]; return adt7470_freq_map[index];
out:
mutex_unlock(&data->lock);
return err;
} }
static int adt7470_pwm_read(struct device *dev, u32 attr, int channel, long *val) static int adt7470_pwm_read(struct device *dev, u32 attr, int channel, long *val)

View File

@ -21,6 +21,8 @@
#include <linux/of.h> #include <linux/of.h>
#include <linux/util_macros.h> #include <linux/util_macros.h>
#include <dt-bindings/pwm/pwm.h>
/* Indexes for the sysfs hooks */ /* Indexes for the sysfs hooks */
enum adt_sysfs_id { enum adt_sysfs_id {
INPUT = 0, INPUT = 0,
@ -1662,6 +1664,130 @@ static int adt7475_set_pwm_polarity(struct i2c_client *client)
return 0; return 0;
} }
struct adt7475_pwm_config {
int index;
int freq;
int flags;
int duty;
};
static int _adt7475_pwm_properties_parse_args(u32 args[4], struct adt7475_pwm_config *cfg)
{
int freq_hz;
int duty;
if (args[1] == 0)
return -EINVAL;
freq_hz = 1000000000UL / args[1];
if (args[3] >= args[1])
duty = 255;
else
duty = div_u64(255ULL * args[3], args[1]);
cfg->index = args[0];
cfg->freq = find_closest(freq_hz, pwmfreq_table, ARRAY_SIZE(pwmfreq_table));
cfg->flags = args[2];
cfg->duty = duty;
return 0;
}
static int adt7475_pwm_properties_parse_reference_args(struct fwnode_handle *fwnode,
struct adt7475_pwm_config *cfg)
{
int ret, i;
struct fwnode_reference_args rargs = {};
u32 args[4] = {};
ret = fwnode_property_get_reference_args(fwnode, "pwms", "#pwm-cells", 0, 0, &rargs);
if (ret)
return ret;
if (rargs.nargs != 4) {
fwnode_handle_put(rargs.fwnode);
return -EINVAL;
}
for (i = 0; i < 4; i++)
args[i] = rargs.args[i];
ret = _adt7475_pwm_properties_parse_args(args, cfg);
fwnode_handle_put(rargs.fwnode);
return ret;
}
static int adt7475_pwm_properties_parse_args(struct fwnode_handle *fwnode,
struct adt7475_pwm_config *cfg)
{
int ret;
u32 args[4] = {};
ret = fwnode_property_read_u32_array(fwnode, "pwms", args, ARRAY_SIZE(args));
if (ret)
return ret;
return _adt7475_pwm_properties_parse_args(args, cfg);
}
static int adt7475_fan_pwm_config(struct i2c_client *client)
{
struct adt7475_data *data = i2c_get_clientdata(client);
struct fwnode_handle *child;
struct adt7475_pwm_config cfg = {};
int ret;
device_for_each_child_node(&client->dev, child) {
if (!fwnode_property_present(child, "pwms"))
continue;
if (is_of_node(child))
ret = adt7475_pwm_properties_parse_reference_args(child, &cfg);
else
ret = adt7475_pwm_properties_parse_args(child, &cfg);
if (cfg.index >= ADT7475_PWM_COUNT)
return -EINVAL;
ret = adt7475_read(PWM_CONFIG_REG(cfg.index));
if (ret < 0)
return ret;
data->pwm[CONTROL][cfg.index] = ret;
if (cfg.flags & PWM_POLARITY_INVERTED)
data->pwm[CONTROL][cfg.index] |= BIT(4);
else
data->pwm[CONTROL][cfg.index] &= ~BIT(4);
/* Force to manual mode so PWM values take effect */
data->pwm[CONTROL][cfg.index] &= ~0xE0;
data->pwm[CONTROL][cfg.index] |= 0x07 << 5;
ret = i2c_smbus_write_byte_data(client, PWM_CONFIG_REG(cfg.index),
data->pwm[CONTROL][cfg.index]);
if (ret)
return ret;
data->pwm[INPUT][cfg.index] = cfg.duty;
ret = i2c_smbus_write_byte_data(client, PWM_REG(cfg.index),
data->pwm[INPUT][cfg.index]);
if (ret)
return ret;
data->range[cfg.index] = adt7475_read(TEMP_TRANGE_REG(cfg.index));
data->range[cfg.index] &= ~0xf;
data->range[cfg.index] |= cfg.freq;
ret = i2c_smbus_write_byte_data(client, TEMP_TRANGE_REG(cfg.index),
data->range[cfg.index]);
if (ret)
return ret;
}
return 0;
}
static int adt7475_probe(struct i2c_client *client) static int adt7475_probe(struct i2c_client *client)
{ {
enum chips chip; enum chips chip;
@ -1774,6 +1900,10 @@ static int adt7475_probe(struct i2c_client *client)
if (ret && ret != -EINVAL) if (ret && ret != -EINVAL)
dev_warn(&client->dev, "Error configuring pwm polarity\n"); dev_warn(&client->dev, "Error configuring pwm polarity\n");
ret = adt7475_fan_pwm_config(client);
if (ret)
dev_warn(&client->dev, "Error %d configuring fan/pwm\n", ret);
/* Start monitoring */ /* Start monitoring */
switch (chip) { switch (chip) {
case adt7475: case adt7475:

View File

@ -170,21 +170,15 @@ static int adt7x10_temp_write(struct adt7x10_data *data, int index, long temp)
static int adt7x10_hyst_read(struct adt7x10_data *data, int index, long *val) static int adt7x10_hyst_read(struct adt7x10_data *data, int index, long *val)
{ {
int hyst, temp, ret; unsigned int regs[2] = {ADT7X10_T_HYST, ADT7X10_REG_TEMP[index]};
int hyst, ret;
u16 regdata[2];
mutex_lock(&data->update_lock); ret = regmap_multi_reg_read(data->regmap, regs, regdata, 2);
ret = regmap_read(data->regmap, ADT7X10_T_HYST, &hyst);
if (ret) {
mutex_unlock(&data->update_lock);
return ret;
}
ret = regmap_read(data->regmap, ADT7X10_REG_TEMP[index], &temp);
mutex_unlock(&data->update_lock);
if (ret) if (ret)
return ret; return ret;
hyst = (hyst & ADT7X10_T_HYST_MASK) * 1000; hyst = (regdata[0] & ADT7X10_T_HYST_MASK) * 1000;
/* /*
* hysteresis is stored as a 4 bit offset in the device, convert it * hysteresis is stored as a 4 bit offset in the device, convert it
@ -194,7 +188,7 @@ static int adt7x10_hyst_read(struct adt7x10_data *data, int index, long *val)
if (index == adt7x10_t_alarm_low) if (index == adt7x10_t_alarm_low)
hyst = -hyst; hyst = -hyst;
*val = ADT7X10_REG_TO_TEMP(data, temp) - hyst; *val = ADT7X10_REG_TO_TEMP(data, regdata[1]) - hyst;
return 0; return 0;
} }

View File

@ -136,29 +136,25 @@ struct amc6821_data {
*/ */
static int amc6821_get_auto_point_temps(struct regmap *regmap, int channel, u8 *temps) static int amc6821_get_auto_point_temps(struct regmap *regmap, int channel, u8 *temps)
{ {
u32 pwm, regval; u32 regs[] = {
AMC6821_REG_DCY_LOW_TEMP,
AMC6821_REG_PSV_TEMP,
channel ? AMC6821_REG_RTEMP_FAN_CTRL : AMC6821_REG_LTEMP_FAN_CTRL
};
u8 regvals[3];
int slope;
int err; int err;
err = regmap_read(regmap, AMC6821_REG_DCY_LOW_TEMP, &pwm); err = regmap_multi_reg_read(regmap, regs, regvals, 3);
if (err) if (err)
return err; return err;
temps[0] = regvals[1];
err = regmap_read(regmap, AMC6821_REG_PSV_TEMP, &regval); temps[1] = FIELD_GET(AMC6821_TEMP_LIMIT_MASK, regvals[2]) * 4;
if (err)
return err;
temps[0] = regval;
err = regmap_read(regmap,
channel ? AMC6821_REG_RTEMP_FAN_CTRL : AMC6821_REG_LTEMP_FAN_CTRL,
&regval);
if (err)
return err;
temps[1] = FIELD_GET(AMC6821_TEMP_LIMIT_MASK, regval) * 4;
/* slope is 32 >> <slope bits> in °C */ /* slope is 32 >> <slope bits> in °C */
regval = 32 >> FIELD_GET(AMC6821_TEMP_SLOPE_MASK, regval); slope = 32 >> FIELD_GET(AMC6821_TEMP_SLOPE_MASK, regvals[2]);
if (regval) if (slope)
temps[2] = temps[1] + DIV_ROUND_CLOSEST(255 - pwm, regval); temps[2] = temps[1] + DIV_ROUND_CLOSEST(255 - regvals[0], slope);
else else
temps[2] = 255; temps[2] = 255;

View File

@ -456,7 +456,6 @@ static int aspeed_pwm_tach_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev, *hwmon; struct device *dev = &pdev->dev, *hwmon;
int ret; int ret;
struct device_node *child;
struct aspeed_pwm_tach_data *priv; struct aspeed_pwm_tach_data *priv;
struct pwm_chip *chip; struct pwm_chip *chip;
@ -498,10 +497,9 @@ static int aspeed_pwm_tach_probe(struct platform_device *pdev)
if (ret) if (ret)
return dev_err_probe(dev, ret, "Failed to add PWM chip\n"); return dev_err_probe(dev, ret, "Failed to add PWM chip\n");
for_each_child_of_node(dev->of_node, child) { for_each_child_of_node_scoped(dev->of_node, child) {
ret = aspeed_create_fan_monitor(dev, child, priv); ret = aspeed_create_fan_monitor(dev, child, priv);
if (ret) { if (ret) {
of_node_put(child);
dev_warn(dev, "Failed to create fan %d", ret); dev_warn(dev, "Failed to create fan %d", ret);
return 0; return 0;
} }

View File

@ -907,7 +907,7 @@ static void aspeed_pwm_tacho_remove(void *data)
static int aspeed_pwm_tacho_probe(struct platform_device *pdev) static int aspeed_pwm_tacho_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct device_node *np, *child; struct device_node *np;
struct aspeed_pwm_tacho_data *priv; struct aspeed_pwm_tacho_data *priv;
void __iomem *regs; void __iomem *regs;
struct device *hwmon; struct device *hwmon;
@ -951,12 +951,10 @@ static int aspeed_pwm_tacho_probe(struct platform_device *pdev)
aspeed_create_type(priv); aspeed_create_type(priv);
for_each_child_of_node(np, child) { for_each_child_of_node_scoped(np, child) {
ret = aspeed_create_fan(dev, child, priv); ret = aspeed_create_fan(dev, child, priv);
if (ret) { if (ret)
of_node_put(child);
return ret; return ret;
}
} }
priv->groups[0] = &pwm_dev_group; priv->groups[0] = &pwm_dev_group;

View File

@ -740,37 +740,26 @@ static int cc2_probe(struct i2c_client *client)
data->client = client; data->client = client;
data->regulator = devm_regulator_get_exclusive(dev, "vdd"); data->regulator = devm_regulator_get_exclusive(dev, "vdd");
if (IS_ERR(data->regulator)) { if (IS_ERR(data->regulator))
dev_err_probe(dev, PTR_ERR(data->regulator), return dev_err_probe(dev, PTR_ERR(data->regulator),
"Failed to get regulator\n"); "Failed to get regulator\n");
return PTR_ERR(data->regulator);
}
ret = cc2_request_ready_irq(data, dev); ret = cc2_request_ready_irq(data, dev);
if (ret) { if (ret)
dev_err_probe(dev, ret, "Failed to request ready irq\n"); return dev_err_probe(dev, ret, "Failed to request ready irq\n");
return ret;
}
ret = cc2_request_alarm_irqs(data, dev); ret = cc2_request_alarm_irqs(data, dev);
if (ret) { if (ret)
dev_err_probe(dev, ret, "Failed to request alarm irqs\n"); return dev_err_probe(dev, ret, "Failed to request alarm irqs\n");
goto disable;
}
data->hwmon = devm_hwmon_device_register_with_info(dev, client->name, data->hwmon = devm_hwmon_device_register_with_info(dev, client->name,
data, &cc2_chip_info, data, &cc2_chip_info,
NULL); NULL);
if (IS_ERR(data->hwmon)) { if (IS_ERR(data->hwmon))
dev_err_probe(dev, PTR_ERR(data->hwmon), return dev_err_probe(dev, PTR_ERR(data->hwmon),
"Failed to register hwmon device\n"); "Failed to register hwmon device\n");
ret = PTR_ERR(data->hwmon);
}
disable: return 0;
cc2_disable(data);
return ret;
} }
static void cc2_remove(struct i2c_client *client) static void cc2_remove(struct i2c_client *client)

View File

@ -1488,6 +1488,14 @@ static const struct dmi_system_id i8k_whitelist_fan_control[] __initconst = {
}, },
.driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3], .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3],
}, },
{
.ident = "Dell Latitude 7320",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Latitude 7320"),
},
.driver_data = (void *)&i8k_fan_control_data[I8K_FAN_30A3_31A3],
},
{ {
.ident = "Dell Latitude E6440", .ident = "Dell Latitude E6440",
.matches = { .matches = {

View File

@ -400,6 +400,7 @@ static const struct of_device_id gsc_hwmon_of_match[] = {
{ .compatible = "gw,gsc-adc", }, { .compatible = "gw,gsc-adc", },
{} {}
}; };
MODULE_DEVICE_TABLE(of, gsc_hwmon_of_match);
static struct platform_driver gsc_hwmon_driver = { static struct platform_driver gsc_hwmon_driver = {
.driver = { .driver = {

View File

@ -1188,24 +1188,6 @@ error:
} }
EXPORT_SYMBOL_GPL(devm_hwmon_device_register_with_info); EXPORT_SYMBOL_GPL(devm_hwmon_device_register_with_info);
static int devm_hwmon_match(struct device *dev, void *res, void *data)
{
struct device **hwdev = res;
return *hwdev == data;
}
/**
* devm_hwmon_device_unregister - removes a previously registered hwmon device
*
* @dev: the parent device of the device to unregister
*/
void devm_hwmon_device_unregister(struct device *dev)
{
WARN_ON(devres_release(dev, devm_hwmon_release, devm_hwmon_match, dev));
}
EXPORT_SYMBOL_GPL(devm_hwmon_device_unregister);
static char *__hwmon_sanitize_name(struct device *dev, const char *old_name) static char *__hwmon_sanitize_name(struct device *dev, const char *old_name)
{ {
char *name, *p; char *name, *p;

File diff suppressed because it is too large Load Diff

View File

@ -813,7 +813,6 @@ static int ina3221_probe_child_from_dt(struct device *dev,
static int ina3221_probe_from_dt(struct device *dev, struct ina3221_data *ina) static int ina3221_probe_from_dt(struct device *dev, struct ina3221_data *ina)
{ {
const struct device_node *np = dev->of_node; const struct device_node *np = dev->of_node;
struct device_node *child;
int ret; int ret;
/* Compatible with non-DT platforms */ /* Compatible with non-DT platforms */
@ -822,12 +821,10 @@ static int ina3221_probe_from_dt(struct device *dev, struct ina3221_data *ina)
ina->single_shot = of_property_read_bool(np, "ti,single-shot"); ina->single_shot = of_property_read_bool(np, "ti,single-shot");
for_each_child_of_node(np, child) { for_each_child_of_node_scoped(np, child) {
ret = ina3221_probe_child_from_dt(dev, child, ina); ret = ina3221_probe_child_from_dt(dev, child, ina);
if (ret) { if (ret)
of_node_put(child);
return ret; return ret;
}
} }
return 0; return 0;

View File

@ -438,16 +438,21 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
data->disp_negative = true; data->disp_negative = true;
} }
if (boot_cpu_data.x86 == 0x15 && data->is_zen = cpu_feature_enabled(X86_FEATURE_ZEN);
if (data->is_zen) {
data->temp_adjust_mask = ZEN_CUR_TEMP_RANGE_SEL_MASK;
data->read_tempreg = read_tempreg_nb_zen;
} else if (boot_cpu_data.x86 == 0x15 &&
((boot_cpu_data.x86_model & 0xf0) == 0x60 || ((boot_cpu_data.x86_model & 0xf0) == 0x60 ||
(boot_cpu_data.x86_model & 0xf0) == 0x70)) { (boot_cpu_data.x86_model & 0xf0) == 0x70)) {
data->read_htcreg = read_htcreg_nb_f15; data->read_htcreg = read_htcreg_nb_f15;
data->read_tempreg = read_tempreg_nb_f15; data->read_tempreg = read_tempreg_nb_f15;
} else if (boot_cpu_data.x86 == 0x17 || boot_cpu_data.x86 == 0x18) { } else {
data->temp_adjust_mask = ZEN_CUR_TEMP_RANGE_SEL_MASK; data->read_htcreg = read_htcreg_pci;
data->read_tempreg = read_tempreg_nb_zen; data->read_tempreg = read_tempreg_pci;
data->is_zen = true; }
if (boot_cpu_data.x86 == 0x17 || boot_cpu_data.x86 == 0x18) {
switch (boot_cpu_data.x86_model) { switch (boot_cpu_data.x86_model) {
case 0x1: /* Zen */ case 0x1: /* Zen */
case 0x8: /* Zen+ */ case 0x8: /* Zen+ */
@ -469,10 +474,6 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
break; break;
} }
} else if (boot_cpu_data.x86 == 0x19) { } else if (boot_cpu_data.x86 == 0x19) {
data->temp_adjust_mask = ZEN_CUR_TEMP_RANGE_SEL_MASK;
data->read_tempreg = read_tempreg_nb_zen;
data->is_zen = true;
switch (boot_cpu_data.x86_model) { switch (boot_cpu_data.x86_model) {
case 0x0 ... 0x1: /* Zen3 SP3/TR */ case 0x0 ... 0x1: /* Zen3 SP3/TR */
case 0x8: /* Zen3 TR Chagall */ case 0x8: /* Zen3 TR Chagall */
@ -496,13 +497,6 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
k10temp_get_ccd_support(data, 12); k10temp_get_ccd_support(data, 12);
break; break;
} }
} else if (boot_cpu_data.x86 == 0x1a) {
data->temp_adjust_mask = ZEN_CUR_TEMP_RANGE_SEL_MASK;
data->read_tempreg = read_tempreg_nb_zen;
data->is_zen = true;
} else {
data->read_htcreg = read_htcreg_pci;
data->read_tempreg = read_tempreg_pci;
} }
for (i = 0; i < ARRAY_SIZE(tctl_offset_table); i++) { for (i = 0; i < ARRAY_SIZE(tctl_offset_table); i++) {

View File

@ -2674,19 +2674,16 @@ static int lm90_parse_dt_channel_info(struct i2c_client *client,
struct lm90_data *data) struct lm90_data *data)
{ {
int err; int err;
struct device_node *child;
struct device *dev = &client->dev; struct device *dev = &client->dev;
const struct device_node *np = dev->of_node; const struct device_node *np = dev->of_node;
for_each_child_of_node(np, child) { for_each_child_of_node_scoped(np, child) {
if (strcmp(child->name, "channel")) if (strcmp(child->name, "channel"))
continue; continue;
err = lm90_probe_channel_from_dt(client, child, data); err = lm90_probe_channel_from_dt(client, child, data);
if (err) { if (err)
of_node_put(child);
return err; return err;
}
} }
return 0; return 0;

View File

@ -27,15 +27,14 @@
* with the LM92. * with the LM92.
*/ */
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/hwmon.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/jiffies.h> #include <linux/regmap.h>
#include <linux/slab.h>
/* /*
* The LM92 and MAX6635 have 2 two-state pins for address selection, * The LM92 and MAX6635 have 2 two-state pins for address selection,
@ -43,8 +42,6 @@
*/ */
static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b,
I2C_CLIENT_END }; I2C_CLIENT_END };
enum chips { lm92, max6635 };
/* The LM92 registers */ /* The LM92 registers */
#define LM92_REG_CONFIG 0x01 /* 8-bit, RW */ #define LM92_REG_CONFIG 0x01 /* 8-bit, RW */
#define LM92_REG_TEMP 0x00 /* 16-bit, RO */ #define LM92_REG_TEMP 0x00 /* 16-bit, RO */
@ -66,10 +63,10 @@ static inline int TEMP_FROM_REG(s16 reg)
return reg / 8 * 625 / 10; return reg / 8 * 625 / 10;
} }
static inline s16 TEMP_TO_REG(long val) static inline s16 TEMP_TO_REG(long val, int resolution)
{ {
val = clamp_val(val, -60000, 160000); val = clamp_val(val, -60000, 160000);
return val * 10 / 625 * 8; return DIV_ROUND_CLOSEST(val << (resolution - 9), 1000) << (16 - resolution);
} }
/* Alarm flags are stored in the 3 LSB of the temperature register */ /* Alarm flags are stored in the 3 LSB of the temperature register */
@ -78,239 +75,336 @@ static inline u8 ALARMS_FROM_REG(s16 reg)
return reg & 0x0007; return reg & 0x0007;
} }
enum temp_index {
t_input,
t_crit,
t_min,
t_max,
t_hyst,
t_num_regs
};
static const u8 regs[t_num_regs] = {
[t_input] = LM92_REG_TEMP,
[t_crit] = LM92_REG_TEMP_CRIT,
[t_min] = LM92_REG_TEMP_LOW,
[t_max] = LM92_REG_TEMP_HIGH,
[t_hyst] = LM92_REG_TEMP_HYST,
};
/* Client data (each client gets its own) */ /* Client data (each client gets its own) */
struct lm92_data { struct lm92_data {
struct i2c_client *client; struct regmap *regmap;
struct mutex update_lock; struct mutex update_lock;
bool valid; /* false until following fields are valid */ int resolution;
unsigned long last_updated; /* in jiffies */
/* registers values */
s16 temp[t_num_regs]; /* index with enum temp_index */
}; };
/* static int lm92_temp_read(struct lm92_data *data, u32 attr, int channel, long *val)
* Sysfs attributes and callback functions
*/
static struct lm92_data *lm92_update_device(struct device *dev)
{ {
struct lm92_data *data = dev_get_drvdata(dev); int reg = -1, hyst_reg = -1, alarm_bit = 0;
struct i2c_client *client = data->client; struct regmap *regmap = data->regmap;
int i; u32 temp;
int ret;
mutex_lock(&data->update_lock); switch (attr) {
case hwmon_temp_input:
if (time_after(jiffies, data->last_updated + HZ) || reg = LM92_REG_TEMP;
!data->valid) { break;
dev_dbg(&client->dev, "Updating lm92 data\n"); case hwmon_temp_min:
for (i = 0; i < t_num_regs; i++) { reg = LM92_REG_TEMP_LOW;
data->temp[i] = break;
i2c_smbus_read_word_swapped(client, regs[i]); case hwmon_temp_max:
} reg = LM92_REG_TEMP_HIGH;
data->last_updated = jiffies; break;
data->valid = true; case hwmon_temp_crit:
reg = LM92_REG_TEMP_CRIT;
break;
case hwmon_temp_min_hyst:
hyst_reg = LM92_REG_TEMP_LOW;
break;
case hwmon_temp_max_hyst:
hyst_reg = LM92_REG_TEMP_HIGH;
break;
case hwmon_temp_crit_hyst:
hyst_reg = LM92_REG_TEMP_CRIT;
break;
case hwmon_temp_min_alarm:
alarm_bit = 0;
break;
case hwmon_temp_max_alarm:
alarm_bit = 1;
break;
case hwmon_temp_crit_alarm:
alarm_bit = 2;
break;
default:
return -EOPNOTSUPP;
} }
if (reg >= 0) {
ret = regmap_read(regmap, reg, &temp);
if (ret < 0)
return ret;
*val = TEMP_FROM_REG(temp);
} else if (hyst_reg >= 0) {
u32 regs[2] = { hyst_reg, LM92_REG_TEMP_HYST };
u16 regvals[2];
mutex_unlock(&data->update_lock); ret = regmap_multi_reg_read(regmap, regs, regvals, 2);
if (ret)
return data; return ret;
if (attr == hwmon_temp_min_hyst)
*val = TEMP_FROM_REG(regvals[0]) + TEMP_FROM_REG(regvals[1]);
else
*val = TEMP_FROM_REG(regvals[0]) - TEMP_FROM_REG(regvals[1]);
} else {
ret = regmap_read(regmap, LM92_REG_TEMP, &temp);
if (ret)
return ret;
*val = !!(temp & BIT(alarm_bit));
}
return 0;
} }
static ssize_t temp_show(struct device *dev, struct device_attribute *devattr, static int lm92_chip_read(struct lm92_data *data, u32 attr, long *val)
char *buf)
{ {
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); u32 temp;
struct lm92_data *data = lm92_update_device(dev); int ret;
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[attr->index])); switch (attr) {
case hwmon_chip_alarms:
ret = regmap_read(data->regmap, LM92_REG_TEMP, &temp);
if (ret)
return ret;
*val = ALARMS_FROM_REG(temp);
break;
default:
return -EOPNOTSUPP;
}
return 0;
} }
static ssize_t temp_store(struct device *dev, static int lm92_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
struct device_attribute *devattr, const char *buf, int channel, long *val)
size_t count)
{ {
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct lm92_data *data = dev_get_drvdata(dev); struct lm92_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
int nr = attr->index;
long val;
int err;
err = kstrtol(buf, 10, &val); switch (type) {
if (err) case hwmon_chip:
return lm92_chip_read(data, attr, val);
case hwmon_temp:
return lm92_temp_read(data, attr, channel, val);
default:
return -EOPNOTSUPP;
}
}
static int lm92_temp_write(struct lm92_data *data, u32 attr, long val)
{
struct regmap *regmap = data->regmap;
int reg, err;
u32 temp;
switch (attr) {
case hwmon_temp_min:
reg = LM92_REG_TEMP_LOW;
break;
case hwmon_temp_max:
reg = LM92_REG_TEMP_HIGH;
break;
case hwmon_temp_crit:
reg = LM92_REG_TEMP_CRIT;
break;
case hwmon_temp_crit_hyst:
val = clamp_val(val, -120000, 220000);
mutex_lock(&data->update_lock);
err = regmap_read(regmap, LM92_REG_TEMP_CRIT, &temp);
if (err)
goto unlock;
val = TEMP_TO_REG(TEMP_FROM_REG(temp) - val, data->resolution);
err = regmap_write(regmap, LM92_REG_TEMP_HYST, val);
unlock:
mutex_unlock(&data->update_lock);
return err; return err;
default:
mutex_lock(&data->update_lock); return -EOPNOTSUPP;
data->temp[nr] = TEMP_TO_REG(val); }
i2c_smbus_write_word_swapped(client, regs[nr], data->temp[nr]); return regmap_write(regmap, reg, TEMP_TO_REG(val, data->resolution));
mutex_unlock(&data->update_lock);
return count;
} }
static ssize_t temp_hyst_show(struct device *dev, static int lm92_write(struct device *dev, enum hwmon_sensor_types type,
struct device_attribute *devattr, char *buf) u32 attr, int channel, long val)
{ {
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct lm92_data *data = lm92_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[attr->index])
- TEMP_FROM_REG(data->temp[t_hyst]));
}
static ssize_t temp1_min_hyst_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct lm92_data *data = lm92_update_device(dev);
return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[t_min])
+ TEMP_FROM_REG(data->temp[t_hyst]));
}
static ssize_t temp_hyst_store(struct device *dev,
struct device_attribute *devattr,
const char *buf, size_t count)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
struct lm92_data *data = dev_get_drvdata(dev); struct lm92_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
long val;
int err;
err = kstrtol(buf, 10, &val); switch (type) {
if (err) case hwmon_temp:
return err; return lm92_temp_write(data, attr, val);
default:
val = clamp_val(val, -120000, 220000); return -EOPNOTSUPP;
mutex_lock(&data->update_lock); }
data->temp[t_hyst] =
TEMP_TO_REG(TEMP_FROM_REG(data->temp[attr->index]) - val);
i2c_smbus_write_word_swapped(client, LM92_REG_TEMP_HYST,
data->temp[t_hyst]);
mutex_unlock(&data->update_lock);
return count;
} }
static ssize_t alarms_show(struct device *dev, struct device_attribute *attr, static umode_t lm92_is_visible(const void *_data, enum hwmon_sensor_types type,
char *buf) u32 attr, int channel)
{ {
struct lm92_data *data = lm92_update_device(dev); switch (type) {
case hwmon_chip:
return sprintf(buf, "%d\n", ALARMS_FROM_REG(data->temp[t_input])); switch (attr) {
case hwmon_chip_alarms:
return 0444;
default:
break;
}
break;
case hwmon_temp:
switch (attr) {
case hwmon_temp_min:
case hwmon_temp_max:
case hwmon_temp_crit:
case hwmon_temp_crit_hyst:
return 0644;
case hwmon_temp_input:
case hwmon_temp_min_hyst:
case hwmon_temp_max_hyst:
case hwmon_temp_min_alarm:
case hwmon_temp_max_alarm:
case hwmon_temp_crit_alarm:
return 0444;
default:
break;
}
break;
default:
break;
}
return 0;
} }
static ssize_t alarm_show(struct device *dev, struct device_attribute *attr, static const struct hwmon_channel_info * const lm92_info[] = {
char *buf) HWMON_CHANNEL_INFO(chip, HWMON_C_ALARMS),
{ HWMON_CHANNEL_INFO(temp,
int bitnr = to_sensor_dev_attr(attr)->index; HWMON_T_INPUT |
struct lm92_data *data = lm92_update_device(dev); HWMON_T_MIN | HWMON_T_MIN_HYST |
return sprintf(buf, "%d\n", (data->temp[t_input] >> bitnr) & 1); HWMON_T_MAX | HWMON_T_MAX_HYST |
} HWMON_T_CRIT | HWMON_T_CRIT_HYST |
HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM |
HWMON_T_CRIT_ALARM),
NULL
};
static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, t_input); static const struct hwmon_ops lm92_hwmon_ops = {
static SENSOR_DEVICE_ATTR_RW(temp1_crit, temp, t_crit); .is_visible = lm92_is_visible,
static SENSOR_DEVICE_ATTR_RW(temp1_crit_hyst, temp_hyst, t_crit); .read = lm92_read,
static SENSOR_DEVICE_ATTR_RW(temp1_min, temp, t_min); .write = lm92_write,
static DEVICE_ATTR_RO(temp1_min_hyst); };
static SENSOR_DEVICE_ATTR_RW(temp1_max, temp, t_max);
static SENSOR_DEVICE_ATTR_RO(temp1_max_hyst, temp_hyst, t_max); static const struct hwmon_chip_info lm92_chip_info = {
static DEVICE_ATTR_RO(alarms); .ops = &lm92_hwmon_ops,
static SENSOR_DEVICE_ATTR_RO(temp1_crit_alarm, alarm, 2); .info = lm92_info,
static SENSOR_DEVICE_ATTR_RO(temp1_min_alarm, alarm, 0); };
static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, alarm, 1);
/* /*
* Detection and registration * Detection and registration
*/ */
static void lm92_init_client(struct i2c_client *client) static int lm92_init_client(struct regmap *regmap)
{ {
u8 config; return regmap_clear_bits(regmap, LM92_REG_CONFIG, 0x01);
/* Start the conversions if needed */
config = i2c_smbus_read_byte_data(client, LM92_REG_CONFIG);
if (config & 0x01)
i2c_smbus_write_byte_data(client, LM92_REG_CONFIG,
config & 0xFE);
} }
static struct attribute *lm92_attrs[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_temp1_crit.dev_attr.attr,
&sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
&sensor_dev_attr_temp1_min.dev_attr.attr,
&dev_attr_temp1_min_hyst.attr,
&sensor_dev_attr_temp1_max.dev_attr.attr,
&sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
&dev_attr_alarms.attr,
&sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
&sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
NULL
};
ATTRIBUTE_GROUPS(lm92);
/* Return 0 if detection is successful, -ENODEV otherwise */ /* Return 0 if detection is successful, -ENODEV otherwise */
static int lm92_detect(struct i2c_client *new_client, static int lm92_detect(struct i2c_client *new_client,
struct i2c_board_info *info) struct i2c_board_info *info)
{ {
struct i2c_adapter *adapter = new_client->adapter; struct i2c_adapter *adapter = new_client->adapter;
u8 config; u8 config_addr = LM92_REG_CONFIG;
u16 man_id; u8 man_id_addr = LM92_REG_MAN_ID;
int i, regval;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA
| I2C_FUNC_SMBUS_WORD_DATA)) | I2C_FUNC_SMBUS_WORD_DATA))
return -ENODEV; return -ENODEV;
config = i2c_smbus_read_byte_data(new_client, LM92_REG_CONFIG); /*
man_id = i2c_smbus_read_word_data(new_client, LM92_REG_MAN_ID); * Register values repeat with multiples of 8.
* Read twice to improve detection accuracy.
if ((config & 0xe0) == 0x00 && man_id == 0x0180) */
pr_info("lm92: Found National Semiconductor LM92 chip\n"); for (i = 0; i < 2; i++) {
else regval = i2c_smbus_read_word_data(new_client, man_id_addr);
return -ENODEV; if (regval != 0x0180)
return -ENODEV;
regval = i2c_smbus_read_byte_data(new_client, config_addr);
if (regval < 0 || (regval & 0xe0))
return -ENODEV;
config_addr += 8;
man_id_addr += 8;
}
strscpy(info->type, "lm92", I2C_NAME_SIZE); strscpy(info->type, "lm92", I2C_NAME_SIZE);
return 0; return 0;
} }
static int lm92_probe(struct i2c_client *new_client) /* regmap */
static int lm92_reg_read(void *context, unsigned int reg, unsigned int *val)
{ {
int ret;
if (reg == LM92_REG_CONFIG)
ret = i2c_smbus_read_byte_data(context, reg);
else
ret = i2c_smbus_read_word_swapped(context, reg);
if (ret < 0)
return ret;
*val = ret;
return 0;
}
static int lm92_reg_write(void *context, unsigned int reg, unsigned int val)
{
if (reg == LM92_REG_CONFIG)
return i2c_smbus_write_byte_data(context, LM92_REG_CONFIG, val);
return i2c_smbus_write_word_swapped(context, reg, val);
}
static bool lm92_regmap_is_volatile(struct device *dev, unsigned int reg)
{
return reg == LM92_REG_TEMP;
}
static bool lm92_regmap_is_writeable(struct device *dev, unsigned int reg)
{
return reg >= LM92_REG_CONFIG;
}
static const struct regmap_config lm92_regmap_config = {
.reg_bits = 8,
.val_bits = 16,
.max_register = LM92_REG_TEMP_HIGH,
.cache_type = REGCACHE_MAPLE,
.volatile_reg = lm92_regmap_is_volatile,
.writeable_reg = lm92_regmap_is_writeable,
};
static const struct regmap_bus lm92_regmap_bus = {
.reg_write = lm92_reg_write,
.reg_read = lm92_reg_read,
};
static int lm92_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct device *hwmon_dev; struct device *hwmon_dev;
struct lm92_data *data; struct lm92_data *data;
struct regmap *regmap;
int err;
data = devm_kzalloc(&new_client->dev, sizeof(struct lm92_data), regmap = devm_regmap_init(dev, &lm92_regmap_bus, client,
GFP_KERNEL); &lm92_regmap_config);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
data = devm_kzalloc(dev, sizeof(struct lm92_data), GFP_KERNEL);
if (!data) if (!data)
return -ENOMEM; return -ENOMEM;
data->client = new_client; data->regmap = regmap;
data->resolution = (unsigned long)i2c_get_match_data(client);
mutex_init(&data->update_lock); mutex_init(&data->update_lock);
/* Initialize the chipset */ /* Initialize the chipset */
lm92_init_client(new_client); err = lm92_init_client(regmap);
if (err)
return err;
hwmon_dev = devm_hwmon_device_register_with_groups(&new_client->dev, hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data,
new_client->name, &lm92_chip_info, NULL);
data, lm92_groups);
return PTR_ERR_OR_ZERO(hwmon_dev); return PTR_ERR_OR_ZERO(hwmon_dev);
} }
@ -318,9 +412,10 @@ static int lm92_probe(struct i2c_client *new_client)
* Module and driver stuff * Module and driver stuff
*/ */
/* .driver_data is limit register resolution */
static const struct i2c_device_id lm92_id[] = { static const struct i2c_device_id lm92_id[] = {
{ "lm92", lm92 }, { "lm92", 13 },
{ "max6635", max6635 }, { "max6635", 9 },
{ } { }
}; };
MODULE_DEVICE_TABLE(i2c, lm92_id); MODULE_DEVICE_TABLE(i2c, lm92_id);

View File

@ -8,16 +8,15 @@
* Copyright (C) 2008, 2010 Davide Rizzo <elpa.rizzo@gmail.com> * Copyright (C) 2008, 2010 Davide Rizzo <elpa.rizzo@gmail.com>
*/ */
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/hwmon.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/sysfs.h> #include <linux/regmap.h>
#include <linux/util_macros.h>
#define DRVNAME "lm95234" #define DRVNAME "lm95234"
@ -32,6 +31,8 @@ static const unsigned short normal_i2c[] = {
#define LM95234_REG_STATUS 0x02 #define LM95234_REG_STATUS 0x02
#define LM95234_REG_CONFIG 0x03 #define LM95234_REG_CONFIG 0x03
#define LM95234_REG_CONVRATE 0x04 #define LM95234_REG_CONVRATE 0x04
#define LM95234_REG_ENABLE 0x05
#define LM95234_REG_FILTER 0x06
#define LM95234_REG_STS_FAULT 0x07 #define LM95234_REG_STS_FAULT 0x07
#define LM95234_REG_STS_TCRIT1 0x08 #define LM95234_REG_STS_TCRIT1 0x08
#define LM95234_REG_STS_TCRIT2 0x09 #define LM95234_REG_STS_TCRIT2 0x09
@ -52,541 +53,372 @@ static const unsigned short normal_i2c[] = {
/* Client data (each client gets its own) */ /* Client data (each client gets its own) */
struct lm95234_data { struct lm95234_data {
struct i2c_client *client; struct regmap *regmap;
const struct attribute_group *groups[3];
struct mutex update_lock; struct mutex update_lock;
unsigned long last_updated, interval; /* in jiffies */ enum chips type;
bool valid; /* false until following fields are valid */
/* registers values */
int temp[5]; /* temperature (signed) */
u32 status; /* fault/alarm status */
u8 tcrit1[5]; /* critical temperature limit */
u8 tcrit2[2]; /* high temperature limit */
s8 toffset[4]; /* remote temperature offset */
u8 thyst; /* common hysteresis */
u8 sensor_type; /* temperature sensor type */
}; };
static int lm95234_read_temp(struct i2c_client *client, int index, int *t) static int lm95234_read_temp(struct regmap *regmap, int index, long *t)
{ {
int val; unsigned int regs[2];
u16 temp = 0; int temp = 0, ret;
u8 regvals[2];
if (index) { if (index) {
val = i2c_smbus_read_byte_data(client, regs[0] = LM95234_REG_UTEMPH(index - 1);
LM95234_REG_UTEMPH(index - 1)); regs[1] = LM95234_REG_UTEMPL(index - 1);
if (val < 0) ret = regmap_multi_reg_read(regmap, regs, regvals, 2);
return val; if (ret)
temp = val << 8; return ret;
val = i2c_smbus_read_byte_data(client, temp = (regvals[0] << 8) | regvals[1];
LM95234_REG_UTEMPL(index - 1));
if (val < 0)
return val;
temp |= val;
*t = temp;
} }
/* /*
* Read signed temperature if unsigned temperature is 0, * Read signed temperature if unsigned temperature is 0,
* or if this is the local sensor. * or if this is the local sensor.
*/ */
if (!temp) { if (!temp) {
val = i2c_smbus_read_byte_data(client, regs[0] = LM95234_REG_TEMPH(index);
LM95234_REG_TEMPH(index)); regs[1] = LM95234_REG_TEMPL(index);
if (val < 0) ret = regmap_multi_reg_read(regmap, regs, regvals, 2);
return val; if (ret)
temp = val << 8; return ret;
val = i2c_smbus_read_byte_data(client, temp = (regvals[0] << 8) | regvals[1];
LM95234_REG_TEMPL(index)); temp = sign_extend32(temp, 15);
if (val < 0) }
return val; *t = DIV_ROUND_CLOSEST(temp * 125, 32);
temp |= val; return 0;
*t = (s16)temp; }
static int lm95234_hyst_get(struct regmap *regmap, int reg, long *val)
{
unsigned int regs[2] = {reg, LM95234_REG_TCRIT_HYST};
u8 regvals[2];
int ret;
ret = regmap_multi_reg_read(regmap, regs, regvals, 2);
if (ret)
return ret;
*val = (regvals[0] - regvals[1]) * 1000;
return 0;
}
static ssize_t lm95234_hyst_set(struct lm95234_data *data, long val)
{
u32 tcrit;
int ret;
mutex_lock(&data->update_lock);
ret = regmap_read(data->regmap, LM95234_REG_TCRIT1(0), &tcrit);
if (ret)
goto unlock;
val = DIV_ROUND_CLOSEST(clamp_val(val, -255000, 255000), 1000);
val = clamp_val((int)tcrit - val, 0, 31);
ret = regmap_write(data->regmap, LM95234_REG_TCRIT_HYST, val);
unlock:
mutex_unlock(&data->update_lock);
return ret;
}
static int lm95234_crit_reg(int channel)
{
if (channel == 1 || channel == 2)
return LM95234_REG_TCRIT2(channel - 1);
return LM95234_REG_TCRIT1(channel);
}
static int lm95234_temp_write(struct device *dev, u32 attr, int channel, long val)
{
struct lm95234_data *data = dev_get_drvdata(dev);
struct regmap *regmap = data->regmap;
switch (attr) {
case hwmon_temp_enable:
if (val && val != 1)
return -EINVAL;
return regmap_update_bits(regmap, LM95234_REG_ENABLE,
BIT(channel), val ? BIT(channel) : 0);
case hwmon_temp_type:
if (val != 1 && val != 2)
return -EINVAL;
return regmap_update_bits(regmap, LM95234_REG_REM_MODEL,
BIT(channel),
val == 1 ? BIT(channel) : 0);
case hwmon_temp_offset:
val = DIV_ROUND_CLOSEST(clamp_val(val, -64000, 63500), 500);
return regmap_write(regmap, LM95234_REG_OFFSET(channel - 1), val);
case hwmon_temp_max:
val = clamp_val(val, 0, channel == 1 ? 127000 : 255000);
val = DIV_ROUND_CLOSEST(val, 1000);
return regmap_write(regmap, lm95234_crit_reg(channel), val);
case hwmon_temp_max_hyst:
return lm95234_hyst_set(data, val);
case hwmon_temp_crit:
val = DIV_ROUND_CLOSEST(clamp_val(val, 0, 255000), 1000);
return regmap_write(regmap, LM95234_REG_TCRIT1(channel), val);
default:
return -EOPNOTSUPP;
}
return 0;
}
static int lm95234_alarm_reg(int channel)
{
if (channel == 1 || channel == 2)
return LM95234_REG_STS_TCRIT2;
return LM95234_REG_STS_TCRIT1;
}
static int lm95234_temp_read(struct device *dev, u32 attr, int channel, long *val)
{
struct lm95234_data *data = dev_get_drvdata(dev);
struct regmap *regmap = data->regmap;
u32 regval, mask;
int ret;
switch (attr) {
case hwmon_temp_enable:
ret = regmap_read(regmap, LM95234_REG_ENABLE, &regval);
if (ret)
return ret;
*val = !!(regval & BIT(channel));
break;
case hwmon_temp_input:
return lm95234_read_temp(regmap, channel, val);
case hwmon_temp_max_alarm:
ret = regmap_read(regmap, lm95234_alarm_reg(channel), &regval);
if (ret)
return ret;
*val = !!(regval & BIT(channel));
break;
case hwmon_temp_crit_alarm:
ret = regmap_read(regmap, LM95234_REG_STS_TCRIT1, &regval);
if (ret)
return ret;
*val = !!(regval & BIT(channel));
break;
case hwmon_temp_crit_hyst:
return lm95234_hyst_get(regmap, LM95234_REG_TCRIT1(channel), val);
case hwmon_temp_type:
ret = regmap_read(regmap, LM95234_REG_REM_MODEL, &regval);
if (ret)
return ret;
*val = (regval & BIT(channel)) ? 1 : 2;
break;
case hwmon_temp_offset:
ret = regmap_read(regmap, LM95234_REG_OFFSET(channel - 1), &regval);
if (ret)
return ret;
*val = sign_extend32(regval, 7) * 500;
break;
case hwmon_temp_fault:
ret = regmap_read(regmap, LM95234_REG_STS_FAULT, &regval);
if (ret)
return ret;
mask = (BIT(0) | BIT(1)) << ((channel - 1) << 1);
*val = !!(regval & mask);
break;
case hwmon_temp_max:
ret = regmap_read(regmap, lm95234_crit_reg(channel), &regval);
if (ret)
return ret;
*val = regval * 1000;
break;
case hwmon_temp_max_hyst:
return lm95234_hyst_get(regmap, lm95234_crit_reg(channel), val);
case hwmon_temp_crit:
ret = regmap_read(regmap, LM95234_REG_TCRIT1(channel), &regval);
if (ret)
return ret;
*val = regval * 1000;
break;
default:
return -EOPNOTSUPP;
} }
return 0; return 0;
} }
static u16 update_intervals[] = { 143, 364, 1000, 2500 }; static u16 update_intervals[] = { 143, 364, 1000, 2500 };
/* Fill value cache. Must be called with update lock held. */ static int lm95234_chip_write(struct device *dev, u32 attr, long val)
static int lm95234_fill_cache(struct lm95234_data *data,
struct i2c_client *client)
{ {
int i, ret; struct lm95234_data *data = dev_get_drvdata(dev);
ret = i2c_smbus_read_byte_data(client, LM95234_REG_CONVRATE); switch (attr) {
if (ret < 0) case hwmon_chip_update_interval:
return ret; val = find_closest(val, update_intervals, ARRAY_SIZE(update_intervals));
return regmap_write(data->regmap, LM95234_REG_CONVRATE, val);
data->interval = msecs_to_jiffies(update_intervals[ret & 0x03]); default:
return -EOPNOTSUPP;
for (i = 0; i < ARRAY_SIZE(data->tcrit1); i++) {
ret = i2c_smbus_read_byte_data(client, LM95234_REG_TCRIT1(i));
if (ret < 0)
return ret;
data->tcrit1[i] = ret;
} }
for (i = 0; i < ARRAY_SIZE(data->tcrit2); i++) {
ret = i2c_smbus_read_byte_data(client, LM95234_REG_TCRIT2(i));
if (ret < 0)
return ret;
data->tcrit2[i] = ret;
}
for (i = 0; i < ARRAY_SIZE(data->toffset); i++) {
ret = i2c_smbus_read_byte_data(client, LM95234_REG_OFFSET(i));
if (ret < 0)
return ret;
data->toffset[i] = ret;
}
ret = i2c_smbus_read_byte_data(client, LM95234_REG_TCRIT_HYST);
if (ret < 0)
return ret;
data->thyst = ret;
ret = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL);
if (ret < 0)
return ret;
data->sensor_type = ret;
return 0; return 0;
} }
static int lm95234_update_device(struct lm95234_data *data) static int lm95234_chip_read(struct device *dev, u32 attr, long *val)
{ {
struct i2c_client *client = data->client; struct lm95234_data *data = dev_get_drvdata(dev);
u32 convrate;
int ret; int ret;
mutex_lock(&data->update_lock); switch (attr) {
case hwmon_chip_update_interval:
ret = regmap_read(data->regmap, LM95234_REG_CONVRATE, &convrate);
if (ret)
return ret;
if (time_after(jiffies, data->last_updated + data->interval) || *val = update_intervals[convrate & 0x03];
!data->valid) { break;
int i; default:
return -EOPNOTSUPP;
if (!data->valid) {
ret = lm95234_fill_cache(data, client);
if (ret < 0)
goto abort;
}
data->valid = false;
for (i = 0; i < ARRAY_SIZE(data->temp); i++) {
ret = lm95234_read_temp(client, i, &data->temp[i]);
if (ret < 0)
goto abort;
}
ret = i2c_smbus_read_byte_data(client, LM95234_REG_STS_FAULT);
if (ret < 0)
goto abort;
data->status = ret;
ret = i2c_smbus_read_byte_data(client, LM95234_REG_STS_TCRIT1);
if (ret < 0)
goto abort;
data->status |= ret << 8;
ret = i2c_smbus_read_byte_data(client, LM95234_REG_STS_TCRIT2);
if (ret < 0)
goto abort;
data->status |= ret << 16;
data->last_updated = jiffies;
data->valid = true;
} }
ret = 0; return 0;
abort:
mutex_unlock(&data->update_lock);
return ret;
} }
static ssize_t temp_show(struct device *dev, struct device_attribute *attr, static int lm95234_write(struct device *dev, enum hwmon_sensor_types type,
char *buf) u32 attr, int channel, long val)
{ {
struct lm95234_data *data = dev_get_drvdata(dev); switch (type) {
int index = to_sensor_dev_attr(attr)->index; case hwmon_chip:
int ret = lm95234_update_device(data); return lm95234_chip_write(dev, attr, val);
case hwmon_temp:
if (ret) return lm95234_temp_write(dev, attr, channel, val);
return ret; default:
return -EOPNOTSUPP;
return sprintf(buf, "%d\n", }
DIV_ROUND_CLOSEST(data->temp[index] * 125, 32));
} }
static ssize_t alarm_show(struct device *dev, struct device_attribute *attr, static int lm95234_read(struct device *dev, enum hwmon_sensor_types type,
char *buf) u32 attr, int channel, long *val)
{ {
struct lm95234_data *data = dev_get_drvdata(dev); switch (type) {
u32 mask = to_sensor_dev_attr(attr)->index; case hwmon_chip:
int ret = lm95234_update_device(data); return lm95234_chip_read(dev, attr, val);
case hwmon_temp:
if (ret) return lm95234_temp_read(dev, attr, channel, val);
return ret; default:
return -EOPNOTSUPP;
return sprintf(buf, "%u", !!(data->status & mask)); }
} }
static ssize_t type_show(struct device *dev, struct device_attribute *attr, static umode_t lm95234_is_visible(const void *_data, enum hwmon_sensor_types type,
char *buf) u32 attr, int channel)
{ {
struct lm95234_data *data = dev_get_drvdata(dev); const struct lm95234_data *data = _data;
u8 mask = to_sensor_dev_attr(attr)->index;
int ret = lm95234_update_device(data);
if (ret) if (data->type == lm95233 && channel > 2)
return ret; return 0;
return sprintf(buf, data->sensor_type & mask ? "1\n" : "2\n"); switch (type) {
} case hwmon_chip:
switch (attr) {
static ssize_t type_store(struct device *dev, struct device_attribute *attr, case hwmon_chip_update_interval:
const char *buf, size_t count) return 0644;
{ default:
struct lm95234_data *data = dev_get_drvdata(dev);
unsigned long val;
u8 mask = to_sensor_dev_attr(attr)->index;
int ret = lm95234_update_device(data);
if (ret)
return ret;
ret = kstrtoul(buf, 10, &val);
if (ret < 0)
return ret;
if (val != 1 && val != 2)
return -EINVAL;
mutex_lock(&data->update_lock);
if (val == 1)
data->sensor_type |= mask;
else
data->sensor_type &= ~mask;
data->valid = false;
i2c_smbus_write_byte_data(data->client, LM95234_REG_REM_MODEL,
data->sensor_type);
mutex_unlock(&data->update_lock);
return count;
}
static ssize_t tcrit2_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct lm95234_data *data = dev_get_drvdata(dev);
int index = to_sensor_dev_attr(attr)->index;
int ret = lm95234_update_device(data);
if (ret)
return ret;
return sprintf(buf, "%u", data->tcrit2[index] * 1000);
}
static ssize_t tcrit2_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct lm95234_data *data = dev_get_drvdata(dev);
int index = to_sensor_dev_attr(attr)->index;
long val;
int ret = lm95234_update_device(data);
if (ret)
return ret;
ret = kstrtol(buf, 10, &val);
if (ret < 0)
return ret;
val = DIV_ROUND_CLOSEST(clamp_val(val, 0, (index ? 255 : 127) * 1000),
1000);
mutex_lock(&data->update_lock);
data->tcrit2[index] = val;
i2c_smbus_write_byte_data(data->client, LM95234_REG_TCRIT2(index), val);
mutex_unlock(&data->update_lock);
return count;
}
static ssize_t tcrit2_hyst_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct lm95234_data *data = dev_get_drvdata(dev);
int index = to_sensor_dev_attr(attr)->index;
int ret = lm95234_update_device(data);
if (ret)
return ret;
/* Result can be negative, so be careful with unsigned operands */
return sprintf(buf, "%d",
((int)data->tcrit2[index] - (int)data->thyst) * 1000);
}
static ssize_t tcrit1_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct lm95234_data *data = dev_get_drvdata(dev);
int index = to_sensor_dev_attr(attr)->index;
return sprintf(buf, "%u", data->tcrit1[index] * 1000);
}
static ssize_t tcrit1_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct lm95234_data *data = dev_get_drvdata(dev);
int index = to_sensor_dev_attr(attr)->index;
int ret = lm95234_update_device(data);
long val;
if (ret)
return ret;
ret = kstrtol(buf, 10, &val);
if (ret < 0)
return ret;
val = DIV_ROUND_CLOSEST(clamp_val(val, 0, 255000), 1000);
mutex_lock(&data->update_lock);
data->tcrit1[index] = val;
i2c_smbus_write_byte_data(data->client, LM95234_REG_TCRIT1(index), val);
mutex_unlock(&data->update_lock);
return count;
}
static ssize_t tcrit1_hyst_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct lm95234_data *data = dev_get_drvdata(dev);
int index = to_sensor_dev_attr(attr)->index;
int ret = lm95234_update_device(data);
if (ret)
return ret;
/* Result can be negative, so be careful with unsigned operands */
return sprintf(buf, "%d",
((int)data->tcrit1[index] - (int)data->thyst) * 1000);
}
static ssize_t tcrit1_hyst_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct lm95234_data *data = dev_get_drvdata(dev);
int index = to_sensor_dev_attr(attr)->index;
int ret = lm95234_update_device(data);
long val;
if (ret)
return ret;
ret = kstrtol(buf, 10, &val);
if (ret < 0)
return ret;
val = DIV_ROUND_CLOSEST(clamp_val(val, -255000, 255000), 1000);
val = clamp_val((int)data->tcrit1[index] - val, 0, 31);
mutex_lock(&data->update_lock);
data->thyst = val;
i2c_smbus_write_byte_data(data->client, LM95234_REG_TCRIT_HYST, val);
mutex_unlock(&data->update_lock);
return count;
}
static ssize_t offset_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct lm95234_data *data = dev_get_drvdata(dev);
int index = to_sensor_dev_attr(attr)->index;
int ret = lm95234_update_device(data);
if (ret)
return ret;
return sprintf(buf, "%d", data->toffset[index] * 500);
}
static ssize_t offset_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct lm95234_data *data = dev_get_drvdata(dev);
int index = to_sensor_dev_attr(attr)->index;
int ret = lm95234_update_device(data);
long val;
if (ret)
return ret;
ret = kstrtol(buf, 10, &val);
if (ret < 0)
return ret;
/* Accuracy is 1/2 degrees C */
val = DIV_ROUND_CLOSEST(clamp_val(val, -64000, 63500), 500);
mutex_lock(&data->update_lock);
data->toffset[index] = val;
i2c_smbus_write_byte_data(data->client, LM95234_REG_OFFSET(index), val);
mutex_unlock(&data->update_lock);
return count;
}
static ssize_t update_interval_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct lm95234_data *data = dev_get_drvdata(dev);
int ret = lm95234_update_device(data);
if (ret)
return ret;
return sprintf(buf, "%lu\n",
DIV_ROUND_CLOSEST(data->interval * 1000, HZ));
}
static ssize_t update_interval_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct lm95234_data *data = dev_get_drvdata(dev);
int ret = lm95234_update_device(data);
unsigned long val;
u8 regval;
if (ret)
return ret;
ret = kstrtoul(buf, 10, &val);
if (ret < 0)
return ret;
for (regval = 0; regval < 3; regval++) {
if (val <= update_intervals[regval])
break; break;
}
break;
case hwmon_temp:
switch (attr) {
case hwmon_temp_input:
case hwmon_temp_max_alarm:
return 0444;
case hwmon_temp_crit_alarm:
case hwmon_temp_crit_hyst:
return (channel && channel < 3) ? 0444 : 0;
case hwmon_temp_type:
case hwmon_temp_offset:
return channel ? 0644 : 0;
case hwmon_temp_fault:
return channel ? 0444 : 0;
case hwmon_temp_max:
case hwmon_temp_enable:
return 0644;
case hwmon_temp_max_hyst:
return channel ? 0444 : 0644;
case hwmon_temp_crit:
return (channel && channel < 3) ? 0644 : 0;
default:
break;
}
break;
default:
break;
} }
return 0;
mutex_lock(&data->update_lock);
data->interval = msecs_to_jiffies(update_intervals[regval]);
i2c_smbus_write_byte_data(data->client, LM95234_REG_CONVRATE, regval);
mutex_unlock(&data->update_lock);
return count;
} }
static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, 0); static const struct hwmon_channel_info * const lm95234_info[] = {
static SENSOR_DEVICE_ATTR_RO(temp2_input, temp, 1); HWMON_CHANNEL_INFO(chip, HWMON_C_UPDATE_INTERVAL),
static SENSOR_DEVICE_ATTR_RO(temp3_input, temp, 2); HWMON_CHANNEL_INFO(temp,
static SENSOR_DEVICE_ATTR_RO(temp4_input, temp, 3); HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
static SENSOR_DEVICE_ATTR_RO(temp5_input, temp, 4); HWMON_T_MAX_ALARM | HWMON_T_ENABLE,
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
static SENSOR_DEVICE_ATTR_RO(temp2_fault, alarm, BIT(0) | BIT(1)); HWMON_T_MAX_ALARM | HWMON_T_FAULT | HWMON_T_TYPE |
static SENSOR_DEVICE_ATTR_RO(temp3_fault, alarm, BIT(2) | BIT(3)); HWMON_T_CRIT | HWMON_T_CRIT_HYST |
static SENSOR_DEVICE_ATTR_RO(temp4_fault, alarm, BIT(4) | BIT(5)); HWMON_T_CRIT_ALARM | HWMON_T_OFFSET | HWMON_T_ENABLE,
static SENSOR_DEVICE_ATTR_RO(temp5_fault, alarm, BIT(6) | BIT(7)); HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
HWMON_T_MAX_ALARM | HWMON_T_FAULT | HWMON_T_TYPE |
static SENSOR_DEVICE_ATTR_RW(temp2_type, type, BIT(1)); HWMON_T_CRIT | HWMON_T_CRIT_HYST |
static SENSOR_DEVICE_ATTR_RW(temp3_type, type, BIT(2)); HWMON_T_CRIT_ALARM | HWMON_T_OFFSET | HWMON_T_ENABLE,
static SENSOR_DEVICE_ATTR_RW(temp4_type, type, BIT(3)); HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
static SENSOR_DEVICE_ATTR_RW(temp5_type, type, BIT(4)); HWMON_T_MAX_ALARM | HWMON_T_FAULT | HWMON_T_TYPE |
HWMON_T_OFFSET | HWMON_T_ENABLE,
static SENSOR_DEVICE_ATTR_RW(temp1_max, tcrit1, 0); HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
static SENSOR_DEVICE_ATTR_RW(temp2_max, tcrit2, 0); HWMON_T_MAX_ALARM | HWMON_T_FAULT | HWMON_T_TYPE |
static SENSOR_DEVICE_ATTR_RW(temp3_max, tcrit2, 1); HWMON_T_OFFSET | HWMON_T_ENABLE),
static SENSOR_DEVICE_ATTR_RW(temp4_max, tcrit1, 3);
static SENSOR_DEVICE_ATTR_RW(temp5_max, tcrit1, 4);
static SENSOR_DEVICE_ATTR_RW(temp1_max_hyst, tcrit1_hyst, 0);
static SENSOR_DEVICE_ATTR_RO(temp2_max_hyst, tcrit2_hyst, 0);
static SENSOR_DEVICE_ATTR_RO(temp3_max_hyst, tcrit2_hyst, 1);
static SENSOR_DEVICE_ATTR_RO(temp4_max_hyst, tcrit1_hyst, 3);
static SENSOR_DEVICE_ATTR_RO(temp5_max_hyst, tcrit1_hyst, 4);
static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, alarm, BIT(0 + 8));
static SENSOR_DEVICE_ATTR_RO(temp2_max_alarm, alarm, BIT(1 + 16));
static SENSOR_DEVICE_ATTR_RO(temp3_max_alarm, alarm, BIT(2 + 16));
static SENSOR_DEVICE_ATTR_RO(temp4_max_alarm, alarm, BIT(3 + 8));
static SENSOR_DEVICE_ATTR_RO(temp5_max_alarm, alarm, BIT(4 + 8));
static SENSOR_DEVICE_ATTR_RW(temp2_crit, tcrit1, 1);
static SENSOR_DEVICE_ATTR_RW(temp3_crit, tcrit1, 2);
static SENSOR_DEVICE_ATTR_RO(temp2_crit_hyst, tcrit1_hyst, 1);
static SENSOR_DEVICE_ATTR_RO(temp3_crit_hyst, tcrit1_hyst, 2);
static SENSOR_DEVICE_ATTR_RO(temp2_crit_alarm, alarm, BIT(1 + 8));
static SENSOR_DEVICE_ATTR_RO(temp3_crit_alarm, alarm, BIT(2 + 8));
static SENSOR_DEVICE_ATTR_RW(temp2_offset, offset, 0);
static SENSOR_DEVICE_ATTR_RW(temp3_offset, offset, 1);
static SENSOR_DEVICE_ATTR_RW(temp4_offset, offset, 2);
static SENSOR_DEVICE_ATTR_RW(temp5_offset, offset, 3);
static DEVICE_ATTR_RW(update_interval);
static struct attribute *lm95234_common_attrs[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_temp2_input.dev_attr.attr,
&sensor_dev_attr_temp3_input.dev_attr.attr,
&sensor_dev_attr_temp2_fault.dev_attr.attr,
&sensor_dev_attr_temp3_fault.dev_attr.attr,
&sensor_dev_attr_temp2_type.dev_attr.attr,
&sensor_dev_attr_temp3_type.dev_attr.attr,
&sensor_dev_attr_temp1_max.dev_attr.attr,
&sensor_dev_attr_temp2_max.dev_attr.attr,
&sensor_dev_attr_temp3_max.dev_attr.attr,
&sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
&sensor_dev_attr_temp2_max_hyst.dev_attr.attr,
&sensor_dev_attr_temp3_max_hyst.dev_attr.attr,
&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp2_crit.dev_attr.attr,
&sensor_dev_attr_temp3_crit.dev_attr.attr,
&sensor_dev_attr_temp2_crit_hyst.dev_attr.attr,
&sensor_dev_attr_temp3_crit_hyst.dev_attr.attr,
&sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
&sensor_dev_attr_temp3_crit_alarm.dev_attr.attr,
&sensor_dev_attr_temp2_offset.dev_attr.attr,
&sensor_dev_attr_temp3_offset.dev_attr.attr,
&dev_attr_update_interval.attr,
NULL NULL
}; };
static const struct attribute_group lm95234_common_group = { static const struct hwmon_ops lm95234_hwmon_ops = {
.attrs = lm95234_common_attrs, .is_visible = lm95234_is_visible,
.read = lm95234_read,
.write = lm95234_write,
}; };
static struct attribute *lm95234_attrs[] = { static const struct hwmon_chip_info lm95234_chip_info = {
&sensor_dev_attr_temp4_input.dev_attr.attr, .ops = &lm95234_hwmon_ops,
&sensor_dev_attr_temp5_input.dev_attr.attr, .info = lm95234_info,
&sensor_dev_attr_temp4_fault.dev_attr.attr,
&sensor_dev_attr_temp5_fault.dev_attr.attr,
&sensor_dev_attr_temp4_type.dev_attr.attr,
&sensor_dev_attr_temp5_type.dev_attr.attr,
&sensor_dev_attr_temp4_max.dev_attr.attr,
&sensor_dev_attr_temp5_max.dev_attr.attr,
&sensor_dev_attr_temp4_max_hyst.dev_attr.attr,
&sensor_dev_attr_temp5_max_hyst.dev_attr.attr,
&sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp5_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp4_offset.dev_attr.attr,
&sensor_dev_attr_temp5_offset.dev_attr.attr,
NULL
}; };
static const struct attribute_group lm95234_group = { static bool lm95234_volatile_reg(struct device *dev, unsigned int reg)
.attrs = lm95234_attrs, {
switch (reg) {
case LM95234_REG_TEMPH(0) ... LM95234_REG_TEMPH(4):
case LM95234_REG_TEMPL(0) ... LM95234_REG_TEMPL(4):
case LM95234_REG_UTEMPH(0) ... LM95234_REG_UTEMPH(3):
case LM95234_REG_UTEMPL(0) ... LM95234_REG_UTEMPL(3):
case LM95234_REG_STS_FAULT:
case LM95234_REG_STS_TCRIT1:
case LM95234_REG_STS_TCRIT2:
case LM95234_REG_REM_MODEL_STS:
return true;
default:
return false;
}
}
static bool lm95234_writeable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case LM95234_REG_CONFIG ... LM95234_REG_FILTER:
case LM95234_REG_REM_MODEL ... LM95234_REG_OFFSET(3):
case LM95234_REG_TCRIT1(0) ... LM95234_REG_TCRIT1(4):
case LM95234_REG_TCRIT2(0) ... LM95234_REG_TCRIT2(1):
case LM95234_REG_TCRIT_HYST:
return true;
default:
return false;
}
}
static const struct regmap_config lm95234_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.writeable_reg = lm95234_writeable_reg,
.volatile_reg = lm95234_volatile_reg,
.cache_type = REGCACHE_MAPLE,
}; };
static int lm95234_detect(struct i2c_client *client, static int lm95234_detect(struct i2c_client *client,
@ -649,61 +481,60 @@ static int lm95234_detect(struct i2c_client *client,
return 0; return 0;
} }
static int lm95234_init_client(struct i2c_client *client) static int lm95234_init_client(struct device *dev, struct regmap *regmap)
{ {
int val, model; u32 val, model;
int ret;
/* start conversion if necessary */ /* start conversion if necessary */
val = i2c_smbus_read_byte_data(client, LM95234_REG_CONFIG); ret = regmap_clear_bits(regmap, LM95234_REG_CONFIG, 0x40);
if (val < 0) if (ret)
return val; return ret;
if (val & 0x40)
i2c_smbus_write_byte_data(client, LM95234_REG_CONFIG,
val & ~0x40);
/* If diode type status reports an error, try to fix it */ /* If diode type status reports an error, try to fix it */
val = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL_STS); ret = regmap_read(regmap, LM95234_REG_REM_MODEL_STS, &val);
if (val < 0) if (ret < 0)
return val; return ret;
model = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL); ret = regmap_read(regmap, LM95234_REG_REM_MODEL, &model);
if (model < 0) if (ret < 0)
return model; return ret;
if (model & val) { if (model & val) {
dev_notice(&client->dev, dev_notice(dev,
"Fixing remote diode type misconfiguration (0x%x)\n", "Fixing remote diode type misconfiguration (0x%x)\n",
val); val);
i2c_smbus_write_byte_data(client, LM95234_REG_REM_MODEL, ret = regmap_write(regmap, LM95234_REG_REM_MODEL, model & ~val);
model & ~val);
} }
return 0; return ret;
} }
static int lm95234_probe(struct i2c_client *client) static int lm95234_probe(struct i2c_client *client)
{ {
enum chips type = (uintptr_t)i2c_get_match_data(client);
struct device *dev = &client->dev; struct device *dev = &client->dev;
struct lm95234_data *data; struct lm95234_data *data;
struct device *hwmon_dev; struct device *hwmon_dev;
struct regmap *regmap;
int err; int err;
data = devm_kzalloc(dev, sizeof(struct lm95234_data), GFP_KERNEL); data = devm_kzalloc(dev, sizeof(struct lm95234_data), GFP_KERNEL);
if (!data) if (!data)
return -ENOMEM; return -ENOMEM;
data->client = client; data->type = (uintptr_t)i2c_get_match_data(client);
regmap = devm_regmap_init_i2c(client, &lm95234_regmap_config);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
data->regmap = regmap;
mutex_init(&data->update_lock); mutex_init(&data->update_lock);
/* Initialize the LM95234 chip */ /* Initialize the LM95234 chip */
err = lm95234_init_client(client); err = lm95234_init_client(dev, regmap);
if (err < 0) if (err < 0)
return err; return err;
data->groups[0] = &lm95234_common_group; hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
if (type == lm95234) data, &lm95234_chip_info, NULL);
data->groups[1] = &lm95234_group;
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
data, data->groups);
return PTR_ERR_OR_ZERO(hwmon_dev); return PTR_ERR_OR_ZERO(hwmon_dev);
} }

View File

@ -161,18 +161,18 @@ static int lm95245_read_temp(struct device *dev, u32 attr, int channel,
{ {
struct lm95245_data *data = dev_get_drvdata(dev); struct lm95245_data *data = dev_get_drvdata(dev);
struct regmap *regmap = data->regmap; struct regmap *regmap = data->regmap;
int ret, regl, regh, regvall, regvalh; unsigned int regs[2];
unsigned int regval;
u8 regvals[2];
int ret;
switch (attr) { switch (attr) {
case hwmon_temp_input: case hwmon_temp_input:
regl = channel ? LM95245_REG_R_REMOTE_TEMPL_S : regs[0] = channel ? LM95245_REG_R_REMOTE_TEMPL_S :
LM95245_REG_R_LOCAL_TEMPL_S; LM95245_REG_R_LOCAL_TEMPL_S;
regh = channel ? LM95245_REG_R_REMOTE_TEMPH_S : regs[1] = channel ? LM95245_REG_R_REMOTE_TEMPH_S :
LM95245_REG_R_LOCAL_TEMPH_S; LM95245_REG_R_LOCAL_TEMPH_S;
ret = regmap_read(regmap, regl, &regvall); ret = regmap_multi_reg_read(regmap, regs, regvals, 2);
if (ret < 0)
return ret;
ret = regmap_read(regmap, regh, &regvalh);
if (ret < 0) if (ret < 0)
return ret; return ret;
/* /*
@ -181,92 +181,77 @@ static int lm95245_read_temp(struct device *dev, u32 attr, int channel,
* Use signed calculation for remote if signed bit is set * Use signed calculation for remote if signed bit is set
* or if reported temperature is below signed limit. * or if reported temperature is below signed limit.
*/ */
if (!channel || (regvalh & 0x80) || regvalh < 0x7f) { if (!channel || (regvals[1] & 0x80) || regvals[1] < 0x7f) {
*val = temp_from_reg_signed(regvalh, regvall); *val = temp_from_reg_signed(regvals[1], regvals[0]);
return 0; return 0;
} }
ret = regmap_read(regmap, LM95245_REG_R_REMOTE_TEMPL_U, ret = regmap_bulk_read(regmap, LM95245_REG_R_REMOTE_TEMPH_U, regvals, 2);
&regvall); if (ret)
if (ret < 0)
return ret; return ret;
ret = regmap_read(regmap, LM95245_REG_R_REMOTE_TEMPH_U, *val = temp_from_reg_unsigned(regvals[0], regvals[1]);
&regvalh);
if (ret < 0)
return ret;
*val = temp_from_reg_unsigned(regvalh, regvall);
return 0; return 0;
case hwmon_temp_max: case hwmon_temp_max:
ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OS_LIMIT, ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OS_LIMIT,
&regvalh); &regval);
if (ret < 0) if (ret < 0)
return ret; return ret;
*val = regvalh * 1000; *val = regval * 1000;
return 0; return 0;
case hwmon_temp_crit: case hwmon_temp_crit:
regh = channel ? LM95245_REG_RW_REMOTE_TCRIT_LIMIT : regs[0] = channel ? LM95245_REG_RW_REMOTE_TCRIT_LIMIT :
LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT; LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT;
ret = regmap_read(regmap, regh, &regvalh); ret = regmap_read(regmap, regs[0], &regval);
if (ret < 0) if (ret < 0)
return ret; return ret;
*val = regvalh * 1000; *val = regval * 1000;
return 0; return 0;
case hwmon_temp_max_hyst: case hwmon_temp_max_hyst:
ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OS_LIMIT, regs[0] = LM95245_REG_RW_REMOTE_OS_LIMIT;
&regvalh); regs[1] = LM95245_REG_RW_COMMON_HYSTERESIS;
ret = regmap_multi_reg_read(regmap, regs, regvals, 2);
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = regmap_read(regmap, LM95245_REG_RW_COMMON_HYSTERESIS, *val = (regvals[0] - regvals[1]) * 1000;
&regvall);
if (ret < 0)
return ret;
*val = (regvalh - regvall) * 1000;
return 0; return 0;
case hwmon_temp_crit_hyst: case hwmon_temp_crit_hyst:
regh = channel ? LM95245_REG_RW_REMOTE_TCRIT_LIMIT : regs[0] = channel ? LM95245_REG_RW_REMOTE_TCRIT_LIMIT :
LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT; LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT;
ret = regmap_read(regmap, regh, &regvalh); regs[1] = LM95245_REG_RW_COMMON_HYSTERESIS;
ret = regmap_multi_reg_read(regmap, regs, regvals, 2);
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = regmap_read(regmap, LM95245_REG_RW_COMMON_HYSTERESIS, *val = (regvals[0] - regvals[1]) * 1000;
&regvall);
if (ret < 0)
return ret;
*val = (regvalh - regvall) * 1000;
return 0; return 0;
case hwmon_temp_type: case hwmon_temp_type:
ret = regmap_read(regmap, LM95245_REG_RW_CONFIG2, &regvalh); ret = regmap_read(regmap, LM95245_REG_RW_CONFIG2, &regval);
if (ret < 0) if (ret < 0)
return ret; return ret;
*val = (regvalh & CFG2_REMOTE_TT) ? 1 : 2; *val = (regval & CFG2_REMOTE_TT) ? 1 : 2;
return 0; return 0;
case hwmon_temp_offset: case hwmon_temp_offset:
ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OFFL, ret = regmap_bulk_read(regmap, LM95245_REG_RW_REMOTE_OFFH, regvals, 2);
&regvall);
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OFFH, *val = temp_from_reg_signed(regvals[0], regvals[1]);
&regvalh);
if (ret < 0)
return ret;
*val = temp_from_reg_signed(regvalh, regvall);
return 0; return 0;
case hwmon_temp_max_alarm: case hwmon_temp_max_alarm:
ret = regmap_read(regmap, LM95245_REG_R_STATUS1, &regvalh); ret = regmap_read(regmap, LM95245_REG_R_STATUS1, &regval);
if (ret < 0) if (ret < 0)
return ret; return ret;
*val = !!(regvalh & STATUS1_ROS); *val = !!(regval & STATUS1_ROS);
return 0; return 0;
case hwmon_temp_crit_alarm: case hwmon_temp_crit_alarm:
ret = regmap_read(regmap, LM95245_REG_R_STATUS1, &regvalh); ret = regmap_read(regmap, LM95245_REG_R_STATUS1, &regval);
if (ret < 0) if (ret < 0)
return ret; return ret;
*val = !!(regvalh & (channel ? STATUS1_RTCRIT : STATUS1_LOC)); *val = !!(regval & (channel ? STATUS1_RTCRIT : STATUS1_LOC));
return 0; return 0;
case hwmon_temp_fault: case hwmon_temp_fault:
ret = regmap_read(regmap, LM95245_REG_R_STATUS1, &regvalh); ret = regmap_read(regmap, LM95245_REG_R_STATUS1, &regval);
if (ret < 0) if (ret < 0)
return ret; return ret;
*val = !!(regvalh & STATUS1_DIODE_FAULT); *val = !!(regval & STATUS1_DIODE_FAULT);
return 0; return 0;
default: default:
return -EOPNOTSUPP; return -EOPNOTSUPP;
@ -279,6 +264,7 @@ static int lm95245_write_temp(struct device *dev, u32 attr, int channel,
struct lm95245_data *data = dev_get_drvdata(dev); struct lm95245_data *data = dev_get_drvdata(dev);
struct regmap *regmap = data->regmap; struct regmap *regmap = data->regmap;
unsigned int regval; unsigned int regval;
u8 regvals[2];
int ret, reg; int ret, reg;
switch (attr) { switch (attr) {
@ -311,16 +297,10 @@ static int lm95245_write_temp(struct device *dev, u32 attr, int channel,
case hwmon_temp_offset: case hwmon_temp_offset:
val = clamp_val(val, -128000, 127875); val = clamp_val(val, -128000, 127875);
val = val * 256 / 1000; val = val * 256 / 1000;
mutex_lock(&data->update_lock); regvals[0] = val >> 8;
ret = regmap_write(regmap, LM95245_REG_RW_REMOTE_OFFL, regvals[1] = val & 0xe0;
val & 0xe0);
if (ret < 0) { ret = regmap_bulk_write(regmap, LM95245_REG_RW_REMOTE_OFFH, regvals, 2);
mutex_unlock(&data->update_lock);
return ret;
}
ret = regmap_write(regmap, LM95245_REG_RW_REMOTE_OFFH,
(val >> 8) & 0xff);
mutex_unlock(&data->update_lock);
return ret; return ret;
case hwmon_temp_type: case hwmon_temp_type:
if (val != 1 && val != 2) if (val != 1 && val != 2)

View File

@ -11,7 +11,8 @@
#include <linux/hwmon.h> #include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h> #include <linux/hwmon-sysfs.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/mod_devicetable.h>
#include <linux/property.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include "ltc2947.h" #include "ltc2947.h"
@ -1034,9 +1035,8 @@ static int ltc2947_setup(struct ltc2947_data *st)
/* 19.89E-6 * 10E9 */ /* 19.89E-6 * 10E9 */
st->lsb_energy = 19890; st->lsb_energy = 19890;
} }
ret = of_property_read_u32_array(st->dev->of_node, ret = device_property_read_u32_array(st->dev, "adi,accumulator-ctl-pol",
"adi,accumulator-ctl-pol", accum, accum, ARRAY_SIZE(accum));
ARRAY_SIZE(accum));
if (!ret) { if (!ret) {
u32 accum_reg = LTC2947_ACCUM_POL_1(accum[0]) | u32 accum_reg = LTC2947_ACCUM_POL_1(accum[0]) |
LTC2947_ACCUM_POL_2(accum[1]); LTC2947_ACCUM_POL_2(accum[1]);
@ -1045,9 +1045,9 @@ static int ltc2947_setup(struct ltc2947_data *st)
if (ret) if (ret)
return ret; return ret;
} }
ret = of_property_read_u32(st->dev->of_node, ret = device_property_read_u32(st->dev,
"adi,accumulation-deadband-microamp", "adi,accumulation-deadband-microamp",
&deadband); &deadband);
if (!ret) { if (!ret) {
/* the LSB is the same as the current, so 3mA */ /* the LSB is the same as the current, so 3mA */
ret = regmap_write(st->map, LTC2947_REG_ACCUM_DEADBAND, ret = regmap_write(st->map, LTC2947_REG_ACCUM_DEADBAND,
@ -1056,7 +1056,7 @@ static int ltc2947_setup(struct ltc2947_data *st)
return ret; return ret;
} }
/* check gpio cfg */ /* check gpio cfg */
ret = of_property_read_u32(st->dev->of_node, "adi,gpio-out-pol", &pol); ret = device_property_read_u32(st->dev, "adi,gpio-out-pol", &pol);
if (!ret) { if (!ret) {
/* setup GPIO as output */ /* setup GPIO as output */
u32 gpio_ctl = LTC2947_GPIO_EN(1) | LTC2947_GPIO_FAN_EN(1) | u32 gpio_ctl = LTC2947_GPIO_EN(1) | LTC2947_GPIO_FAN_EN(1) |
@ -1067,8 +1067,8 @@ static int ltc2947_setup(struct ltc2947_data *st)
if (ret) if (ret)
return ret; return ret;
} }
ret = of_property_read_u32_array(st->dev->of_node, "adi,gpio-in-accum", ret = device_property_read_u32_array(st->dev, "adi,gpio-in-accum",
accum, ARRAY_SIZE(accum)); accum, ARRAY_SIZE(accum));
if (!ret) { if (!ret) {
/* /*
* Setup the accum options. The gpioctl is already defined as * Setup the accum options. The gpioctl is already defined as

View File

@ -854,33 +854,24 @@ static const struct regmap_config ltc2992_regmap_config = {
static int ltc2992_parse_dt(struct ltc2992_state *st) static int ltc2992_parse_dt(struct ltc2992_state *st)
{ {
struct fwnode_handle *fwnode;
struct fwnode_handle *child;
u32 addr; u32 addr;
u32 val; u32 val;
int ret; int ret;
fwnode = dev_fwnode(&st->client->dev); device_for_each_child_node_scoped(&st->client->dev, child) {
fwnode_for_each_available_child_node(fwnode, child) {
ret = fwnode_property_read_u32(child, "reg", &addr); ret = fwnode_property_read_u32(child, "reg", &addr);
if (ret < 0) { if (ret < 0)
fwnode_handle_put(child);
return ret; return ret;
}
if (addr > 1) { if (addr > 1)
fwnode_handle_put(child);
return -EINVAL; return -EINVAL;
}
ret = fwnode_property_read_u32(child, "shunt-resistor-micro-ohms", &val); ret = fwnode_property_read_u32(child, "shunt-resistor-micro-ohms", &val);
if (!ret) { if (!ret) {
if (!val) { if (!val)
fwnode_handle_put(child);
return dev_err_probe(&st->client->dev, -EINVAL, return dev_err_probe(&st->client->dev, -EINVAL,
"shunt resistor value cannot be zero\n"); "shunt resistor value cannot be zero\n");
}
st->r_sense_uohm[addr] = val; st->r_sense_uohm[addr] = val;
} }
} }

View File

@ -79,7 +79,7 @@ static const bool max16065_have_current[] = {
}; };
struct max16065_data { struct max16065_data {
enum chips type; enum chips chip;
struct i2c_client *client; struct i2c_client *client;
const struct attribute_group *groups[4]; const struct attribute_group *groups[4];
struct mutex update_lock; struct mutex update_lock;
@ -114,9 +114,10 @@ static inline int LIMIT_TO_MV(int limit, int range)
return limit * range / 256; return limit * range / 256;
} }
static inline int MV_TO_LIMIT(int mv, int range) static inline int MV_TO_LIMIT(unsigned long mv, int range)
{ {
return clamp_val(DIV_ROUND_CLOSEST(mv * 256, range), 0, 255); mv = clamp_val(mv, 0, ULONG_MAX / 256);
return DIV_ROUND_CLOSEST(clamp_val(mv * 256, 0, range * 255), range);
} }
static inline int ADC_TO_CURR(int adc, int gain) static inline int ADC_TO_CURR(int adc, int gain)
@ -161,10 +162,17 @@ static struct max16065_data *max16065_update_device(struct device *dev)
MAX16065_CURR_SENSE); MAX16065_CURR_SENSE);
} }
for (i = 0; i < DIV_ROUND_UP(data->num_adc, 8); i++) for (i = 0; i < 2; i++)
data->fault[i] data->fault[i]
= i2c_smbus_read_byte_data(client, MAX16065_FAULT(i)); = i2c_smbus_read_byte_data(client, MAX16065_FAULT(i));
/*
* MAX16067 and MAX16068 have separate undervoltage and
* overvoltage alarm bits. Squash them together.
*/
if (data->chip == max16067 || data->chip == max16068)
data->fault[0] |= data->fault[1];
data->last_updated = jiffies; data->last_updated = jiffies;
data->valid = true; data->valid = true;
} }
@ -513,6 +521,7 @@ static int max16065_probe(struct i2c_client *client)
if (unlikely(!data)) if (unlikely(!data))
return -ENOMEM; return -ENOMEM;
data->chip = chip;
data->client = client; data->client = client;
mutex_init(&data->update_lock); mutex_init(&data->update_lock);

View File

@ -12,275 +12,356 @@
* http://pdfserv.maxim-ic.com/en/ds/MAX1619.pdf * http://pdfserv.maxim-ic.com/en/ds/MAX1619.pdf
*/ */
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/mutex.h> #include <linux/hwmon.h>
#include <linux/sysfs.h> #include <linux/i2c.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/util_macros.h>
static const unsigned short normal_i2c[] = { static const unsigned short normal_i2c[] = {
0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END }; 0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END };
/* #define MAX1619_REG_LOCAL_TEMP 0x00
* The MAX1619 registers #define MAX1619_REG_REMOTE_TEMP 0x01
*/ #define MAX1619_REG_STATUS 0x02
#define MAX1619_REG_CONFIG 0x03
#define MAX1619_REG_CONVRATE 0x04
#define MAX1619_REG_REMOTE_HIGH 0x07
#define MAX1619_REG_REMOTE_LOW 0x08
#define MAX1619_REG_REMOTE_CRIT 0x10
#define MAX1619_REG_REMOTE_CRIT_HYST 0x11
#define MAX1619_REG_MAN_ID 0xFE
#define MAX1619_REG_CHIP_ID 0xFF
#define MAX1619_REG_R_MAN_ID 0xFE static int get_alarms(struct regmap *regmap)
#define MAX1619_REG_R_CHIP_ID 0xFF
#define MAX1619_REG_R_CONFIG 0x03
#define MAX1619_REG_W_CONFIG 0x09
#define MAX1619_REG_R_CONVRATE 0x04
#define MAX1619_REG_W_CONVRATE 0x0A
#define MAX1619_REG_R_STATUS 0x02
#define MAX1619_REG_R_LOCAL_TEMP 0x00
#define MAX1619_REG_R_REMOTE_TEMP 0x01
#define MAX1619_REG_R_REMOTE_HIGH 0x07
#define MAX1619_REG_W_REMOTE_HIGH 0x0D
#define MAX1619_REG_R_REMOTE_LOW 0x08
#define MAX1619_REG_W_REMOTE_LOW 0x0E
#define MAX1619_REG_R_REMOTE_CRIT 0x10
#define MAX1619_REG_W_REMOTE_CRIT 0x12
#define MAX1619_REG_R_TCRIT_HYST 0x11
#define MAX1619_REG_W_TCRIT_HYST 0x13
/*
* Conversions
*/
static int temp_from_reg(int val)
{ {
return (val & 0x80 ? val-0x100 : val) * 1000; static u32 regs[2] = { MAX1619_REG_STATUS, MAX1619_REG_CONFIG };
u8 regdata[2];
int ret;
ret = regmap_multi_reg_read(regmap, regs, regdata, 2);
if (ret)
return ret;
/* OVERT status bit may be reversed */
if (!(regdata[1] & 0x20))
regdata[0] ^= 0x02;
return regdata[0] & 0x1e;
} }
static int temp_to_reg(int val) static int max1619_temp_read(struct regmap *regmap, u32 attr, int channel, long *val)
{ {
return (val < 0 ? val+0x100*1000 : val) / 1000; int reg = -1, alarm_bit = 0;
} u32 temp;
int ret;
enum temp_index { switch (attr) {
t_input1 = 0, case hwmon_temp_input:
t_input2, reg = channel ? MAX1619_REG_REMOTE_TEMP : MAX1619_REG_LOCAL_TEMP;
t_low2, break;
t_high2, case hwmon_temp_min:
t_crit2, reg = MAX1619_REG_REMOTE_LOW;
t_hyst2, break;
t_num_regs case hwmon_temp_max:
}; reg = MAX1619_REG_REMOTE_HIGH;
break;
/* case hwmon_temp_crit:
* Client data (each client gets its own) reg = MAX1619_REG_REMOTE_CRIT;
*/ break;
case hwmon_temp_crit_hyst:
struct max1619_data { reg = MAX1619_REG_REMOTE_CRIT_HYST;
struct i2c_client *client; break;
struct mutex update_lock; case hwmon_temp_min_alarm:
bool valid; /* false until following fields are valid */ alarm_bit = 3;
unsigned long last_updated; /* in jiffies */ break;
case hwmon_temp_max_alarm:
/* registers values */ alarm_bit = 4;
u8 temp[t_num_regs]; /* index with enum temp_index */ break;
u8 alarms; case hwmon_temp_crit_alarm:
}; alarm_bit = 1;
break;
static const u8 regs_read[t_num_regs] = { case hwmon_temp_fault:
[t_input1] = MAX1619_REG_R_LOCAL_TEMP, alarm_bit = 2;
[t_input2] = MAX1619_REG_R_REMOTE_TEMP, break;
[t_low2] = MAX1619_REG_R_REMOTE_LOW, default:
[t_high2] = MAX1619_REG_R_REMOTE_HIGH, return -EOPNOTSUPP;
[t_crit2] = MAX1619_REG_R_REMOTE_CRIT,
[t_hyst2] = MAX1619_REG_R_TCRIT_HYST,
};
static const u8 regs_write[t_num_regs] = {
[t_low2] = MAX1619_REG_W_REMOTE_LOW,
[t_high2] = MAX1619_REG_W_REMOTE_HIGH,
[t_crit2] = MAX1619_REG_W_REMOTE_CRIT,
[t_hyst2] = MAX1619_REG_W_TCRIT_HYST,
};
static struct max1619_data *max1619_update_device(struct device *dev)
{
struct max1619_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
int config, i;
mutex_lock(&data->update_lock);
if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) {
dev_dbg(&client->dev, "Updating max1619 data.\n");
for (i = 0; i < t_num_regs; i++)
data->temp[i] = i2c_smbus_read_byte_data(client,
regs_read[i]);
data->alarms = i2c_smbus_read_byte_data(client,
MAX1619_REG_R_STATUS);
/* If OVERT polarity is low, reverse alarm bit */
config = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONFIG);
if (!(config & 0x20))
data->alarms ^= 0x02;
data->last_updated = jiffies;
data->valid = true;
} }
if (reg >= 0) {
mutex_unlock(&data->update_lock); ret = regmap_read(regmap, reg, &temp);
if (ret < 0)
return data; return ret;
*val = sign_extend32(temp, 7) * 1000;
} else {
ret = get_alarms(regmap);
if (ret < 0)
return ret;
*val = !!(ret & BIT(alarm_bit));
}
return 0;
} }
/* static u16 update_intervals[] = { 16000, 8000, 4000, 2000, 1000, 500, 250, 125 };
* Sysfs stuff
*/
static ssize_t temp_show(struct device *dev, struct device_attribute *devattr, static int max1619_chip_read(struct regmap *regmap, u32 attr, long *val)
char *buf)
{ {
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); int alarms, ret;
struct max1619_data *data = max1619_update_device(dev); u32 regval;
return sprintf(buf, "%d\n", temp_from_reg(data->temp[attr->index])); switch (attr) {
case hwmon_chip_update_interval:
ret = regmap_read(regmap, MAX1619_REG_CONVRATE, &regval);
if (ret < 0)
return ret;
*val = update_intervals[regval & 7];
break;
case hwmon_chip_alarms:
alarms = get_alarms(regmap);
if (alarms < 0)
return alarms;
*val = alarms;
break;
default:
return -EOPNOTSUPP;
}
return 0;
} }
static ssize_t temp_store(struct device *dev, static int max1619_read(struct device *dev, enum hwmon_sensor_types type,
struct device_attribute *devattr, const char *buf, u32 attr, int channel, long *val)
size_t count)
{ {
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct regmap *regmap = dev_get_drvdata(dev);
struct max1619_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
long val;
int err = kstrtol(buf, 10, &val);
if (err)
return err;
mutex_lock(&data->update_lock); switch (type) {
data->temp[attr->index] = temp_to_reg(val); case hwmon_chip:
i2c_smbus_write_byte_data(client, regs_write[attr->index], return max1619_chip_read(regmap, attr, val);
data->temp[attr->index]); case hwmon_temp:
mutex_unlock(&data->update_lock); return max1619_temp_read(regmap, attr, channel, val);
return count; default:
return -EOPNOTSUPP;
}
} }
static ssize_t alarms_show(struct device *dev, struct device_attribute *attr, static int max1619_chip_write(struct regmap *regmap, u32 attr, long val)
char *buf)
{ {
struct max1619_data *data = max1619_update_device(dev); switch (attr) {
return sprintf(buf, "%d\n", data->alarms); case hwmon_chip_update_interval:
val = find_closest_descending(val, update_intervals, ARRAY_SIZE(update_intervals));
return regmap_write(regmap, MAX1619_REG_CONVRATE, val);
default:
return -EOPNOTSUPP;
}
} }
static ssize_t alarm_show(struct device *dev, struct device_attribute *attr, static int max1619_temp_write(struct regmap *regmap,
char *buf) u32 attr, int channel, long val)
{ {
int bitnr = to_sensor_dev_attr(attr)->index; int reg;
struct max1619_data *data = max1619_update_device(dev);
return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1); switch (attr) {
case hwmon_temp_min:
reg = MAX1619_REG_REMOTE_LOW;
break;
case hwmon_temp_max:
reg = MAX1619_REG_REMOTE_HIGH;
break;
case hwmon_temp_crit:
reg = MAX1619_REG_REMOTE_CRIT;
break;
case hwmon_temp_crit_hyst:
reg = MAX1619_REG_REMOTE_CRIT_HYST;
break;
default:
return -EOPNOTSUPP;
}
val = DIV_ROUND_CLOSEST(clamp_val(val, -128000, 127000), 1000);
return regmap_write(regmap, reg, val);
} }
static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, t_input1); static int max1619_write(struct device *dev, enum hwmon_sensor_types type,
static SENSOR_DEVICE_ATTR_RO(temp2_input, temp, t_input2); u32 attr, int channel, long val)
static SENSOR_DEVICE_ATTR_RW(temp2_min, temp, t_low2); {
static SENSOR_DEVICE_ATTR_RW(temp2_max, temp, t_high2); struct regmap *regmap = dev_get_drvdata(dev);
static SENSOR_DEVICE_ATTR_RW(temp2_crit, temp, t_crit2);
static SENSOR_DEVICE_ATTR_RW(temp2_crit_hyst, temp, t_hyst2);
static DEVICE_ATTR_RO(alarms); switch (type) {
static SENSOR_DEVICE_ATTR_RO(temp2_crit_alarm, alarm, 1); case hwmon_chip:
static SENSOR_DEVICE_ATTR_RO(temp2_fault, alarm, 2); return max1619_chip_write(regmap, attr, val);
static SENSOR_DEVICE_ATTR_RO(temp2_min_alarm, alarm, 3); case hwmon_temp:
static SENSOR_DEVICE_ATTR_RO(temp2_max_alarm, alarm, 4); return max1619_temp_write(regmap, attr, channel, val);
default:
return -EOPNOTSUPP;
}
}
static struct attribute *max1619_attrs[] = { static umode_t max1619_is_visible(const void *_data, enum hwmon_sensor_types type,
&sensor_dev_attr_temp1_input.dev_attr.attr, u32 attr, int channel)
&sensor_dev_attr_temp2_input.dev_attr.attr, {
&sensor_dev_attr_temp2_min.dev_attr.attr, switch (type) {
&sensor_dev_attr_temp2_max.dev_attr.attr, case hwmon_chip:
&sensor_dev_attr_temp2_crit.dev_attr.attr, switch (attr) {
&sensor_dev_attr_temp2_crit_hyst.dev_attr.attr, case hwmon_chip_update_interval:
return 0644;
case hwmon_chip_alarms:
return 0444;
default:
break;
}
break;
case hwmon_temp:
switch (attr) {
case hwmon_temp_input:
return 0444;
case hwmon_temp_min:
case hwmon_temp_max:
case hwmon_temp_crit:
case hwmon_temp_crit_hyst:
return 0644;
case hwmon_temp_min_alarm:
case hwmon_temp_max_alarm:
case hwmon_temp_crit_alarm:
case hwmon_temp_fault:
return 0444;
default:
break;
}
break;
default:
break;
}
return 0;
}
&dev_attr_alarms.attr, static const struct hwmon_channel_info * const max1619_info[] = {
&sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, HWMON_CHANNEL_INFO(chip, HWMON_C_ALARMS | HWMON_C_UPDATE_INTERVAL),
&sensor_dev_attr_temp2_fault.dev_attr.attr, HWMON_CHANNEL_INFO(temp,
&sensor_dev_attr_temp2_min_alarm.dev_attr.attr, HWMON_T_INPUT,
&sensor_dev_attr_temp2_max_alarm.dev_attr.attr, HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
HWMON_T_CRIT | HWMON_T_CRIT_HYST |
HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM |
HWMON_T_CRIT_ALARM | HWMON_T_FAULT),
NULL NULL
}; };
ATTRIBUTE_GROUPS(max1619);
static const struct hwmon_ops max1619_hwmon_ops = {
.is_visible = max1619_is_visible,
.read = max1619_read,
.write = max1619_write,
};
static const struct hwmon_chip_info max1619_chip_info = {
.ops = &max1619_hwmon_ops,
.info = max1619_info,
};
/* Return 0 if detection is successful, -ENODEV otherwise */ /* Return 0 if detection is successful, -ENODEV otherwise */
static int max1619_detect(struct i2c_client *client, static int max1619_detect(struct i2c_client *client,
struct i2c_board_info *info) struct i2c_board_info *info)
{ {
struct i2c_adapter *adapter = client->adapter; struct i2c_adapter *adapter = client->adapter;
u8 reg_config, reg_convrate, reg_status, man_id, chip_id; int regval;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV; return -ENODEV;
/* detection */ regval = i2c_smbus_read_byte_data(client, MAX1619_REG_CONFIG);
reg_config = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONFIG); if (regval < 0 || (regval & 0x03))
reg_convrate = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONVRATE); return -ENODEV;
reg_status = i2c_smbus_read_byte_data(client, MAX1619_REG_R_STATUS); regval = i2c_smbus_read_byte_data(client, MAX1619_REG_CONVRATE);
if ((reg_config & 0x03) != 0x00 if (regval < 0 || regval > 0x07)
|| reg_convrate > 0x07 || (reg_status & 0x61) != 0x00) { return -ENODEV;
dev_dbg(&adapter->dev, "MAX1619 detection failed at 0x%02x\n", regval = i2c_smbus_read_byte_data(client, MAX1619_REG_STATUS);
client->addr); if (regval < 0 || (regval & 0x61))
return -ENODEV; return -ENODEV;
}
/* identification */ regval = i2c_smbus_read_byte_data(client, MAX1619_REG_MAN_ID);
man_id = i2c_smbus_read_byte_data(client, MAX1619_REG_R_MAN_ID); if (regval != 0x4d)
chip_id = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CHIP_ID); return -ENODEV;
if (man_id != 0x4D || chip_id != 0x04) { regval = i2c_smbus_read_byte_data(client, MAX1619_REG_CHIP_ID);
dev_info(&adapter->dev, if (regval != 0x04)
"Unsupported chip (man_id=0x%02X, chip_id=0x%02X).\n",
man_id, chip_id);
return -ENODEV; return -ENODEV;
}
strscpy(info->type, "max1619", I2C_NAME_SIZE); strscpy(info->type, "max1619", I2C_NAME_SIZE);
return 0; return 0;
} }
static void max1619_init_client(struct i2c_client *client) static int max1619_init_chip(struct regmap *regmap)
{ {
u8 config; int ret;
/* ret = regmap_write(regmap, MAX1619_REG_CONVRATE, 5); /* 2 Hz */
* Start the conversions. if (ret)
*/ return ret;
i2c_smbus_write_byte_data(client, MAX1619_REG_W_CONVRATE,
5); /* 2 Hz */ /* Start conversions */
config = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONFIG); return regmap_clear_bits(regmap, MAX1619_REG_CONFIG, 0x40);
if (config & 0x40)
i2c_smbus_write_byte_data(client, MAX1619_REG_W_CONFIG,
config & 0xBF); /* run */
} }
static int max1619_probe(struct i2c_client *new_client) /* regmap */
static int max1619_reg_read(void *context, unsigned int reg, unsigned int *val)
{ {
struct max1619_data *data; int ret;
ret = i2c_smbus_read_byte_data(context, reg);
if (ret < 0)
return ret;
*val = ret;
return 0;
}
static int max1619_reg_write(void *context, unsigned int reg, unsigned int val)
{
int offset = reg < MAX1619_REG_REMOTE_CRIT ? 6 : 2;
return i2c_smbus_write_byte_data(context, reg + offset, val);
}
static bool max1619_regmap_is_volatile(struct device *dev, unsigned int reg)
{
return reg <= MAX1619_REG_STATUS;
}
static bool max1619_regmap_is_writeable(struct device *dev, unsigned int reg)
{
return reg > MAX1619_REG_STATUS && reg <= MAX1619_REG_REMOTE_CRIT_HYST;
}
static const struct regmap_config max1619_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = MAX1619_REG_REMOTE_CRIT_HYST,
.cache_type = REGCACHE_MAPLE,
.volatile_reg = max1619_regmap_is_volatile,
.writeable_reg = max1619_regmap_is_writeable,
};
static const struct regmap_bus max1619_regmap_bus = {
.reg_write = max1619_reg_write,
.reg_read = max1619_reg_read,
};
static int max1619_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct device *hwmon_dev; struct device *hwmon_dev;
struct regmap *regmap;
int ret;
data = devm_kzalloc(&new_client->dev, sizeof(struct max1619_data), regmap = devm_regmap_init(dev, &max1619_regmap_bus, client,
GFP_KERNEL); &max1619_regmap_config);
if (!data) if (IS_ERR(regmap))
return -ENOMEM; return PTR_ERR(regmap);
data->client = new_client; ret = max1619_init_chip(regmap);
mutex_init(&data->update_lock); if (ret)
return ret;
/* Initialize the MAX1619 chip */ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
max1619_init_client(new_client); regmap, &max1619_chip_info, NULL);
hwmon_dev = devm_hwmon_device_register_with_groups(&new_client->dev,
new_client->name,
data,
max1619_groups);
return PTR_ERR_OR_ZERO(hwmon_dev); return PTR_ERR_OR_ZERO(hwmon_dev);
} }

View File

@ -6,15 +6,15 @@
* some credit to Christoph Scheurer, but largely a rewrite * some credit to Christoph Scheurer, but largely a rewrite
*/ */
#include <linux/module.h> #include <linux/bitops.h>
#include <linux/init.h> #include <linux/bits.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/mutex.h> #include <linux/hwmon.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/slab.h>
/* Addresses to scan */ /* Addresses to scan */
static const unsigned short max1668_addr_list[] = { static const unsigned short max1668_addr_list[] = {
@ -30,14 +30,10 @@ static const unsigned short max1668_addr_list[] = {
/* limits */ /* limits */
/* write high limits */ /* high limits */
#define MAX1668_REG_LIMH_WR(nr) (0x13 + 2 * (nr)) #define MAX1668_REG_LIMH(nr) (0x08 + 2 * (nr))
/* write low limits */
#define MAX1668_REG_LIML_WR(nr) (0x14 + 2 * (nr))
/* read high limits */
#define MAX1668_REG_LIMH_RD(nr) (0x08 + 2 * (nr))
/* read low limits */ /* read low limits */
#define MAX1668_REG_LIML_RD(nr) (0x09 + 2 * (nr)) #define MAX1668_REG_LIML(nr) (0x09 + 2 * (nr))
/* manufacturer and device ID Constants */ /* manufacturer and device ID Constants */
#define MAN_ID_MAXIM 0x4d #define MAN_ID_MAXIM 0x4d
@ -50,309 +46,146 @@ static bool read_only;
module_param(read_only, bool, 0); module_param(read_only, bool, 0);
MODULE_PARM_DESC(read_only, "Don't set any values, read only mode"); MODULE_PARM_DESC(read_only, "Don't set any values, read only mode");
enum chips { max1668, max1805, max1989 };
struct max1668_data { struct max1668_data {
struct i2c_client *client; struct regmap *regmap;
const struct attribute_group *groups[3]; int channels;
enum chips type;
struct mutex update_lock;
bool valid; /* true if following fields are valid */
unsigned long last_updated; /* In jiffies */
/* 1x local and 4x remote */
s8 temp_max[5];
s8 temp_min[5];
s8 temp[5];
u16 alarms;
}; };
static struct max1668_data *max1668_update_device(struct device *dev) static int max1668_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{ {
struct max1668_data *data = dev_get_drvdata(dev); struct max1668_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client; struct regmap *regmap = data->regmap;
struct max1668_data *ret = data; u32 regs[2] = { MAX1668_REG_STAT1, MAX1668_REG_TEMP(channel) };
s32 val; u8 regvals[2];
int i; u32 regval;
mutex_lock(&data->update_lock);
if (data->valid && !time_after(jiffies,
data->last_updated + HZ + HZ / 2))
goto abort;
for (i = 0; i < 5; i++) {
val = i2c_smbus_read_byte_data(client, MAX1668_REG_TEMP(i));
if (unlikely(val < 0)) {
ret = ERR_PTR(val);
goto abort;
}
data->temp[i] = (s8) val;
val = i2c_smbus_read_byte_data(client, MAX1668_REG_LIMH_RD(i));
if (unlikely(val < 0)) {
ret = ERR_PTR(val);
goto abort;
}
data->temp_max[i] = (s8) val;
val = i2c_smbus_read_byte_data(client, MAX1668_REG_LIML_RD(i));
if (unlikely(val < 0)) {
ret = ERR_PTR(val);
goto abort;
}
data->temp_min[i] = (s8) val;
}
val = i2c_smbus_read_byte_data(client, MAX1668_REG_STAT1);
if (unlikely(val < 0)) {
ret = ERR_PTR(val);
goto abort;
}
data->alarms = val << 8;
val = i2c_smbus_read_byte_data(client, MAX1668_REG_STAT2);
if (unlikely(val < 0)) {
ret = ERR_PTR(val);
goto abort;
}
data->alarms |= val;
data->last_updated = jiffies;
data->valid = true;
abort:
mutex_unlock(&data->update_lock);
return ret;
}
static ssize_t show_temp(struct device *dev,
struct device_attribute *devattr, char *buf)
{
int index = to_sensor_dev_attr(devattr)->index;
struct max1668_data *data = max1668_update_device(dev);
if (IS_ERR(data))
return PTR_ERR(data);
return sprintf(buf, "%d\n", data->temp[index] * 1000);
}
static ssize_t show_temp_max(struct device *dev,
struct device_attribute *devattr, char *buf)
{
int index = to_sensor_dev_attr(devattr)->index;
struct max1668_data *data = max1668_update_device(dev);
if (IS_ERR(data))
return PTR_ERR(data);
return sprintf(buf, "%d\n", data->temp_max[index] * 1000);
}
static ssize_t show_temp_min(struct device *dev,
struct device_attribute *devattr, char *buf)
{
int index = to_sensor_dev_attr(devattr)->index;
struct max1668_data *data = max1668_update_device(dev);
if (IS_ERR(data))
return PTR_ERR(data);
return sprintf(buf, "%d\n", data->temp_min[index] * 1000);
}
static ssize_t show_alarm(struct device *dev, struct device_attribute *attr,
char *buf)
{
int index = to_sensor_dev_attr(attr)->index;
struct max1668_data *data = max1668_update_device(dev);
if (IS_ERR(data))
return PTR_ERR(data);
return sprintf(buf, "%u\n", (data->alarms >> index) & 0x1);
}
static ssize_t show_fault(struct device *dev,
struct device_attribute *devattr, char *buf)
{
int index = to_sensor_dev_attr(devattr)->index;
struct max1668_data *data = max1668_update_device(dev);
if (IS_ERR(data))
return PTR_ERR(data);
return sprintf(buf, "%u\n",
(data->alarms & (1 << 12)) && data->temp[index] == 127);
}
static ssize_t set_temp_max(struct device *dev,
struct device_attribute *devattr,
const char *buf, size_t count)
{
int index = to_sensor_dev_attr(devattr)->index;
struct max1668_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
long temp;
int ret; int ret;
ret = kstrtol(buf, 10, &temp); switch (attr) {
if (ret < 0) case hwmon_temp_input:
return ret; ret = regmap_read(regmap, MAX1668_REG_TEMP(channel), &regval);
if (ret)
mutex_lock(&data->update_lock); return ret;
data->temp_max[index] = clamp_val(temp/1000, -128, 127); *val = sign_extend32(regval, 7) * 1000;
ret = i2c_smbus_write_byte_data(client, break;
MAX1668_REG_LIMH_WR(index), case hwmon_temp_min:
data->temp_max[index]); ret = regmap_read(regmap, MAX1668_REG_LIML(channel), &regval);
if (ret < 0) if (ret)
count = ret; return ret;
mutex_unlock(&data->update_lock); *val = sign_extend32(regval, 7) * 1000;
break;
return count; case hwmon_temp_max:
ret = regmap_read(regmap, MAX1668_REG_LIMH(channel), &regval);
if (ret)
return ret;
*val = sign_extend32(regval, 7) * 1000;
break;
case hwmon_temp_min_alarm:
ret = regmap_read(regmap,
channel ? MAX1668_REG_STAT2 : MAX1668_REG_STAT1,
&regval);
if (ret)
return ret;
if (channel)
*val = !!(regval & BIT(9 - channel * 2));
else
*val = !!(regval & BIT(5));
break;
case hwmon_temp_max_alarm:
ret = regmap_read(regmap,
channel ? MAX1668_REG_STAT2 : MAX1668_REG_STAT1,
&regval);
if (ret)
return ret;
if (channel)
*val = !!(regval & BIT(8 - channel * 2));
else
*val = !!(regval & BIT(6));
break;
case hwmon_temp_fault:
ret = regmap_multi_reg_read(regmap, regs, regvals, 2);
if (ret)
return ret;
*val = !!((regvals[0] & BIT(4)) && regvals[1] == 127);
break;
default:
return -EOPNOTSUPP;
}
return 0;
} }
static ssize_t set_temp_min(struct device *dev, static int max1668_write(struct device *dev, enum hwmon_sensor_types type,
struct device_attribute *devattr, u32 attr, int channel, long val)
const char *buf, size_t count)
{ {
int index = to_sensor_dev_attr(devattr)->index;
struct max1668_data *data = dev_get_drvdata(dev); struct max1668_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client; struct regmap *regmap = data->regmap;
long temp;
int ret;
ret = kstrtol(buf, 10, &temp); val = clamp_val(val / 1000, -128, 127);
if (ret < 0)
return ret;
mutex_lock(&data->update_lock); switch (attr) {
data->temp_min[index] = clamp_val(temp/1000, -128, 127); case hwmon_temp_min:
ret = i2c_smbus_write_byte_data(client, return regmap_write(regmap, MAX1668_REG_LIML(channel), val);
MAX1668_REG_LIML_WR(index), case hwmon_temp_max:
data->temp_min[index]); return regmap_write(regmap, MAX1668_REG_LIMH(channel), val);
if (ret < 0) default:
count = ret; return -EOPNOTSUPP;
mutex_unlock(&data->update_lock); }
return count;
} }
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0); static umode_t max1668_is_visible(const void *_data, enum hwmon_sensor_types type,
static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp_max, u32 attr, int channel)
set_temp_max, 0); {
static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO, show_temp_min, const struct max1668_data *data = _data;
set_temp_min, 0);
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1);
static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO, show_temp_max,
set_temp_max, 1);
static SENSOR_DEVICE_ATTR(temp2_min, S_IRUGO, show_temp_min,
set_temp_min, 1);
static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2);
static SENSOR_DEVICE_ATTR(temp3_max, S_IRUGO, show_temp_max,
set_temp_max, 2);
static SENSOR_DEVICE_ATTR(temp3_min, S_IRUGO, show_temp_min,
set_temp_min, 2);
static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 3);
static SENSOR_DEVICE_ATTR(temp4_max, S_IRUGO, show_temp_max,
set_temp_max, 3);
static SENSOR_DEVICE_ATTR(temp4_min, S_IRUGO, show_temp_min,
set_temp_min, 3);
static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, show_temp, NULL, 4);
static SENSOR_DEVICE_ATTR(temp5_max, S_IRUGO, show_temp_max,
set_temp_max, 4);
static SENSOR_DEVICE_ATTR(temp5_min, S_IRUGO, show_temp_min,
set_temp_min, 4);
static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 14); if (channel >= data->channels)
static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL, 13); return 0;
static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_alarm, NULL, 7);
static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 6);
static SENSOR_DEVICE_ATTR(temp3_min_alarm, S_IRUGO, show_alarm, NULL, 5);
static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, show_alarm, NULL, 4);
static SENSOR_DEVICE_ATTR(temp4_min_alarm, S_IRUGO, show_alarm, NULL, 3);
static SENSOR_DEVICE_ATTR(temp4_max_alarm, S_IRUGO, show_alarm, NULL, 2);
static SENSOR_DEVICE_ATTR(temp5_min_alarm, S_IRUGO, show_alarm, NULL, 1);
static SENSOR_DEVICE_ATTR(temp5_max_alarm, S_IRUGO, show_alarm, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_fault, NULL, 1); switch (attr) {
static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_fault, NULL, 2); case hwmon_temp_min:
static SENSOR_DEVICE_ATTR(temp4_fault, S_IRUGO, show_fault, NULL, 3); case hwmon_temp_max:
static SENSOR_DEVICE_ATTR(temp5_fault, S_IRUGO, show_fault, NULL, 4); return read_only ? 0444 : 0644;
case hwmon_temp_input:
case hwmon_temp_min_alarm:
case hwmon_temp_max_alarm:
return 0444;
case hwmon_temp_fault:
if (channel)
return 0444;
break;
default:
break;
}
return 0;
}
/* Attributes common to MAX1668, MAX1989 and MAX1805 */ static const struct hwmon_channel_info * const max1668_info[] = {
static struct attribute *max1668_attribute_common[] = { HWMON_CHANNEL_INFO(temp,
&sensor_dev_attr_temp1_max.dev_attr.attr, HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
&sensor_dev_attr_temp1_min.dev_attr.attr, HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM,
&sensor_dev_attr_temp1_input.dev_attr.attr, HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
&sensor_dev_attr_temp2_max.dev_attr.attr, HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM |
&sensor_dev_attr_temp2_min.dev_attr.attr, HWMON_T_FAULT,
&sensor_dev_attr_temp2_input.dev_attr.attr, HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
&sensor_dev_attr_temp3_max.dev_attr.attr, HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM |
&sensor_dev_attr_temp3_min.dev_attr.attr, HWMON_T_FAULT,
&sensor_dev_attr_temp3_input.dev_attr.attr, HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM |
&sensor_dev_attr_temp1_max_alarm.dev_attr.attr, HWMON_T_FAULT,
&sensor_dev_attr_temp1_min_alarm.dev_attr.attr, HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
&sensor_dev_attr_temp2_max_alarm.dev_attr.attr, HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM |
&sensor_dev_attr_temp2_min_alarm.dev_attr.attr, HWMON_T_FAULT),
&sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp3_min_alarm.dev_attr.attr,
&sensor_dev_attr_temp2_fault.dev_attr.attr,
&sensor_dev_attr_temp3_fault.dev_attr.attr,
NULL NULL
}; };
/* Attributes not present on MAX1805 */ static const struct hwmon_ops max1668_hwmon_ops = {
static struct attribute *max1668_attribute_unique[] = { .is_visible = max1668_is_visible,
&sensor_dev_attr_temp4_max.dev_attr.attr, .read = max1668_read,
&sensor_dev_attr_temp4_min.dev_attr.attr, .write = max1668_write,
&sensor_dev_attr_temp4_input.dev_attr.attr,
&sensor_dev_attr_temp5_max.dev_attr.attr,
&sensor_dev_attr_temp5_min.dev_attr.attr,
&sensor_dev_attr_temp5_input.dev_attr.attr,
&sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp4_min_alarm.dev_attr.attr,
&sensor_dev_attr_temp5_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp5_min_alarm.dev_attr.attr,
&sensor_dev_attr_temp4_fault.dev_attr.attr,
&sensor_dev_attr_temp5_fault.dev_attr.attr,
NULL
}; };
static umode_t max1668_attribute_mode(struct kobject *kobj, static const struct hwmon_chip_info max1668_chip_info = {
struct attribute *attr, int index) .ops = &max1668_hwmon_ops,
{ .info = max1668_info,
umode_t ret = S_IRUGO;
if (read_only)
return ret;
if (attr == &sensor_dev_attr_temp1_max.dev_attr.attr ||
attr == &sensor_dev_attr_temp2_max.dev_attr.attr ||
attr == &sensor_dev_attr_temp3_max.dev_attr.attr ||
attr == &sensor_dev_attr_temp4_max.dev_attr.attr ||
attr == &sensor_dev_attr_temp5_max.dev_attr.attr ||
attr == &sensor_dev_attr_temp1_min.dev_attr.attr ||
attr == &sensor_dev_attr_temp2_min.dev_attr.attr ||
attr == &sensor_dev_attr_temp3_min.dev_attr.attr ||
attr == &sensor_dev_attr_temp4_min.dev_attr.attr ||
attr == &sensor_dev_attr_temp5_min.dev_attr.attr)
ret |= S_IWUSR;
return ret;
}
static const struct attribute_group max1668_group_common = {
.attrs = max1668_attribute_common,
.is_visible = max1668_attribute_mode
};
static const struct attribute_group max1668_group_unique = {
.attrs = max1668_attribute_unique,
.is_visible = max1668_attribute_mode
}; };
/* Return 0 if detection is successful, -ENODEV otherwise */ /* Return 0 if detection is successful, -ENODEV otherwise */
@ -391,6 +224,48 @@ static int max1668_detect(struct i2c_client *client,
return 0; return 0;
} }
/* regmap */
static int max1668_reg_read(void *context, unsigned int reg, unsigned int *val)
{
int ret;
ret = i2c_smbus_read_byte_data(context, reg);
if (ret < 0)
return ret;
*val = ret;
return 0;
}
static int max1668_reg_write(void *context, unsigned int reg, unsigned int val)
{
return i2c_smbus_write_byte_data(context, reg + 11, val);
}
static bool max1668_regmap_is_volatile(struct device *dev, unsigned int reg)
{
return reg <= MAX1668_REG_STAT2;
}
static bool max1668_regmap_is_writeable(struct device *dev, unsigned int reg)
{
return reg > MAX1668_REG_STAT2 && reg <= MAX1668_REG_LIML(4);
}
static const struct regmap_config max1668_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.cache_type = REGCACHE_MAPLE,
.volatile_reg = max1668_regmap_is_volatile,
.writeable_reg = max1668_regmap_is_writeable,
};
static const struct regmap_bus max1668_regmap_bus = {
.reg_write = max1668_reg_write,
.reg_read = max1668_reg_read,
};
static int max1668_probe(struct i2c_client *client) static int max1668_probe(struct i2c_client *client)
{ {
struct i2c_adapter *adapter = client->adapter; struct i2c_adapter *adapter = client->adapter;
@ -405,24 +280,22 @@ static int max1668_probe(struct i2c_client *client)
if (!data) if (!data)
return -ENOMEM; return -ENOMEM;
data->client = client; data->regmap = devm_regmap_init(dev, &max1668_regmap_bus, client,
data->type = (uintptr_t)i2c_get_match_data(client); &max1668_regmap_config);
mutex_init(&data->update_lock); if (IS_ERR(data->regmap))
return PTR_ERR(data->regmap);
/* sysfs hooks */ data->channels = (uintptr_t)i2c_get_match_data(client);
data->groups[0] = &max1668_group_common;
if (data->type == max1668 || data->type == max1989)
data->groups[1] = &max1668_group_unique;
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data,
data, data->groups); &max1668_chip_info, NULL);
return PTR_ERR_OR_ZERO(hwmon_dev); return PTR_ERR_OR_ZERO(hwmon_dev);
} }
static const struct i2c_device_id max1668_id[] = { static const struct i2c_device_id max1668_id[] = {
{ "max1668", max1668 }, { "max1668", 5 },
{ "max1805", max1805 }, { "max1805", 3 },
{ "max1989", max1989 }, { "max1989", 5 },
{ } { }
}; };
MODULE_DEVICE_TABLE(i2c, max1668_id); MODULE_DEVICE_TABLE(i2c, max1668_id);

View File

@ -88,25 +88,16 @@ struct max6639_data {
static int max6639_temp_read_input(struct device *dev, int channel, long *temp) static int max6639_temp_read_input(struct device *dev, int channel, long *temp)
{ {
u32 regs[2] = { MAX6639_REG_TEMP_EXT(channel), MAX6639_REG_TEMP(channel) };
struct max6639_data *data = dev_get_drvdata(dev); struct max6639_data *data = dev_get_drvdata(dev);
unsigned int val; u8 regvals[2];
int res; int res;
/* res = regmap_multi_reg_read(data->regmap, regs, regvals, 2);
* Lock isn't needed as MAX6639_REG_TEMP wpnt change for at least 250ms after reading
* MAX6639_REG_TEMP_EXT
*/
res = regmap_read(data->regmap, MAX6639_REG_TEMP_EXT(channel), &val);
if (res < 0) if (res < 0)
return res; return res;
*temp = val >> 5; *temp = ((regvals[0] >> 5) | (regvals[1] << 3)) * 125;
res = regmap_read(data->regmap, MAX6639_REG_TEMP(channel), &val);
if (res < 0)
return res;
*temp |= val << 3;
*temp *= 125;
return 0; return 0;
} }
@ -290,8 +281,10 @@ static umode_t max6639_fan_is_visible(const void *_data, u32 attr, int channel)
static int max6639_read_pwm(struct device *dev, u32 attr, int channel, static int max6639_read_pwm(struct device *dev, u32 attr, int channel,
long *pwm_val) long *pwm_val)
{ {
u32 regs[2] = { MAX6639_REG_FAN_CONFIG3(channel), MAX6639_REG_GCONFIG };
struct max6639_data *data = dev_get_drvdata(dev); struct max6639_data *data = dev_get_drvdata(dev);
unsigned int val; unsigned int val;
u8 regvals[2];
int res; int res;
u8 i; u8 i;
@ -303,26 +296,13 @@ static int max6639_read_pwm(struct device *dev, u32 attr, int channel,
*pwm_val = val * 255 / 120; *pwm_val = val * 255 / 120;
return 0; return 0;
case hwmon_pwm_freq: case hwmon_pwm_freq:
mutex_lock(&data->update_lock); res = regmap_multi_reg_read(data->regmap, regs, regvals, 2);
res = regmap_read(data->regmap, MAX6639_REG_FAN_CONFIG3(channel), &val); if (res < 0)
if (res < 0) {
mutex_unlock(&data->update_lock);
return res; return res;
} i = regvals[0] & MAX6639_FAN_CONFIG3_FREQ_MASK;
i = val & MAX6639_FAN_CONFIG3_FREQ_MASK; if (regvals[1] & MAX6639_GCONFIG_PWM_FREQ_HI)
res = regmap_read(data->regmap, MAX6639_REG_GCONFIG, &val);
if (res < 0) {
mutex_unlock(&data->update_lock);
return res;
}
if (val & MAX6639_GCONFIG_PWM_FREQ_HI)
i |= 0x4; i |= 0x4;
i &= 0x7;
*pwm_val = freq_table[i]; *pwm_val = freq_table[i];
mutex_unlock(&data->update_lock);
return 0; return 0;
default: default:
return -EOPNOTSUPP; return -EOPNOTSUPP;

File diff suppressed because it is too large Load Diff

View File

@ -1269,6 +1269,7 @@ static const char * const asus_msi_boards[] = {
"EX-B760M-V5 D4", "EX-B760M-V5 D4",
"EX-H510M-V3", "EX-H510M-V3",
"EX-H610M-V3 D4", "EX-H610M-V3 D4",
"G15CF",
"PRIME A620M-A", "PRIME A620M-A",
"PRIME B560-PLUS", "PRIME B560-PLUS",
"PRIME B560-PLUS AC-HES", "PRIME B560-PLUS AC-HES",

View File

@ -229,41 +229,34 @@ abort:
static int nct7802_read_fan(struct nct7802_data *data, u8 reg_fan) static int nct7802_read_fan(struct nct7802_data *data, u8 reg_fan)
{ {
unsigned int f1, f2; unsigned int regs[2] = {reg_fan, REG_FANCOUNT_LOW};
u8 f[2];
int ret; int ret;
mutex_lock(&data->access_lock); ret = regmap_multi_reg_read(data->regmap, regs, f, 2);
ret = regmap_read(data->regmap, reg_fan, &f1); if (ret)
if (ret < 0) return ret;
goto abort; ret = (f[0] << 5) | (f[1] >> 3);
ret = regmap_read(data->regmap, REG_FANCOUNT_LOW, &f2);
if (ret < 0)
goto abort;
ret = (f1 << 5) | (f2 >> 3);
/* convert fan count to rpm */ /* convert fan count to rpm */
if (ret == 0x1fff) /* maximum value, assume fan is stopped */ if (ret == 0x1fff) /* maximum value, assume fan is stopped */
ret = 0; ret = 0;
else if (ret) else if (ret)
ret = DIV_ROUND_CLOSEST(1350000U, ret); ret = DIV_ROUND_CLOSEST(1350000U, ret);
abort:
mutex_unlock(&data->access_lock);
return ret; return ret;
} }
static int nct7802_read_fan_min(struct nct7802_data *data, u8 reg_fan_low, static int nct7802_read_fan_min(struct nct7802_data *data, u8 reg_fan_low,
u8 reg_fan_high) u8 reg_fan_high)
{ {
unsigned int f1, f2; unsigned int regs[2] = {reg_fan_low, reg_fan_high};
u8 f[2];
int ret; int ret;
mutex_lock(&data->access_lock); ret = regmap_multi_reg_read(data->regmap, regs, f, 2);
ret = regmap_read(data->regmap, reg_fan_low, &f1);
if (ret < 0) if (ret < 0)
goto abort; return ret;
ret = regmap_read(data->regmap, reg_fan_high, &f2);
if (ret < 0) ret = f[0] | ((f[1] & 0xf8) << 5);
goto abort;
ret = f1 | ((f2 & 0xf8) << 5);
/* convert fan count to rpm */ /* convert fan count to rpm */
if (ret == 0x1fff) /* maximum value, assume no limit */ if (ret == 0x1fff) /* maximum value, assume no limit */
ret = 0; ret = 0;
@ -271,8 +264,6 @@ static int nct7802_read_fan_min(struct nct7802_data *data, u8 reg_fan_low,
ret = DIV_ROUND_CLOSEST(1350000U, ret); ret = DIV_ROUND_CLOSEST(1350000U, ret);
else else
ret = 1350000U; ret = 1350000U;
abort:
mutex_unlock(&data->access_lock);
return ret; return ret;
} }
@ -302,33 +293,26 @@ static u8 nct7802_vmul[] = { 4, 2, 2, 2, 2 };
static int nct7802_read_voltage(struct nct7802_data *data, int nr, int index) static int nct7802_read_voltage(struct nct7802_data *data, int nr, int index)
{ {
unsigned int v1, v2; u8 v[2];
int ret; int ret;
mutex_lock(&data->access_lock);
if (index == 0) { /* voltage */ if (index == 0) { /* voltage */
ret = regmap_read(data->regmap, REG_VOLTAGE[nr], &v1); unsigned int regs[2] = {REG_VOLTAGE[nr], REG_VOLTAGE_LOW};
ret = regmap_multi_reg_read(data->regmap, regs, v, 2);
if (ret < 0) if (ret < 0)
goto abort; return ret;
ret = regmap_read(data->regmap, REG_VOLTAGE_LOW, &v2); ret = ((v[0] << 2) | (v[1] >> 6)) * nct7802_vmul[nr];
if (ret < 0)
goto abort;
ret = ((v1 << 2) | (v2 >> 6)) * nct7802_vmul[nr];
} else { /* limit */ } else { /* limit */
int shift = 8 - REG_VOLTAGE_LIMIT_MSB_SHIFT[index - 1][nr]; int shift = 8 - REG_VOLTAGE_LIMIT_MSB_SHIFT[index - 1][nr];
unsigned int regs[2] = {REG_VOLTAGE_LIMIT_LSB[index - 1][nr],
REG_VOLTAGE_LIMIT_MSB[nr]};
ret = regmap_read(data->regmap, ret = regmap_multi_reg_read(data->regmap, regs, v, 2);
REG_VOLTAGE_LIMIT_LSB[index - 1][nr], &v1);
if (ret < 0) if (ret < 0)
goto abort; return ret;
ret = regmap_read(data->regmap, REG_VOLTAGE_LIMIT_MSB[nr], ret = (v[0] | ((v[1] << shift) & 0x300)) * nct7802_vmul[nr];
&v2);
if (ret < 0)
goto abort;
ret = (v1 | ((v2 << shift) & 0x300)) * nct7802_vmul[nr];
} }
abort:
mutex_unlock(&data->access_lock);
return ret; return ret;
} }
@ -1145,17 +1129,14 @@ static int nct7802_configure_channels(struct device *dev,
{ {
/* Enable local temperature sensor by default */ /* Enable local temperature sensor by default */
u8 mode_mask = MODE_LTD_EN, mode_val = MODE_LTD_EN; u8 mode_mask = MODE_LTD_EN, mode_val = MODE_LTD_EN;
struct device_node *node;
int err; int err;
if (dev->of_node) { if (dev->of_node) {
for_each_child_of_node(dev->of_node, node) { for_each_child_of_node_scoped(dev->of_node, node) {
err = nct7802_get_channel_config(dev, node, &mode_mask, err = nct7802_get_channel_config(dev, node, &mode_mask,
&mode_val); &mode_val);
if (err) { if (err)
of_node_put(node);
return err; return err;
}
} }
} }

View File

@ -927,7 +927,7 @@ static int npcm7xx_en_pwm_fan(struct device *dev,
static int npcm7xx_pwm_fan_probe(struct platform_device *pdev) static int npcm7xx_pwm_fan_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct device_node *np, *child; struct device_node *np;
struct npcm7xx_pwm_fan_data *data; struct npcm7xx_pwm_fan_data *data;
struct resource *res; struct resource *res;
struct device *hwmon; struct device *hwmon;
@ -1004,11 +1004,10 @@ static int npcm7xx_pwm_fan_probe(struct platform_device *pdev)
} }
} }
for_each_child_of_node(np, child) { for_each_child_of_node_scoped(np, child) {
ret = npcm7xx_en_pwm_fan(dev, child, data); ret = npcm7xx_en_pwm_fan(dev, child, data);
if (ret) { if (ret) {
dev_err(dev, "enable pwm and fan failed\n"); dev_err(dev, "enable pwm and fan failed\n");
of_node_put(child);
return ret; return ret;
} }
} }

View File

@ -62,6 +62,7 @@ static const struct platform_device_id ntc_thermistor_id[] = {
[NTC_SSG1404001221] = { "ssg1404_001221", TYPE_NCPXXWB473 }, [NTC_SSG1404001221] = { "ssg1404_001221", TYPE_NCPXXWB473 },
[NTC_LAST] = { }, [NTC_LAST] = { },
}; };
MODULE_DEVICE_TABLE(platform, ntc_thermistor_id);
/* /*
* A compensation table should be sorted by the values of .ohm * A compensation table should be sorted by the values of .ohm

View File

@ -1,18 +1,21 @@
// SPDX-License-Identifier: GPL-2.0+ // SPDX-License-Identifier: GPL-2.0+
/* /*
* Platform driver for OneXPlayer, AOK ZOE, and Aya Neo Handhelds that expose * Platform driver for OneXPlayer, AOKZOE, AYANEO, and OrangePi Handhelds
* fan reading and control via hwmon sysfs. * that expose fan reading and control via hwmon sysfs.
* *
* Old OXP boards have the same DMI strings and they are told apart by * Old OXP boards have the same DMI strings and they are told apart by
* the boot cpu vendor (Intel/AMD). Currently only AMD boards are * the boot cpu vendor (Intel/AMD). Of these older models only AMD is
* supported but the code is made to be simple to add other handheld * supported.
* boards in the future. *
* Fan control is provided via pwm interface in the range [0-255]. * Fan control is provided via pwm interface in the range [0-255].
* Old AMD boards use [0-100] as range in the EC, the written value is * Old AMD boards use [0-100] as range in the EC, the written value is
* scaled to accommodate for that. Newer boards like the mini PRO and * scaled to accommodate for that. Newer boards like the mini PRO and
* AOK ZOE are not scaled but have the same EC layout. * AOKZOE are not scaled but have the same EC layout. Newer models
* like the 2 and X1 are [0-184] and are scaled to 0-255. OrangePi
* are [1-244] and scaled to 0-255.
* *
* Copyright (C) 2022 Joaquín I. Aramendía <samsagax@gmail.com> * Copyright (C) 2022 Joaquín I. Aramendía <samsagax@gmail.com>
* Copyright (C) 2024 Derek J. Clark <derekjohn.clark@gmail.com>
*/ */
#include <linux/acpi.h> #include <linux/acpi.h>
@ -43,32 +46,48 @@ enum oxp_board {
aok_zoe_a1 = 1, aok_zoe_a1 = 1,
aya_neo_2, aya_neo_2,
aya_neo_air, aya_neo_air,
aya_neo_air_1s,
aya_neo_air_plus_mendo, aya_neo_air_plus_mendo,
aya_neo_air_pro, aya_neo_air_pro,
aya_neo_flip,
aya_neo_geek, aya_neo_geek,
aya_neo_kun,
orange_pi_neo,
oxp_2,
oxp_fly,
oxp_mini_amd, oxp_mini_amd,
oxp_mini_amd_a07, oxp_mini_amd_a07,
oxp_mini_amd_pro, oxp_mini_amd_pro,
oxp_x1,
}; };
static enum oxp_board board; static enum oxp_board board;
/* Fan reading and PWM */ /* Fan reading and PWM */
#define OXP_SENSOR_FAN_REG 0x76 /* Fan reading is 2 registers long */ #define OXP_SENSOR_FAN_REG 0x76 /* Fan reading is 2 registers long */
#define OXP_SENSOR_PWM_ENABLE_REG 0x4A /* PWM enable is 1 register long */ #define OXP_2_SENSOR_FAN_REG 0x58 /* Fan reading is 2 registers long */
#define OXP_SENSOR_PWM_REG 0x4B /* PWM reading is 1 register long */ #define OXP_SENSOR_PWM_ENABLE_REG 0x4A /* PWM enable is 1 register long */
#define OXP_SENSOR_PWM_REG 0x4B /* PWM reading is 1 register long */
#define PWM_MODE_AUTO 0x00
#define PWM_MODE_MANUAL 0x01
/* OrangePi fan reading and PWM */
#define ORANGEPI_SENSOR_FAN_REG 0x78 /* Fan reading is 2 registers long */
#define ORANGEPI_SENSOR_PWM_ENABLE_REG 0x40 /* PWM enable is 1 register long */
#define ORANGEPI_SENSOR_PWM_REG 0x38 /* PWM reading is 1 register long */
/* Turbo button takeover function /* Turbo button takeover function
* Older boards have different values and EC registers * Different boards have different values and EC registers
* for the same function * for the same function
*/ */
#define OXP_OLD_TURBO_SWITCH_REG 0x1E #define OXP_TURBO_SWITCH_REG 0xF1 /* Mini Pro, OneXFly, AOKZOE */
#define OXP_OLD_TURBO_TAKE_VAL 0x01 #define OXP_2_TURBO_SWITCH_REG 0xEB /* OXP2 and X1 */
#define OXP_OLD_TURBO_RETURN_VAL 0x00 #define OXP_MINI_TURBO_SWITCH_REG 0x1E /* Mini AO7 */
#define OXP_TURBO_SWITCH_REG 0xF1 #define OXP_MINI_TURBO_TAKE_VAL 0x01 /* Mini AO7 */
#define OXP_TURBO_TAKE_VAL 0x40 #define OXP_TURBO_TAKE_VAL 0x40 /* All other models */
#define OXP_TURBO_RETURN_VAL 0x00
#define OXP_TURBO_RETURN_VAL 0x00 /* Common return val */
static const struct dmi_system_id dmi_table[] = { static const struct dmi_system_id dmi_table[] = {
{ {
@ -88,7 +107,7 @@ static const struct dmi_system_id dmi_table[] = {
{ {
.matches = { .matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
DMI_EXACT_MATCH(DMI_BOARD_NAME, "AYANEO 2"), DMI_MATCH(DMI_BOARD_NAME, "AYANEO 2"),
}, },
.driver_data = (void *)aya_neo_2, .driver_data = (void *)aya_neo_2,
}, },
@ -99,6 +118,13 @@ static const struct dmi_system_id dmi_table[] = {
}, },
.driver_data = (void *)aya_neo_air, .driver_data = (void *)aya_neo_air,
}, },
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
DMI_EXACT_MATCH(DMI_BOARD_NAME, "AIR 1S"),
},
.driver_data = (void *)aya_neo_air_1s,
},
{ {
.matches = { .matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
@ -116,10 +142,31 @@ static const struct dmi_system_id dmi_table[] = {
{ {
.matches = { .matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"), DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
DMI_EXACT_MATCH(DMI_BOARD_NAME, "GEEK"), DMI_MATCH(DMI_BOARD_NAME, "FLIP"),
},
.driver_data = (void *)aya_neo_flip,
},
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
DMI_MATCH(DMI_BOARD_NAME, "GEEK"),
}, },
.driver_data = (void *)aya_neo_geek, .driver_data = (void *)aya_neo_geek,
}, },
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
DMI_EXACT_MATCH(DMI_BOARD_NAME, "KUN"),
},
.driver_data = (void *)aya_neo_kun,
},
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "OrangePi"),
DMI_EXACT_MATCH(DMI_BOARD_NAME, "NEO-01"),
},
.driver_data = (void *)orange_pi_neo,
},
{ {
.matches = { .matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
@ -127,6 +174,20 @@ static const struct dmi_system_id dmi_table[] = {
}, },
.driver_data = (void *)oxp_mini_amd, .driver_data = (void *)oxp_mini_amd,
}, },
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
DMI_MATCH(DMI_BOARD_NAME, "ONEXPLAYER 2"),
},
.driver_data = (void *)oxp_2,
},
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER F1"),
},
.driver_data = (void *)oxp_fly,
},
{ {
.matches = { .matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
@ -141,6 +202,13 @@ static const struct dmi_system_id dmi_table[] = {
}, },
.driver_data = (void *)oxp_mini_amd_pro, .driver_data = (void *)oxp_mini_amd_pro,
}, },
{
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
DMI_MATCH(DMI_BOARD_NAME, "ONEXPLAYER X1"),
},
.driver_data = (void *)oxp_x1,
},
{}, {},
}; };
@ -192,14 +260,20 @@ static int tt_toggle_enable(void)
switch (board) { switch (board) {
case oxp_mini_amd_a07: case oxp_mini_amd_a07:
reg = OXP_OLD_TURBO_SWITCH_REG; reg = OXP_MINI_TURBO_SWITCH_REG;
val = OXP_OLD_TURBO_TAKE_VAL; val = OXP_MINI_TURBO_TAKE_VAL;
break; break;
case oxp_mini_amd_pro:
case aok_zoe_a1: case aok_zoe_a1:
case oxp_fly:
case oxp_mini_amd_pro:
reg = OXP_TURBO_SWITCH_REG; reg = OXP_TURBO_SWITCH_REG;
val = OXP_TURBO_TAKE_VAL; val = OXP_TURBO_TAKE_VAL;
break; break;
case oxp_2:
case oxp_x1:
reg = OXP_2_TURBO_SWITCH_REG;
val = OXP_TURBO_TAKE_VAL;
break;
default: default:
return -EINVAL; return -EINVAL;
} }
@ -213,14 +287,20 @@ static int tt_toggle_disable(void)
switch (board) { switch (board) {
case oxp_mini_amd_a07: case oxp_mini_amd_a07:
reg = OXP_OLD_TURBO_SWITCH_REG; reg = OXP_MINI_TURBO_SWITCH_REG;
val = OXP_OLD_TURBO_RETURN_VAL; val = OXP_TURBO_RETURN_VAL;
break; break;
case oxp_mini_amd_pro:
case aok_zoe_a1: case aok_zoe_a1:
case oxp_fly:
case oxp_mini_amd_pro:
reg = OXP_TURBO_SWITCH_REG; reg = OXP_TURBO_SWITCH_REG;
val = OXP_TURBO_RETURN_VAL; val = OXP_TURBO_RETURN_VAL;
break; break;
case oxp_2:
case oxp_x1:
reg = OXP_2_TURBO_SWITCH_REG;
val = OXP_TURBO_RETURN_VAL;
break;
default: default:
return -EINVAL; return -EINVAL;
} }
@ -233,8 +313,11 @@ static umode_t tt_toggle_is_visible(struct kobject *kobj,
{ {
switch (board) { switch (board) {
case aok_zoe_a1: case aok_zoe_a1:
case oxp_2:
case oxp_fly:
case oxp_mini_amd_a07: case oxp_mini_amd_a07:
case oxp_mini_amd_pro: case oxp_mini_amd_pro:
case oxp_x1:
return attr->mode; return attr->mode;
default: default:
break; break;
@ -273,12 +356,17 @@ static ssize_t tt_toggle_show(struct device *dev,
switch (board) { switch (board) {
case oxp_mini_amd_a07: case oxp_mini_amd_a07:
reg = OXP_OLD_TURBO_SWITCH_REG; reg = OXP_MINI_TURBO_SWITCH_REG;
break; break;
case oxp_mini_amd_pro:
case aok_zoe_a1: case aok_zoe_a1:
case oxp_fly:
case oxp_mini_amd_pro:
reg = OXP_TURBO_SWITCH_REG; reg = OXP_TURBO_SWITCH_REG;
break; break;
case oxp_2:
case oxp_x1:
reg = OXP_2_TURBO_SWITCH_REG;
break;
default: default:
return -EINVAL; return -EINVAL;
} }
@ -295,12 +383,53 @@ static DEVICE_ATTR_RW(tt_toggle);
/* PWM enable/disable functions */ /* PWM enable/disable functions */
static int oxp_pwm_enable(void) static int oxp_pwm_enable(void)
{ {
return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, 0x01); switch (board) {
case orange_pi_neo:
return write_to_ec(ORANGEPI_SENSOR_PWM_ENABLE_REG, PWM_MODE_MANUAL);
case aok_zoe_a1:
case aya_neo_2:
case aya_neo_air:
case aya_neo_air_plus_mendo:
case aya_neo_air_pro:
case aya_neo_flip:
case aya_neo_geek:
case aya_neo_kun:
case oxp_2:
case oxp_fly:
case oxp_mini_amd:
case oxp_mini_amd_a07:
case oxp_mini_amd_pro:
case oxp_x1:
return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, PWM_MODE_MANUAL);
default:
return -EINVAL;
}
} }
static int oxp_pwm_disable(void) static int oxp_pwm_disable(void)
{ {
return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, 0x00); switch (board) {
case orange_pi_neo:
return write_to_ec(ORANGEPI_SENSOR_PWM_ENABLE_REG, PWM_MODE_AUTO);
case aok_zoe_a1:
case aya_neo_2:
case aya_neo_air:
case aya_neo_air_1s:
case aya_neo_air_plus_mendo:
case aya_neo_air_pro:
case aya_neo_flip:
case aya_neo_geek:
case aya_neo_kun:
case oxp_2:
case oxp_fly:
case oxp_mini_amd:
case oxp_mini_amd_a07:
case oxp_mini_amd_pro:
case oxp_x1:
return write_to_ec(OXP_SENSOR_PWM_ENABLE_REG, PWM_MODE_AUTO);
default:
return -EINVAL;
}
} }
/* Callbacks for hwmon interface */ /* Callbacks for hwmon interface */
@ -326,7 +455,30 @@ static int oxp_platform_read(struct device *dev, enum hwmon_sensor_types type,
case hwmon_fan: case hwmon_fan:
switch (attr) { switch (attr) {
case hwmon_fan_input: case hwmon_fan_input:
return read_from_ec(OXP_SENSOR_FAN_REG, 2, val); switch (board) {
case orange_pi_neo:
return read_from_ec(ORANGEPI_SENSOR_FAN_REG, 2, val);
case oxp_2:
case oxp_x1:
return read_from_ec(OXP_2_SENSOR_FAN_REG, 2, val);
case aok_zoe_a1:
case aya_neo_2:
case aya_neo_air:
case aya_neo_air_1s:
case aya_neo_air_plus_mendo:
case aya_neo_air_pro:
case aya_neo_flip:
case aya_neo_geek:
case aya_neo_kun:
case oxp_fly:
case oxp_mini_amd:
case oxp_mini_amd_a07:
case oxp_mini_amd_pro:
return read_from_ec(OXP_SENSOR_FAN_REG, 2, val);
default:
break;
}
break;
default: default:
break; break;
} }
@ -334,27 +486,72 @@ static int oxp_platform_read(struct device *dev, enum hwmon_sensor_types type,
case hwmon_pwm: case hwmon_pwm:
switch (attr) { switch (attr) {
case hwmon_pwm_input: case hwmon_pwm_input:
ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val);
if (ret)
return ret;
switch (board) { switch (board) {
case orange_pi_neo:
ret = read_from_ec(ORANGEPI_SENSOR_PWM_REG, 1, val);
if (ret)
return ret;
/* scale from range [1-244] */
*val = ((*val - 1) * 254 / 243) + 1;
break;
case oxp_2:
case oxp_x1:
ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val);
if (ret)
return ret;
/* scale from range [0-184] */
*val = (*val * 255) / 184;
break;
case aya_neo_2: case aya_neo_2:
case aya_neo_air: case aya_neo_air:
case aya_neo_air_1s:
case aya_neo_air_plus_mendo: case aya_neo_air_plus_mendo:
case aya_neo_air_pro: case aya_neo_air_pro:
case aya_neo_flip:
case aya_neo_geek: case aya_neo_geek:
case aya_neo_kun:
case oxp_mini_amd: case oxp_mini_amd:
case oxp_mini_amd_a07: case oxp_mini_amd_a07:
ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val);
if (ret)
return ret;
/* scale from range [0-100] */
*val = (*val * 255) / 100; *val = (*val * 255) / 100;
break; break;
case oxp_mini_amd_pro:
case aok_zoe_a1: case aok_zoe_a1:
case oxp_fly:
case oxp_mini_amd_pro:
default: default:
ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val);
if (ret)
return ret;
break; break;
} }
return 0; return 0;
case hwmon_pwm_enable: case hwmon_pwm_enable:
return read_from_ec(OXP_SENSOR_PWM_ENABLE_REG, 1, val); switch (board) {
case orange_pi_neo:
return read_from_ec(ORANGEPI_SENSOR_PWM_ENABLE_REG, 1, val);
case aok_zoe_a1:
case aya_neo_2:
case aya_neo_air:
case aya_neo_air_1s:
case aya_neo_air_plus_mendo:
case aya_neo_air_pro:
case aya_neo_flip:
case aya_neo_geek:
case aya_neo_kun:
case oxp_2:
case oxp_fly:
case oxp_mini_amd:
case oxp_mini_amd_a07:
case oxp_mini_amd_pro:
case oxp_x1:
return read_from_ec(OXP_SENSOR_PWM_ENABLE_REG, 1, val);
default:
break;
}
break;
default: default:
break; break;
} }
@ -381,21 +578,36 @@ static int oxp_platform_write(struct device *dev, enum hwmon_sensor_types type,
if (val < 0 || val > 255) if (val < 0 || val > 255)
return -EINVAL; return -EINVAL;
switch (board) { switch (board) {
case orange_pi_neo:
/* scale to range [1-244] */
val = ((val - 1) * 243 / 254) + 1;
return write_to_ec(ORANGEPI_SENSOR_PWM_REG, val);
case oxp_2:
case oxp_x1:
/* scale to range [0-184] */
val = (val * 184) / 255;
return write_to_ec(OXP_SENSOR_PWM_REG, val);
case aya_neo_2: case aya_neo_2:
case aya_neo_air: case aya_neo_air:
case aya_neo_air_1s:
case aya_neo_air_plus_mendo: case aya_neo_air_plus_mendo:
case aya_neo_air_pro: case aya_neo_air_pro:
case aya_neo_flip:
case aya_neo_geek: case aya_neo_geek:
case aya_neo_kun:
case oxp_mini_amd: case oxp_mini_amd:
case oxp_mini_amd_a07: case oxp_mini_amd_a07:
/* scale to range [0-100] */
val = (val * 100) / 255; val = (val * 100) / 255;
break; return write_to_ec(OXP_SENSOR_PWM_REG, val);
case aok_zoe_a1: case aok_zoe_a1:
case oxp_fly:
case oxp_mini_amd_pro: case oxp_mini_amd_pro:
return write_to_ec(OXP_SENSOR_PWM_REG, val);
default: default:
break; break;
} }
return write_to_ec(OXP_SENSOR_PWM_REG, val); break;
default: default:
break; break;
} }
@ -467,19 +679,20 @@ static int __init oxp_platform_init(void)
{ {
const struct dmi_system_id *dmi_entry; const struct dmi_system_id *dmi_entry;
/*
* Have to check for AMD processor here because DMI strings are the
* same between Intel and AMD boards, the only way to tell them apart
* is the CPU.
* Intel boards seem to have different EC registers and values to
* read/write.
*/
dmi_entry = dmi_first_match(dmi_table); dmi_entry = dmi_first_match(dmi_table);
if (!dmi_entry || boot_cpu_data.x86_vendor != X86_VENDOR_AMD) if (!dmi_entry)
return -ENODEV; return -ENODEV;
board = (enum oxp_board)(unsigned long)dmi_entry->driver_data; board = (enum oxp_board)(unsigned long)dmi_entry->driver_data;
/*
* Have to check for AMD processor here because DMI strings are the same
* between Intel and AMD boards on older OneXPlayer devices, the only way
* to tell them apart is the CPU. Old Intel boards have an unsupported EC.
*/
if (board == oxp_mini_amd && boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
return -ENODEV;
oxp_platform_device = oxp_platform_device =
platform_create_bundle(&oxp_platform_driver, platform_create_bundle(&oxp_platform_driver,
oxp_platform_probe, NULL, 0, NULL, 0); oxp_platform_probe, NULL, 0, NULL, 0);

View File

@ -1315,7 +1315,7 @@ static void pc87360_init_device(struct platform_device *pdev,
(reg & 0xC0) | 0x11); (reg & 0xC0) | 0x11);
} }
nr = data->innr < 11 ? data->innr : 11; nr = min(data->innr, 11);
for (i = 0; i < nr; i++) { for (i = 0; i < nr; i++) {
reg = pc87360_read_value(data, LD_IN, i, reg = pc87360_read_value(data, LD_IN, i,
PC87365_REG_IN_STATUS); PC87365_REG_IN_STATUS);

View File

@ -31,8 +31,6 @@ MODULE_DEVICE_TABLE(i2c, max15301_id);
struct max15301_data { struct max15301_data {
int id; int id;
ktime_t access; /* Chip access time */
int delay; /* Delay between chip accesses in us */
struct pmbus_driver_info info; struct pmbus_driver_info info;
}; };
@ -55,89 +53,6 @@ static struct max15301_data max15301_data = {
} }
}; };
/* This chip needs a delay between accesses */
static inline void max15301_wait(const struct max15301_data *data)
{
if (data->delay) {
s64 delta = ktime_us_delta(ktime_get(), data->access);
if (delta < data->delay)
udelay(data->delay - delta);
}
}
static int max15301_read_word_data(struct i2c_client *client, int page,
int phase, int reg)
{
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
struct max15301_data *data = to_max15301_data(info);
int ret;
if (page > 0)
return -ENXIO;
if (reg >= PMBUS_VIRT_BASE)
return -ENXIO;
max15301_wait(data);
ret = pmbus_read_word_data(client, page, phase, reg);
data->access = ktime_get();
return ret;
}
static int max15301_read_byte_data(struct i2c_client *client, int page, int reg)
{
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
struct max15301_data *data = to_max15301_data(info);
int ret;
if (page > 0)
return -ENXIO;
max15301_wait(data);
ret = pmbus_read_byte_data(client, page, reg);
data->access = ktime_get();
return ret;
}
static int max15301_write_word_data(struct i2c_client *client, int page, int reg,
u16 word)
{
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
struct max15301_data *data = to_max15301_data(info);
int ret;
if (page > 0)
return -ENXIO;
if (reg >= PMBUS_VIRT_BASE)
return -ENXIO;
max15301_wait(data);
ret = pmbus_write_word_data(client, page, reg, word);
data->access = ktime_get();
return ret;
}
static int max15301_write_byte(struct i2c_client *client, int page, u8 value)
{
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
struct max15301_data *data = to_max15301_data(info);
int ret;
if (page > 0)
return -ENXIO;
max15301_wait(data);
ret = pmbus_write_byte(client, page, value);
data->access = ktime_get();
return ret;
}
static int max15301_probe(struct i2c_client *client) static int max15301_probe(struct i2c_client *client)
{ {
int status; int status;
@ -164,12 +79,7 @@ static int max15301_probe(struct i2c_client *client)
return -ENODEV; return -ENODEV;
} }
max15301_data.delay = delay; info->access_delay = delay;
info->read_byte_data = max15301_read_byte_data;
info->read_word_data = max15301_read_word_data;
info->write_byte = max15301_write_byte;
info->write_word_data = max15301_write_word_data;
return pmbus_do_probe(client, info); return pmbus_do_probe(client, info);
} }

View File

@ -35,7 +35,7 @@ struct mpq7932_data {
}; };
#if IS_ENABLED(CONFIG_SENSORS_MPQ7932_REGULATOR) #if IS_ENABLED(CONFIG_SENSORS_MPQ7932_REGULATOR)
static struct regulator_desc mpq7932_regulators_desc[] = { static const struct regulator_desc mpq7932_regulators_desc[] = {
PMBUS_REGULATOR_STEP("buck", 0, MPQ7932_N_VOLTAGES, PMBUS_REGULATOR_STEP("buck", 0, MPQ7932_N_VOLTAGES,
MPQ7932_UV_STEP, MPQ7932_BUCK_UV_MIN), MPQ7932_UV_STEP, MPQ7932_BUCK_UV_MIN),
PMBUS_REGULATOR_STEP("buck", 1, MPQ7932_N_VOLTAGES, PMBUS_REGULATOR_STEP("buck", 1, MPQ7932_N_VOLTAGES,

View File

@ -54,30 +54,6 @@ static int pli1209bc_read_word_data(struct i2c_client *client, int page,
} }
} }
static int pli1209bc_write_byte(struct i2c_client *client, int page, u8 reg)
{
int ret;
switch (reg) {
case PMBUS_CLEAR_FAULTS:
ret = pmbus_write_byte(client, page, reg);
/*
* PLI1209 takes 230 usec to execute the CLEAR_FAULTS command.
* During that time it's busy and NACKs all requests on the
* SMBUS interface. It also NACKs reads on PMBUS_STATUS_BYTE
* making it impossible to poll the BUSY flag.
*
* Just wait for not BUSY unconditionally.
*/
usleep_range(250, 300);
break;
default:
ret = -ENODATA;
break;
}
return ret;
}
#if IS_ENABLED(CONFIG_SENSORS_PLI1209BC_REGULATOR) #if IS_ENABLED(CONFIG_SENSORS_PLI1209BC_REGULATOR)
static const struct regulator_desc pli1209bc_reg_desc = { static const struct regulator_desc pli1209bc_reg_desc = {
.name = "vout2", .name = "vout2",
@ -127,7 +103,7 @@ static struct pmbus_driver_info pli1209bc_info = {
| PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP
| PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_INPUT, | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_INPUT,
.read_word_data = pli1209bc_read_word_data, .read_word_data = pli1209bc_read_word_data,
.write_byte = pli1209bc_write_byte, .write_delay = 250,
#if IS_ENABLED(CONFIG_SENSORS_PLI1209BC_REGULATOR) #if IS_ENABLED(CONFIG_SENSORS_PLI1209BC_REGULATOR)
.num_regulators = 1, .num_regulators = 1,
.reg_desc = &pli1209bc_reg_desc, .reg_desc = &pli1209bc_reg_desc,

View File

@ -472,6 +472,16 @@ struct pmbus_driver_info {
/* custom attributes */ /* custom attributes */
const struct attribute_group **groups; const struct attribute_group **groups;
/*
* Some chips need a little delay between SMBus communication. When
* set, the generic PMBus helper functions will wait if necessary
* to meet this requirement. The access delay is honored after
* every SMBus operation. The write delay is only honored after
* SMBus write operations.
*/
int access_delay; /* in microseconds */
int write_delay; /* in microseconds */
}; };
/* Regulator ops */ /* Regulator ops */

View File

@ -7,6 +7,7 @@
*/ */
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/math64.h> #include <linux/math64.h>
#include <linux/module.h> #include <linux/module.h>
@ -110,6 +111,8 @@ struct pmbus_data {
int vout_low[PMBUS_PAGES]; /* voltage low margin */ int vout_low[PMBUS_PAGES]; /* voltage low margin */
int vout_high[PMBUS_PAGES]; /* voltage high margin */ int vout_high[PMBUS_PAGES]; /* voltage high margin */
ktime_t write_time; /* Last SMBUS write timestamp */
ktime_t access_time; /* Last SMBUS access timestamp */
}; };
struct pmbus_debugfs_entry { struct pmbus_debugfs_entry {
@ -160,6 +163,39 @@ void pmbus_set_update(struct i2c_client *client, u8 reg, bool update)
} }
EXPORT_SYMBOL_NS_GPL(pmbus_set_update, PMBUS); EXPORT_SYMBOL_NS_GPL(pmbus_set_update, PMBUS);
/* Some chips need a delay between accesses. */
static void pmbus_wait(struct i2c_client *client)
{
struct pmbus_data *data = i2c_get_clientdata(client);
const struct pmbus_driver_info *info = data->info;
s64 delta;
if (info->access_delay) {
delta = ktime_us_delta(ktime_get(), data->access_time);
if (delta < info->access_delay)
fsleep(info->access_delay - delta);
} else if (info->write_delay) {
delta = ktime_us_delta(ktime_get(), data->write_time);
if (delta < info->write_delay)
fsleep(info->write_delay - delta);
}
}
/* Sets the last accessed timestamp for pmbus_wait */
static void pmbus_update_ts(struct i2c_client *client, bool write_op)
{
struct pmbus_data *data = i2c_get_clientdata(client);
const struct pmbus_driver_info *info = data->info;
if (info->access_delay) {
data->access_time = ktime_get();
} else if (info->write_delay && write_op) {
data->write_time = ktime_get();
}
}
int pmbus_set_page(struct i2c_client *client, int page, int phase) int pmbus_set_page(struct i2c_client *client, int page, int phase)
{ {
struct pmbus_data *data = i2c_get_clientdata(client); struct pmbus_data *data = i2c_get_clientdata(client);
@ -170,11 +206,15 @@ int pmbus_set_page(struct i2c_client *client, int page, int phase)
if (!(data->info->func[page] & PMBUS_PAGE_VIRTUAL) && if (!(data->info->func[page] & PMBUS_PAGE_VIRTUAL) &&
data->info->pages > 1 && page != data->currpage) { data->info->pages > 1 && page != data->currpage) {
pmbus_wait(client);
rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page); rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page);
pmbus_update_ts(client, true);
if (rv < 0) if (rv < 0)
return rv; return rv;
pmbus_wait(client);
rv = i2c_smbus_read_byte_data(client, PMBUS_PAGE); rv = i2c_smbus_read_byte_data(client, PMBUS_PAGE);
pmbus_update_ts(client, false);
if (rv < 0) if (rv < 0)
return rv; return rv;
@ -185,8 +225,10 @@ int pmbus_set_page(struct i2c_client *client, int page, int phase)
if (data->info->phases[page] && data->currphase != phase && if (data->info->phases[page] && data->currphase != phase &&
!(data->info->func[page] & PMBUS_PHASE_VIRTUAL)) { !(data->info->func[page] & PMBUS_PHASE_VIRTUAL)) {
pmbus_wait(client);
rv = i2c_smbus_write_byte_data(client, PMBUS_PHASE, rv = i2c_smbus_write_byte_data(client, PMBUS_PHASE,
phase); phase);
pmbus_update_ts(client, true);
if (rv) if (rv)
return rv; return rv;
} }
@ -204,7 +246,11 @@ int pmbus_write_byte(struct i2c_client *client, int page, u8 value)
if (rv < 0) if (rv < 0)
return rv; return rv;
return i2c_smbus_write_byte(client, value); pmbus_wait(client);
rv = i2c_smbus_write_byte(client, value);
pmbus_update_ts(client, true);
return rv;
} }
EXPORT_SYMBOL_NS_GPL(pmbus_write_byte, PMBUS); EXPORT_SYMBOL_NS_GPL(pmbus_write_byte, PMBUS);
@ -235,7 +281,11 @@ int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg,
if (rv < 0) if (rv < 0)
return rv; return rv;
return i2c_smbus_write_word_data(client, reg, word); pmbus_wait(client);
rv = i2c_smbus_write_word_data(client, reg, word);
pmbus_update_ts(client, true);
return rv;
} }
EXPORT_SYMBOL_NS_GPL(pmbus_write_word_data, PMBUS); EXPORT_SYMBOL_NS_GPL(pmbus_write_word_data, PMBUS);
@ -353,7 +403,11 @@ int pmbus_read_word_data(struct i2c_client *client, int page, int phase, u8 reg)
if (rv < 0) if (rv < 0)
return rv; return rv;
return i2c_smbus_read_word_data(client, reg); pmbus_wait(client);
rv = i2c_smbus_read_word_data(client, reg);
pmbus_update_ts(client, false);
return rv;
} }
EXPORT_SYMBOL_NS_GPL(pmbus_read_word_data, PMBUS); EXPORT_SYMBOL_NS_GPL(pmbus_read_word_data, PMBUS);
@ -412,7 +466,11 @@ int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg)
if (rv < 0) if (rv < 0)
return rv; return rv;
return i2c_smbus_read_byte_data(client, reg); pmbus_wait(client);
rv = i2c_smbus_read_byte_data(client, reg);
pmbus_update_ts(client, false);
return rv;
} }
EXPORT_SYMBOL_NS_GPL(pmbus_read_byte_data, PMBUS); EXPORT_SYMBOL_NS_GPL(pmbus_read_byte_data, PMBUS);
@ -424,7 +482,11 @@ int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, u8 value)
if (rv < 0) if (rv < 0)
return rv; return rv;
return i2c_smbus_write_byte_data(client, reg, value); pmbus_wait(client);
rv = i2c_smbus_write_byte_data(client, reg, value);
pmbus_update_ts(client, true);
return rv;
} }
EXPORT_SYMBOL_NS_GPL(pmbus_write_byte_data, PMBUS); EXPORT_SYMBOL_NS_GPL(pmbus_write_byte_data, PMBUS);
@ -456,7 +518,11 @@ static int pmbus_read_block_data(struct i2c_client *client, int page, u8 reg,
if (rv < 0) if (rv < 0)
return rv; return rv;
return i2c_smbus_read_block_data(client, reg, data_buf); pmbus_wait(client);
rv = i2c_smbus_read_block_data(client, reg, data_buf);
pmbus_update_ts(client, false);
return rv;
} }
static struct pmbus_sensor *pmbus_find_sensor(struct pmbus_data *data, int page, static struct pmbus_sensor *pmbus_find_sensor(struct pmbus_data *data, int page,
@ -2457,9 +2523,11 @@ static int pmbus_read_coefficients(struct i2c_client *client,
data.block[1] = attr->reg; data.block[1] = attr->reg;
data.block[2] = 0x01; data.block[2] = 0x01;
pmbus_wait(client);
rv = i2c_smbus_xfer(client->adapter, client->addr, client->flags, rv = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
I2C_SMBUS_WRITE, PMBUS_COEFFICIENTS, I2C_SMBUS_WRITE, PMBUS_COEFFICIENTS,
I2C_SMBUS_BLOCK_PROC_CALL, &data); I2C_SMBUS_BLOCK_PROC_CALL, &data);
pmbus_update_ts(client, true);
if (rv < 0) if (rv < 0)
return rv; return rv;
@ -2611,7 +2679,10 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data,
/* Enable PEC if the controller and bus supports it */ /* Enable PEC if the controller and bus supports it */
if (!(data->flags & PMBUS_NO_CAPABILITY)) { if (!(data->flags & PMBUS_NO_CAPABILITY)) {
pmbus_wait(client);
ret = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY); ret = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY);
pmbus_update_ts(client, false);
if (ret >= 0 && (ret & PB_CAPABILITY_ERROR_CHECK)) { if (ret >= 0 && (ret & PB_CAPABILITY_ERROR_CHECK)) {
if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_PEC)) if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_PEC))
client->flags |= I2C_CLIENT_PEC; client->flags |= I2C_CLIENT_PEC;
@ -2624,10 +2695,16 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data,
* Bail out if both registers are not supported. * Bail out if both registers are not supported.
*/ */
data->read_status = pmbus_read_status_word; data->read_status = pmbus_read_status_word;
pmbus_wait(client);
ret = i2c_smbus_read_word_data(client, PMBUS_STATUS_WORD); ret = i2c_smbus_read_word_data(client, PMBUS_STATUS_WORD);
pmbus_update_ts(client, false);
if (ret < 0 || ret == 0xffff) { if (ret < 0 || ret == 0xffff) {
data->read_status = pmbus_read_status_byte; data->read_status = pmbus_read_status_byte;
pmbus_wait(client);
ret = i2c_smbus_read_byte_data(client, PMBUS_STATUS_BYTE); ret = i2c_smbus_read_byte_data(client, PMBUS_STATUS_BYTE);
pmbus_update_ts(client, false);
if (ret < 0 || ret == 0xff) { if (ret < 0 || ret == 0xff) {
dev_err(dev, "PMBus status register not found\n"); dev_err(dev, "PMBus status register not found\n");
return -ENODEV; return -ENODEV;
@ -2642,7 +2719,10 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data,
* limit registers need to be disabled. * limit registers need to be disabled.
*/ */
if (!(data->flags & PMBUS_NO_WRITE_PROTECT)) { if (!(data->flags & PMBUS_NO_WRITE_PROTECT)) {
pmbus_wait(client);
ret = i2c_smbus_read_byte_data(client, PMBUS_WRITE_PROTECT); ret = i2c_smbus_read_byte_data(client, PMBUS_WRITE_PROTECT);
pmbus_update_ts(client, false);
if (ret > 0 && (ret & PB_WP_ANY)) if (ret > 0 && (ret & PB_WP_ANY))
data->flags |= PMBUS_WRITE_PROTECTED | PMBUS_SKIP_STATUS_CHECK; data->flags |= PMBUS_WRITE_PROTECTED | PMBUS_SKIP_STATUS_CHECK;
} }

View File

@ -67,7 +67,6 @@ struct ucd9000_data {
struct gpio_chip gpio; struct gpio_chip gpio;
#endif #endif
struct dentry *debugfs; struct dentry *debugfs;
ktime_t write_time;
}; };
#define to_ucd9000_data(_info) container_of(_info, struct ucd9000_data, info) #define to_ucd9000_data(_info) container_of(_info, struct ucd9000_data, info)
@ -86,63 +85,6 @@ struct ucd9000_debugfs_entry {
*/ */
#define UCD90320_WAIT_DELAY_US 500 #define UCD90320_WAIT_DELAY_US 500
static inline void ucd90320_wait(const struct ucd9000_data *data)
{
s64 delta = ktime_us_delta(ktime_get(), data->write_time);
if (delta < UCD90320_WAIT_DELAY_US)
udelay(UCD90320_WAIT_DELAY_US - delta);
}
static int ucd90320_read_word_data(struct i2c_client *client, int page,
int phase, int reg)
{
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
struct ucd9000_data *data = to_ucd9000_data(info);
if (reg >= PMBUS_VIRT_BASE)
return -ENXIO;
ucd90320_wait(data);
return pmbus_read_word_data(client, page, phase, reg);
}
static int ucd90320_read_byte_data(struct i2c_client *client, int page, int reg)
{
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
struct ucd9000_data *data = to_ucd9000_data(info);
ucd90320_wait(data);
return pmbus_read_byte_data(client, page, reg);
}
static int ucd90320_write_word_data(struct i2c_client *client, int page,
int reg, u16 word)
{
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
struct ucd9000_data *data = to_ucd9000_data(info);
int ret;
ucd90320_wait(data);
ret = pmbus_write_word_data(client, page, reg, word);
data->write_time = ktime_get();
return ret;
}
static int ucd90320_write_byte(struct i2c_client *client, int page, u8 value)
{
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
struct ucd9000_data *data = to_ucd9000_data(info);
int ret;
ucd90320_wait(data);
ret = pmbus_write_byte(client, page, value);
data->write_time = ktime_get();
return ret;
}
static int ucd9000_get_fan_config(struct i2c_client *client, int fan) static int ucd9000_get_fan_config(struct i2c_client *client, int fan)
{ {
int fan_config = 0; int fan_config = 0;
@ -667,10 +609,8 @@ static int ucd9000_probe(struct i2c_client *client)
info->func[0] |= PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12 info->func[0] |= PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12
| PMBUS_HAVE_FAN34 | PMBUS_HAVE_STATUS_FAN34; | PMBUS_HAVE_FAN34 | PMBUS_HAVE_STATUS_FAN34;
} else if (mid->driver_data == ucd90320) { } else if (mid->driver_data == ucd90320) {
info->read_byte_data = ucd90320_read_byte_data; /* Delay SMBus operations after a write */
info->read_word_data = ucd90320_read_word_data; info->write_delay = UCD90320_WAIT_DELAY_US;
info->write_byte = ucd90320_write_byte;
info->write_word_data = ucd90320_write_word_data;
} }
ucd9000_probe_gpio(client, mid, data); ucd9000_probe_gpio(client, mid, data);

View File

@ -22,8 +22,6 @@ enum chips { zl2004, zl2005, zl2006, zl2008, zl2105, zl2106, zl6100, zl6105,
struct zl6100_data { struct zl6100_data {
int id; int id;
ktime_t access; /* chip access time */
int delay; /* Delay between chip accesses in uS */
struct pmbus_driver_info info; struct pmbus_driver_info info;
}; };
@ -122,16 +120,6 @@ static u16 zl6100_d2l(long val)
return (mantissa & 0x7ff) | ((exponent << 11) & 0xf800); return (mantissa & 0x7ff) | ((exponent << 11) & 0xf800);
} }
/* Some chips need a delay between accesses */
static inline void zl6100_wait(const struct zl6100_data *data)
{
if (data->delay) {
s64 delta = ktime_us_delta(ktime_get(), data->access);
if (delta < data->delay)
udelay(data->delay - delta);
}
}
static int zl6100_read_word_data(struct i2c_client *client, int page, static int zl6100_read_word_data(struct i2c_client *client, int page,
int phase, int reg) int phase, int reg)
{ {
@ -174,9 +162,7 @@ static int zl6100_read_word_data(struct i2c_client *client, int page,
break; break;
} }
zl6100_wait(data);
ret = pmbus_read_word_data(client, page, phase, vreg); ret = pmbus_read_word_data(client, page, phase, vreg);
data->access = ktime_get();
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -195,14 +181,11 @@ static int zl6100_read_word_data(struct i2c_client *client, int page,
static int zl6100_read_byte_data(struct i2c_client *client, int page, int reg) static int zl6100_read_byte_data(struct i2c_client *client, int page, int reg)
{ {
const struct pmbus_driver_info *info = pmbus_get_driver_info(client); const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
struct zl6100_data *data = to_zl6100_data(info);
int ret, status; int ret, status;
if (page >= info->pages) if (page >= info->pages)
return -ENXIO; return -ENXIO;
zl6100_wait(data);
switch (reg) { switch (reg) {
case PMBUS_VIRT_STATUS_VMON: case PMBUS_VIRT_STATUS_VMON:
ret = pmbus_read_byte_data(client, 0, ret = pmbus_read_byte_data(client, 0,
@ -225,7 +208,6 @@ static int zl6100_read_byte_data(struct i2c_client *client, int page, int reg)
ret = pmbus_read_byte_data(client, page, reg); ret = pmbus_read_byte_data(client, page, reg);
break; break;
} }
data->access = ktime_get();
return ret; return ret;
} }
@ -234,8 +216,7 @@ static int zl6100_write_word_data(struct i2c_client *client, int page, int reg,
u16 word) u16 word)
{ {
const struct pmbus_driver_info *info = pmbus_get_driver_info(client); const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
struct zl6100_data *data = to_zl6100_data(info); int vreg;
int ret, vreg;
if (page >= info->pages) if (page >= info->pages)
return -ENXIO; return -ENXIO;
@ -265,27 +246,7 @@ static int zl6100_write_word_data(struct i2c_client *client, int page, int reg,
vreg = reg; vreg = reg;
} }
zl6100_wait(data); return pmbus_write_word_data(client, page, vreg, word);
ret = pmbus_write_word_data(client, page, vreg, word);
data->access = ktime_get();
return ret;
}
static int zl6100_write_byte(struct i2c_client *client, int page, u8 value)
{
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
struct zl6100_data *data = to_zl6100_data(info);
int ret;
if (page >= info->pages)
return -ENXIO;
zl6100_wait(data);
ret = pmbus_write_byte(client, page, value);
data->access = ktime_get();
return ret;
} }
static const struct i2c_device_id zl6100_id[] = { static const struct i2c_device_id zl6100_id[] = {
@ -363,14 +324,7 @@ static int zl6100_probe(struct i2c_client *client)
* supported chips are known to require a wait time between I2C * supported chips are known to require a wait time between I2C
* accesses. * accesses.
*/ */
data->delay = delay; udelay(delay);
/*
* Since there was a direct I2C device access above, wait before
* accessing the chip again.
*/
data->access = ktime_get();
zl6100_wait(data);
info = &data->info; info = &data->info;
@ -404,8 +358,7 @@ static int zl6100_probe(struct i2c_client *client)
if (ret < 0) if (ret < 0)
return ret; return ret;
data->access = ktime_get(); udelay(delay);
zl6100_wait(data);
if (ret & ZL8802_MFR_PHASES_MASK) if (ret & ZL8802_MFR_PHASES_MASK)
info->func[1] |= PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT; info->func[1] |= PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT;
@ -418,8 +371,7 @@ static int zl6100_probe(struct i2c_client *client)
if (ret < 0) if (ret < 0)
return ret; return ret;
data->access = ktime_get(); udelay(delay);
zl6100_wait(data);
ret = i2c_smbus_read_word_data(client, ZL8802_MFR_USER_CONFIG); ret = i2c_smbus_read_word_data(client, ZL8802_MFR_USER_CONFIG);
if (ret < 0) if (ret < 0)
@ -428,8 +380,7 @@ static int zl6100_probe(struct i2c_client *client)
if (ret & ZL8802_MFR_XTEMP_ENABLE_2) if (ret & ZL8802_MFR_XTEMP_ENABLE_2)
info->func[i] |= PMBUS_HAVE_TEMP2; info->func[i] |= PMBUS_HAVE_TEMP2;
data->access = ktime_get(); udelay(delay);
zl6100_wait(data);
} }
ret = i2c_smbus_read_word_data(client, ZL8802_MFR_USER_GLOBAL_CONFIG); ret = i2c_smbus_read_word_data(client, ZL8802_MFR_USER_GLOBAL_CONFIG);
if (ret < 0) if (ret < 0)
@ -446,13 +397,12 @@ static int zl6100_probe(struct i2c_client *client)
info->func[0] |= PMBUS_HAVE_TEMP2; info->func[0] |= PMBUS_HAVE_TEMP2;
} }
data->access = ktime_get(); udelay(delay);
zl6100_wait(data);
info->access_delay = delay;
info->read_word_data = zl6100_read_word_data; info->read_word_data = zl6100_read_word_data;
info->read_byte_data = zl6100_read_byte_data; info->read_byte_data = zl6100_read_byte_data;
info->write_word_data = zl6100_write_word_data; info->write_word_data = zl6100_write_word_data;
info->write_byte = zl6100_write_byte;
return pmbus_do_probe(client, info); return pmbus_do_probe(client, info);
} }

View File

@ -167,7 +167,7 @@ disable_regulator:
return ret; return ret;
} }
static int pwm_fan_power_off(struct pwm_fan_ctx *ctx) static int pwm_fan_power_off(struct pwm_fan_ctx *ctx, bool force_disable)
{ {
struct pwm_state *state = &ctx->pwm_state; struct pwm_state *state = &ctx->pwm_state;
bool enable_regulator = false; bool enable_regulator = false;
@ -180,7 +180,8 @@ static int pwm_fan_power_off(struct pwm_fan_ctx *ctx)
state, state,
&enable_regulator); &enable_regulator);
state->enabled = false; if (force_disable)
state->enabled = false;
state->duty_cycle = 0; state->duty_cycle = 0;
ret = pwm_apply_might_sleep(ctx->pwm, state); ret = pwm_apply_might_sleep(ctx->pwm, state);
if (ret) { if (ret) {
@ -213,7 +214,7 @@ static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
return ret; return ret;
ret = pwm_fan_power_on(ctx); ret = pwm_fan_power_on(ctx);
} else { } else {
ret = pwm_fan_power_off(ctx); ret = pwm_fan_power_off(ctx, false);
} }
if (!ret) if (!ret)
ctx->pwm_value = pwm; ctx->pwm_value = pwm;
@ -468,7 +469,7 @@ static void pwm_fan_cleanup(void *__ctx)
del_timer_sync(&ctx->rpm_timer); del_timer_sync(&ctx->rpm_timer);
/* Switch off everything */ /* Switch off everything */
ctx->enable_mode = pwm_disable_reg_disable; ctx->enable_mode = pwm_disable_reg_disable;
pwm_fan_power_off(ctx); pwm_fan_power_off(ctx, true);
} }
static int pwm_fan_probe(struct platform_device *pdev) static int pwm_fan_probe(struct platform_device *pdev)
@ -661,7 +662,7 @@ static int pwm_fan_suspend(struct device *dev)
{ {
struct pwm_fan_ctx *ctx = dev_get_drvdata(dev); struct pwm_fan_ctx *ctx = dev_get_drvdata(dev);
return pwm_fan_power_off(ctx); return pwm_fan_power_off(ctx, true);
} }
static int pwm_fan_resume(struct device *dev) static int pwm_fan_resume(struct device *dev)

View File

@ -416,8 +416,7 @@ static int sch5636_probe(struct platform_device *pdev)
id[i] = '\0'; id[i] = '\0';
if (strcmp(id, "THS")) { if (strcmp(id, "THS")) {
pr_err("Unknown Fujitsu id: %02x%02x%02x\n", pr_err("Unknown Fujitsu id: %3pE (%3ph)\n", id, id);
id[0], id[1], id[2]);
err = -ENODEV; err = -ENODEV;
goto error; goto error;
} }

View File

@ -22,4 +22,3 @@ int sch56xx_read_virtual_reg12(u16 addr, u16 msb_reg, u16 lsn_reg,
void sch56xx_watchdog_register(struct device *parent, u16 addr, u32 revision, void sch56xx_watchdog_register(struct device *parent, u16 addr, u32 revision,
struct mutex *io_lock, int check_enabled); struct mutex *io_lock, int check_enabled);
void sch56xx_watchdog_unregister(struct sch56xx_watchdog_data *data);

388
drivers/hwmon/sg2042-mcu.c Normal file
View File

@ -0,0 +1,388 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2024 Inochi Amaoto <inochiama@outlook.com>
*
* Sophgo power control mcu for SG2042
*/
#include <linux/cleanup.h>
#include <linux/debugfs.h>
#include <linux/err.h>
#include <linux/hwmon.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
/* fixed MCU registers */
#define REG_BOARD_TYPE 0x00
#define REG_MCU_FIRMWARE_VERSION 0x01
#define REG_PCB_VERSION 0x02
#define REG_PWR_CTRL 0x03
#define REG_SOC_TEMP 0x04
#define REG_BOARD_TEMP 0x05
#define REG_RST_COUNT 0x0a
#define REG_UPTIME 0x0b
#define REG_RESET_REASON 0x0d
#define REG_MCU_TYPE 0x18
#define REG_REPOWER_POLICY 0x65
#define REG_CRITICAL_TEMP 0x66
#define REG_REPOWER_TEMP 0x67
#define REPOWER_POLICY_REBOOT 1
#define REPOWER_POLICY_KEEP_OFF 2
#define MCU_POWER_MAX 0xff
#define DEFINE_MCU_DEBUG_ATTR(_name, _reg, _format) \
static int _name##_show(struct seq_file *seqf, \
void *unused) \
{ \
struct sg2042_mcu_data *mcu = seqf->private; \
int ret; \
ret = i2c_smbus_read_byte_data(mcu->client, (_reg)); \
if (ret < 0) \
return ret; \
seq_printf(seqf, _format "\n", ret); \
return 0; \
} \
DEFINE_SHOW_ATTRIBUTE(_name) \
struct sg2042_mcu_data {
struct i2c_client *client;
struct dentry *debugfs;
struct mutex mutex;
};
static struct dentry *sgmcu_debugfs;
static ssize_t reset_count_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct sg2042_mcu_data *mcu = dev_get_drvdata(dev);
int ret;
ret = i2c_smbus_read_byte_data(mcu->client, REG_RST_COUNT);
if (ret < 0)
return ret;
return sprintf(buf, "%d\n", ret);
}
static ssize_t uptime_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct sg2042_mcu_data *mcu = dev_get_drvdata(dev);
u8 time_val[2];
int ret;
ret = i2c_smbus_read_i2c_block_data(mcu->client, REG_UPTIME,
sizeof(time_val), time_val);
if (ret < 0)
return ret;
return sprintf(buf, "%d\n",
(time_val[0]) | (time_val[1] << 8));
}
static ssize_t reset_reason_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct sg2042_mcu_data *mcu = dev_get_drvdata(dev);
int ret;
ret = i2c_smbus_read_byte_data(mcu->client, REG_RESET_REASON);
if (ret < 0)
return ret;
return sprintf(buf, "0x%02x\n", ret);
}
static ssize_t repower_policy_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct sg2042_mcu_data *mcu = dev_get_drvdata(dev);
int ret;
const char *action;
ret = i2c_smbus_read_byte_data(mcu->client, REG_REPOWER_POLICY);
if (ret < 0)
return ret;
if (ret == REPOWER_POLICY_REBOOT)
action = "repower";
else if (ret == REPOWER_POLICY_KEEP_OFF)
action = "keep";
else
action = "unknown";
return sprintf(buf, "%s\n", action);
}
static ssize_t repower_policy_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct sg2042_mcu_data *mcu = dev_get_drvdata(dev);
u8 value;
int ret;
if (sysfs_streq("repower", buf))
value = REPOWER_POLICY_REBOOT;
else if (sysfs_streq("keep", buf))
value = REPOWER_POLICY_KEEP_OFF;
else
return -EINVAL;
ret = i2c_smbus_write_byte_data(mcu->client,
REG_REPOWER_POLICY, value);
if (ret < 0)
return ret;
return count;
}
static DEVICE_ATTR_RO(reset_count);
static DEVICE_ATTR_RO(uptime);
static DEVICE_ATTR_RO(reset_reason);
static DEVICE_ATTR_RW(repower_policy);
DEFINE_MCU_DEBUG_ATTR(firmware_version, REG_MCU_FIRMWARE_VERSION, "0x%02x");
DEFINE_MCU_DEBUG_ATTR(pcb_version, REG_PCB_VERSION, "0x%02x");
DEFINE_MCU_DEBUG_ATTR(board_type, REG_BOARD_TYPE, "0x%02x");
DEFINE_MCU_DEBUG_ATTR(mcu_type, REG_MCU_TYPE, "%d");
static struct attribute *sg2042_mcu_attrs[] = {
&dev_attr_reset_count.attr,
&dev_attr_uptime.attr,
&dev_attr_reset_reason.attr,
&dev_attr_repower_policy.attr,
NULL
};
static const struct attribute_group sg2042_mcu_attr_group = {
.attrs = sg2042_mcu_attrs,
};
static const struct attribute_group *sg2042_mcu_groups[] = {
&sg2042_mcu_attr_group,
NULL
};
static const struct hwmon_channel_info * const sg2042_mcu_info[] = {
HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ),
HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_CRIT |
HWMON_T_CRIT_HYST,
HWMON_T_INPUT),
NULL
};
static int sg2042_mcu_read(struct device *dev,
enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
struct sg2042_mcu_data *mcu = dev_get_drvdata(dev);
int tmp;
u8 reg;
switch (attr) {
case hwmon_temp_input:
reg = channel ? REG_BOARD_TEMP : REG_SOC_TEMP;
break;
case hwmon_temp_crit:
reg = REG_CRITICAL_TEMP;
break;
case hwmon_temp_crit_hyst:
reg = REG_REPOWER_TEMP;
break;
default:
return -EOPNOTSUPP;
}
tmp = i2c_smbus_read_byte_data(mcu->client, reg);
if (tmp < 0)
return tmp;
*val = tmp * 1000;
return 0;
}
static int sg2042_mcu_write(struct device *dev,
enum hwmon_sensor_types type,
u32 attr, int channel, long val)
{
struct sg2042_mcu_data *mcu = dev_get_drvdata(dev);
int temp = val / 1000;
int hyst_temp, crit_temp;
u8 reg;
temp = clamp_val(temp, 0, MCU_POWER_MAX);
guard(mutex)(&mcu->mutex);
switch (attr) {
case hwmon_temp_crit:
hyst_temp = i2c_smbus_read_byte_data(mcu->client,
REG_REPOWER_TEMP);
if (hyst_temp < 0)
return hyst_temp;
crit_temp = temp;
reg = REG_CRITICAL_TEMP;
break;
case hwmon_temp_crit_hyst:
crit_temp = i2c_smbus_read_byte_data(mcu->client,
REG_CRITICAL_TEMP);
if (crit_temp < 0)
return crit_temp;
hyst_temp = temp;
reg = REG_REPOWER_TEMP;
break;
default:
return -EOPNOTSUPP;
}
/*
* ensure hyst_temp is smaller to avoid MCU from
* keeping triggering repower event.
*/
if (crit_temp < hyst_temp)
return -EINVAL;
return i2c_smbus_write_byte_data(mcu->client, reg, temp);
}
static umode_t sg2042_mcu_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:
return 0444;
case hwmon_temp_crit:
case hwmon_temp_crit_hyst:
if (channel == 0)
return 0644;
break;
default:
break;
}
break;
default:
break;
}
return 0;
}
static const struct hwmon_ops sg2042_mcu_ops = {
.is_visible = sg2042_mcu_is_visible,
.read = sg2042_mcu_read,
.write = sg2042_mcu_write,
};
static const struct hwmon_chip_info sg2042_mcu_chip_info = {
.ops = &sg2042_mcu_ops,
.info = sg2042_mcu_info,
};
static void sg2042_mcu_debugfs_init(struct sg2042_mcu_data *mcu,
struct device *dev)
{
mcu->debugfs = debugfs_create_dir(dev_name(dev), sgmcu_debugfs);
debugfs_create_file("firmware_version", 0444, mcu->debugfs,
mcu, &firmware_version_fops);
debugfs_create_file("pcb_version", 0444, mcu->debugfs, mcu,
&pcb_version_fops);
debugfs_create_file("mcu_type", 0444, mcu->debugfs, mcu,
&mcu_type_fops);
debugfs_create_file("board_type", 0444, mcu->debugfs, mcu,
&board_type_fops);
}
static int sg2042_mcu_i2c_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct sg2042_mcu_data *mcu;
struct device *hwmon_dev;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA))
return -ENODEV;
mcu = devm_kmalloc(dev, sizeof(*mcu), GFP_KERNEL);
if (!mcu)
return -ENOMEM;
mutex_init(&mcu->mutex);
mcu->client = client;
i2c_set_clientdata(client, mcu);
hwmon_dev = devm_hwmon_device_register_with_info(dev, "sg2042_mcu",
mcu,
&sg2042_mcu_chip_info,
NULL);
if (IS_ERR(hwmon_dev))
return PTR_ERR(hwmon_dev);
sg2042_mcu_debugfs_init(mcu, dev);
return 0;
}
static void sg2042_mcu_i2c_remove(struct i2c_client *client)
{
struct sg2042_mcu_data *mcu = i2c_get_clientdata(client);
debugfs_remove_recursive(mcu->debugfs);
}
static const struct i2c_device_id sg2042_mcu_id[] = {
{ "sg2042-hwmon-mcu", 0 },
{},
};
MODULE_DEVICE_TABLE(i2c, sg2042_mcu_id);
static const struct of_device_id sg2042_mcu_of_id[] = {
{ .compatible = "sophgo,sg2042-hwmon-mcu" },
{},
};
MODULE_DEVICE_TABLE(of, sg2042_mcu_of_id);
static struct i2c_driver sg2042_mcu_driver = {
.driver = {
.name = "sg2042-mcu",
.of_match_table = sg2042_mcu_of_id,
.dev_groups = sg2042_mcu_groups,
},
.probe = sg2042_mcu_i2c_probe,
.remove = sg2042_mcu_i2c_remove,
.id_table = sg2042_mcu_id,
};
static int __init sg2042_mcu_init(void)
{
sgmcu_debugfs = debugfs_create_dir("sg2042-mcu", NULL);
return i2c_add_driver(&sg2042_mcu_driver);
}
static void __exit sg2042_mcu_exit(void)
{
debugfs_remove_recursive(sgmcu_debugfs);
i2c_del_driver(&sg2042_mcu_driver);
}
module_init(sg2042_mcu_init);
module_exit(sg2042_mcu_exit);
MODULE_AUTHOR("Inochi Amaoto <inochiama@outlook.com>");
MODULE_DESCRIPTION("MCU I2C driver for SG2042 soc platform");
MODULE_LICENSE("GPL");

View File

@ -199,10 +199,7 @@ static ssize_t eic_read(struct sht21 *sht21)
eic[6] = rx[0]; eic[6] = rx[0];
eic[7] = rx[1]; eic[7] = rx[1];
ret = snprintf(sht21->eic, sizeof(sht21->eic), ret = snprintf(sht21->eic, sizeof(sht21->eic), "%8phN\n", eic);
"%02x%02x%02x%02x%02x%02x%02x%02x\n",
eic[0], eic[1], eic[2], eic[3],
eic[4], eic[5], eic[6], eic[7]);
out: out:
if (ret < 0) if (ret < 0)
sht21->eic[0] = 0; sht21->eic[0] = 0;

View File

@ -77,7 +77,7 @@ static const struct i2c_device_id stts751_id[] = {
}; };
static const struct of_device_id __maybe_unused stts751_of_match[] = { static const struct of_device_id __maybe_unused stts751_of_match[] = {
{ .compatible = "stts751" }, { .compatible = "st,stts751" },
{ }, { },
}; };
MODULE_DEVICE_TABLE(of, stts751_of_match); MODULE_DEVICE_TABLE(of, stts751_of_match);

View File

@ -0,0 +1,235 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Thermal sensor subsystem driver for Surface System Aggregator Module (SSAM).
*
* Copyright (C) 2022-2023 Maximilian Luz <luzmaximilian@gmail.com>
*/
#include <linux/bitops.h>
#include <linux/hwmon.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/surface_aggregator/controller.h>
#include <linux/surface_aggregator/device.h>
/* -- SAM interface. -------------------------------------------------------- */
/*
* Available sensors are indicated by a 16-bit bitfield, where a 1 marks the
* presence of a sensor. So we have at most 16 possible sensors/channels.
*/
#define SSAM_TMP_SENSOR_MAX_COUNT 16
/*
* All names observed so far are 6 characters long, but there's only
* zeros after the name, so perhaps they can be longer. This number reflects
* the maximum zero-padded space observed in the returned buffer.
*/
#define SSAM_TMP_SENSOR_NAME_LENGTH 18
struct ssam_tmp_get_name_rsp {
__le16 unknown1;
char unknown2;
char name[SSAM_TMP_SENSOR_NAME_LENGTH];
} __packed;
static_assert(sizeof(struct ssam_tmp_get_name_rsp) == 21);
SSAM_DEFINE_SYNC_REQUEST_CL_R(__ssam_tmp_get_available_sensors, __le16, {
.target_category = SSAM_SSH_TC_TMP,
.command_id = 0x04,
});
SSAM_DEFINE_SYNC_REQUEST_MD_R(__ssam_tmp_get_temperature, __le16, {
.target_category = SSAM_SSH_TC_TMP,
.command_id = 0x01,
});
SSAM_DEFINE_SYNC_REQUEST_MD_R(__ssam_tmp_get_name, struct ssam_tmp_get_name_rsp, {
.target_category = SSAM_SSH_TC_TMP,
.command_id = 0x0e,
});
static int ssam_tmp_get_available_sensors(struct ssam_device *sdev, s16 *sensors)
{
__le16 sensors_le;
int status;
status = __ssam_tmp_get_available_sensors(sdev, &sensors_le);
if (status)
return status;
*sensors = le16_to_cpu(sensors_le);
return 0;
}
static int ssam_tmp_get_temperature(struct ssam_device *sdev, u8 iid, long *temperature)
{
__le16 temp_le;
int status;
status = __ssam_tmp_get_temperature(sdev->ctrl, sdev->uid.target, iid, &temp_le);
if (status)
return status;
/* Convert 1/10 °K to 1/1000 °C */
*temperature = (le16_to_cpu(temp_le) - 2731) * 100L;
return 0;
}
static int ssam_tmp_get_name(struct ssam_device *sdev, u8 iid, char *buf, size_t buf_len)
{
struct ssam_tmp_get_name_rsp name_rsp;
int status;
status = __ssam_tmp_get_name(sdev->ctrl, sdev->uid.target, iid, &name_rsp);
if (status)
return status;
/*
* This should not fail unless the name in the returned struct is not
* null-terminated or someone changed something in the struct
* definitions above, since our buffer and struct have the same
* capacity by design. So if this fails, log an error message. Since
* the more likely cause is that the returned string isn't
* null-terminated, we might have received garbage (as opposed to just
* an incomplete string), so also fail the function.
*/
status = strscpy(buf, name_rsp.name, buf_len);
if (status < 0) {
dev_err(&sdev->dev, "received non-null-terminated sensor name string\n");
return status;
}
return 0;
}
/* -- Driver.---------------------------------------------------------------- */
struct ssam_temp {
struct ssam_device *sdev;
s16 sensors;
char names[SSAM_TMP_SENSOR_MAX_COUNT][SSAM_TMP_SENSOR_NAME_LENGTH];
};
static umode_t ssam_temp_hwmon_is_visible(const void *data,
enum hwmon_sensor_types type,
u32 attr, int channel)
{
const struct ssam_temp *ssam_temp = data;
if (!(ssam_temp->sensors & BIT(channel)))
return 0;
return 0444;
}
static int ssam_temp_hwmon_read(struct device *dev,
enum hwmon_sensor_types type,
u32 attr, int channel, long *value)
{
const struct ssam_temp *ssam_temp = dev_get_drvdata(dev);
return ssam_tmp_get_temperature(ssam_temp->sdev, channel + 1, value);
}
static int ssam_temp_hwmon_read_string(struct device *dev,
enum hwmon_sensor_types type,
u32 attr, int channel, const char **str)
{
const struct ssam_temp *ssam_temp = dev_get_drvdata(dev);
*str = ssam_temp->names[channel];
return 0;
}
static const struct hwmon_channel_info * const ssam_temp_hwmon_info[] = {
HWMON_CHANNEL_INFO(chip,
HWMON_C_REGISTER_TZ),
HWMON_CHANNEL_INFO(temp,
HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_INPUT | HWMON_T_LABEL,
HWMON_T_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),
NULL
};
static const struct hwmon_ops ssam_temp_hwmon_ops = {
.is_visible = ssam_temp_hwmon_is_visible,
.read = ssam_temp_hwmon_read,
.read_string = ssam_temp_hwmon_read_string,
};
static const struct hwmon_chip_info ssam_temp_hwmon_chip_info = {
.ops = &ssam_temp_hwmon_ops,
.info = ssam_temp_hwmon_info,
};
static int ssam_temp_probe(struct ssam_device *sdev)
{
struct ssam_temp *ssam_temp;
struct device *hwmon_dev;
s16 sensors;
int channel;
int status;
status = ssam_tmp_get_available_sensors(sdev, &sensors);
if (status)
return status;
ssam_temp = devm_kzalloc(&sdev->dev, sizeof(*ssam_temp), GFP_KERNEL);
if (!ssam_temp)
return -ENOMEM;
ssam_temp->sdev = sdev;
ssam_temp->sensors = sensors;
/* Retrieve the name for each available sensor. */
for (channel = 0; channel < SSAM_TMP_SENSOR_MAX_COUNT; channel++) {
if (!(sensors & BIT(channel)))
continue;
status = ssam_tmp_get_name(sdev, channel + 1, ssam_temp->names[channel],
SSAM_TMP_SENSOR_NAME_LENGTH);
if (status)
return status;
}
hwmon_dev = devm_hwmon_device_register_with_info(&sdev->dev, "surface_thermal", ssam_temp,
&ssam_temp_hwmon_chip_info, NULL);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
static const struct ssam_device_id ssam_temp_match[] = {
{ SSAM_SDEV(TMP, SAM, 0x00, 0x02) },
{ },
};
MODULE_DEVICE_TABLE(ssam, ssam_temp_match);
static struct ssam_device_driver ssam_temp = {
.probe = ssam_temp_probe,
.match_table = ssam_temp_match,
.driver = {
.name = "surface_temp",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
};
module_ssam_device_driver(ssam_temp);
MODULE_AUTHOR("Maximilian Luz <luzmaximilian@gmail.com>");
MODULE_DESCRIPTION("Thermal sensor subsystem driver for Surface System Aggregator Module");
MODULE_LICENSE("GPL");

View File

@ -308,7 +308,9 @@ static int tmp401_temp_read(struct device *dev, u32 attr, int channel, long *val
{ {
struct tmp401_data *data = dev_get_drvdata(dev); struct tmp401_data *data = dev_get_drvdata(dev);
struct regmap *regmap = data->regmap; struct regmap *regmap = data->regmap;
unsigned int regs[2] = { TMP401_TEMP_MSB[3][channel], TMP401_TEMP_CRIT_HYST };
unsigned int regval; unsigned int regval;
u16 regvals[2];
int reg, ret; int reg, ret;
switch (attr) { switch (attr) {
@ -325,20 +327,11 @@ static int tmp401_temp_read(struct device *dev, u32 attr, int channel, long *val
*val = tmp401_register_to_temp(regval, data->extended_range); *val = tmp401_register_to_temp(regval, data->extended_range);
break; break;
case hwmon_temp_crit_hyst: case hwmon_temp_crit_hyst:
mutex_lock(&data->update_lock); ret = regmap_multi_reg_read(regmap, regs, regvals, 2);
reg = TMP401_TEMP_MSB[3][channel];
ret = regmap_read(regmap, reg, &regval);
if (ret < 0)
goto unlock;
*val = tmp401_register_to_temp(regval, data->extended_range);
ret = regmap_read(regmap, TMP401_TEMP_CRIT_HYST, &regval);
if (ret < 0)
goto unlock;
*val -= regval * 1000;
unlock:
mutex_unlock(&data->update_lock);
if (ret < 0) if (ret < 0)
return ret; return ret;
*val = tmp401_register_to_temp(regvals[0], data->extended_range) -
(regvals[1] * 1000);
break; break;
case hwmon_temp_fault: case hwmon_temp_fault:
case hwmon_temp_min_alarm: case hwmon_temp_min_alarm:

View File

@ -410,18 +410,15 @@ static int tmp421_probe_from_dt(struct i2c_client *client, struct tmp421_data *d
{ {
struct device *dev = &client->dev; struct device *dev = &client->dev;
const struct device_node *np = dev->of_node; const struct device_node *np = dev->of_node;
struct device_node *child;
int err; int err;
for_each_child_of_node(np, child) { for_each_child_of_node_scoped(np, child) {
if (strcmp(child->name, "channel")) if (strcmp(child->name, "channel"))
continue; continue;
err = tmp421_probe_child_from_dt(client, child, data); err = tmp421_probe_child_from_dt(client, child, data);
if (err) { if (err)
of_node_put(child);
return err; return err;
}
} }
return 0; return 0;

View File

@ -147,11 +147,11 @@ static int tmp464_temp_read(struct device *dev, u32 attr, int channel, long *val
{ {
struct tmp464_data *data = dev_get_drvdata(dev); struct tmp464_data *data = dev_get_drvdata(dev);
struct regmap *regmap = data->regmap; struct regmap *regmap = data->regmap;
unsigned int regval, regval2; unsigned int regs[2];
unsigned int regval;
u16 regvals[2];
int err = 0; int err = 0;
mutex_lock(&data->update_lock);
switch (attr) { switch (attr) {
case hwmon_temp_max_alarm: case hwmon_temp_max_alarm:
err = regmap_read(regmap, TMP464_THERM_STATUS_REG, &regval); err = regmap_read(regmap, TMP464_THERM_STATUS_REG, &regval);
@ -172,26 +172,27 @@ static int tmp464_temp_read(struct device *dev, u32 attr, int channel, long *val
* complete. That means we have to cache the value internally * complete. That means we have to cache the value internally
* for one measurement cycle and report the cached value. * for one measurement cycle and report the cached value.
*/ */
mutex_lock(&data->update_lock);
if (!data->valid || time_after(jiffies, data->last_updated + if (!data->valid || time_after(jiffies, data->last_updated +
msecs_to_jiffies(data->update_interval))) { msecs_to_jiffies(data->update_interval))) {
err = regmap_read(regmap, TMP464_REMOTE_OPEN_REG, &regval); err = regmap_read(regmap, TMP464_REMOTE_OPEN_REG, &regval);
if (err < 0) if (err < 0)
break; goto unlock;
data->open_reg = regval; data->open_reg = regval;
data->last_updated = jiffies; data->last_updated = jiffies;
data->valid = true; data->valid = true;
} }
*val = !!(data->open_reg & BIT(channel + 7)); *val = !!(data->open_reg & BIT(channel + 7));
unlock:
mutex_unlock(&data->update_lock);
break; break;
case hwmon_temp_max_hyst: case hwmon_temp_max_hyst:
err = regmap_read(regmap, TMP464_THERM_LIMIT[channel], &regval); regs[0] = TMP464_THERM_LIMIT[channel];
regs[1] = TMP464_TEMP_HYST_REG;
err = regmap_multi_reg_read(regmap, regs, regvals, 2);
if (err < 0) if (err < 0)
break; break;
err = regmap_read(regmap, TMP464_TEMP_HYST_REG, &regval2); *val = temp_from_reg(regvals[0] - regvals[1]);
if (err < 0)
break;
regval -= regval2;
*val = temp_from_reg(regval);
break; break;
case hwmon_temp_max: case hwmon_temp_max:
err = regmap_read(regmap, TMP464_THERM_LIMIT[channel], &regval); err = regmap_read(regmap, TMP464_THERM_LIMIT[channel], &regval);
@ -200,14 +201,12 @@ static int tmp464_temp_read(struct device *dev, u32 attr, int channel, long *val
*val = temp_from_reg(regval); *val = temp_from_reg(regval);
break; break;
case hwmon_temp_crit_hyst: case hwmon_temp_crit_hyst:
err = regmap_read(regmap, TMP464_THERM2_LIMIT[channel], &regval); regs[0] = TMP464_THERM2_LIMIT[channel];
regs[1] = TMP464_TEMP_HYST_REG;
err = regmap_multi_reg_read(regmap, regs, regvals, 2);
if (err < 0) if (err < 0)
break; break;
err = regmap_read(regmap, TMP464_TEMP_HYST_REG, &regval2); *val = temp_from_reg(regvals[0] - regvals[1]);
if (err < 0)
break;
regval -= regval2;
*val = temp_from_reg(regval);
break; break;
case hwmon_temp_crit: case hwmon_temp_crit:
err = regmap_read(regmap, TMP464_THERM2_LIMIT[channel], &regval); err = regmap_read(regmap, TMP464_THERM2_LIMIT[channel], &regval);
@ -239,8 +238,6 @@ static int tmp464_temp_read(struct device *dev, u32 attr, int channel, long *val
break; break;
} }
mutex_unlock(&data->update_lock);
return err; return err;
} }
@ -565,18 +562,15 @@ static int tmp464_probe_child_from_dt(struct device *dev,
static int tmp464_probe_from_dt(struct device *dev, struct tmp464_data *data) static int tmp464_probe_from_dt(struct device *dev, struct tmp464_data *data)
{ {
const struct device_node *np = dev->of_node; const struct device_node *np = dev->of_node;
struct device_node *child;
int err; int err;
for_each_child_of_node(np, child) { for_each_child_of_node_scoped(np, child) {
if (strcmp(child->name, "channel")) if (strcmp(child->name, "channel"))
continue; continue;
err = tmp464_probe_child_from_dt(dev, child, data); err = tmp464_probe_child_from_dt(dev, child, data);
if (err) { if (err)
of_node_put(child);
return err; return err;
}
} }
return 0; return 0;

View File

@ -72,7 +72,7 @@ static umode_t vexpress_hwmon_attr_is_visible(struct kobject *kobj,
struct device_attribute, attr); struct device_attribute, attr);
if (dev_attr->show == vexpress_hwmon_label_show && if (dev_attr->show == vexpress_hwmon_label_show &&
!of_get_property(dev->of_node, "label", NULL)) !of_property_present(dev->of_node, "label"))
return 0; return 0;
return attr->mode; return attr->mode;

View File

@ -481,7 +481,6 @@ devm_hwmon_device_register_with_info(struct device *dev,
const struct attribute_group **extra_groups); const struct attribute_group **extra_groups);
void hwmon_device_unregister(struct device *dev); void hwmon_device_unregister(struct device *dev);
void devm_hwmon_device_unregister(struct device *dev);
int hwmon_notify_event(struct device *dev, enum hwmon_sensor_types type, int hwmon_notify_event(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel); u32 attr, int channel);

View File

@ -1,33 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* max6697.h
* Copyright (c) 2012 Guenter Roeck <linux@roeck-us.net>
*/
#ifndef MAX6697_H
#define MAX6697_H
#include <linux/types.h>
/*
* For all bit masks:
* bit 0: local temperature
* bit 1..7: remote temperatures
*/
struct max6697_platform_data {
bool smbus_timeout_disable; /* set to disable SMBus timeouts */
bool extended_range_enable; /* set to enable extended temp range */
bool beta_compensation; /* set to enable beta compensation */
u8 alert_mask; /* set bit to 1 to disable alert */
u8 over_temperature_mask; /* set bit to 1 to disable */
u8 resistance_cancellation; /* set bit to 0 to disable
* bit mask for MAX6581,
* boolean for other chips
*/
u8 ideality_mask; /* set bit to 0 to disable */
u8 ideality_value; /* transistor ideality as per
* MAX6581 datasheet
*/
};
#endif /* MAX6697_H */