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

Pull hwmon updates from Guenter Roeck:
 "New drivers:
   - KEBA fan controller
   - KEBA battery monitoring controller
   - MAX77705

  Support added to existing drivers:
   - MAXIMUS VI HERO and ROG MAXIMUS Z90 Formula support (asus-ec-sensors)
   - SQ52206 support (ina238)
   - lt3074 support (pmbus/lt3074)
   - ADPM12160 support (pmbus/max34440)
   - MPM82504 and for MPM3695 family support (pmbus/mpq8785)
   - Add the Dell OptiPlex 7050 to the DMI whitelist (dell-smm)
   - Zen5 Ryzen Desktop support (k10temp)

  Various other minor fixes and improvements"

* tag 'hwmon-for-v6.16' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (48 commits)
  doc: hwmon: acpi_power_meter: Add information about enabling the power capping feature.
  hwmon: (isl28022) Fix current reading calculation
  hwmon: (lm75) Fix I3C transfer buffer pointer for incoming data
  hwmon: Add KEBA fan controller support
  hwmon: pmbus: mpq8785: Add support for MPM3695 family
  hwmon: pmbus: mpq8785: Add support for MPM82504
  hwmon: pmbus: mpq8785: Implement VOUT feedback resistor divider ratio configuration
  hwmon: pmbus: mpq8785: Prepare driver for multiple device support
  dt-bindings: hwmon: Add bindings for mpq8785 driver
  hwmon: (ina238) Modify the calculation formula to adapt to different chips
  hwmon: (ina238) Add support for SQ52206
  dt-bindings: Add SQ52206 to ina2xx devicetree bindings
  hwmon: (ina238) Add ina238_config to save configurations for different chips
  hwmon: (ausus-ec-sensors) add MAXIMUS VI HERO.
  hwmon: (isl28022, nct7363) Convert to use maple tree register cache
  hwmon: (asus-ec-sensors) check sensor index in read_string()
  hwmon: (asus-ec-sensors) add ROG MAXIMUS Z90 Formula.
  dt-bindings: hwmon: Add Sophgo SG2044 external hardware monitor support
  hwmon: (max77705) Add initial support
  hwmon: (tmp102) add vcc regulator support
  ...
This commit is contained in:
Linus Torvalds
2025-06-03 09:11:26 -07:00
54 changed files with 2176 additions and 353 deletions
@@ -0,0 +1,50 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/pmbus/adi,lt3074.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Analog Devices LT3074 voltage regulator
maintainers:
- Cedric Encarnacion <cedricjustine.encarnacion@analog.com>
description: |
The LT3074 is a low voltage, ultra-low noise and ultra-fast transient
response linear regulator. It allows telemetry for input/output voltage,
output current and temperature through the PMBus serial interface.
Datasheet:
https://www.analog.com/en/products/lt3074.html
allOf:
- $ref: /schemas/regulator/regulator.yaml#
properties:
compatible:
enum:
- adi,lt3074
reg:
maxItems: 1
required:
- compatible
- reg
unevaluatedProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
regulator@6d {
compatible = "adi,lt3074";
reg = <0x6d>;
regulator-name = "vout";
regulator-max-microvolt = <1250000>;
regulator-min-microvolt = <1150000>;
};
};
@@ -0,0 +1,74 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/pmbus/mps,mpq8785.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Monolithic Power Systems Multiphase Voltage Regulators with PMBus
maintainers:
- Charles Hsu <ythsu0511@gmail.com>
description:
Monolithic Power Systems digital multiphase voltage regulators with PMBus.
properties:
compatible:
enum:
- mps,mpm3695
- mps,mpm3695-25
- mps,mpm82504
- mps,mpq8785
reg:
maxItems: 1
mps,vout-fb-divider-ratio-permille:
description:
The feedback resistor divider ratio, expressed in permille
(Vfb / Vout * 1000). This value is written to the PMBUS_VOUT_SCALE_LOOP
register and is required for correct output voltage presentation.
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 1
maximum: 4095
default: 706
required:
- compatible
- reg
allOf:
- if:
properties:
compatible:
enum:
- mps,mpm3695
- mps,mpm82504
then:
properties:
mps,vout-fb-divider-ratio-permille:
maximum: 1023
- if:
properties:
compatible:
const: mps,mpq8785
then:
properties:
mps,vout-fb-divider-ratio-permille:
maximum: 2047
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
pmic@30 {
compatible = "mps,mpm82504";
reg = <0x30>;
mps,vout-fb-divider-ratio-permille = <600>;
};
};
@@ -11,7 +11,11 @@ maintainers:
properties:
compatible:
const: sophgo,sg2042-hwmon-mcu
oneOf:
- items:
- const: sophgo,sg2044-hwmon-mcu
- const: sophgo,sg2042-hwmon-mcu
- const: sophgo,sg2042-hwmon-mcu
reg:
maxItems: 1
@@ -28,6 +28,17 @@ properties:
i2c-mux:
type: object
fan:
$ref: fan-common.yaml#
unevaluatedProperties: false
"#pwm-cells":
const: 2
description: |
Number of cells in a PWM specifier.
- cell 0: PWM period in nanoseconds
- cell 1: PWM polarity: 0 or PWM_POLARITY_INVERTED
required:
- compatible
- reg
@@ -50,9 +61,14 @@ examples:
#address-cells = <1>;
#size-cells = <0>;
fan@18 {
fan_controller: fan@18 {
compatible = "ti,amc6821";
reg = <0x18>;
#pwm-cells = <2>;
fan {
pwms = <&fan_controller 40000 0>;
};
};
};
@@ -19,6 +19,7 @@ description: |
properties:
compatible:
enum:
- silergy,sq52206
- silergy,sy24655
- ti,ina209
- ti,ina219
@@ -58,6 +59,9 @@ properties:
shunt voltage, and a value of 4 maps to ADCRANGE=0 such that a wider
voltage range is used.
For SQ52206,the shunt-gain value 1 mapps to ADCRANGE=10/11, the value 2
mapps to ADCRANGE=01, and the value 4 mapps to ADCRANGE=00.
The default value is device dependent, and is defined by the reset value
of PGA/ADCRANGE in the respective configuration registers.
$ref: /schemas/types.yaml#/definitions/uint32
@@ -97,6 +101,7 @@ allOf:
compatible:
contains:
enum:
- silergy,sq52206
- silergy,sy24655
- ti,ina209
- ti,ina219
@@ -23,6 +23,9 @@ properties:
"#thermal-sensor-cells":
const: 1
vcc-supply:
description: Power supply for tmp102
required:
- compatible
- reg
@@ -42,6 +45,7 @@ examples:
reg = <0x48>;
interrupt-parent = <&gpio7>;
interrupts = <16 IRQ_TYPE_LEVEL_LOW>;
vcc-supply = <&supply>;
#thermal-sensor-cells = <1>;
};
};
@@ -295,8 +295,6 @@ properties:
- mps,mp5990
# Monolithic Power Systems Inc. digital step-down converter mp9941
- mps,mp9941
# Monolithic Power Systems Inc. synchronous step-down converter mpq8785
- mps,mpq8785
# Temperature sensor with integrated fan control
- national,lm63
# Serial Interface ACPI-Compatible Microprocessor System Hardware Monitor
+26 -3
View File
@@ -37,9 +37,16 @@ arbitrary strings that ACPI provides with the meter. The measures/ directory
contains symlinks to the devices that this meter measures.
Some computers have the ability to enforce a power cap in hardware. If this is
the case, the `power[1-*]_cap` and related sysfs files will appear. When the
average power consumption exceeds the cap, an ACPI event will be broadcast on
the netlink event socket and a poll notification will be sent to the
the case, the `power[1-*]_cap` and related sysfs files will appear.
For information on enabling the power cap feature, refer to the description
of the "force_on_cap" option in the "Module Parameters" chapter.
To use the power cap feature properly, you need to set appropriate value
(in microWatts) to the `power[1-*]_cap` sysfs files.
The value must be within the range between the minimum value at `power[1-]_cap_min`
and the maximum value at `power[1-]_cap_max (both in microWatts)`.
When the average power consumption exceeds the cap, an ACPI event will be
broadcast on the netlink event socket and a poll notification will be sent to the
appropriate `power[1-*]_alarm` file to indicate that capping has begun, and the
hardware has taken action to reduce power consumption. Most likely this will
result in reduced performance.
@@ -52,3 +59,19 @@ follows:
`power[1-*]_cap` will be notified if the firmware changes the power cap.
`power[1-*]_interval` will be notified if the firmware changes the averaging
interval.
Module Parameters
-----------------
* force_cap_on: bool
Forcefully enable the power capping feature to specify
the upper limit of the system's power consumption.
By default, the driver's power capping feature is only
enabled on IBM products.
Therefore, on other systems that support power capping,
you will need to use the option to enable it.
Note: power capping is potentially unsafe feature.
Please check the platform specifications to make sure
that capping is supported before using this option.
+2
View File
@@ -4,6 +4,7 @@ Kernel driver asus_ec_sensors
=================================
Supported boards:
* MAXIMUS VI HERO
* PRIME X470-PRO
* PRIME X570-PRO
* PRIME X670E-PRO WIFI
@@ -20,6 +21,7 @@ Supported boards:
* ROG CROSSHAIR X670E GENE
* ROG MAXIMUS XI HERO
* ROG MAXIMUS XI HERO (WI-FI)
* ROG MAXIMUS Z690 FORMULA
* ROG STRIX B550-E GAMING
* ROG STRIX B550-I GAMING
* ROG STRIX X570-E GAMING
+15
View File
@@ -14,6 +14,12 @@ Supported chips:
Datasheet:
https://www.ti.com/lit/gpn/ina238
* Silergy SQ52206
Prefix: 'SQ52206'
Addresses: I2C 0x40 - 0x4f
Author: Nathan Rossi <nathan.rossi@digi.com>
Description
@@ -54,3 +60,12 @@ temp1_input Die temperature measurement (mC)
temp1_max Maximum die temperature threshold (mC)
temp1_max_alarm Maximum die temperature alarm
======================= =======================================================
Additional sysfs entries for sq52206
------------------------------------
======================= =======================================================
energy1_input Energy measurement (mJ)
power1_input_highest Peak Power (uW)
======================= =======================================================
+4
View File
@@ -106,6 +106,8 @@ Hardware Monitoring Kernel Drivers
jc42
k10temp
k8temp
kbatt
kfan
lan966x
lineage-pem
lm25066
@@ -125,6 +127,7 @@ Hardware Monitoring Kernel Drivers
lm95234
lm95245
lochnagar
lt3074
lt7182s
ltc2992
ltc2945
@@ -161,6 +164,7 @@ Hardware Monitoring Kernel Drivers
max6639
max6650
max6697
max77705
max8688
mc13783-adc
mc34vr500
+60
View File
@@ -0,0 +1,60 @@
.. SPDX-License-Identifier: GPL-2.0
Kernel driver kbatt
===================
Supported chips:
* KEBA battery monitoring controller (IP core in FPGA)
Prefix: 'kbatt'
Authors:
Gerhard Engleder <eg@keba.com>
Petar Bojanic <boja@keba.com>
Description
-----------
The KEBA battery monitoring controller is an IP core for FPGAs, which
monitors the health of a coin cell battery. The coin cell battery is
typically used to supply the RTC during power off to keep the current
time. E.g., the CP500 FPGA includes this IP core to monitor the coin cell
battery of PLCs and the corresponding cp500 driver creates an auxiliary
device for the kbatt driver.
This driver provides information about the coin cell battery health to
user space. Actually the user space shall be informed that the coin cell
battery is nearly empty and needs to be replaced.
The coin cell battery must be tested actively to get to know if its nearly
empty or not. Therefore, a load is put on the coin cell battery and the
resulting voltage is evaluated. This evaluation is done by some hard wired
analog logic, which compares the voltage to a defined limit. If the
voltage is above the limit, then the coin cell battery is assumed to be
ok. If the voltage is below the limit, then the coin cell battery is
nearly empty (or broken, removed, ...) and shall be replaced by a new one.
The KEBA battery monitoring controller allows to start the test of the
coin cell battery and to get the result if the voltage is above or below
the limit. The actual voltage is not available. Only the information if
the voltage is below a limit is available.
The test load, which is put on the coin cell battery for the health check,
is similar to the load during power off. Therefore, the lifetime of the
coin cell battery is reduced directly by the duration of each test. To
limit the negative impact to the lifetime the test is limited to at most
once every 10 seconds. The test load is put on the coin cell battery for
100ms. Thus, in worst case the coin cell battery lifetime is reduced by
1% of the uptime or 3.65 days per year. As the coin cell battery lasts
multiple years, this lifetime reduction negligible.
This driver only provides a single alarm attribute, which is raised when
the coin cell battery is nearly empty.
====================== ==== ===================================================
Attribute R/W Contents
====================== ==== ===================================================
in0_min_alarm R voltage of coin cell battery under load is below
limit
====================== ==== ===================================================
+39
View File
@@ -0,0 +1,39 @@
.. SPDX-License-Identifier: GPL-2.0
Kernel driver kfan
==================
Supported chips:
* KEBA fan controller (IP core in FPGA)
Prefix: 'kfan'
Authors:
Gerhard Engleder <eg@keba.com>
Petar Bojanic <boja@keba.com>
Description
-----------
The KEBA fan controller is an IP core for FPGAs, which monitors the health
and controls the speed of a fan. The fan is typically used to cool the CPU
and the whole device. E.g., the CP500 FPGA includes this IP core to monitor
and control the fan of PLCs and the corresponding cp500 driver creates an
auxiliary device for the kfan driver.
This driver provides information about the fan health to user space.
The user space shall be informed if the fan is removed or blocked.
Additionally, the speed in RPM is reported for fans with tacho signal.
For fan control PWM is supported. For PWM 255 equals 100%. None-regulable
fans can be turned on with PWM 255 and turned off with PWM 0.
====================== ==== ===================================================
Attribute R/W Contents
====================== ==== ===================================================
fan1_fault R Fan fault
fan1_input R Fan tachometer input (in RPM)
pwm1 RW Fan target duty cycle (0..255)
====================== ==== ===================================================
+72
View File
@@ -0,0 +1,72 @@
.. SPDX-License-Identifier: GPL-2.0
Kernel driver lt3074
====================
Supported chips:
* Analog Devices LT3074
Prefix: 'lt3074'
Addresses scanned: -
Datasheet: https://www.analog.com/en/products/lt3074.html
Authors: Cedric Encarnacion <cedricjustine.encarnacion@analog.com>
Description
-----------
This driver supports hardware monitoring for Analog Devices LT3074 Linear
Regulator with PMBus interface.
The LT3074 is a low voltage, ultra-low noise and ultra-fast transient
response linear regulator with PMBus serial interface. PMBus telemetry
feature provides information regarding the output voltage and current,
input voltage, bias voltage and die temperature.
The driver is a client driver to the core PMBus driver. Please see
Documentation/hwmon/pmbus.rst for details on PMBus client drivers.
Usage Notes
-----------
This driver does not auto-detect devices. You will have to instantiate
the devices explicitly. Please see Documentation/i2c/instantiating-devices.rst
for details.
Platform data support
---------------------
The driver supports standard PMBus driver platform data.
Sysfs entries
-------------
======================= =======================================================
in1_label "vin"
in1_input Measured input voltage
in1_max Input overvoltage warning limit
in1_max_alarm Input overvoltage warning status
in1_min Input undervoltage warning limit
in1_min_alarm Input undervoltage warning status
in2_label "vmon"
in2_input Measured bias voltage
in2_max Bias overvoltage warning limit
in2_min Bias undervoltage warning limit
in3_label "vout1"
in3_input Measured output voltage
in3_max Output overvoltage warning limit
in3_max_alarm Output overvoltage warning status
in3_min Output undervoltage warning limit
in3_min_alarm Output undervoltage warning status
curr1_label "iout1"
curr1_input Measured output current.
curr1_crit Output overcurrent fault limit
curr1_crit_alarm Output overcurrent fault status
temp1_input Measured temperature
temp1_max Maximum temperature limit
temp1_max_alarm Overtemperature warning status
======================= =======================================================
+22 -8
View File
@@ -3,6 +3,14 @@ Kernel driver max34440
Supported chips:
* ADI ADPM12160
Prefixes: 'adpm12160'
Addresses scanned: -
Datasheet: -
* Maxim MAX34440
Prefixes: 'max34440'
@@ -67,13 +75,14 @@ Author: Guenter Roeck <linux@roeck-us.net>
Description
-----------
This driver supports hardware monitoring for Maxim MAX34440 PMBus 6-Channel
Power-Supply Manager, MAX34441 PMBus 5-Channel Power-Supply Manager
and Intelligent Fan Controller, and MAX34446 PMBus Power-Supply Data Logger.
It also supports the MAX34451, MAX34460, and MAX34461 PMBus Voltage Monitor &
Sequencers. The MAX34451 supports monitoring voltage or current of 12 channels
based on GIN pins. The MAX34460 supports 12 voltage channels, and the MAX34461
supports 16 voltage channels.
This driver supports multiple devices: hardware monitoring for Maxim MAX34440
PMBus 6-Channel Power-Supply Manager, MAX34441 PMBus 5-Channel Power-Supply
Manager and Intelligent Fan Controller, and MAX34446 PMBus Power-Supply Data
Logger; PMBus Voltage Monitor and Sequencers for MAX34451, MAX34460, and
MAX34461; PMBus DC/DC Power Module ADPM12160. The MAX34451 supports monitoring
voltage or current of 12 channels based on GIN pins. The MAX34460 supports 12
voltage channels, and the MAX34461 supports 16 voltage channels. The ADPM1260
also monitors both input and output of voltage and current.
The driver is a client driver to the core PMBus driver. Please see
Documentation/hwmon/pmbus.rst for details on PMBus client drivers.
@@ -128,7 +137,10 @@ in[1-6]_highest Historical maximum voltage.
in[1-6]_reset_history Write any value to reset history.
======================= =======================================================
.. note:: MAX34446 only supports in[1-4].
.. note::
- MAX34446 only supports in[1-4].
- ADPM12160 only supports in[1-2]. Label is "vin1" and "vout1" respectively.
Curr
~~~~
@@ -150,6 +162,7 @@ curr[1-6]_reset_history Write any value to reset history.
- in6 and curr6 attributes only exist for MAX34440.
- MAX34446 only supports curr[1-4].
- For ADPM12160, curr[1] is "iin1" and curr[2-6] are "iout[1-5].
Power
~~~~~
@@ -185,6 +198,7 @@ temp[1-8]_reset_history Write any value to reset history.
.. note::
- temp7 and temp8 attributes only exist for MAX34440.
- MAX34446 only supports temp[1-3].
- ADPM12160 only supports temp[1].
.. note::
+39
View File
@@ -0,0 +1,39 @@
.. SPDX-License-Identifier: GPL-2.0
Kernel driver max77705
======================
Supported chips:
* Maxim Integrated MAX77705
Prefix: 'max77705'
Addresses scanned: none
Datasheet: Not available
Authors:
- Dzmitry Sankouski <dsankouski@gmail.com>
Description
-----------
The MAX77705 PMIC provides current and voltage measurements besides fuelgauge:
- chip input current
- system bus current and voltage
- VBYP voltage
Sysfs Attributes
----------------
================= ========================================
in1_label "vbyp"
in1_input Measured chip vbyp voltage
in2_label "vsys"
in2_input Measured chip system bus voltage
curr1_label "iin"
curr1_input Measured chip input current.
curr2_label "isys"
curr2_input Measured chip system bus current.
================= ========================================
+21 -6
View File
@@ -5,6 +5,8 @@ Kernel driver mpq8785
Supported chips:
* MPS MPM3695 family
* MPS MPM82504
* MPS MPQ8785
Prefix: 'mpq8785'
@@ -14,6 +16,22 @@ Author: Charles Hsu <ythsu0511@gmail.com>
Description
-----------
The MPM3695 family is a scalable, ultra-thin, fully integrated power module with
a PMBus interface. It offers a complete power solution that achieves up to
10A (-10 variant), 20A (-25 variant), 25A (-20 variant), 100A (-100 variant)
of output current with excellent load and line regulation across a wide input
voltage range. It operates at high efficiency over a wide load range, and can
be parallled to deliver higher current. Variants -10,-20 and -100 have different
voltage scale configuration register range (10 bits) than -25 version (11 bits).
The MPM82504 is a quad 25A, scalable, fully integrated power module with a PMBus
interface. The device offers a complete power solution that achieves up to 25A
per output channel. The MPM82504 has four output channels that can be paralleled
to provide 50A, 75A, or 100A of output current for flexible configurations.
The device can also operate in parallel with the MPM3695-100 and additional
MPM82504 devices to provide a higher output current. The MPM82504 operates
at high efficiency across a wide load range.
The MPQ8785 is a fully integrated, PMBus-compatible, high-frequency, synchronous
buck converter. The MPQ8785 offers a very compact solution that achieves up to
40A output current per phase, with excellent load and line regulation over a
@@ -23,19 +41,16 @@ output current load range.
The PMBus interface provides converter configurations and key parameters
monitoring.
The MPQ8785 adopts MPS's proprietary multi-phase digital constant-on-time (MCOT)
The devices adopts MPS's proprietary multi-phase digital constant-on-time (MCOT)
control, which provides fast transient response and eases loop stabilization.
The MCOT scheme also allows multiple MPQ8785 devices to be connected in parallel
with excellent current sharing and phase interleaving for high-current
The MCOT scheme also allows multiple devices or channels to be connected in
parallel with excellent current sharing and phase interleaving for high-current
applications.
Fully integrated protection features include over-current protection (OCP),
over-voltage protection (OVP), under-voltage protection (UVP), and
over-temperature protection (OTP).
The MPQ8785 requires a minimal number of readily available, standard external
components, and is available in a TLGA (5mmx6mm) package.
Device compliant with:
- PMBus rev 1.3 interface.
+16
View File
@@ -14156,6 +14156,15 @@ L: linux-scsi@vger.kernel.org
S: Maintained
F: drivers/scsi/sym53c8xx_2/
LT3074 HARDWARE MONITOR DRIVER
M: Cedric Encarnacion <cedricjustine.encarnacion@analog.com>
L: linux-hwmon@vger.kernel.org
S: Supported
W: https://ez.analog.com/linux-software-drivers
F: Documentation/devicetree/bindings/hwmon/pmbus/adi,lt3074.yaml
F: Documentation/hwmon/lt3074.rst
F: drivers/hwmon/pmbus/lt3074.c
LTC1660 DAC DRIVER
M: Marcus Folkesson <marcus.folkesson@gmail.com>
L: linux-iio@vger.kernel.org
@@ -18777,6 +18786,13 @@ S: Maintained
F: Documentation/hwmon/pc87427.rst
F: drivers/hwmon/pc87427.c
MAX77705 HARDWARE MONITORING DRIVER
M: Dzmitry Sankouski <dsankouski@gmail.com>
L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/max77705.rst
F: drivers/hwmon/max77705-hwmon.c
PCA9532 LED DRIVER
M: Riku Voipio <riku.voipio@iki.fi>
S: Maintained
+29
View File
@@ -335,6 +335,26 @@ config SENSORS_K10TEMP
This driver can also be built as a module. If so, the module
will be called k10temp.
config SENSORS_KBATT
tristate "KEBA battery controller support"
depends on KEBA_CP500
help
This driver supports the battery monitoring controller found in
KEBA system FPGA devices.
This driver can also be built as a module. If so, the module
will be called kbatt.
config SENSORS_KFAN
tristate "KEBA fan controller support"
depends on KEBA_CP500
help
This driver supports the fan controller found in KEBA system
FPGA devices.
This driver can also be built as a module. If so, the module
will be called kfan.
config SENSORS_FAM15H_POWER
tristate "AMD Family 15h processor power"
depends on X86 && PCI && CPU_SUP_AMD
@@ -1308,6 +1328,15 @@ config SENSORS_MAX31790
This driver can also be built as a module. If so, the module
will be called max31790.
config SENSORS_MAX77705
tristate "MAX77705 current and voltage sensor"
depends on MFD_MAX77705
help
If you say yes here you get support for MAX77705 sensors connected with I2C.
This driver can also be built as a module. If so, the module
will be called max77705-hwmon.
config SENSORS_MC34VR500
tristate "NXP MC34VR500 hardware monitoring driver"
depends on I2C
+3
View File
@@ -110,6 +110,8 @@ obj-$(CONFIG_SENSORS_IT87) += it87.o
obj-$(CONFIG_SENSORS_JC42) += jc42.o
obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o
obj-$(CONFIG_SENSORS_K10TEMP) += k10temp.o
obj-$(CONFIG_SENSORS_KBATT) += kbatt.o
obj-$(CONFIG_SENSORS_KFAN) += kfan.o
obj-$(CONFIG_SENSORS_LAN966X) += lan966x-hwmon.o
obj-$(CONFIG_SENSORS_LENOVO_EC) += lenovo-ec-sensors.o
obj-$(CONFIG_SENSORS_LINEAGE) += lineage-pem.o
@@ -161,6 +163,7 @@ obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
obj-$(CONFIG_SENSORS_MAX6697) += max6697.o
obj-$(CONFIG_SENSORS_MAX31790) += max31790.o
obj-$(CONFIG_MAX31827) += max31827.o
obj-$(CONFIG_SENSORS_MAX77705) += max77705-hwmon.o
obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o
obj-$(CONFIG_SENSORS_MC34VR500) += mc34vr500.o
obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o
+8 -8
View File
@@ -94,7 +94,7 @@ struct aht10_data {
unsigned int meas_size;
};
/**
/*
* aht10_init() - Initialize an AHT10/AHT20 chip
* @data: the data associated with this AHT10/AHT20 chip
* Return: 0 if successful, 1 if not
@@ -124,7 +124,7 @@ static int aht10_init(struct aht10_data *data)
return 0;
}
/**
/*
* aht10_polltime_expired() - check if the minimum poll interval has
* expired
* @data: the data containing the time to compare
@@ -140,7 +140,7 @@ static int aht10_polltime_expired(struct aht10_data *data)
DECLARE_CRC8_TABLE(crc8_table);
/**
/*
* crc8_check() - check crc of the sensor's measurements
* @raw_data: data frame received from sensor(including crc as the last byte)
* @count: size of the data frame
@@ -155,7 +155,7 @@ static int crc8_check(u8 *raw_data, int count)
return crc8(crc8_table, raw_data, count, CRC8_INIT_VALUE);
}
/**
/*
* aht10_read_values() - read and parse the raw data from the AHT10/AHT20
* @data: the struct aht10_data to use for the lock
* Return: 0 if successful, 1 if not
@@ -214,7 +214,7 @@ static int aht10_read_values(struct aht10_data *data)
return 0;
}
/**
/*
* aht10_interval_write() - store the given minimum poll interval.
* Return: 0 on success, -EINVAL if a value lower than the
* AHT10_MIN_POLL_INTERVAL is given
@@ -226,7 +226,7 @@ static ssize_t aht10_interval_write(struct aht10_data *data,
return 0;
}
/**
/*
* aht10_interval_read() - read the minimum poll interval
* in milliseconds
*/
@@ -237,7 +237,7 @@ static ssize_t aht10_interval_read(struct aht10_data *data,
return 0;
}
/**
/*
* aht10_temperature1_read() - read the temperature in millidegrees
*/
static int aht10_temperature1_read(struct aht10_data *data, long *val)
@@ -252,7 +252,7 @@ static int aht10_temperature1_read(struct aht10_data *data, long *val)
return 0;
}
/**
/*
* aht10_humidity1_read() - read the relative humidity in millipercent
*/
static int aht10_humidity1_read(struct aht10_data *data, long *val)
+45 -5
View File
@@ -23,9 +23,12 @@
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of_platform.h>
#include <linux/pwm.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <dt-bindings/pwm/pwm.h>
/*
* Addresses to scan.
*/
@@ -37,7 +40,7 @@ static const unsigned short normal_i2c[] = {0x18, 0x19, 0x1a, 0x2c, 0x2d, 0x2e,
* Insmod parameters
*/
static int pwminv; /*Inverted PWM output. */
static int pwminv = -1; /*Inverted PWM output. */
module_param(pwminv, int, 0444);
static int init = 1; /*Power-on initialization.*/
@@ -845,9 +848,43 @@ static int amc6821_detect(struct i2c_client *client, struct i2c_board_info *info
return 0;
}
static int amc6821_init_client(struct amc6821_data *data)
static enum pwm_polarity amc6821_pwm_polarity(struct i2c_client *client)
{
enum pwm_polarity polarity = PWM_POLARITY_NORMAL;
struct of_phandle_args args;
struct device_node *fan_np;
/*
* For backward compatibility, the pwminv module parameter takes
* always the precedence over any other device description
*/
if (pwminv == 0)
return PWM_POLARITY_NORMAL;
if (pwminv > 0)
return PWM_POLARITY_INVERSED;
fan_np = of_get_child_by_name(client->dev.of_node, "fan");
if (!fan_np)
return PWM_POLARITY_NORMAL;
if (of_parse_phandle_with_args(fan_np, "pwms", "#pwm-cells", 0, &args))
goto out;
of_node_put(args.np);
if (args.args_count != 2)
goto out;
if (args.args[1] & PWM_POLARITY_INVERTED)
polarity = PWM_POLARITY_INVERSED;
out:
of_node_put(fan_np);
return polarity;
}
static int amc6821_init_client(struct i2c_client *client, struct amc6821_data *data)
{
struct regmap *regmap = data->regmap;
u32 regval;
int err;
if (init) {
@@ -864,11 +901,14 @@ static int amc6821_init_client(struct amc6821_data *data)
if (err)
return err;
regval = AMC6821_CONF1_START;
if (amc6821_pwm_polarity(client) == PWM_POLARITY_INVERSED)
regval |= AMC6821_CONF1_PWMINV;
err = regmap_update_bits(regmap, AMC6821_REG_CONF1,
AMC6821_CONF1_THERMOVIE | AMC6821_CONF1_FANIE |
AMC6821_CONF1_START | AMC6821_CONF1_PWMINV,
AMC6821_CONF1_START |
(pwminv ? AMC6821_CONF1_PWMINV : 0));
regval);
if (err)
return err;
}
@@ -916,7 +956,7 @@ static int amc6821_probe(struct i2c_client *client)
"Failed to initialize regmap\n");
data->regmap = regmap;
err = amc6821_init_client(data);
err = amc6821_init_client(client, data);
if (err)
return err;
+45 -8
View File
@@ -169,7 +169,11 @@ enum board_family {
family_intel_600_series
};
/* All the known sensors for ASUS EC controllers */
/*
* All the known sensors for ASUS EC controllers. These arrays have to be sorted
* by the full ((bank << 8) + index) register index (see asus_ec_block_read() as
* to why).
*/
static const struct ec_sensor_info sensors_family_amd_400[] = {
[ec_sensor_temp_chipset] =
EC_SENSOR("Chipset", hwmon_temp, 1, 0x00, 0x3a),
@@ -183,10 +187,10 @@ static const struct ec_sensor_info sensors_family_amd_400[] = {
EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x3e),
[ec_sensor_in_cpu_core] =
EC_SENSOR("CPU Core", hwmon_in, 2, 0x00, 0xa2),
[ec_sensor_fan_cpu_opt] =
EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xbc),
[ec_sensor_fan_vrm_hs] =
EC_SENSOR("VRM HS", hwmon_fan, 2, 0x00, 0xb2),
[ec_sensor_fan_cpu_opt] =
EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xbc),
[ec_sensor_fan_chipset] =
/* no chipset fans in this generation */
EC_SENSOR("Chipset", hwmon_fan, 0, 0x00, 0x00),
@@ -194,10 +198,10 @@ static const struct ec_sensor_info sensors_family_amd_400[] = {
EC_SENSOR("Water_Flow", hwmon_fan, 2, 0x00, 0xb4),
[ec_sensor_curr_cpu] =
EC_SENSOR("CPU", hwmon_curr, 1, 0x00, 0xf4),
[ec_sensor_temp_water_in] =
EC_SENSOR("Water_In", hwmon_temp, 1, 0x01, 0x0d),
[ec_sensor_temp_water_out] =
EC_SENSOR("Water_Out", hwmon_temp, 1, 0x01, 0x0b),
[ec_sensor_temp_water_in] =
EC_SENSOR("Water_In", hwmon_temp, 1, 0x01, 0x0d),
};
static const struct ec_sensor_info sensors_family_amd_500[] = {
@@ -239,19 +243,20 @@ static const struct ec_sensor_info sensors_family_amd_500[] = {
static const struct ec_sensor_info sensors_family_amd_600[] = {
[ec_sensor_temp_cpu] = EC_SENSOR("CPU", hwmon_temp, 1, 0x00, 0x30),
[ec_sensor_temp_cpu_package] = EC_SENSOR("CPU Package", hwmon_temp, 1, 0x00, 0x31),
[ec_sensor_temp_cpu_package] =
EC_SENSOR("CPU Package", hwmon_temp, 1, 0x00, 0x31),
[ec_sensor_temp_mb] =
EC_SENSOR("Motherboard", hwmon_temp, 1, 0x00, 0x32),
[ec_sensor_temp_vrm] =
EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x33),
[ec_sensor_temp_t_sensor] =
EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x36),
[ec_sensor_fan_cpu_opt] =
EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xb0),
[ec_sensor_temp_water_in] =
EC_SENSOR("Water_In", hwmon_temp, 1, 0x01, 0x00),
[ec_sensor_temp_water_out] =
EC_SENSOR("Water_Out", hwmon_temp, 1, 0x01, 0x01),
[ec_sensor_fan_cpu_opt] =
EC_SENSOR("CPU_Opt", hwmon_fan, 2, 0x00, 0xb0),
};
static const struct ec_sensor_info sensors_family_intel_300[] = {
@@ -278,6 +283,14 @@ static const struct ec_sensor_info sensors_family_intel_600[] = {
[ec_sensor_temp_t_sensor] =
EC_SENSOR("T_Sensor", hwmon_temp, 1, 0x00, 0x3d),
[ec_sensor_temp_vrm] = EC_SENSOR("VRM", hwmon_temp, 1, 0x00, 0x3e),
[ec_sensor_fan_water_flow] =
EC_SENSOR("Water_Flow", hwmon_fan, 2, 0x00, 0xbe),
[ec_sensor_temp_water_in] =
EC_SENSOR("Water_In", hwmon_temp, 1, 0x01, 0x00),
[ec_sensor_temp_water_out] =
EC_SENSOR("Water_Out", hwmon_temp, 1, 0x01, 0x01),
[ec_sensor_temp_water_block_in] =
EC_SENSOR("Water_Block_In", hwmon_temp, 1, 0x01, 0x02),
};
/* Shortcuts for common combinations */
@@ -300,6 +313,15 @@ struct ec_board_info {
enum board_family family;
};
static const struct ec_board_info board_info_maximus_vi_hero = {
.sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
SENSOR_TEMP_T_SENSOR |
SENSOR_TEMP_VRM | SENSOR_SET_TEMP_WATER |
SENSOR_FAN_CPU_OPT | SENSOR_FAN_WATER_FLOW,
.mutex_path = ACPI_GLOBAL_LOCK_PSEUDO_PATH,
.family = family_intel_300_series,
};
static const struct ec_board_info board_info_prime_x470_pro = {
.sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM |
@@ -402,6 +424,13 @@ static const struct ec_board_info board_info_maximus_xi_hero = {
.family = family_intel_300_series,
};
static const struct ec_board_info board_info_maximus_z690_formula = {
.sensors = SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM |
SENSOR_SET_TEMP_WATER | SENSOR_FAN_WATER_FLOW,
.mutex_path = ASUS_HW_ACCESS_MUTEX_RMTW_ASMX,
.family = family_intel_600_series,
};
static const struct ec_board_info board_info_crosshair_viii_impact = {
.sensors = SENSOR_SET_TEMP_CHIPSET_CPU_MB |
SENSOR_TEMP_T_SENSOR | SENSOR_TEMP_VRM |
@@ -507,6 +536,8 @@ static const struct ec_board_info board_info_tuf_gaming_x670e_plus = {
}
static const struct dmi_system_id dmi_table[] = {
DMI_EXACT_MATCH_ASUS_BOARD_NAME("MAXIMUS VI HERO",
&board_info_maximus_vi_hero),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("PRIME X470-PRO",
&board_info_prime_x470_pro),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("PRIME X570-PRO",
@@ -537,6 +568,8 @@ static const struct dmi_system_id dmi_table[] = {
&board_info_maximus_xi_hero),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG MAXIMUS XI HERO (WI-FI)",
&board_info_maximus_xi_hero),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG MAXIMUS Z690 FORMULA",
&board_info_maximus_z690_formula),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG CROSSHAIR VIII IMPACT",
&board_info_crosshair_viii_impact),
DMI_EXACT_MATCH_ASUS_BOARD_NAME("ROG STRIX B550-E GAMING",
@@ -933,6 +966,10 @@ static int asus_ec_hwmon_read_string(struct device *dev,
{
struct ec_sensors_data *state = dev_get_drvdata(dev);
int sensor_index = find_ec_sensor_index(state, type, channel);
if (sensor_index < 0)
return sensor_index;
*str = get_sensor_info(state, sensor_index)->label;
return 0;
+7
View File
@@ -1273,6 +1273,13 @@ static const struct dmi_system_id i8k_dmi_table[] __initconst = {
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "OptiPlex 7060"),
},
},
{
.ident = "Dell OptiPlex 7050",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "OptiPlex 7050"),
},
},
{
.ident = "Dell Precision",
.matches = {
+87 -16
View File
@@ -20,6 +20,9 @@
#include <linux/gpio/consumer.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/thermal.h>
struct gpio_fan_speed {
@@ -42,6 +45,7 @@ struct gpio_fan_data {
bool pwm_enable;
struct gpio_desc *alarm_gpio;
struct work_struct alarm_work;
struct regulator *supply;
};
/*
@@ -125,13 +129,32 @@ static int __get_fan_ctrl(struct gpio_fan_data *fan_data)
}
/* Must be called with fan_data->lock held, except during initialization. */
static void set_fan_speed(struct gpio_fan_data *fan_data, int speed_index)
static int set_fan_speed(struct gpio_fan_data *fan_data, int speed_index)
{
if (fan_data->speed_index == speed_index)
return;
return 0;
if (fan_data->speed_index == 0 && speed_index > 0) {
int ret;
ret = pm_runtime_resume_and_get(fan_data->dev);
if (ret < 0)
return ret;
}
__set_fan_ctrl(fan_data, fan_data->speed[speed_index].ctrl_val);
if (fan_data->speed_index > 0 && speed_index == 0) {
int ret;
ret = pm_runtime_put_sync(fan_data->dev);
if (ret < 0)
return ret;
}
fan_data->speed_index = speed_index;
return 0;
}
static int get_fan_speed_index(struct gpio_fan_data *fan_data)
@@ -176,7 +199,7 @@ static ssize_t pwm1_store(struct device *dev, struct device_attribute *attr,
struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
unsigned long pwm;
int speed_index;
int ret = count;
int ret;
if (kstrtoul(buf, 10, &pwm) || pwm > 255)
return -EINVAL;
@@ -189,12 +212,12 @@ static ssize_t pwm1_store(struct device *dev, struct device_attribute *attr,
}
speed_index = DIV_ROUND_UP(pwm * (fan_data->num_speed - 1), 255);
set_fan_speed(fan_data, speed_index);
ret = set_fan_speed(fan_data, speed_index);
exit_unlock:
mutex_unlock(&fan_data->lock);
return ret;
return ret ? ret : count;
}
static ssize_t pwm1_enable_show(struct device *dev,
@@ -211,6 +234,7 @@ static ssize_t pwm1_enable_store(struct device *dev,
{
struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
unsigned long val;
int ret = 0;
if (kstrtoul(buf, 10, &val) || val > 1)
return -EINVAL;
@@ -224,11 +248,11 @@ static ssize_t pwm1_enable_store(struct device *dev,
/* Disable manual control mode: set fan at full speed. */
if (val == 0)
set_fan_speed(fan_data, fan_data->num_speed - 1);
ret = set_fan_speed(fan_data, fan_data->num_speed - 1);
mutex_unlock(&fan_data->lock);
return count;
return ret ? ret : count;
}
static ssize_t pwm1_mode_show(struct device *dev,
@@ -279,7 +303,7 @@ static ssize_t set_rpm(struct device *dev, struct device_attribute *attr,
goto exit_unlock;
}
set_fan_speed(fan_data, rpm_to_speed_index(fan_data, rpm));
ret = set_fan_speed(fan_data, rpm_to_speed_index(fan_data, rpm));
exit_unlock:
mutex_unlock(&fan_data->lock);
@@ -386,6 +410,7 @@ static int gpio_fan_set_cur_state(struct thermal_cooling_device *cdev,
unsigned long state)
{
struct gpio_fan_data *fan_data = cdev->devdata;
int ret;
if (!fan_data)
return -EINVAL;
@@ -395,11 +420,11 @@ static int gpio_fan_set_cur_state(struct thermal_cooling_device *cdev,
mutex_lock(&fan_data->lock);
set_fan_speed(fan_data, state);
ret = set_fan_speed(fan_data, state);
mutex_unlock(&fan_data->lock);
return 0;
return ret;
}
static const struct thermal_cooling_device_ops gpio_fan_cool_ops = {
@@ -499,6 +524,8 @@ static void gpio_fan_stop(void *data)
mutex_lock(&fan_data->lock);
set_fan_speed(data, 0);
mutex_unlock(&fan_data->lock);
pm_runtime_disable(fan_data->dev);
}
static int gpio_fan_probe(struct platform_device *pdev)
@@ -521,6 +548,11 @@ static int gpio_fan_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, fan_data);
mutex_init(&fan_data->lock);
fan_data->supply = devm_regulator_get(dev, "fan");
if (IS_ERR(fan_data->supply))
return dev_err_probe(dev, PTR_ERR(fan_data->supply),
"Failed to get fan-supply");
/* Configure control GPIOs if available. */
if (fan_data->gpios && fan_data->num_gpios > 0) {
if (!fan_data->speed || fan_data->num_speed <= 1)
@@ -548,6 +580,17 @@ static int gpio_fan_probe(struct platform_device *pdev)
return err;
}
pm_runtime_set_suspended(&pdev->dev);
pm_runtime_enable(&pdev->dev);
/* If current GPIO state is active, mark RPM as active as well */
if (fan_data->speed_index > 0) {
int ret;
ret = pm_runtime_resume_and_get(&pdev->dev);
if (ret)
return ret;
}
/* Optional cooling device register for Device tree platforms */
fan_data->cdev = devm_thermal_of_cooling_device_register(dev, np,
"gpio-fan", fan_data, &gpio_fan_cool_ops);
@@ -565,41 +608,69 @@ static void gpio_fan_shutdown(struct platform_device *pdev)
set_fan_speed(fan_data, 0);
}
static int gpio_fan_runtime_suspend(struct device *dev)
{
struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
int ret = 0;
if (fan_data->supply)
ret = regulator_disable(fan_data->supply);
return ret;
}
static int gpio_fan_runtime_resume(struct device *dev)
{
struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
int ret = 0;
if (fan_data->supply)
ret = regulator_enable(fan_data->supply);
return ret;
}
static int gpio_fan_suspend(struct device *dev)
{
struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
int ret = 0;
if (fan_data->gpios) {
fan_data->resume_speed = fan_data->speed_index;
mutex_lock(&fan_data->lock);
set_fan_speed(fan_data, 0);
ret = set_fan_speed(fan_data, 0);
mutex_unlock(&fan_data->lock);
}
return 0;
return ret;
}
static int gpio_fan_resume(struct device *dev)
{
struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
int ret = 0;
if (fan_data->gpios) {
mutex_lock(&fan_data->lock);
set_fan_speed(fan_data, fan_data->resume_speed);
ret = set_fan_speed(fan_data, fan_data->resume_speed);
mutex_unlock(&fan_data->lock);
}
return 0;
return ret;
}
static DEFINE_SIMPLE_DEV_PM_OPS(gpio_fan_pm, gpio_fan_suspend, gpio_fan_resume);
static const struct dev_pm_ops gpio_fan_pm = {
RUNTIME_PM_OPS(gpio_fan_runtime_suspend,
gpio_fan_runtime_resume, NULL)
SYSTEM_SLEEP_PM_OPS(gpio_fan_suspend, gpio_fan_resume)
};
static struct platform_driver gpio_fan_driver = {
.probe = gpio_fan_probe,
.shutdown = gpio_fan_shutdown,
.driver = {
.name = "gpio-fan",
.pm = pm_sleep_ptr(&gpio_fan_pm),
.pm = pm_ptr(&gpio_fan_pm),
.of_match_table = of_gpio_fan_match,
},
};
+180 -34
View File
@@ -21,11 +21,14 @@
#define INA238_CONFIG 0x0
#define INA238_ADC_CONFIG 0x1
#define INA238_SHUNT_CALIBRATION 0x2
#define SQ52206_SHUNT_TEMPCO 0x3
#define INA238_SHUNT_VOLTAGE 0x4
#define INA238_BUS_VOLTAGE 0x5
#define INA238_DIE_TEMP 0x6
#define INA238_CURRENT 0x7
#define INA238_POWER 0x8
#define SQ52206_ENERGY 0x9
#define SQ52206_CHARGE 0xa
#define INA238_DIAG_ALERT 0xb
#define INA238_SHUNT_OVER_VOLTAGE 0xc
#define INA238_SHUNT_UNDER_VOLTAGE 0xd
@@ -33,9 +36,12 @@
#define INA238_BUS_UNDER_VOLTAGE 0xf
#define INA238_TEMP_LIMIT 0x10
#define INA238_POWER_LIMIT 0x11
#define SQ52206_POWER_PEAK 0x20
#define INA238_DEVICE_ID 0x3f /* not available on INA237 */
#define INA238_CONFIG_ADCRANGE BIT(4)
#define SQ52206_CONFIG_ADCRANGE_HIGH BIT(4)
#define SQ52206_CONFIG_ADCRANGE_LOW BIT(3)
#define INA238_DIAG_ALERT_TMPOL BIT(7)
#define INA238_DIAG_ALERT_SHNTOL BIT(6)
@@ -44,12 +50,13 @@
#define INA238_DIAG_ALERT_BUSUL BIT(3)
#define INA238_DIAG_ALERT_POL BIT(2)
#define INA238_REGISTERS 0x11
#define INA238_REGISTERS 0x20
#define INA238_RSHUNT_DEFAULT 10000 /* uOhm */
/* Default configuration of device on reset. */
#define INA238_CONFIG_DEFAULT 0
#define SQ52206_CONFIG_DEFAULT 0x0005
/* 16 sample averaging, 1052us conversion time, continuous mode */
#define INA238_ADC_CONFIG_DEFAULT 0xfb6a
/* Configure alerts to be based on averaged value (SLOWALERT) */
@@ -87,14 +94,19 @@
* shunt = 0x4000 / (819.2 * 10^6) / 0.001 = 20000 uOhms (with 1mA/lsb)
*
* Current (mA) = register value * 20000 / rshunt / 4 * gain
* Power (W) = 0.2 * register value * 20000 / rshunt / 4 * gain
* Power (mW) = 0.2 * register value * 20000 / rshunt / 4 * gain
* (Specific for SQ52206)
* Power (mW) = 0.24 * register value * 20000 / rshunt / 4 * gain
* Energy (mJ) = 16 * 0.24 * register value * 20000 / rshunt / 4 * gain
*/
#define INA238_CALIBRATION_VALUE 16384
#define INA238_FIXED_SHUNT 20000
#define INA238_SHUNT_VOLTAGE_LSB 5 /* 5 uV/lsb */
#define INA238_BUS_VOLTAGE_LSB 3125 /* 3.125 mV/lsb */
#define INA238_DIE_TEMP_LSB 125 /* 125 mC/lsb */
#define INA238_DIE_TEMP_LSB 1250000 /* 125.0000 mC/lsb */
#define SQ52206_BUS_VOLTAGE_LSB 3750 /* 3.75 mV/lsb */
#define SQ52206_DIE_TEMP_LSB 78125 /* 7.8125 mC/lsb */
static const struct regmap_config ina238_regmap_config = {
.max_register = INA238_REGISTERS,
@@ -102,7 +114,20 @@ static const struct regmap_config ina238_regmap_config = {
.val_bits = 16,
};
enum ina238_ids { ina238, ina237, sq52206 };
struct ina238_config {
bool has_power_highest; /* chip detection power peak */
bool has_energy; /* chip detection energy */
u8 temp_shift; /* fixed parameters for temp calculate */
u32 power_calculate_factor; /* fixed parameters for power calculate */
u16 config_default; /* Power-on default state */
int bus_voltage_lsb; /* use for temperature calculate, uV/lsb */
int temp_lsb; /* use for temperature calculate */
};
struct ina238_data {
const struct ina238_config *config;
struct i2c_client *client;
struct mutex config_lock;
struct regmap *regmap;
@@ -110,6 +135,36 @@ struct ina238_data {
int gain;
};
static const struct ina238_config ina238_config[] = {
[ina238] = {
.has_energy = false,
.has_power_highest = false,
.temp_shift = 4,
.power_calculate_factor = 20,
.config_default = INA238_CONFIG_DEFAULT,
.bus_voltage_lsb = INA238_BUS_VOLTAGE_LSB,
.temp_lsb = INA238_DIE_TEMP_LSB,
},
[ina237] = {
.has_energy = false,
.has_power_highest = false,
.temp_shift = 4,
.power_calculate_factor = 20,
.config_default = INA238_CONFIG_DEFAULT,
.bus_voltage_lsb = INA238_BUS_VOLTAGE_LSB,
.temp_lsb = INA238_DIE_TEMP_LSB,
},
[sq52206] = {
.has_energy = true,
.has_power_highest = true,
.temp_shift = 0,
.power_calculate_factor = 24,
.config_default = SQ52206_CONFIG_DEFAULT,
.bus_voltage_lsb = SQ52206_BUS_VOLTAGE_LSB,
.temp_lsb = SQ52206_DIE_TEMP_LSB,
},
};
static int ina238_read_reg24(const struct i2c_client *client, u8 reg, u32 *val)
{
u8 data[3];
@@ -126,6 +181,24 @@ static int ina238_read_reg24(const struct i2c_client *client, u8 reg, u32 *val)
return 0;
}
static int ina238_read_reg40(const struct i2c_client *client, u8 reg, u64 *val)
{
u8 data[5];
u32 low;
int err;
/* 40-bit register read */
err = i2c_smbus_read_i2c_block_data(client, reg, 5, data);
if (err < 0)
return err;
if (err != 5)
return -EIO;
low = (data[1] << 24) | (data[2] << 16) | (data[3] << 8) | data[4];
*val = ((long long)data[0] << 32) | low;
return 0;
}
static int ina238_read_in(struct device *dev, u32 attr, int channel,
long *val)
{
@@ -197,10 +270,10 @@ static int ina238_read_in(struct device *dev, u32 attr, int channel,
regval = (s16)regval;
if (channel == 0)
/* gain of 1 -> LSB / 4 */
*val = (regval * INA238_SHUNT_VOLTAGE_LSB) /
(1000 * (4 - data->gain + 1));
*val = (regval * INA238_SHUNT_VOLTAGE_LSB) *
data->gain / (1000 * 4);
else
*val = (regval * INA238_BUS_VOLTAGE_LSB) / 1000;
*val = (regval * data->config->bus_voltage_lsb) / 1000;
break;
case hwmon_in_max_alarm:
case hwmon_in_min_alarm:
@@ -225,8 +298,8 @@ static int ina238_write_in(struct device *dev, u32 attr, int channel,
case 0:
/* signed value, clamp to max range +/-163 mV */
regval = clamp_val(val, -163, 163);
regval = (regval * 1000 * (4 - data->gain + 1)) /
INA238_SHUNT_VOLTAGE_LSB;
regval = (regval * 1000 * 4) /
(INA238_SHUNT_VOLTAGE_LSB * data->gain);
regval = clamp_val(regval, S16_MIN, S16_MAX);
switch (attr) {
@@ -242,7 +315,7 @@ static int ina238_write_in(struct device *dev, u32 attr, int channel,
case 1:
/* signed value, positive values only. Clamp to max 102.396 V */
regval = clamp_val(val, 0, 102396);
regval = (regval * 1000) / INA238_BUS_VOLTAGE_LSB;
regval = (regval * 1000) / data->config->bus_voltage_lsb;
regval = clamp_val(regval, 0, S16_MAX);
switch (attr) {
@@ -297,8 +370,19 @@ static int ina238_read_power(struct device *dev, u32 attr, long *val)
return err;
/* Fixed 1mA lsb, scaled by 1000000 to have result in uW */
power = div_u64(regval * 1000ULL * INA238_FIXED_SHUNT *
data->gain, 20 * data->rshunt);
power = div_u64(regval * 1000ULL * INA238_FIXED_SHUNT * data->gain *
data->config->power_calculate_factor, 4 * 100 * data->rshunt);
/* Clamp value to maximum value of long */
*val = clamp_val(power, 0, LONG_MAX);
break;
case hwmon_power_input_highest:
err = ina238_read_reg24(data->client, SQ52206_POWER_PEAK, &regval);
if (err)
return err;
/* Fixed 1mA lsb, scaled by 1000000 to have result in uW */
power = div_u64(regval * 1000ULL * INA238_FIXED_SHUNT * data->gain *
data->config->power_calculate_factor, 4 * 100 * data->rshunt);
/* Clamp value to maximum value of long */
*val = clamp_val(power, 0, LONG_MAX);
break;
@@ -311,8 +395,8 @@ static int ina238_read_power(struct device *dev, u32 attr, long *val)
* Truncated 24-bit compare register, lower 8-bits are
* truncated. Same conversion to/from uW as POWER register.
*/
power = div_u64((regval << 8) * 1000ULL * INA238_FIXED_SHUNT *
data->gain, 20 * data->rshunt);
power = div_u64((regval << 8) * 1000ULL * INA238_FIXED_SHUNT * data->gain *
data->config->power_calculate_factor, 4 * 100 * data->rshunt);
/* Clamp value to maximum value of long */
*val = clamp_val(power, 0, LONG_MAX);
break;
@@ -344,8 +428,8 @@ static int ina238_write_power(struct device *dev, u32 attr, long val)
* register.
*/
regval = clamp_val(val, 0, LONG_MAX);
regval = div_u64(val * 20ULL * data->rshunt,
1000ULL * INA238_FIXED_SHUNT * data->gain);
regval = div_u64(val * 4 * 100 * data->rshunt, data->config->power_calculate_factor *
1000ULL * INA238_FIXED_SHUNT * data->gain);
regval = clamp_val(regval >> 8, 0, U16_MAX);
return regmap_write(data->regmap, INA238_POWER_LIMIT, regval);
@@ -362,17 +446,17 @@ static int ina238_read_temp(struct device *dev, u32 attr, long *val)
err = regmap_read(data->regmap, INA238_DIE_TEMP, &regval);
if (err)
return err;
/* Signed, bits 15-4 of register, result in mC */
*val = ((s16)regval >> 4) * INA238_DIE_TEMP_LSB;
/* Signed, result in mC */
*val = div_s64(((s64)((s16)regval) >> data->config->temp_shift) *
(s64)data->config->temp_lsb, 10000);
break;
case hwmon_temp_max:
err = regmap_read(data->regmap, INA238_TEMP_LIMIT, &regval);
if (err)
return err;
/* Signed, bits 15-4 of register, result in mC */
*val = ((s16)regval >> 4) * INA238_DIE_TEMP_LSB;
/* Signed, result in mC */
*val = div_s64(((s64)((s16)regval) >> data->config->temp_shift) *
(s64)data->config->temp_lsb, 10000);
break;
case hwmon_temp_max_alarm:
err = regmap_read(data->regmap, INA238_DIAG_ALERT, &regval);
@@ -396,13 +480,33 @@ static int ina238_write_temp(struct device *dev, u32 attr, long val)
if (attr != hwmon_temp_max)
return -EOPNOTSUPP;
/* Signed, bits 15-4 of register */
regval = (val / INA238_DIE_TEMP_LSB) << 4;
regval = clamp_val(regval, S16_MIN, S16_MAX) & 0xfff0;
/* Signed */
regval = clamp_val(val, -40000, 125000);
regval = div_s64(val * 10000, data->config->temp_lsb) << data->config->temp_shift;
regval = clamp_val(regval, S16_MIN, S16_MAX) & (0xffff << data->config->temp_shift);
return regmap_write(data->regmap, INA238_TEMP_LIMIT, regval);
}
static ssize_t energy1_input_show(struct device *dev,
struct device_attribute *da, char *buf)
{
struct ina238_data *data = dev_get_drvdata(dev);
int ret;
u64 regval;
u64 energy;
ret = ina238_read_reg40(data->client, SQ52206_ENERGY, &regval);
if (ret)
return ret;
/* result in mJ */
energy = div_u64(regval * INA238_FIXED_SHUNT * data->gain * 16 *
data->config->power_calculate_factor, 4 * 100 * data->rshunt);
return sysfs_emit(buf, "%llu\n", energy);
}
static int ina238_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
@@ -422,7 +526,7 @@ static int ina238_read(struct device *dev, enum hwmon_sensor_types type,
}
static int ina238_write(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long val)
u32 attr, int channel, long val)
{
struct ina238_data *data = dev_get_drvdata(dev);
int err;
@@ -452,6 +556,9 @@ static umode_t ina238_is_visible(const void *drvdata,
enum hwmon_sensor_types type,
u32 attr, int channel)
{
const struct ina238_data *data = drvdata;
bool has_power_highest = data->config->has_power_highest;
switch (type) {
case hwmon_in:
switch (attr) {
@@ -479,6 +586,10 @@ static umode_t ina238_is_visible(const void *drvdata,
return 0444;
case hwmon_power_max:
return 0644;
case hwmon_power_input_highest:
if (has_power_highest)
return 0444;
return 0;
default:
return 0;
}
@@ -512,7 +623,8 @@ static const struct hwmon_channel_info * const ina238_info[] = {
HWMON_C_INPUT),
HWMON_CHANNEL_INFO(power,
/* 0: power */
HWMON_P_INPUT | HWMON_P_MAX | HWMON_P_MAX_ALARM),
HWMON_P_INPUT | HWMON_P_MAX |
HWMON_P_MAX_ALARM | HWMON_P_INPUT_HIGHEST),
HWMON_CHANNEL_INFO(temp,
/* 0: die temperature */
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_ALARM),
@@ -530,20 +642,35 @@ static const struct hwmon_chip_info ina238_chip_info = {
.info = ina238_info,
};
/* energy attributes are 5 bytes wide so we need u64 */
static DEVICE_ATTR_RO(energy1_input);
static struct attribute *ina238_attrs[] = {
&dev_attr_energy1_input.attr,
NULL,
};
ATTRIBUTE_GROUPS(ina238);
static int ina238_probe(struct i2c_client *client)
{
struct ina2xx_platform_data *pdata = dev_get_platdata(&client->dev);
struct device *dev = &client->dev;
struct device *hwmon_dev;
struct ina238_data *data;
enum ina238_ids chip;
int config;
int ret;
chip = (uintptr_t)i2c_get_match_data(client);
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->client = client;
/* set the device type */
data->config = &ina238_config[chip];
mutex_init(&data->config_lock);
data->regmap = devm_regmap_init_i2c(client, &ina238_regmap_config);
@@ -564,15 +691,21 @@ static int ina238_probe(struct i2c_client *client)
/* load shunt gain value */
if (device_property_read_u32(dev, "ti,shunt-gain", &data->gain) < 0)
data->gain = 4; /* Default of ADCRANGE = 0 */
if (data->gain != 1 && data->gain != 4) {
if (data->gain != 1 && data->gain != 2 && data->gain != 4) {
dev_err(dev, "invalid shunt gain value %u\n", data->gain);
return -EINVAL;
}
/* Setup CONFIG register */
config = INA238_CONFIG_DEFAULT;
if (data->gain == 1)
config = data->config->config_default;
if (chip == sq52206) {
if (data->gain == 1)
config |= SQ52206_CONFIG_ADCRANGE_HIGH; /* ADCRANGE = 10/11 is /1 */
else if (data->gain == 2)
config |= SQ52206_CONFIG_ADCRANGE_LOW; /* ADCRANGE = 01 is /2 */
} else if (data->gain == 1) {
config |= INA238_CONFIG_ADCRANGE; /* ADCRANGE = 1 is /1 */
}
ret = regmap_write(data->regmap, INA238_CONFIG, config);
if (ret < 0) {
dev_err(dev, "error configuring the device: %d\n", ret);
@@ -605,7 +738,8 @@ static int ina238_probe(struct i2c_client *client)
hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data,
&ina238_chip_info,
NULL);
data->config->has_energy ?
ina238_groups : NULL);
if (IS_ERR(hwmon_dev))
return PTR_ERR(hwmon_dev);
@@ -616,15 +750,27 @@ static int ina238_probe(struct i2c_client *client)
}
static const struct i2c_device_id ina238_id[] = {
{ "ina238" },
{ "ina237", ina237 },
{ "ina238", ina238 },
{ "sq52206", sq52206 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ina238_id);
static const struct of_device_id __maybe_unused ina238_of_match[] = {
{ .compatible = "ti,ina237" },
{ .compatible = "ti,ina238" },
{ },
{
.compatible = "ti,ina237",
.data = (void *)ina237
},
{
.compatible = "ti,ina238",
.data = (void *)ina238
},
{
.compatible = "silergy,sq52206",
.data = (void *)sq52206
},
{ }
};
MODULE_DEVICE_TABLE(of, ina238_of_match);
+6 -2
View File
@@ -959,8 +959,12 @@ static int ina2xx_probe(struct i2c_client *client)
return PTR_ERR(data->regmap);
}
ret = devm_regulator_get_enable(dev, "vs");
if (ret)
/*
* Regulator core returns -ENODEV if the 'vs' is not available.
* Hence the check for -ENODEV return code is necessary.
*/
ret = devm_regulator_get_enable_optional(dev, "vs");
if (ret < 0 && ret != -ENODEV)
return dev_err_probe(dev, ret, "failed to enable vs regulator\n");
ret = ina2xx_init(dev, data);
+5 -3
View File
@@ -154,6 +154,7 @@ static int isl28022_read_current(struct device *dev, u32 attr, long *val)
struct isl28022_data *data = dev_get_drvdata(dev);
unsigned int regval;
int err;
u16 sign_bit;
switch (attr) {
case hwmon_curr_input:
@@ -161,8 +162,9 @@ static int isl28022_read_current(struct device *dev, u32 attr, long *val)
ISL28022_REG_CURRENT, &regval);
if (err < 0)
return err;
*val = ((long)regval * 1250L * (long)data->gain) /
(long)data->shunt;
sign_bit = (regval >> 15) & 0x01;
*val = (((long)(((u16)regval) & 0x7FFF) - (sign_bit * 32768)) *
1250L * (long)data->gain) / (long)data->shunt;
break;
default:
return -EOPNOTSUPP;
@@ -301,7 +303,7 @@ static const struct regmap_config isl28022_regmap_config = {
.writeable_reg = isl28022_is_writeable_reg,
.volatile_reg = isl28022_is_volatile_reg,
.val_format_endian = REGMAP_ENDIAN_BIG,
.cache_type = REGCACHE_RBTREE,
.cache_type = REGCACHE_MAPLE,
.use_single_read = true,
.use_single_write = true,
};
+7
View File
@@ -503,6 +503,13 @@ 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) {
switch (boot_cpu_data.x86_model) {
case 0x40 ... 0x4f: /* Zen5 Ryzen Desktop */
data->ccd_offset = 0x308;
k10temp_get_ccd_support(data, 8);
break;
}
}
for (i = 0; i < ARRAY_SIZE(tctl_offset_table); i++) {
+147
View File
@@ -0,0 +1,147 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2025 KEBA Industrial Automation GmbH
*
* Driver for KEBA battery monitoring controller FPGA IP core
*/
#include <linux/hwmon.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/auxiliary_bus.h>
#include <linux/misc/keba.h>
#include <linux/mutex.h>
#define KBATT "kbatt"
#define KBATT_CONTROL_REG 0x4
#define KBATT_CONTROL_BAT_TEST 0x01
#define KBATT_STATUS_REG 0x8
#define KBATT_STATUS_BAT_OK 0x01
#define KBATT_MAX_UPD_INTERVAL (10 * HZ)
#define KBATT_SETTLE_TIME_US (100 * USEC_PER_MSEC)
struct kbatt {
/* update lock */
struct mutex lock;
void __iomem *base;
unsigned long next_update; /* in jiffies */
bool alarm;
};
static bool kbatt_alarm(struct kbatt *kbatt)
{
mutex_lock(&kbatt->lock);
if (!kbatt->next_update || time_after(jiffies, kbatt->next_update)) {
/* switch load on */
iowrite8(KBATT_CONTROL_BAT_TEST,
kbatt->base + KBATT_CONTROL_REG);
/* wait some time to let things settle */
fsleep(KBATT_SETTLE_TIME_US);
/* check battery state */
if (ioread8(kbatt->base + KBATT_STATUS_REG) &
KBATT_STATUS_BAT_OK)
kbatt->alarm = false;
else
kbatt->alarm = true;
/* switch load off */
iowrite8(0, kbatt->base + KBATT_CONTROL_REG);
kbatt->next_update = jiffies + KBATT_MAX_UPD_INTERVAL;
}
mutex_unlock(&kbatt->lock);
return kbatt->alarm;
}
static int kbatt_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
struct kbatt *kbatt = dev_get_drvdata(dev);
*val = kbatt_alarm(kbatt) ? 1 : 0;
return 0;
}
static umode_t kbatt_is_visible(const void *data, enum hwmon_sensor_types type,
u32 attr, int channel)
{
if (channel == 0 && attr == hwmon_in_min_alarm)
return 0444;
return 0;
}
static const struct hwmon_channel_info *kbatt_info[] = {
HWMON_CHANNEL_INFO(in,
/* 0: input minimum alarm channel */
HWMON_I_MIN_ALARM),
NULL
};
static const struct hwmon_ops kbatt_hwmon_ops = {
.is_visible = kbatt_is_visible,
.read = kbatt_read,
};
static const struct hwmon_chip_info kbatt_chip_info = {
.ops = &kbatt_hwmon_ops,
.info = kbatt_info,
};
static int kbatt_probe(struct auxiliary_device *auxdev,
const struct auxiliary_device_id *id)
{
struct keba_batt_auxdev *kbatt_auxdev =
container_of(auxdev, struct keba_batt_auxdev, auxdev);
struct device *dev = &auxdev->dev;
struct device *hwmon_dev;
struct kbatt *kbatt;
int retval;
kbatt = devm_kzalloc(dev, sizeof(*kbatt), GFP_KERNEL);
if (!kbatt)
return -ENOMEM;
retval = devm_mutex_init(dev, &kbatt->lock);
if (retval)
return retval;
kbatt->base = devm_ioremap_resource(dev, &kbatt_auxdev->io);
if (IS_ERR(kbatt->base))
return PTR_ERR(kbatt->base);
hwmon_dev = devm_hwmon_device_register_with_info(dev, KBATT, kbatt,
&kbatt_chip_info,
NULL);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
static const struct auxiliary_device_id kbatt_devtype_aux[] = {
{ .name = "keba.batt" },
{}
};
MODULE_DEVICE_TABLE(auxiliary, kbatt_devtype_aux);
static struct auxiliary_driver kbatt_driver_aux = {
.name = KBATT,
.id_table = kbatt_devtype_aux,
.probe = kbatt_probe,
};
module_auxiliary_driver(kbatt_driver_aux);
MODULE_AUTHOR("Petar Bojanic <boja@keba.com>");
MODULE_AUTHOR("Gerhard Engleder <eg@keba.com>");
MODULE_DESCRIPTION("KEBA battery monitoring controller driver");
MODULE_LICENSE("GPL");
+246
View File
@@ -0,0 +1,246 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2025 KEBA Industrial Automation GmbH
*
* Driver for KEBA fan controller FPGA IP core
*
*/
#include <linux/hwmon.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/auxiliary_bus.h>
#include <linux/misc/keba.h>
#define KFAN "kfan"
#define KFAN_CONTROL_REG 0x04
#define KFAN_STATUS_REG 0x08
#define KFAN_STATUS_PRESENT 0x01
#define KFAN_STATUS_REGULABLE 0x02
#define KFAN_STATUS_TACHO 0x04
#define KFAN_STATUS_BLOCKED 0x08
#define KFAN_TACHO_REG 0x0c
#define KFAN_DEFAULT_DIV 2
struct kfan {
void __iomem *base;
bool tacho;
bool regulable;
/* hwmon API configuration */
u32 fan_channel_config[2];
struct hwmon_channel_info fan_info;
u32 pwm_channel_config[2];
struct hwmon_channel_info pwm_info;
const struct hwmon_channel_info *info[3];
struct hwmon_chip_info chip;
};
static bool kfan_get_fault(struct kfan *kfan)
{
u8 status = ioread8(kfan->base + KFAN_STATUS_REG);
if (!(status & KFAN_STATUS_PRESENT))
return true;
if (!kfan->tacho && (status & KFAN_STATUS_BLOCKED))
return true;
return false;
}
static unsigned int kfan_count_to_rpm(u16 count)
{
if (count == 0 || count == 0xffff)
return 0;
return 5000000UL / (KFAN_DEFAULT_DIV * count);
}
static unsigned int kfan_get_rpm(struct kfan *kfan)
{
unsigned int rpm;
u16 count;
count = ioread16(kfan->base + KFAN_TACHO_REG);
rpm = kfan_count_to_rpm(count);
return rpm;
}
static unsigned int kfan_get_pwm(struct kfan *kfan)
{
return ioread8(kfan->base + KFAN_CONTROL_REG);
}
static int kfan_set_pwm(struct kfan *kfan, long val)
{
if (val < 0 || val > 0xff)
return -EINVAL;
/* if none-regulable, then only 0 or 0xff can be written */
if (!kfan->regulable && val > 0)
val = 0xff;
iowrite8(val, kfan->base + KFAN_CONTROL_REG);
return 0;
}
static int kfan_write(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long val)
{
struct kfan *kfan = dev_get_drvdata(dev);
switch (type) {
case hwmon_pwm:
switch (attr) {
case hwmon_pwm_input:
return kfan_set_pwm(kfan, val);
default:
break;
}
break;
default:
break;
}
return -EOPNOTSUPP;
}
static int kfan_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
struct kfan *kfan = dev_get_drvdata(dev);
switch (type) {
case hwmon_fan:
switch (attr) {
case hwmon_fan_fault:
*val = kfan_get_fault(kfan);
return 0;
case hwmon_fan_input:
*val = kfan_get_rpm(kfan);
return 0;
default:
break;
}
break;
case hwmon_pwm:
switch (attr) {
case hwmon_pwm_input:
*val = kfan_get_pwm(kfan);
return 0;
default:
break;
}
break;
default:
break;
}
return -EOPNOTSUPP;
}
static umode_t kfan_is_visible(const void *data, enum hwmon_sensor_types type,
u32 attr, int channel)
{
switch (type) {
case hwmon_fan:
switch (attr) {
case hwmon_fan_input:
return 0444;
case hwmon_fan_fault:
return 0444;
default:
break;
}
break;
case hwmon_pwm:
switch (attr) {
case hwmon_pwm_input:
return 0644;
default:
break;
}
break;
default:
break;
}
return 0;
}
static const struct hwmon_ops kfan_hwmon_ops = {
.is_visible = kfan_is_visible,
.read = kfan_read,
.write = kfan_write,
};
static int kfan_probe(struct auxiliary_device *auxdev,
const struct auxiliary_device_id *id)
{
struct keba_fan_auxdev *kfan_auxdev =
container_of(auxdev, struct keba_fan_auxdev, auxdev);
struct device *dev = &auxdev->dev;
struct device *hwmon_dev;
struct kfan *kfan;
u8 status;
kfan = devm_kzalloc(dev, sizeof(*kfan), GFP_KERNEL);
if (!kfan)
return -ENOMEM;
kfan->base = devm_ioremap_resource(dev, &kfan_auxdev->io);
if (IS_ERR(kfan->base))
return PTR_ERR(kfan->base);
status = ioread8(kfan->base + KFAN_STATUS_REG);
if (status & KFAN_STATUS_REGULABLE)
kfan->regulable = true;
if (status & KFAN_STATUS_TACHO)
kfan->tacho = true;
/* fan */
kfan->fan_channel_config[0] = HWMON_F_FAULT;
if (kfan->tacho)
kfan->fan_channel_config[0] |= HWMON_F_INPUT;
kfan->fan_info.type = hwmon_fan;
kfan->fan_info.config = kfan->fan_channel_config;
kfan->info[0] = &kfan->fan_info;
/* PWM */
kfan->pwm_channel_config[0] = HWMON_PWM_INPUT;
kfan->pwm_info.type = hwmon_pwm;
kfan->pwm_info.config = kfan->pwm_channel_config;
kfan->info[1] = &kfan->pwm_info;
kfan->chip.ops = &kfan_hwmon_ops;
kfan->chip.info = kfan->info;
hwmon_dev = devm_hwmon_device_register_with_info(dev, KFAN, kfan,
&kfan->chip, NULL);
return PTR_ERR_OR_ZERO(hwmon_dev);
}
static const struct auxiliary_device_id kfan_devtype_aux[] = {
{ .name = "keba.fan" },
{}
};
MODULE_DEVICE_TABLE(auxiliary, kfan_devtype_aux);
static struct auxiliary_driver kfan_driver_aux = {
.name = KFAN,
.id_table = kfan_devtype_aux,
.probe = kfan_probe,
};
module_auxiliary_driver(kfan_driver_aux);
MODULE_AUTHOR("Petar Bojanic <boja@keba.com>");
MODULE_AUTHOR("Gerhard Engleder <eg@keba.com>");
MODULE_DESCRIPTION("KEBA fan controller driver");
MODULE_LICENSE("GPL");
+1 -1
View File
@@ -622,7 +622,7 @@ static int lm75_i3c_reg_read(void *context, unsigned int reg, unsigned int *val)
{
.rnw = true,
.len = 2,
.data.out = data->val_buf,
.data.in = data->val_buf,
},
};
int ret;
+1 -1
View File
@@ -1235,7 +1235,7 @@ static int lm90_update_alarms(struct lm90_data *data, bool force)
static void lm90_alert_work(struct work_struct *__work)
{
struct delayed_work *delayed_work = container_of(__work, struct delayed_work, work);
struct delayed_work *delayed_work = to_delayed_work(__work);
struct lm90_data *data = container_of(delayed_work, struct lm90_data, alert_work);
/* Nothing to do if alerts are enabled */
+20 -10
View File
@@ -256,33 +256,38 @@ static int ltc2992_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask
return 0;
}
static void ltc2992_gpio_set(struct gpio_chip *chip, unsigned int offset, int value)
static int ltc2992_gpio_set(struct gpio_chip *chip, unsigned int offset,
int value)
{
struct ltc2992_state *st = gpiochip_get_data(chip);
unsigned long gpio_ctrl;
int reg;
int reg, ret;
mutex_lock(&st->gpio_mutex);
reg = ltc2992_read_reg(st, ltc2992_gpio_addr_map[offset].ctrl, 1);
if (reg < 0) {
mutex_unlock(&st->gpio_mutex);
return;
return reg;
}
gpio_ctrl = reg;
assign_bit(ltc2992_gpio_addr_map[offset].ctrl_bit, &gpio_ctrl, value);
ltc2992_write_reg(st, ltc2992_gpio_addr_map[offset].ctrl, 1, gpio_ctrl);
ret = ltc2992_write_reg(st, ltc2992_gpio_addr_map[offset].ctrl, 1,
gpio_ctrl);
mutex_unlock(&st->gpio_mutex);
return ret;
}
static void ltc2992_gpio_set_multiple(struct gpio_chip *chip, unsigned long *mask,
unsigned long *bits)
static int ltc2992_gpio_set_multiple(struct gpio_chip *chip, unsigned long *mask,
unsigned long *bits)
{
struct ltc2992_state *st = gpiochip_get_data(chip);
unsigned long gpio_ctrl_io = 0;
unsigned long gpio_ctrl = 0;
unsigned int gpio_nr;
int ret;
for_each_set_bit(gpio_nr, mask, LTC2992_GPIO_NR) {
if (gpio_nr < 3)
@@ -293,9 +298,14 @@ static void ltc2992_gpio_set_multiple(struct gpio_chip *chip, unsigned long *mas
}
mutex_lock(&st->gpio_mutex);
ltc2992_write_reg(st, LTC2992_GPIO_IO_CTRL, 1, gpio_ctrl_io);
ltc2992_write_reg(st, LTC2992_GPIO_CTRL, 1, gpio_ctrl);
ret = ltc2992_write_reg(st, LTC2992_GPIO_IO_CTRL, 1, gpio_ctrl_io);
if (ret)
goto out;
ret = ltc2992_write_reg(st, LTC2992_GPIO_CTRL, 1, gpio_ctrl);
out:
mutex_unlock(&st->gpio_mutex);
return ret;
}
static int ltc2992_config_gpio(struct ltc2992_state *st)
@@ -329,8 +339,8 @@ static int ltc2992_config_gpio(struct ltc2992_state *st)
st->gc.ngpio = ARRAY_SIZE(st->gpio_names);
st->gc.get = ltc2992_gpio_get;
st->gc.get_multiple = ltc2992_gpio_get_multiple;
st->gc.set = ltc2992_gpio_set;
st->gc.set_multiple = ltc2992_gpio_set_multiple;
st->gc.set_rv = ltc2992_gpio_set;
st->gc.set_multiple_rv = ltc2992_gpio_set_multiple;
ret = devm_gpiochip_add_data(&st->client->dev, &st->gc, st);
if (ret)
+14 -2
View File
@@ -80,6 +80,7 @@ struct max6639_data {
/* Register values initialized only once */
u8 ppr[MAX6639_NUM_CHANNELS]; /* Pulses per rotation 0..3 for 1..4 ppr */
u8 rpm_range[MAX6639_NUM_CHANNELS]; /* Index in above rpm_ranges table */
u32 target_rpm[MAX6639_NUM_CHANNELS];
/* Optional regulator for FAN supply */
struct regulator *reg;
@@ -563,6 +564,10 @@ static int max6639_probe_child_from_dt(struct i2c_client *client,
if (!err)
data->rpm_range[i] = rpm_range_to_reg(val);
err = of_property_read_u32(child, "target-rpm", &val);
if (!err)
data->target_rpm[i] = val;
return 0;
}
@@ -573,6 +578,7 @@ static int max6639_init_client(struct i2c_client *client,
const struct device_node *np = dev->of_node;
struct device_node *child;
int i, err;
u8 target_duty;
/* Reset chip to default values, see below for GCONFIG setup */
err = regmap_write(data->regmap, MAX6639_REG_GCONFIG, MAX6639_GCONFIG_POR);
@@ -586,6 +592,8 @@ static int max6639_init_client(struct i2c_client *client,
/* default: 4000 RPM */
data->rpm_range[0] = 1;
data->rpm_range[1] = 1;
data->target_rpm[0] = 4000;
data->target_rpm[1] = 4000;
for_each_child_of_node(np, child) {
if (strcmp(child->name, "fan"))
@@ -639,8 +647,12 @@ static int max6639_init_client(struct i2c_client *client,
if (err)
return err;
/* PWM 120/120 (i.e. 100%) */
err = regmap_write(data->regmap, MAX6639_REG_TARGTDUTY(i), 120);
/* Set PWM based on target RPM if specified */
if (data->target_rpm[i] > rpm_ranges[data->rpm_range[i]])
data->target_rpm[i] = rpm_ranges[data->rpm_range[i]];
target_duty = 120 * data->target_rpm[i] / rpm_ranges[data->rpm_range[i]];
err = regmap_write(data->regmap, MAX6639_REG_TARGTDUTY(i), target_duty);
if (err)
return err;
}
+221
View File
@@ -0,0 +1,221 @@
// SPDX-License-Identifier: GPL-2.0
/*
* MAX77705 voltage and current hwmon driver.
*
* Copyright (C) 2025 Dzmitry Sankouski <dsankouski@gmail.com>
*/
#include <linux/err.h>
#include <linux/hwmon-sysfs.h>
#include <linux/hwmon.h>
#include <linux/kernel.h>
#include <linux/mfd/max77705-private.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
struct channel_desc {
u8 reg;
u8 avg_reg;
const char *const label;
// register resolution. nano Volts for voltage, nano Amperes for current
u32 resolution;
};
static const struct channel_desc current_channel_desc[] = {
{
.reg = IIN_REG,
.label = "IIN_REG",
.resolution = 125000
},
{
.reg = ISYS_REG,
.avg_reg = AVGISYS_REG,
.label = "ISYS_REG",
.resolution = 312500
}
};
static const struct channel_desc voltage_channel_desc[] = {
{
.reg = VBYP_REG,
.label = "VBYP_REG",
.resolution = 427246
},
{
.reg = VSYS_REG,
.label = "VSYS_REG",
.resolution = 156250
}
};
static int max77705_read_and_convert(struct regmap *regmap, u8 reg, u32 res,
bool is_signed, long *val)
{
int ret;
u32 regval;
ret = regmap_read(regmap, reg, &regval);
if (ret < 0)
return ret;
if (is_signed)
*val = mult_frac((long)sign_extend32(regval, 15), res, 1000000);
else
*val = mult_frac((long)regval, res, 1000000);
return 0;
}
static umode_t max77705_is_visible(const void *data,
enum hwmon_sensor_types type,
u32 attr, int channel)
{
switch (type) {
case hwmon_in:
switch (attr) {
case hwmon_in_input:
case hwmon_in_label:
return 0444;
default:
break;
}
break;
case hwmon_curr:
switch (attr) {
case hwmon_curr_input:
case hwmon_in_label:
return 0444;
case hwmon_curr_average:
if (current_channel_desc[channel].avg_reg)
return 0444;
break;
default:
break;
}
break;
default:
break;
}
return 0;
}
static int max77705_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr,
int channel, const char **buf)
{
switch (type) {
case hwmon_curr:
switch (attr) {
case hwmon_in_label:
*buf = current_channel_desc[channel].label;
return 0;
default:
return -EOPNOTSUPP;
}
case hwmon_in:
switch (attr) {
case hwmon_in_label:
*buf = voltage_channel_desc[channel].label;
return 0;
default:
return -EOPNOTSUPP;
}
default:
return -EOPNOTSUPP;
}
}
static int max77705_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
struct regmap *regmap = dev_get_drvdata(dev);
u8 reg;
u32 res;
switch (type) {
case hwmon_curr:
switch (attr) {
case hwmon_curr_input:
reg = current_channel_desc[channel].reg;
res = current_channel_desc[channel].resolution;
return max77705_read_and_convert(regmap, reg, res, true, val);
case hwmon_curr_average:
reg = current_channel_desc[channel].avg_reg;
res = current_channel_desc[channel].resolution;
return max77705_read_and_convert(regmap, reg, res, true, val);
default:
return -EOPNOTSUPP;
}
case hwmon_in:
switch (attr) {
case hwmon_in_input:
reg = voltage_channel_desc[channel].reg;
res = voltage_channel_desc[channel].resolution;
return max77705_read_and_convert(regmap, reg, res, false, val);
default:
return -EOPNOTSUPP;
}
default:
return -EOPNOTSUPP;
}
return 0;
}
static const struct hwmon_ops max77705_hwmon_ops = {
.is_visible = max77705_is_visible,
.read = max77705_read,
.read_string = max77705_read_string,
};
static const struct hwmon_channel_info *max77705_info[] = {
HWMON_CHANNEL_INFO(in,
HWMON_I_INPUT | HWMON_I_LABEL,
HWMON_I_INPUT | HWMON_I_LABEL
),
HWMON_CHANNEL_INFO(curr,
HWMON_C_INPUT | HWMON_C_LABEL,
HWMON_C_INPUT | HWMON_C_AVERAGE | HWMON_C_LABEL
),
NULL
};
static const struct hwmon_chip_info max77705_chip_info = {
.ops = &max77705_hwmon_ops,
.info = max77705_info,
};
static int max77705_hwmon_probe(struct platform_device *pdev)
{
struct device *hwmon_dev;
struct regmap *regmap;
regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (!regmap)
return -ENODEV;
hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, "max77705", regmap,
&max77705_chip_info, NULL);
if (IS_ERR(hwmon_dev))
return dev_err_probe(&pdev->dev, PTR_ERR(hwmon_dev),
"Unable to register hwmon device\n");
return 0;
};
static struct platform_driver max77705_hwmon_driver = {
.driver = {
.name = "max77705-hwmon",
},
.probe = max77705_hwmon_probe,
};
module_platform_driver(max77705_hwmon_driver);
MODULE_AUTHOR("Dzmitry Sankouski <dsankouski@gmail.com>");
MODULE_DESCRIPTION("MAX77705 monitor driver");
MODULE_LICENSE("GPL");
+1 -1
View File
@@ -391,7 +391,7 @@ static const struct regmap_config nct7363_regmap_config = {
.val_bits = 8,
.use_single_read = true,
.use_single_write = true,
.cache_type = REGCACHE_RBTREE,
.cache_type = REGCACHE_MAPLE,
.volatile_reg = nct7363_regmap_is_volatile,
};
+18
View File
@@ -218,6 +218,24 @@ config SENSORS_LM25066_REGULATOR
If you say yes here you get regulator support for National
Semiconductor LM25066, LM5064, and LM5066.
config SENSORS_LT3074
tristate "Analog Devices LT3074"
help
If you say yes here you get hardware monitoring support for Analog
Devices LT3074.
This driver can also be built as a module. If so, the module will
be called lt3074.
config SENSORS_LT3074_REGULATOR
tristate "Regulator support for LT3074"
depends on SENSORS_LT3074 && REGULATOR
help
If you say yes here you get regulator support for Analog Devices
LT3074. The LT3074 is a low voltage, ultralow noise, high PSRR,
dropout linear regulator. The device supplies up to 3A with a
typical dropout voltage of 45mV.
config SENSORS_LT7182S
tristate "Analog Devices LT7182S"
help
+1
View File
@@ -23,6 +23,7 @@ obj-$(CONFIG_SENSORS_IR38064) += ir38064.o
obj-$(CONFIG_SENSORS_IRPS5401) += irps5401.o
obj-$(CONFIG_SENSORS_ISL68137) += isl68137.o
obj-$(CONFIG_SENSORS_LM25066) += lm25066.o
obj-$(CONFIG_SENSORS_LT3074) += lt3074.o
obj-$(CONFIG_SENSORS_LT7182S) += lt7182s.o
obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o
obj-$(CONFIG_SENSORS_LTC3815) += ltc3815.o
+1 -1
View File
@@ -437,7 +437,7 @@ static int lm25066_write_word_data(struct i2c_client *client, int page, int reg,
#if IS_ENABLED(CONFIG_SENSORS_LM25066_REGULATOR)
static const struct regulator_desc lm25066_reg_desc[] = {
PMBUS_REGULATOR_ONE("vout"),
PMBUS_REGULATOR_ONE_NODE("vout"),
};
#endif
+122
View File
@@ -0,0 +1,122 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Hardware monitoring driver for Analog Devices LT3074
*
* Copyright (C) 2025 Analog Devices, Inc.
*/
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include "pmbus.h"
#define LT3074_MFR_READ_VBIAS 0xc6
#define LT3074_MFR_BIAS_OV_WARN_LIMIT 0xc7
#define LT3074_MFR_BIAS_UV_WARN_LIMIT 0xc8
#define LT3074_MFR_SPECIAL_ID 0xe7
#define LT3074_SPECIAL_ID_VALUE 0x1c1d
static const struct regulator_desc __maybe_unused lt3074_reg_desc[] = {
PMBUS_REGULATOR_ONE("regulator"),
};
static int lt3074_read_word_data(struct i2c_client *client, int page,
int phase, int reg)
{
switch (reg) {
case PMBUS_VIRT_READ_VMON:
return pmbus_read_word_data(client, page, phase,
LT3074_MFR_READ_VBIAS);
case PMBUS_VIRT_VMON_UV_WARN_LIMIT:
return pmbus_read_word_data(client, page, phase,
LT3074_MFR_BIAS_UV_WARN_LIMIT);
case PMBUS_VIRT_VMON_OV_WARN_LIMIT:
return pmbus_read_word_data(client, page, phase,
LT3074_MFR_BIAS_OV_WARN_LIMIT);
default:
return -ENODATA;
}
}
static int lt3074_write_word_data(struct i2c_client *client, int page,
int reg, u16 word)
{
switch (reg) {
case PMBUS_VIRT_VMON_UV_WARN_LIMIT:
return pmbus_write_word_data(client, 0,
LT3074_MFR_BIAS_UV_WARN_LIMIT,
word);
case PMBUS_VIRT_VMON_OV_WARN_LIMIT:
return pmbus_write_word_data(client, 0,
LT3074_MFR_BIAS_OV_WARN_LIMIT,
word);
default:
return -ENODATA;
}
}
static struct pmbus_driver_info lt3074_info = {
.pages = 1,
.format[PSC_VOLTAGE_IN] = linear,
.format[PSC_VOLTAGE_OUT] = linear,
.format[PSC_CURRENT_OUT] = linear,
.format[PSC_TEMPERATURE] = linear,
.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
PMBUS_HAVE_TEMP | PMBUS_HAVE_VMON |
PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT |
PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP,
.read_word_data = lt3074_read_word_data,
.write_word_data = lt3074_write_word_data,
#if IS_ENABLED(CONFIG_SENSORS_LT3074_REGULATOR)
.num_regulators = 1,
.reg_desc = lt3074_reg_desc,
#endif
};
static int lt3074_probe(struct i2c_client *client)
{
int ret;
struct device *dev = &client->dev;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_READ_WORD_DATA))
return -ENODEV;
ret = i2c_smbus_read_word_data(client, LT3074_MFR_SPECIAL_ID);
if (ret < 0)
return dev_err_probe(dev, ret, "Failed to read ID\n");
if (ret != LT3074_SPECIAL_ID_VALUE)
return dev_err_probe(dev, -ENODEV, "ID mismatch\n");
return pmbus_do_probe(client, &lt3074_info);
}
static const struct i2c_device_id lt3074_id[] = {
{ "lt3074", 0 },
{}
};
MODULE_DEVICE_TABLE(i2c, lt3074_id);
static const struct of_device_id __maybe_unused lt3074_of_match[] = {
{ .compatible = "adi,lt3074" },
{}
};
MODULE_DEVICE_TABLE(of, lt3074_of_match);
static struct i2c_driver lt3074_driver = {
.driver = {
.name = "lt3074",
.of_match_table = of_match_ptr(lt3074_of_match),
},
.probe = lt3074_probe,
.id_table = lt3074_id,
};
module_i2c_driver(lt3074_driver);
MODULE_AUTHOR("Cedric Encarnacion <cedricjustine.encarnacion@analog.com>");
MODULE_DESCRIPTION("PMBus driver for Analog Devices LT3074");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS("PMBUS");
+112 -7
View File
@@ -12,9 +12,26 @@
#include <linux/init.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include "pmbus.h"
enum chips { max34440, max34441, max34446, max34451, max34460, max34461 };
enum chips {
adpm12160,
max34440,
max34441,
max34446,
max34451,
max34460,
max34461,
};
/*
* Firmware is sometimes not ready if we try and read the
* data from the page immediately after setting. Maxim
* recommends 50us delay due to the chip failing to clock
* stretch long enough here.
*/
#define MAX34440_PAGE_CHANGE_DELAY 50
#define MAX34440_MFR_VOUT_PEAK 0xd4
#define MAX34440_MFR_IOUT_PEAK 0xd5
@@ -34,16 +51,21 @@ enum chips { max34440, max34441, max34446, max34451, max34460, max34461 };
/*
* The whole max344* family have IOUT_OC_WARN_LIMIT and IOUT_OC_FAULT_LIMIT
* swapped from the standard pmbus spec addresses.
* For max34451, version MAX34451ETNA6+ and later has this issue fixed.
*/
#define MAX34440_IOUT_OC_WARN_LIMIT 0x46
#define MAX34440_IOUT_OC_FAULT_LIMIT 0x4A
#define MAX34451ETNA6_MFR_REV 0x0012
#define MAX34451_MFR_CHANNEL_CONFIG 0xe4
#define MAX34451_MFR_CHANNEL_CONFIG_SEL_MASK 0x3f
struct max34440_data {
int id;
struct pmbus_driver_info info;
u8 iout_oc_warn_limit;
u8 iout_oc_fault_limit;
};
#define to_max34440_data(x) container_of(x, struct max34440_data, info)
@@ -60,11 +82,11 @@ static int max34440_read_word_data(struct i2c_client *client, int page,
switch (reg) {
case PMBUS_IOUT_OC_FAULT_LIMIT:
ret = pmbus_read_word_data(client, page, phase,
MAX34440_IOUT_OC_FAULT_LIMIT);
data->iout_oc_fault_limit);
break;
case PMBUS_IOUT_OC_WARN_LIMIT:
ret = pmbus_read_word_data(client, page, phase,
MAX34440_IOUT_OC_WARN_LIMIT);
data->iout_oc_warn_limit);
break;
case PMBUS_VIRT_READ_VOUT_MIN:
ret = pmbus_read_word_data(client, page, phase,
@@ -75,7 +97,8 @@ static int max34440_read_word_data(struct i2c_client *client, int page,
MAX34440_MFR_VOUT_PEAK);
break;
case PMBUS_VIRT_READ_IOUT_AVG:
if (data->id != max34446 && data->id != max34451)
if (data->id != max34446 && data->id != max34451 &&
data->id != adpm12160)
return -ENXIO;
ret = pmbus_read_word_data(client, page, phase,
MAX34446_MFR_IOUT_AVG);
@@ -133,11 +156,11 @@ static int max34440_write_word_data(struct i2c_client *client, int page,
switch (reg) {
case PMBUS_IOUT_OC_FAULT_LIMIT:
ret = pmbus_write_word_data(client, page, MAX34440_IOUT_OC_FAULT_LIMIT,
ret = pmbus_write_word_data(client, page, data->iout_oc_fault_limit,
word);
break;
case PMBUS_IOUT_OC_WARN_LIMIT:
ret = pmbus_write_word_data(client, page, MAX34440_IOUT_OC_WARN_LIMIT,
ret = pmbus_write_word_data(client, page, data->iout_oc_warn_limit,
word);
break;
case PMBUS_VIRT_RESET_POUT_HISTORY:
@@ -159,7 +182,8 @@ static int max34440_write_word_data(struct i2c_client *client, int page,
case PMBUS_VIRT_RESET_IOUT_HISTORY:
ret = pmbus_write_word_data(client, page,
MAX34440_MFR_IOUT_PEAK, 0);
if (!ret && (data->id == max34446 || data->id == max34451))
if (!ret && (data->id == max34446 || data->id == max34451 ||
data->id == adpm12160))
ret = pmbus_write_word_data(client, page,
MAX34446_MFR_IOUT_AVG, 0);
@@ -235,9 +259,29 @@ static int max34451_set_supported_funcs(struct i2c_client *client,
*/
int page, rv;
bool max34451_na6 = false;
rv = i2c_smbus_read_word_data(client, PMBUS_MFR_REVISION);
if (rv < 0)
return rv;
if (rv >= MAX34451ETNA6_MFR_REV) {
max34451_na6 = true;
data->info.format[PSC_VOLTAGE_IN] = direct;
data->info.format[PSC_CURRENT_IN] = direct;
data->info.m[PSC_VOLTAGE_IN] = 1;
data->info.b[PSC_VOLTAGE_IN] = 0;
data->info.R[PSC_VOLTAGE_IN] = 3;
data->info.m[PSC_CURRENT_IN] = 1;
data->info.b[PSC_CURRENT_IN] = 0;
data->info.R[PSC_CURRENT_IN] = 2;
data->iout_oc_fault_limit = PMBUS_IOUT_OC_FAULT_LIMIT;
data->iout_oc_warn_limit = PMBUS_IOUT_OC_WARN_LIMIT;
}
for (page = 0; page < 16; page++) {
rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page);
fsleep(MAX34440_PAGE_CHANGE_DELAY);
if (rv < 0)
return rv;
@@ -251,16 +295,30 @@ static int max34451_set_supported_funcs(struct i2c_client *client,
case 0x20:
data->info.func[page] = PMBUS_HAVE_VOUT |
PMBUS_HAVE_STATUS_VOUT;
if (max34451_na6)
data->info.func[page] |= PMBUS_HAVE_VIN |
PMBUS_HAVE_STATUS_INPUT;
break;
case 0x21:
data->info.func[page] = PMBUS_HAVE_VOUT;
if (max34451_na6)
data->info.func[page] |= PMBUS_HAVE_VIN;
break;
case 0x22:
data->info.func[page] = PMBUS_HAVE_IOUT |
PMBUS_HAVE_STATUS_IOUT;
if (max34451_na6)
data->info.func[page] |= PMBUS_HAVE_IIN |
PMBUS_HAVE_STATUS_INPUT;
break;
case 0x23:
data->info.func[page] = PMBUS_HAVE_IOUT;
if (max34451_na6)
data->info.func[page] |= PMBUS_HAVE_IIN;
break;
default:
break;
@@ -271,6 +329,41 @@ static int max34451_set_supported_funcs(struct i2c_client *client,
}
static struct pmbus_driver_info max34440_info[] = {
[adpm12160] = {
.pages = 19,
.format[PSC_VOLTAGE_IN] = direct,
.format[PSC_VOLTAGE_OUT] = direct,
.format[PSC_CURRENT_IN] = direct,
.format[PSC_CURRENT_OUT] = direct,
.format[PSC_TEMPERATURE] = direct,
.m[PSC_VOLTAGE_IN] = 1,
.b[PSC_VOLTAGE_IN] = 0,
.R[PSC_VOLTAGE_IN] = 0,
.m[PSC_VOLTAGE_OUT] = 1,
.b[PSC_VOLTAGE_OUT] = 0,
.R[PSC_VOLTAGE_OUT] = 0,
.m[PSC_CURRENT_IN] = 1,
.b[PSC_CURRENT_IN] = 0,
.R[PSC_CURRENT_IN] = 2,
.m[PSC_CURRENT_OUT] = 1,
.b[PSC_CURRENT_OUT] = 0,
.R[PSC_CURRENT_OUT] = 2,
.m[PSC_TEMPERATURE] = 1,
.b[PSC_TEMPERATURE] = 0,
.R[PSC_TEMPERATURE] = 2,
/* absent func below [18] are not for monitoring */
.func[2] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT,
.func[4] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
.func[5] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
.func[6] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
.func[7] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
.func[8] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
.func[9] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT,
.func[10] = PMBUS_HAVE_IIN | PMBUS_HAVE_STATUS_INPUT,
.func[18] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
.read_word_data = max34440_read_word_data,
.write_word_data = max34440_write_word_data,
},
[max34440] = {
.pages = 14,
.format[PSC_VOLTAGE_IN] = direct,
@@ -312,6 +405,7 @@ static struct pmbus_driver_info max34440_info[] = {
.read_byte_data = max34440_read_byte_data,
.read_word_data = max34440_read_word_data,
.write_word_data = max34440_write_word_data,
.page_change_delay = MAX34440_PAGE_CHANGE_DELAY,
},
[max34441] = {
.pages = 12,
@@ -355,6 +449,7 @@ static struct pmbus_driver_info max34440_info[] = {
.read_byte_data = max34440_read_byte_data,
.read_word_data = max34440_read_word_data,
.write_word_data = max34440_write_word_data,
.page_change_delay = MAX34440_PAGE_CHANGE_DELAY,
},
[max34446] = {
.pages = 7,
@@ -392,6 +487,7 @@ static struct pmbus_driver_info max34440_info[] = {
.read_byte_data = max34440_read_byte_data,
.read_word_data = max34440_read_word_data,
.write_word_data = max34440_write_word_data,
.page_change_delay = MAX34440_PAGE_CHANGE_DELAY,
},
[max34451] = {
.pages = 21,
@@ -415,6 +511,7 @@ static struct pmbus_driver_info max34440_info[] = {
.func[20] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
.read_word_data = max34440_read_word_data,
.write_word_data = max34440_write_word_data,
.page_change_delay = MAX34440_PAGE_CHANGE_DELAY,
},
[max34460] = {
.pages = 18,
@@ -445,6 +542,7 @@ static struct pmbus_driver_info max34440_info[] = {
.func[17] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
.read_word_data = max34440_read_word_data,
.write_word_data = max34440_write_word_data,
.page_change_delay = MAX34440_PAGE_CHANGE_DELAY,
},
[max34461] = {
.pages = 23,
@@ -480,6 +578,7 @@ static struct pmbus_driver_info max34440_info[] = {
.func[21] = PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
.read_word_data = max34440_read_word_data,
.write_word_data = max34440_write_word_data,
.page_change_delay = MAX34440_PAGE_CHANGE_DELAY,
},
};
@@ -494,17 +593,23 @@ static int max34440_probe(struct i2c_client *client)
return -ENOMEM;
data->id = i2c_match_id(max34440_id, client)->driver_data;
data->info = max34440_info[data->id];
data->iout_oc_fault_limit = MAX34440_IOUT_OC_FAULT_LIMIT;
data->iout_oc_warn_limit = MAX34440_IOUT_OC_WARN_LIMIT;
if (data->id == max34451) {
rv = max34451_set_supported_funcs(client, data);
if (rv)
return rv;
} else if (data->id == adpm12160) {
data->iout_oc_fault_limit = PMBUS_IOUT_OC_FAULT_LIMIT;
data->iout_oc_warn_limit = PMBUS_IOUT_OC_WARN_LIMIT;
}
return pmbus_do_probe(client, &data->info);
}
static const struct i2c_device_id max34440_id[] = {
{"adpm12160", adpm12160},
{"max34440", max34440},
{"max34441", max34441},
{"max34446", max34446},
+2 -2
View File
@@ -51,8 +51,8 @@ static const struct regulator_desc mpq7932_regulators_desc[] = {
};
static const struct regulator_desc mpq7932_regulators_desc_one[] = {
PMBUS_REGULATOR_STEP_ONE("buck", MPQ7932_N_VOLTAGES,
MPQ7932_UV_STEP, MPQ7932_BUCK_UV_MIN),
PMBUS_REGULATOR_STEP_ONE_NODE("buck", MPQ7932_N_VOLTAGES,
MPQ7932_UV_STEP, MPQ7932_BUCK_UV_MIN),
};
#endif
+83 -8
View File
@@ -4,10 +4,23 @@
*/
#include <linux/i2c.h>
#include <linux/bitops.h>
#include <linux/module.h>
#include <linux/property.h>
#include <linux/of_device.h>
#include "pmbus.h"
#define MPM82504_READ_TEMPERATURE_1_SIGN_POS 9
enum chips { mpm3695, mpm3695_25, mpm82504, mpq8785 };
static u16 voltage_scale_loop_max_val[] = {
[mpm3695] = GENMASK(9, 0),
[mpm3695_25] = GENMASK(11, 0),
[mpm82504] = GENMASK(9, 0),
[mpq8785] = GENMASK(10, 0),
};
static int mpq8785_identify(struct i2c_client *client,
struct pmbus_driver_info *info)
{
@@ -34,6 +47,20 @@ static int mpq8785_identify(struct i2c_client *client,
return 0;
};
static int mpm82504_read_word_data(struct i2c_client *client, int page,
int phase, int reg)
{
int ret;
ret = pmbus_read_word_data(client, page, phase, reg);
if (ret < 0 || reg != PMBUS_READ_TEMPERATURE_1)
return ret;
/* Fix PMBUS_READ_TEMPERATURE_1 signedness */
return sign_extend32(ret, MPM82504_READ_TEMPERATURE_1_SIGN_POS) & 0xffff;
}
static struct pmbus_driver_info mpq8785_info = {
.pages = 1,
.format[PSC_VOLTAGE_IN] = direct,
@@ -53,26 +80,74 @@ static struct pmbus_driver_info mpq8785_info = {
PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT |
PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT |
PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP,
.identify = mpq8785_identify,
};
static int mpq8785_probe(struct i2c_client *client)
{
return pmbus_do_probe(client, &mpq8785_info);
};
static const struct i2c_device_id mpq8785_id[] = {
{ "mpq8785" },
{ "mpm3695", mpm3695 },
{ "mpm3695-25", mpm3695_25 },
{ "mpm82504", mpm82504 },
{ "mpq8785", mpq8785 },
{ },
};
MODULE_DEVICE_TABLE(i2c, mpq8785_id);
static const struct of_device_id __maybe_unused mpq8785_of_match[] = {
{ .compatible = "mps,mpq8785" },
{ .compatible = "mps,mpm3695", .data = (void *)mpm3695 },
{ .compatible = "mps,mpm3695-25", .data = (void *)mpm3695_25 },
{ .compatible = "mps,mpm82504", .data = (void *)mpm82504 },
{ .compatible = "mps,mpq8785", .data = (void *)mpq8785 },
{}
};
MODULE_DEVICE_TABLE(of, mpq8785_of_match);
static int mpq8785_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct pmbus_driver_info *info;
enum chips chip_id;
u32 voltage_scale;
int ret;
info = devm_kmemdup(dev, &mpq8785_info, sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
if (dev->of_node)
chip_id = (kernel_ulong_t)of_device_get_match_data(dev);
else
chip_id = (kernel_ulong_t)i2c_get_match_data(client);
switch (chip_id) {
case mpm3695:
case mpm3695_25:
case mpm82504:
info->format[PSC_VOLTAGE_OUT] = direct;
info->m[PSC_VOLTAGE_OUT] = 8;
info->b[PSC_VOLTAGE_OUT] = 0;
info->R[PSC_VOLTAGE_OUT] = 2;
info->read_word_data = mpm82504_read_word_data;
break;
case mpq8785:
info->identify = mpq8785_identify;
break;
default:
return -ENODEV;
}
if (!device_property_read_u32(dev, "mps,vout-fb-divider-ratio-permille",
&voltage_scale)) {
if (voltage_scale > voltage_scale_loop_max_val[chip_id])
return -EINVAL;
ret = i2c_smbus_write_word_data(client, PMBUS_VOUT_SCALE_LOOP,
voltage_scale);
if (ret)
return ret;
}
return pmbus_do_probe(client, info);
};
static struct i2c_driver mpq8785_driver = {
.driver = {
.name = "mpq8785",
+16 -3
View File
@@ -482,6 +482,7 @@ struct pmbus_driver_info {
*/
int access_delay; /* in microseconds */
int write_delay; /* in microseconds */
int page_change_delay; /* in microseconds */
};
/* Regulator ops */
@@ -508,11 +509,11 @@ int pmbus_regulator_init_cb(struct regulator_dev *rdev,
#define PMBUS_REGULATOR(_name, _id) PMBUS_REGULATOR_STEP(_name, _id, 0, 0, 0)
#define PMBUS_REGULATOR_STEP_ONE(_name, _voltages, _step, _min_uV) \
#define __PMBUS_REGULATOR_STEP_ONE(_name, _node, _voltages, _step, _min_uV) \
{ \
.name = (_name), \
.of_match = of_match_ptr(_name), \
.regulators_node = of_match_ptr("regulators"), \
.regulators_node = of_match_ptr(_node), \
.ops = &pmbus_regulator_ops, \
.type = REGULATOR_VOLTAGE, \
.owner = THIS_MODULE, \
@@ -522,7 +523,19 @@ int pmbus_regulator_init_cb(struct regulator_dev *rdev,
.init_cb = pmbus_regulator_init_cb, \
}
#define PMBUS_REGULATOR_ONE(_name) PMBUS_REGULATOR_STEP_ONE(_name, 0, 0, 0)
/*
* _NODE macros are defined for historic reasons and MUST NOT be used in new
* drivers.
*/
#define PMBUS_REGULATOR_STEP_ONE_NODE(_name, _voltages, _step, _min_uV) \
__PMBUS_REGULATOR_STEP_ONE(_name, "regulators", _voltages, _step, _min_uV)
#define PMBUS_REGULATOR_ONE_NODE(_name) PMBUS_REGULATOR_STEP_ONE_NODE(_name, 0, 0, 0)
#define PMBUS_REGULATOR_STEP_ONE(_name, _voltages, _step, _min_uV) \
__PMBUS_REGULATOR_STEP_ONE(_name, NULL, _voltages, _step, _min_uV)
#define PMBUS_REGULATOR_ONE(_name) PMBUS_REGULATOR_STEP_ONE(_name, 0, 0, 0)
/* Function declarations */
+35 -34
View File
@@ -32,6 +32,13 @@
#define PMBUS_ATTR_ALLOC_SIZE 32
#define PMBUS_NAME_SIZE 24
/*
* The type of operation used for picking the delay between
* successive pmbus operations.
*/
#define PMBUS_OP_WRITE BIT(0)
#define PMBUS_OP_PAGE_CHANGE BIT(1)
static int wp = -1;
module_param(wp, int, 0444);
@@ -113,8 +120,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 */
ktime_t next_access_backoff; /* Wait until at least this time */
};
struct pmbus_debugfs_entry {
@@ -169,32 +176,26 @@ EXPORT_SYMBOL_NS_GPL(pmbus_set_update, "PMBUS");
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;
s64 delay = ktime_us_delta(data->next_access_backoff, ktime_get());
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);
}
if (delay > 0)
fsleep(delay);
}
/* Sets the last accessed timestamp for pmbus_wait */
static void pmbus_update_ts(struct i2c_client *client, bool write_op)
/* Sets the last operation timestamp for pmbus_wait */
static void pmbus_update_ts(struct i2c_client *client, int op)
{
struct pmbus_data *data = i2c_get_clientdata(client);
const struct pmbus_driver_info *info = data->info;
int delay = info->access_delay;
if (info->access_delay)
data->access_time = ktime_get();
else if (info->write_delay && write_op)
data->write_time = ktime_get();
if (op & PMBUS_OP_WRITE)
delay = max(delay, info->write_delay);
if (op & PMBUS_OP_PAGE_CHANGE)
delay = max(delay, info->page_change_delay);
if (delay > 0)
data->next_access_backoff = ktime_add_us(ktime_get(), delay);
}
int pmbus_set_page(struct i2c_client *client, int page, int phase)
@@ -209,13 +210,13 @@ int pmbus_set_page(struct i2c_client *client, int page, int phase)
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);
pmbus_update_ts(client, PMBUS_OP_WRITE | PMBUS_OP_PAGE_CHANGE);
if (rv < 0)
return rv;
pmbus_wait(client);
rv = i2c_smbus_read_byte_data(client, PMBUS_PAGE);
pmbus_update_ts(client, false);
pmbus_update_ts(client, 0);
if (rv < 0)
return rv;
@@ -229,7 +230,7 @@ int pmbus_set_page(struct i2c_client *client, int page, int phase)
pmbus_wait(client);
rv = i2c_smbus_write_byte_data(client, PMBUS_PHASE,
phase);
pmbus_update_ts(client, true);
pmbus_update_ts(client, PMBUS_OP_WRITE);
if (rv)
return rv;
}
@@ -249,7 +250,7 @@ int pmbus_write_byte(struct i2c_client *client, int page, u8 value)
pmbus_wait(client);
rv = i2c_smbus_write_byte(client, value);
pmbus_update_ts(client, true);
pmbus_update_ts(client, PMBUS_OP_WRITE);
return rv;
}
@@ -284,7 +285,7 @@ int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg,
pmbus_wait(client);
rv = i2c_smbus_write_word_data(client, reg, word);
pmbus_update_ts(client, true);
pmbus_update_ts(client, PMBUS_OP_WRITE);
return rv;
}
@@ -405,7 +406,7 @@ int pmbus_read_word_data(struct i2c_client *client, int page, int phase, u8 reg)
pmbus_wait(client);
rv = i2c_smbus_read_word_data(client, reg);
pmbus_update_ts(client, false);
pmbus_update_ts(client, 0);
return rv;
}
@@ -468,7 +469,7 @@ int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg)
pmbus_wait(client);
rv = i2c_smbus_read_byte_data(client, reg);
pmbus_update_ts(client, false);
pmbus_update_ts(client, 0);
return rv;
}
@@ -484,7 +485,7 @@ int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, u8 value)
pmbus_wait(client);
rv = i2c_smbus_write_byte_data(client, reg, value);
pmbus_update_ts(client, true);
pmbus_update_ts(client, PMBUS_OP_WRITE);
return rv;
}
@@ -520,7 +521,7 @@ static int pmbus_read_block_data(struct i2c_client *client, int page, u8 reg,
pmbus_wait(client);
rv = i2c_smbus_read_block_data(client, reg, data_buf);
pmbus_update_ts(client, false);
pmbus_update_ts(client, 0);
return rv;
}
@@ -2524,7 +2525,7 @@ static int pmbus_read_coefficients(struct i2c_client *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);
pmbus_update_ts(client, PMBUS_OP_WRITE);
if (rv < 0)
return rv;
@@ -2728,7 +2729,7 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data,
if (!(data->flags & PMBUS_NO_CAPABILITY)) {
pmbus_wait(client);
ret = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY);
pmbus_update_ts(client, false);
pmbus_update_ts(client, 0);
if (ret >= 0 && (ret & PB_CAPABILITY_ERROR_CHECK)) {
if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_PEC))
@@ -2744,13 +2745,13 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data,
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);
pmbus_update_ts(client, 0);
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);
pmbus_update_ts(client, 0);
if (ret < 0 || ret == 0xff) {
dev_err(dev, "PMBus status register not found\n");
+1 -1
View File
@@ -15,7 +15,7 @@
#include "pmbus.h"
static const struct regulator_desc __maybe_unused tda38640_reg_desc[] = {
PMBUS_REGULATOR_ONE("vout"),
PMBUS_REGULATOR_ONE_NODE("vout"),
};
struct tda38640_data {
+1 -1
View File
@@ -333,7 +333,7 @@ static int tps25990_write_byte_data(struct i2c_client *client,
#if IS_ENABLED(CONFIG_SENSORS_TPS25990_REGULATOR)
static const struct regulator_desc tps25990_reg_desc[] = {
PMBUS_REGULATOR_ONE("vout"),
PMBUS_REGULATOR_ONE_NODE("vout"),
};
#endif
+9 -7
View File
@@ -212,8 +212,8 @@ static int ucd9000_gpio_get(struct gpio_chip *gc, unsigned int offset)
return !!(ret & UCD9000_GPIO_CONFIG_STATUS);
}
static void ucd9000_gpio_set(struct gpio_chip *gc, unsigned int offset,
int value)
static int ucd9000_gpio_set(struct gpio_chip *gc, unsigned int offset,
int value)
{
struct i2c_client *client = gpiochip_get_data(gc);
int ret;
@@ -222,17 +222,17 @@ static void ucd9000_gpio_set(struct gpio_chip *gc, unsigned int offset,
if (ret < 0) {
dev_dbg(&client->dev, "failed to read GPIO %d config: %d\n",
offset, ret);
return;
return ret;
}
if (value) {
if (ret & UCD9000_GPIO_CONFIG_STATUS)
return;
return 0;
ret |= UCD9000_GPIO_CONFIG_STATUS;
} else {
if (!(ret & UCD9000_GPIO_CONFIG_STATUS))
return;
return 0;
ret &= ~UCD9000_GPIO_CONFIG_STATUS;
}
@@ -244,7 +244,7 @@ static void ucd9000_gpio_set(struct gpio_chip *gc, unsigned int offset,
if (ret < 0) {
dev_dbg(&client->dev, "Failed to write GPIO %d config: %d\n",
offset, ret);
return;
return ret;
}
ret &= ~UCD9000_GPIO_CONFIG_ENABLE;
@@ -253,6 +253,8 @@ static void ucd9000_gpio_set(struct gpio_chip *gc, unsigned int offset,
if (ret < 0)
dev_dbg(&client->dev, "Failed to write GPIO %d config: %d\n",
offset, ret);
return ret;
}
static int ucd9000_gpio_get_direction(struct gpio_chip *gc,
@@ -362,7 +364,7 @@ static void ucd9000_probe_gpio(struct i2c_client *client,
data->gpio.direction_input = ucd9000_gpio_direction_input;
data->gpio.direction_output = ucd9000_gpio_direction_output;
data->gpio.get = ucd9000_gpio_get;
data->gpio.set = ucd9000_gpio_set;
data->gpio.set_rv = ucd9000_gpio_set;
data->gpio.can_sleep = true;
data->gpio.base = -1;
data->gpio.parent = &client->dev;
+2 -2
View File
@@ -620,8 +620,8 @@ static int pwm_fan_probe(struct platform_device *pdev)
if (tach->irq == -EPROBE_DEFER)
return tach->irq;
if (tach->irq > 0) {
ret = devm_request_irq(dev, tach->irq, pulse_handler, 0,
pdev->name, tach);
ret = devm_request_irq(dev, tach->irq, pulse_handler,
IRQF_NO_THREAD, pdev->name, tach);
if (ret) {
dev_err(dev,
"Failed to request interrupt: %d\n",
-1
View File
@@ -6,7 +6,6 @@
* Copyright (C) 2024 Heiko Stuebner <heiko@sntech.de>
*/
#include <linux/fwnode.h>
#include <linux/hwmon.h>
#include <linux/mfd/qnap-mcu.h>
#include <linux/module.h>
+220 -139
View File
@@ -66,6 +66,9 @@ static const unsigned short normal_i2c[] = {
#define SPD5118_EEPROM_BASE 0x80
#define SPD5118_EEPROM_SIZE (SPD5118_PAGE_SIZE * SPD5118_NUM_PAGES)
#define PAGE_ADDR0(page) (((page) & BIT(0)) << 6)
#define PAGE_ADDR1_4(page) (((page) & GENMASK(4, 1)) >> 1)
/* Temperature unit in millicelsius */
#define SPD5118_TEMP_UNIT (MILLIDEGREE_PER_DEGREE / 4)
/* Representable temperature range in millicelsius */
@@ -75,6 +78,7 @@ static const unsigned short normal_i2c[] = {
struct spd5118_data {
struct regmap *regmap;
struct mutex nvmem_lock;
bool is_16bit;
};
/* hwmon */
@@ -305,51 +309,6 @@ static bool spd5118_vendor_valid(u8 bank, u8 id)
return id && id != 0x7f;
}
/* Return 0 if detection is successful, -ENODEV otherwise */
static int spd5118_detect(struct i2c_client *client, struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
int regval;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA))
return -ENODEV;
regval = i2c_smbus_read_word_swapped(client, SPD5118_REG_TYPE);
if (regval != 0x5118)
return -ENODEV;
regval = i2c_smbus_read_word_data(client, SPD5118_REG_VENDOR);
if (regval < 0 || !spd5118_vendor_valid(regval & 0xff, regval >> 8))
return -ENODEV;
regval = i2c_smbus_read_byte_data(client, SPD5118_REG_CAPABILITY);
if (regval < 0)
return -ENODEV;
if (!(regval & SPD5118_CAP_TS_SUPPORT) || (regval & 0xfc))
return -ENODEV;
regval = i2c_smbus_read_byte_data(client, SPD5118_REG_TEMP_CLR);
if (regval)
return -ENODEV;
regval = i2c_smbus_read_byte_data(client, SPD5118_REG_ERROR_CLR);
if (regval)
return -ENODEV;
regval = i2c_smbus_read_byte_data(client, SPD5118_REG_REVISION);
if (regval < 0 || (regval & 0xc1))
return -ENODEV;
regval = i2c_smbus_read_byte_data(client, SPD5118_REG_TEMP_CONFIG);
if (regval < 0)
return -ENODEV;
if (regval & ~SPD5118_TS_DISABLE)
return -ENODEV;
strscpy(info->type, "spd5118", I2C_NAME_SIZE);
return 0;
}
static const struct hwmon_channel_info *spd5118_info[] = {
HWMON_CHANNEL_INFO(chip,
HWMON_C_REGISTER_TZ),
@@ -376,11 +335,12 @@ static const struct hwmon_chip_info spd5118_chip_info = {
/* nvmem */
static ssize_t spd5118_nvmem_read_page(struct regmap *regmap, char *buf,
static ssize_t spd5118_nvmem_read_page(struct spd5118_data *data, char *buf,
unsigned int offset, size_t count)
{
int addr = (offset >> SPD5118_PAGE_SHIFT) * 0x100 + SPD5118_EEPROM_BASE;
int err;
int page = offset >> SPD5118_PAGE_SHIFT;
struct regmap *regmap = data->regmap;
int err, addr;
offset &= SPD5118_PAGE_MASK;
@@ -388,6 +348,12 @@ static ssize_t spd5118_nvmem_read_page(struct regmap *regmap, char *buf,
if (offset + count > SPD5118_PAGE_SIZE)
count = SPD5118_PAGE_SIZE - offset;
if (data->is_16bit) {
addr = SPD5118_EEPROM_BASE | PAGE_ADDR0(page) |
(PAGE_ADDR1_4(page) << 8);
} else {
addr = page * 0x100 + SPD5118_EEPROM_BASE;
}
err = regmap_bulk_read(regmap, addr + offset, buf, count);
if (err)
return err;
@@ -410,7 +376,7 @@ static int spd5118_nvmem_read(void *priv, unsigned int off, void *val, size_t co
mutex_lock(&data->nvmem_lock);
while (count) {
ret = spd5118_nvmem_read_page(data->regmap, buf, off, count);
ret = spd5118_nvmem_read_page(data, buf, off, count);
if (ret < 0) {
mutex_unlock(&data->nvmem_lock);
return ret;
@@ -483,7 +449,7 @@ static bool spd5118_volatile_reg(struct device *dev, unsigned int reg)
}
}
static const struct regmap_range_cfg spd5118_regmap_range_cfg[] = {
static const struct regmap_range_cfg spd5118_i2c_regmap_range_cfg[] = {
{
.selector_reg = SPD5118_REG_I2C_LEGACY_MODE,
.selector_mask = SPD5118_LEGACY_PAGE_MASK,
@@ -495,7 +461,7 @@ static const struct regmap_range_cfg spd5118_regmap_range_cfg[] = {
},
};
static const struct regmap_config spd5118_regmap_config = {
static const struct regmap_config spd5118_regmap8_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0x7ff,
@@ -503,89 +469,76 @@ static const struct regmap_config spd5118_regmap_config = {
.volatile_reg = spd5118_volatile_reg,
.cache_type = REGCACHE_MAPLE,
.ranges = spd5118_regmap_range_cfg,
.num_ranges = ARRAY_SIZE(spd5118_regmap_range_cfg),
.ranges = spd5118_i2c_regmap_range_cfg,
.num_ranges = ARRAY_SIZE(spd5118_i2c_regmap_range_cfg),
};
static int spd5118_init(struct i2c_client *client)
static const struct regmap_config spd5118_regmap16_config = {
.reg_bits = 16,
.val_bits = 8,
.max_register = 0x7ff,
.writeable_reg = spd5118_writeable_reg,
.volatile_reg = spd5118_volatile_reg,
.cache_type = REGCACHE_MAPLE,
};
static int spd5118_suspend(struct device *dev)
{
struct i2c_adapter *adapter = client->adapter;
int err, regval, mode;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA))
return -ENODEV;
regval = i2c_smbus_read_word_swapped(client, SPD5118_REG_TYPE);
if (regval < 0 || (regval && regval != 0x5118))
return -ENODEV;
struct spd5118_data *data = dev_get_drvdata(dev);
struct regmap *regmap = data->regmap;
u32 regval;
int err;
/*
* If the device type registers return 0, it is possible that the chip
* has a non-zero page selected and takes the specification literally,
* i.e. disables access to volatile registers besides the page register
* if the page is not 0. Try to identify such chips.
* Make sure the configuration register in the regmap cache is current
* before bypassing it.
*/
if (!regval) {
/* Vendor ID registers must also be 0 */
regval = i2c_smbus_read_word_data(client, SPD5118_REG_VENDOR);
if (regval)
return -ENODEV;
err = regmap_read(regmap, SPD5118_REG_TEMP_CONFIG, &regval);
if (err < 0)
return err;
/* The selected page in MR11 must not be 0 */
mode = i2c_smbus_read_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE);
if (mode < 0 || (mode & ~SPD5118_LEGACY_MODE_MASK) ||
!(mode & SPD5118_LEGACY_PAGE_MASK))
return -ENODEV;
regcache_cache_bypass(regmap, true);
regmap_update_bits(regmap, SPD5118_REG_TEMP_CONFIG, SPD5118_TS_DISABLE,
SPD5118_TS_DISABLE);
regcache_cache_bypass(regmap, false);
err = i2c_smbus_write_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE,
mode & SPD5118_LEGACY_MODE_ADDR);
if (err)
return -ENODEV;
regcache_cache_only(regmap, true);
regcache_mark_dirty(regmap);
/*
* If the device type registers are still bad after selecting
* page 0, this is not a SPD5118 device. Restore original
* legacy mode register value and abort.
*/
regval = i2c_smbus_read_word_swapped(client, SPD5118_REG_TYPE);
if (regval != 0x5118) {
i2c_smbus_write_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE, mode);
return -ENODEV;
}
}
/* We are reasonably sure that this is really a SPD5118 hub controller */
return 0;
}
static int spd5118_probe(struct i2c_client *client)
static int spd5118_resume(struct device *dev)
{
struct device *dev = &client->dev;
unsigned int regval, revision, vendor, bank;
struct spd5118_data *data = dev_get_drvdata(dev);
struct regmap *regmap = data->regmap;
regcache_cache_only(regmap, false);
return regcache_sync(regmap);
}
static DEFINE_SIMPLE_DEV_PM_OPS(spd5118_pm_ops, spd5118_suspend, spd5118_resume);
static int spd5118_common_probe(struct device *dev, struct regmap *regmap,
bool is_16bit)
{
unsigned int capability, revision, vendor, bank;
struct spd5118_data *data;
struct device *hwmon_dev;
struct regmap *regmap;
int err;
err = spd5118_init(client);
if (err)
return err;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
regmap = devm_regmap_init_i2c(client, &spd5118_regmap_config);
if (IS_ERR(regmap))
return dev_err_probe(dev, PTR_ERR(regmap), "regmap init failed\n");
err = regmap_read(regmap, SPD5118_REG_CAPABILITY, &regval);
err = regmap_read(regmap, SPD5118_REG_CAPABILITY, &capability);
if (err)
return err;
if (!(regval & SPD5118_CAP_TS_SUPPORT))
if (!(capability & SPD5118_CAP_TS_SUPPORT))
return -ENODEV;
data->is_16bit = is_16bit;
err = regmap_read(regmap, SPD5118_REG_REVISION, &revision);
if (err)
return err;
@@ -627,48 +580,176 @@ static int spd5118_probe(struct i2c_client *client)
return 0;
}
static int spd5118_suspend(struct device *dev)
/* I2C */
/* Return 0 if detection is successful, -ENODEV otherwise */
static int spd5118_detect(struct i2c_client *client, struct i2c_board_info *info)
{
struct spd5118_data *data = dev_get_drvdata(dev);
struct regmap *regmap = data->regmap;
u32 regval;
int err;
struct i2c_adapter *adapter = client->adapter;
int regval;
/*
* Make sure the configuration register in the regmap cache is current
* before bypassing it.
*/
err = regmap_read(regmap, SPD5118_REG_TEMP_CONFIG, &regval);
if (err < 0)
return err;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA))
return -ENODEV;
regcache_cache_bypass(regmap, true);
regmap_update_bits(regmap, SPD5118_REG_TEMP_CONFIG, SPD5118_TS_DISABLE,
SPD5118_TS_DISABLE);
regcache_cache_bypass(regmap, false);
regval = i2c_smbus_read_word_swapped(client, SPD5118_REG_TYPE);
if (regval != 0x5118)
return -ENODEV;
regcache_cache_only(regmap, true);
regcache_mark_dirty(regmap);
regval = i2c_smbus_read_word_data(client, SPD5118_REG_VENDOR);
if (regval < 0 || !spd5118_vendor_valid(regval & 0xff, regval >> 8))
return -ENODEV;
regval = i2c_smbus_read_byte_data(client, SPD5118_REG_CAPABILITY);
if (regval < 0)
return -ENODEV;
if (!(regval & SPD5118_CAP_TS_SUPPORT) || (regval & 0xfc))
return -ENODEV;
regval = i2c_smbus_read_byte_data(client, SPD5118_REG_TEMP_CLR);
if (regval)
return -ENODEV;
regval = i2c_smbus_read_byte_data(client, SPD5118_REG_ERROR_CLR);
if (regval)
return -ENODEV;
regval = i2c_smbus_read_byte_data(client, SPD5118_REG_REVISION);
if (regval < 0 || (regval & 0xc1))
return -ENODEV;
regval = i2c_smbus_read_byte_data(client, SPD5118_REG_TEMP_CONFIG);
if (regval < 0)
return -ENODEV;
if (regval & ~SPD5118_TS_DISABLE)
return -ENODEV;
strscpy(info->type, "spd5118", I2C_NAME_SIZE);
return 0;
}
static int spd5118_resume(struct device *dev)
static int spd5118_i2c_init(struct i2c_client *client)
{
struct spd5118_data *data = dev_get_drvdata(dev);
struct regmap *regmap = data->regmap;
struct i2c_adapter *adapter = client->adapter;
int err, regval, mode;
regcache_cache_only(regmap, false);
return regcache_sync(regmap);
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA))
return -ENODEV;
regval = i2c_smbus_read_word_swapped(client, SPD5118_REG_TYPE);
if (regval < 0 || (regval && regval != 0x5118))
return -ENODEV;
/*
* If the device type registers return 0, it is possible that the chip
* has a non-zero page selected and takes the specification literally,
* i.e. disables access to volatile registers besides the page register
* if the page is not 0. The Renesas/ITD SPD5118 Hub Controller is known
* to show this behavior. Try to identify such chips.
*/
if (!regval) {
/* Vendor ID registers must also be 0 */
regval = i2c_smbus_read_word_data(client, SPD5118_REG_VENDOR);
if (regval)
return -ENODEV;
/* The selected page in MR11 must not be 0 */
mode = i2c_smbus_read_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE);
if (mode < 0 || (mode & ~SPD5118_LEGACY_MODE_MASK) ||
!(mode & SPD5118_LEGACY_PAGE_MASK))
return -ENODEV;
err = i2c_smbus_write_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE,
mode & SPD5118_LEGACY_MODE_ADDR);
if (err)
return -ENODEV;
/*
* If the device type registers are still bad after selecting
* page 0, this is not a SPD5118 device. Restore original
* legacy mode register value and abort.
*/
regval = i2c_smbus_read_word_swapped(client, SPD5118_REG_TYPE);
if (regval != 0x5118) {
i2c_smbus_write_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE, mode);
return -ENODEV;
}
}
/* We are reasonably sure that this is really a SPD5118 hub controller */
return 0;
}
static DEFINE_SIMPLE_DEV_PM_OPS(spd5118_pm_ops, spd5118_suspend, spd5118_resume);
/*
* 16-bit addressing note:
*
* If I2C_FUNC_I2C is not supported by an I2C adapter driver, regmap uses
* SMBus operations as alternative. To simulate a read operation with a 16-bit
* address, it writes the address using i2c_smbus_write_byte_data(), followed
* by one or more calls to i2c_smbus_read_byte() to read the data.
* Per spd5118 standard, a read operation after writing the address must start
* with <Sr> (Repeat Start). However, a SMBus read byte operation starts with
* <S> (Start). This resets the register address in the spd5118 chip. As result,
* i2c_smbus_read_byte() always returns data from register address 0x00.
*
* A working alternative to access chips with 16-bit register addresses in the
* absence of I2C_FUNC_I2C support is not known.
*
* For this reason, 16-bit addressing can only be supported with I2C if the
* adapter supports I2C_FUNC_I2C.
*
* For I2C, the addressing mode selected by the BIOS must not be changed.
* Experiments show that at least some PC BIOS versions will not change the
* addressing mode on a soft reboot and end up in setup, claiming that some
* configuration change happened. This will happen again after a power cycle,
* which does reset the addressing mode. To prevent this from happening,
* detect if 16-bit addressing is enabled and always use the currently
* configured addressing mode.
*/
static const struct i2c_device_id spd5118_id[] = {
static int spd5118_i2c_probe(struct i2c_client *client)
{
const struct regmap_config *config;
struct device *dev = &client->dev;
struct regmap *regmap;
int err, mode;
bool is_16bit;
err = spd5118_i2c_init(client);
if (err)
return err;
mode = i2c_smbus_read_byte_data(client, SPD5118_REG_I2C_LEGACY_MODE);
if (mode < 0)
return mode;
is_16bit = mode & SPD5118_LEGACY_MODE_ADDR;
if (is_16bit) {
/*
* See 16-bit addressing note above explaining why it is
* necessary to check for I2C_FUNC_I2C support here.
*/
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
dev_err(dev, "Adapter does not support 16-bit register addresses\n");
return -ENODEV;
}
config = &spd5118_regmap16_config;
} else {
config = &spd5118_regmap8_config;
}
regmap = devm_regmap_init_i2c(client, config);
if (IS_ERR(regmap))
return dev_err_probe(dev, PTR_ERR(regmap), "regmap init failed\n");
return spd5118_common_probe(dev, regmap, is_16bit);
}
static const struct i2c_device_id spd5118_i2c_id[] = {
{ "spd5118" },
{ }
};
MODULE_DEVICE_TABLE(i2c, spd5118_id);
MODULE_DEVICE_TABLE(i2c, spd5118_i2c_id);
static const struct of_device_id spd5118_of_ids[] = {
{ .compatible = "jedec,spd5118", },
@@ -676,20 +757,20 @@ static const struct of_device_id spd5118_of_ids[] = {
};
MODULE_DEVICE_TABLE(of, spd5118_of_ids);
static struct i2c_driver spd5118_driver = {
static struct i2c_driver spd5118_i2c_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "spd5118",
.of_match_table = spd5118_of_ids,
.pm = pm_sleep_ptr(&spd5118_pm_ops),
},
.probe = spd5118_probe,
.id_table = spd5118_id,
.probe = spd5118_i2c_probe,
.id_table = spd5118_i2c_id,
.detect = IS_ENABLED(CONFIG_SENSORS_SPD5118_DETECT) ? spd5118_detect : NULL,
.address_list = IS_ENABLED(CONFIG_SENSORS_SPD5118_DETECT) ? normal_i2c : NULL,
};
module_i2c_driver(spd5118_driver);
module_i2c_driver(spd5118_i2c_driver);
MODULE_AUTHOR("René Rebe <rene@exactcode.de>");
MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
+5
View File
@@ -16,6 +16,7 @@
#include <linux/device.h>
#include <linux/jiffies.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/of.h>
#define DRIVER_NAME "tmp102"
@@ -204,6 +205,10 @@ static int tmp102_probe(struct i2c_client *client)
return -ENODEV;
}
err = devm_regulator_get_enable_optional(dev, "vcc");
if (err < 0 && err != -ENODEV)
return dev_err_probe(dev, err, "Failed to enable regulator\n");
tmp102 = devm_kzalloc(dev, sizeof(*tmp102), GFP_KERNEL);
if (!tmp102)
return -ENOMEM;
+4 -35
View File
@@ -103,8 +103,6 @@ struct xgene_hwmon_dev {
struct device *hwmon_dev;
bool temp_critical_alarm;
phys_addr_t comm_base_addr;
void *pcc_comm_addr;
unsigned int usecs_lat;
};
@@ -125,7 +123,8 @@ static u16 xgene_word_tst_and_clr(u16 *addr, u16 mask)
static int xgene_hwmon_pcc_rd(struct xgene_hwmon_dev *ctx, u32 *msg)
{
struct acpi_pcct_shared_memory *generic_comm_base = ctx->pcc_comm_addr;
struct acpi_pcct_shared_memory __iomem *generic_comm_base =
ctx->pcc_chan->shmem;
u32 *ptr = (void *)(generic_comm_base + 1);
int rc, i;
u16 val;
@@ -523,7 +522,8 @@ static void xgene_hwmon_rx_cb(struct mbox_client *cl, void *msg)
static void xgene_hwmon_pcc_rx_cb(struct mbox_client *cl, void *msg)
{
struct xgene_hwmon_dev *ctx = to_xgene_hwmon_dev(cl);
struct acpi_pcct_shared_memory *generic_comm_base = ctx->pcc_comm_addr;
struct acpi_pcct_shared_memory __iomem *generic_comm_base =
ctx->pcc_chan->shmem;
struct slimpro_resp_msg amsg;
/*
@@ -649,7 +649,6 @@ static int xgene_hwmon_probe(struct platform_device *pdev)
} else {
struct pcc_mbox_chan *pcc_chan;
const struct acpi_device_id *acpi_id;
int version;
acpi_id = acpi_match_device(pdev->dev.driver->acpi_match_table,
&pdev->dev);
@@ -658,8 +657,6 @@ static int xgene_hwmon_probe(struct platform_device *pdev)
goto out_mbox_free;
}
version = (int)acpi_id->driver_data;
if (device_property_read_u32(&pdev->dev, "pcc-channel",
&ctx->mbox_idx)) {
dev_err(&pdev->dev, "no pcc-channel property\n");
@@ -685,34 +682,6 @@ static int xgene_hwmon_probe(struct platform_device *pdev)
goto out;
}
/*
* This is the shared communication region
* for the OS and Platform to communicate over.
*/
ctx->comm_base_addr = pcc_chan->shmem_base_addr;
if (ctx->comm_base_addr) {
if (version == XGENE_HWMON_V2)
ctx->pcc_comm_addr = (void __force *)devm_ioremap(&pdev->dev,
ctx->comm_base_addr,
pcc_chan->shmem_size);
else
ctx->pcc_comm_addr = devm_memremap(&pdev->dev,
ctx->comm_base_addr,
pcc_chan->shmem_size,
MEMREMAP_WB);
} else {
dev_err(&pdev->dev, "Failed to get PCC comm region\n");
rc = -ENODEV;
goto out;
}
if (IS_ERR_OR_NULL(ctx->pcc_comm_addr)) {
dev_err(&pdev->dev,
"Failed to ioremap PCC comm region\n");
rc = -ENOMEM;
goto out;
}
/*
* pcc_chan->latency is just a Nominal value. In reality
* the remote processor could be much slower to reply.