Commit 076f222a authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'hwmon-for-v5.19-rc1' of...

Merge tag 'hwmon-for-v5.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging

Pull hwmon updates from Guenter Roeck:
 "New drivers:

   - Driver for the Microchip LAN966x SoC

   - PMBus driver for Infineon Digital Multi-phase xdp152 family
     controllers

  Chip support added to existing drivers:

   - asus-ec-sensors:
      - Support for ROG STRIX X570-E GAMING WIFI II, PRIME X470-PRO, and
        ProArt X570 Creator WIFI
      - External temperature sensor support for ASUS WS X570-ACE

   - nct6775:
      - Support for I2C driver
      - Support for ASUS PRO H410T / PRIME H410M-R /
        ROG X570-E GAMING WIFI II

   - lm75:
      - Support for - Atmel AT30TS74

   - pmbus/max16601:
      - Support for MAX16602

   - aquacomputer_d5next:
      - Support for Aquacomputer Farbwerk
      - Support for Aquacomputer Octo

   - jc42:
      - Support for S-34TS04A

  Kernel API changes / clarifications:

   - The chip parameter of with_info API is now mandatory

   - New hwmon_device_register_for_thermal API call for use by the
     thermal subsystem

  Improvements:

   - PMBus and JC42 drivers now register with thermal subsystem

   - PMBus drivers now support get_voltage/set_voltage power operations

   - The adt7475 driver now supports pin configuration

   - The lm90 driver now supports setting extended range temperatures
     configuration with a devicetree property

   - The dell-smm driver now registers as cooling device

   - The OCC driver delays hwmon registration until requested by
     userspace

  ... and various other minor fixes and improvements"

* tag 'hwmon-for-v5.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (71 commits)
  hwmon: (aquacomputer_d5next) Fix an error handling path in aqc_probe()
  hwmon: (sl28cpld) Fix typo in comment
  hwmon: (pmbus) Check PEC support before reading other registers
  hwmon: (dimmtemp) Fix bitmap handling
  hwmon: (lm90) enable extended range according to DTS node
  dt-bindings: hwmon: lm90: add ti,extended-range-enable property
  dt-bindings: hwmon: lm90: add missing ti,tmp461
  hwmon: (ibmaem) Directly use ida_alloc()/free()
  hwmon: Directly use ida_alloc()/free()
  hwmon: (asus-ec-sensors) fix Formula VIII definition
  dt-bindings: trivial-devices: Add xdp152
  hwmon: (sl28cpld-hwmon) Use HWMON_CHANNEL_INFO macro
  hwmon: (pwm-fan) Use HWMON_CHANNEL_INFO macro
  hwmon: (peci/dimmtemp) Use HWMON_CHANNEL_INFO macro
  hwmon: (peci/cputemp) Use HWMON_CHANNEL_INFO macro
  hwmon: (mr75203) Use HWMON_CHANNEL_INFO macro
  hwmon: (ltc2992) Use HWMON_CHANNEL_INFO macro
  hwmon: (as370-hwmon) Use HWMON_CHANNEL_INFO macro
  hwmon: Make chip parameter for with_info API mandatory
  thermal/drivers/thermal_hwmon: Use hwmon_device_register_for_thermal()
  ...
parents 0350785b 8877ecb0
......@@ -61,6 +61,26 @@ patternProperties:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1]
"adi,pin(5|10)-function":
description: |
Configures the function for pin 5 on the adi,adt7473 and adi,adt7475. Or
pin 10 on the adi,adt7476 and adi,adt7490.
$ref: /schemas/types.yaml#/definitions/string
enum:
- pwm2
- smbalert#
"adi,pin(9|14)-function":
description: |
Configures the function for pin 9 on the adi,adt7473 and adi,adt7475. Or
pin 14 on the adi,adt7476 and adi,adt7490
$ref: /schemas/types.yaml#/definitions/string
enum:
- tach4
- therm#
- smbalert#
- gpio
required:
- compatible
- reg
......@@ -79,6 +99,8 @@ examples:
adi,bypass-attenuator-in0 = <1>;
adi,bypass-attenuator-in1 = <0>;
adi,pwm-active-state = <1 0 1>;
adi,pin10-function = "smbalert#";
adi,pin14-function = "tach4";
};
};
......@@ -14,6 +14,7 @@ properties:
compatible:
enum:
- adi,adt75
- atmel,at30ts74
- dallas,ds1775
- dallas,ds75
- dallas,ds7505
......
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/microchip,lan966x.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Microchip LAN966x Hardware Monitor
maintainers:
- Michael Walle <michael@walle.cc>
description: |
Microchip LAN966x temperature monitor and fan controller
properties:
compatible:
enum:
- microchip,lan9668-hwmon
reg:
items:
- description: PVT registers
- description: FAN registers
reg-names:
items:
- const: pvt
- const: fan
clocks:
maxItems: 1
'#thermal-sensor-cells':
const: 0
required:
- compatible
- reg
- reg-names
- clocks
additionalProperties: false
examples:
- |
hwmon: hwmon@e2010180 {
compatible = "microchip,lan9668-hwmon";
reg = <0xe2010180 0xc>,
<0xe20042a8 0xc>;
reg-names = "pvt", "fan";
clocks = <&sys_clk>;
#thermal-sensor-cells = <0>;
};
......@@ -34,6 +34,7 @@ properties:
- nxp,sa56004
- onnn,nct1008
- ti,tmp451
- ti,tmp461
- winbond,w83l771
......@@ -52,10 +53,29 @@ properties:
vcc-supply:
description: phandle to the regulator that provides the +VCC supply
ti,extended-range-enable:
description: Set to enable extended range temperature.
type: boolean
required:
- compatible
- reg
allOf:
- if:
not:
properties:
compatible:
contains:
enum:
- adi,adt7461
- adi,adt7461a
- ti,tmp451
- ti,tmp461
then:
properties:
ti,extended-range-enable: false
additionalProperties: false
examples:
......
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/nuvoton,nct6775.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Nuvoton NCT6775 and compatible Super I/O chips
maintainers:
- Zev Weiss <zev@bewilderbeest.net>
properties:
compatible:
enum:
- nuvoton,nct6106
- nuvoton,nct6116
- nuvoton,nct6775
- nuvoton,nct6776
- nuvoton,nct6779
- nuvoton,nct6791
- nuvoton,nct6792
- nuvoton,nct6793
- nuvoton,nct6795
- nuvoton,nct6796
- nuvoton,nct6797
- nuvoton,nct6798
reg:
maxItems: 1
nuvoton,tsi-channel-mask:
description:
Bitmask indicating which TSI temperature sensor channels are
active. LSB is TSI0, bit 1 is TSI1, etc.
$ref: /schemas/types.yaml#/definitions/uint32
maximum: 0xff
default: 0
required:
- compatible
- reg
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
superio@4d {
compatible = "nuvoton,nct6779";
reg = <0x4d>;
nuvoton,tsi-channel-mask = <0x03>;
};
};
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/hwmon/ti,tmp401.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: TMP401, TPM411 and TMP43x temperature sensor
maintainers:
- Guenter Roeck <linux@roeck-us.net>
description: |
±1°C Remote and Local temperature sensor
Datasheets:
https://www.ti.com/lit/ds/symlink/tmp401.pdf
https://www.ti.com/lit/ds/symlink/tmp411.pdf
https://www.ti.com/lit/ds/symlink/tmp431.pdf
https://www.ti.com/lit/ds/symlink/tmp435.pdf
properties:
compatible:
enum:
- ti,tmp401
- ti,tmp411
- ti,tmp431
- ti,tmp432
- ti,tmp435
reg:
maxItems: 1
ti,extended-range-enable:
description:
When set, this sensor measures over extended temperature range.
type: boolean
ti,n-factor:
description:
value to be used for converting remote channel measurements to
temperature.
$ref: /schemas/types.yaml#/definitions/int32
items:
minimum: -128
maximum: 127
ti,beta-compensation:
description:
value to select beta correction range.
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 0
maximum: 15
allOf:
- if:
properties:
compatible:
contains:
enum:
- ti,tmp401
then:
properties:
ti,n-factor: false
- if:
properties:
compatible:
contains:
enum:
- ti,tmp401
- ti,tmp411
then:
properties:
ti,beta-compensation: false
required:
- compatible
- reg
additionalProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
sensor@4c {
compatible = "ti,tmp401";
reg = <0x4c>;
};
};
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
sensor@4c {
compatible = "ti,tmp431";
reg = <0x4c>;
ti,extended-range-enable;
ti,n-factor = <0x3b>;
ti,beta-compensation = <0x7>;
};
};
......@@ -143,6 +143,10 @@ properties:
- infineon,xdpe12254
# Infineon Multi-phase Digital VR Controller xdpe12284
- infineon,xdpe12284
# Infineon Multi-phase Digital VR Controller xdpe15284
- infineon,xdpe15284
# Infineon Multi-phase Digital VR Controller xdpe152c4
- infineon,xdpe152c4
# Injoinic IP5108 2.0A Power Bank IC with I2C
- injoinic,ip5108
# Injoinic IP5109 2.1A Power Bank IC with I2C
......
......@@ -6,7 +6,9 @@ Kernel driver aquacomputer-d5next
Supported devices:
* Aquacomputer D5 Next watercooling pump
* Aquacomputer Farbwerk RGB controller
* Aquacomputer Farbwerk 360 RGB controller
* Aquacomputer Octo fan controller
Author: Aleksa Savic
......@@ -28,7 +30,10 @@ seems to require sending it a complete configuration. That includes addressable
RGB LEDs, for which there is no standard sysfs interface. Thus, that task is
better suited for userspace tools.
The Farbwerk 360 exposes four temperature sensors. Depending on the device,
The Octo exposes four temperature sensors and eight PWM controllable fans, along
with their speed (in RPM), power, voltage and current.
The Farbwerk and Farbwerk 360 expose four temperature sensors. Depending on the device,
not all sysfs and debugfs entries will be available.
Usage notes
......
......@@ -4,17 +4,20 @@ Kernel driver asus_ec_sensors
=================================
Supported boards:
* PRIME X570-PRO,
* Pro WS X570-ACE,
* ROG CROSSHAIR VIII DARK HERO,
* PRIME X470-PRO
* PRIME X570-PRO
* Pro WS X570-ACE
* ProArt X570-CREATOR WIFI
* ROG CROSSHAIR VIII DARK HERO
* ROG CROSSHAIR VIII HERO (WI-FI)
* ROG CROSSHAIR VIII FORMULA,
* ROG CROSSHAIR VIII HERO,
* ROG CROSSHAIR VIII IMPACT,
* ROG STRIX B550-E GAMING,
* ROG STRIX B550-I GAMING,
* ROG STRIX X570-E GAMING,
* ROG STRIX X570-F GAMING,
* ROG CROSSHAIR VIII FORMULA
* ROG CROSSHAIR VIII HERO
* ROG CROSSHAIR VIII IMPACT
* ROG STRIX B550-E GAMING
* ROG STRIX B550-I GAMING
* ROG STRIX X570-E GAMING
* ROG STRIX X570-E GAMING WIFI II
* ROG STRIX X570-F GAMING
* ROG STRIX X570-I GAMING
Authors:
......@@ -52,3 +55,5 @@ Module Parameters
the path is mostly identical for them). If ASUS changes this path
in a future BIOS update, this parameter can be used to override
the stored in the driver value until it gets updated.
A special string ":GLOBAL_LOCK" can be passed to use the ACPI
global lock instead of a dedicated mutex.
......@@ -86,6 +86,13 @@ probe the BIOS on your machine and discover the appropriate codes.
Again, when you find new codes, we'd be happy to have your patches!
``thermal`` interface
---------------------------
The driver also exports the fans as thermal cooling devices with
``type`` set to ``dell-smm-fan[1-3]``. This allows for easy fan control
using one of the thermal governors.
Module parameters
-----------------
......@@ -324,6 +331,8 @@ Reading of fan types causes erratic fan behaviour. Studio XPS 8000
Inspiron 580
Inspiron 3505
Fan-related SMM calls take too long (about 500ms). Inspiron 7720
Vostro 3360
......
......@@ -50,6 +50,10 @@ register/unregister functions::
void devm_hwmon_device_unregister(struct device *dev);
char *hwmon_sanitize_name(const char *name);
char *devm_hwmon_sanitize_name(struct device *dev, const char *name);
hwmon_device_register_with_groups registers a hardware monitoring device.
The first parameter of this function is a pointer to the parent device.
The name parameter is a pointer to the hwmon device name. The registration
......@@ -72,7 +76,7 @@ hwmon_device_register_with_info is the most comprehensive and preferred means
to register a hardware monitoring device. It creates the standard sysfs
attributes in the hardware monitoring core, letting the driver focus on reading
from and writing to the chip instead of having to bother with sysfs attributes.
The parent device parameter cannot be NULL with non-NULL chip info. Its
The parent device parameter as well as the chip parameter must not be NULL. Its
parameters are described in more detail below.
devm_hwmon_device_register_with_info is similar to
......@@ -95,6 +99,18 @@ All supported hwmon device registration functions only accept valid device
names. Device names including invalid characters (whitespace, '*', or '-')
will be rejected. The 'name' parameter is mandatory.
If the driver doesn't use a static device name (for example it uses
dev_name()), and therefore cannot make sure the name only contains valid
characters, hwmon_sanitize_name can be used. This convenience function
will duplicate the string and replace any invalid characters with an
underscore. It will allocate memory for the new string and it is the
responsibility of the caller to release the memory when the device is
removed.
devm_hwmon_sanitize_name is the resource managed version of
hwmon_sanitize_name; the memory will be freed automatically on device
removal.
Using devm_hwmon_device_register_with_info()
--------------------------------------------
......
......@@ -90,6 +90,7 @@ Hardware Monitoring Kernel Drivers
jc42
k10temp
k8temp
lan966x
lineage-pem
lm25066
lm63
......@@ -223,6 +224,7 @@ Hardware Monitoring Kernel Drivers
wm8350
xgene-hwmon
xdpe12284
xdpe152c4
zl6100
.. only:: subproject and html
......
.. SPDX-License-Identifier: GPL-2.0
Kernel driver lan966x-hwmon
===========================
Supported chips:
* Microchip LAN9668 (sensor in SoC)
Prefix: 'lan9668-hwmon'
Datasheet: https://microchip-ung.github.io/lan9668_reginfo
Authors:
Michael Walle <michael@walle.cc>
Description
-----------
This driver implements support for the Microchip LAN9668 on-chip
temperature sensor as well as its fan controller. It provides one
temperature sensor and one fan controller. The temperature range
of the sensor is specified from -40 to +125 degrees Celsius and
its accuracy is +/- 5 degrees Celsius. The fan controller has a
tacho input and a PWM output with a customizable PWM output
frequency ranging from ~20Hz to ~650kHz.
No alarms are supported by the SoC.
The driver exports temperature values, fan tacho input and PWM
settings via the following sysfs files:
**temp1_input**
**fan1_input**
**pwm1**
**pwm1_freq**
......@@ -21,6 +21,14 @@ Supported chips:
Datasheet: Not published
* Maxim MAX16602
Prefix: 'max16602'
Addresses scanned: -
Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX16602.pdf
Author: Guenter Roeck <linux@roeck-us.net>
......
.. SPDX-License-Identifier: GPL-2.0
Kernel driver xdpe152
=====================
Supported chips:
* Infineon XDPE152C4
Prefix: 'xdpe152c4'
* Infineon XDPE15284
Prefix: 'xdpe15284'
Authors:
Greg Schwendimann <greg.schwendimann@infineon.com>
Description
-----------
This driver implements support for Infineon Digital Multi-phase Controller
XDPE152C4 and XDPE15284 dual loop voltage regulators.
The devices are compliant with:
- Intel VR13, VR13HC and VR14 rev 1.86
converter specification.
- Intel SVID rev 1.93. protocol.
- PMBus rev 1.3.1 interface.
Devices support linear format for reading input and output voltage, input
and output current, input and output power and temperature.
Devices support two pages for telemetry.
The driver provides for current: input, maximum and critical thresholds
and maximum and critical alarms. Low Critical thresholds and Low critical alarm are
supported only for current output.
The driver exports the following attributes for via the sysfs files, where
indexes 1, 2 are for "iin" and 3, 4 for "iout":
**curr[1-4]_crit**
**curr[1-4]_crit_alarm**
**curr[1-4]_input**
**curr[1-4]_label**
**curr[1-4]_max**
**curr[1-4]_max_alarm**
**curr[3-4]_lcrit**
**curr[3-4]_lcrit_alarm**
**curr[3-4]_rated_max**
The driver provides for voltage: input, critical and low critical thresholds
and critical and low critical alarms.
The driver exports the following attributes for via the sysfs files, where
indexes 1, 2 are for "vin" and 3, 4 for "vout":
**in[1-4]_min**
**in[1-4]_crit**
**in[1-4_crit_alarm**
**in[1-4]_input**
**in[1-4]_label**
**in[1-4]_max**
**in[1-4]_max_alarm**
**in[1-4]_min**
**in[1-4]_min_alarm**
**in[3-4]_lcrit**
**in[3-4]_lcrit_alarm**
**in[3-4]_rated_max**
**in[3-4]_rated_min**
The driver provides for power: input and alarms.
The driver exports the following attributes for via the sysfs files, where
indexes 1, 2 are for "pin" and 3, 4 for "pout":
**power[1-2]_alarm**
**power[1-4]_input**
**power[1-4]_label**
**power[1-4]_max**
**power[1-4]_rated_max**
The driver provides for temperature: input, maximum and critical thresholds
and maximum and critical alarms.
The driver exports the following attributes for via the sysfs files:
**temp[1-2]_crit**
**temp[1-2]_crit_alarm**
**temp[1-2]_input**
**temp[1-2]_max**
**temp[1-2]_max_alarm**
......@@ -1447,6 +1447,7 @@ F: drivers/media/i2c/aptina-pll.*
AQUACOMPUTER D5 NEXT PUMP SENSOR DRIVER
M: Aleksa Savic <savicaleksa83@gmail.com>
M: Jack Doan <me@jackdoan.com>
L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/aquacomputer_d5next.rst
......@@ -13554,12 +13555,21 @@ M: Samuel Mendoza-Jonas <sam@mendozajonas.com>
S: Maintained
F: net/ncsi/
NCT6775 HARDWARE MONITOR DRIVER
NCT6775 HARDWARE MONITOR DRIVER - CORE & PLATFORM DRIVER
M: Guenter Roeck <linux@roeck-us.net>
L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/hwmon/nct6775.rst
F: drivers/hwmon/nct6775.c
F: drivers/hwmon/nct6775-core.c
F: drivers/hwmon/nct6775-platform.c
F: drivers/hwmon/nct6775.h
NCT6775 HARDWARE MONITOR DRIVER - I2C DRIVER
M: Zev Weiss <zev@bewilderbeest.net>
L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/hwmon/nuvoton,nct6775.yaml
F: drivers/hwmon/nct6775-i2c.c
NETDEVSIM
M: Jakub Kicinski <kuba@kernel.org>
......@@ -19870,6 +19880,7 @@ TMP401 HARDWARE MONITOR DRIVER
M: Guenter Roeck <linux@roeck-us.net>
L: linux-hwmon@vger.kernel.org
S: Maintained
F: Documentation/devicetree/bindings/hwmon/ti,tmp401.yaml
F: Documentation/hwmon/tmp401.rst
F: drivers/hwmon/tmp401.c
......
......@@ -256,11 +256,14 @@ config SENSORS_AHT10
will be called aht10.
config SENSORS_AQUACOMPUTER_D5NEXT
tristate "Aquacomputer D5 Next watercooling pump"
tristate "Aquacomputer D5 Next, Octo, Farbwerk, and Farbwerk 360"
depends on USB_HID
select CRC16
help
If you say yes here you get support for the Aquacomputer D5 Next
watercooling pump sensors.
If you say yes here you get support for sensors and fans of
the Aquacomputer D5 Next watercooling pump, Octo fan
controller, Farbwerk and Farbwerk 360 RGB controllers, where
available.
This driver can also be built as a module. If so, the module
will be called aquacomputer_d5next.
......@@ -415,6 +418,7 @@ config SENSORS_ATXP1
config SENSORS_BT1_PVT
tristate "Baikal-T1 Process, Voltage, Temperature sensor driver"
depends on MIPS_BAIKAL_T1 || COMPILE_TEST
select POLYNOMIAL
help
If you say yes here you get support for Baikal-T1 PVT sensor
embedded into the SoC.
......@@ -498,6 +502,7 @@ config SENSORS_DS1621
config SENSORS_DELL_SMM
tristate "Dell laptop SMM BIOS hwmon driver"
depends on X86
imply THERMAL
help
This hwmon driver adds support for reporting temperature of different
sensors and controls the fans on Dell laptops via System Management
......@@ -814,6 +819,18 @@ config SENSORS_POWR1220
This driver can also be built as a module. If so, the module
will be called powr1220.
config SENSORS_LAN966X
tristate "Microchip LAN966x Hardware Monitoring"
depends on SOC_LAN966 || COMPILE_TEST
select REGMAP
select POLYNOMIAL
help
If you say yes here you get support for temperature monitoring
on the Microchip LAN966x SoC.
This driver can also be built as a module. If so, the module
will be called lan966x-hwmon.
config SENSORS_LINEAGE
tristate "Lineage Compact Power Line Power Entry Module"
depends on I2C
......@@ -1248,6 +1265,7 @@ config SENSORS_LM75
temperature sensor chip, with models including:
- Analog Devices ADT75
- Atmel (now Microchip) AT30TS74
- Dallas Semiconductor DS75, DS1775 and DS7505
- Global Mixed-mode Technology (GMT) G751
- Maxim MAX6625 and MAX6626
......@@ -1457,11 +1475,23 @@ config SENSORS_NCT6683
This driver can also be built as a module. If so, the module
will be called nct6683.
config SENSORS_NCT6775_CORE
tristate
select REGMAP
help
This module contains common code shared by the platform and
i2c versions of the nct6775 driver; it is not useful on its
own.
If built as a module, the module will be called
nct6775-core.
config SENSORS_NCT6775
tristate "Nuvoton NCT6775F and compatibles"
tristate "Platform driver for Nuvoton NCT6775F and compatibles"
depends on !PPC
depends on ACPI_WMI || ACPI_WMI=n
select HWMON_VID
select SENSORS_NCT6775_CORE
help
If you say yes here you get support for the hardware monitoring
functionality of the Nuvoton NCT6106D, NCT6775F, NCT6776F, NCT6779D,
......@@ -1472,6 +1502,23 @@ config SENSORS_NCT6775
This driver can also be built as a module. If so, the module
will be called nct6775.
config SENSORS_NCT6775_I2C
tristate "I2C driver for Nuvoton NCT6775F and compatibles"
depends on I2C
select REGMAP_I2C
select SENSORS_NCT6775_CORE
help
If you say yes here you get support for the hardware monitoring
functionality of the Nuvoton NCT6106D, NCT6775F, NCT6776F, NCT6779D,
NCT6791D, NCT6792D, NCT6793D, NCT6795D, NCT6796D, and compatible
Super-I/O chips via their I2C interface.
If you're not building a kernel for a BMC, this is probably
not the driver you want (see CONFIG_SENSORS_NCT6775).
This driver can also be built as a module. If so, the module
will be called nct6775-i2c.
config SENSORS_NCT7802
tristate "Nuvoton NCT7802Y"
depends on I2C
......
......@@ -100,6 +100,7 @@ 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_LAN966X) += lan966x-hwmon.o
obj-$(CONFIG_SENSORS_LINEAGE) += lineage-pem.o
obj-$(CONFIG_SENSORS_LOCHNAGAR) += lochnagar-hwmon.o
obj-$(CONFIG_SENSORS_LM63) += lm63.o
......@@ -154,7 +155,10 @@ obj-$(CONFIG_SENSORS_MLXREG_FAN) += mlxreg-fan.o
obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o
obj-$(CONFIG_SENSORS_MR75203) += mr75203.o
obj-$(CONFIG_SENSORS_NCT6683) += nct6683.o
obj-$(CONFIG_SENSORS_NCT6775_CORE) += nct6775-core.o
nct6775-objs := nct6775-platform.o
obj-$(CONFIG_SENSORS_NCT6775) += nct6775.o
obj-$(CONFIG_SENSORS_NCT6775_I2C) += nct6775-i2c.o
obj-$(CONFIG_SENSORS_NCT7802) += nct7802.o
obj-$(CONFIG_SENSORS_NCT7904) += nct7904.o
obj-$(CONFIG_SENSORS_NPCM7XX) += npcm750-pwm-fan.o
......
......@@ -481,7 +481,7 @@ static struct sensor_template meter_attrs[] = {
RO_SENSOR_TEMPLATE("power1_average_interval_max", show_val, 1),
RO_SENSOR_TEMPLATE("power1_is_battery", show_val, 5),
RW_SENSOR_TEMPLATE(POWER_AVG_INTERVAL_NAME, show_avg_interval,
set_avg_interval, 0),
set_avg_interval, 0),
{},
};
......@@ -530,6 +530,7 @@ static void remove_domain_devices(struct acpi_power_meter_resource *resource)
for (i = 0; i < resource->num_domain_devices; i++) {
struct acpi_device *obj = resource->domain_devices[i];
if (!obj)
continue;
......@@ -580,7 +581,7 @@ static int read_domain_devices(struct acpi_power_meter_resource *resource)
}
resource->holders_dir = kobject_create_and_add("measures",
&resource->acpi_dev->dev.kobj);
&resource->acpi_dev->dev.kobj);
if (!resource->holders_dir) {
res = -ENOMEM;
goto exit_free;
......@@ -590,7 +591,7 @@ static int read_domain_devices(struct acpi_power_meter_resource *resource)
for (i = 0; i < pss->package.count; i++) {
struct acpi_device *obj;
union acpi_object *element = &(pss->package.elements[i]);
union acpi_object *element = &pss->package.elements[i];
/* Refuse non-references */
if (element->type != ACPI_TYPE_LOCAL_REFERENCE)
......@@ -603,7 +604,7 @@ static int read_domain_devices(struct acpi_power_meter_resource *resource)
continue;
res = sysfs_create_link(resource->holders_dir, &obj->dev.kobj,
kobject_name(&obj->dev.kobj));
kobject_name(&obj->dev.kobj));
if (res) {
acpi_dev_put(obj);
resource->domain_devices[i] = NULL;
......@@ -788,7 +789,7 @@ static int read_capabilities(struct acpi_power_meter_resource *resource)
str = &resource->model_number;
for (i = 11; i < 14; i++) {
union acpi_object *element = &(pss->package.elements[i]);
union acpi_object *element = &pss->package.elements[i];
if (element->type != ACPI_TYPE_STRING) {
res = -EINVAL;
......@@ -868,8 +869,7 @@ static int acpi_power_meter_add(struct acpi_device *device)
if (!device)
return -EINVAL;
resource = kzalloc(sizeof(struct acpi_power_meter_resource),
GFP_KERNEL);
resource = kzalloc(sizeof(*resource), GFP_KERNEL);
if (!resource)
return -ENOMEM;
......@@ -884,7 +884,8 @@ static int acpi_power_meter_add(struct acpi_device *device)
if (res)
goto exit_free;
resource->trip[0] = resource->trip[1] = -1;
resource->trip[0] = -1;
resource->trip[1] = -1;
res = setup_attrs(resource);
if (res)
......
......@@ -112,6 +112,8 @@
#define CONFIG3_THERM 0x02
#define CONFIG4_PINFUNC 0x03
#define CONFIG4_THERM 0x01
#define CONFIG4_SMBALERT 0x02
#define CONFIG4_MAXDUTY 0x08
#define CONFIG4_ATTN_IN10 0x30
#define CONFIG4_ATTN_IN43 0xC0
......@@ -1460,6 +1462,96 @@ static int adt7475_update_limits(struct i2c_client *client)
return 0;
}
static int load_config3(const struct i2c_client *client, const char *propname)
{
const char *function;
u8 config3;
int ret;
ret = of_property_read_string(client->dev.of_node, propname, &function);
if (!ret) {
ret = adt7475_read(REG_CONFIG3);
if (ret < 0)
return ret;
config3 = ret & ~CONFIG3_SMBALERT;
if (!strcmp("pwm2", function))
;
else if (!strcmp("smbalert#", function))
config3 |= CONFIG3_SMBALERT;
else
return -EINVAL;
return i2c_smbus_write_byte_data(client, REG_CONFIG3, config3);
}
return 0;
}
static int load_config4(const struct i2c_client *client, const char *propname)
{
const char *function;
u8 config4;
int ret;
ret = of_property_read_string(client->dev.of_node, propname, &function);
if (!ret) {
ret = adt7475_read(REG_CONFIG4);
if (ret < 0)
return ret;
config4 = ret & ~CONFIG4_PINFUNC;
if (!strcmp("tach4", function))
;
else if (!strcmp("therm#", function))
config4 |= CONFIG4_THERM;
else if (!strcmp("smbalert#", function))
config4 |= CONFIG4_SMBALERT;
else if (!strcmp("gpio", function))
config4 |= CONFIG4_PINFUNC;
else
return -EINVAL;
return i2c_smbus_write_byte_data(client, REG_CONFIG4, config4);
}
return 0;
}
static int load_config(const struct i2c_client *client, enum chips chip)
{
int err;
const char *prop1, *prop2;
switch (chip) {
case adt7473:
case adt7475:
prop1 = "adi,pin5-function";
prop2 = "adi,pin9-function";
break;
case adt7476:
case adt7490:
prop1 = "adi,pin10-function";
prop2 = "adi,pin14-function";
break;
}
err = load_config3(client, prop1);
if (err) {
dev_err(&client->dev, "failed to configure %s\n", prop1);
return err;
}
err = load_config4(client, prop2);
if (err) {
dev_err(&client->dev, "failed to configure %s\n", prop2);
return err;
}
return 0;
}
static int set_property_bit(const struct i2c_client *client, char *property,
u8 *config, u8 bit_index)
{
......@@ -1477,12 +1569,12 @@ static int set_property_bit(const struct i2c_client *client, char *property,
return ret;
}
static int load_attenuators(const struct i2c_client *client, int chip,
static int load_attenuators(const struct i2c_client *client, enum chips chip,
struct adt7475_data *data)
{
int ret;
if (chip == adt7476 || chip == adt7490) {
switch (chip) {
case adt7476:
case adt7490:
set_property_bit(client, "adi,bypass-attenuator-in0",
&data->config4, 4);
set_property_bit(client, "adi,bypass-attenuator-in1",
......@@ -1492,18 +1584,15 @@ static int load_attenuators(const struct i2c_client *client, int chip,
set_property_bit(client, "adi,bypass-attenuator-in4",
&data->config4, 7);
ret = i2c_smbus_write_byte_data(client, REG_CONFIG4,
data->config4);
if (ret < 0)
return ret;
} else if (chip == adt7473 || chip == adt7475) {
return i2c_smbus_write_byte_data(client, REG_CONFIG4,
data->config4);
case adt7473:
case adt7475:
set_property_bit(client, "adi,bypass-attenuator-in1",
&data->config2, 5);
ret = i2c_smbus_write_byte_data(client, REG_CONFIG2,
data->config2);
if (ret < 0)
return ret;
return i2c_smbus_write_byte_data(client, REG_CONFIG2,
data->config2);
}
return 0;
......@@ -1585,6 +1674,10 @@ static int adt7475_probe(struct i2c_client *client)
revision = adt7475_read(REG_DEVID2) & 0x07;
}
ret = load_config(client, chip);
if (ret)
return ret;
config3 = adt7475_read(REG_CONFIG3);
/* Pin PWM2 may alternatively be used for ALERT output */
if (!(config3 & CONFIG3_SMBALERT))
......
This diff is collapsed.
......@@ -76,18 +76,8 @@ as370_hwmon_is_visible(const void *data, enum hwmon_sensor_types type,
}
}
static const u32 as370_hwmon_temp_config[] = {
HWMON_T_INPUT,
0
};
static const struct hwmon_channel_info as370_hwmon_temp = {
.type = hwmon_temp,
.config = as370_hwmon_temp_config,
};
static const struct hwmon_channel_info *as370_hwmon_info[] = {
&as370_hwmon_temp,
HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
NULL
};
......
This diff is collapsed.
......@@ -26,6 +26,7 @@
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/polynomial.h>
#include <linux/seqlock.h>
#include <linux/sysfs.h>
#include <linux/types.h>
......@@ -65,7 +66,7 @@ static const struct pvt_sensor_info pvt_info[] = {
* 48380,
* where T = [-48380, 147438] mC and N = [0, 1023].
*/
static const struct pvt_poly __maybe_unused poly_temp_to_N = {
static const struct polynomial __maybe_unused poly_temp_to_N = {
.total_divider = 10000,
.terms = {
{4, 18322, 10000, 10000},
......@@ -76,7 +77,7 @@ static const struct pvt_poly __maybe_unused poly_temp_to_N = {
}
};
static const struct pvt_poly poly_N_to_temp = {
static const struct polynomial poly_N_to_temp = {
.total_divider = 1,
.terms = {
{4, -16743, 1000, 1},
......@@ -97,7 +98,7 @@ static const struct pvt_poly poly_N_to_temp = {
* N = (18658e-3*V - 11572) / 10,
* V = N * 10^5 / 18658 + 11572 * 10^4 / 18658.
*/
static const struct pvt_poly __maybe_unused poly_volt_to_N = {
static const struct polynomial __maybe_unused poly_volt_to_N = {
.total_divider = 10,
.terms = {
{1, 18658, 1000, 1},
......@@ -105,7 +106,7 @@ static const struct pvt_poly __maybe_unused poly_volt_to_N = {
}
};
static const struct pvt_poly poly_N_to_volt = {
static const struct polynomial poly_N_to_volt = {
.total_divider = 10,
.terms = {
{1, 100000, 18658, 1},
......@@ -113,31 +114,6 @@ static const struct pvt_poly poly_N_to_volt = {
}
};
/*
* Here is the polynomial calculation function, which performs the
* redistributed terms calculations. It's pretty straightforward. We walk
* over each degree term up to the free one, and perform the redistributed
* multiplication of the term coefficient, its divider (as for the rationale
* fraction representation), data power and the rational fraction divider
* leftover. Then all of this is collected in a total sum variable, which
* value is normalized by the total divider before being returned.
*/
static long pvt_calc_poly(const struct pvt_poly *poly, long data)
{
const struct pvt_poly_term *term = poly->terms;
long tmp, ret = 0;
int deg;
do {
tmp = term->coef;
for (deg = 0; deg < term->deg; ++deg)
tmp = mult_frac(tmp, data, term->divider);
ret += tmp / term->divider_leftover;
} while ((term++)->deg);
return ret / poly->total_divider;
}
static inline u32 pvt_update(void __iomem *reg, u32 mask, u32 data)
{
u32 old;
......@@ -324,9 +300,9 @@ static int pvt_read_data(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
} while (read_seqretry(&cache->data_seqlock, seq));
if (type == PVT_TEMP)
*val = pvt_calc_poly(&poly_N_to_temp, data);
*val = polynomial_calc(&poly_N_to_temp, data);
else
*val = pvt_calc_poly(&poly_N_to_volt, data);
*val = polynomial_calc(&poly_N_to_volt, data);
return 0;
}
......@@ -345,9 +321,9 @@ static int pvt_read_limit(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
data = FIELD_GET(PVT_THRES_HI_MASK, data);
if (type == PVT_TEMP)
*val = pvt_calc_poly(&poly_N_to_temp, data);
*val = polynomial_calc(&poly_N_to_temp, data);
else
*val = pvt_calc_poly(&poly_N_to_volt, data);
*val = polynomial_calc(&poly_N_to_volt, data);
return 0;
}
......@@ -360,10 +336,10 @@ static int pvt_write_limit(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
if (type == PVT_TEMP) {
val = clamp(val, PVT_TEMP_MIN, PVT_TEMP_MAX);
data = pvt_calc_poly(&poly_temp_to_N, val);
data = polynomial_calc(&poly_temp_to_N, val);
} else {
val = clamp(val, PVT_VOLT_MIN, PVT_VOLT_MAX);
data = pvt_calc_poly(&poly_volt_to_N, val);
data = polynomial_calc(&poly_volt_to_N, val);
}
/* Serialize limit update, since a part of the register is changed. */
......@@ -522,9 +498,9 @@ static int pvt_read_data(struct pvt_hwmon *pvt, enum pvt_sensor_type type,
return -ETIMEDOUT;
if (type == PVT_TEMP)
*val = pvt_calc_poly(&poly_N_to_temp, data);
*val = polynomial_calc(&poly_N_to_temp, data);
else
*val = pvt_calc_poly(&poly_N_to_volt, data);
*val = polynomial_calc(&poly_N_to_volt, data);
return 0;
}
......
......@@ -21,14 +21,17 @@
#include <linux/errno.h>
#include <linux/hwmon.h>
#include <linux/init.h>
#include <linux/kconfig.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/smp.h>
#include <linux/string.h>
#include <linux/thermal.h>
#include <linux/types.h>
#include <linux/uaccess.h>
......@@ -46,8 +49,11 @@
#define I8K_SMM_GET_DELL_SIG1 0xfea3
#define I8K_SMM_GET_DELL_SIG2 0xffa3
/* in usecs */
#define DELL_SMM_MAX_DURATION 250000
#define I8K_FAN_MULT 30
#define I8K_FAN_MAX_RPM 30000
#define I8K_FAN_RPM_THRESHOLD 1000
#define I8K_MAX_TEMP 127
#define I8K_FN_NONE 0x00
......@@ -80,6 +86,11 @@ struct dell_smm_data {
int *fan_nominal_speed[DELL_SMM_NO_FANS];
};
struct dell_smm_cooling_data {
u8 fan_num;
struct dell_smm_data *data;
};
MODULE_AUTHOR("Massimo Dal Zotto (dz@debian.org)");
MODULE_AUTHOR("Pali Rohár <pali@kernel.org>");
MODULE_DESCRIPTION("Dell laptop SMM BIOS hwmon driver");
......@@ -231,6 +242,9 @@ static int i8k_smm_func(void *par)
pr_debug("smm(0x%.4x 0x%.4x) = 0x%.4x (took %7lld usecs)\n", eax, ebx,
(rc ? 0xffff : regs->eax & 0xffff), duration);
if (duration > DELL_SMM_MAX_DURATION)
pr_warn_once("SMM call took %lld usecs!\n", duration);
return rc;
}
......@@ -318,7 +332,7 @@ static int __init i8k_get_fan_nominal_speed(const struct dell_smm_data *data, u8
if (data->disallow_fan_support)
return -EINVAL;
return i8k_smm(&regs) ? : (regs.eax & 0xffff) * data->i8k_fan_mult;
return i8k_smm(&regs) ? : (regs.eax & 0xffff);
}
/*
......@@ -638,9 +652,50 @@ static void __init i8k_init_procfs(struct device *dev)
#endif
/*
* Hwmon interface
*/
static int dell_smm_get_max_state(struct thermal_cooling_device *dev, unsigned long *state)
{
struct dell_smm_cooling_data *cdata = dev->devdata;
*state = cdata->data->i8k_fan_max;
return 0;
}
static int dell_smm_get_cur_state(struct thermal_cooling_device *dev, unsigned long *state)
{
struct dell_smm_cooling_data *cdata = dev->devdata;
int ret;
ret = i8k_get_fan_status(cdata->data, cdata->fan_num);
if (ret < 0)
return ret;
*state = ret;
return 0;
}
static int dell_smm_set_cur_state(struct thermal_cooling_device *dev, unsigned long state)
{
struct dell_smm_cooling_data *cdata = dev->devdata;
struct dell_smm_data *data = cdata->data;
int ret;
if (state > data->i8k_fan_max)
return -EINVAL;
mutex_lock(&data->i8k_mutex);
ret = i8k_set_fan(data, cdata->fan_num, (int)state);
mutex_unlock(&data->i8k_mutex);
return ret;
}
static const struct thermal_cooling_device_ops dell_smm_cooling_ops = {
.get_max_state = dell_smm_get_max_state,
.get_cur_state = dell_smm_get_cur_state,
.set_cur_state = dell_smm_set_cur_state,
};
static umode_t dell_smm_is_visible(const void *drvdata, enum hwmon_sensor_types type, u32 attr,
int channel)
......@@ -727,6 +782,7 @@ static int dell_smm_read(struct device *dev, enum hwmon_sensor_types type, u32 a
long *val)
{
struct dell_smm_data *data = dev_get_drvdata(dev);
int mult = data->i8k_fan_mult;
int ret;
switch (type) {
......@@ -755,11 +811,11 @@ static int dell_smm_read(struct device *dev, enum hwmon_sensor_types type, u32 a
return 0;
case hwmon_fan_min:
*val = data->fan_nominal_speed[channel][0];
*val = data->fan_nominal_speed[channel][0] * mult;
return 0;
case hwmon_fan_max:
*val = data->fan_nominal_speed[channel][data->i8k_fan_max];
*val = data->fan_nominal_speed[channel][data->i8k_fan_max] * mult;
return 0;
case hwmon_fan_target:
......@@ -770,7 +826,7 @@ static int dell_smm_read(struct device *dev, enum hwmon_sensor_types type, u32 a
if (ret > data->i8k_fan_max)
ret = data->i8k_fan_max;
*val = data->fan_nominal_speed[channel][ret];
*val = data->fan_nominal_speed[channel][ret] * mult;
return 0;
default:
......@@ -941,6 +997,37 @@ static const struct hwmon_chip_info dell_smm_chip_info = {
.info = dell_smm_info,
};
static int __init dell_smm_init_cdev(struct device *dev, u8 fan_num)
{
struct dell_smm_data *data = dev_get_drvdata(dev);
struct thermal_cooling_device *cdev;
struct dell_smm_cooling_data *cdata;
int ret = 0;
char *name;
name = kasprintf(GFP_KERNEL, "dell-smm-fan%u", fan_num + 1);
if (!name)
return -ENOMEM;
cdata = devm_kmalloc(dev, sizeof(*cdata), GFP_KERNEL);
if (cdata) {
cdata->fan_num = fan_num;
cdata->data = data;
cdev = devm_thermal_of_cooling_device_register(dev, NULL, name, cdata,
&dell_smm_cooling_ops);
if (IS_ERR(cdev)) {
devm_kfree(dev, cdata);
ret = PTR_ERR(cdev);
}
} else {
ret = -ENOMEM;
}
kfree(name);
return ret;
}
static int __init dell_smm_init_hwmon(struct device *dev)
{
struct dell_smm_data *data = dev_get_drvdata(dev);
......@@ -967,6 +1054,15 @@ static int __init dell_smm_init_hwmon(struct device *dev)
continue;
data->fan[i] = true;
/* the cooling device is not critical, ignore failures */
if (IS_REACHABLE(CONFIG_THERMAL)) {
err = dell_smm_init_cdev(dev, i);
if (err < 0)
dev_warn(dev, "Failed to register cooling device for fan %u\n",
i + 1);
}
data->fan_nominal_speed[i] = devm_kmalloc_array(dev, data->i8k_fan_max + 1,
sizeof(*data->fan_nominal_speed[i]),
GFP_KERNEL);
......@@ -982,6 +1078,13 @@ static int __init dell_smm_init_hwmon(struct device *dev)
break;
}
data->fan_nominal_speed[i][state] = err;
/*
* Autodetect fan multiplier based on nominal rpm if multiplier
* was not specified as module param or in DMI. If fan reports
* rpm value too high then set multiplier to 1.
*/
if (!fan_mult && err > I8K_FAN_RPM_THRESHOLD)
data->i8k_fan_mult = 1;
}
}
......@@ -1270,15 +1373,12 @@ static int __init dell_smm_probe(struct platform_device *pdev)
struct dell_smm_data *data;
const struct dmi_system_id *id, *fan_control;
int ret;
u8 fan;
data = devm_kzalloc(&pdev->dev, sizeof(struct dell_smm_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
mutex_init(&data->i8k_mutex);
data->i8k_fan_mult = I8K_FAN_MULT;
data->i8k_fan_max = I8K_FAN_HIGH;
platform_set_drvdata(pdev, data);
if (dmi_check_system(i8k_blacklist_fan_support_dmi_table)) {
......@@ -1313,7 +1413,9 @@ static int __init dell_smm_probe(struct platform_device *pdev)
fan_max = conf->fan_max;
}
data->i8k_fan_max = fan_max ? : I8K_FAN_HIGH; /* Must not be 0 */
/* All options must not be 0 */
data->i8k_fan_mult = fan_mult ? : I8K_FAN_MULT;
data->i8k_fan_max = fan_max ? : I8K_FAN_HIGH;
data->i8k_pwm_mult = DIV_ROUND_UP(255, data->i8k_fan_max);
fan_control = dmi_first_match(i8k_whitelist_fan_control);
......@@ -1325,25 +1427,6 @@ static int __init dell_smm_probe(struct platform_device *pdev)
dev_info(&pdev->dev, "enabling support for setting automatic/manual fan control\n");
}
if (!fan_mult) {
/*
* Autodetect fan multiplier based on nominal rpm
* If fan reports rpm value too high then set multiplier to 1
*/
for (fan = 0; fan < DELL_SMM_NO_FANS; ++fan) {
ret = i8k_get_fan_nominal_speed(data, fan, data->i8k_fan_max);
if (ret < 0)
continue;
if (ret > I8K_FAN_MAX_RPM)
data->i8k_fan_mult = 1;
break;
}
} else {
/* Fan multiplier was specified in module param or in dmi */
data->i8k_fan_mult = fan_mult;
}
ret = dell_smm_init_hwmon(&pdev->dev);
if (ret)
return ret;
......
......@@ -764,7 +764,7 @@ __hwmon_device_register(struct device *dev, const char *name, void *drvdata,
"hwmon: '%s' is not a valid name attribute, please fix\n",
name);
id = ida_simple_get(&hwmon_ida, 0, 0, GFP_KERNEL);
id = ida_alloc(&hwmon_ida, GFP_KERNEL);
if (id < 0)
return ERR_PTR(id);
......@@ -856,7 +856,7 @@ __hwmon_device_register(struct device *dev, const char *name, void *drvdata,
free_hwmon:
hwmon_dev_release(hdev);
ida_remove:
ida_simple_remove(&hwmon_ida, id);
ida_free(&hwmon_ida, id);
return ERR_PTR(err);
}
......@@ -886,11 +886,12 @@ EXPORT_SYMBOL_GPL(hwmon_device_register_with_groups);
/**
* hwmon_device_register_with_info - register w/ hwmon
* @dev: the parent device
* @name: hwmon name attribute
* @drvdata: driver data to attach to created device
* @chip: pointer to hwmon chip information
* @dev: the parent device (mandatory)
* @name: hwmon name attribute (mandatory)
* @drvdata: driver data to attach to created device (optional)
* @chip: pointer to hwmon chip information (mandatory)
* @extra_groups: pointer to list of additional non-standard attribute groups
* (optional)
*
* hwmon_device_unregister() must be called when the device is no
* longer needed.
......@@ -903,19 +904,41 @@ hwmon_device_register_with_info(struct device *dev, const char *name,
const struct hwmon_chip_info *chip,
const struct attribute_group **extra_groups)
{
if (!name)
return ERR_PTR(-EINVAL);
if (chip && (!chip->ops || !chip->ops->is_visible || !chip->info))
if (!dev || !name || !chip)
return ERR_PTR(-EINVAL);
if (chip && !dev)
if (!chip->ops || !chip->ops->is_visible || !chip->info)
return ERR_PTR(-EINVAL);
return __hwmon_device_register(dev, name, drvdata, chip, extra_groups);
}
EXPORT_SYMBOL_GPL(hwmon_device_register_with_info);
/**
* hwmon_device_register_for_thermal - register hwmon device for thermal subsystem
* @dev: the parent device
* @name: hwmon name attribute
* @drvdata: driver data to attach to created device
*
* The use of this function is restricted. It is provided for legacy reasons
* and must only be called from the thermal subsystem.
*
* hwmon_device_unregister() must be called when the device is no
* longer needed.
*
* Returns the pointer to the new device.
*/
struct device *
hwmon_device_register_for_thermal(struct device *dev, const char *name,
void *drvdata)
{
if (!name || !dev)
return ERR_PTR(-EINVAL);
return __hwmon_device_register(dev, name, drvdata, NULL, NULL);
}
EXPORT_SYMBOL_NS_GPL(hwmon_device_register_for_thermal, HWMON_THERMAL);
/**
* hwmon_device_register - register w/ hwmon
* @dev: the device to register
......@@ -945,7 +968,7 @@ void hwmon_device_unregister(struct device *dev)
if (likely(sscanf(dev_name(dev), HWMON_ID_FORMAT, &id) == 1)) {
device_unregister(dev);
ida_simple_remove(&hwmon_ida, id);
ida_free(&hwmon_ida, id);
} else
dev_dbg(dev->parent,
"hwmon_device_unregister() failed: bad class ID!\n");
......@@ -1057,6 +1080,59 @@ void devm_hwmon_device_unregister(struct device *dev)
}
EXPORT_SYMBOL_GPL(devm_hwmon_device_unregister);
static char *__hwmon_sanitize_name(struct device *dev, const char *old_name)
{
char *name, *p;
if (dev)
name = devm_kstrdup(dev, old_name, GFP_KERNEL);
else
name = kstrdup(old_name, GFP_KERNEL);
if (!name)
return ERR_PTR(-ENOMEM);
for (p = name; *p; p++)
if (hwmon_is_bad_char(*p))
*p = '_';
return name;
}
/**
* hwmon_sanitize_name - Replaces invalid characters in a hwmon name
* @name: NUL-terminated name
*
* Allocates a new string where any invalid characters will be replaced
* by an underscore. It is the responsibility of the caller to release
* the memory.
*
* Returns newly allocated name, or ERR_PTR on error.
*/
char *hwmon_sanitize_name(const char *name)
{
return __hwmon_sanitize_name(NULL, name);
}
EXPORT_SYMBOL_GPL(hwmon_sanitize_name);
/**
* devm_hwmon_sanitize_name - resource managed hwmon_sanitize_name()
* @dev: device to allocate memory for
* @name: NUL-terminated name
*
* Allocates a new string where any invalid characters will be replaced
* by an underscore.
*
* Returns newly allocated name, or ERR_PTR on error.
*/
char *devm_hwmon_sanitize_name(struct device *dev, const char *name)
{
if (!dev)
return ERR_PTR(-EINVAL);
return __hwmon_sanitize_name(dev, name);
}
EXPORT_SYMBOL_GPL(devm_hwmon_sanitize_name);
static void __init hwmon_pci_quirks(void)
{
#if defined CONFIG_X86 && defined CONFIG_PCI
......
......@@ -482,7 +482,7 @@ static void aem_delete(struct aem_data *data)
ipmi_destroy_user(data->ipmi.user);
platform_set_drvdata(data->pdev, NULL);
platform_device_unregister(data->pdev);
ida_simple_remove(&aem_ida, data->id);
ida_free(&aem_ida, data->id);
kfree(data);
}
......@@ -539,7 +539,7 @@ static int aem_init_aem1_inst(struct aem_ipmi_data *probe, u8 module_handle)
data->power_period[i] = AEM_DEFAULT_POWER_INTERVAL;
/* Create sub-device for this fw instance */
data->id = ida_simple_get(&aem_ida, 0, 0, GFP_KERNEL);
data->id = ida_alloc(&aem_ida, GFP_KERNEL);
if (data->id < 0)
goto id_err;
......@@ -600,7 +600,7 @@ static int aem_init_aem1_inst(struct aem_ipmi_data *probe, u8 module_handle)
platform_set_drvdata(data->pdev, NULL);
platform_device_unregister(data->pdev);
dev_err:
ida_simple_remove(&aem_ida, data->id);
ida_free(&aem_ida, data->id);
id_err:
kfree(data);
......@@ -679,7 +679,7 @@ static int aem_init_aem2_inst(struct aem_ipmi_data *probe,
data->power_period[i] = AEM_DEFAULT_POWER_INTERVAL;
/* Create sub-device for this fw instance */
data->id = ida_simple_get(&aem_ida, 0, 0, GFP_KERNEL);
data->id = ida_alloc(&aem_ida, GFP_KERNEL);
if (data->id < 0)
goto id_err;
......@@ -740,7 +740,7 @@ static int aem_init_aem2_inst(struct aem_ipmi_data *probe,
platform_set_drvdata(data->pdev, NULL);
platform_device_unregister(data->pdev);
dev_err:
ida_simple_remove(&aem_ida, data->id);
ida_free(&aem_ida, data->id);
id_err:
kfree(data);
......
......@@ -515,7 +515,6 @@ static int m10bmc_hwmon_probe(struct platform_device *pdev)
struct intel_m10bmc *m10bmc = dev_get_drvdata(pdev->dev.parent);
struct device *hwmon_dev, *dev = &pdev->dev;
struct m10bmc_hwmon *hw;
int i;
hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL);
if (!hw)
......@@ -528,13 +527,9 @@ static int m10bmc_hwmon_probe(struct platform_device *pdev)
hw->chip.info = hw->bdata->hinfo;
hw->chip.ops = &m10bmc_hwmon_ops;
hw->hw_name = devm_kstrdup(dev, id->name, GFP_KERNEL);
if (!hw->hw_name)
return -ENOMEM;
for (i = 0; hw->hw_name[i]; i++)
if (hwmon_is_bad_char(hw->hw_name[i]))
hw->hw_name[i] = '_';
hw->hw_name = devm_hwmon_sanitize_name(dev, id->name);
if (IS_ERR(hw->hw_name))
return PTR_ERR(hw->hw_name);
hwmon_dev = devm_hwmon_device_register_with_info(dev, hw->hw_name,
hw, &hw->chip, NULL);
......
......@@ -63,6 +63,7 @@ static const unsigned short normal_i2c[] = {
#define STM_MANID 0x104a /* ST Microelectronics */
#define GT_MANID 0x1c68 /* Giantec */
#define GT_MANID2 0x132d /* Giantec, 2nd mfg ID */
#define SI_MANID 0x1c85 /* Seiko Instruments */
/* SMBUS register */
#define SMBUS_STMOUT BIT(7) /* SMBus time-out, active low */
......@@ -156,6 +157,10 @@ static const unsigned short normal_i2c[] = {
#define STTS3000_DEVID 0x0200
#define STTS3000_DEVID_MASK 0xffff
/* Seiko Instruments */
#define S34TS04A_DEVID 0x2221
#define S34TS04A_DEVID_MASK 0xffff
static u16 jc42_hysteresis[] = { 0, 1500, 3000, 6000 };
struct jc42_chips {
......@@ -186,6 +191,7 @@ static struct jc42_chips jc42_chips[] = {
{ ONS_MANID, CAT34TS04_DEVID, CAT34TS04_DEVID_MASK },
{ ONS_MANID, N34TS04_DEVID, N34TS04_DEVID_MASK },
{ NXP_MANID, SE98_DEVID, SE98_DEVID_MASK },
{ SI_MANID, S34TS04A_DEVID, S34TS04A_DEVID_MASK },
{ STM_MANID, STTS424_DEVID, STTS424_DEVID_MASK },
{ STM_MANID, STTS424E_DEVID, STTS424E_DEVID_MASK },
{ STM_MANID, STTS2002_DEVID, STTS2002_DEVID_MASK },
......@@ -443,6 +449,8 @@ static int jc42_detect(struct i2c_client *client, struct i2c_board_info *info)
}
static const struct hwmon_channel_info *jc42_info[] = {
HWMON_CHANNEL_INFO(chip,
HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL),
HWMON_CHANNEL_INFO(temp,
HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
HWMON_T_CRIT | HWMON_T_MAX_HYST |
......
This diff is collapsed.
......@@ -26,6 +26,7 @@
enum lm75_type { /* keep sorted in alphabetical order */
adt75,
at30ts74,
ds1775,
ds75,
ds7505,
......@@ -128,6 +129,14 @@ static const struct lm75_params device_params[] = {
.default_resolution = 12,
.default_sample_time = MSEC_PER_SEC / 10,
},
[at30ts74] = {
.set_mask = 3 << 5, /* 12-bit mode*/
.default_resolution = 12,
.default_sample_time = 200,
.num_sample_times = 4,
.sample_times = (unsigned int []){ 25, 50, 100, 200 },
.resolutions = (u8 []) {9, 10, 11, 12 },
},
[ds1775] = {
.clr_mask = 3 << 5,
.set_mask = 2 << 5, /* 11-bit mode */
......@@ -645,6 +654,7 @@ static int lm75_probe(struct i2c_client *client)
static const struct i2c_device_id lm75_ids[] = {
{ "adt75", adt75, },
{ "at30ts74", at30ts74, },
{ "ds1775", ds1775, },
{ "ds75", ds75, },
{ "ds7505", ds7505, },
......@@ -680,6 +690,10 @@ static const struct of_device_id __maybe_unused lm75_of_match[] = {
.compatible = "adi,adt75",
.data = (void *)adt75
},
{
.compatible = "atmel,at30ts74",
.data = (void *)at30ts74
},
{
.compatible = "dallas,ds1775",
.data = (void *)ds1775
......
......@@ -24,10 +24,8 @@
#include <linux/init.h>
#include <linux/hwmon.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
/*
* Addresses to scan
......
......@@ -1707,6 +1707,7 @@ static void lm90_restore_conf(void *_data)
static int lm90_init_client(struct i2c_client *client, struct lm90_data *data)
{
struct device_node *np = client->dev.of_node;
int config, convrate;
convrate = lm90_read_reg(client, LM90_REG_R_CONVRATE);
......@@ -1727,6 +1728,9 @@ static int lm90_init_client(struct i2c_client *client, struct lm90_data *data)
/* Check Temperature Range Select */
if (data->flags & LM90_HAVE_EXTENDED_TEMP) {
if (of_property_read_bool(np, "ti,extended-range-enable"))
config |= 0x04;
if (config & 0x04)
data->flags |= LM90_FLAG_ADT7461_EXT;
}
......
......@@ -811,68 +811,32 @@ static const struct hwmon_ops ltc2992_hwmon_ops = {
.write = ltc2992_write,
};
static const u32 ltc2992_chip_config[] = {
HWMON_C_IN_RESET_HISTORY,
0
};
static const struct hwmon_channel_info ltc2992_chip = {
.type = hwmon_chip,
.config = ltc2992_chip_config,
};
static const u32 ltc2992_in_config[] = {
HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN | HWMON_I_MAX |
HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM,
HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN | HWMON_I_MAX |
HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM,
HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN | HWMON_I_MAX |
HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM,
HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN | HWMON_I_MAX |
HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM,
HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN | HWMON_I_MAX |
HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM,
HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN | HWMON_I_MAX |
HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM,
0
};
static const struct hwmon_channel_info ltc2992_in = {
.type = hwmon_in,
.config = ltc2992_in_config,
};
static const u32 ltc2992_curr_config[] = {
HWMON_C_INPUT | HWMON_C_LOWEST | HWMON_C_HIGHEST | HWMON_C_MIN | HWMON_C_MAX |
HWMON_C_MIN_ALARM | HWMON_C_MAX_ALARM,
HWMON_C_INPUT | HWMON_C_LOWEST | HWMON_C_HIGHEST | HWMON_C_MIN | HWMON_C_MAX |
HWMON_C_MIN_ALARM | HWMON_C_MAX_ALARM,
0
};
static const struct hwmon_channel_info ltc2992_curr = {
.type = hwmon_curr,
.config = ltc2992_curr_config,
};
static const u32 ltc2992_power_config[] = {
HWMON_P_INPUT | HWMON_P_INPUT_LOWEST | HWMON_P_INPUT_HIGHEST | HWMON_P_MIN | HWMON_P_MAX |
HWMON_P_MIN_ALARM | HWMON_P_MAX_ALARM,
HWMON_P_INPUT | HWMON_P_INPUT_LOWEST | HWMON_P_INPUT_HIGHEST | HWMON_P_MIN | HWMON_P_MAX |
HWMON_P_MIN_ALARM | HWMON_P_MAX_ALARM,
0
};
static const struct hwmon_channel_info ltc2992_power = {
.type = hwmon_power,
.config = ltc2992_power_config,
};
static const struct hwmon_channel_info *ltc2992_info[] = {
&ltc2992_chip,
&ltc2992_in,
&ltc2992_curr,
&ltc2992_power,
HWMON_CHANNEL_INFO(chip,
HWMON_C_IN_RESET_HISTORY),
HWMON_CHANNEL_INFO(in,
HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN |
HWMON_I_MAX | HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM,
HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN |
HWMON_I_MAX | HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM,
HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN |
HWMON_I_MAX | HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM,
HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN |
HWMON_I_MAX | HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM,
HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN |
HWMON_I_MAX | HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM,
HWMON_I_INPUT | HWMON_I_LOWEST | HWMON_I_HIGHEST | HWMON_I_MIN |
HWMON_I_MAX | HWMON_I_MIN_ALARM | HWMON_I_MAX_ALARM),
HWMON_CHANNEL_INFO(curr,
HWMON_C_INPUT | HWMON_C_LOWEST | HWMON_C_HIGHEST | HWMON_C_MIN |
HWMON_C_MAX | HWMON_C_MIN_ALARM | HWMON_C_MAX_ALARM,
HWMON_C_INPUT | HWMON_C_LOWEST | HWMON_C_HIGHEST | HWMON_C_MIN |
HWMON_C_MAX | HWMON_C_MIN_ALARM | HWMON_C_MAX_ALARM),
HWMON_CHANNEL_INFO(power,
HWMON_P_INPUT | HWMON_P_INPUT_LOWEST | HWMON_P_INPUT_HIGHEST |
HWMON_P_MIN | HWMON_P_MAX | HWMON_P_MIN_ALARM | HWMON_P_MAX_ALARM,
HWMON_P_INPUT | HWMON_P_INPUT_LOWEST | HWMON_P_INPUT_HIGHEST |
HWMON_P_MIN | HWMON_P_MAX | HWMON_P_MIN_ALARM | HWMON_P_MAX_ALARM),
NULL
};
......
......@@ -223,16 +223,6 @@ static int pvt_read(struct device *dev, enum hwmon_sensor_types type,
}
}
static const u32 pvt_chip_config[] = {
HWMON_C_REGISTER_TZ,
0
};
static const struct hwmon_channel_info pvt_chip = {
.type = hwmon_chip,
.config = pvt_chip_config,
};
static struct hwmon_channel_info pvt_temp = {
.type = hwmon_temp,
};
......@@ -555,7 +545,7 @@ static int mr75203_probe(struct platform_device *pdev)
pvt_info = devm_kcalloc(dev, val + 2, sizeof(*pvt_info), GFP_KERNEL);
if (!pvt_info)
return -ENOMEM;
pvt_info[0] = &pvt_chip;
pvt_info[0] = HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ);
index = 1;
if (ts_num) {
......
// SPDX-License-Identifier: GPL-2.0
/*
* nct6775-i2c - I2C driver for the hardware monitoring functionality of
* Nuvoton NCT677x Super-I/O chips
*
* Copyright (C) 2022 Zev Weiss <zev@bewilderbeest.net>
*
* This driver interacts with the chip via it's "back door" i2c interface, as
* is often exposed to a BMC. Because the host may still be operating the
* chip via the ("front door") LPC interface, this driver cannot assume that
* it actually has full control of the chip, and in particular must avoid
* making any changes that could confuse the host's LPC usage of it. It thus
* operates in a strictly read-only fashion, with the only exception being the
* bank-select register (which seems, thankfully, to be replicated for the i2c
* interface so it doesn't affect the LPC interface).
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/i2c.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/of_device.h>
#include <linux/regmap.h>
#include "nct6775.h"
static int nct6775_i2c_read(void *ctx, unsigned int reg, unsigned int *val)
{
int ret;
u32 tmp;
u8 bank = reg >> 8;
struct nct6775_data *data = ctx;
struct i2c_client *client = data->driver_data;
if (bank != data->bank) {
ret = i2c_smbus_write_byte_data(client, NCT6775_REG_BANK, bank);
if (ret)
return ret;
data->bank = bank;
}
ret = i2c_smbus_read_byte_data(client, reg & 0xff);
if (ret < 0)
return ret;
tmp = ret;
if (nct6775_reg_is_word_sized(data, reg)) {
ret = i2c_smbus_read_byte_data(client, (reg & 0xff) + 1);
if (ret < 0)
return ret;
tmp = (tmp << 8) | ret;
}
*val = tmp;
return 0;
}
/*
* The write operation is a dummy so as not to disturb anything being done
* with the chip via LPC.
*/
static int nct6775_i2c_write(void *ctx, unsigned int reg, unsigned int value)
{
struct nct6775_data *data = ctx;
struct i2c_client *client = data->driver_data;
dev_dbg(&client->dev, "skipping attempted write: %02x -> %03x\n", value, reg);
/*
* This is a lie, but writing anything but the bank-select register is
* something this driver shouldn't be doing.
*/
return 0;
}
static const struct of_device_id __maybe_unused nct6775_i2c_of_match[] = {
{ .compatible = "nuvoton,nct6106", .data = (void *)nct6106, },
{ .compatible = "nuvoton,nct6116", .data = (void *)nct6116, },
{ .compatible = "nuvoton,nct6775", .data = (void *)nct6775, },
{ .compatible = "nuvoton,nct6776", .data = (void *)nct6776, },
{ .compatible = "nuvoton,nct6779", .data = (void *)nct6779, },
{ .compatible = "nuvoton,nct6791", .data = (void *)nct6791, },
{ .compatible = "nuvoton,nct6792", .data = (void *)nct6792, },
{ .compatible = "nuvoton,nct6793", .data = (void *)nct6793, },
{ .compatible = "nuvoton,nct6795", .data = (void *)nct6795, },
{ .compatible = "nuvoton,nct6796", .data = (void *)nct6796, },
{ .compatible = "nuvoton,nct6797", .data = (void *)nct6797, },
{ .compatible = "nuvoton,nct6798", .data = (void *)nct6798, },
{ },
};
MODULE_DEVICE_TABLE(of, nct6775_i2c_of_match);
static const struct i2c_device_id nct6775_i2c_id[] = {
{ "nct6106", nct6106 },
{ "nct6116", nct6116 },
{ "nct6775", nct6775 },
{ "nct6776", nct6776 },
{ "nct6779", nct6779 },
{ "nct6791", nct6791 },
{ "nct6792", nct6792 },
{ "nct6793", nct6793 },
{ "nct6795", nct6795 },
{ "nct6796", nct6796 },
{ "nct6797", nct6797 },
{ "nct6798", nct6798 },
{ }
};
MODULE_DEVICE_TABLE(i2c, nct6775_i2c_id);
static int nct6775_i2c_probe_init(struct nct6775_data *data)
{
u32 tsi_channel_mask;
struct i2c_client *client = data->driver_data;
/*
* The i2c interface doesn't provide access to the control registers
* needed to determine the presence of other fans, but fans 1 and 2
* are (in principle) always there.
*
* In practice this is perhaps a little silly, because the system
* using this driver is mostly likely a BMC, and hence probably has
* totally separate fan tachs & pwms of its own that are actually
* controlling/monitoring the fans -- these are thus unlikely to be
* doing anything actually useful.
*/
data->has_fan = 0x03;
data->has_fan_min = 0x03;
data->has_pwm = 0x03;
/*
* Because on a BMC this driver may be bound very shortly after power
* is first applied to the device, the automatic TSI channel detection
* in nct6775_probe() (which has already been run at this point) may
* not find anything if a channel hasn't yet produced a temperature
* reading. Augment whatever was found via autodetection (if
* anything) with the channels DT says should be active.
*/
if (!of_property_read_u32(client->dev.of_node, "nuvoton,tsi-channel-mask",
&tsi_channel_mask))
data->have_tsi_temp |= tsi_channel_mask & GENMASK(NUM_TSI_TEMP - 1, 0);
return 0;
}
static const struct regmap_config nct6775_i2c_regmap_config = {
.reg_bits = 16,
.val_bits = 16,
.reg_read = nct6775_i2c_read,
.reg_write = nct6775_i2c_write,
};
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->read_only = true;
data->driver_data = client;
data->driver_init = nct6775_i2c_probe_init;
return nct6775_probe(dev, data, &nct6775_i2c_regmap_config);
}
static struct i2c_driver nct6775_i2c_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "nct6775-i2c",
.of_match_table = of_match_ptr(nct6775_i2c_of_match),
},
.probe_new = nct6775_i2c_probe,
.id_table = nct6775_i2c_id,
};
module_i2c_driver(nct6775_i2c_driver);
MODULE_AUTHOR("Zev Weiss <zev@bewilderbeest.net>");
MODULE_DESCRIPTION("I2C driver for NCT6775F and compatible chips");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(HWMON_NCT6775);
This diff is collapsed.
This diff is collapsed.
......@@ -1149,44 +1149,75 @@ static void occ_parse_poll_response(struct occ *occ)
sizeof(*header), size + sizeof(*header));
}
int occ_setup(struct occ *occ, const char *name)
int occ_active(struct occ *occ, bool active)
{
int rc;
mutex_init(&occ->lock);
occ->groups[0] = &occ->group;
int rc = mutex_lock_interruptible(&occ->lock);
/* no need to lock */
rc = occ_poll(occ);
if (rc == -ESHUTDOWN) {
dev_info(occ->bus_dev, "host is not ready\n");
return rc;
} else if (rc < 0) {
dev_err(occ->bus_dev,
"failed to get OCC poll response=%02x: %d\n",
occ->resp.return_status, rc);
if (rc)
return rc;
}
occ->next_update = jiffies + OCC_UPDATE_FREQUENCY;
occ_parse_poll_response(occ);
if (active) {
if (occ->active) {
rc = -EALREADY;
goto unlock;
}
rc = occ_setup_sensor_attrs(occ);
if (rc) {
dev_err(occ->bus_dev, "failed to setup sensor attrs: %d\n",
rc);
return rc;
}
occ->error_count = 0;
occ->last_safe = 0;
occ->hwmon = devm_hwmon_device_register_with_groups(occ->bus_dev, name,
occ, occ->groups);
if (IS_ERR(occ->hwmon)) {
rc = PTR_ERR(occ->hwmon);
dev_err(occ->bus_dev, "failed to register hwmon device: %d\n",
rc);
return rc;
rc = occ_poll(occ);
if (rc < 0) {
dev_err(occ->bus_dev,
"failed to get OCC poll response=%02x: %d\n",
occ->resp.return_status, rc);
goto unlock;
}
occ->active = true;
occ->next_update = jiffies + OCC_UPDATE_FREQUENCY;
occ_parse_poll_response(occ);
rc = occ_setup_sensor_attrs(occ);
if (rc) {
dev_err(occ->bus_dev,
"failed to setup sensor attrs: %d\n", rc);
goto unlock;
}
occ->hwmon = hwmon_device_register_with_groups(occ->bus_dev,
"occ", occ,
occ->groups);
if (IS_ERR(occ->hwmon)) {
rc = PTR_ERR(occ->hwmon);
occ->hwmon = NULL;
dev_err(occ->bus_dev,
"failed to register hwmon device: %d\n", rc);
goto unlock;
}
} else {
if (!occ->active) {
rc = -EALREADY;
goto unlock;
}
if (occ->hwmon)
hwmon_device_unregister(occ->hwmon);
occ->active = false;
occ->hwmon = NULL;
}
unlock:
mutex_unlock(&occ->lock);
return rc;
}
int occ_setup(struct occ *occ)
{
int rc;
mutex_init(&occ->lock);
occ->groups[0] = &occ->group;
rc = occ_setup_sysfs(occ);
if (rc)
dev_err(occ->bus_dev, "failed to setup sysfs: %d\n", rc);
......@@ -1195,6 +1226,15 @@ int occ_setup(struct occ *occ, const char *name)
}
EXPORT_SYMBOL_GPL(occ_setup);
void occ_shutdown(struct occ *occ)
{
occ_shutdown_sysfs(occ);
if (occ->hwmon)
hwmon_device_unregister(occ->hwmon);
}
EXPORT_SYMBOL_GPL(occ_shutdown);
MODULE_AUTHOR("Eddie James <eajames@linux.ibm.com>");
MODULE_DESCRIPTION("Common OCC hwmon code");
MODULE_LICENSE("GPL");
......@@ -106,6 +106,7 @@ struct occ {
struct attribute_group group;
const struct attribute_group *groups[2];
bool active;
int error; /* final transfer error after retry */
int last_error; /* latest transfer error */
unsigned int error_count; /* number of xfr errors observed */
......@@ -123,9 +124,11 @@ struct occ {
u8 prev_mode;
};
int occ_setup(struct occ *occ, const char *name);
int occ_active(struct occ *occ, bool active);
int occ_setup(struct occ *occ);
int occ_setup_sysfs(struct occ *occ);
void occ_shutdown(struct occ *occ);
void occ_shutdown_sysfs(struct occ *occ);
void occ_sysfs_poll_done(struct occ *occ);
int occ_update_response(struct occ *occ);
......
......@@ -223,7 +223,7 @@ static int p8_i2c_occ_probe(struct i2c_client *client)
occ->poll_cmd_data = 0x10; /* P8 OCC poll data */
occ->send_cmd = p8_i2c_occ_send_cmd;
return occ_setup(occ, "p8_occ");
return occ_setup(occ);
}
static int p8_i2c_occ_remove(struct i2c_client *client)
......
......@@ -145,7 +145,7 @@ static int p9_sbe_occ_probe(struct platform_device *pdev)
occ->poll_cmd_data = 0x20; /* P9 OCC poll data */
occ->send_cmd = p9_sbe_occ_send_cmd;
rc = occ_setup(occ, "p9_occ");
rc = occ_setup(occ);
if (rc == -ESHUTDOWN)
rc = -ENODEV; /* Host is shutdown, don't spew errors */
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -43,5 +43,6 @@ obj-$(CONFIG_SENSORS_TPS53679) += tps53679.o
obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o
obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o
obj-$(CONFIG_SENSORS_XDPE122) += xdpe12284.o
obj-$(CONFIG_SENSORS_XDPE152) += xdpe152c4.o
obj-$(CONFIG_SENSORS_ZL6100) += zl6100.o
obj-$(CONFIG_SENSORS_PIM4328) += pim4328.o
This diff is collapsed.
This diff is collapsed.
......@@ -438,6 +438,8 @@ struct pmbus_driver_info {
int (*read_byte_data)(struct i2c_client *client, int page, int reg);
int (*read_word_data)(struct i2c_client *client, int page, int phase,
int reg);
int (*write_byte_data)(struct i2c_client *client, int page, int reg,
u8 byte);
int (*write_word_data)(struct i2c_client *client, int page, int reg,
u16 word);
int (*write_byte)(struct i2c_client *client, int page, u8 value);
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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