mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2024-12-28 16:52:18 +00:00
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:
commit
c27ea952c6
@ -45,12 +45,31 @@ properties:
|
||||
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.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32-array
|
||||
deprecated: true
|
||||
minItems: 3
|
||||
maxItems: 3
|
||||
items:
|
||||
enum: [0, 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:
|
||||
"^adi,bypass-attenuator-in[0-4]$":
|
||||
description: |
|
||||
@ -81,6 +100,10 @@ patternProperties:
|
||||
- smbalert#
|
||||
- gpio
|
||||
|
||||
"^fan-[0-9]+$":
|
||||
$ref: fan-common.yaml#
|
||||
unevaluatedProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
@ -89,17 +112,27 @@ additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/pwm/pwm.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
hwmon@2e {
|
||||
pwm: hwmon@2e {
|
||||
compatible = "adi,adt7476";
|
||||
reg = <0x2e>;
|
||||
adi,bypass-attenuator-in0 = <1>;
|
||||
adi,bypass-attenuator-in1 = <0>;
|
||||
adi,pwm-active-state = <1 0 1>;
|
||||
adi,pin10-function = "smbalert#";
|
||||
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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
94
Documentation/devicetree/bindings/hwmon/lltc,ltc2978.yaml
Normal file
94
Documentation/devicetree/bindings/hwmon/lltc,ltc2978.yaml
Normal 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";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -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";
|
||||
};
|
||||
};
|
||||
};
|
70
Documentation/devicetree/bindings/hwmon/maxim,max31790.yaml
Normal file
70
Documentation/devicetree/bindings/hwmon/maxim,max31790.yaml
Normal 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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -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>;
|
||||
};
|
||||
};
|
@ -38,8 +38,6 @@ register/unregister functions::
|
||||
|
||||
void hwmon_device_unregister(struct device *dev);
|
||||
|
||||
void devm_hwmon_device_unregister(struct device *dev);
|
||||
|
||||
char *hwmon_sanitize_name(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
|
||||
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
|
||||
names. Device names including invalid characters (whitespace, '*', or '-')
|
||||
will be rejected. The 'name' parameter is mandatory.
|
||||
|
@ -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_crit Critical high shunt voltage
|
||||
in0_lcrit_alarm Shunt voltage critical low alarm
|
||||
|
@ -206,6 +206,7 @@ Hardware Monitoring Kernel Drivers
|
||||
sch5636
|
||||
scpi-hwmon
|
||||
sfctemp
|
||||
sg2042-mcu
|
||||
sht15
|
||||
sht21
|
||||
sht3x
|
||||
|
@ -3,29 +3,29 @@ Kernel driver lm92
|
||||
|
||||
Supported chips:
|
||||
|
||||
* National Semiconductor LM92
|
||||
* National Semiconductor / Texas Instruments LM92
|
||||
|
||||
Prefix: 'lm92'
|
||||
|
||||
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'
|
||||
|
||||
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'
|
||||
|
||||
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:
|
||||
@ -36,13 +36,13 @@ Authors:
|
||||
Description
|
||||
-----------
|
||||
|
||||
This driver implements support for the National Semiconductor LM92
|
||||
temperature sensor.
|
||||
This driver implements support for the National Semiconductor / Texas
|
||||
Instruments LM92 temperature sensor.
|
||||
|
||||
Each LM92 temperature sensor supports a single temperature sensor. There are
|
||||
alarms for high, low, and critical thresholds. There's also an hysteresis to
|
||||
control the thresholds for resetting alarms.
|
||||
|
||||
Support was added later for the LM76 and Maxim MAX6633/MAX6634/MAX6635,
|
||||
which are mostly compatible. They have not all been tested, so you
|
||||
may need to use the force parameter.
|
||||
The driver also supports LM76 and Maxim MAX6633/MAX6634/MAX6635, which are
|
||||
mostly compatible but do not have a vendor ID register and therefore must be
|
||||
instantiated explicitly.
|
||||
|
@ -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.
|
||||
|
||||
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.
|
||||
|
@ -10,41 +10,59 @@ Authors:
|
||||
Description:
|
||||
------------
|
||||
|
||||
Handheld devices from One Netbook and Aya Neo provide fan readings and fan
|
||||
control through their embedded controllers.
|
||||
Handheld devices from OneNetbook, AOKZOE, AYANEO, And OrangePi provide fan
|
||||
readings and fan control through their embedded controllers.
|
||||
|
||||
Currently only supports AMD boards from One X Player, AOK ZOE, and some Aya
|
||||
Neo devices. One X Player Intel boards could be supported if we could figure
|
||||
out the EC registers and values to write to since the EC layout and model is
|
||||
different. Aya Neo devices preceding the AIR may not be supportable as the EC
|
||||
model is different and do not appear to have manual control capabilities.
|
||||
Currently supports OneXPlayer devices, AOKZOE, AYANEO, and OrangePi
|
||||
handheld devices. AYANEO devices preceding the AIR and OneXPlayer devices
|
||||
preceding the Mini A07 are not supportable as the EC model is different
|
||||
and do not have manual control capabilities.
|
||||
|
||||
Some models have a toggle for changing the behaviour of the "Turbo/Silent"
|
||||
button of the device. It will change the key event that it triggers with
|
||||
a flip of the `tt_toggle` attribute. See below for boards that support this
|
||||
function.
|
||||
Some OneXPlayer and AOKZOE models have a toggle for changing the behaviour
|
||||
of the "Turbo/Silent" button of the device. It will change the key event
|
||||
that it triggers with a flip of the `tt_toggle` attribute. See below for
|
||||
boards that support this function.
|
||||
|
||||
Supported devices
|
||||
-----------------
|
||||
|
||||
Currently the driver supports the following handhelds:
|
||||
|
||||
- AOK ZOE A1
|
||||
- AOK ZOE A1 PRO
|
||||
- Aya Neo 2
|
||||
- Aya Neo AIR
|
||||
- Aya Neo AIR Plus (Mendocino)
|
||||
- Aya Neo AIR Pro
|
||||
- Aya Neo Geek
|
||||
- AOKZOE A1
|
||||
- AOKZOE A1 PRO
|
||||
- AYANEO 2
|
||||
- AYANEO 2S
|
||||
- AYANEO AIR
|
||||
- AYANEO AIR 1S
|
||||
- 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 mini AMD
|
||||
- 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:
|
||||
- AOK ZOE A1
|
||||
- AOK ZOE A1 PRO
|
||||
- OneXPlayer 2
|
||||
- OneXPlayer 2 Pro
|
||||
- OneXPlayer mini AMD (only with updated alpha BIOS)
|
||||
- OneXPlayer mini AMD PRO
|
||||
- OneXPlayer OneXFly
|
||||
- OneXPlayer X1 A
|
||||
- OneXPlayer X1 i
|
||||
- OneXPlayer X1 mini
|
||||
|
||||
Sysfs entries
|
||||
-------------
|
||||
@ -52,7 +70,7 @@ Sysfs entries
|
||||
The following attributes are supported:
|
||||
|
||||
fan1_input
|
||||
Read Only. Reads current fan RMP.
|
||||
Read Only. Reads current fan RPM.
|
||||
|
||||
pwm1_enable
|
||||
Read Write. Enable manual fan control. Write "1" to set to manual, write "0"
|
||||
|
78
Documentation/hwmon/sg2042-mcu.rst
Normal file
78
Documentation/hwmon/sg2042-mcu.rst
Normal 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
|
||||
================= ======= =============================================
|
@ -15295,6 +15295,12 @@ S: Maintained
|
||||
F: Documentation/hwmon/surface_fan.rst
|
||||
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
|
||||
M: Maximilian Luz <luzmaximilian@gmail.com>
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
|
@ -1511,9 +1511,10 @@ config SENSORS_LM90
|
||||
config SENSORS_LM92
|
||||
tristate "National Semiconductor LM92 and compatibles"
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
help
|
||||
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
|
||||
will be called lm92.
|
||||
@ -1532,6 +1533,7 @@ config SENSORS_LM93
|
||||
config SENSORS_LM95234
|
||||
tristate "National Semiconductor LM95234 and compatibles"
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
help
|
||||
If you say yes here you get support for the LM95233 and LM95234
|
||||
temperature sensor chips.
|
||||
@ -2066,6 +2068,17 @@ config SENSORS_SFCTEMP
|
||||
This driver can also be built as a module. If so, the module
|
||||
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
|
||||
tristate "Surface Fan Driver"
|
||||
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.
|
||||
|
||||
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
|
||||
tristate "Texas Instruments ADC128D818"
|
||||
depends on I2C
|
||||
|
@ -194,6 +194,7 @@ obj-$(CONFIG_SENSORS_SCH56XX_COMMON)+= sch56xx-common.o
|
||||
obj-$(CONFIG_SENSORS_SCH5627) += sch5627.o
|
||||
obj-$(CONFIG_SENSORS_SCH5636) += sch5636.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_SHT15) += sht15.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_STTS751) += stts751.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_AMC6821) += amc6821.o
|
||||
obj-$(CONFIG_SENSORS_TC74) += tc74.o
|
||||
|
@ -728,30 +728,22 @@ static const int adt7470_freq_map[] = {
|
||||
static int pwm1_freq_get(struct device *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 err;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
err = regmap_read(data->regmap, ADT7470_REG_CFG, &cfg_reg_1);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
err = regmap_read(data->regmap, ADT7470_REG_CFG_2, &cfg_reg_2);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
mutex_unlock(&data->lock);
|
||||
err = regmap_multi_reg_read(data->regmap, regs, cfg_reg, 2);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
index = (cfg_reg_2 & ADT7470_FREQ_MASK) >> ADT7470_FREQ_SHIFT;
|
||||
if (!(cfg_reg_1 & ADT7470_CFG_LF))
|
||||
index = (cfg_reg[1] & ADT7470_FREQ_MASK) >> ADT7470_FREQ_SHIFT;
|
||||
if (!(cfg_reg[0] & ADT7470_CFG_LF))
|
||||
index += 8;
|
||||
if (index >= ARRAY_SIZE(adt7470_freq_map))
|
||||
index = ARRAY_SIZE(adt7470_freq_map) - 1;
|
||||
|
||||
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)
|
||||
|
@ -21,6 +21,8 @@
|
||||
#include <linux/of.h>
|
||||
#include <linux/util_macros.h>
|
||||
|
||||
#include <dt-bindings/pwm/pwm.h>
|
||||
|
||||
/* Indexes for the sysfs hooks */
|
||||
enum adt_sysfs_id {
|
||||
INPUT = 0,
|
||||
@ -1662,6 +1664,130 @@ static int adt7475_set_pwm_polarity(struct i2c_client *client)
|
||||
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)
|
||||
{
|
||||
enum chips chip;
|
||||
@ -1774,6 +1900,10 @@ static int adt7475_probe(struct i2c_client *client)
|
||||
if (ret && ret != -EINVAL)
|
||||
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 */
|
||||
switch (chip) {
|
||||
case adt7475:
|
||||
|
@ -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)
|
||||
{
|
||||
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_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);
|
||||
ret = regmap_multi_reg_read(data->regmap, regs, regdata, 2);
|
||||
if (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
|
||||
@ -194,7 +188,7 @@ static int adt7x10_hyst_read(struct adt7x10_data *data, int index, long *val)
|
||||
if (index == adt7x10_t_alarm_low)
|
||||
hyst = -hyst;
|
||||
|
||||
*val = ADT7X10_REG_TO_TEMP(data, temp) - hyst;
|
||||
*val = ADT7X10_REG_TO_TEMP(data, regdata[1]) - hyst;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -136,29 +136,25 @@ struct amc6821_data {
|
||||
*/
|
||||
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;
|
||||
|
||||
err = regmap_read(regmap, AMC6821_REG_DCY_LOW_TEMP, &pwm);
|
||||
err = regmap_multi_reg_read(regmap, regs, regvals, 3);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = regmap_read(regmap, AMC6821_REG_PSV_TEMP, ®val);
|
||||
if (err)
|
||||
return err;
|
||||
temps[0] = regval;
|
||||
|
||||
err = regmap_read(regmap,
|
||||
channel ? AMC6821_REG_RTEMP_FAN_CTRL : AMC6821_REG_LTEMP_FAN_CTRL,
|
||||
®val);
|
||||
if (err)
|
||||
return err;
|
||||
temps[1] = FIELD_GET(AMC6821_TEMP_LIMIT_MASK, regval) * 4;
|
||||
temps[0] = regvals[1];
|
||||
temps[1] = FIELD_GET(AMC6821_TEMP_LIMIT_MASK, regvals[2]) * 4;
|
||||
|
||||
/* slope is 32 >> <slope bits> in °C */
|
||||
regval = 32 >> FIELD_GET(AMC6821_TEMP_SLOPE_MASK, regval);
|
||||
if (regval)
|
||||
temps[2] = temps[1] + DIV_ROUND_CLOSEST(255 - pwm, regval);
|
||||
slope = 32 >> FIELD_GET(AMC6821_TEMP_SLOPE_MASK, regvals[2]);
|
||||
if (slope)
|
||||
temps[2] = temps[1] + DIV_ROUND_CLOSEST(255 - regvals[0], slope);
|
||||
else
|
||||
temps[2] = 255;
|
||||
|
||||
|
@ -456,7 +456,6 @@ static int aspeed_pwm_tach_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev, *hwmon;
|
||||
int ret;
|
||||
struct device_node *child;
|
||||
struct aspeed_pwm_tach_data *priv;
|
||||
struct pwm_chip *chip;
|
||||
|
||||
@ -498,10 +497,9 @@ static int aspeed_pwm_tach_probe(struct platform_device *pdev)
|
||||
if (ret)
|
||||
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);
|
||||
if (ret) {
|
||||
of_node_put(child);
|
||||
dev_warn(dev, "Failed to create fan %d", ret);
|
||||
return 0;
|
||||
}
|
||||
|
@ -907,7 +907,7 @@ static void aspeed_pwm_tacho_remove(void *data)
|
||||
static int aspeed_pwm_tacho_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np, *child;
|
||||
struct device_node *np;
|
||||
struct aspeed_pwm_tacho_data *priv;
|
||||
void __iomem *regs;
|
||||
struct device *hwmon;
|
||||
@ -951,12 +951,10 @@ static int aspeed_pwm_tacho_probe(struct platform_device *pdev)
|
||||
|
||||
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);
|
||||
if (ret) {
|
||||
of_node_put(child);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
priv->groups[0] = &pwm_dev_group;
|
||||
|
@ -740,37 +740,26 @@ static int cc2_probe(struct i2c_client *client)
|
||||
data->client = client;
|
||||
|
||||
data->regulator = devm_regulator_get_exclusive(dev, "vdd");
|
||||
if (IS_ERR(data->regulator)) {
|
||||
dev_err_probe(dev, PTR_ERR(data->regulator),
|
||||
"Failed to get regulator\n");
|
||||
return PTR_ERR(data->regulator);
|
||||
}
|
||||
if (IS_ERR(data->regulator))
|
||||
return dev_err_probe(dev, PTR_ERR(data->regulator),
|
||||
"Failed to get regulator\n");
|
||||
|
||||
ret = cc2_request_ready_irq(data, dev);
|
||||
if (ret) {
|
||||
dev_err_probe(dev, ret, "Failed to request ready irq\n");
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to request ready irq\n");
|
||||
|
||||
ret = cc2_request_alarm_irqs(data, dev);
|
||||
if (ret) {
|
||||
dev_err_probe(dev, ret, "Failed to request alarm irqs\n");
|
||||
goto disable;
|
||||
}
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to request alarm irqs\n");
|
||||
|
||||
data->hwmon = devm_hwmon_device_register_with_info(dev, client->name,
|
||||
data, &cc2_chip_info,
|
||||
NULL);
|
||||
if (IS_ERR(data->hwmon)) {
|
||||
dev_err_probe(dev, PTR_ERR(data->hwmon),
|
||||
"Failed to register hwmon device\n");
|
||||
ret = PTR_ERR(data->hwmon);
|
||||
}
|
||||
if (IS_ERR(data->hwmon))
|
||||
return dev_err_probe(dev, PTR_ERR(data->hwmon),
|
||||
"Failed to register hwmon device\n");
|
||||
|
||||
disable:
|
||||
cc2_disable(data);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cc2_remove(struct i2c_client *client)
|
||||
|
@ -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],
|
||||
},
|
||||
{
|
||||
.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",
|
||||
.matches = {
|
||||
|
@ -400,6 +400,7 @@ static const struct of_device_id gsc_hwmon_of_match[] = {
|
||||
{ .compatible = "gw,gsc-adc", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, gsc_hwmon_of_match);
|
||||
|
||||
static struct platform_driver gsc_hwmon_driver = {
|
||||
.driver = {
|
||||
|
@ -1188,24 +1188,6 @@ devm_hwmon_device_register_with_info(struct device *dev, const char *name,
|
||||
}
|
||||
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)
|
||||
{
|
||||
char *name, *p;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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)
|
||||
{
|
||||
const struct device_node *np = dev->of_node;
|
||||
struct device_node *child;
|
||||
int ret;
|
||||
|
||||
/* 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");
|
||||
|
||||
for_each_child_of_node(np, child) {
|
||||
for_each_child_of_node_scoped(np, child) {
|
||||
ret = ina3221_probe_child_from_dt(dev, child, ina);
|
||||
if (ret) {
|
||||
of_node_put(child);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -438,16 +438,21 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
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) == 0x70)) {
|
||||
data->read_htcreg = read_htcreg_nb_f15;
|
||||
data->read_tempreg = read_tempreg_nb_f15;
|
||||
} else if (boot_cpu_data.x86 == 0x17 || boot_cpu_data.x86 == 0x18) {
|
||||
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;
|
||||
}
|
||||
|
||||
if (boot_cpu_data.x86 == 0x17 || boot_cpu_data.x86 == 0x18) {
|
||||
switch (boot_cpu_data.x86_model) {
|
||||
case 0x1: /* Zen */
|
||||
case 0x8: /* Zen+ */
|
||||
@ -469,10 +474,6 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
break;
|
||||
}
|
||||
} 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) {
|
||||
case 0x0 ... 0x1: /* Zen3 SP3/TR */
|
||||
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);
|
||||
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++) {
|
||||
|
@ -2674,19 +2674,16 @@ static int lm90_parse_dt_channel_info(struct i2c_client *client,
|
||||
struct lm90_data *data)
|
||||
{
|
||||
int err;
|
||||
struct device_node *child;
|
||||
struct device *dev = &client->dev;
|
||||
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"))
|
||||
continue;
|
||||
|
||||
err = lm90_probe_channel_from_dt(client, child, data);
|
||||
if (err) {
|
||||
of_node_put(child);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -27,15 +27,14 @@
|
||||
* 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/hwmon.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.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,
|
||||
@ -43,8 +42,6 @@
|
||||
*/
|
||||
static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b,
|
||||
I2C_CLIENT_END };
|
||||
enum chips { lm92, max6635 };
|
||||
|
||||
/* The LM92 registers */
|
||||
#define LM92_REG_CONFIG 0x01 /* 8-bit, RW */
|
||||
#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;
|
||||
}
|
||||
|
||||
static inline s16 TEMP_TO_REG(long val)
|
||||
static inline s16 TEMP_TO_REG(long val, int resolution)
|
||||
{
|
||||
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 */
|
||||
@ -78,239 +75,336 @@ static inline u8 ALARMS_FROM_REG(s16 reg)
|
||||
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) */
|
||||
struct lm92_data {
|
||||
struct i2c_client *client;
|
||||
struct regmap *regmap;
|
||||
struct mutex update_lock;
|
||||
bool valid; /* false until following fields are valid */
|
||||
unsigned long last_updated; /* in jiffies */
|
||||
|
||||
/* registers values */
|
||||
s16 temp[t_num_regs]; /* index with enum temp_index */
|
||||
int resolution;
|
||||
};
|
||||
|
||||
/*
|
||||
* Sysfs attributes and callback functions
|
||||
*/
|
||||
|
||||
static struct lm92_data *lm92_update_device(struct device *dev)
|
||||
static int lm92_temp_read(struct lm92_data *data, u32 attr, int channel, long *val)
|
||||
{
|
||||
struct lm92_data *data = dev_get_drvdata(dev);
|
||||
struct i2c_client *client = data->client;
|
||||
int i;
|
||||
int reg = -1, hyst_reg = -1, alarm_bit = 0;
|
||||
struct regmap *regmap = data->regmap;
|
||||
u32 temp;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
if (time_after(jiffies, data->last_updated + HZ) ||
|
||||
!data->valid) {
|
||||
dev_dbg(&client->dev, "Updating lm92 data\n");
|
||||
for (i = 0; i < t_num_regs; i++) {
|
||||
data->temp[i] =
|
||||
i2c_smbus_read_word_swapped(client, regs[i]);
|
||||
}
|
||||
data->last_updated = jiffies;
|
||||
data->valid = true;
|
||||
switch (attr) {
|
||||
case hwmon_temp_input:
|
||||
reg = LM92_REG_TEMP;
|
||||
break;
|
||||
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_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);
|
||||
|
||||
return data;
|
||||
ret = regmap_multi_reg_read(regmap, regs, regvals, 2);
|
||||
if (ret)
|
||||
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,
|
||||
char *buf)
|
||||
static int lm92_chip_read(struct lm92_data *data, u32 attr, long *val)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct lm92_data *data = lm92_update_device(dev);
|
||||
u32 temp;
|
||||
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,
|
||||
struct device_attribute *devattr, const char *buf,
|
||||
size_t count)
|
||||
static int lm92_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
|
||||
int channel, long *val)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct 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);
|
||||
if (err)
|
||||
switch (type) {
|
||||
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;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->temp[nr] = TEMP_TO_REG(val);
|
||||
i2c_smbus_write_word_swapped(client, regs[nr], data->temp[nr]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
return regmap_write(regmap, reg, TEMP_TO_REG(val, data->resolution));
|
||||
}
|
||||
|
||||
static ssize_t temp_hyst_show(struct device *dev,
|
||||
struct device_attribute *devattr, char *buf)
|
||||
static int lm92_write(struct device *dev, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel, long val)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct 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 i2c_client *client = data->client;
|
||||
long val;
|
||||
int err;
|
||||
|
||||
err = kstrtol(buf, 10, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
val = clamp_val(val, -120000, 220000);
|
||||
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;
|
||||
switch (type) {
|
||||
case hwmon_temp:
|
||||
return lm92_temp_write(data, attr, val);
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t alarms_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
static umode_t lm92_is_visible(const void *_data, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel)
|
||||
{
|
||||
struct lm92_data *data = lm92_update_device(dev);
|
||||
|
||||
return sprintf(buf, "%d\n", ALARMS_FROM_REG(data->temp[t_input]));
|
||||
switch (type) {
|
||||
case hwmon_chip:
|
||||
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,
|
||||
char *buf)
|
||||
{
|
||||
int bitnr = to_sensor_dev_attr(attr)->index;
|
||||
struct lm92_data *data = lm92_update_device(dev);
|
||||
return sprintf(buf, "%d\n", (data->temp[t_input] >> bitnr) & 1);
|
||||
}
|
||||
static const struct hwmon_channel_info * const lm92_info[] = {
|
||||
HWMON_CHANNEL_INFO(chip, HWMON_C_ALARMS),
|
||||
HWMON_CHANNEL_INFO(temp,
|
||||
HWMON_T_INPUT |
|
||||
HWMON_T_MIN | HWMON_T_MIN_HYST |
|
||||
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 SENSOR_DEVICE_ATTR_RW(temp1_crit, temp, t_crit);
|
||||
static SENSOR_DEVICE_ATTR_RW(temp1_crit_hyst, temp_hyst, t_crit);
|
||||
static SENSOR_DEVICE_ATTR_RW(temp1_min, temp, t_min);
|
||||
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 DEVICE_ATTR_RO(alarms);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp1_crit_alarm, alarm, 2);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp1_min_alarm, alarm, 0);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, alarm, 1);
|
||||
static const struct hwmon_ops lm92_hwmon_ops = {
|
||||
.is_visible = lm92_is_visible,
|
||||
.read = lm92_read,
|
||||
.write = lm92_write,
|
||||
};
|
||||
|
||||
static const struct hwmon_chip_info lm92_chip_info = {
|
||||
.ops = &lm92_hwmon_ops,
|
||||
.info = lm92_info,
|
||||
};
|
||||
|
||||
/*
|
||||
* Detection and registration
|
||||
*/
|
||||
|
||||
static void lm92_init_client(struct i2c_client *client)
|
||||
static int lm92_init_client(struct regmap *regmap)
|
||||
{
|
||||
u8 config;
|
||||
|
||||
/* 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);
|
||||
return regmap_clear_bits(regmap, LM92_REG_CONFIG, 0x01);
|
||||
}
|
||||
|
||||
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 */
|
||||
static int lm92_detect(struct i2c_client *new_client,
|
||||
struct i2c_board_info *info)
|
||||
{
|
||||
struct i2c_adapter *adapter = new_client->adapter;
|
||||
u8 config;
|
||||
u16 man_id;
|
||||
u8 config_addr = LM92_REG_CONFIG;
|
||||
u8 man_id_addr = LM92_REG_MAN_ID;
|
||||
int i, regval;
|
||||
|
||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA
|
||||
| I2C_FUNC_SMBUS_WORD_DATA))
|
||||
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);
|
||||
|
||||
if ((config & 0xe0) == 0x00 && man_id == 0x0180)
|
||||
pr_info("lm92: Found National Semiconductor LM92 chip\n");
|
||||
else
|
||||
return -ENODEV;
|
||||
/*
|
||||
* Register values repeat with multiples of 8.
|
||||
* Read twice to improve detection accuracy.
|
||||
*/
|
||||
for (i = 0; i < 2; i++) {
|
||||
regval = i2c_smbus_read_word_data(new_client, man_id_addr);
|
||||
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);
|
||||
|
||||
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 lm92_data *data;
|
||||
struct regmap *regmap;
|
||||
int err;
|
||||
|
||||
data = devm_kzalloc(&new_client->dev, sizeof(struct lm92_data),
|
||||
GFP_KERNEL);
|
||||
regmap = devm_regmap_init(dev, &lm92_regmap_bus, client,
|
||||
&lm92_regmap_config);
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(struct lm92_data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->client = new_client;
|
||||
data->regmap = regmap;
|
||||
data->resolution = (unsigned long)i2c_get_match_data(client);
|
||||
mutex_init(&data->update_lock);
|
||||
|
||||
/* 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,
|
||||
new_client->name,
|
||||
data, lm92_groups);
|
||||
hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data,
|
||||
&lm92_chip_info, NULL);
|
||||
return PTR_ERR_OR_ZERO(hwmon_dev);
|
||||
}
|
||||
|
||||
@ -318,9 +412,10 @@ static int lm92_probe(struct i2c_client *new_client)
|
||||
* Module and driver stuff
|
||||
*/
|
||||
|
||||
/* .driver_data is limit register resolution */
|
||||
static const struct i2c_device_id lm92_id[] = {
|
||||
{ "lm92", lm92 },
|
||||
{ "max6635", max6635 },
|
||||
{ "lm92", 13 },
|
||||
{ "max6635", 9 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, lm92_id);
|
||||
|
@ -8,16 +8,15 @@
|
||||
* 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/hwmon.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/util_macros.h>
|
||||
|
||||
#define DRVNAME "lm95234"
|
||||
|
||||
@ -32,6 +31,8 @@ static const unsigned short normal_i2c[] = {
|
||||
#define LM95234_REG_STATUS 0x02
|
||||
#define LM95234_REG_CONFIG 0x03
|
||||
#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_TCRIT1 0x08
|
||||
#define LM95234_REG_STS_TCRIT2 0x09
|
||||
@ -52,541 +53,372 @@ static const unsigned short normal_i2c[] = {
|
||||
|
||||
/* Client data (each client gets its own) */
|
||||
struct lm95234_data {
|
||||
struct i2c_client *client;
|
||||
const struct attribute_group *groups[3];
|
||||
struct regmap *regmap;
|
||||
struct mutex update_lock;
|
||||
unsigned long last_updated, interval; /* in jiffies */
|
||||
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 */
|
||||
enum chips 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;
|
||||
u16 temp = 0;
|
||||
unsigned int regs[2];
|
||||
int temp = 0, ret;
|
||||
u8 regvals[2];
|
||||
|
||||
if (index) {
|
||||
val = i2c_smbus_read_byte_data(client,
|
||||
LM95234_REG_UTEMPH(index - 1));
|
||||
if (val < 0)
|
||||
return val;
|
||||
temp = val << 8;
|
||||
val = i2c_smbus_read_byte_data(client,
|
||||
LM95234_REG_UTEMPL(index - 1));
|
||||
if (val < 0)
|
||||
return val;
|
||||
temp |= val;
|
||||
*t = temp;
|
||||
regs[0] = LM95234_REG_UTEMPH(index - 1);
|
||||
regs[1] = LM95234_REG_UTEMPL(index - 1);
|
||||
ret = regmap_multi_reg_read(regmap, regs, regvals, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
temp = (regvals[0] << 8) | regvals[1];
|
||||
}
|
||||
/*
|
||||
* Read signed temperature if unsigned temperature is 0,
|
||||
* or if this is the local sensor.
|
||||
*/
|
||||
if (!temp) {
|
||||
val = i2c_smbus_read_byte_data(client,
|
||||
LM95234_REG_TEMPH(index));
|
||||
if (val < 0)
|
||||
return val;
|
||||
temp = val << 8;
|
||||
val = i2c_smbus_read_byte_data(client,
|
||||
LM95234_REG_TEMPL(index));
|
||||
if (val < 0)
|
||||
return val;
|
||||
temp |= val;
|
||||
*t = (s16)temp;
|
||||
regs[0] = LM95234_REG_TEMPH(index);
|
||||
regs[1] = LM95234_REG_TEMPL(index);
|
||||
ret = regmap_multi_reg_read(regmap, regs, regvals, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
temp = (regvals[0] << 8) | regvals[1];
|
||||
temp = sign_extend32(temp, 15);
|
||||
}
|
||||
*t = DIV_ROUND_CLOSEST(temp * 125, 32);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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, ®val);
|
||||
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), ®val);
|
||||
if (ret)
|
||||
return ret;
|
||||
*val = !!(regval & BIT(channel));
|
||||
break;
|
||||
case hwmon_temp_crit_alarm:
|
||||
ret = regmap_read(regmap, LM95234_REG_STS_TCRIT1, ®val);
|
||||
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, ®val);
|
||||
if (ret)
|
||||
return ret;
|
||||
*val = (regval & BIT(channel)) ? 1 : 2;
|
||||
break;
|
||||
case hwmon_temp_offset:
|
||||
ret = regmap_read(regmap, LM95234_REG_OFFSET(channel - 1), ®val);
|
||||
if (ret)
|
||||
return ret;
|
||||
*val = sign_extend32(regval, 7) * 500;
|
||||
break;
|
||||
case hwmon_temp_fault:
|
||||
ret = regmap_read(regmap, LM95234_REG_STS_FAULT, ®val);
|
||||
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), ®val);
|
||||
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), ®val);
|
||||
if (ret)
|
||||
return ret;
|
||||
*val = regval * 1000;
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u16 update_intervals[] = { 143, 364, 1000, 2500 };
|
||||
|
||||
/* Fill value cache. Must be called with update lock held. */
|
||||
|
||||
static int lm95234_fill_cache(struct lm95234_data *data,
|
||||
struct i2c_client *client)
|
||||
static int lm95234_chip_write(struct device *dev, u32 attr, long val)
|
||||
{
|
||||
int i, ret;
|
||||
struct lm95234_data *data = dev_get_drvdata(dev);
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, LM95234_REG_CONVRATE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
data->interval = msecs_to_jiffies(update_intervals[ret & 0x03]);
|
||||
|
||||
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;
|
||||
switch (attr) {
|
||||
case hwmon_chip_update_interval:
|
||||
val = find_closest(val, update_intervals, ARRAY_SIZE(update_intervals));
|
||||
return regmap_write(data->regmap, LM95234_REG_CONVRATE, val);
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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) ||
|
||||
!data->valid) {
|
||||
int i;
|
||||
|
||||
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;
|
||||
*val = update_intervals[convrate & 0x03];
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
ret = 0;
|
||||
abort:
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t temp_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
static int lm95234_write(struct device *dev, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel, long val)
|
||||
{
|
||||
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\n",
|
||||
DIV_ROUND_CLOSEST(data->temp[index] * 125, 32));
|
||||
switch (type) {
|
||||
case hwmon_chip:
|
||||
return lm95234_chip_write(dev, attr, val);
|
||||
case hwmon_temp:
|
||||
return lm95234_temp_write(dev, attr, channel, val);
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t alarm_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
static int lm95234_read(struct device *dev, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel, long *val)
|
||||
{
|
||||
struct lm95234_data *data = dev_get_drvdata(dev);
|
||||
u32 mask = to_sensor_dev_attr(attr)->index;
|
||||
int ret = lm95234_update_device(data);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return sprintf(buf, "%u", !!(data->status & mask));
|
||||
switch (type) {
|
||||
case hwmon_chip:
|
||||
return lm95234_chip_read(dev, attr, val);
|
||||
case hwmon_temp:
|
||||
return lm95234_temp_read(dev, attr, channel, val);
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t type_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
static umode_t lm95234_is_visible(const void *_data, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel)
|
||||
{
|
||||
struct lm95234_data *data = dev_get_drvdata(dev);
|
||||
u8 mask = to_sensor_dev_attr(attr)->index;
|
||||
int ret = lm95234_update_device(data);
|
||||
const struct lm95234_data *data = _data;
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
if (data->type == lm95233 && channel > 2)
|
||||
return 0;
|
||||
|
||||
return sprintf(buf, data->sensor_type & mask ? "1\n" : "2\n");
|
||||
}
|
||||
|
||||
static ssize_t type_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
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])
|
||||
switch (type) {
|
||||
case hwmon_chip:
|
||||
switch (attr) {
|
||||
case hwmon_chip_update_interval:
|
||||
return 0644;
|
||||
default:
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, 0);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp2_input, temp, 1);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp3_input, temp, 2);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp4_input, temp, 3);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp5_input, temp, 4);
|
||||
|
||||
static SENSOR_DEVICE_ATTR_RO(temp2_fault, alarm, BIT(0) | BIT(1));
|
||||
static SENSOR_DEVICE_ATTR_RO(temp3_fault, alarm, BIT(2) | BIT(3));
|
||||
static SENSOR_DEVICE_ATTR_RO(temp4_fault, alarm, BIT(4) | BIT(5));
|
||||
static SENSOR_DEVICE_ATTR_RO(temp5_fault, alarm, BIT(6) | BIT(7));
|
||||
|
||||
static SENSOR_DEVICE_ATTR_RW(temp2_type, type, BIT(1));
|
||||
static SENSOR_DEVICE_ATTR_RW(temp3_type, type, BIT(2));
|
||||
static SENSOR_DEVICE_ATTR_RW(temp4_type, type, BIT(3));
|
||||
static SENSOR_DEVICE_ATTR_RW(temp5_type, type, BIT(4));
|
||||
|
||||
static SENSOR_DEVICE_ATTR_RW(temp1_max, tcrit1, 0);
|
||||
static SENSOR_DEVICE_ATTR_RW(temp2_max, tcrit2, 0);
|
||||
static SENSOR_DEVICE_ATTR_RW(temp3_max, tcrit2, 1);
|
||||
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,
|
||||
static const struct hwmon_channel_info * const lm95234_info[] = {
|
||||
HWMON_CHANNEL_INFO(chip, HWMON_C_UPDATE_INTERVAL),
|
||||
HWMON_CHANNEL_INFO(temp,
|
||||
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
|
||||
HWMON_T_MAX_ALARM | HWMON_T_ENABLE,
|
||||
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
|
||||
HWMON_T_MAX_ALARM | HWMON_T_FAULT | HWMON_T_TYPE |
|
||||
HWMON_T_CRIT | HWMON_T_CRIT_HYST |
|
||||
HWMON_T_CRIT_ALARM | HWMON_T_OFFSET | HWMON_T_ENABLE,
|
||||
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
|
||||
HWMON_T_MAX_ALARM | HWMON_T_FAULT | HWMON_T_TYPE |
|
||||
HWMON_T_CRIT | HWMON_T_CRIT_HYST |
|
||||
HWMON_T_CRIT_ALARM | HWMON_T_OFFSET | HWMON_T_ENABLE,
|
||||
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
|
||||
HWMON_T_MAX_ALARM | HWMON_T_FAULT | HWMON_T_TYPE |
|
||||
HWMON_T_OFFSET | HWMON_T_ENABLE,
|
||||
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
|
||||
HWMON_T_MAX_ALARM | HWMON_T_FAULT | HWMON_T_TYPE |
|
||||
HWMON_T_OFFSET | HWMON_T_ENABLE),
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group lm95234_common_group = {
|
||||
.attrs = lm95234_common_attrs,
|
||||
static const struct hwmon_ops lm95234_hwmon_ops = {
|
||||
.is_visible = lm95234_is_visible,
|
||||
.read = lm95234_read,
|
||||
.write = lm95234_write,
|
||||
};
|
||||
|
||||
static struct attribute *lm95234_attrs[] = {
|
||||
&sensor_dev_attr_temp4_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp5_input.dev_attr.attr,
|
||||
&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 hwmon_chip_info lm95234_chip_info = {
|
||||
.ops = &lm95234_hwmon_ops,
|
||||
.info = lm95234_info,
|
||||
};
|
||||
|
||||
static const struct attribute_group lm95234_group = {
|
||||
.attrs = lm95234_attrs,
|
||||
static bool lm95234_volatile_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
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,
|
||||
@ -649,61 +481,60 @@ static int lm95234_detect(struct i2c_client *client,
|
||||
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 */
|
||||
val = i2c_smbus_read_byte_data(client, LM95234_REG_CONFIG);
|
||||
if (val < 0)
|
||||
return val;
|
||||
if (val & 0x40)
|
||||
i2c_smbus_write_byte_data(client, LM95234_REG_CONFIG,
|
||||
val & ~0x40);
|
||||
ret = regmap_clear_bits(regmap, LM95234_REG_CONFIG, 0x40);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* If diode type status reports an error, try to fix it */
|
||||
val = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL_STS);
|
||||
if (val < 0)
|
||||
return val;
|
||||
model = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL);
|
||||
if (model < 0)
|
||||
return model;
|
||||
ret = regmap_read(regmap, LM95234_REG_REM_MODEL_STS, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = regmap_read(regmap, LM95234_REG_REM_MODEL, &model);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (model & val) {
|
||||
dev_notice(&client->dev,
|
||||
dev_notice(dev,
|
||||
"Fixing remote diode type misconfiguration (0x%x)\n",
|
||||
val);
|
||||
i2c_smbus_write_byte_data(client, LM95234_REG_REM_MODEL,
|
||||
model & ~val);
|
||||
ret = regmap_write(regmap, LM95234_REG_REM_MODEL, model & ~val);
|
||||
}
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lm95234_probe(struct i2c_client *client)
|
||||
{
|
||||
enum chips type = (uintptr_t)i2c_get_match_data(client);
|
||||
struct device *dev = &client->dev;
|
||||
struct lm95234_data *data;
|
||||
struct device *hwmon_dev;
|
||||
struct regmap *regmap;
|
||||
int err;
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(struct lm95234_data), GFP_KERNEL);
|
||||
if (!data)
|
||||
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);
|
||||
|
||||
/* Initialize the LM95234 chip */
|
||||
err = lm95234_init_client(client);
|
||||
err = lm95234_init_client(dev, regmap);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
data->groups[0] = &lm95234_common_group;
|
||||
if (type == lm95234)
|
||||
data->groups[1] = &lm95234_group;
|
||||
|
||||
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
|
||||
data, data->groups);
|
||||
hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
|
||||
data, &lm95234_chip_info, NULL);
|
||||
return PTR_ERR_OR_ZERO(hwmon_dev);
|
||||
}
|
||||
|
||||
|
@ -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 regmap *regmap = data->regmap;
|
||||
int ret, regl, regh, regvall, regvalh;
|
||||
unsigned int regs[2];
|
||||
unsigned int regval;
|
||||
u8 regvals[2];
|
||||
int ret;
|
||||
|
||||
switch (attr) {
|
||||
case hwmon_temp_input:
|
||||
regl = channel ? LM95245_REG_R_REMOTE_TEMPL_S :
|
||||
LM95245_REG_R_LOCAL_TEMPL_S;
|
||||
regh = channel ? LM95245_REG_R_REMOTE_TEMPH_S :
|
||||
LM95245_REG_R_LOCAL_TEMPH_S;
|
||||
ret = regmap_read(regmap, regl, ®vall);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = regmap_read(regmap, regh, ®valh);
|
||||
regs[0] = channel ? LM95245_REG_R_REMOTE_TEMPL_S :
|
||||
LM95245_REG_R_LOCAL_TEMPL_S;
|
||||
regs[1] = channel ? LM95245_REG_R_REMOTE_TEMPH_S :
|
||||
LM95245_REG_R_LOCAL_TEMPH_S;
|
||||
ret = regmap_multi_reg_read(regmap, regs, regvals, 2);
|
||||
if (ret < 0)
|
||||
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
|
||||
* or if reported temperature is below signed limit.
|
||||
*/
|
||||
if (!channel || (regvalh & 0x80) || regvalh < 0x7f) {
|
||||
*val = temp_from_reg_signed(regvalh, regvall);
|
||||
if (!channel || (regvals[1] & 0x80) || regvals[1] < 0x7f) {
|
||||
*val = temp_from_reg_signed(regvals[1], regvals[0]);
|
||||
return 0;
|
||||
}
|
||||
ret = regmap_read(regmap, LM95245_REG_R_REMOTE_TEMPL_U,
|
||||
®vall);
|
||||
if (ret < 0)
|
||||
ret = regmap_bulk_read(regmap, LM95245_REG_R_REMOTE_TEMPH_U, regvals, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = regmap_read(regmap, LM95245_REG_R_REMOTE_TEMPH_U,
|
||||
®valh);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = temp_from_reg_unsigned(regvalh, regvall);
|
||||
*val = temp_from_reg_unsigned(regvals[0], regvals[1]);
|
||||
return 0;
|
||||
case hwmon_temp_max:
|
||||
ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OS_LIMIT,
|
||||
®valh);
|
||||
®val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = regvalh * 1000;
|
||||
*val = regval * 1000;
|
||||
return 0;
|
||||
case hwmon_temp_crit:
|
||||
regh = channel ? LM95245_REG_RW_REMOTE_TCRIT_LIMIT :
|
||||
LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT;
|
||||
ret = regmap_read(regmap, regh, ®valh);
|
||||
regs[0] = channel ? LM95245_REG_RW_REMOTE_TCRIT_LIMIT :
|
||||
LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT;
|
||||
ret = regmap_read(regmap, regs[0], ®val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = regvalh * 1000;
|
||||
*val = regval * 1000;
|
||||
return 0;
|
||||
case hwmon_temp_max_hyst:
|
||||
ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OS_LIMIT,
|
||||
®valh);
|
||||
regs[0] = LM95245_REG_RW_REMOTE_OS_LIMIT;
|
||||
regs[1] = LM95245_REG_RW_COMMON_HYSTERESIS;
|
||||
ret = regmap_multi_reg_read(regmap, regs, regvals, 2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = regmap_read(regmap, LM95245_REG_RW_COMMON_HYSTERESIS,
|
||||
®vall);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = (regvalh - regvall) * 1000;
|
||||
*val = (regvals[0] - regvals[1]) * 1000;
|
||||
return 0;
|
||||
case hwmon_temp_crit_hyst:
|
||||
regh = channel ? LM95245_REG_RW_REMOTE_TCRIT_LIMIT :
|
||||
LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT;
|
||||
ret = regmap_read(regmap, regh, ®valh);
|
||||
regs[0] = channel ? LM95245_REG_RW_REMOTE_TCRIT_LIMIT :
|
||||
LM95245_REG_RW_LOCAL_OS_TCRIT_LIMIT;
|
||||
regs[1] = LM95245_REG_RW_COMMON_HYSTERESIS;
|
||||
|
||||
ret = regmap_multi_reg_read(regmap, regs, regvals, 2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = regmap_read(regmap, LM95245_REG_RW_COMMON_HYSTERESIS,
|
||||
®vall);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = (regvalh - regvall) * 1000;
|
||||
*val = (regvals[0] - regvals[1]) * 1000;
|
||||
return 0;
|
||||
case hwmon_temp_type:
|
||||
ret = regmap_read(regmap, LM95245_REG_RW_CONFIG2, ®valh);
|
||||
ret = regmap_read(regmap, LM95245_REG_RW_CONFIG2, ®val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = (regvalh & CFG2_REMOTE_TT) ? 1 : 2;
|
||||
*val = (regval & CFG2_REMOTE_TT) ? 1 : 2;
|
||||
return 0;
|
||||
case hwmon_temp_offset:
|
||||
ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OFFL,
|
||||
®vall);
|
||||
ret = regmap_bulk_read(regmap, LM95245_REG_RW_REMOTE_OFFH, regvals, 2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = regmap_read(regmap, LM95245_REG_RW_REMOTE_OFFH,
|
||||
®valh);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = temp_from_reg_signed(regvalh, regvall);
|
||||
*val = temp_from_reg_signed(regvals[0], regvals[1]);
|
||||
return 0;
|
||||
case hwmon_temp_max_alarm:
|
||||
ret = regmap_read(regmap, LM95245_REG_R_STATUS1, ®valh);
|
||||
ret = regmap_read(regmap, LM95245_REG_R_STATUS1, ®val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = !!(regvalh & STATUS1_ROS);
|
||||
*val = !!(regval & STATUS1_ROS);
|
||||
return 0;
|
||||
case hwmon_temp_crit_alarm:
|
||||
ret = regmap_read(regmap, LM95245_REG_R_STATUS1, ®valh);
|
||||
ret = regmap_read(regmap, LM95245_REG_R_STATUS1, ®val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = !!(regvalh & (channel ? STATUS1_RTCRIT : STATUS1_LOC));
|
||||
*val = !!(regval & (channel ? STATUS1_RTCRIT : STATUS1_LOC));
|
||||
return 0;
|
||||
case hwmon_temp_fault:
|
||||
ret = regmap_read(regmap, LM95245_REG_R_STATUS1, ®valh);
|
||||
ret = regmap_read(regmap, LM95245_REG_R_STATUS1, ®val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = !!(regvalh & STATUS1_DIODE_FAULT);
|
||||
*val = !!(regval & STATUS1_DIODE_FAULT);
|
||||
return 0;
|
||||
default:
|
||||
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 regmap *regmap = data->regmap;
|
||||
unsigned int regval;
|
||||
u8 regvals[2];
|
||||
int ret, reg;
|
||||
|
||||
switch (attr) {
|
||||
@ -311,16 +297,10 @@ static int lm95245_write_temp(struct device *dev, u32 attr, int channel,
|
||||
case hwmon_temp_offset:
|
||||
val = clamp_val(val, -128000, 127875);
|
||||
val = val * 256 / 1000;
|
||||
mutex_lock(&data->update_lock);
|
||||
ret = regmap_write(regmap, LM95245_REG_RW_REMOTE_OFFL,
|
||||
val & 0xe0);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&data->update_lock);
|
||||
return ret;
|
||||
}
|
||||
ret = regmap_write(regmap, LM95245_REG_RW_REMOTE_OFFH,
|
||||
(val >> 8) & 0xff);
|
||||
mutex_unlock(&data->update_lock);
|
||||
regvals[0] = val >> 8;
|
||||
regvals[1] = val & 0xe0;
|
||||
|
||||
ret = regmap_bulk_write(regmap, LM95245_REG_RW_REMOTE_OFFH, regvals, 2);
|
||||
return ret;
|
||||
case hwmon_temp_type:
|
||||
if (val != 1 && val != 2)
|
||||
|
@ -11,7 +11,8 @@
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/hwmon-sysfs.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include "ltc2947.h"
|
||||
@ -1034,9 +1035,8 @@ static int ltc2947_setup(struct ltc2947_data *st)
|
||||
/* 19.89E-6 * 10E9 */
|
||||
st->lsb_energy = 19890;
|
||||
}
|
||||
ret = of_property_read_u32_array(st->dev->of_node,
|
||||
"adi,accumulator-ctl-pol", accum,
|
||||
ARRAY_SIZE(accum));
|
||||
ret = device_property_read_u32_array(st->dev, "adi,accumulator-ctl-pol",
|
||||
accum, ARRAY_SIZE(accum));
|
||||
if (!ret) {
|
||||
u32 accum_reg = LTC2947_ACCUM_POL_1(accum[0]) |
|
||||
LTC2947_ACCUM_POL_2(accum[1]);
|
||||
@ -1045,9 +1045,9 @@ static int ltc2947_setup(struct ltc2947_data *st)
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
ret = of_property_read_u32(st->dev->of_node,
|
||||
"adi,accumulation-deadband-microamp",
|
||||
&deadband);
|
||||
ret = device_property_read_u32(st->dev,
|
||||
"adi,accumulation-deadband-microamp",
|
||||
&deadband);
|
||||
if (!ret) {
|
||||
/* the LSB is the same as the current, so 3mA */
|
||||
ret = regmap_write(st->map, LTC2947_REG_ACCUM_DEADBAND,
|
||||
@ -1056,7 +1056,7 @@ static int ltc2947_setup(struct ltc2947_data *st)
|
||||
return ret;
|
||||
}
|
||||
/* 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) {
|
||||
/* setup GPIO as output */
|
||||
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)
|
||||
return ret;
|
||||
}
|
||||
ret = of_property_read_u32_array(st->dev->of_node, "adi,gpio-in-accum",
|
||||
accum, ARRAY_SIZE(accum));
|
||||
ret = device_property_read_u32_array(st->dev, "adi,gpio-in-accum",
|
||||
accum, ARRAY_SIZE(accum));
|
||||
if (!ret) {
|
||||
/*
|
||||
* Setup the accum options. The gpioctl is already defined as
|
||||
|
@ -854,33 +854,24 @@ static const struct regmap_config ltc2992_regmap_config = {
|
||||
|
||||
static int ltc2992_parse_dt(struct ltc2992_state *st)
|
||||
{
|
||||
struct fwnode_handle *fwnode;
|
||||
struct fwnode_handle *child;
|
||||
u32 addr;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
fwnode = dev_fwnode(&st->client->dev);
|
||||
|
||||
fwnode_for_each_available_child_node(fwnode, child) {
|
||||
device_for_each_child_node_scoped(&st->client->dev, child) {
|
||||
ret = fwnode_property_read_u32(child, "reg", &addr);
|
||||
if (ret < 0) {
|
||||
fwnode_handle_put(child);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (addr > 1) {
|
||||
fwnode_handle_put(child);
|
||||
if (addr > 1)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = fwnode_property_read_u32(child, "shunt-resistor-micro-ohms", &val);
|
||||
if (!ret) {
|
||||
if (!val) {
|
||||
fwnode_handle_put(child);
|
||||
if (!val)
|
||||
return dev_err_probe(&st->client->dev, -EINVAL,
|
||||
"shunt resistor value cannot be zero\n");
|
||||
}
|
||||
|
||||
st->r_sense_uohm[addr] = val;
|
||||
}
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ static const bool max16065_have_current[] = {
|
||||
};
|
||||
|
||||
struct max16065_data {
|
||||
enum chips type;
|
||||
enum chips chip;
|
||||
struct i2c_client *client;
|
||||
const struct attribute_group *groups[4];
|
||||
struct mutex update_lock;
|
||||
@ -114,9 +114,10 @@ static inline int LIMIT_TO_MV(int limit, int range)
|
||||
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)
|
||||
@ -161,10 +162,17 @@ static struct max16065_data *max16065_update_device(struct device *dev)
|
||||
MAX16065_CURR_SENSE);
|
||||
}
|
||||
|
||||
for (i = 0; i < DIV_ROUND_UP(data->num_adc, 8); i++)
|
||||
for (i = 0; i < 2; i++)
|
||||
data->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->valid = true;
|
||||
}
|
||||
@ -513,6 +521,7 @@ static int max16065_probe(struct i2c_client *client)
|
||||
if (unlikely(!data))
|
||||
return -ENOMEM;
|
||||
|
||||
data->chip = chip;
|
||||
data->client = client;
|
||||
mutex_init(&data->update_lock);
|
||||
|
||||
|
@ -12,275 +12,356 @@
|
||||
* 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/mutex.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/hwmon.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[] = {
|
||||
0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END };
|
||||
|
||||
/*
|
||||
* The MAX1619 registers
|
||||
*/
|
||||
#define MAX1619_REG_LOCAL_TEMP 0x00
|
||||
#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
|
||||
#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)
|
||||
static int get_alarms(struct regmap *regmap)
|
||||
{
|
||||
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 {
|
||||
t_input1 = 0,
|
||||
t_input2,
|
||||
t_low2,
|
||||
t_high2,
|
||||
t_crit2,
|
||||
t_hyst2,
|
||||
t_num_regs
|
||||
};
|
||||
|
||||
/*
|
||||
* Client data (each client gets its own)
|
||||
*/
|
||||
|
||||
struct max1619_data {
|
||||
struct i2c_client *client;
|
||||
struct mutex update_lock;
|
||||
bool valid; /* false until following fields are valid */
|
||||
unsigned long last_updated; /* in jiffies */
|
||||
|
||||
/* registers values */
|
||||
u8 temp[t_num_regs]; /* index with enum temp_index */
|
||||
u8 alarms;
|
||||
};
|
||||
|
||||
static const u8 regs_read[t_num_regs] = {
|
||||
[t_input1] = MAX1619_REG_R_LOCAL_TEMP,
|
||||
[t_input2] = MAX1619_REG_R_REMOTE_TEMP,
|
||||
[t_low2] = MAX1619_REG_R_REMOTE_LOW,
|
||||
[t_high2] = MAX1619_REG_R_REMOTE_HIGH,
|
||||
[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;
|
||||
switch (attr) {
|
||||
case hwmon_temp_input:
|
||||
reg = channel ? MAX1619_REG_REMOTE_TEMP : MAX1619_REG_LOCAL_TEMP;
|
||||
break;
|
||||
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;
|
||||
case hwmon_temp_min_alarm:
|
||||
alarm_bit = 3;
|
||||
break;
|
||||
case hwmon_temp_max_alarm:
|
||||
alarm_bit = 4;
|
||||
break;
|
||||
case hwmon_temp_crit_alarm:
|
||||
alarm_bit = 1;
|
||||
break;
|
||||
case hwmon_temp_fault:
|
||||
alarm_bit = 2;
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return data;
|
||||
if (reg >= 0) {
|
||||
ret = regmap_read(regmap, reg, &temp);
|
||||
if (ret < 0)
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sysfs stuff
|
||||
*/
|
||||
static u16 update_intervals[] = { 16000, 8000, 4000, 2000, 1000, 500, 250, 125 };
|
||||
|
||||
static ssize_t temp_show(struct device *dev, struct device_attribute *devattr,
|
||||
char *buf)
|
||||
static int max1619_chip_read(struct regmap *regmap, u32 attr, long *val)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct max1619_data *data = max1619_update_device(dev);
|
||||
int alarms, ret;
|
||||
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, ®val);
|
||||
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,
|
||||
struct device_attribute *devattr, const char *buf,
|
||||
size_t count)
|
||||
static int max1619_read(struct device *dev, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel, long *val)
|
||||
{
|
||||
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
|
||||
struct 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;
|
||||
struct regmap *regmap = dev_get_drvdata(dev);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->temp[attr->index] = temp_to_reg(val);
|
||||
i2c_smbus_write_byte_data(client, regs_write[attr->index],
|
||||
data->temp[attr->index]);
|
||||
mutex_unlock(&data->update_lock);
|
||||
return count;
|
||||
switch (type) {
|
||||
case hwmon_chip:
|
||||
return max1619_chip_read(regmap, attr, val);
|
||||
case hwmon_temp:
|
||||
return max1619_temp_read(regmap, attr, channel, val);
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t alarms_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
static int max1619_chip_write(struct regmap *regmap, u32 attr, long val)
|
||||
{
|
||||
struct max1619_data *data = max1619_update_device(dev);
|
||||
return sprintf(buf, "%d\n", data->alarms);
|
||||
switch (attr) {
|
||||
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,
|
||||
char *buf)
|
||||
static int max1619_temp_write(struct regmap *regmap,
|
||||
u32 attr, int channel, long val)
|
||||
{
|
||||
int bitnr = to_sensor_dev_attr(attr)->index;
|
||||
struct max1619_data *data = max1619_update_device(dev);
|
||||
return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1);
|
||||
int reg;
|
||||
|
||||
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 SENSOR_DEVICE_ATTR_RO(temp2_input, temp, t_input2);
|
||||
static SENSOR_DEVICE_ATTR_RW(temp2_min, temp, t_low2);
|
||||
static SENSOR_DEVICE_ATTR_RW(temp2_max, temp, t_high2);
|
||||
static SENSOR_DEVICE_ATTR_RW(temp2_crit, temp, t_crit2);
|
||||
static SENSOR_DEVICE_ATTR_RW(temp2_crit_hyst, temp, t_hyst2);
|
||||
static int max1619_write(struct device *dev, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel, long val)
|
||||
{
|
||||
struct regmap *regmap = dev_get_drvdata(dev);
|
||||
|
||||
static DEVICE_ATTR_RO(alarms);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp2_crit_alarm, alarm, 1);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp2_fault, alarm, 2);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp2_min_alarm, alarm, 3);
|
||||
static SENSOR_DEVICE_ATTR_RO(temp2_max_alarm, alarm, 4);
|
||||
switch (type) {
|
||||
case hwmon_chip:
|
||||
return max1619_chip_write(regmap, attr, val);
|
||||
case hwmon_temp:
|
||||
return max1619_temp_write(regmap, attr, channel, val);
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
static struct attribute *max1619_attrs[] = {
|
||||
&sensor_dev_attr_temp1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_min.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_crit.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_crit_hyst.dev_attr.attr,
|
||||
static umode_t max1619_is_visible(const void *_data, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel)
|
||||
{
|
||||
switch (type) {
|
||||
case hwmon_chip:
|
||||
switch (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,
|
||||
&sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_fault.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
|
||||
static const struct hwmon_channel_info * const max1619_info[] = {
|
||||
HWMON_CHANNEL_INFO(chip, HWMON_C_ALARMS | HWMON_C_UPDATE_INTERVAL),
|
||||
HWMON_CHANNEL_INFO(temp,
|
||||
HWMON_T_INPUT,
|
||||
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
|
||||
};
|
||||
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 */
|
||||
static int max1619_detect(struct i2c_client *client,
|
||||
struct i2c_board_info *info)
|
||||
{
|
||||
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))
|
||||
return -ENODEV;
|
||||
|
||||
/* detection */
|
||||
reg_config = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONFIG);
|
||||
reg_convrate = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONVRATE);
|
||||
reg_status = i2c_smbus_read_byte_data(client, MAX1619_REG_R_STATUS);
|
||||
if ((reg_config & 0x03) != 0x00
|
||||
|| reg_convrate > 0x07 || (reg_status & 0x61) != 0x00) {
|
||||
dev_dbg(&adapter->dev, "MAX1619 detection failed at 0x%02x\n",
|
||||
client->addr);
|
||||
regval = i2c_smbus_read_byte_data(client, MAX1619_REG_CONFIG);
|
||||
if (regval < 0 || (regval & 0x03))
|
||||
return -ENODEV;
|
||||
regval = i2c_smbus_read_byte_data(client, MAX1619_REG_CONVRATE);
|
||||
if (regval < 0 || regval > 0x07)
|
||||
return -ENODEV;
|
||||
regval = i2c_smbus_read_byte_data(client, MAX1619_REG_STATUS);
|
||||
if (regval < 0 || (regval & 0x61))
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* identification */
|
||||
man_id = i2c_smbus_read_byte_data(client, MAX1619_REG_R_MAN_ID);
|
||||
chip_id = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CHIP_ID);
|
||||
if (man_id != 0x4D || chip_id != 0x04) {
|
||||
dev_info(&adapter->dev,
|
||||
"Unsupported chip (man_id=0x%02X, chip_id=0x%02X).\n",
|
||||
man_id, chip_id);
|
||||
regval = i2c_smbus_read_byte_data(client, MAX1619_REG_MAN_ID);
|
||||
if (regval != 0x4d)
|
||||
return -ENODEV;
|
||||
regval = i2c_smbus_read_byte_data(client, MAX1619_REG_CHIP_ID);
|
||||
if (regval != 0x04)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
strscpy(info->type, "max1619", I2C_NAME_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void max1619_init_client(struct i2c_client *client)
|
||||
static int max1619_init_chip(struct regmap *regmap)
|
||||
{
|
||||
u8 config;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Start the conversions.
|
||||
*/
|
||||
i2c_smbus_write_byte_data(client, MAX1619_REG_W_CONVRATE,
|
||||
5); /* 2 Hz */
|
||||
config = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONFIG);
|
||||
if (config & 0x40)
|
||||
i2c_smbus_write_byte_data(client, MAX1619_REG_W_CONFIG,
|
||||
config & 0xBF); /* run */
|
||||
ret = regmap_write(regmap, MAX1619_REG_CONVRATE, 5); /* 2 Hz */
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Start conversions */
|
||||
return regmap_clear_bits(regmap, MAX1619_REG_CONFIG, 0x40);
|
||||
}
|
||||
|
||||
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 regmap *regmap;
|
||||
int ret;
|
||||
|
||||
data = devm_kzalloc(&new_client->dev, sizeof(struct max1619_data),
|
||||
GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
regmap = devm_regmap_init(dev, &max1619_regmap_bus, client,
|
||||
&max1619_regmap_config);
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
data->client = new_client;
|
||||
mutex_init(&data->update_lock);
|
||||
ret = max1619_init_chip(regmap);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Initialize the MAX1619 chip */
|
||||
max1619_init_client(new_client);
|
||||
|
||||
hwmon_dev = devm_hwmon_device_register_with_groups(&new_client->dev,
|
||||
new_client->name,
|
||||
data,
|
||||
max1619_groups);
|
||||
hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
|
||||
regmap, &max1619_chip_info, NULL);
|
||||
return PTR_ERR_OR_ZERO(hwmon_dev);
|
||||
}
|
||||
|
||||
|
@ -6,15 +6,15 @@
|
||||
* some credit to Christoph Scheurer, but largely a rewrite
|
||||
*/
|
||||
|
||||
#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/bitops.h>
|
||||
#include <linux/bits.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 */
|
||||
static const unsigned short max1668_addr_list[] = {
|
||||
@ -30,14 +30,10 @@ static const unsigned short max1668_addr_list[] = {
|
||||
|
||||
/* limits */
|
||||
|
||||
/* write high limits */
|
||||
#define MAX1668_REG_LIMH_WR(nr) (0x13 + 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))
|
||||
/* high limits */
|
||||
#define MAX1668_REG_LIMH(nr) (0x08 + 2 * (nr))
|
||||
/* 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 */
|
||||
#define MAN_ID_MAXIM 0x4d
|
||||
@ -50,309 +46,146 @@ static bool read_only;
|
||||
module_param(read_only, bool, 0);
|
||||
MODULE_PARM_DESC(read_only, "Don't set any values, read only mode");
|
||||
|
||||
enum chips { max1668, max1805, max1989 };
|
||||
|
||||
struct max1668_data {
|
||||
struct i2c_client *client;
|
||||
const struct attribute_group *groups[3];
|
||||
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;
|
||||
struct regmap *regmap;
|
||||
int channels;
|
||||
};
|
||||
|
||||
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 i2c_client *client = data->client;
|
||||
struct max1668_data *ret = data;
|
||||
s32 val;
|
||||
int i;
|
||||
|
||||
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;
|
||||
struct regmap *regmap = data->regmap;
|
||||
u32 regs[2] = { MAX1668_REG_STAT1, MAX1668_REG_TEMP(channel) };
|
||||
u8 regvals[2];
|
||||
u32 regval;
|
||||
int ret;
|
||||
|
||||
ret = kstrtol(buf, 10, &temp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->temp_max[index] = clamp_val(temp/1000, -128, 127);
|
||||
ret = i2c_smbus_write_byte_data(client,
|
||||
MAX1668_REG_LIMH_WR(index),
|
||||
data->temp_max[index]);
|
||||
if (ret < 0)
|
||||
count = ret;
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return count;
|
||||
switch (attr) {
|
||||
case hwmon_temp_input:
|
||||
ret = regmap_read(regmap, MAX1668_REG_TEMP(channel), ®val);
|
||||
if (ret)
|
||||
return ret;
|
||||
*val = sign_extend32(regval, 7) * 1000;
|
||||
break;
|
||||
case hwmon_temp_min:
|
||||
ret = regmap_read(regmap, MAX1668_REG_LIML(channel), ®val);
|
||||
if (ret)
|
||||
return ret;
|
||||
*val = sign_extend32(regval, 7) * 1000;
|
||||
break;
|
||||
case hwmon_temp_max:
|
||||
ret = regmap_read(regmap, MAX1668_REG_LIMH(channel), ®val);
|
||||
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,
|
||||
®val);
|
||||
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,
|
||||
®val);
|
||||
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,
|
||||
struct device_attribute *devattr,
|
||||
const char *buf, size_t count)
|
||||
static int max1668_write(struct device *dev, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel, long val)
|
||||
{
|
||||
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;
|
||||
struct regmap *regmap = data->regmap;
|
||||
|
||||
ret = kstrtol(buf, 10, &temp);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
val = clamp_val(val / 1000, -128, 127);
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
data->temp_min[index] = clamp_val(temp/1000, -128, 127);
|
||||
ret = i2c_smbus_write_byte_data(client,
|
||||
MAX1668_REG_LIML_WR(index),
|
||||
data->temp_min[index]);
|
||||
if (ret < 0)
|
||||
count = ret;
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
return count;
|
||||
switch (attr) {
|
||||
case hwmon_temp_min:
|
||||
return regmap_write(regmap, MAX1668_REG_LIML(channel), val);
|
||||
case hwmon_temp_max:
|
||||
return regmap_write(regmap, MAX1668_REG_LIMH(channel), val);
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
|
||||
static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp_max,
|
||||
set_temp_max, 0);
|
||||
static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO, show_temp_min,
|
||||
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 umode_t max1668_is_visible(const void *_data, enum hwmon_sensor_types type,
|
||||
u32 attr, int channel)
|
||||
{
|
||||
const struct max1668_data *data = _data;
|
||||
|
||||
static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 14);
|
||||
static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL, 13);
|
||||
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);
|
||||
if (channel >= data->channels)
|
||||
return 0;
|
||||
|
||||
static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_fault, NULL, 1);
|
||||
static SENSOR_DEVICE_ATTR(temp3_fault, S_IRUGO, show_fault, NULL, 2);
|
||||
static SENSOR_DEVICE_ATTR(temp4_fault, S_IRUGO, show_fault, NULL, 3);
|
||||
static SENSOR_DEVICE_ATTR(temp5_fault, S_IRUGO, show_fault, NULL, 4);
|
||||
switch (attr) {
|
||||
case hwmon_temp_min:
|
||||
case hwmon_temp_max:
|
||||
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 struct attribute *max1668_attribute_common[] = {
|
||||
&sensor_dev_attr_temp1_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_min.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_min.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_input.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_min.dev_attr.attr,
|
||||
&sensor_dev_attr_temp3_input.dev_attr.attr,
|
||||
|
||||
&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
|
||||
&sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
|
||||
&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,
|
||||
static const struct hwmon_channel_info * const max1668_info[] = {
|
||||
HWMON_CHANNEL_INFO(temp,
|
||||
HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
|
||||
HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM,
|
||||
HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
|
||||
HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM |
|
||||
HWMON_T_FAULT,
|
||||
HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
|
||||
HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM |
|
||||
HWMON_T_FAULT,
|
||||
HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
|
||||
HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM |
|
||||
HWMON_T_FAULT,
|
||||
HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
|
||||
HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM |
|
||||
HWMON_T_FAULT),
|
||||
NULL
|
||||
};
|
||||
|
||||
/* Attributes not present on MAX1805 */
|
||||
static struct attribute *max1668_attribute_unique[] = {
|
||||
&sensor_dev_attr_temp4_max.dev_attr.attr,
|
||||
&sensor_dev_attr_temp4_min.dev_attr.attr,
|
||||
&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 const struct hwmon_ops max1668_hwmon_ops = {
|
||||
.is_visible = max1668_is_visible,
|
||||
.read = max1668_read,
|
||||
.write = max1668_write,
|
||||
};
|
||||
|
||||
static umode_t max1668_attribute_mode(struct kobject *kobj,
|
||||
struct attribute *attr, int index)
|
||||
{
|
||||
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
|
||||
static const struct hwmon_chip_info max1668_chip_info = {
|
||||
.ops = &max1668_hwmon_ops,
|
||||
.info = max1668_info,
|
||||
};
|
||||
|
||||
/* Return 0 if detection is successful, -ENODEV otherwise */
|
||||
@ -391,6 +224,48 @@ static int max1668_detect(struct i2c_client *client,
|
||||
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)
|
||||
{
|
||||
struct i2c_adapter *adapter = client->adapter;
|
||||
@ -405,24 +280,22 @@ static int max1668_probe(struct i2c_client *client)
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->client = client;
|
||||
data->type = (uintptr_t)i2c_get_match_data(client);
|
||||
mutex_init(&data->update_lock);
|
||||
data->regmap = devm_regmap_init(dev, &max1668_regmap_bus, client,
|
||||
&max1668_regmap_config);
|
||||
if (IS_ERR(data->regmap))
|
||||
return PTR_ERR(data->regmap);
|
||||
|
||||
/* sysfs hooks */
|
||||
data->groups[0] = &max1668_group_common;
|
||||
if (data->type == max1668 || data->type == max1989)
|
||||
data->groups[1] = &max1668_group_unique;
|
||||
data->channels = (uintptr_t)i2c_get_match_data(client);
|
||||
|
||||
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
|
||||
data, data->groups);
|
||||
hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data,
|
||||
&max1668_chip_info, NULL);
|
||||
return PTR_ERR_OR_ZERO(hwmon_dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id max1668_id[] = {
|
||||
{ "max1668", max1668 },
|
||||
{ "max1805", max1805 },
|
||||
{ "max1989", max1989 },
|
||||
{ "max1668", 5 },
|
||||
{ "max1805", 3 },
|
||||
{ "max1989", 5 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, max1668_id);
|
||||
|
@ -88,25 +88,16 @@ struct max6639_data {
|
||||
|
||||
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);
|
||||
unsigned int val;
|
||||
u8 regvals[2];
|
||||
int res;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
res = regmap_multi_reg_read(data->regmap, regs, regvals, 2);
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
||||
*temp = val >> 5;
|
||||
res = regmap_read(data->regmap, MAX6639_REG_TEMP(channel), &val);
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
||||
*temp |= val << 3;
|
||||
*temp *= 125;
|
||||
*temp = ((regvals[0] >> 5) | (regvals[1] << 3)) * 125;
|
||||
|
||||
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,
|
||||
long *pwm_val)
|
||||
{
|
||||
u32 regs[2] = { MAX6639_REG_FAN_CONFIG3(channel), MAX6639_REG_GCONFIG };
|
||||
struct max6639_data *data = dev_get_drvdata(dev);
|
||||
unsigned int val;
|
||||
u8 regvals[2];
|
||||
int res;
|
||||
u8 i;
|
||||
|
||||
@ -303,26 +296,13 @@ static int max6639_read_pwm(struct device *dev, u32 attr, int channel,
|
||||
*pwm_val = val * 255 / 120;
|
||||
return 0;
|
||||
case hwmon_pwm_freq:
|
||||
mutex_lock(&data->update_lock);
|
||||
res = regmap_read(data->regmap, MAX6639_REG_FAN_CONFIG3(channel), &val);
|
||||
if (res < 0) {
|
||||
mutex_unlock(&data->update_lock);
|
||||
res = regmap_multi_reg_read(data->regmap, regs, regvals, 2);
|
||||
if (res < 0)
|
||||
return res;
|
||||
}
|
||||
i = val & MAX6639_FAN_CONFIG3_FREQ_MASK;
|
||||
|
||||
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 = regvals[0] & MAX6639_FAN_CONFIG3_FREQ_MASK;
|
||||
if (regvals[1] & MAX6639_GCONFIG_PWM_FREQ_HI)
|
||||
i |= 0x4;
|
||||
i &= 0x7;
|
||||
*pwm_val = freq_table[i];
|
||||
|
||||
mutex_unlock(&data->update_lock);
|
||||
return 0;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1269,6 +1269,7 @@ static const char * const asus_msi_boards[] = {
|
||||
"EX-B760M-V5 D4",
|
||||
"EX-H510M-V3",
|
||||
"EX-H610M-V3 D4",
|
||||
"G15CF",
|
||||
"PRIME A620M-A",
|
||||
"PRIME B560-PLUS",
|
||||
"PRIME B560-PLUS AC-HES",
|
||||
|
@ -229,41 +229,34 @@ static int nct7802_read_temp(struct nct7802_data *data,
|
||||
|
||||
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;
|
||||
|
||||
mutex_lock(&data->access_lock);
|
||||
ret = regmap_read(data->regmap, reg_fan, &f1);
|
||||
if (ret < 0)
|
||||
goto abort;
|
||||
ret = regmap_read(data->regmap, REG_FANCOUNT_LOW, &f2);
|
||||
if (ret < 0)
|
||||
goto abort;
|
||||
ret = (f1 << 5) | (f2 >> 3);
|
||||
ret = regmap_multi_reg_read(data->regmap, regs, f, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = (f[0] << 5) | (f[1] >> 3);
|
||||
/* convert fan count to rpm */
|
||||
if (ret == 0x1fff) /* maximum value, assume fan is stopped */
|
||||
ret = 0;
|
||||
else if (ret)
|
||||
ret = DIV_ROUND_CLOSEST(1350000U, ret);
|
||||
abort:
|
||||
mutex_unlock(&data->access_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nct7802_read_fan_min(struct nct7802_data *data, u8 reg_fan_low,
|
||||
u8 reg_fan_high)
|
||||
{
|
||||
unsigned int f1, f2;
|
||||
unsigned int regs[2] = {reg_fan_low, reg_fan_high};
|
||||
u8 f[2];
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->access_lock);
|
||||
ret = regmap_read(data->regmap, reg_fan_low, &f1);
|
||||
ret = regmap_multi_reg_read(data->regmap, regs, f, 2);
|
||||
if (ret < 0)
|
||||
goto abort;
|
||||
ret = regmap_read(data->regmap, reg_fan_high, &f2);
|
||||
if (ret < 0)
|
||||
goto abort;
|
||||
ret = f1 | ((f2 & 0xf8) << 5);
|
||||
return ret;
|
||||
|
||||
ret = f[0] | ((f[1] & 0xf8) << 5);
|
||||
/* convert fan count to rpm */
|
||||
if (ret == 0x1fff) /* maximum value, assume no limit */
|
||||
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);
|
||||
else
|
||||
ret = 1350000U;
|
||||
abort:
|
||||
mutex_unlock(&data->access_lock);
|
||||
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)
|
||||
{
|
||||
unsigned int v1, v2;
|
||||
u8 v[2];
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->access_lock);
|
||||
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)
|
||||
goto abort;
|
||||
ret = regmap_read(data->regmap, REG_VOLTAGE_LOW, &v2);
|
||||
if (ret < 0)
|
||||
goto abort;
|
||||
ret = ((v1 << 2) | (v2 >> 6)) * nct7802_vmul[nr];
|
||||
return ret;
|
||||
ret = ((v[0] << 2) | (v[1] >> 6)) * nct7802_vmul[nr];
|
||||
} else { /* limit */
|
||||
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,
|
||||
REG_VOLTAGE_LIMIT_LSB[index - 1][nr], &v1);
|
||||
ret = regmap_multi_reg_read(data->regmap, regs, v, 2);
|
||||
if (ret < 0)
|
||||
goto abort;
|
||||
ret = regmap_read(data->regmap, REG_VOLTAGE_LIMIT_MSB[nr],
|
||||
&v2);
|
||||
if (ret < 0)
|
||||
goto abort;
|
||||
ret = (v1 | ((v2 << shift) & 0x300)) * nct7802_vmul[nr];
|
||||
return ret;
|
||||
ret = (v[0] | ((v[1] << shift) & 0x300)) * nct7802_vmul[nr];
|
||||
}
|
||||
abort:
|
||||
mutex_unlock(&data->access_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -1145,17 +1129,14 @@ static int nct7802_configure_channels(struct device *dev,
|
||||
{
|
||||
/* Enable local temperature sensor by default */
|
||||
u8 mode_mask = MODE_LTD_EN, mode_val = MODE_LTD_EN;
|
||||
struct device_node *node;
|
||||
int err;
|
||||
|
||||
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,
|
||||
&mode_val);
|
||||
if (err) {
|
||||
of_node_put(node);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -927,7 +927,7 @@ static int npcm7xx_en_pwm_fan(struct device *dev,
|
||||
static int npcm7xx_pwm_fan_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np, *child;
|
||||
struct device_node *np;
|
||||
struct npcm7xx_pwm_fan_data *data;
|
||||
struct resource *res;
|
||||
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);
|
||||
if (ret) {
|
||||
dev_err(dev, "enable pwm and fan failed\n");
|
||||
of_node_put(child);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
@ -62,6 +62,7 @@ static const struct platform_device_id ntc_thermistor_id[] = {
|
||||
[NTC_SSG1404001221] = { "ssg1404_001221", TYPE_NCPXXWB473 },
|
||||
[NTC_LAST] = { },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, ntc_thermistor_id);
|
||||
|
||||
/*
|
||||
* A compensation table should be sorted by the values of .ohm
|
||||
|
@ -1,18 +1,21 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Platform driver for OneXPlayer, AOK ZOE, and Aya Neo Handhelds that expose
|
||||
* fan reading and control via hwmon sysfs.
|
||||
* Platform driver for OneXPlayer, AOKZOE, AYANEO, and OrangePi Handhelds
|
||||
* that expose fan reading and control via hwmon sysfs.
|
||||
*
|
||||
* 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
|
||||
* supported but the code is made to be simple to add other handheld
|
||||
* boards in the future.
|
||||
* the boot cpu vendor (Intel/AMD). Of these older models only AMD is
|
||||
* supported.
|
||||
*
|
||||
* 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
|
||||
* 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) 2024 Derek J. Clark <derekjohn.clark@gmail.com>
|
||||
*/
|
||||
|
||||
#include <linux/acpi.h>
|
||||
@ -43,32 +46,48 @@ enum oxp_board {
|
||||
aok_zoe_a1 = 1,
|
||||
aya_neo_2,
|
||||
aya_neo_air,
|
||||
aya_neo_air_1s,
|
||||
aya_neo_air_plus_mendo,
|
||||
aya_neo_air_pro,
|
||||
aya_neo_flip,
|
||||
aya_neo_geek,
|
||||
aya_neo_kun,
|
||||
orange_pi_neo,
|
||||
oxp_2,
|
||||
oxp_fly,
|
||||
oxp_mini_amd,
|
||||
oxp_mini_amd_a07,
|
||||
oxp_mini_amd_pro,
|
||||
oxp_x1,
|
||||
};
|
||||
|
||||
static enum oxp_board board;
|
||||
|
||||
/* Fan reading and PWM */
|
||||
#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_SENSOR_PWM_REG 0x4B /* PWM reading is 1 register long */
|
||||
#define OXP_SENSOR_FAN_REG 0x76 /* Fan reading is 2 registers long */
|
||||
#define OXP_2_SENSOR_FAN_REG 0x58 /* Fan reading is 2 registers 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
|
||||
* Older boards have different values and EC registers
|
||||
* Different boards have different values and EC registers
|
||||
* for the same function
|
||||
*/
|
||||
#define OXP_OLD_TURBO_SWITCH_REG 0x1E
|
||||
#define OXP_OLD_TURBO_TAKE_VAL 0x01
|
||||
#define OXP_OLD_TURBO_RETURN_VAL 0x00
|
||||
#define OXP_TURBO_SWITCH_REG 0xF1 /* Mini Pro, OneXFly, AOKZOE */
|
||||
#define OXP_2_TURBO_SWITCH_REG 0xEB /* OXP2 and X1 */
|
||||
#define OXP_MINI_TURBO_SWITCH_REG 0x1E /* Mini AO7 */
|
||||
|
||||
#define OXP_TURBO_SWITCH_REG 0xF1
|
||||
#define OXP_TURBO_TAKE_VAL 0x40
|
||||
#define OXP_TURBO_RETURN_VAL 0x00
|
||||
#define OXP_MINI_TURBO_TAKE_VAL 0x01 /* Mini AO7 */
|
||||
#define OXP_TURBO_TAKE_VAL 0x40 /* All other models */
|
||||
|
||||
#define OXP_TURBO_RETURN_VAL 0x00 /* Common return val */
|
||||
|
||||
static const struct dmi_system_id dmi_table[] = {
|
||||
{
|
||||
@ -88,7 +107,7 @@ static const struct dmi_system_id dmi_table[] = {
|
||||
{
|
||||
.matches = {
|
||||
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,
|
||||
},
|
||||
@ -99,6 +118,13 @@ static const struct dmi_system_id dmi_table[] = {
|
||||
},
|
||||
.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 = {
|
||||
DMI_MATCH(DMI_BOARD_VENDOR, "AYANEO"),
|
||||
@ -116,10 +142,31 @@ static const struct dmi_system_id dmi_table[] = {
|
||||
{
|
||||
.matches = {
|
||||
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,
|
||||
},
|
||||
{
|
||||
.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 = {
|
||||
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,
|
||||
},
|
||||
{
|
||||
.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 = {
|
||||
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,
|
||||
},
|
||||
{
|
||||
.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) {
|
||||
case oxp_mini_amd_a07:
|
||||
reg = OXP_OLD_TURBO_SWITCH_REG;
|
||||
val = OXP_OLD_TURBO_TAKE_VAL;
|
||||
reg = OXP_MINI_TURBO_SWITCH_REG;
|
||||
val = OXP_MINI_TURBO_TAKE_VAL;
|
||||
break;
|
||||
case oxp_mini_amd_pro:
|
||||
case aok_zoe_a1:
|
||||
case oxp_fly:
|
||||
case oxp_mini_amd_pro:
|
||||
reg = OXP_TURBO_SWITCH_REG;
|
||||
val = OXP_TURBO_TAKE_VAL;
|
||||
break;
|
||||
case oxp_2:
|
||||
case oxp_x1:
|
||||
reg = OXP_2_TURBO_SWITCH_REG;
|
||||
val = OXP_TURBO_TAKE_VAL;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -213,14 +287,20 @@ static int tt_toggle_disable(void)
|
||||
|
||||
switch (board) {
|
||||
case oxp_mini_amd_a07:
|
||||
reg = OXP_OLD_TURBO_SWITCH_REG;
|
||||
val = OXP_OLD_TURBO_RETURN_VAL;
|
||||
reg = OXP_MINI_TURBO_SWITCH_REG;
|
||||
val = OXP_TURBO_RETURN_VAL;
|
||||
break;
|
||||
case oxp_mini_amd_pro:
|
||||
case aok_zoe_a1:
|
||||
case oxp_fly:
|
||||
case oxp_mini_amd_pro:
|
||||
reg = OXP_TURBO_SWITCH_REG;
|
||||
val = OXP_TURBO_RETURN_VAL;
|
||||
break;
|
||||
case oxp_2:
|
||||
case oxp_x1:
|
||||
reg = OXP_2_TURBO_SWITCH_REG;
|
||||
val = OXP_TURBO_RETURN_VAL;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -233,8 +313,11 @@ static umode_t tt_toggle_is_visible(struct kobject *kobj,
|
||||
{
|
||||
switch (board) {
|
||||
case aok_zoe_a1:
|
||||
case oxp_2:
|
||||
case oxp_fly:
|
||||
case oxp_mini_amd_a07:
|
||||
case oxp_mini_amd_pro:
|
||||
case oxp_x1:
|
||||
return attr->mode;
|
||||
default:
|
||||
break;
|
||||
@ -273,12 +356,17 @@ static ssize_t tt_toggle_show(struct device *dev,
|
||||
|
||||
switch (board) {
|
||||
case oxp_mini_amd_a07:
|
||||
reg = OXP_OLD_TURBO_SWITCH_REG;
|
||||
reg = OXP_MINI_TURBO_SWITCH_REG;
|
||||
break;
|
||||
case oxp_mini_amd_pro:
|
||||
case aok_zoe_a1:
|
||||
case oxp_fly:
|
||||
case oxp_mini_amd_pro:
|
||||
reg = OXP_TURBO_SWITCH_REG;
|
||||
break;
|
||||
case oxp_2:
|
||||
case oxp_x1:
|
||||
reg = OXP_2_TURBO_SWITCH_REG;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -295,12 +383,53 @@ static DEVICE_ATTR_RW(tt_toggle);
|
||||
/* PWM enable/disable functions */
|
||||
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)
|
||||
{
|
||||
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 */
|
||||
@ -326,7 +455,30 @@ static int oxp_platform_read(struct device *dev, enum hwmon_sensor_types type,
|
||||
case hwmon_fan:
|
||||
switch (attr) {
|
||||
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:
|
||||
break;
|
||||
}
|
||||
@ -334,27 +486,72 @@ static int oxp_platform_read(struct device *dev, enum hwmon_sensor_types type,
|
||||
case hwmon_pwm:
|
||||
switch (attr) {
|
||||
case hwmon_pwm_input:
|
||||
ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
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_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_mini_amd:
|
||||
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;
|
||||
break;
|
||||
case oxp_mini_amd_pro:
|
||||
case aok_zoe_a1:
|
||||
case oxp_fly:
|
||||
case oxp_mini_amd_pro:
|
||||
default:
|
||||
ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
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:
|
||||
break;
|
||||
}
|
||||
@ -381,21 +578,36 @@ static int oxp_platform_write(struct device *dev, enum hwmon_sensor_types type,
|
||||
if (val < 0 || val > 255)
|
||||
return -EINVAL;
|
||||
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_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_mini_amd:
|
||||
case oxp_mini_amd_a07:
|
||||
/* scale to range [0-100] */
|
||||
val = (val * 100) / 255;
|
||||
break;
|
||||
return write_to_ec(OXP_SENSOR_PWM_REG, val);
|
||||
case aok_zoe_a1:
|
||||
case oxp_fly:
|
||||
case oxp_mini_amd_pro:
|
||||
return write_to_ec(OXP_SENSOR_PWM_REG, val);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return write_to_ec(OXP_SENSOR_PWM_REG, val);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -467,19 +679,20 @@ static int __init oxp_platform_init(void)
|
||||
{
|
||||
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);
|
||||
if (!dmi_entry || boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
|
||||
if (!dmi_entry)
|
||||
return -ENODEV;
|
||||
|
||||
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 =
|
||||
platform_create_bundle(&oxp_platform_driver,
|
||||
oxp_platform_probe, NULL, 0, NULL, 0);
|
||||
|
@ -1315,7 +1315,7 @@ static void pc87360_init_device(struct platform_device *pdev,
|
||||
(reg & 0xC0) | 0x11);
|
||||
}
|
||||
|
||||
nr = data->innr < 11 ? data->innr : 11;
|
||||
nr = min(data->innr, 11);
|
||||
for (i = 0; i < nr; i++) {
|
||||
reg = pc87360_read_value(data, LD_IN, i,
|
||||
PC87365_REG_IN_STATUS);
|
||||
|
@ -31,8 +31,6 @@ MODULE_DEVICE_TABLE(i2c, max15301_id);
|
||||
|
||||
struct max15301_data {
|
||||
int id;
|
||||
ktime_t access; /* Chip access time */
|
||||
int delay; /* Delay between chip accesses in us */
|
||||
struct pmbus_driver_info info;
|
||||
};
|
||||
|
||||
@ -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)
|
||||
{
|
||||
int status;
|
||||
@ -164,12 +79,7 @@ static int max15301_probe(struct i2c_client *client)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
max15301_data.delay = delay;
|
||||
|
||||
info->read_byte_data = max15301_read_byte_data;
|
||||
info->read_word_data = max15301_read_word_data;
|
||||
info->write_byte = max15301_write_byte;
|
||||
info->write_word_data = max15301_write_word_data;
|
||||
info->access_delay = delay;
|
||||
|
||||
return pmbus_do_probe(client, info);
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ struct mpq7932_data {
|
||||
};
|
||||
|
||||
#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,
|
||||
MPQ7932_UV_STEP, MPQ7932_BUCK_UV_MIN),
|
||||
PMBUS_REGULATOR_STEP("buck", 1, MPQ7932_N_VOLTAGES,
|
||||
|
@ -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)
|
||||
static const struct regulator_desc pli1209bc_reg_desc = {
|
||||
.name = "vout2",
|
||||
@ -127,7 +103,7 @@ static struct pmbus_driver_info pli1209bc_info = {
|
||||
| PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP
|
||||
| PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_INPUT,
|
||||
.read_word_data = pli1209bc_read_word_data,
|
||||
.write_byte = pli1209bc_write_byte,
|
||||
.write_delay = 250,
|
||||
#if IS_ENABLED(CONFIG_SENSORS_PLI1209BC_REGULATOR)
|
||||
.num_regulators = 1,
|
||||
.reg_desc = &pli1209bc_reg_desc,
|
||||
|
@ -472,6 +472,16 @@ struct pmbus_driver_info {
|
||||
|
||||
/* custom attributes */
|
||||
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 */
|
||||
|
@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/module.h>
|
||||
@ -110,6 +111,8 @@ struct pmbus_data {
|
||||
|
||||
int vout_low[PMBUS_PAGES]; /* voltage low 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 {
|
||||
@ -160,6 +163,39 @@ void pmbus_set_update(struct i2c_client *client, u8 reg, bool update)
|
||||
}
|
||||
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)
|
||||
{
|
||||
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) &&
|
||||
data->info->pages > 1 && page != data->currpage) {
|
||||
pmbus_wait(client);
|
||||
rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page);
|
||||
pmbus_update_ts(client, true);
|
||||
if (rv < 0)
|
||||
return rv;
|
||||
|
||||
pmbus_wait(client);
|
||||
rv = i2c_smbus_read_byte_data(client, PMBUS_PAGE);
|
||||
pmbus_update_ts(client, false);
|
||||
if (rv < 0)
|
||||
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 &&
|
||||
!(data->info->func[page] & PMBUS_PHASE_VIRTUAL)) {
|
||||
pmbus_wait(client);
|
||||
rv = i2c_smbus_write_byte_data(client, PMBUS_PHASE,
|
||||
phase);
|
||||
pmbus_update_ts(client, true);
|
||||
if (rv)
|
||||
return rv;
|
||||
}
|
||||
@ -204,7 +246,11 @@ int pmbus_write_byte(struct i2c_client *client, int page, u8 value)
|
||||
if (rv < 0)
|
||||
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);
|
||||
|
||||
@ -235,7 +281,11 @@ int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg,
|
||||
if (rv < 0)
|
||||
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);
|
||||
|
||||
@ -353,7 +403,11 @@ int pmbus_read_word_data(struct i2c_client *client, int page, int phase, u8 reg)
|
||||
if (rv < 0)
|
||||
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);
|
||||
|
||||
@ -412,7 +466,11 @@ int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg)
|
||||
if (rv < 0)
|
||||
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);
|
||||
|
||||
@ -424,7 +482,11 @@ int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, u8 value)
|
||||
if (rv < 0)
|
||||
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);
|
||||
|
||||
@ -456,7 +518,11 @@ static int pmbus_read_block_data(struct i2c_client *client, int page, u8 reg,
|
||||
if (rv < 0)
|
||||
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,
|
||||
@ -2457,9 +2523,11 @@ static int pmbus_read_coefficients(struct i2c_client *client,
|
||||
data.block[1] = attr->reg;
|
||||
data.block[2] = 0x01;
|
||||
|
||||
pmbus_wait(client);
|
||||
rv = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
|
||||
I2C_SMBUS_WRITE, PMBUS_COEFFICIENTS,
|
||||
I2C_SMBUS_BLOCK_PROC_CALL, &data);
|
||||
pmbus_update_ts(client, true);
|
||||
|
||||
if (rv < 0)
|
||||
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 */
|
||||
if (!(data->flags & PMBUS_NO_CAPABILITY)) {
|
||||
pmbus_wait(client);
|
||||
ret = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY);
|
||||
pmbus_update_ts(client, false);
|
||||
|
||||
if (ret >= 0 && (ret & PB_CAPABILITY_ERROR_CHECK)) {
|
||||
if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_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.
|
||||
*/
|
||||
data->read_status = pmbus_read_status_word;
|
||||
pmbus_wait(client);
|
||||
ret = i2c_smbus_read_word_data(client, PMBUS_STATUS_WORD);
|
||||
pmbus_update_ts(client, false);
|
||||
|
||||
if (ret < 0 || ret == 0xffff) {
|
||||
data->read_status = pmbus_read_status_byte;
|
||||
pmbus_wait(client);
|
||||
ret = i2c_smbus_read_byte_data(client, PMBUS_STATUS_BYTE);
|
||||
pmbus_update_ts(client, false);
|
||||
|
||||
if (ret < 0 || ret == 0xff) {
|
||||
dev_err(dev, "PMBus status register not found\n");
|
||||
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.
|
||||
*/
|
||||
if (!(data->flags & PMBUS_NO_WRITE_PROTECT)) {
|
||||
pmbus_wait(client);
|
||||
ret = i2c_smbus_read_byte_data(client, PMBUS_WRITE_PROTECT);
|
||||
pmbus_update_ts(client, false);
|
||||
|
||||
if (ret > 0 && (ret & PB_WP_ANY))
|
||||
data->flags |= PMBUS_WRITE_PROTECTED | PMBUS_SKIP_STATUS_CHECK;
|
||||
}
|
||||
|
@ -67,7 +67,6 @@ struct ucd9000_data {
|
||||
struct gpio_chip gpio;
|
||||
#endif
|
||||
struct dentry *debugfs;
|
||||
ktime_t write_time;
|
||||
};
|
||||
#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
|
||||
|
||||
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)
|
||||
{
|
||||
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
|
||||
| PMBUS_HAVE_FAN34 | PMBUS_HAVE_STATUS_FAN34;
|
||||
} else if (mid->driver_data == ucd90320) {
|
||||
info->read_byte_data = ucd90320_read_byte_data;
|
||||
info->read_word_data = ucd90320_read_word_data;
|
||||
info->write_byte = ucd90320_write_byte;
|
||||
info->write_word_data = ucd90320_write_word_data;
|
||||
/* Delay SMBus operations after a write */
|
||||
info->write_delay = UCD90320_WAIT_DELAY_US;
|
||||
}
|
||||
|
||||
ucd9000_probe_gpio(client, mid, data);
|
||||
|
@ -22,8 +22,6 @@ enum chips { zl2004, zl2005, zl2006, zl2008, zl2105, zl2106, zl6100, zl6105,
|
||||
|
||||
struct zl6100_data {
|
||||
int id;
|
||||
ktime_t access; /* chip access time */
|
||||
int delay; /* Delay between chip accesses in uS */
|
||||
struct pmbus_driver_info info;
|
||||
};
|
||||
|
||||
@ -122,16 +120,6 @@ static u16 zl6100_d2l(long val)
|
||||
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,
|
||||
int phase, int reg)
|
||||
{
|
||||
@ -174,9 +162,7 @@ static int zl6100_read_word_data(struct i2c_client *client, int page,
|
||||
break;
|
||||
}
|
||||
|
||||
zl6100_wait(data);
|
||||
ret = pmbus_read_word_data(client, page, phase, vreg);
|
||||
data->access = ktime_get();
|
||||
if (ret < 0)
|
||||
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)
|
||||
{
|
||||
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
|
||||
struct zl6100_data *data = to_zl6100_data(info);
|
||||
int ret, status;
|
||||
|
||||
if (page >= info->pages)
|
||||
return -ENXIO;
|
||||
|
||||
zl6100_wait(data);
|
||||
|
||||
switch (reg) {
|
||||
case PMBUS_VIRT_STATUS_VMON:
|
||||
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);
|
||||
break;
|
||||
}
|
||||
data->access = ktime_get();
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -234,8 +216,7 @@ static int zl6100_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 zl6100_data *data = to_zl6100_data(info);
|
||||
int ret, vreg;
|
||||
int vreg;
|
||||
|
||||
if (page >= info->pages)
|
||||
return -ENXIO;
|
||||
@ -265,27 +246,7 @@ static int zl6100_write_word_data(struct i2c_client *client, int page, int reg,
|
||||
vreg = reg;
|
||||
}
|
||||
|
||||
zl6100_wait(data);
|
||||
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;
|
||||
return pmbus_write_word_data(client, page, vreg, word);
|
||||
}
|
||||
|
||||
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
|
||||
* accesses.
|
||||
*/
|
||||
data->delay = delay;
|
||||
|
||||
/*
|
||||
* Since there was a direct I2C device access above, wait before
|
||||
* accessing the chip again.
|
||||
*/
|
||||
data->access = ktime_get();
|
||||
zl6100_wait(data);
|
||||
udelay(delay);
|
||||
|
||||
info = &data->info;
|
||||
|
||||
@ -404,8 +358,7 @@ static int zl6100_probe(struct i2c_client *client)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
data->access = ktime_get();
|
||||
zl6100_wait(data);
|
||||
udelay(delay);
|
||||
|
||||
if (ret & ZL8802_MFR_PHASES_MASK)
|
||||
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)
|
||||
return ret;
|
||||
|
||||
data->access = ktime_get();
|
||||
zl6100_wait(data);
|
||||
udelay(delay);
|
||||
|
||||
ret = i2c_smbus_read_word_data(client, ZL8802_MFR_USER_CONFIG);
|
||||
if (ret < 0)
|
||||
@ -428,8 +380,7 @@ static int zl6100_probe(struct i2c_client *client)
|
||||
if (ret & ZL8802_MFR_XTEMP_ENABLE_2)
|
||||
info->func[i] |= PMBUS_HAVE_TEMP2;
|
||||
|
||||
data->access = ktime_get();
|
||||
zl6100_wait(data);
|
||||
udelay(delay);
|
||||
}
|
||||
ret = i2c_smbus_read_word_data(client, ZL8802_MFR_USER_GLOBAL_CONFIG);
|
||||
if (ret < 0)
|
||||
@ -446,13 +397,12 @@ static int zl6100_probe(struct i2c_client *client)
|
||||
info->func[0] |= PMBUS_HAVE_TEMP2;
|
||||
}
|
||||
|
||||
data->access = ktime_get();
|
||||
zl6100_wait(data);
|
||||
udelay(delay);
|
||||
|
||||
info->access_delay = delay;
|
||||
info->read_word_data = zl6100_read_word_data;
|
||||
info->read_byte_data = zl6100_read_byte_data;
|
||||
info->write_word_data = zl6100_write_word_data;
|
||||
info->write_byte = zl6100_write_byte;
|
||||
|
||||
return pmbus_do_probe(client, info);
|
||||
}
|
||||
|
@ -167,7 +167,7 @@ static int pwm_fan_power_on(struct pwm_fan_ctx *ctx)
|
||||
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;
|
||||
bool enable_regulator = false;
|
||||
@ -180,7 +180,8 @@ static int pwm_fan_power_off(struct pwm_fan_ctx *ctx)
|
||||
state,
|
||||
&enable_regulator);
|
||||
|
||||
state->enabled = false;
|
||||
if (force_disable)
|
||||
state->enabled = false;
|
||||
state->duty_cycle = 0;
|
||||
ret = pwm_apply_might_sleep(ctx->pwm, state);
|
||||
if (ret) {
|
||||
@ -213,7 +214,7 @@ static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm)
|
||||
return ret;
|
||||
ret = pwm_fan_power_on(ctx);
|
||||
} else {
|
||||
ret = pwm_fan_power_off(ctx);
|
||||
ret = pwm_fan_power_off(ctx, false);
|
||||
}
|
||||
if (!ret)
|
||||
ctx->pwm_value = pwm;
|
||||
@ -468,7 +469,7 @@ static void pwm_fan_cleanup(void *__ctx)
|
||||
del_timer_sync(&ctx->rpm_timer);
|
||||
/* Switch off everything */
|
||||
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)
|
||||
@ -661,7 +662,7 @@ static int pwm_fan_suspend(struct device *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)
|
||||
|
@ -416,8 +416,7 @@ static int sch5636_probe(struct platform_device *pdev)
|
||||
id[i] = '\0';
|
||||
|
||||
if (strcmp(id, "THS")) {
|
||||
pr_err("Unknown Fujitsu id: %02x%02x%02x\n",
|
||||
id[0], id[1], id[2]);
|
||||
pr_err("Unknown Fujitsu id: %3pE (%3ph)\n", id, id);
|
||||
err = -ENODEV;
|
||||
goto error;
|
||||
}
|
||||
|
@ -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,
|
||||
struct mutex *io_lock, int check_enabled);
|
||||
void sch56xx_watchdog_unregister(struct sch56xx_watchdog_data *data);
|
||||
|
388
drivers/hwmon/sg2042-mcu.c
Normal file
388
drivers/hwmon/sg2042-mcu.c
Normal 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");
|
@ -199,10 +199,7 @@ static ssize_t eic_read(struct sht21 *sht21)
|
||||
eic[6] = rx[0];
|
||||
eic[7] = rx[1];
|
||||
|
||||
ret = snprintf(sht21->eic, sizeof(sht21->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]);
|
||||
ret = snprintf(sht21->eic, sizeof(sht21->eic), "%8phN\n", eic);
|
||||
out:
|
||||
if (ret < 0)
|
||||
sht21->eic[0] = 0;
|
||||
|
@ -77,7 +77,7 @@ static const struct i2c_device_id stts751_id[] = {
|
||||
};
|
||||
|
||||
static const struct of_device_id __maybe_unused stts751_of_match[] = {
|
||||
{ .compatible = "stts751" },
|
||||
{ .compatible = "st,stts751" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, stts751_of_match);
|
||||
|
235
drivers/hwmon/surface_temp.c
Normal file
235
drivers/hwmon/surface_temp.c
Normal 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");
|
@ -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 regmap *regmap = data->regmap;
|
||||
unsigned int regs[2] = { TMP401_TEMP_MSB[3][channel], TMP401_TEMP_CRIT_HYST };
|
||||
unsigned int regval;
|
||||
u16 regvals[2];
|
||||
int reg, ret;
|
||||
|
||||
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);
|
||||
break;
|
||||
case hwmon_temp_crit_hyst:
|
||||
mutex_lock(&data->update_lock);
|
||||
reg = TMP401_TEMP_MSB[3][channel];
|
||||
ret = regmap_read(regmap, reg, ®val);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
*val = tmp401_register_to_temp(regval, data->extended_range);
|
||||
ret = regmap_read(regmap, TMP401_TEMP_CRIT_HYST, ®val);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
*val -= regval * 1000;
|
||||
unlock:
|
||||
mutex_unlock(&data->update_lock);
|
||||
ret = regmap_multi_reg_read(regmap, regs, regvals, 2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = tmp401_register_to_temp(regvals[0], data->extended_range) -
|
||||
(regvals[1] * 1000);
|
||||
break;
|
||||
case hwmon_temp_fault:
|
||||
case hwmon_temp_min_alarm:
|
||||
|
@ -410,18 +410,15 @@ static int tmp421_probe_from_dt(struct i2c_client *client, struct tmp421_data *d
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
const struct device_node *np = dev->of_node;
|
||||
struct device_node *child;
|
||||
int err;
|
||||
|
||||
for_each_child_of_node(np, child) {
|
||||
for_each_child_of_node_scoped(np, child) {
|
||||
if (strcmp(child->name, "channel"))
|
||||
continue;
|
||||
|
||||
err = tmp421_probe_child_from_dt(client, child, data);
|
||||
if (err) {
|
||||
of_node_put(child);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -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 regmap *regmap = data->regmap;
|
||||
unsigned int regval, regval2;
|
||||
unsigned int regs[2];
|
||||
unsigned int regval;
|
||||
u16 regvals[2];
|
||||
int err = 0;
|
||||
|
||||
mutex_lock(&data->update_lock);
|
||||
|
||||
switch (attr) {
|
||||
case hwmon_temp_max_alarm:
|
||||
err = regmap_read(regmap, TMP464_THERM_STATUS_REG, ®val);
|
||||
@ -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
|
||||
* for one measurement cycle and report the cached value.
|
||||
*/
|
||||
mutex_lock(&data->update_lock);
|
||||
if (!data->valid || time_after(jiffies, data->last_updated +
|
||||
msecs_to_jiffies(data->update_interval))) {
|
||||
err = regmap_read(regmap, TMP464_REMOTE_OPEN_REG, ®val);
|
||||
if (err < 0)
|
||||
break;
|
||||
goto unlock;
|
||||
data->open_reg = regval;
|
||||
data->last_updated = jiffies;
|
||||
data->valid = true;
|
||||
}
|
||||
*val = !!(data->open_reg & BIT(channel + 7));
|
||||
unlock:
|
||||
mutex_unlock(&data->update_lock);
|
||||
break;
|
||||
case hwmon_temp_max_hyst:
|
||||
err = regmap_read(regmap, TMP464_THERM_LIMIT[channel], ®val);
|
||||
regs[0] = TMP464_THERM_LIMIT[channel];
|
||||
regs[1] = TMP464_TEMP_HYST_REG;
|
||||
err = regmap_multi_reg_read(regmap, regs, regvals, 2);
|
||||
if (err < 0)
|
||||
break;
|
||||
err = regmap_read(regmap, TMP464_TEMP_HYST_REG, ®val2);
|
||||
if (err < 0)
|
||||
break;
|
||||
regval -= regval2;
|
||||
*val = temp_from_reg(regval);
|
||||
*val = temp_from_reg(regvals[0] - regvals[1]);
|
||||
break;
|
||||
case hwmon_temp_max:
|
||||
err = regmap_read(regmap, TMP464_THERM_LIMIT[channel], ®val);
|
||||
@ -200,14 +201,12 @@ static int tmp464_temp_read(struct device *dev, u32 attr, int channel, long *val
|
||||
*val = temp_from_reg(regval);
|
||||
break;
|
||||
case hwmon_temp_crit_hyst:
|
||||
err = regmap_read(regmap, TMP464_THERM2_LIMIT[channel], ®val);
|
||||
regs[0] = TMP464_THERM2_LIMIT[channel];
|
||||
regs[1] = TMP464_TEMP_HYST_REG;
|
||||
err = regmap_multi_reg_read(regmap, regs, regvals, 2);
|
||||
if (err < 0)
|
||||
break;
|
||||
err = regmap_read(regmap, TMP464_TEMP_HYST_REG, ®val2);
|
||||
if (err < 0)
|
||||
break;
|
||||
regval -= regval2;
|
||||
*val = temp_from_reg(regval);
|
||||
*val = temp_from_reg(regvals[0] - regvals[1]);
|
||||
break;
|
||||
case hwmon_temp_crit:
|
||||
err = regmap_read(regmap, TMP464_THERM2_LIMIT[channel], ®val);
|
||||
@ -239,8 +238,6 @@ static int tmp464_temp_read(struct device *dev, u32 attr, int channel, long *val
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&data->update_lock);
|
||||
|
||||
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)
|
||||
{
|
||||
const struct device_node *np = dev->of_node;
|
||||
struct device_node *child;
|
||||
int err;
|
||||
|
||||
for_each_child_of_node(np, child) {
|
||||
for_each_child_of_node_scoped(np, child) {
|
||||
if (strcmp(child->name, "channel"))
|
||||
continue;
|
||||
|
||||
err = tmp464_probe_child_from_dt(dev, child, data);
|
||||
if (err) {
|
||||
of_node_put(child);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -72,7 +72,7 @@ static umode_t vexpress_hwmon_attr_is_visible(struct kobject *kobj,
|
||||
struct device_attribute, attr);
|
||||
|
||||
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 attr->mode;
|
||||
|
@ -481,7 +481,6 @@ devm_hwmon_device_register_with_info(struct device *dev,
|
||||
const struct attribute_group **extra_groups);
|
||||
|
||||
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,
|
||||
u32 attr, int channel);
|
||||
|
@ -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 */
|
Loading…
Reference in New Issue
Block a user