Commit 5dfec3cf authored by Linus Torvalds's avatar Linus Torvalds

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

Pull hwmon updates from Guenter Roeck:
 "New drivers:
   - pmbus: Support for MPS Multi-phase mp2856/mp2857 controller
   - pmbus: Support for MPS Multi-phase mp5990
   - Driver for Gigabyte AORUS Waterforce AIO coolers

  Added support to existing drivers:
   - lm75: Support for AMS AS6200 temperature sensor
   - k10temp: Support for AMD Family 19h Model 8h
   - max31827: Support for max31828 and max31829
   - sht3x: Support for sts3x
   - Add support for WMI SMM interface, and various related improvements.
    Add support for Optiplex 7000
   - emc1403: Support for EMC1442
   - npcm750-pwm-fan: Support for NPCM8xx
   - nct6775: Add support for 2 additional fan controls

  Minor improvements and bug fixes:
   - gigabyte_waterforce: Mark status report as received under a spinlock
   - aquacomputer_d5next: Remove unneeded CONFIG_DEBUG_FS #ifdef
   - gpio-fan: Convert txt bindings to yaml
   - smsc47m1: Various cleanups / improvements
   - corsair-cpro: use NULL instead of 0
   - hp-wmi-sensors: Fix failure to load on EliteDesk 800 G6
   - tmp513: Various cleanups
   - peci/dimmtemp: Bump timeout
   - pc87360: Bounds check data->innr usage
   - nct6775: Fix fan speed set failure in automatic mode
   - ABI: sysfs-class-hwmon: document various missing attributes
   - lm25066, max6650, nct6775: Use i2c_get_match_data()
   - aspeed-pwm-tacho: Fix -Wstringop-overflow warning"

* tag 'hwmon-for-v6.8' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (59 commits)
  hwmon: (gigabyte_waterforce) Mark status report as received under a spinlock
  hwmon: (lm75) Fix tmp112 default config
  hwmon: (lm75) Add AMS AS6200 temperature sensor
  dt-bindings: hwmon: (lm75) Add AMS AS6200 temperature sensor
  hwmon: (lm75) remove now-unused include
  hwmon: (pmbus) Add support for MPS Multi-phase mp2856/mp2857 controller
  dt-bindings: Add MP2856/MP2857 voltage regulator device
  hwmon: (aquacomputer_d5next) Remove unneeded CONFIG_DEBUG_FS #ifdef
  dt-bindings: hwmon: gpio-fan: Convert txt bindings to yaml
  hwmon: (k10temp) Add support for AMD Family 19h Model 8h
  hwmon: Add driver for Gigabyte AORUS Waterforce AIO coolers
  hwmon: (smsc47m1) Rename global platform device variable
  hwmon: (smsc47m1) Simplify device registration
  hwmon: (smsc47m1) Convert to platform remove callback returning void
  hwmon: (smsc47m1) Mark driver struct with __refdata to prevent section mismatch
  MAINTAINERS: Add maintainer for Baikal-T1 PVT hwmon driver
  hwmon: (sht3x) add sts3x support
  hwmon: (pmbus) Add ltc4286 driver
  dt-bindings: hwmon: Add lltc ltc4286 driver bindings
  hwmon: (max31827) Add custom attribute for resolution
  ...
parents 7912a639 41c71105
......@@ -381,6 +381,15 @@ Description:
RW
What: /sys/class/hwmon/hwmonX/tempY_max_alarm
Description:
Maximum temperature alarm flag.
- 0: OK
- 1: temperature has reached tempY_max
RO
What: /sys/class/hwmon/hwmonX/tempY_min
Description:
Temperature min value.
......@@ -389,6 +398,15 @@ Description:
RW
What: /sys/class/hwmon/hwmonX/tempY_min_alarm
Description:
Minimum temperature alarm flag.
- 0: OK
- 1: temperature has reached tempY_min
RO
What: /sys/class/hwmon/hwmonX/tempY_max_hyst
Description:
Temperature hysteresis value for max limit.
......@@ -434,12 +452,7 @@ Description:
- 0: OK
- 1: temperature has reached tempY_crit
RW
Contrary to regular alarm flags which clear themselves
automatically when read, this one sticks until cleared by
the user. This is done by writing 0 to the file. Writing
other values is unsupported.
RO
What: /sys/class/hwmon/hwmonX/tempY_crit_hyst
Description:
......@@ -462,6 +475,15 @@ Description:
RW
What: /sys/class/hwmon/hwmonX/tempY_emergency_alarm
Description:
Emergency high temperature alarm flag.
- 0: OK
- 1: temperature has reached tempY_emergency
RO
What: /sys/class/hwmon/hwmonX/tempY_emergency_hyst
Description:
Temperature hysteresis value for emergency limit.
......@@ -887,15 +909,15 @@ Description:
RW
What: /sys/class/hwmon/hwmonX/humidityY_input
What: /sys/class/hwmon/hwmonX/humidityY_alarm
Description:
Humidity
Humidity limit detection
Unit: milli-percent (per cent mille, pcm)
- 0: OK
- 1: Humidity limit has been reached
RO
What: /sys/class/hwmon/hwmonX/humidityY_enable
Description:
Enable or disable the sensors
......@@ -908,6 +930,74 @@ Description:
RW
What: /sys/class/hwmon/hwmonX/humidityY_fault
Description:
Reports a humidity sensor failure.
- 1: Failed
- 0: Ok
RO
What: /sys/class/hwmon/hwmonX/humidityY_input
Description:
Humidity
Unit: milli-percent (per cent mille, pcm)
RO
What: /sys/class/hwmon/hwmonX/humidityY_label
Description:
Suggested humidity channel label.
Text string
Should only be created if the driver has hints about what
this humidity channel is being used for, and user-space
doesn't. In all other cases, the label is provided by
user-space.
RO
What: /sys/class/hwmon/hwmonX/humidityY_max
Description:
Humidity max value.
Unit: milli-percent (per cent mille, pcm)
RW
What: /sys/class/hwmon/hwmonX/humidityY_max_hyst
Description:
Humidity hysteresis value for max limit.
Unit: milli-percent (per cent mille, pcm)
Must be reported as an absolute humidity, NOT a delta
from the max value.
RW
What: /sys/class/hwmon/hwmonX/humidityY_min
Description:
Humidity min value.
Unit: milli-percent (per cent mille, pcm)
RW
What: /sys/class/hwmon/hwmonX/humidityY_min_hyst
Description:
Humidity hysteresis value for min limit.
Unit: milli-percent (per cent mille, pcm)
Must be reported as an absolute humidity, NOT a delta
from the min value.
RW
What: /sys/class/hwmon/hwmonX/humidityY_rated_min
Description:
Minimum rated humidity.
......
Bindings for fan connected to GPIO lines
Required properties:
- compatible : "gpio-fan"
Optional properties:
- gpios: Specifies the pins that map to bits in the control value,
ordered MSB-->LSB.
- gpio-fan,speed-map: A mapping of possible fan RPM speeds and the
control value that should be set to achieve them. This array
must have the RPM values in ascending order.
- alarm-gpios: This pin going active indicates something is wrong with
the fan, and a udev event will be fired.
- #cooling-cells: If used as a cooling device, must be <2>
Also see:
Documentation/devicetree/bindings/thermal/thermal-cooling-devices.yaml
min and max states are derived from the speed-map of the fan.
Note: At least one the "gpios" or "alarm-gpios" properties must be set.
Examples:
gpio_fan {
compatible = "gpio-fan";
gpios = <&gpio1 14 1
&gpio1 13 1>;
gpio-fan,speed-map = <0 0
3000 1
6000 2>;
alarm-gpios = <&gpio1 15 1>;
};
gpio_fan_cool: gpio_fan {
compatible = "gpio-fan";
gpios = <&gpio2 14 1
&gpio2 13 1>;
gpio-fan,speed-map = <0 0>,
<3000 1>,
<6000 2>;
alarm-gpios = <&gpio2 15 1>;
#cooling-cells = <2>; /* min followed by max */
};
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/gpio-fan.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Fan connected to GPIO lines
maintainers:
- Rob Herring <robh@kernel.org>
properties:
compatible:
const: gpio-fan
gpios:
description: |
Specifies the pins that map to bits in the control value,
ordered MSB-->LSB.
minItems: 1
maxItems: 7
alarm-gpios:
maxItems: 1
gpio-fan,speed-map:
$ref: /schemas/types.yaml#/definitions/uint32-matrix
minItems: 2
maxItems: 127
items:
items:
- description: fan speed in RPMs
- description: control value
description: |
A mapping of possible fan RPM speeds and the
control value that should be set to achieve them. This array
must have the RPM values in ascending order.
'#cooling-cells':
const: 2
required:
- compatible
- gpios
- gpio-fan,speed-map
additionalProperties: false
examples:
- |
gpio-fan {
compatible = "gpio-fan";
gpios = <&gpio2 14 1
&gpio2 13 1>;
gpio-fan,speed-map = < 0 0>,
<3000 1>,
<6000 2>;
alarm-gpios = <&gpio2 15 1>;
#cooling-cells = <2>; /* min followed by max */
};
......@@ -19,7 +19,7 @@ properties:
io-channels:
minItems: 1
maxItems: 8 # Should be enough
maxItems: 51 # Should be enough
description: >
List of phandles to ADC channels to read the monitoring values
......
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/lltc,ltc4286.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: LTC4286 power monitors
maintainers:
- Delphine CC Chiu <Delphine_CC_Chiu@Wiwynn.com>
properties:
compatible:
enum:
- lltc,ltc4286
- lltc,ltc4287
reg:
maxItems: 1
adi,vrange-low-enable:
description:
This property is a bool parameter to represent the
voltage range is 25.6 volts or 102.4 volts for this chip.
The default is 102.4 volts.
type: boolean
shunt-resistor-micro-ohms:
description:
Resistor value micro-ohms.
required:
- compatible
- reg
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
power-monitor@40 {
compatible = "lltc,ltc4286";
reg = <0x40>;
adi,vrange-low-enable;
shunt-resistor-micro-ohms = <300>;
};
};
......@@ -14,6 +14,7 @@ properties:
compatible:
enum:
- adi,adt75
- ams,as6200
- atmel,at30ts74
- dallas,ds1775
- dallas,ds75
......@@ -48,10 +49,28 @@ properties:
vs-supply:
description: phandle to the regulator that provides the +VS supply
interrupts:
maxItems: 1
required:
- compatible
- reg
allOf:
- if:
not:
properties:
compatible:
contains:
enum:
- ams,as6200
- ti,tmp100
- ti,tmp101
- ti,tmp112
then:
properties:
interrupts: false
additionalProperties: false
examples:
......@@ -66,3 +85,17 @@ examples:
vs-supply = <&vs>;
};
};
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
temperature-sensor@48 {
compatible = "ams,as6200";
reg = <0x48>;
vs-supply = <&vs>;
interrupt-parent = <&gpio1>;
interrupts = <17 IRQ_TYPE_EDGE_BOTH>;
};
};
......@@ -117,6 +117,10 @@ properties:
- fsl,mpl3115
# MPR121: Proximity Capacitive Touch Sensor Controller
- fsl,mpr121
# Monolithic Power Systems Inc. multi-phase controller mp2856
- mps,mp2856
# Monolithic Power Systems Inc. multi-phase controller mp2857
- mps,mp2857
# Monolithic Power Systems Inc. multi-phase controller mp2888
- mps,mp2888
# Monolithic Power Systems Inc. multi-phase controller mp2971
......@@ -125,6 +129,8 @@ properties:
- mps,mp2973
# Monolithic Power Systems Inc. multi-phase controller mp2975
- mps,mp2975
# Monolithic Power Systems Inc. multi-phase hot-swap controller mp5990
- mps,mp5990
# Honeywell Humidicon HIH-6130 humidity/temperature sensor
- honeywell,hi6130
# IBM Common Form Factor Power Supply Versions (all versions)
......
......@@ -186,8 +186,7 @@ SMM Interface
The driver uses the SMM interface to send commands to the system BIOS.
This interface is normally used by Dell's 32-bit diagnostic program or
on newer notebook models by the buildin BIOS diagnostics.
The SMM is triggered by writing to the special ioports ``0xb2`` and ``0x84``,
and may cause short hangs when the BIOS code is taking too long to
The SMM may cause short hangs when the BIOS code is taking too long to
execute.
The SMM handler inside the system BIOS looks at the contents of the
......@@ -210,7 +209,40 @@ The SMM handler can signal a failure by either:
- setting the lower sixteen bits of ``eax`` to ``0xffff``
- not modifying ``eax`` at all
- setting the carry flag
- setting the carry flag (legacy SMM interface only)
Legacy SMM Interface
--------------------
When using the legacy SMM interface, a SMM is triggered by writing the least significant byte
of the command code to the special ioports ``0xb2`` and ``0x84``. This interface is not
described inside the ACPI tables and can thus only be detected by issuing a test SMM call.
WMI SMM Interface
-----------------
On modern Dell machines, the SMM calls are done over ACPI WMI:
::
#pragma namespace("\\\\.\\root\\dcim\\sysman\\diagnostics")
[WMI, Provider("Provider_DiagnosticsServices"), Dynamic, Locale("MS\\0x409"),
Description("RunDellDiag"), guid("{F1DDEE52-063C-4784-A11E-8A06684B9B01}")]
class LegacyDiags {
[key, read] string InstanceName;
[read] boolean Active;
[WmiMethodId(1), Implemented, read, write, Description("Legacy Method ")]
void Execute([in, out] uint32 EaxLen, [in, out, WmiSizeIs("EaxLen") : ToInstance] uint8 EaxVal[],
[in, out] uint32 EbxLen, [in, out, WmiSizeIs("EbxLen") : ToInstance] uint8 EbxVal[],
[in, out] uint32 EcxLen, [in, out, WmiSizeIs("EcxLen") : ToInstance] uint8 EcxVal[],
[in, out] uint32 EdxLen, [in, out, WmiSizeIs("EdxLen") : ToInstance] uint8 EdxVal[]);
};
Some machines support only the WMI SMM interface, while some machines support both interfaces.
The driver automatically detects which interfaces are present and will use the WMI SMM interface
if the legacy SMM interface is not present. The WMI SMM interface is usually slower than the
legacy SMM interface since ACPI methods need to be called in order to trigger a SMM.
SMM command codes
-----------------
......
.. SPDX-License-Identifier: GPL-2.0-or-later
Kernel driver gigabyte_waterforce
=================================
Supported devices:
* Gigabyte AORUS WATERFORCE X240
* Gigabyte AORUS WATERFORCE X280
* Gigabyte AORUS WATERFORCE X360
Author: Aleksa Savic
Description
-----------
This driver enables hardware monitoring support for the listed Gigabyte Waterforce
all-in-one CPU liquid coolers. Available sensors are pump and fan speed in RPM, as
well as coolant temperature. Also available through debugfs is the firmware version.
Attaching a fan is optional and allows it to be controlled from the device. If
it's not connected, the fan-related sensors will report zeroes.
The addressable RGB LEDs and LCD screen are not supported in this driver and should
be controlled through userspace tools.
Usage notes
-----------
As these are USB HIDs, the driver can be loaded automatically by the kernel and
supports hot swapping.
Sysfs entries
-------------
=========== =============================================
fan1_input Fan speed (in rpm)
fan2_input Pump speed (in rpm)
temp1_input Coolant temperature (in millidegrees Celsius)
=========== =============================================
Debugfs entries
---------------
================ =======================
firmware_version Device firmware version
================ =======================
......@@ -73,6 +73,7 @@ Hardware Monitoring Kernel Drivers
ftsteutates
g760a
g762
gigabyte_waterforce
gsc-hwmon
gl518sm
gxp-fan-ctrl
......@@ -128,6 +129,7 @@ Hardware Monitoring Kernel Drivers
ltc4245
ltc4260
ltc4261
ltc4286
max127
max15301
max16064
......@@ -156,9 +158,11 @@ Hardware Monitoring Kernel Drivers
mcp3021
menf21bmc
mlxreg-fan
mp2856
mp2888
mp2975
mp5023
mp5990
nct6683
nct6775
nct7802
......
......@@ -133,6 +133,16 @@ Supported chips:
https://www.nxp.com/docs/en/data-sheet/PCT2075.pdf
* AMS OSRAM AS6200
Prefix: 'as6200'
Addresses scanned: none
Datasheet: Publicly available at the AMS website
https://ams.com/documents/20143/36005/AS6200_DS000449_4-00.pdf
Author: Frodo Looijaard <frodol@dds.nl>
Description
......
.. SPDX-License-Identifier: GPL-2.0-or-later
Kernel driver ltc4286
=====================
Supported chips:
* Analog Devices LTC4286
Prefix: 'ltc4286'
Addresses scanned: -
Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ltc4286.pdf
* Analog Devices LTC4287
Prefix: 'ltc4287'
Addresses scanned: -
Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ltc4287.pdf
Author: Delphine CC Chiu <Delphine_CC_Chiu@Wiwynn.com>
Description
-----------
This driver supports hardware monitoring for Analog Devices LTC4286
and LTC4287 Hot-Swap Controller and Digital Power Monitors.
LTC4286 and LTC4287 are hot-swap controllers that allow a circuit board
to be removed from or inserted into a live backplane. They also feature
current and voltage readback via an integrated 12 bit analog-to-digital
converter (ADC), accessed using a PMBus interface.
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.
The shunt value in micro-ohms can be set via device tree at compile-time. Please
refer to the Documentation/devicetree/bindings/hwmon/lltc,ltc4286.yaml for bindings
if the device tree is used.
Platform data support
---------------------
The driver supports standard PMBus driver platform data. Please see
Documentation/hwmon/pmbus.rst for details.
Sysfs entries
-------------
The following attributes are supported. Limits are read-write, history reset
attributes are write-only, all other attributes are read-only.
======================= =======================================================
in1_label "vin"
in1_input Measured voltage.
in1_alarm Input voltage alarm.
in1_min Minimum input voltage.
in1_max Maximum input voltage.
in2_label "vout1"
in2_input Measured voltage.
in2_alarm Output voltage alarm.
in2_min Minimum output voltage.
in2_max Maximum output voltage.
curr1_label "iout1"
curr1_input Measured current.
curr1_alarm Output current alarm.
curr1_max Maximum current.
power1_label "pin"
power1_input Input power.
power1_alarm Input power alarm.
power1_max Maximum poewr.
temp1_input Chip temperature.
temp1_min Minimum chip temperature.
temp1_max Maximum chip temperature.
temp1_crit Critical chip temperature.
temp1_alarm Chip temperature alarm.
======================= =======================================================
......@@ -52,13 +52,21 @@ MAX31827 has low and over temperature alarms with an effective value and a
hysteresis value: -40 and -30 degrees for under temperature alarm and +100 and
+90 degrees for over temperature alarm.
The alarm can be configured in comparator and interrupt mode. Currently only
comparator mode is implemented. In Comparator mode, the OT/UT status bits have a
value of 1 when the temperature rises above the TH value or falls below TL,
which is also subject to the Fault Queue selection. OT status returns to 0 when
the temperature drops below the TH_HYST value or when shutdown mode is entered.
Similarly, UT status returns to 0 when the temperature rises above TL_HYST value
or when shutdown mode is entered.
The alarm can be configured in comparator and interrupt mode from the
devicetree. In Comparator mode, the OT/UT status bits have a value of 1 when the
temperature rises above the TH value or falls below TL, which is also subject to
the Fault Queue selection. OT status returns to 0 when the temperature drops
below the TH_HYST value or when shutdown mode is entered. Similarly, UT status
returns to 0 when the temperature rises above TL_HYST value or when shutdown
mode is entered.
In interrupt mode exceeding TH also sets OT status to 1, which remains set until
a read operation is performed on the configuration/status register (max or min
attribute); at this point, it returns to 0. Once OT status is set to 1 from
exceeding TH and reset, it is set to 1 again only when the temperature drops
below TH_HYST. The output remains asserted until it is reset by a read. It is
set again if the temperature rises above TH, and so on. The same logic applies
to the operation of the UT status bit.
Putting the MAX31827 into shutdown mode also resets the OT/UT status bits. Note
that if the mode is changed while OT/UT status bits are set, an OT/UT status
......@@ -68,13 +76,42 @@ clear the status bits before changing the operating mode.
The conversions can be manual with the one-shot functionality and automatic with
a set frequency. When powered on, the chip measures temperatures with 1 conv/s.
The conversion rate can be modified with update_interval attribute of the chip.
Conversion/second = 1/update_interval. Thus, the available options according to
the data sheet are:
- 64000 (ms) = 1 conv/64 sec
- 32000 (ms) = 1 conv/32 sec
- 16000 (ms) = 1 conv/16 sec
- 4000 (ms) = 1 conv/4 sec
- 1000 (ms) = 1 conv/sec (default)
- 250 (ms) = 4 conv/sec
- 125 (ms) = 8 conv/sec
Enabling the device when it is already enabled has the side effect of setting
the conversion frequency to 1 conv/s. The conversion time varies depending on
the resolution. The conversion time doubles with every bit of increased
resolution. For 10 bit resolution 35ms are needed, while for 12 bit resolution
(default) 140ms. When chip is in shutdown mode and a read operation is
requested, one-shot is triggered, the device waits for 140 (conversion time) ms,
and only after that is the temperature value register read.
the resolution.
The conversion time doubles with every bit of increased resolution. The
available resolutions are:
- 8 bit -> 8.75 ms conversion time
- 9 bit -> 17.5 ms conversion time
- 10 bit -> 35 ms conversion time
- 12 bit (default) -> 140 ms conversion time
There is a temp1_resolution attribute which indicates the unit change in the
input temperature in milli-degrees C.
- 1000 mC -> 8 bit
- 500 mC -> 9 bit
- 250 mC -> 10 bit
- 62 mC -> 12 bit (default) - actually this is 62.5, but the fil returns 62
When chip is in shutdown mode and a read operation is requested, one-shot is
triggered, the device waits for <conversion time> ms, and only after that is
the temperature value register read. Note that the conversion times are rounded
up to the nearest possible integer.
The LSB of the temperature values is 0.0625 degrees Celsius, but the values of
the temperatures are displayed in milli-degrees. This means, that some data is
......@@ -83,8 +120,18 @@ in the writing of alarm values too. For positive numbers the user-input value
will always be rounded down to the nearest possible value, for negative numbers
the user-input will always be rounded up to the nearest possible value.
Bus timeout resets the I2C-compatible interface when SCL is low for more than
30ms (nominal).
Alarm polarity determines if the active state of the alarm is low or high. The
behavior for both settings is dependent on the Fault Queue setting. The ALARM
pin is an open-drain output and requires a pullup resistor to operate.
The Fault Queue bits select how many consecutive temperature faults must occur
before overtemperature or undertemperature faults are indicated in the
corresponding status bits.
Notes
-----
Currently fault queue, alarm polarity and resolution cannot be modified.
PEC is not implemented either.
PEC is not implemented.
.. SPDX-License-Identifier: GPL-2.0
Kernel driver mp2856
====================
Supported chips:
* MPS MP2856
Prefix: 'mp2856'
* MPS MP2857
Prefix: 'mp2857'
Author:
Peter Yin <peter.yin@quantatw.com>
Description
-----------
This driver implements support for Monolithic Power Systems, Inc. (MPS)
vendor dual-loop, digital, multi-phase controller MP2856/MP2857
This device:
- Supports up to two power rail.
- Supports two pages 0 and 1 for and also pages 2 for configuration.
- Can configured VOUT readout in direct or VID format and allows
setting of different formats on rails 1 and 2. For VID the following
protocols are available: AMD SVI3 mode with 5-mV/LSB.
Device supports:
- SVID interface.
- AVSBus interface.
Device compliant with:
- PMBus rev 1.3 interface.
Device supports direct format for reading output current, output voltage,
input and output power and temperature.
Device supports linear format for reading input voltage and input power.
Device supports VID and direct formats for reading output voltage.
The below VID modes are supported: AMD SVI3.
The driver provides the following sysfs attributes for current measurements:
- indexes 1 for "iin";
- indexes 2, 3 for "iout";
**curr[1-3]_alarm**
**curr[1-3]_input**
**curr[1-3]_label**
The driver provides the following sysfs attributes for voltage measurements.
- indexes 1 for "vin";
- indexes 2, 3 for "vout";
**in[1-3]_crit**
**in[1-3]_crit_alarm**
**in[1-3]_input**
**in[1-3]_label**
**in[1-3]_lcrit**
**in[1-3]_lcrit_alarm**
The driver provides the following sysfs attributes for power measurements.
- indexes 1 for "pin";
- indexes 2, 3 for "pout";
**power[1-3]_alarm**
**power[1-3]_input**
**power[1-3]_label**
The driver provides the following sysfs attributes for temperature measurements.
**temp[1-2]_crit**
**temp[1-2]_crit_alarm**
**temp[1-2]_input**
**temp[1-2]_max**
**temp[1-2]_max_alarm**
.. SPDX-License-Identifier: GPL-2.0
Kernel driver mp5990
====================
Supported chips:
* MPS MP5990
Prefix: 'mp5990'
* Datasheet
Publicly available at the MPS website : https://www.monolithicpower.com/en/mp5990.html
Author:
Peter Yin <peteryin.openbmc@gmail.com>
Description
-----------
This driver implements support for Monolithic Power Systems, Inc. (MPS)
MP5990 Hot-Swap Controller.
Device compliant with:
- PMBus rev 1.3 interface.
Device supports direct and linear format for reading input voltage,
output voltage, output current, input power and temperature.
The driver exports the following attributes via the 'sysfs' files
for input voltage:
**in1_input**
**in1_label**
**in1_max**
**in1_max_alarm**
**in1_min**
**in1_min_alarm**
The driver provides the following attributes for output voltage:
**in2_input**
**in2_label**
**in2_alarm**
The driver provides the following attributes for output current:
**curr1_input**
**curr1_label**
**curr1_alarm**
**curr1_max**
The driver provides the following attributes for input power:
**power1_input**
**power1_label**
**power1_alarm**
The driver provides the following attributes for temperature:
**temp1_input**
**temp1_max**
**temp1_max_alarm**
**temp1_crit**
**temp1_crit_alarm**
......@@ -9,7 +9,19 @@ Supported chips:
Addresses scanned: none
Datasheet: https://www.sensirion.com/file/datasheet_sht3x_digital
Datasheets:
- https://sensirion.com/media/documents/213E6A3B/63A5A569/Datasheet_SHT3x_DIS.pdf
- https://sensirion.com/media/documents/051DF50B/639C8101/Sensirion_Humidity_and_Temperature_Sensors_Datasheet_SHT33.pdf
* Sensirion STS3x-DIS
Prefix: 'sts3x'
Addresses scanned: none
Datasheets:
- https://sensirion.com/media/documents/1DA31AFD/61641F76/Sensirion_Temperature_Sensors_STS3x_Datasheet.pdf
- https://sensirion.com/media/documents/292A335C/65537BAF/Sensirion_Datasheet_STS32_STS33.pdf
Author:
......@@ -19,16 +31,17 @@ Author:
Description
-----------
This driver implements support for the Sensirion SHT3x-DIS chip, a humidity
and temperature sensor. Temperature is measured in degrees celsius, relative
humidity is expressed as a percentage. In the sysfs interface, all values are
scaled by 1000, i.e. the value for 31.5 degrees celsius is 31500.
This driver implements support for the Sensirion SHT3x-DIS and STS3x-DIS
series of humidity and temperature sensors. Temperature is measured in degrees
celsius, relative humidity is expressed as a percentage. In the sysfs interface,
all values are scaled by 1000, i.e. the value for 31.5 degrees celsius is 31500.
The device communicates with the I2C protocol. Sensors can have the I2C
addresses 0x44 or 0x45, depending on the wiring. See
Documentation/i2c/instantiating-devices.rst for methods to instantiate the device.
addresses 0x44 or 0x45 (0x4a or 0x4b for sts3x), depending on the wiring. See
Documentation/i2c/instantiating-devices.rst for methods to instantiate the
device.
Even if sht3x sensor supports clock-strech(blocking mode) and non-strench
Even if sht3x sensor supports clock-stretch (blocking mode) and non-stretch
(non-blocking mode) in single-shot mode, this driver only supports the latter.
The sht3x sensor supports a single shot mode as well as 5 periodic measure
......
......@@ -3451,6 +3451,14 @@ F: drivers/video/backlight/
F: include/linux/backlight.h
F: include/linux/pwm_backlight.h
BAIKAL-T1 PVT HARDWARE MONITOR DRIVER
M: Serge Semin <fancer.lancer@gmail.com>
L: linux-hwmon@vger.kernel.org
S: Supported
F: Documentation/devicetree/bindings/hwmon/baikal,bt1-pvt.yaml
F: Documentation/hwmon/bt1-pvt.rst
F: drivers/hwmon/bt1-pvt.[ch]
BARCO P50 GPIO DRIVER
M: Santosh Kumar Yadav <santoshkumar.yadav@barco.com>
M: Peter Korsgaard <peter.korsgaard@barco.com>
......@@ -8948,6 +8956,13 @@ F: Documentation/filesystems/gfs2*
F: fs/gfs2/
F: include/uapi/linux/gfs2_ondisk.h
GIGABYTE WATERFORCE SENSOR DRIVER
M: Aleksa Savic <savicaleksa83@gmail.com>
L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/gigabyte_waterforce.rst
F: drivers/hwmon/gigabyte_waterforce.c
GIGABYTE WMI DRIVER
M: Thomas Weißschuh <thomas@weissschuh.net>
L: platform-driver-x86@vger.kernel.org
......@@ -12686,6 +12701,16 @@ S: Maintained
F: Documentation/hwmon/ltc4261.rst
F: drivers/hwmon/ltc4261.c
LTC4286 HARDWARE MONITOR DRIVER
M: Delphine CC Chiu <Delphine_CC_Chiu@Wiwynn.com>
L: linux-i2c@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/hwmon/lltc,ltc4286.yaml
F: Documentation/hwmon/ltc4286.rst
F: drivers/hwmon/pmbus/Kconfig
F: drivers/hwmon/pmbus/Makefile
F: drivers/hwmon/pmbus/ltc4286.c
LTC4306 I2C MULTIPLEXER DRIVER
M: Michael Hennerich <michael.hennerich@analog.com>
L: linux-i2c@vger.kernel.org
......
......@@ -512,6 +512,7 @@ config SENSORS_DS1621
config SENSORS_DELL_SMM
tristate "Dell laptop SMM BIOS hwmon driver"
depends on ACPI_WMI
depends on X86
imply THERMAL
help
......@@ -663,6 +664,16 @@ config SENSORS_FTSTEUTATES
This driver can also be built as a module. If so, the module
will be called ftsteutates.
config SENSORS_GIGABYTE_WATERFORCE
tristate "Gigabyte Waterforce X240/X280/X360 AIO CPU coolers"
depends on USB_HID
help
If you say yes here you get support for hardware monitoring for the
Gigabyte Waterforce X240/X280/X360 all-in-one CPU liquid coolers.
This driver can also be built as a module. If so, the module
will be called gigabyte_waterforce.
config SENSORS_GL518SM
tristate "Genesys Logic GL518SM"
depends on I2C
......
......@@ -80,6 +80,7 @@ obj-$(CONFIG_SENSORS_FSCHMD) += fschmd.o
obj-$(CONFIG_SENSORS_FTSTEUTATES) += ftsteutates.o
obj-$(CONFIG_SENSORS_G760A) += g760a.o
obj-$(CONFIG_SENSORS_G762) += g762.o
obj-$(CONFIG_SENSORS_GIGABYTE_WATERFORCE) += gigabyte_waterforce.o
obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o
obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o
obj-$(CONFIG_SENSORS_GSC) += gsc-hwmon.o
......
......@@ -1476,8 +1476,6 @@ static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8
return 0;
}
#ifdef CONFIG_DEBUG_FS
static int serial_number_show(struct seq_file *seqf, void *unused)
{
struct aqc_data *priv = seqf->private;
......@@ -1527,14 +1525,6 @@ static void aqc_debugfs_init(struct aqc_data *priv)
debugfs_create_file("power_cycles", 0444, priv->debugfs, priv, &power_cycles_fops);
}
#else
static void aqc_debugfs_init(struct aqc_data *priv)
{
}
#endif
static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
struct aqc_data *priv;
......
......@@ -166,6 +166,8 @@
#define MAX_CDEV_NAME_LEN 16
#define MAX_ASPEED_FAN_TACH_CHANNELS 16
struct aspeed_cooling_device {
char name[16];
struct aspeed_pwm_tacho_data *priv;
......@@ -181,7 +183,7 @@ struct aspeed_pwm_tacho_data {
struct reset_control *rst;
unsigned long clk_freq;
bool pwm_present[8];
bool fan_tach_present[16];
bool fan_tach_present[MAX_ASPEED_FAN_TACH_CHANNELS];
u8 type_pwm_clock_unit[3];
u8 type_pwm_clock_division_h[3];
u8 type_pwm_clock_division_l[3];
......@@ -190,7 +192,7 @@ struct aspeed_pwm_tacho_data {
u16 type_fan_tach_unit[3];
u8 pwm_port_type[8];
u8 pwm_port_fan_ctrl[8];
u8 fan_tach_ch_source[16];
u8 fan_tach_ch_source[MAX_ASPEED_FAN_TACH_CHANNELS];
struct aspeed_cooling_device *cdev[8];
const struct attribute_group *groups[3];
};
......@@ -737,20 +739,27 @@ static void aspeed_create_pwm_port(struct aspeed_pwm_tacho_data *priv,
aspeed_set_pwm_port_fan_ctrl(priv, pwm_port, INIT_FAN_CTRL);
}
static void aspeed_create_fan_tach_channel(struct aspeed_pwm_tacho_data *priv,
u8 *fan_tach_ch,
int count,
u8 pwm_source)
static int aspeed_create_fan_tach_channel(struct device *dev,
struct aspeed_pwm_tacho_data *priv,
u8 *fan_tach_ch,
int count,
u8 pwm_source)
{
u8 val, index;
for (val = 0; val < count; val++) {
index = fan_tach_ch[val];
if (index >= MAX_ASPEED_FAN_TACH_CHANNELS) {
dev_err(dev, "Invalid Fan Tach input channel %u\n.", index);
return -EINVAL;
}
aspeed_set_fan_tach_ch_enable(priv->regmap, index, true);
priv->fan_tach_present[index] = true;
priv->fan_tach_ch_source[index] = pwm_source;
aspeed_set_fan_tach_ch_source(priv->regmap, index, pwm_source);
}
return 0;
}
static int
......@@ -874,7 +883,10 @@ static int aspeed_create_fan(struct device *dev,
fan_tach_ch, count);
if (ret)
return ret;
aspeed_create_fan_tach_channel(priv, fan_tach_ch, count, pwm_port);
ret = aspeed_create_fan_tach_channel(dev, priv, fan_tach_ch, count, pwm_port);
if (ret)
return ret;
return 0;
}
......
......@@ -524,7 +524,7 @@ static int ccp_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (ret)
goto out_hw_close;
ccp->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "corsaircpro",
ccp, &ccp_chip_info, 0);
ccp, &ccp_chip_info, NULL);
if (IS_ERR(ccp->hwmon_dev)) {
ret = PTR_ERR(ccp->hwmon_dev);
goto out_hw_close;
......
This diff is collapsed.
......@@ -346,6 +346,9 @@ static int emc1403_detect(struct i2c_client *client,
case 0x27:
strscpy(info->type, "emc1424", I2C_NAME_SIZE);
break;
case 0x60:
strscpy(info->type, "emc1442", I2C_NAME_SIZE);
break;
default:
return -ENODEV;
}
......@@ -430,7 +433,7 @@ static int emc1403_probe(struct i2c_client *client)
}
static const unsigned short emc1403_address_list[] = {
0x18, 0x1c, 0x29, 0x4c, 0x4d, 0x5c, I2C_CLIENT_END
0x18, 0x1c, 0x29, 0x3c, 0x4c, 0x4d, 0x5c, I2C_CLIENT_END
};
/* Last digit of chip name indicates number of channels */
......@@ -444,6 +447,7 @@ static const struct i2c_device_id emc1403_idtable[] = {
{ "emc1422", emc1402 },
{ "emc1423", emc1403 },
{ "emc1424", emc1404 },
{ "emc1442", emc1402 },
{ }
};
MODULE_DEVICE_TABLE(i2c, emc1403_idtable);
......
This diff is collapsed.
......@@ -17,6 +17,8 @@
* Available: https://github.com/linuxhw/ACPI
* [4] P. Rohár, "bmfdec - Decompile binary MOF file (BMF) from WMI buffer",
* 2017. [Online]. Available: https://github.com/pali/bmfdec
* [5] Microsoft Corporation, "Driver-Defined WMI Data Items", 2017. [Online].
* Available: https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/driver-defined-wmi-data-items
*/
#include <linux/acpi.h>
......@@ -24,6 +26,7 @@
#include <linux/hwmon.h>
#include <linux/jiffies.h>
#include <linux/mutex.h>
#include <linux/nls.h>
#include <linux/units.h>
#include <linux/wmi.h>
......@@ -395,6 +398,50 @@ struct hp_wmi_sensors {
struct mutex lock; /* Lock polling WMI and driver state changes. */
};
static bool is_raw_wmi_string(const u8 *pointer, u32 length)
{
const u16 *ptr;
u16 len;
/* WMI strings are length-prefixed UTF-16 [5]. */
if (length <= sizeof(*ptr))
return false;
length -= sizeof(*ptr);
ptr = (const u16 *)pointer;
len = *ptr;
return len <= length && !(len & 1);
}
static char *convert_raw_wmi_string(const u8 *buf)
{
const wchar_t *src;
unsigned int cps;
unsigned int len;
char *dst;
int i;
src = (const wchar_t *)buf;
/* Count UTF-16 code points. Exclude trailing null padding. */
cps = *src / sizeof(*src);
while (cps && !src[cps])
cps--;
/* Each code point becomes up to 3 UTF-8 characters. */
len = min(cps * 3, HP_WMI_MAX_STR_SIZE - 1);
dst = kmalloc((len + 1) * sizeof(*dst), GFP_KERNEL);
if (!dst)
return NULL;
i = utf16s_to_utf8s(++src, cps, UTF16_LITTLE_ENDIAN, dst, len);
dst[i] = '\0';
return dst;
}
/* hp_wmi_strdup - devm_kstrdup, but length-limited */
static char *hp_wmi_strdup(struct device *dev, const char *src)
{
......@@ -412,6 +459,23 @@ static char *hp_wmi_strdup(struct device *dev, const char *src)
return dst;
}
/* hp_wmi_wstrdup - hp_wmi_strdup, but for a raw WMI string */
static char *hp_wmi_wstrdup(struct device *dev, const u8 *buf)
{
char *src;
char *dst;
src = convert_raw_wmi_string(buf);
if (!src)
return NULL;
dst = hp_wmi_strdup(dev, strim(src)); /* Note: Copy is trimmed. */
kfree(src);
return dst;
}
/*
* hp_wmi_get_wobj - poll WMI for a WMI object instance
* @guid: WMI object GUID
......@@ -462,8 +526,14 @@ static int check_wobj(const union acpi_object *wobj,
for (prop = 0; prop <= last_prop; prop++) {
type = elements[prop].type;
valid_type = property_map[prop];
if (type != valid_type)
if (type != valid_type) {
if (type == ACPI_TYPE_BUFFER &&
valid_type == ACPI_TYPE_STRING &&
is_raw_wmi_string(elements[prop].buffer.pointer,
elements[prop].buffer.length))
continue;
return -EINVAL;
}
}
return 0;
......@@ -480,7 +550,9 @@ static int extract_acpi_value(struct device *dev,
break;
case ACPI_TYPE_STRING:
*out_string = hp_wmi_strdup(dev, strim(element->string.pointer));
*out_string = element->type == ACPI_TYPE_BUFFER ?
hp_wmi_wstrdup(dev, element->buffer.pointer) :
hp_wmi_strdup(dev, strim(element->string.pointer));
if (!*out_string)
return -ENOMEM;
break;
......@@ -861,7 +933,9 @@ update_numeric_sensor_from_wobj(struct device *dev,
{
const union acpi_object *elements;
const union acpi_object *element;
const char *string;
const char *new_string;
char *trimmed;
char *string;
bool is_new;
int offset;
u8 size;
......@@ -885,11 +959,21 @@ update_numeric_sensor_from_wobj(struct device *dev,
offset = is_new ? size - 1 : -2;
element = &elements[HP_WMI_PROPERTY_CURRENT_STATE + offset];
string = strim(element->string.pointer);
if (strcmp(string, nsensor->current_state)) {
devm_kfree(dev, nsensor->current_state);
nsensor->current_state = hp_wmi_strdup(dev, string);
string = element->type == ACPI_TYPE_BUFFER ?
convert_raw_wmi_string(element->buffer.pointer) :
element->string.pointer;
if (string) {
trimmed = strim(string);
if (strcmp(trimmed, nsensor->current_state)) {
new_string = hp_wmi_strdup(dev, trimmed);
if (new_string) {
devm_kfree(dev, nsensor->current_state);
nsensor->current_state = new_string;
}
}
if (element->type == ACPI_TYPE_BUFFER)
kfree(string);
}
/* Old variant: -2 (not -1) because it lacks the Size property. */
......@@ -996,11 +1080,15 @@ static int check_event_wobj(const union acpi_object *wobj)
HP_WMI_EVENT_PROPERTY_STATUS);
}
static int populate_event_from_wobj(struct hp_wmi_event *event,
static int populate_event_from_wobj(struct device *dev,
struct hp_wmi_event *event,
union acpi_object *wobj)
{
int prop = HP_WMI_EVENT_PROPERTY_NAME;
union acpi_object *element;
acpi_object_type type;
char *string;
u32 value;
int err;
err = check_event_wobj(wobj);
......@@ -1009,20 +1097,24 @@ static int populate_event_from_wobj(struct hp_wmi_event *event,
element = wobj->package.elements;
/* Extracted strings are NOT device-managed copies. */
for (; prop <= HP_WMI_EVENT_PROPERTY_CATEGORY; prop++, element++) {
type = hp_wmi_event_property_map[prop];
err = extract_acpi_value(dev, element, type, &value, &string);
if (err)
return err;
switch (prop) {
case HP_WMI_EVENT_PROPERTY_NAME:
event->name = strim(element->string.pointer);
event->name = string;
break;
case HP_WMI_EVENT_PROPERTY_DESCRIPTION:
event->description = strim(element->string.pointer);
event->description = string;
break;
case HP_WMI_EVENT_PROPERTY_CATEGORY:
event->category = element->integer.value;
event->category = value;
break;
default:
......@@ -1511,8 +1603,8 @@ static void hp_wmi_notify(u32 value, void *context)
struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
struct hp_wmi_sensors *state = context;
struct device *dev = &state->wdev->dev;
struct hp_wmi_event event = {};
struct hp_wmi_info *fan_info;
struct hp_wmi_event event;
union acpi_object *wobj;
acpi_status err;
int event_type;
......@@ -1546,7 +1638,7 @@ static void hp_wmi_notify(u32 value, void *context)
wobj = out.pointer;
err = populate_event_from_wobj(&event, wobj);
err = populate_event_from_wobj(dev, &event, wobj);
if (err) {
dev_warn(dev, "Bad event data (ACPI type %d)\n", wobj->type);
goto out_free_wobj;
......@@ -1577,6 +1669,9 @@ static void hp_wmi_notify(u32 value, void *context)
out_free_wobj:
kfree(wobj);
devm_kfree(dev, event.name);
devm_kfree(dev, event.description);
out_unlock:
mutex_unlock(&state->lock);
}
......
......@@ -455,6 +455,7 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
switch (boot_cpu_data.x86_model) {
case 0x0 ... 0x1: /* Zen3 SP3/TR */
case 0x8: /* Zen3 TR Chagall */
case 0x21: /* Zen3 Ryzen Desktop */
case 0x50 ... 0x5f: /* Green Sardine */
data->ccd_offset = 0x154;
......
......@@ -7,11 +7,11 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/of.h>
#include <linux/regmap.h>
......@@ -25,6 +25,7 @@
enum lm75_type { /* keep sorted in alphabetical order */
adt75,
as6200,
at30ts74,
ds1775,
ds75,
......@@ -55,6 +56,7 @@ enum lm75_type { /* keep sorted in alphabetical order */
/**
* struct lm75_params - lm75 configuration parameters.
* @config_reg_16bits: Configure register size is 2 bytes.
* @set_mask: Bits to set in configuration register when configuring
* the chip.
* @clr_mask: Bits to clear in configuration register when configuring
......@@ -75,17 +77,20 @@ enum lm75_type { /* keep sorted in alphabetical order */
* @sample_times: All the possible sample times to be set. Mandatory if
* num_sample_times is larger than 1. If set, number of
* entries must match num_sample_times.
* @alarm: Alarm bit is supported.
*/
struct lm75_params {
u8 set_mask;
u8 clr_mask;
bool config_reg_16bits;
u16 set_mask;
u16 clr_mask;
u8 default_resolution;
u8 resolution_limits;
const u8 *resolutions;
unsigned int default_sample_time;
u8 num_sample_times;
const unsigned int *sample_times;
bool alarm;
};
/* Addresses scanned */
......@@ -104,8 +109,8 @@ struct lm75_data {
struct i2c_client *client;
struct regmap *regmap;
struct regulator *vs;
u8 orig_conf;
u8 current_conf;
u16 orig_conf;
u16 current_conf;
u8 resolution; /* In bits, 9 to 16 */
unsigned int sample_time; /* In ms */
enum lm75_type kind;
......@@ -128,6 +133,15 @@ static const struct lm75_params device_params[] = {
.default_resolution = 12,
.default_sample_time = MSEC_PER_SEC / 10,
},
[as6200] = {
.config_reg_16bits = true,
.set_mask = 0x94C0, /* 8 sample/s, 4 CF, positive polarity */
.default_resolution = 12,
.default_sample_time = 125,
.num_sample_times = 4,
.sample_times = (unsigned int []){ 125, 250, 1000, 4000 },
.alarm = true,
},
[at30ts74] = {
.set_mask = 3 << 5, /* 12-bit mode*/
.default_resolution = 12,
......@@ -255,8 +269,9 @@ static const struct lm75_params device_params[] = {
.resolutions = (u8 []) {9, 10, 11, 12 },
},
[tmp112] = {
.set_mask = 3 << 5, /* 8 samples / second */
.clr_mask = 1 << 7, /* no one-shot mode*/
.config_reg_16bits = true,
.set_mask = 0x60C0, /* 12-bit mode, 8 samples / second */
.clr_mask = 1 << 15, /* no one-shot mode*/
.default_resolution = 12,
.default_sample_time = 125,
.num_sample_times = 4,
......@@ -317,20 +332,23 @@ static inline long lm75_reg_to_mc(s16 temp, u8 resolution)
return ((temp >> (16 - resolution)) * 1000) >> (resolution - 8);
}
static int lm75_write_config(struct lm75_data *data, u8 set_mask,
u8 clr_mask)
static int lm75_write_config(struct lm75_data *data, u16 set_mask,
u16 clr_mask)
{
u8 value;
unsigned int value;
clr_mask |= LM75_SHUTDOWN;
clr_mask |= LM75_SHUTDOWN << (8 * data->params->config_reg_16bits);
value = data->current_conf & ~clr_mask;
value |= set_mask;
if (data->current_conf != value) {
s32 err;
err = i2c_smbus_write_byte_data(data->client, LM75_REG_CONF,
value);
if (data->params->config_reg_16bits)
err = regmap_write(data->regmap, LM75_REG_CONF, value);
else
err = i2c_smbus_write_byte_data(data->client,
LM75_REG_CONF,
value);
if (err)
return err;
data->current_conf = value;
......@@ -338,6 +356,27 @@ static int lm75_write_config(struct lm75_data *data, u8 set_mask,
return 0;
}
static int lm75_read_config(struct lm75_data *data)
{
int ret;
unsigned int status;
if (data->params->config_reg_16bits) {
ret = regmap_read(data->regmap, LM75_REG_CONF, &status);
return ret ? ret : status;
}
return i2c_smbus_read_byte_data(data->client, LM75_REG_CONF);
}
static irqreturn_t lm75_alarm_handler(int irq, void *private)
{
struct device *hwmon_dev = private;
hwmon_notify_event(hwmon_dev, hwmon_temp, hwmon_temp_alarm, 0);
return IRQ_HANDLED;
}
static int lm75_read(struct device *dev, enum hwmon_sensor_types type,
u32 attr, int channel, long *val)
{
......@@ -366,6 +405,9 @@ static int lm75_read(struct device *dev, enum hwmon_sensor_types type,
case hwmon_temp_max_hyst:
reg = LM75_REG_HYST;
break;
case hwmon_temp_alarm:
reg = LM75_REG_CONF;
break;
default:
return -EINVAL;
}
......@@ -373,7 +415,17 @@ static int lm75_read(struct device *dev, enum hwmon_sensor_types type,
if (err < 0)
return err;
*val = lm75_reg_to_mc(regval, data->resolution);
if (attr == hwmon_temp_alarm) {
switch (data->kind) {
case as6200:
*val = (regval >> 5) & 0x1;
break;
default:
return -EINVAL;
}
} else {
*val = lm75_reg_to_mc(regval, data->resolution);
}
break;
default:
return -EINVAL;
......@@ -436,6 +488,7 @@ static int lm75_update_interval(struct device *dev, long val)
data->resolution = data->params->resolutions[index];
break;
case tmp112:
case as6200:
err = regmap_read(data->regmap, LM75_REG_CONF, &reg);
if (err < 0)
return err;
......@@ -503,6 +556,10 @@ static umode_t lm75_is_visible(const void *data, enum hwmon_sensor_types type,
case hwmon_temp_max:
case hwmon_temp_max_hyst:
return 0644;
case hwmon_temp_alarm:
if (config_data->params->alarm)
return 0444;
break;
}
break;
default:
......@@ -515,7 +572,8 @@ static const struct hwmon_channel_info * const lm75_info[] = {
HWMON_CHANNEL_INFO(chip,
HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL),
HWMON_CHANNEL_INFO(temp,
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST),
HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
HWMON_T_ALARM),
NULL
};
......@@ -623,7 +681,7 @@ static int lm75_probe(struct i2c_client *client)
return err;
/* Cache original configuration */
status = i2c_smbus_read_byte_data(client, LM75_REG_CONF);
status = lm75_read_config(data);
if (status < 0) {
dev_dbg(dev, "Can't read config? %d\n", status);
return status;
......@@ -646,6 +704,23 @@ static int lm75_probe(struct i2c_client *client)
if (IS_ERR(hwmon_dev))
return PTR_ERR(hwmon_dev);
if (client->irq) {
if (data->params->alarm) {
err = devm_request_threaded_irq(dev,
client->irq,
NULL,
&lm75_alarm_handler,
IRQF_ONESHOT,
client->name,
hwmon_dev);
if (err)
return err;
} else {
/* alarm is only supported for chips with alarm bit */
dev_err(dev, "alarm interrupt is not supported\n");
}
}
dev_info(dev, "%s: sensor '%s'\n", dev_name(hwmon_dev), client->name);
return 0;
......@@ -653,6 +728,7 @@ static int lm75_probe(struct i2c_client *client)
static const struct i2c_device_id lm75_ids[] = {
{ "adt75", adt75, },
{ "as6200", as6200, },
{ "at30ts74", at30ts74, },
{ "ds1775", ds1775, },
{ "ds75", ds75, },
......@@ -689,6 +765,10 @@ static const struct of_device_id __maybe_unused lm75_of_match[] = {
.compatible = "adi,adt75",
.data = (void *)adt75
},
{
.compatible = "ams,as6200",
.data = (void *)as6200
},
{
.compatible = "atmel,at30ts74",
.data = (void *)at30ts74
......
......@@ -54,7 +54,6 @@
#define LTC2991_VCC_CH_NR 0
struct ltc2991_state {
struct device *dev;
struct regmap *regmap;
u32 r_sense_uohm[LTC2991_MAX_CHANNEL];
bool temp_en[LTC2991_MAX_CHANNEL];
......@@ -283,19 +282,19 @@ static const struct regmap_config ltc2991_regmap_config = {
.max_register = 0x1D,
};
static int ltc2991_init(struct ltc2991_state *st)
static int ltc2991_init(struct ltc2991_state *st, struct device *dev)
{
struct fwnode_handle *child;
int ret;
u32 val, addr;
u8 v5_v8_reg_data = 0, v1_v4_reg_data = 0;
ret = devm_regulator_get_enable(st->dev, "vcc");
ret = devm_regulator_get_enable(dev, "vcc");
if (ret)
return dev_err_probe(st->dev, ret,
return dev_err_probe(dev, ret,
"failed to enable regulator\n");
device_for_each_child_node(st->dev, child) {
device_for_each_child_node(dev, child) {
ret = fwnode_property_read_u32(child, "reg", &addr);
if (ret < 0) {
fwnode_handle_put(child);
......@@ -312,7 +311,7 @@ static int ltc2991_init(struct ltc2991_state *st)
&val);
if (!ret) {
if (!val)
return dev_err_probe(st->dev, -EINVAL,
return dev_err_probe(dev, -EINVAL,
"shunt resistor value cannot be zero\n");
st->r_sense_uohm[addr] = val;
......@@ -361,18 +360,18 @@ static int ltc2991_init(struct ltc2991_state *st)
ret = regmap_write(st->regmap, LTC2991_V5_V8_CTRL, v5_v8_reg_data);
if (ret)
return dev_err_probe(st->dev, ret,
return dev_err_probe(dev, ret,
"Error: Failed to set V5-V8 CTRL reg.\n");
ret = regmap_write(st->regmap, LTC2991_V1_V4_CTRL, v1_v4_reg_data);
if (ret)
return dev_err_probe(st->dev, ret,
return dev_err_probe(dev, ret,
"Error: Failed to set V1-V4 CTRL reg.\n");
ret = regmap_write(st->regmap, LTC2991_PWM_TH_LSB_T_INT,
LTC2991_REPEAT_ACQ_EN);
if (ret)
return dev_err_probe(st->dev, ret,
return dev_err_probe(dev, ret,
"Error: Failed to set continuous mode.\n");
/* Enable all channels and trigger conversions */
......@@ -392,12 +391,11 @@ static int ltc2991_i2c_probe(struct i2c_client *client)
if (!st)
return -ENOMEM;
st->dev = &client->dev;
st->regmap = devm_regmap_init_i2c(client, &ltc2991_regmap_config);
if (IS_ERR(st->regmap))
return PTR_ERR(st->regmap);
ret = ltc2991_init(st);
ret = ltc2991_init(st, &client->dev);
if (ret)
return ret;
......
This diff is collapsed.
......@@ -26,7 +26,7 @@
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/of_device.h>
#include <linux/of.h>
#include <linux/thermal.h>
/*
......@@ -763,8 +763,6 @@ static int max6650_probe(struct i2c_client *client)
{
struct thermal_cooling_device *cooling_dev;
struct device *dev = &client->dev;
const struct of_device_id *of_id =
of_match_device(of_match_ptr(max6650_dt_match), dev);
struct max6650_data *data;
struct device *hwmon_dev;
int err;
......@@ -776,8 +774,8 @@ static int max6650_probe(struct i2c_client *client)
data->client = client;
i2c_set_clientdata(client, data);
mutex_init(&data->update_lock);
data->nr_fans = of_id ? (int)(uintptr_t)of_id->data :
i2c_match_id(max6650_id, client)->driver_data;
data->nr_fans = (uintptr_t)i2c_get_match_data(client);
/*
* Initialize the max6650 chip
......
......@@ -63,19 +63,19 @@
/* used to set data->name = nct6775_device_names[data->sio_kind] */
static const char * const nct6775_device_names[] = {
"nct6106",
"nct6116",
"nct6775",
"nct6776",
"nct6779",
"nct6791",
"nct6792",
"nct6793",
"nct6795",
"nct6796",
"nct6797",
"nct6798",
"nct6799",
[nct6106] = "nct6106",
[nct6116] = "nct6116",
[nct6775] = "nct6775",
[nct6776] = "nct6776",
[nct6779] = "nct6779",
[nct6791] = "nct6791",
[nct6792] = "nct6792",
[nct6793] = "nct6793",
[nct6795] = "nct6795",
[nct6796] = "nct6796",
[nct6797] = "nct6797",
[nct6798] = "nct6798",
[nct6799] = "nct6799",
};
/* Common and NCT6775 specific data */
......@@ -767,9 +767,9 @@ static const u16 NCT6106_REG_FAN_MIN[] = { 0xe0, 0xe2, 0xe4 };
static const u16 NCT6106_REG_FAN_PULSES[] = { 0xf6, 0xf6, 0xf6 };
static const u16 NCT6106_FAN_PULSE_SHIFT[] = { 0, 2, 4 };
static const u8 NCT6106_REG_PWM_MODE[] = { 0xf3, 0xf3, 0xf3 };
static const u8 NCT6106_PWM_MODE_MASK[] = { 0x01, 0x02, 0x04 };
static const u16 NCT6106_REG_PWM_READ[] = { 0x4a, 0x4b, 0x4c };
static const u8 NCT6106_REG_PWM_MODE[] = { 0xf3, 0xf3, 0xf3, 0, 0 };
static const u8 NCT6106_PWM_MODE_MASK[] = { 0x01, 0x02, 0x04, 0, 0 };
static const u16 NCT6106_REG_PWM_READ[] = { 0x4a, 0x4b, 0x4c, 0xd8, 0xd9 };
static const u16 NCT6106_REG_FAN_MODE[] = { 0x113, 0x123, 0x133 };
static const u16 NCT6106_REG_TEMP_SOURCE[] = {
0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5 };
......@@ -2553,6 +2553,13 @@ store_pwm(struct device *dev, struct device_attribute *attr, const char *buf,
int err;
u16 reg;
/*
* The fan control mode should be set to manual if the user wants to adjust
* the fan speed. Otherwise, it will fail to set.
*/
if (index == 0 && data->pwm_enable[nr] > manual)
return -EBUSY;
err = kstrtoul(buf, 10, &val);
if (err < 0)
return err;
......@@ -3595,7 +3602,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data,
break;
case nct6116:
data->in_num = 9;
data->pwm_num = 3;
data->pwm_num = 5;
data->auto_pwm_num = 4;
data->temp_fixed_num = 3;
data->num_temp_alarms = 3;
......
......@@ -21,7 +21,7 @@
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/of_device.h>
#include <linux/of.h>
#include <linux/regmap.h>
#include "nct6775.h"
......@@ -155,23 +155,13 @@ static const struct regmap_config nct6775_i2c_regmap_config = {
static int nct6775_i2c_probe(struct i2c_client *client)
{
struct nct6775_data *data;
const struct of_device_id *of_id;
const struct i2c_device_id *i2c_id;
struct device *dev = &client->dev;
of_id = of_match_device(nct6775_i2c_of_match, dev);
i2c_id = i2c_match_id(nct6775_i2c_id, client);
if (of_id && (unsigned long)of_id->data != i2c_id->driver_data)
dev_notice(dev, "Device mismatch: %s in device tree, %s detected\n",
of_id->name, i2c_id->name);
data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->kind = i2c_id->driver_data;
data->kind = (enum kinds)(uintptr_t)i2c_get_match_data(client);
data->read_only = true;
data->driver_data = client;
data->driver_init = nct6775_i2c_probe_init;
......
......@@ -23,19 +23,19 @@
enum sensor_access { access_direct, access_asuswmi };
static const char * const nct6775_sio_names[] __initconst = {
"NCT6106D",
"NCT6116D",
"NCT6775F",
"NCT6776D/F",
"NCT6779D",
"NCT6791D",
"NCT6792D",
"NCT6793D",
"NCT6795D",
"NCT6796D",
"NCT6797D",
"NCT6798D",
"NCT6796D-S/NCT6799D-R",
[nct6106] = "NCT6106D",
[nct6116] = "NCT6116D",
[nct6775] = "NCT6775F",
[nct6776] = "NCT6776D/F",
[nct6779] = "NCT6779D",
[nct6791] = "NCT6791D",
[nct6792] = "NCT6792D",
[nct6793] = "NCT6793D",
[nct6795] = "NCT6795D",
[nct6796] = "NCT6796D",
[nct6797] = "NCT6797D",
[nct6798] = "NCT6798D",
[nct6799] = "NCT6796D-S/NCT6799D-R",
};
static unsigned short force_id;
......
......@@ -4,7 +4,7 @@
#include <linux/types.h>
enum kinds { nct6106, nct6116, nct6775, nct6776, nct6779, nct6791, nct6792,
enum kinds { nct6106 = 1, nct6116, nct6775, nct6776, nct6779, nct6791, nct6792,
nct6793, nct6795, nct6796, nct6797, nct6798, nct6799 };
enum pwm_enable { off, manual, thermal_cruise, speed_cruise, sf3, sf4 };
......
......@@ -46,9 +46,9 @@
#define NPCM7XX_PWM_CTRL_CH3_EN_BIT BIT(16)
/* Define the maximum PWM channel number */
#define NPCM7XX_PWM_MAX_CHN_NUM 8
#define NPCM7XX_PWM_MAX_CHN_NUM 12
#define NPCM7XX_PWM_MAX_CHN_NUM_IN_A_MODULE 4
#define NPCM7XX_PWM_MAX_MODULES 2
#define NPCM7XX_PWM_MAX_MODULES 3
/* Define the Counter Register, value = 100 for match 100% */
#define NPCM7XX_PWM_COUNTER_DEFAULT_NUM 255
......@@ -171,6 +171,10 @@
#define FAN_PREPARE_TO_GET_FIRST_CAPTURE 0x01
#define FAN_ENOUGH_SAMPLE 0x02
struct npcm_hwmon_info {
u32 pwm_max_channel;
};
struct npcm7xx_fan_dev {
u8 fan_st_flg;
u8 fan_pls_per_rev;
......@@ -204,6 +208,7 @@ struct npcm7xx_pwm_fan_data {
struct timer_list fan_timer;
struct npcm7xx_fan_dev fan_dev[NPCM7XX_FAN_MAX_CHN_NUM];
struct npcm7xx_cooling_device *cdev[NPCM7XX_PWM_MAX_CHN_NUM];
const struct npcm_hwmon_info *info;
u8 fan_select;
};
......@@ -542,7 +547,7 @@ static umode_t npcm7xx_pwm_is_visible(const void *_data, u32 attr, int channel)
{
const struct npcm7xx_pwm_fan_data *data = _data;
if (!data->pwm_present[channel])
if (!data->pwm_present[channel] || channel >= data->info->pwm_max_channel)
return 0;
switch (attr) {
......@@ -638,6 +643,10 @@ static const struct hwmon_channel_info * const npcm7xx_info[] = {
HWMON_PWM_INPUT,
HWMON_PWM_INPUT,
HWMON_PWM_INPUT,
HWMON_PWM_INPUT,
HWMON_PWM_INPUT,
HWMON_PWM_INPUT,
HWMON_PWM_INPUT,
HWMON_PWM_INPUT),
HWMON_CHANNEL_INFO(fan,
HWMON_F_INPUT,
......@@ -670,6 +679,14 @@ static const struct hwmon_chip_info npcm7xx_chip_info = {
.info = npcm7xx_info,
};
static const struct npcm_hwmon_info npxm7xx_hwmon_info = {
.pwm_max_channel = 8,
};
static const struct npcm_hwmon_info npxm8xx_hwmon_info = {
.pwm_max_channel = 12,
};
static u32 npcm7xx_pwm_init(struct npcm7xx_pwm_fan_data *data)
{
int m, ch;
......@@ -925,6 +942,10 @@ static int npcm7xx_pwm_fan_probe(struct platform_device *pdev)
if (!data)
return -ENOMEM;
data->info = device_get_match_data(dev);
if (!data->info)
return -EINVAL;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwm");
if (!res) {
dev_err(dev, "pwm resource not found\n");
......@@ -1017,7 +1038,8 @@ static int npcm7xx_pwm_fan_probe(struct platform_device *pdev)
}
static const struct of_device_id of_pwm_fan_match_table[] = {
{ .compatible = "nuvoton,npcm750-pwm-fan", },
{ .compatible = "nuvoton,npcm750-pwm-fan", .data = &npxm7xx_hwmon_info},
{ .compatible = "nuvoton,npcm845-pwm-fan", .data = &npxm8xx_hwmon_info},
{},
};
MODULE_DEVICE_TABLE(of, of_pwm_fan_match_table);
......
......@@ -323,7 +323,11 @@ static struct pc87360_data *pc87360_update_device(struct device *dev)
}
/* Voltages */
for (i = 0; i < data->innr; i++) {
/*
* The min() below does not have any practical meaning and is
* only needed to silence a warning observed with gcc 12+.
*/
for (i = 0; i < min(data->innr, ARRAY_SIZE(data->in)); i++) {
data->in_status[i] = pc87360_read_value(data, LD_IN, i,
PC87365_REG_IN_STATUS);
/* Clear bits */
......
......@@ -47,7 +47,7 @@
#define GET_TEMP_MAX(x) (((x) & DIMM_TEMP_MAX) >> 8)
#define GET_TEMP_CRIT(x) (((x) & DIMM_TEMP_CRIT) >> 16)
#define NO_DIMM_RETRY_COUNT_MAX 5
#define NO_DIMM_RETRY_COUNT_MAX 120
struct peci_dimmtemp;
......
......@@ -227,6 +227,16 @@ config SENSORS_LTC3815
This driver can also be built as a module. If so, the module will
be called ltc3815.
config SENSORS_LTC4286
bool "Analog Devices LTC4286"
help
LTC4286 is an integrated solution for hot swap applications that
allows a board to be safely inserted and removed from a live
backplane.
This chip could be used to monitor voltage, current, ...etc.
If you say yes here you get hardware monitoring support for Analog
Devices LTC4286.
config SENSORS_MAX15301
tristate "Maxim MAX15301"
help
......@@ -299,6 +309,15 @@ config SENSORS_MAX8688
This driver can also be built as a module. If so, the module will
be called max8688.
config SENSORS_MP2856
tristate "MPS MP2856"
help
If you say yes here you get hardware monitoring support for MPS
MP2856 MP2857 Dual Loop Digital Multi-Phase Controller.
This driver can also be built as a module. If so, the module will
be called mp2856.
config SENSORS_MP2888
tristate "MPS MP2888"
help
......@@ -333,6 +352,15 @@ config SENSORS_MP5023
This driver can also be built as a module. If so, the module will
be called mp5023.
config SENSORS_MP5990
tristate "MPS MP5990"
help
If you say yes here you get hardware monitoring support for MPS
MP5990.
This driver can also be built as a module. If so, the module will
be called mp5990.
config SENSORS_MPQ7932_REGULATOR
bool "Regulator support for MPQ7932"
depends on SENSORS_MPQ7932 && REGULATOR
......
......@@ -24,6 +24,7 @@ obj-$(CONFIG_SENSORS_LM25066) += lm25066.o
obj-$(CONFIG_SENSORS_LT7182S) += lt7182s.o
obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o
obj-$(CONFIG_SENSORS_LTC3815) += ltc3815.o
obj-$(CONFIG_SENSORS_LTC4286) += ltc4286.o
obj-$(CONFIG_SENSORS_MAX15301) += max15301.o
obj-$(CONFIG_SENSORS_MAX16064) += max16064.o
obj-$(CONFIG_SENSORS_MAX16601) += max16601.o
......@@ -32,9 +33,11 @@ obj-$(CONFIG_SENSORS_MAX20751) += max20751.o
obj-$(CONFIG_SENSORS_MAX31785) += max31785.o
obj-$(CONFIG_SENSORS_MAX34440) += max34440.o
obj-$(CONFIG_SENSORS_MAX8688) += max8688.o
obj-$(CONFIG_SENSORS_MP2856) += mp2856.o
obj-$(CONFIG_SENSORS_MP2888) += mp2888.o
obj-$(CONFIG_SENSORS_MP2975) += mp2975.o
obj-$(CONFIG_SENSORS_MP5023) += mp5023.o
obj-$(CONFIG_SENSORS_MP5990) += mp5990.o
obj-$(CONFIG_SENSORS_MPQ7932) += mpq7932.o
obj-$(CONFIG_SENSORS_PLI1209BC) += pli1209bc.o
obj-$(CONFIG_SENSORS_PM6764TR) += pm6764tr.o
......
......@@ -14,10 +14,10 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/log2.h>
#include <linux/of_device.h>
#include <linux/of.h>
#include "pmbus.h"
enum chips { lm25056, lm25066, lm5064, lm5066, lm5066i };
enum chips { lm25056 = 1, lm25066, lm5064, lm5066, lm5066i };
#define LM25066_READ_VAUX 0xd0
#define LM25066_MFR_READ_IIN 0xd1
......@@ -468,8 +468,6 @@ static int lm25066_probe(struct i2c_client *client)
struct lm25066_data *data;
struct pmbus_driver_info *info;
const struct __coeff *coeff;
const struct of_device_id *of_id;
const struct i2c_device_id *i2c_id;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_READ_BYTE_DATA))
......@@ -484,14 +482,8 @@ static int lm25066_probe(struct i2c_client *client)
if (config < 0)
return config;
i2c_id = i2c_match_id(lm25066_id, client);
data->id = (enum chips)(unsigned long)i2c_get_match_data(client);
of_id = of_match_device(lm25066_of_match, &client->dev);
if (of_id && (unsigned long)of_id->data != i2c_id->driver_data)
dev_notice(&client->dev, "Device mismatch: %s in device tree, %s detected\n",
of_id->name, i2c_id->name);
data->id = i2c_id->driver_data;
info = &data->info;
info->pages = 1;
......
// SPDX-License-Identifier: GPL-2.0-or-later
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pmbus.h>
#include "pmbus.h"
/* LTC4286 register */
#define LTC4286_MFR_CONFIG1 0xF2
/* LTC4286 configuration */
#define VRANGE_SELECT_BIT BIT(1)
#define LTC4286_MFR_ID_SIZE 3
/*
* Initialize the MBR as default settings which is referred to LTC4286 datasheet
* (March 22, 2022 version) table 3 page 16
*/
static struct pmbus_driver_info ltc4286_info = {
.pages = 1,
.format[PSC_VOLTAGE_IN] = direct,
.format[PSC_VOLTAGE_OUT] = direct,
.format[PSC_CURRENT_OUT] = direct,
.format[PSC_POWER] = direct,
.format[PSC_TEMPERATURE] = direct,
.m[PSC_VOLTAGE_IN] = 32,
.b[PSC_VOLTAGE_IN] = 0,
.R[PSC_VOLTAGE_IN] = 1,
.m[PSC_VOLTAGE_OUT] = 32,
.b[PSC_VOLTAGE_OUT] = 0,
.R[PSC_VOLTAGE_OUT] = 1,
.m[PSC_CURRENT_OUT] = 1024,
.b[PSC_CURRENT_OUT] = 0,
/*
* The rsense value used in MBR formula in LTC4286 datasheet should be ohm unit.
* However, the rsense value that user input is micro ohm.
* Thus, the MBR setting which involves rsense should be shifted by 6 digits.
*/
.R[PSC_CURRENT_OUT] = 3 - 6,
.m[PSC_POWER] = 1,
.b[PSC_POWER] = 0,
/*
* The rsense value used in MBR formula in LTC4286 datasheet should be ohm unit.
* However, the rsense value that user input is micro ohm.
* Thus, the MBR setting which involves rsense should be shifted by 6 digits.
*/
.R[PSC_POWER] = 4 - 6,
.m[PSC_TEMPERATURE] = 1,
.b[PSC_TEMPERATURE] = 273,
.R[PSC_TEMPERATURE] = 0,
.func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
PMBUS_HAVE_PIN | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_VOUT |
PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_TEMP,
};
static const struct i2c_device_id ltc4286_id[] = {
{ "ltc4286", 0 },
{ "ltc4287", 1 },
{}
};
MODULE_DEVICE_TABLE(i2c, ltc4286_id);
static int ltc4286_probe(struct i2c_client *client)
{
int ret;
const struct i2c_device_id *mid;
u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1];
struct pmbus_driver_info *info;
u32 rsense;
int vrange_nval, vrange_oval;
ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, block_buffer);
if (ret < 0) {
return dev_err_probe(&client->dev, ret,
"Failed to read manufacturer id\n");
}
/*
* Refer to ltc4286 datasheet page 20
* the manufacturer id is LTC
*/
if (ret != LTC4286_MFR_ID_SIZE ||
strncmp(block_buffer, "LTC", LTC4286_MFR_ID_SIZE)) {
return dev_err_probe(&client->dev, -ENODEV,
"Manufacturer id mismatch\n");
}
ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, block_buffer);
if (ret < 0) {
return dev_err_probe(&client->dev, ret,
"Failed to read manufacturer model\n");
}
for (mid = ltc4286_id; mid->name[0]; mid++) {
if (!strncasecmp(mid->name, block_buffer, strlen(mid->name)))
break;
}
if (!mid->name[0])
return dev_err_probe(&client->dev, -ENODEV,
"Unsupported device\n");
if (of_property_read_u32(client->dev.of_node,
"shunt-resistor-micro-ohms", &rsense))
rsense = 300; /* 0.3 mOhm if not set via DT */
if (rsense == 0)
return -EINVAL;
/* Check for the latter MBR value won't overflow */
if (rsense > (INT_MAX / 1024))
return -EINVAL;
info = devm_kmemdup(&client->dev, &ltc4286_info, sizeof(*info),
GFP_KERNEL);
if (!info)
return -ENOMEM;
/* Check MFR1 CONFIG register bit 1 VRANGE_SELECT before driver loading */
vrange_oval = i2c_smbus_read_word_data(client, LTC4286_MFR_CONFIG1);
if (vrange_oval < 0)
return dev_err_probe(&client->dev, vrange_oval,
"Failed to read manufacturer configuration one\n");
vrange_nval = vrange_oval;
if (device_property_read_bool(&client->dev, "adi,vrange-low-enable")) {
vrange_nval &=
~VRANGE_SELECT_BIT; /* VRANGE_SELECT = 0, 25.6 volts */
info->m[PSC_VOLTAGE_IN] = 128;
info->m[PSC_VOLTAGE_OUT] = 128;
info->m[PSC_POWER] = 4 * rsense;
} else {
vrange_nval |=
VRANGE_SELECT_BIT; /* VRANGE_SELECT = 1, 102.4 volts */
info->m[PSC_POWER] = rsense;
}
if (vrange_nval != vrange_oval) {
/* Set MFR1 CONFIG register bit 1 VRANGE_SELECT */
ret = i2c_smbus_write_word_data(client, LTC4286_MFR_CONFIG1,
vrange_nval);
if (ret < 0)
return dev_err_probe(&client->dev, ret,
"Failed to set vrange\n");
}
info->m[PSC_CURRENT_OUT] = 1024 * rsense;
return pmbus_do_probe(client, info);
}
static const struct of_device_id ltc4286_of_match[] = {
{ .compatible = "lltc,ltc4286" },
{ .compatible = "lltc,ltc4287" },
{}
};
static struct i2c_driver ltc4286_driver = {
.driver = {
.name = "ltc4286",
.of_match_table = ltc4286_of_match,
},
.probe = ltc4286_probe,
.id_table = ltc4286_id,
};
module_i2c_driver(ltc4286_driver);
MODULE_AUTHOR("Delphine CC Chiu <Delphine_CC_Chiu@wiwynn.com>");
MODULE_DESCRIPTION("PMBUS driver for LTC4286 and compatibles");
MODULE_LICENSE("GPL");
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for MPS MP5990 Hot-Swap Controller
*/
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include "pmbus.h"
#define MP5990_EFUSE_CFG (0xC4)
#define MP5990_VOUT_FORMAT BIT(9)
struct mp5990_data {
struct pmbus_driver_info info;
u8 vout_mode;
u8 vout_linear_exponent;
};
#define to_mp5990_data(x) container_of(x, struct mp5990_data, info)
static int mp5990_read_byte_data(struct i2c_client *client, int page, int reg)
{
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
struct mp5990_data *data = to_mp5990_data(info);
switch (reg) {
case PMBUS_VOUT_MODE:
if (data->vout_mode == linear) {
/*
* The VOUT format used by the chip is linear11,
* not linear16. Report that VOUT is in linear mode
* and return exponent value extracted while probing
* the chip.
*/
return data->vout_linear_exponent;
}
/*
* The datasheet does not support the VOUT command,
* but the device responds with a default value of 0x17.
* In the standard, 0x17 represents linear mode.
* Therefore, we should report that VOUT is in direct
* format when the chip is configured for it.
*/
return PB_VOUT_MODE_DIRECT;
default:
return -ENODATA;
}
}
static int mp5990_read_word_data(struct i2c_client *client, int page,
int phase, int reg)
{
const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
struct mp5990_data *data = to_mp5990_data(info);
int ret;
s32 mantissa;
switch (reg) {
case PMBUS_READ_VOUT:
ret = pmbus_read_word_data(client, page, phase, reg);
if (ret < 0)
return ret;
/*
* Because the VOUT format used by the chip is linear11 and not
* linear16, we disregard bits[15:11]. The exponent is reported
* as part of the VOUT_MODE command.
*/
if (data->vout_mode == linear) {
mantissa = ((s16)((ret & 0x7ff) << 5)) >> 5;
ret = mantissa;
}
break;
default:
return -ENODATA;
}
return ret;
}
static struct pmbus_driver_info mp5990_info = {
.pages = 1,
.format[PSC_VOLTAGE_IN] = direct,
.format[PSC_VOLTAGE_OUT] = direct,
.format[PSC_CURRENT_OUT] = direct,
.format[PSC_POWER] = direct,
.format[PSC_TEMPERATURE] = direct,
.m[PSC_VOLTAGE_IN] = 32,
.b[PSC_VOLTAGE_IN] = 0,
.R[PSC_VOLTAGE_IN] = 0,
.m[PSC_VOLTAGE_OUT] = 32,
.b[PSC_VOLTAGE_OUT] = 0,
.R[PSC_VOLTAGE_OUT] = 0,
.m[PSC_CURRENT_OUT] = 16,
.b[PSC_CURRENT_OUT] = 0,
.R[PSC_CURRENT_OUT] = 0,
.m[PSC_POWER] = 1,
.b[PSC_POWER] = 0,
.R[PSC_POWER] = 0,
.m[PSC_TEMPERATURE] = 1,
.b[PSC_TEMPERATURE] = 0,
.R[PSC_TEMPERATURE] = 0,
.func[0] =
PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_PIN |
PMBUS_HAVE_TEMP | PMBUS_HAVE_IOUT |
PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP,
.read_byte_data = mp5990_read_byte_data,
.read_word_data = mp5990_read_word_data,
};
static int mp5990_probe(struct i2c_client *client)
{
struct pmbus_driver_info *info;
struct mp5990_data *data;
int ret;
data = devm_kzalloc(&client->dev, sizeof(struct mp5990_data),
GFP_KERNEL);
if (!data)
return -ENOMEM;
memcpy(&data->info, &mp5990_info, sizeof(*info));
info = &data->info;
/* Read Vout Config */
ret = i2c_smbus_read_word_data(client, MP5990_EFUSE_CFG);
if (ret < 0) {
dev_err(&client->dev, "Can't get vout mode.");
return ret;
}
/*
* EFUSE_CFG (0xC4) bit9=1 is linear mode, bit=0 is direct mode.
*/
if (ret & MP5990_VOUT_FORMAT) {
data->vout_mode = linear;
data->info.format[PSC_VOLTAGE_IN] = linear;
data->info.format[PSC_VOLTAGE_OUT] = linear;
data->info.format[PSC_CURRENT_OUT] = linear;
data->info.format[PSC_POWER] = linear;
ret = i2c_smbus_read_word_data(client, PMBUS_READ_VOUT);
if (ret < 0) {
dev_err(&client->dev, "Can't get vout exponent.");
return ret;
}
data->vout_linear_exponent = (u8)((ret >> 11) & 0x1f);
} else {
data->vout_mode = direct;
}
return pmbus_do_probe(client, info);
}
static const struct of_device_id mp5990_of_match[] = {
{ .compatible = "mps,mp5990" },
{}
};
static const struct i2c_device_id mp5990_id[] = {
{"mp5990", 0},
{ }
};
MODULE_DEVICE_TABLE(i2c, mp5990_id);
static struct i2c_driver mp5990_driver = {
.driver = {
.name = "mp5990",
.of_match_table = mp5990_of_match,
},
.probe = mp5990_probe,
.id_table = mp5990_id,
};
module_i2c_driver(mp5990_driver);
MODULE_AUTHOR("Peter Yin <peter.yin@quantatw.com>");
MODULE_DESCRIPTION("PMBus driver for MP5990 HSC");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(PMBUS);
......@@ -49,6 +49,7 @@ DECLARE_CRC8_TABLE(sht4x_crc8_table);
* struct sht4x_data - All the data required to operate an SHT4X chip
* @client: the i2c client associated with the SHT4X
* @lock: a mutex that is used to prevent parallel access to the i2c client
* @valid: validity of fields below
* @update_interval: the minimum poll interval
* @last_updated: the previous time that the SHT4X was polled
* @temperature: the latest temperature value received from the SHT4X
......@@ -66,7 +67,7 @@ struct sht4x_data {
/**
* sht4x_read_values() - read and parse the raw data from the SHT4X
* @sht4x_data: the struct sht4x_data to use for the lock
* @data: the struct sht4x_data to use for the lock
* Return: 0 if successful, -ERRNO if not
*/
static int sht4x_read_values(struct sht4x_data *data)
......
......@@ -33,7 +33,7 @@ static unsigned short force_id;
module_param(force_id, ushort, 0);
MODULE_PARM_DESC(force_id, "Override the detected device ID");
static struct platform_device *pdev;
static struct platform_device *smsc47m1_pdev;
#define DRVNAME "smsc47m1"
enum chips { smsc47m1, smsc47m2 };
......@@ -840,70 +840,57 @@ static int __init smsc47m1_probe(struct platform_device *pdev)
return err;
}
static int __exit smsc47m1_remove(struct platform_device *pdev)
static void __exit smsc47m1_remove(struct platform_device *pdev)
{
struct smsc47m1_data *data = platform_get_drvdata(pdev);
hwmon_device_unregister(data->hwmon_dev);
smsc47m1_remove_files(&pdev->dev);
return 0;
}
static struct platform_driver smsc47m1_driver = {
/*
* smsc47m1_remove() lives in .exit.text. For drivers registered via
* module_platform_driver_probe() this ok because they cannot get unbound at
* runtime. The driver needs to be marked with __refdata, otherwise modpost
* triggers a section mismatch warning.
*/
static struct platform_driver smsc47m1_driver __refdata = {
.driver = {
.name = DRVNAME,
},
.remove = __exit_p(smsc47m1_remove),
.remove_new = __exit_p(smsc47m1_remove),
};
static int __init smsc47m1_device_add(unsigned short address,
const struct smsc47m1_sio_data *sio_data)
{
struct resource res = {
const struct resource res = {
.start = address,
.end = address + SMSC_EXTENT - 1,
.name = DRVNAME,
.flags = IORESOURCE_IO,
};
const struct platform_device_info pdevinfo = {
.name = DRVNAME,
.id = address,
.res = &res,
.num_res = 1,
.data = sio_data,
.size_data = sizeof(struct smsc47m1_sio_data),
};
int err;
err = smsc47m1_handle_resources(address, sio_data->type, CHECK, NULL);
if (err)
goto exit;
return err;
pdev = platform_device_alloc(DRVNAME, address);
if (!pdev) {
err = -ENOMEM;
smsc47m1_pdev = platform_device_register_full(&pdevinfo);
if (IS_ERR(smsc47m1_pdev)) {
pr_err("Device allocation failed\n");
goto exit;
}
err = platform_device_add_resources(pdev, &res, 1);
if (err) {
pr_err("Device resource addition failed (%d)\n", err);
goto exit_device_put;
}
err = platform_device_add_data(pdev, sio_data,
sizeof(struct smsc47m1_sio_data));
if (err) {
pr_err("Platform data allocation failed\n");
goto exit_device_put;
}
err = platform_device_add(pdev);
if (err) {
pr_err("Device addition failed (%d)\n", err);
goto exit_device_put;
return PTR_ERR(smsc47m1_pdev);
}
return 0;
exit_device_put:
platform_device_put(pdev);
exit:
return err;
}
static int __init sm_smsc47m1_init(void)
......@@ -917,7 +904,7 @@ static int __init sm_smsc47m1_init(void)
return err;
address = err;
/* Sets global pdev as a side effect */
/* Sets global smsc47m1_pdev as a side effect */
err = smsc47m1_device_add(address, &sio_data);
if (err)
return err;
......@@ -929,7 +916,7 @@ static int __init sm_smsc47m1_init(void)
return 0;
exit_device:
platform_device_unregister(pdev);
platform_device_unregister(smsc47m1_pdev);
smsc47m1_restore(&sio_data);
return err;
}
......@@ -937,8 +924,8 @@ static int __init sm_smsc47m1_init(void)
static void __exit sm_smsc47m1_exit(void)
{
platform_driver_unregister(&smsc47m1_driver);
smsc47m1_restore(dev_get_platdata(&pdev->dev));
platform_device_unregister(pdev);
smsc47m1_restore(dev_get_platdata(&smsc47m1_pdev->dev));
platform_device_unregister(smsc47m1_pdev);
}
MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>");
......
......@@ -19,15 +19,20 @@
* the Free Software Foundation; version 2 of the License.
*/
#include <linux/bitops.h>
#include <linux/bug.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/hwmon.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/math.h>
#include <linux/module.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/util_macros.h>
#include <linux/types.h>
#include <linux/units.h>
// Common register definition
#define TMP51X_SHUNT_CONFIG 0x00
......@@ -97,8 +102,8 @@
#define TMP51X_REMOTE_TEMP_LIMIT_2_POS 8
#define TMP513_REMOTE_TEMP_LIMIT_3_POS 7
#define TMP51X_VBUS_RANGE_32V 32000000
#define TMP51X_VBUS_RANGE_16V 16000000
#define TMP51X_VBUS_RANGE_32V (32 * MICRO)
#define TMP51X_VBUS_RANGE_16V (16 * MICRO)
// Max and Min value
#define MAX_BUS_VOLTAGE_32_LIMIT 32764
......@@ -200,7 +205,7 @@ static int tmp51x_get_value(struct tmp51x_data *data, u8 reg, u8 pos,
* on the pga gain setting. 1lsb = 10uV
*/
*val = sign_extend32(regval, 17 - tmp51x_get_pga_shift(data));
*val = DIV_ROUND_CLOSEST(*val * 10000, data->shunt_uohms);
*val = DIV_ROUND_CLOSEST(*val * 10 * MILLI, data->shunt_uohms);
break;
case TMP51X_BUS_VOLTAGE_RESULT:
case TMP51X_BUS_VOLTAGE_H_LIMIT:
......@@ -216,7 +221,7 @@ static int tmp51x_get_value(struct tmp51x_data *data, u8 reg, u8 pos,
case TMP51X_BUS_CURRENT_RESULT:
// Current = (ShuntVoltage * CalibrationRegister) / 4096
*val = sign_extend32(regval, 16) * data->curr_lsb_ua;
*val = DIV_ROUND_CLOSEST(*val, 1000);
*val = DIV_ROUND_CLOSEST(*val, MILLI);
break;
case TMP51X_LOCAL_TEMP_RESULT:
case TMP51X_REMOTE_TEMP_RESULT_1:
......@@ -256,7 +261,7 @@ static int tmp51x_set_value(struct tmp51x_data *data, u8 reg, long val)
* The user enter current value and we convert it to
* voltage. 1lsb = 10uV
*/
val = DIV_ROUND_CLOSEST(val * data->shunt_uohms, 10000);
val = DIV_ROUND_CLOSEST(val * data->shunt_uohms, 10 * MILLI);
max_val = U16_MAX >> tmp51x_get_pga_shift(data);
regval = clamp_val(val, -max_val, max_val);
break;
......@@ -546,18 +551,16 @@ static int tmp51x_calibrate(struct tmp51x_data *data)
if (data->shunt_uohms == 0)
return regmap_write(data->regmap, TMP51X_SHUNT_CALIBRATION, 0);
max_curr_ma = DIV_ROUND_CLOSEST_ULL(vshunt_max * 1000 * 1000,
data->shunt_uohms);
max_curr_ma = DIV_ROUND_CLOSEST_ULL(vshunt_max * MICRO, data->shunt_uohms);
/*
* Calculate the minimal bit resolution for the current and the power.
* Those values will be used during register interpretation.
*/
data->curr_lsb_ua = DIV_ROUND_CLOSEST_ULL(max_curr_ma * 1000, 32767);
data->curr_lsb_ua = DIV_ROUND_CLOSEST_ULL(max_curr_ma * MILLI, 32767);
data->pwr_lsb_uw = 20 * data->curr_lsb_ua;
div = DIV_ROUND_CLOSEST_ULL(data->curr_lsb_ua * data->shunt_uohms,
1000 * 1000);
div = DIV_ROUND_CLOSEST_ULL(data->curr_lsb_ua * data->shunt_uohms, MICRO);
return regmap_write(data->regmap, TMP51X_SHUNT_CALIBRATION,
DIV_ROUND_CLOSEST(40960, div));
......@@ -626,9 +629,9 @@ static int tmp51x_vbus_range_to_reg(struct device *dev,
} else if (data->vbus_range_uvolt == TMP51X_VBUS_RANGE_16V) {
data->shunt_config &= ~TMP51X_BUS_VOLTAGE_MASK;
} else {
dev_err(dev, "ti,bus-range-microvolt is invalid: %u\n",
data->vbus_range_uvolt);
return -EINVAL;
return dev_err_probe(dev, -EINVAL,
"ti,bus-range-microvolt is invalid: %u\n",
data->vbus_range_uvolt);
}
return 0;
}
......@@ -644,8 +647,8 @@ static int tmp51x_pga_gain_to_reg(struct device *dev, struct tmp51x_data *data)
} else if (data->pga_gain == 1) {
data->shunt_config |= CURRENT_SENSE_VOLTAGE_40_MASK;
} else {
dev_err(dev, "ti,pga-gain is invalid: %u\n", data->pga_gain);
return -EINVAL;
return dev_err_probe(dev, -EINVAL,
"ti,pga-gain is invalid: %u\n", data->pga_gain);
}
return 0;
}
......@@ -674,10 +677,10 @@ static int tmp51x_read_properties(struct device *dev, struct tmp51x_data *data)
data->max_channels - 1);
// Check if shunt value is compatible with pga-gain
if (data->shunt_uohms > data->pga_gain * 40 * 1000 * 1000) {
dev_err(dev, "shunt-resistor: %u too big for pga_gain: %u\n",
data->shunt_uohms, data->pga_gain);
return -EINVAL;
if (data->shunt_uohms > data->pga_gain * 40 * MICRO) {
return dev_err_probe(dev, -EINVAL,
"shunt-resistor: %u too big for pga_gain: %u\n",
data->shunt_uohms, data->pga_gain);
}
return 0;
......@@ -717,22 +720,17 @@ static int tmp51x_probe(struct i2c_client *client)
data->max_channels = (uintptr_t)i2c_get_match_data(client);
ret = tmp51x_configure(dev, data);
if (ret < 0) {
dev_err(dev, "error configuring the device: %d\n", ret);
return ret;
}
if (ret < 0)
return dev_err_probe(dev, ret, "error configuring the device\n");
data->regmap = devm_regmap_init_i2c(client, &tmp51x_regmap_config);
if (IS_ERR(data->regmap)) {
dev_err(dev, "failed to allocate register map\n");
return PTR_ERR(data->regmap);
}
if (IS_ERR(data->regmap))
return dev_err_probe(dev, PTR_ERR(data->regmap),
"failed to allocate register map\n");
ret = tmp51x_init(data);
if (ret < 0) {
dev_err(dev, "error configuring the device: %d\n", ret);
return -ENODEV;
}
if (ret < 0)
return dev_err_probe(dev, ret, "error configuring the device\n");
hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
data,
......
......@@ -92,6 +92,7 @@ static const char * const allow_duplicates[] = {
"8A42EA14-4F2A-FD45-6422-0087F7A7E608", /* dell-wmi-ddv */
"44FADEB1-B204-40F2-8581-394BBDC1B651", /* intel-wmi-sbl-fw-update */
"86CCFD48-205E-4A77-9C48-2021CBEDE341", /* intel-wmi-thunderbolt */
"F1DDEE52-063C-4784-A11E-8A06684B9B01", /* dell-smm-hwmon */
NULL
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment