Merge tag 'hwmon-for-v5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck...
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 26 Apr 2021 21:59:21 +0000 (14:59 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 26 Apr 2021 21:59:21 +0000 (14:59 -0700)
Pull hwmon updates from Guenter Roeck:
 "The most notable change is the removal of the amd_energy driver. It
  was rendered all but unusable by making its attributes privileged-only
  to work around a security issue. A suggested remedy was rejected by
  AMD, so the only real solution was to remove the driver. For the
  future, we'll have to make sure that no privileged-access-only drivers
  are accepted into the hwmon subsystem in the first place. The hwmon
  ABI document was updated accordingly.

  Other changes:

  PMBus drivers:
   - Added driver for MAX15301
   - Added driver for BluTek BPA-RS600
   - Added driver for fsp-3y PSUs and PDUs
   - Added driver for Infineon IR36021
   - Added driver for ST STPDDC60
   - Added support for TI TPS53676 to tps53679 driver
   - Introduced PMBUS symbol namespace. This was made necessary by a
     suggestion to use its exported functions from outside the hwmon
     subsystem.
   - Minor improvements and bug fixes

  New drivers:
   - Driver for NZXT Kraken X42/X52/X62/X72

  Driver enhancements:
   - Added support for Intel D5005 to intel-m10-bmc-hwmon driver
   - Added support for NCT6686D to nct6683 driver

  Other:
   - Converted sch5627 and amd9240 drivers to
     hwmon_device_register_with_info()
   - Added support for fan drawers capability and present registers to
     mlxreg-fan driver
   - Added Dell Latitude E7440 to fan control whitelist in dell-smm
     driver
   - Replaced snprintf in show functions with sysfs_emit. Done with
     coccinelle script for all drivers to preempt endless per-driver
     submissions of the same change.
   - Use kobj_to_dev().  Another coccinelle based change to preempt
     endless per-driver submissions of the same change.
   - Various minor fixes and improvements"

* tag 'hwmon-for-v5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (38 commits)
  hwmon: Remove amd_energy driver
  hwmon: Clarify scope of attribute access
  hwmon: (pmbus) Introduce PMBUS symbol namespace
  hwmon: (pmbus) Add pmbus driver for MAX15301
  hwmon: (sch5627) Remove unnecessary error path
  hwmon: (sch5627) Use devres function
  hwmon: (pmbus/pxe1610) don't bail out when not all pages are active
  hwmon: Add driver for fsp-3y PSUs and PDUs
  hwmon: (intel-m10-bmc-hwmon) add sensor support of Intel D5005 card
  hwmon: (sch5627) Split sch5627_update_device()
  hwmon: (sch5627) Convert to hwmon_device_register_with_info()
  hwmon: (nct6683) remove useless function
  hwmon: (dell-smm) Add Dell Latitude E7440 to fan control whitelist
  MAINTAINERS: Add keyword pattern for hwmon registration functions
  hwmon: (mlxreg-fan) Add support for fan drawers capability and present registers
  hwmon: (pmbus/tps53679) Add support for TI TPS53676
  dt-bindings: Add trivial device entry for TPS53676
  hwmon: (ftsteutates) Rudimentary typo fixes
  hwmon: (pmbus) Add driver for BluTek BPA-RS600
  dt-bindings: Add vendor prefix and trivial device for BluTek BPA-RS600
  ...

93 files changed:
Documentation/devicetree/bindings/trivial-devices.yaml
Documentation/devicetree/bindings/vendor-prefixes.yaml
Documentation/hwmon/amd_energy.rst [deleted file]
Documentation/hwmon/bpa-rs600.rst [new file with mode: 0644]
Documentation/hwmon/corsair-psu.rst
Documentation/hwmon/fsp-3y.rst [new file with mode: 0644]
Documentation/hwmon/index.rst
Documentation/hwmon/ir36021.rst [new file with mode: 0644]
Documentation/hwmon/max15301.rst [new file with mode: 0644]
Documentation/hwmon/nzxt-kraken2.rst [new file with mode: 0644]
Documentation/hwmon/stpddc60.rst [new file with mode: 0644]
Documentation/hwmon/sysfs-interface.rst
Documentation/hwmon/tps53679.rst
MAINTAINERS
drivers/hwmon/Kconfig
drivers/hwmon/Makefile
drivers/hwmon/adc128d818.c
drivers/hwmon/adm9240.c
drivers/hwmon/amd_energy.c [deleted file]
drivers/hwmon/applesmc.c
drivers/hwmon/corsair-psu.c
drivers/hwmon/dell-smm-hwmon.c
drivers/hwmon/ds1621.c
drivers/hwmon/ftsteutates.c
drivers/hwmon/hwmon.c
drivers/hwmon/ina209.c
drivers/hwmon/ina2xx.c
drivers/hwmon/ina3221.c
drivers/hwmon/intel-m10-bmc-hwmon.c
drivers/hwmon/it87.c
drivers/hwmon/lineage-pem.c
drivers/hwmon/lm63.c
drivers/hwmon/ltc2945.c
drivers/hwmon/ltc2990.c
drivers/hwmon/ltc4151.c
drivers/hwmon/ltc4215.c
drivers/hwmon/ltc4222.c
drivers/hwmon/ltc4260.c
drivers/hwmon/ltc4261.c
drivers/hwmon/max16065.c
drivers/hwmon/max6697.c
drivers/hwmon/mlxreg-fan.c
drivers/hwmon/nct6683.c
drivers/hwmon/nzxt-kraken2.c [new file with mode: 0644]
drivers/hwmon/occ/common.c
drivers/hwmon/occ/sysfs.c
drivers/hwmon/pmbus/Kconfig
drivers/hwmon/pmbus/Makefile
drivers/hwmon/pmbus/adm1266.c
drivers/hwmon/pmbus/adm1275.c
drivers/hwmon/pmbus/bel-pfe.c
drivers/hwmon/pmbus/bpa-rs600.c [new file with mode: 0644]
drivers/hwmon/pmbus/fsp-3y.c [new file with mode: 0644]
drivers/hwmon/pmbus/ibm-cffps.c
drivers/hwmon/pmbus/inspur-ipsps.c
drivers/hwmon/pmbus/ir35221.c
drivers/hwmon/pmbus/ir36021.c [new file with mode: 0644]
drivers/hwmon/pmbus/ir38064.c
drivers/hwmon/pmbus/irps5401.c
drivers/hwmon/pmbus/isl68137.c
drivers/hwmon/pmbus/lm25066.c
drivers/hwmon/pmbus/ltc2978.c
drivers/hwmon/pmbus/ltc3815.c
drivers/hwmon/pmbus/max15301.c [new file with mode: 0644]
drivers/hwmon/pmbus/max16064.c
drivers/hwmon/pmbus/max16601.c
drivers/hwmon/pmbus/max20730.c
drivers/hwmon/pmbus/max20751.c
drivers/hwmon/pmbus/max31785.c
drivers/hwmon/pmbus/max34440.c
drivers/hwmon/pmbus/max8688.c
drivers/hwmon/pmbus/mp2975.c
drivers/hwmon/pmbus/pm6764tr.c
drivers/hwmon/pmbus/pmbus.c
drivers/hwmon/pmbus/pmbus.h
drivers/hwmon/pmbus/pmbus_core.c
drivers/hwmon/pmbus/pxe1610.c
drivers/hwmon/pmbus/q54sj108a2.c
drivers/hwmon/pmbus/stpddc60.c [new file with mode: 0644]
drivers/hwmon/pmbus/tps40422.c
drivers/hwmon/pmbus/tps53679.c
drivers/hwmon/pmbus/ucd9000.c
drivers/hwmon/pmbus/ucd9200.c
drivers/hwmon/pmbus/xdpe12284.c
drivers/hwmon/pmbus/zl6100.c
drivers/hwmon/s3c-hwmon.c
drivers/hwmon/sch5627.c
drivers/hwmon/sch5636.c
drivers/hwmon/smm665.c
drivers/hwmon/stts751.c
drivers/hwmon/vexpress-hwmon.c
drivers/hwmon/xgene-hwmon.c
drivers/mfd/intel-m10-bmc.c

index a327130..08e417e 100644 (file)
@@ -50,6 +50,8 @@ properties:
           - atmel,atsha204a
             # i2c h/w elliptic curve crypto module
           - atmel,atecc508a
+            # BPA-RS600: Power Supply
+          - blutek,bpa-rs600
             # Bosch Sensortec pressure, temperature, humididty and VOC sensor
           - bosch,bme680
             # CM32181: Ambient Light Sensor
@@ -102,6 +104,8 @@ properties:
           - mps,mp2975
             # G751: Digital Temperature Sensor and Thermal Watchdog with Two-Wire Interface
           - gmt,g751
+            # Infineon IR36021 digital POL buck controller
+          - infineon,ir36021
             # Infineon IR38064 Voltage Regulator
           - infineon,ir38064
             # Infineon SLB9635 (Soft-) I2C TPM (old protocol, max 100khz)
@@ -288,6 +292,8 @@ properties:
           - ti,tmp103
             # Digital Temperature Sensor
           - ti,tmp275
+            # TI Dual channel DCAP+ multiphase controller TPS53676 with AVSBus
+          - ti,tps53676
             # TI Dual channel DCAP+ multiphase controller TPS53679
           - ti,tps53679
             # TI Dual channel DCAP+ multiphase controller TPS53688
index b6e71a7..b6e9d78 100644 (file)
@@ -171,6 +171,8 @@ patternProperties:
     description: Beckhoff Automation GmbH & Co. KG
   "^bitmain,.*":
     description: Bitmain Technologies
+  "^blutek,.*":
+    description: BluTek Power
   "^boe,.*":
     description: BOE Technology Group Co., Ltd.
   "^bosch,.*":
diff --git a/Documentation/hwmon/amd_energy.rst b/Documentation/hwmon/amd_energy.rst
deleted file mode 100644 (file)
index 9d58cd5..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0
-
-Kernel driver amd_energy
-==========================
-
-Supported chips:
-
-* AMD Family 17h Processors: Model 30h
-
-* AMD Family 19h Processors: Model 01h
-
-  Prefix: 'amd_energy'
-
-  Addresses used:  RAPL MSRs
-
-  Datasheets:
-
-  - Processor Programming Reference (PPR) for AMD Family 17h Model 01h, Revision B1 Processors
-
-       https://developer.amd.com/wp-content/resources/55570-B1_PUB.zip
-
-  - Preliminary Processor Programming Reference (PPR) for AMD Family 17h Model 31h, Revision B0 Processors
-
-       https://developer.amd.com/wp-content/resources/56176_ppr_Family_17h_Model_71h_B0_pub_Rev_3.06.zip
-
-Author: Naveen Krishna Chatradhi <nchatrad@amd.com>
-
-Description
------------
-
-The Energy driver exposes the energy counters that are
-reported via the Running Average Power Limit (RAPL)
-Model-specific Registers (MSRs) via the hardware monitor
-(HWMON) sysfs interface.
-
-1. Power, Energy and Time Units
-   MSR_RAPL_POWER_UNIT/ C001_0299:
-   shared with all cores in the socket
-
-2. Energy consumed by each Core
-   MSR_CORE_ENERGY_STATUS/ C001_029A:
-   32-bitRO, Accumulator, core-level power reporting
-
-3. Energy consumed by Socket
-   MSR_PACKAGE_ENERGY_STATUS/ C001_029B:
-   32-bitRO, Accumulator, socket-level power reporting,
-   shared with all cores in socket
-
-These registers are updated every 1ms and cleared on
-reset of the system.
-
-Note: If SMT is enabled, Linux enumerates all threads as cpus.
-Since, the energy status registers are accessed at core level,
-reading those registers from the sibling threads would result
-in duplicate values. Hence, energy counter entries are not
-populated for the siblings.
-
-Energy Caluclation
-------------------
-
-Energy information (in Joules) is based on the multiplier,
-1/2^ESU; where ESU is an unsigned integer read from
-MSR_RAPL_POWER_UNIT register. Default value is 10000b,
-indicating energy status unit is 15.3 micro-Joules increment.
-
-Reported values are scaled as per the formula
-
-scaled value = ((1/2^ESU) * (Raw value) * 1000000UL) in uJoules
-
-Users calculate power for a given domain by calculating
-       dEnergy/dTime for that domain.
-
-Energy accumulation
---------------------------
-
-Current, Socket energy status register is 32bit, assuming a 240W
-2P system, the register would wrap around in
-
-       2^32*15.3 e-6/240 * 2 = 547.60833024 secs to wrap(~9 mins)
-
-The Core energy register may wrap around after several days.
-
-To improve the wrap around time, a kernel thread is implemented
-to accumulate the socket energy counters and one core energy counter
-per run to a respective 64-bit counter. The kernel thread starts
-running during probe, wakes up every 100secs and stops running
-when driver is removed.
-
-Frequency of the accumulator thread is set during the probe
-based on the chosen energy unit resolution. For example
-A. fine grain (1.625 micro J)
-B. course grain (0.125 milli J)
-
-A socket and core energy read would return the current register
-value added to the respective energy accumulator.
-
-Sysfs attributes
-----------------
-
-=============== ========  =====================================
-Attribute      Label     Description
-===============        ========  =====================================
-
-* For index N between [1] and [nr_cpus]
-
-===============        ========  ======================================
-energy[N]_input EcoreX   Core Energy   X = [0] to [nr_cpus - 1]
-                         Measured input core energy
-===============        ========  ======================================
-
-* For N between [nr_cpus] and [nr_cpus + nr_socks]
-
-===============        ========  ======================================
-energy[N]_input EsocketX  Socket Energy X = [0] to [nr_socks -1]
-                         Measured input socket energy
-=============== ========  ======================================
-
-Note: To address CVE-2020-12912, the visibility of the energy[N]_input
-attributes is restricted to owner and groups only.
diff --git a/Documentation/hwmon/bpa-rs600.rst b/Documentation/hwmon/bpa-rs600.rst
new file mode 100644 (file)
index 0000000..2831399
--- /dev/null
@@ -0,0 +1,74 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Kernel driver bpa-rs600
+=======================
+
+Supported chips:
+
+  * BPA-RS600-120
+
+    Datasheet: Publicly available at the BluTek website
+       http://blutekpower.com/wp-content/uploads/2019/01/BPA-RS600-120-07-19-2018.pdf
+
+Authors:
+      - Chris Packham <chris.packham@alliedtelesis.co.nz>
+
+Description
+-----------
+
+The BPA-RS600 is a compact 600W removable power supply module.
+
+Usage Notes
+-----------
+
+This driver does not probe for PMBus devices. You will have to instantiate
+devices explicitly.
+
+Sysfs attributes
+----------------
+
+======================= ============================================
+curr1_label             "iin"
+curr1_input            Measured input current
+curr1_max              Maximum input current
+curr1_max_alarm                Input current high alarm
+
+curr2_label            "iout1"
+curr2_input            Measured output current
+curr2_max              Maximum output current
+curr2_max_alarm                Output current high alarm
+
+fan1_input             Measured fan speed
+fan1_alarm             Fan warning
+fan1_fault             Fan fault
+
+in1_label              "vin"
+in1_input              Measured input voltage
+in1_max                        Maximum input voltage
+in1_max_alarm          Input voltage high alarm
+in1_min                        Minimum input voltage
+in1_min_alarm          Input voltage low alarm
+
+in2_label              "vout1"
+in2_input              Measured output voltage
+in2_max                        Maximum output voltage
+in2_max_alarm          Output voltage high alarm
+in2_min                        Maximum output voltage
+in2_min_alarm          Output voltage low alarm
+
+power1_label           "pin"
+power1_input           Measured input power
+power1_alarm           Input power alarm
+power1_max             Maximum input power
+
+power2_label           "pout1"
+power2_input           Measured output power
+power2_max             Maximum output power
+power2_max_alarm       Output power high alarm
+
+temp1_input            Measured temperature around input connector
+temp1_alarm            Temperature alarm
+
+temp2_input            Measured temperature around output connector
+temp2_alarm            Temperature alarm
+======================= ============================================
index 396b95c..e8378e7 100644 (file)
@@ -47,19 +47,30 @@ Sysfs entries
 =======================        ========================================================
 curr1_input            Total current usage
 curr2_input            Current on the 12v psu rail
+curr2_crit             Current max critical value on the 12v psu rail
 curr3_input            Current on the 5v psu rail
+curr3_crit             Current max critical value on the 5v psu rail
 curr4_input            Current on the 3.3v psu rail
+curr4_crit             Current max critical value on the 3.3v psu rail
 fan1_input             RPM of psu fan
 in0_input              Voltage of the psu ac input
 in1_input              Voltage of the 12v psu rail
+in1_crit               Voltage max critical value on the 12v psu rail
+in1_lcrit              Voltage min critical value on the 12v psu rail
 in2_input              Voltage of the 5v psu rail
-in3_input              Voltage of the 3.3 psu rail
+in2_crit               Voltage max critical value on the 5v psu rail
+in2_lcrit              Voltage min critical value on the 5v psu rail
+in3_input              Voltage of the 3.3v psu rail
+in3_crit               Voltage max critical value on the 3.3v psu rail
+in3_lcrit              Voltage min critical value on the 3.3v psu rail
 power1_input           Total power usage
 power2_input           Power usage of the 12v psu rail
 power3_input           Power usage of the 5v psu rail
 power4_input           Power usage of the 3.3v psu rail
 temp1_input            Temperature of the psu vrm component
+temp1_crit             Temperature max cirtical value of the psu vrm component
 temp2_input            Temperature of the psu case
+temp2_crit             Temperature max critical value of psu case
 =======================        ========================================================
 
 Usage Notes
diff --git a/Documentation/hwmon/fsp-3y.rst b/Documentation/hwmon/fsp-3y.rst
new file mode 100644 (file)
index 0000000..5693d83
--- /dev/null
@@ -0,0 +1,28 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Kernel driver fsp3y
+======================
+Supported devices:
+  * 3Y POWER YH-5151E
+  * 3Y POWER YM-2151E
+
+Author: Václav Kubernát <kubernat@cesnet.cz>
+
+Description
+-----------
+This driver implements limited support for two 3Y POWER devices.
+
+Sysfs entries
+-------------
+  * in1_input            input voltage
+  * in2_input            12V output voltage
+  * in3_input            5V output voltage
+  * curr1_input          input current
+  * curr2_input          12V output current
+  * curr3_input          5V output current
+  * fan1_input           fan rpm
+  * temp1_input          temperature 1
+  * temp2_input          temperature 2
+  * temp3_input          temperature 3
+  * power1_input         input power
+  * power2_input         output power
index 8d5a2df..9ed60fa 100644 (file)
@@ -39,12 +39,12 @@ Hardware Monitoring Kernel Drivers
    adt7475
    aht10
    amc6821
-   amd_energy
    asb100
    asc7621
    aspeed-pwm-tacho
    bcm54140
    bel-pfe
+   bpa-rs600
    bt1-pvt
    coretemp
    corsair-cpro
@@ -62,6 +62,7 @@ Hardware Monitoring Kernel Drivers
    f71805f
    f71882fg
    fam15h_power
+   fsp-3y
    ftsteutates
    g760a
    g762
@@ -77,6 +78,7 @@ Hardware Monitoring Kernel Drivers
    intel-m10-bmc-hwmon
    ir35221
    ir38064
+   ir36021
    isl68137
    it87
    jc42
@@ -112,6 +114,7 @@ Hardware Monitoring Kernel Drivers
    ltc4260
    ltc4261
    max127
+   max15301
    max16064
    max16065
    max1619
@@ -142,6 +145,7 @@ Hardware Monitoring Kernel Drivers
    npcm750-pwm-fan
    nsa320
    ntc_thermistor
+   nzxt-kraken2
    occ
    pc87360
    pc87427
@@ -168,6 +172,7 @@ Hardware Monitoring Kernel Drivers
    smsc47m192
    smsc47m1
    sparx5-temp
+   stpddc60
    tc654
    tc74
    thmc50
diff --git a/Documentation/hwmon/ir36021.rst b/Documentation/hwmon/ir36021.rst
new file mode 100644 (file)
index 0000000..ca3436b
--- /dev/null
@@ -0,0 +1,63 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Kernel driver ir36021
+=====================
+
+Supported chips:
+
+  * Infineon IR36021
+
+    Prefix: ir36021
+    Addresses scanned: -
+
+    Datasheet: Publicly available at the Infineon website
+      https://www.infineon.com/dgdl/ir36021.pdf?fileId=5546d462533600a4015355d0aa2d1775
+
+Authors:
+      - Chris Packham <chris.packham@alliedtelesis.co.nz>
+
+Description
+-----------
+
+The IR36021 is a dual‐loop digital multi‐phase buck controller designed for
+point of load applications.
+
+Usage Notes
+-----------
+
+This driver does not probe for PMBus devices. You will have to instantiate
+devices explicitly.
+
+Sysfs attributes
+----------------
+
+======================= ===========================
+curr1_label             "iin"
+curr1_input             Measured input current
+curr1_alarm             Input fault alarm
+
+curr2_label             "iout1"
+curr2_input             Measured output current
+curr2_alarm             Output over-current alarm
+
+in1_label               "vin"
+in1_input               Measured input voltage
+in1_alarm               Input under-voltage alarm
+
+in2_label               "vout1"
+in2_input               Measured output voltage
+in2_alarm               Output over-voltage alarm
+
+power1_label            "pin"
+power1_input            Measured input power
+power1_alarm            Input under-voltage alarm
+
+power2_label            "pout1"
+power2_input            Measured output power
+
+temp1_input             Measured temperature
+temp1_alarm             Temperature alarm
+
+temp2_input             Measured other loop temperature
+temp2_alarm             Temperature alarm
+======================= ===========================
diff --git a/Documentation/hwmon/max15301.rst b/Documentation/hwmon/max15301.rst
new file mode 100644 (file)
index 0000000..e3dc22f
--- /dev/null
@@ -0,0 +1,87 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Kernel driver max15301
+======================
+
+Supported chips:
+
+  * Maxim MAX15301
+
+    Prefix: 'max15301', 'bmr461'
+
+    Addresses scanned: -
+
+    Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX15301.pdf
+
+Author: Erik Rosen <erik.rosen@metormote.com>
+
+
+Description
+-----------
+
+This driver supports hardware monitoring for Maxim MAX15301 controller chip and
+compatible modules.
+
+The driver is a client driver to the core PMBus driver. Please see
+Documentation/hwmon/pmbus.rst and Documentation.hwmon/pmbus-core for details
+on PMBus client drivers.
+
+
+Usage Notes
+-----------
+
+This driver does not auto-detect devices. You will have to instantiate the
+devices explicitly. Please see Documentation/i2c/instantiating-devices.rst for
+details.
+
+
+Platform data support
+---------------------
+
+The driver supports standard PMBus driver platform data.
+
+
+Module parameters
+-----------------
+
+delay
+-----
+
+The controller requires a minimum interval between I2C bus accesses.
+The default interval is set to 100 us. For manual override, the driver
+provides a writeable module parameter, 'delay', which can be used to
+set the interval to a value between 0 and 65,535 microseconds.
+
+
+Sysfs entries
+-------------
+
+The following attributes are supported. Limits are read-write; all other
+attributes are read-only.
+
+======================= ========================================================
+in1_label              "vin"
+in1_input              Measured input voltage.
+in1_lcrit              Critical minimum input voltage.
+in1_crit               Critical maximum input voltage.
+in1_lcrit_alarm                Input voltage critical low alarm.
+in1_crit_alarm         Input voltage critical high alarm.
+
+in2_label              "vout1"
+in2_input              Measured output voltage.
+in2_lcrit              Critical minimum output Voltage.
+in2_crit               Critical maximum output voltage.
+in2_lcrit_alarm                Critical output voltage critical low alarm.
+in2_crit_alarm         Critical output voltage critical high alarm.
+
+curr1_label            "iout1"
+curr1_input            Measured output current.
+curr1_crit             Critical maximum output current.
+curr1_crit_alarm       Output current critical high alarm.
+
+temp1_input            Measured maximum temperature of all phases.
+temp1_max              Maximum temperature limit.
+temp1_max_alarm                High temperature alarm.
+temp1_crit             Critical maximum temperature limit.
+temp1_crit_alarm       Critical maximum temperature alarm.
+======================= ========================================================
diff --git a/Documentation/hwmon/nzxt-kraken2.rst b/Documentation/hwmon/nzxt-kraken2.rst
new file mode 100644 (file)
index 0000000..94025de
--- /dev/null
@@ -0,0 +1,42 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+Kernel driver nzxt-kraken2
+==========================
+
+Supported devices:
+
+* NZXT Kraken X42
+* NZXT Kraken X52
+* NZXT Kraken X62
+* NZXT Kraken X72
+
+Author: Jonas Malaco
+
+Description
+-----------
+
+This driver enables hardware monitoring support for NZXT Kraken X42/X52/X62/X72
+all-in-one CPU liquid coolers.  Three sensors are available: fan speed, pump
+speed and coolant temperature.
+
+Fan and pump control, while supported by the firmware, are not currently
+exposed.  The addressable RGB LEDs, present in the integrated CPU water block
+and pump head, are not supported either.  But both features can be found in
+existing user-space tools (e.g. `liquidctl`_).
+
+.. _liquidctl: https://github.com/liquidctl/liquidctl
+
+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)
+=======================        ========================================================
diff --git a/Documentation/hwmon/stpddc60.rst b/Documentation/hwmon/stpddc60.rst
new file mode 100644 (file)
index 0000000..7f7ce7f
--- /dev/null
@@ -0,0 +1,90 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Kernel driver stpddc60
+======================
+
+Supported chips:
+
+  * ST STPDDC60
+
+    Prefix: 'stpddc60', 'bmr481'
+
+    Addresses scanned: -
+
+    Datasheet: https://flexpowermodules.com/documents/fpm-techspec-bmr481
+
+Author: Erik Rosen <erik.rosen@metormote.com>
+
+
+Description
+-----------
+
+This driver supports hardware monitoring for ST STPDDC60 controller chip and
+compatible modules.
+
+The driver is a client driver to the core PMBus driver. Please see
+Documentation/hwmon/pmbus.rst and Documentation.hwmon/pmbus-core 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 vout under- and over-voltage limits are set in relation to the commanded
+output voltage as a positive or negative offset in the interval 50mV to 400mV
+in 50mV steps. This means that the absolute values of the limits will change
+when the commanded output voltage changes. Also, care should be taken when
+writing to those limits since in the worst case the commanded output voltage
+could change at the same time as the limit is written to, wich will lead to
+unpredictable results.
+
+
+Platform data support
+---------------------
+
+The driver supports standard PMBus driver platform data.
+
+
+Sysfs entries
+-------------
+
+The following attributes are supported. Vin, iout, pout and temp limits
+are read-write; all other attributes are read-only.
+
+======================= ========================================================
+in1_label              "vin"
+in1_input              Measured input voltage.
+in1_lcrit              Critical minimum input voltage.
+in1_crit               Critical maximum input voltage.
+in1_lcrit_alarm                Input voltage critical low alarm.
+in1_crit_alarm         Input voltage critical high alarm.
+
+in2_label              "vout1"
+in2_input              Measured output voltage.
+in2_lcrit              Critical minimum output voltage.
+in2_crit               Critical maximum output voltage.
+in2_lcrit_alarm                Critical output voltage critical low alarm.
+in2_crit_alarm         Critical output voltage critical high alarm.
+
+curr1_label            "iout1"
+curr1_input            Measured output current.
+curr1_max              Maximum output current.
+curr1_max_alarm                Output current high alarm.
+curr1_crit             Critical maximum output current.
+curr1_crit_alarm       Output current critical high alarm.
+
+power1_label           "pout1"
+power1_input           Measured output power.
+power1_crit            Critical maximum output power.
+power1_crit_alarm      Output power critical high alarm.
+
+temp1_input            Measured maximum temperature of all phases.
+temp1_max              Maximum temperature limit.
+temp1_max_alarm                High temperature alarm.
+temp1_crit             Critical maximum temperature limit.
+temp1_crit_alarm       Critical maximum temperature alarm.
+======================= ========================================================
index 678c9c6..13c5acb 100644 (file)
@@ -65,6 +65,14 @@ the desired value must be written, note that strings which are not a number
 are interpreted as 0! For more on how written strings are interpreted see the
 "sysfs attribute writes interpretation" section at the end of this file.
 
+Attribute access
+----------------
+
+Hardware monitoring sysfs attributes are displayed by unrestricted userspace
+applications. For this reason, all standard ABI attributes shall be world
+readable. Writeable standard ABI attributes shall be writeable only for
+privileged users.
+
 -------------------------------------------------------------------------
 
 ======= ===========================================
index c7c589e..3b95616 100644 (file)
@@ -19,6 +19,14 @@ Supported chips:
 
     Datasheet: https://www.ti.com/lit/gpn/TPS53667
 
+  * Texas Instruments TPS53676
+
+    Prefix: 'tps53676'
+
+    Addresses scanned: -
+
+    Datasheet: https://www.ti.com/lit/gpn/TPS53676
+
   * Texas Instruments TPS53679
 
     Prefix: 'tps53679'
@@ -136,7 +144,7 @@ power1_input                Measured input power.
 power[N]_label         "pout[1-2]".
 
                        - TPS53647, TPS53667: N=2
-                       - TPS53679, TPS53681, TPS53588: N=2,3
+                       - TPS53676, TPS53679, TPS53681, TPS53588: N=2,3
 
 power[N]_input         Measured output power.
 
@@ -156,10 +164,11 @@ curr[N]_label             "iout[1-2]" or "iout1.[0-5]".
 
                        The first digit is the output channel, the second
                        digit is the phase within the channel. Per-phase
-                       telemetry supported on TPS53681 only.
+                       telemetry supported on TPS53676 and TPS53681 only.
 
                        - TPS53647, TPS53667: N=2
                        - TPS53679, TPS53588: N=2,3
+                       - TPS53676: N=2-8
                        - TPS53681: N=2-9
 
 curr[N]_input          Measured output current.
index ac0ff5f..5e678f6 100644 (file)
@@ -880,13 +880,6 @@ S: Supported
 T:     git git://people.freedesktop.org/~agd5f/linux
 F:     drivers/gpu/drm/amd/display/
 
-AMD ENERGY DRIVER
-M:     Naveen Krishna Chatradhi <nchatrad@amd.com>
-L:     linux-hwmon@vger.kernel.org
-S:     Maintained
-F:     Documentation/hwmon/amd_energy.rst
-F:     drivers/hwmon/amd_energy.c
-
 AMD FAM15H PROCESSOR POWER MONITORING DRIVER
 M:     Huang Rui <ray.huang@amd.com>
 L:     linux-hwmon@vger.kernel.org
@@ -7927,6 +7920,7 @@ F:        Documentation/hwmon/
 F:     drivers/hwmon/
 F:     include/linux/hwmon*.h
 F:     include/trace/events/hwmon*.h
+K:     (devm_)?hwmon_device_(un)?register(|_with_groups|_with_info)
 
 HARDWARE RANDOM NUMBER GENERATOR CORE
 M:     Matt Mackall <mpm@selenic.com>
@@ -10898,6 +10892,13 @@ S:     Orphan
 F:     drivers/video/fbdev/matrox/matroxfb_*
 F:     include/uapi/linux/matroxfb.h
 
+MAX15301 DRIVER
+M:     Daniel Nilsson <daniel.nilsson@flex.com>
+L:     linux-hwmon@vger.kernel.org
+S:     Maintained
+F:     Documentation/hwmon/max15301.rst
+F:     drivers/hwmon/pmbus/max15301.c
+
 MAX16065 HARDWARE MONITOR DRIVER
 M:     Guenter Roeck <linux@roeck-us.net>
 L:     linux-hwmon@vger.kernel.org
@@ -13027,6 +13028,13 @@ L:     linux-nfc@lists.01.org (moderated for non-subscribers)
 S:     Supported
 F:     drivers/nfc/nxp-nci
 
+NZXT-KRAKEN2 HARDWARE MONITORING DRIVER
+M:     Jonas Malaco <jonas@protocubo.io>
+L:     linux-hwmon@vger.kernel.org
+S:     Maintained
+F:     Documentation/hwmon/nzxt-kraken2.rst
+F:     drivers/hwmon/nzxt-kraken2.c
+
 OBJAGG
 M:     Jiri Pirko <jiri@nvidia.com>
 L:     netdev@vger.kernel.org
@@ -17068,6 +17076,13 @@ L:     linux-i2c@vger.kernel.org
 S:     Maintained
 F:     drivers/i2c/busses/i2c-stm32*
 
+ST STPDDC60 DRIVER
+M:     Daniel Nilsson <daniel.nilsson@flex.com>
+L:     linux-hwmon@vger.kernel.org
+S:     Maintained
+F:     Documentation/hwmon/stpddc60.rst
+F:     drivers/hwmon/pmbus/stpddc60.c
+
 ST VL53L0X ToF RANGER(I2C) IIO DRIVER
 M:     Song Qiang <songqiang1304521@gmail.com>
 L:     linux-iio@vger.kernel.org
index 54f04e6..8762490 100644 (file)
@@ -321,16 +321,6 @@ config SENSORS_FAM15H_POWER
          This driver can also be built as a module. If so, the module
          will be called fam15h_power.
 
-config SENSORS_AMD_ENERGY
-       tristate "AMD RAPL MSR based Energy driver"
-       depends on X86
-       help
-         If you say yes here you get support for core and package energy
-         sensors, based on RAPL MSR for AMD family 17h and above CPUs.
-
-         This driver can also be built as a module. If so, the module
-         will be called as amd_energy.
-
 config SENSORS_APPLESMC
        tristate "Apple SMC (Motion sensor, light sensor, keyboard backlight)"
        depends on INPUT && X86
@@ -1492,6 +1482,16 @@ config SENSORS_NSA320
          This driver can also be built as a module. If so, the module
          will be called nsa320-hwmon.
 
+config SENSORS_NZXT_KRAKEN2
+       tristate "NZXT Kraken X42/X51/X62/X72 liquid coolers"
+       depends on USB_HID
+       help
+         If you say yes here you get support for hardware monitoring for the
+         NZXT Kraken X42/X52/X62/X72 all-in-one CPU liquid coolers.
+
+         This driver can also be built as a module. If so, the module
+         will be called nzxt-kraken2.
+
 source "drivers/hwmon/occ/Kconfig"
 
 config SENSORS_PCF8591
index fe38e8a..59e78bc 100644 (file)
@@ -155,6 +155,7 @@ obj-$(CONFIG_SENSORS_NCT7904)       += nct7904.o
 obj-$(CONFIG_SENSORS_NPCM7XX)  += npcm750-pwm-fan.o
 obj-$(CONFIG_SENSORS_NSA320)   += nsa320-hwmon.o
 obj-$(CONFIG_SENSORS_NTC_THERMISTOR)   += ntc_thermistor.o
+obj-$(CONFIG_SENSORS_NZXT_KRAKEN2) += nzxt-kraken2.o
 obj-$(CONFIG_SENSORS_PC87360)  += pc87360.o
 obj-$(CONFIG_SENSORS_PC87427)  += pc87427.o
 obj-$(CONFIG_SENSORS_PCF8591)  += pcf8591.o
index 6c9a906..fd938c7 100644 (file)
@@ -248,7 +248,7 @@ static ssize_t adc128_alarm_show(struct device *dev,
 static umode_t adc128_is_visible(struct kobject *kobj,
                                 struct attribute *attr, int index)
 {
-       struct device *dev = container_of(kobj, struct device, kobj);
+       struct device *dev = kobj_to_dev(kobj);
        struct adc128_data *data = dev_get_drvdata(dev);
 
        if (index < ADC128_ATTR_NUM_VOLT) {
index cc3e018..5677263 100644 (file)
@@ -28,6 +28,7 @@
  * LM81 extended temp reading not implemented
  */
 
+#include <linux/bits.h>
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/slab.h>
@@ -37,7 +38,6 @@
 #include <linux/hwmon-vid.h>
 #include <linux/err.h>
 #include <linux/mutex.h>
-#include <linux/jiffies.h>
 #include <linux/regmap.h>
 
 /* Addresses to scan */
@@ -123,32 +123,18 @@ static inline unsigned int AOUT_FROM_REG(u8 reg)
 
 /* per client data */
 struct adm9240_data {
-       struct i2c_client *client;
+       struct device *dev;
        struct regmap *regmap;
        struct mutex update_lock;
-       char valid;
-       unsigned long last_updated_measure;
-       unsigned long last_updated_config;
-
-       u8 in[6];               /* ro   in0_input */
-       u8 in_max[6];           /* rw   in0_max */
-       u8 in_min[6];           /* rw   in0_min */
-       u8 fan[2];              /* ro   fan1_input */
-       u8 fan_min[2];          /* rw   fan1_min */
+
        u8 fan_div[2];          /* rw   fan1_div, read-only accessor */
-       s16 temp;               /* ro   temp1_input, 9-bit sign-extended */
-       s8 temp_max[2];         /* rw   0 -> temp_max, 1 -> temp_max_hyst */
-       u16 alarms;             /* ro   alarms */
-       u8 aout;                /* rw   aout_output */
-       u8 vid;                 /* ro   vid */
        u8 vrm;                 /* --   vrm set on startup, no accessor */
 };
 
 /* write new fan div, callers must hold data->update_lock */
-static int adm9240_write_fan_div(struct adm9240_data *data, int nr,
-               u8 fan_div)
+static int adm9240_write_fan_div(struct adm9240_data *data, int channel, u8 fan_div)
 {
-       unsigned int reg, old, shift = (nr + 2) * 2;
+       unsigned int reg, old, shift = (channel + 2) * 2;
        int err;
 
        err = regmap_read(data->regmap, ADM9240_REG_VID_FAN_DIV, &reg);
@@ -160,336 +146,13 @@ static int adm9240_write_fan_div(struct adm9240_data *data, int nr,
        err = regmap_write(data->regmap, ADM9240_REG_VID_FAN_DIV, reg);
        if (err < 0)
                return err;
-       dev_dbg(&data->client->dev,
-               "fan%d clock divider changed from %u to %u\n",
-               nr + 1, 1 << old, 1 << fan_div);
-
-       return 0;
-}
-
-static int adm9240_update_measure(struct adm9240_data *data)
-{
-       unsigned int val;
-       u8 regs[2];
-       int err;
-       int i;
-
-       err = regmap_bulk_read(data->regmap, ADM9240_REG_IN(0), &data->in[0], 6);
-       if (err < 0)
-               return err;
-       err = regmap_bulk_read(data->regmap, ADM9240_REG_INT(0), &regs, 2);
-       if (err < 0)
-               return err;
-
-       data->alarms = regs[0] | regs[1] << 8;
-
-       /*
-        * read temperature: assume temperature changes less than
-        * 0.5'C per two measurement cycles thus ignore possible
-        * but unlikely aliasing error on lsb reading. --Grant
-        */
-       err = regmap_read(data->regmap, ADM9240_REG_TEMP, &val);
-       if (err < 0)
-               return err;
-       data->temp = val << 8;
-       err = regmap_read(data->regmap, ADM9240_REG_TEMP_CONF, &val);
-       if (err < 0)
-               return err;
-       data->temp |= val;
-
-       err = regmap_bulk_read(data->regmap, ADM9240_REG_FAN(0),
-                              &data->fan[0], 2);
-       if (err < 0)
-               return err;
-
-       for (i = 0; i < 2; i++) { /* read fans */
-               /* adjust fan clock divider on overflow */
-               if (data->valid && data->fan[i] == 255 &&
-                               data->fan_div[i] < 3) {
-
-                       err = adm9240_write_fan_div(data, i,
-                                       ++data->fan_div[i]);
-                       if (err < 0)
-                               return err;
-
-                       /* adjust fan_min if active, but not to 0 */
-                       if (data->fan_min[i] < 255 &&
-                                       data->fan_min[i] >= 2)
-                               data->fan_min[i] /= 2;
-               }
-       }
+       dev_dbg(data->dev,
+               "fan%d clock divider changed from %lu to %lu\n",
+               channel + 1, BIT(old), BIT(fan_div));
 
        return 0;
 }
 
-static int adm9240_update_config(struct adm9240_data *data)
-{
-       unsigned int val;
-       int i;
-       int err;
-
-       for (i = 0; i < 6; i++) {
-               err = regmap_raw_read(data->regmap, ADM9240_REG_IN_MIN(i),
-                                     &data->in_min[i], 1);
-               if (err < 0)
-                       return err;
-               err = regmap_raw_read(data->regmap, ADM9240_REG_IN_MAX(i),
-                                     &data->in_max[i], 1);
-               if (err < 0)
-                       return err;
-       }
-       err = regmap_bulk_read(data->regmap, ADM9240_REG_FAN_MIN(0),
-                                     &data->fan_min[0], 2);
-       if (err < 0)
-               return err;
-       err = regmap_bulk_read(data->regmap, ADM9240_REG_TEMP_MAX(0),
-                                     &data->temp_max[0], 2);
-       if (err < 0)
-               return err;
-
-       /* read fan divs and 5-bit VID */
-       err = regmap_read(data->regmap, ADM9240_REG_VID_FAN_DIV, &val);
-       if (err < 0)
-               return err;
-       data->fan_div[0] = (val >> 4) & 3;
-       data->fan_div[1] = (val >> 6) & 3;
-       data->vid = val & 0x0f;
-       err = regmap_read(data->regmap, ADM9240_REG_VID4, &val);
-       if (err < 0)
-               return err;
-       data->vid |= (val & 1) << 4;
-       /* read analog out */
-       err = regmap_raw_read(data->regmap, ADM9240_REG_ANALOG_OUT,
-                             &data->aout, 1);
-
-       return err;
-}
-
-static struct adm9240_data *adm9240_update_device(struct device *dev)
-{
-       struct adm9240_data *data = dev_get_drvdata(dev);
-       int err;
-
-       mutex_lock(&data->update_lock);
-
-       /* minimum measurement cycle: 1.75 seconds */
-       if (time_after(jiffies, data->last_updated_measure + (HZ * 7 / 4))
-                       || !data->valid) {
-               err = adm9240_update_measure(data);
-               if (err < 0) {
-                       data->valid = 0;
-                       mutex_unlock(&data->update_lock);
-                       return ERR_PTR(err);
-               }
-               data->last_updated_measure = jiffies;
-       }
-
-       /* minimum config reading cycle: 300 seconds */
-       if (time_after(jiffies, data->last_updated_config + (HZ * 300))
-                       || !data->valid) {
-               err = adm9240_update_config(data);
-               if (err < 0) {
-                       data->valid = 0;
-                       mutex_unlock(&data->update_lock);
-                       return ERR_PTR(err);
-               }
-               data->last_updated_config = jiffies;
-               data->valid = 1;
-       }
-       mutex_unlock(&data->update_lock);
-       return data;
-}
-
-/*** sysfs accessors ***/
-
-/* temperature */
-static ssize_t temp1_input_show(struct device *dev,
-                               struct device_attribute *dummy, char *buf)
-{
-       struct adm9240_data *data = adm9240_update_device(dev);
-
-       if (IS_ERR(data))
-               return PTR_ERR(data);
-
-       return sprintf(buf, "%d\n", data->temp / 128 * 500); /* 9-bit value */
-}
-
-static ssize_t max_show(struct device *dev, struct device_attribute *devattr,
-                       char *buf)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct adm9240_data *data = adm9240_update_device(dev);
-
-       if (IS_ERR(data))
-               return PTR_ERR(data);
-
-       return sprintf(buf, "%d\n", data->temp_max[attr->index] * 1000);
-}
-
-static ssize_t max_store(struct device *dev, struct device_attribute *devattr,
-                        const char *buf, size_t count)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct adm9240_data *data = dev_get_drvdata(dev);
-       long val;
-       int err;
-
-       err = kstrtol(buf, 10, &val);
-       if (err)
-               return err;
-
-       mutex_lock(&data->update_lock);
-       data->temp_max[attr->index] = TEMP_TO_REG(val);
-       err = regmap_write(data->regmap, ADM9240_REG_TEMP_MAX(attr->index),
-                          data->temp_max[attr->index]);
-       mutex_unlock(&data->update_lock);
-       return err < 0 ? err : count;
-}
-
-static DEVICE_ATTR_RO(temp1_input);
-static SENSOR_DEVICE_ATTR_RW(temp1_max, max, 0);
-static SENSOR_DEVICE_ATTR_RW(temp1_max_hyst, max, 1);
-
-/* voltage */
-static ssize_t in_show(struct device *dev, struct device_attribute *devattr,
-                      char *buf)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct adm9240_data *data = adm9240_update_device(dev);
-
-       if (IS_ERR(data))
-               return PTR_ERR(data);
-
-       return sprintf(buf, "%d\n", IN_FROM_REG(data->in[attr->index],
-                               attr->index));
-}
-
-static ssize_t in_min_show(struct device *dev,
-                          struct device_attribute *devattr, char *buf)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct adm9240_data *data = adm9240_update_device(dev);
-
-       if (IS_ERR(data))
-               return PTR_ERR(data);
-
-       return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[attr->index],
-                               attr->index));
-}
-
-static ssize_t in_max_show(struct device *dev,
-                          struct device_attribute *devattr, char *buf)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct adm9240_data *data = adm9240_update_device(dev);
-
-       if (IS_ERR(data))
-               return PTR_ERR(data);
-
-       return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[attr->index],
-                               attr->index));
-}
-
-static ssize_t in_min_store(struct device *dev,
-                           struct device_attribute *devattr, const char *buf,
-                           size_t count)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct adm9240_data *data = dev_get_drvdata(dev);
-       unsigned long val;
-       int err;
-
-       err = kstrtoul(buf, 10, &val);
-       if (err)
-               return err;
-
-       mutex_lock(&data->update_lock);
-       data->in_min[attr->index] = IN_TO_REG(val, attr->index);
-       err = regmap_write(data->regmap, ADM9240_REG_IN_MIN(attr->index),
-                          data->in_min[attr->index]);
-       mutex_unlock(&data->update_lock);
-       return err < 0 ? err : count;
-}
-
-static ssize_t in_max_store(struct device *dev,
-                           struct device_attribute *devattr, const char *buf,
-                           size_t count)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct adm9240_data *data = dev_get_drvdata(dev);
-       unsigned long val;
-       int err;
-
-       err = kstrtoul(buf, 10, &val);
-       if (err)
-               return err;
-
-       mutex_lock(&data->update_lock);
-       data->in_max[attr->index] = IN_TO_REG(val, attr->index);
-       err = regmap_write(data->regmap, ADM9240_REG_IN_MAX(attr->index),
-                          data->in_max[attr->index]);
-       mutex_unlock(&data->update_lock);
-       return err < 0 ? err : count;
-}
-
-static SENSOR_DEVICE_ATTR_RO(in0_input, in, 0);
-static SENSOR_DEVICE_ATTR_RW(in0_min, in_min, 0);
-static SENSOR_DEVICE_ATTR_RW(in0_max, in_max, 0);
-static SENSOR_DEVICE_ATTR_RO(in1_input, in, 1);
-static SENSOR_DEVICE_ATTR_RW(in1_min, in_min, 1);
-static SENSOR_DEVICE_ATTR_RW(in1_max, in_max, 1);
-static SENSOR_DEVICE_ATTR_RO(in2_input, in, 2);
-static SENSOR_DEVICE_ATTR_RW(in2_min, in_min, 2);
-static SENSOR_DEVICE_ATTR_RW(in2_max, in_max, 2);
-static SENSOR_DEVICE_ATTR_RO(in3_input, in, 3);
-static SENSOR_DEVICE_ATTR_RW(in3_min, in_min, 3);
-static SENSOR_DEVICE_ATTR_RW(in3_max, in_max, 3);
-static SENSOR_DEVICE_ATTR_RO(in4_input, in, 4);
-static SENSOR_DEVICE_ATTR_RW(in4_min, in_min, 4);
-static SENSOR_DEVICE_ATTR_RW(in4_max, in_max, 4);
-static SENSOR_DEVICE_ATTR_RO(in5_input, in, 5);
-static SENSOR_DEVICE_ATTR_RW(in5_min, in_min, 5);
-static SENSOR_DEVICE_ATTR_RW(in5_max, in_max, 5);
-
-/* fans */
-static ssize_t fan_show(struct device *dev, struct device_attribute *devattr,
-                       char *buf)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct adm9240_data *data = adm9240_update_device(dev);
-
-       if (IS_ERR(data))
-               return PTR_ERR(data);
-
-       return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[attr->index],
-                               1 << data->fan_div[attr->index]));
-}
-
-static ssize_t fan_min_show(struct device *dev,
-                           struct device_attribute *devattr, char *buf)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct adm9240_data *data = adm9240_update_device(dev);
-
-       if (IS_ERR(data))
-               return PTR_ERR(data);
-
-       return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[attr->index],
-                               1 << data->fan_div[attr->index]));
-}
-
-static ssize_t fan_div_show(struct device *dev,
-                           struct device_attribute *devattr, char *buf)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct adm9240_data *data = adm9240_update_device(dev);
-
-       if (IS_ERR(data))
-               return PTR_ERR(data);
-
-       return sprintf(buf, "%d\n", 1 << data->fan_div[attr->index]);
-}
-
 /*
  * set fan speed low limit:
  *
@@ -501,38 +164,25 @@ static ssize_t fan_div_show(struct device *dev,
  * - otherwise: select fan clock divider to suit fan speed low limit,
  *   measurement code may adjust registers to ensure fan speed reading
  */
-static ssize_t fan_min_store(struct device *dev,
-                            struct device_attribute *devattr,
-                            const char *buf, size_t count)
+static int adm9240_fan_min_write(struct adm9240_data *data, int channel, long val)
 {
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct adm9240_data *data = dev_get_drvdata(dev);
-       struct i2c_client *client = data->client;
-       int nr = attr->index;
        u8 new_div;
-       unsigned long val;
+       u8 fan_min;
        int err;
 
-       err = kstrtoul(buf, 10, &val);
-       if (err)
-               return err;
-
        mutex_lock(&data->update_lock);
 
        if (!val) {
-               data->fan_min[nr] = 255;
-               new_div = data->fan_div[nr];
-
-               dev_dbg(&client->dev, "fan%u low limit set disabled\n",
-                               nr + 1);
+               fan_min = 255;
+               new_div = data->fan_div[channel];
 
+               dev_dbg(data->dev, "fan%u low limit set disabled\n", channel + 1);
        } else if (val < 1350000 / (8 * 254)) {
                new_div = 3;
-               data->fan_min[nr] = 254;
-
-               dev_dbg(&client->dev, "fan%u low limit set minimum %u\n",
-                               nr + 1, FAN_FROM_REG(254, 1 << new_div));
+               fan_min = 254;
 
+               dev_dbg(data->dev, "fan%u low limit set minimum %u\n",
+                       channel + 1, FAN_FROM_REG(254, BIT(new_div)));
        } else {
                unsigned int new_min = 1350000 / val;
 
@@ -544,87 +194,55 @@ static ssize_t fan_min_store(struct device *dev,
                if (!new_min) /* keep > 0 */
                        new_min++;
 
-               data->fan_min[nr] = new_min;
+               fan_min = new_min;
 
-               dev_dbg(&client->dev, "fan%u low limit set fan speed %u\n",
-                               nr + 1, FAN_FROM_REG(new_min, 1 << new_div));
+               dev_dbg(data->dev, "fan%u low limit set fan speed %u\n",
+                       channel + 1, FAN_FROM_REG(new_min, BIT(new_div)));
        }
 
-       if (new_div != data->fan_div[nr]) {
-               data->fan_div[nr] = new_div;
-               adm9240_write_fan_div(data, nr, new_div);
+       if (new_div != data->fan_div[channel]) {
+               data->fan_div[channel] = new_div;
+               adm9240_write_fan_div(data, channel, new_div);
        }
-       err = regmap_write(data->regmap, ADM9240_REG_FAN_MIN(nr),
-                          data->fan_min[nr]);
+       err = regmap_write(data->regmap, ADM9240_REG_FAN_MIN(channel), fan_min);
 
        mutex_unlock(&data->update_lock);
-       return err < 0 ? err : count;
-}
-
-static SENSOR_DEVICE_ATTR_RO(fan1_input, fan, 0);
-static SENSOR_DEVICE_ATTR_RW(fan1_min, fan_min, 0);
-static SENSOR_DEVICE_ATTR_RO(fan1_div, fan_div, 0);
-static SENSOR_DEVICE_ATTR_RO(fan2_input, fan, 1);
-static SENSOR_DEVICE_ATTR_RW(fan2_min, fan_min, 1);
-static SENSOR_DEVICE_ATTR_RO(fan2_div, fan_div, 1);
 
-/* alarms */
-static ssize_t alarms_show(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct adm9240_data *data = adm9240_update_device(dev);
-
-       if (IS_ERR(data))
-               return PTR_ERR(data);
-
-       return sprintf(buf, "%u\n", data->alarms);
+       return err;
 }
-static DEVICE_ATTR_RO(alarms);
 
-static ssize_t alarm_show(struct device *dev, struct device_attribute *attr,
-                         char *buf)
-{
-       int bitnr = to_sensor_dev_attr(attr)->index;
-       struct adm9240_data *data = adm9240_update_device(dev);
-
-       if (IS_ERR(data))
-               return PTR_ERR(data);
-
-       return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1);
-}
-static SENSOR_DEVICE_ATTR_RO(in0_alarm, alarm, 0);
-static SENSOR_DEVICE_ATTR_RO(in1_alarm, alarm, 1);
-static SENSOR_DEVICE_ATTR_RO(in2_alarm, alarm, 2);
-static SENSOR_DEVICE_ATTR_RO(in3_alarm, alarm, 3);
-static SENSOR_DEVICE_ATTR_RO(in4_alarm, alarm, 8);
-static SENSOR_DEVICE_ATTR_RO(in5_alarm, alarm, 9);
-static SENSOR_DEVICE_ATTR_RO(temp1_alarm, alarm, 4);
-static SENSOR_DEVICE_ATTR_RO(fan1_alarm, alarm, 6);
-static SENSOR_DEVICE_ATTR_RO(fan2_alarm, alarm, 7);
-
-/* vid */
 static ssize_t cpu0_vid_show(struct device *dev,
                             struct device_attribute *attr, char *buf)
 {
-       struct adm9240_data *data = adm9240_update_device(dev);
-
-       if (IS_ERR(data))
-               return PTR_ERR(data);
+       struct adm9240_data *data = dev_get_drvdata(dev);
+       unsigned int regval;
+       int err;
+       u8 vid;
 
-       return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
+       err = regmap_read(data->regmap, ADM9240_REG_VID_FAN_DIV, &regval);
+       if (err < 0)
+               return err;
+       vid = regval & 0x0f;
+       err = regmap_read(data->regmap, ADM9240_REG_VID4, &regval);
+       if (err < 0)
+               return err;
+       vid |= (regval & 1) << 4;
+       return sprintf(buf, "%d\n", vid_from_reg(vid, data->vrm));
 }
 static DEVICE_ATTR_RO(cpu0_vid);
 
-/* analog output */
 static ssize_t aout_output_show(struct device *dev,
                                struct device_attribute *attr, char *buf)
 {
-       struct adm9240_data *data = adm9240_update_device(dev);
+       struct adm9240_data *data = dev_get_drvdata(dev);
+       unsigned int regval;
+       int err;
 
-       if (IS_ERR(data))
-               return PTR_ERR(data);
+       err = regmap_read(data->regmap, ADM9240_REG_ANALOG_OUT, &regval);
+       if (err)
+               return err;
 
-       return sprintf(buf, "%d\n", AOUT_FROM_REG(data->aout));
+       return sprintf(buf, "%d\n", AOUT_FROM_REG(regval));
 }
 
 static ssize_t aout_output_store(struct device *dev,
@@ -639,76 +257,13 @@ static ssize_t aout_output_store(struct device *dev,
        if (err)
                return err;
 
-       mutex_lock(&data->update_lock);
-       data->aout = AOUT_TO_REG(val);
-       err = regmap_write(data->regmap, ADM9240_REG_ANALOG_OUT, data->aout);
-       mutex_unlock(&data->update_lock);
+       err = regmap_write(data->regmap, ADM9240_REG_ANALOG_OUT, AOUT_TO_REG(val));
        return err < 0 ? err : count;
 }
 static DEVICE_ATTR_RW(aout_output);
 
-static ssize_t alarm_store(struct device *dev, struct device_attribute *attr,
-                          const char *buf, size_t count)
-{
-       struct adm9240_data *data = dev_get_drvdata(dev);
-       unsigned long val;
-       int err;
-
-       if (kstrtoul(buf, 10, &val) || val != 0)
-               return -EINVAL;
-
-       mutex_lock(&data->update_lock);
-       err = regmap_write(data->regmap, ADM9240_REG_CHASSIS_CLEAR, 0x80);
-       data->valid = 0;                /* Force cache refresh */
-       mutex_unlock(&data->update_lock);
-       if (err < 0)
-               return err;
-       dev_dbg(&data->client->dev, "chassis intrusion latch cleared\n");
-
-       return count;
-}
-static SENSOR_DEVICE_ATTR_RW(intrusion0_alarm, alarm, 12);
-
 static struct attribute *adm9240_attrs[] = {
-       &sensor_dev_attr_in0_input.dev_attr.attr,
-       &sensor_dev_attr_in0_min.dev_attr.attr,
-       &sensor_dev_attr_in0_max.dev_attr.attr,
-       &sensor_dev_attr_in0_alarm.dev_attr.attr,
-       &sensor_dev_attr_in1_input.dev_attr.attr,
-       &sensor_dev_attr_in1_min.dev_attr.attr,
-       &sensor_dev_attr_in1_max.dev_attr.attr,
-       &sensor_dev_attr_in1_alarm.dev_attr.attr,
-       &sensor_dev_attr_in2_input.dev_attr.attr,
-       &sensor_dev_attr_in2_min.dev_attr.attr,
-       &sensor_dev_attr_in2_max.dev_attr.attr,
-       &sensor_dev_attr_in2_alarm.dev_attr.attr,
-       &sensor_dev_attr_in3_input.dev_attr.attr,
-       &sensor_dev_attr_in3_min.dev_attr.attr,
-       &sensor_dev_attr_in3_max.dev_attr.attr,
-       &sensor_dev_attr_in3_alarm.dev_attr.attr,
-       &sensor_dev_attr_in4_input.dev_attr.attr,
-       &sensor_dev_attr_in4_min.dev_attr.attr,
-       &sensor_dev_attr_in4_max.dev_attr.attr,
-       &sensor_dev_attr_in4_alarm.dev_attr.attr,
-       &sensor_dev_attr_in5_input.dev_attr.attr,
-       &sensor_dev_attr_in5_min.dev_attr.attr,
-       &sensor_dev_attr_in5_max.dev_attr.attr,
-       &sensor_dev_attr_in5_alarm.dev_attr.attr,
-       &dev_attr_temp1_input.attr,
-       &sensor_dev_attr_temp1_max.dev_attr.attr,
-       &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
-       &sensor_dev_attr_temp1_alarm.dev_attr.attr,
-       &sensor_dev_attr_fan1_input.dev_attr.attr,
-       &sensor_dev_attr_fan1_div.dev_attr.attr,
-       &sensor_dev_attr_fan1_min.dev_attr.attr,
-       &sensor_dev_attr_fan1_alarm.dev_attr.attr,
-       &sensor_dev_attr_fan2_input.dev_attr.attr,
-       &sensor_dev_attr_fan2_div.dev_attr.attr,
-       &sensor_dev_attr_fan2_min.dev_attr.attr,
-       &sensor_dev_attr_fan2_alarm.dev_attr.attr,
-       &dev_attr_alarms.attr,
        &dev_attr_aout_output.attr,
-       &sensor_dev_attr_intrusion0_alarm.dev_attr.attr,
        &dev_attr_cpu0_vid.attr,
        NULL
 };
@@ -730,26 +285,19 @@ static int adm9240_detect(struct i2c_client *new_client,
                return -ENODEV;
 
        /* verify chip: reg address should match i2c address */
-       if (i2c_smbus_read_byte_data(new_client, ADM9240_REG_I2C_ADDR)
-                       != address) {
-               dev_err(&adapter->dev, "detect fail: address match, 0x%02x\n",
-                       address);
+       if (i2c_smbus_read_byte_data(new_client, ADM9240_REG_I2C_ADDR) != address)
                return -ENODEV;
-       }
 
        /* check known chip manufacturer */
        man_id = i2c_smbus_read_byte_data(new_client, ADM9240_REG_MAN_ID);
-       if (man_id == 0x23) {
+       if (man_id == 0x23)
                name = "adm9240";
-       } else if (man_id == 0xda) {
+       else if (man_id == 0xda)
                name = "ds1780";
-       } else if (man_id == 0x01) {
+       else if (man_id == 0x01)
                name = "lm81";
-       } else {
-               dev_err(&adapter->dev, "detect fail: unknown manuf, 0x%02x\n",
-                       man_id);
+       else
                return -ENODEV;
-       }
 
        /* successful detect, print chip info */
        die_rev = i2c_smbus_read_byte_data(new_client, ADM9240_REG_DIE_REV);
@@ -757,13 +305,14 @@ static int adm9240_detect(struct i2c_client *new_client,
                 man_id == 0x23 ? "ADM9240" :
                 man_id == 0xda ? "DS1780" : "LM81", die_rev);
 
-       strlcpy(info->type, name, I2C_NAME_SIZE);
+       strscpy(info->type, name, I2C_NAME_SIZE);
 
        return 0;
 }
 
-static int adm9240_init_client(struct i2c_client *client, struct adm9240_data *data)
+static int adm9240_init_client(struct adm9240_data *data)
 {
+       unsigned int regval;
        u8 conf, mode;
        int err;
 
@@ -777,13 +326,13 @@ static int adm9240_init_client(struct i2c_client *client, struct adm9240_data *d
 
        data->vrm = vid_which_vrm(); /* need this to report vid as mV */
 
-       dev_info(&client->dev, "Using VRM: %d.%d\n", data->vrm / 10,
-                       data->vrm % 10);
+       dev_info(data->dev, "Using VRM: %d.%d\n", data->vrm / 10,
+                data->vrm % 10);
 
        if (conf & 1) { /* measurement cycle running: report state */
 
-               dev_info(&client->dev, "status: config 0x%02x mode %u\n",
-                               conf, mode);
+               dev_info(data->dev, "status: config 0x%02x mode %u\n",
+                        conf, mode);
 
        } else { /* cold start: open limits before starting chip */
                int i;
@@ -800,13 +349,13 @@ static int adm9240_init_client(struct i2c_client *client, struct adm9240_data *d
                }
                for (i = 0; i < 2; i++) {
                        err = regmap_write(data->regmap,
-                                       ADM9240_REG_FAN_MIN(i), 255);
+                                          ADM9240_REG_FAN_MIN(i), 255);
                        if (err < 0)
                                return err;
                }
                for (i = 0; i < 2; i++) {
                        err = regmap_write(data->regmap,
-                                       ADM9240_REG_TEMP_MAX(i), 127);
+                                          ADM9240_REG_TEMP_MAX(i), 127);
                        if (err < 0)
                                return err;
                }
@@ -816,23 +365,417 @@ static int adm9240_init_client(struct i2c_client *client, struct adm9240_data *d
                if (err < 0)
                        return err;
 
-               dev_info(&client->dev,
+               dev_info(data->dev,
                         "cold start: config was 0x%02x mode %u\n", conf, mode);
        }
 
+       /* read fan divs */
+       err = regmap_read(data->regmap, ADM9240_REG_VID_FAN_DIV, &regval);
+       if (err < 0)
+               return err;
+       data->fan_div[0] = (regval >> 4) & 3;
+       data->fan_div[1] = (regval >> 6) & 3;
        return 0;
 }
 
+static int adm9240_chip_read(struct device *dev, u32 attr, long *val)
+{
+       struct adm9240_data *data = dev_get_drvdata(dev);
+       u8 regs[2];
+       int err;
+
+       switch (attr) {
+       case hwmon_chip_alarms:
+               err = regmap_bulk_read(data->regmap, ADM9240_REG_INT(0), &regs, 2);
+               if (err < 0)
+                       return err;
+               *val = regs[0] | regs[1] << 8;
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+       return 0;
+}
+
+static int adm9240_intrusion_read(struct device *dev, u32 attr, long *val)
+{
+       struct adm9240_data *data = dev_get_drvdata(dev);
+       unsigned int regval;
+       int err;
+
+       switch (attr) {
+       case hwmon_intrusion_alarm:
+               err = regmap_read(data->regmap, ADM9240_REG_INT(1), &regval);
+               if (err < 0)
+                       return err;
+               *val = !!(regval & BIT(4));
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+       return 0;
+}
+
+static int adm9240_intrusion_write(struct device *dev, u32 attr, long val)
+{
+       struct adm9240_data *data = dev_get_drvdata(dev);
+       int err;
+
+       switch (attr) {
+       case hwmon_intrusion_alarm:
+               if (val)
+                       return -EINVAL;
+               err = regmap_write(data->regmap, ADM9240_REG_CHASSIS_CLEAR, 0x80);
+               if (err < 0)
+                       return err;
+               dev_dbg(data->dev, "chassis intrusion latch cleared\n");
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+       return 0;
+}
+
+static int adm9240_in_read(struct device *dev, u32 attr, int channel, long *val)
+{
+       struct adm9240_data *data = dev_get_drvdata(dev);
+       unsigned int regval;
+       int reg;
+       int err;
+
+       switch (attr) {
+       case hwmon_in_input:
+               reg = ADM9240_REG_IN(channel);
+               break;
+       case hwmon_in_min:
+               reg = ADM9240_REG_IN_MIN(channel);
+               break;
+       case hwmon_in_max:
+               reg = ADM9240_REG_IN_MAX(channel);
+               break;
+       case hwmon_in_alarm:
+               if (channel < 4) {
+                       reg = ADM9240_REG_INT(0);
+               } else {
+                       reg = ADM9240_REG_INT(1);
+                       channel -= 4;
+               }
+               err = regmap_read(data->regmap, reg, &regval);
+               if (err < 0)
+                       return err;
+               *val = !!(regval & BIT(channel));
+               return 0;
+       default:
+               return -EOPNOTSUPP;
+       }
+       err = regmap_read(data->regmap, reg, &regval);
+       if (err < 0)
+               return err;
+       *val = IN_FROM_REG(regval, channel);
+       return 0;
+}
+
+static int adm9240_in_write(struct device *dev, u32 attr, int channel, long val)
+{
+       struct adm9240_data *data = dev_get_drvdata(dev);
+       int reg;
+
+       switch (attr) {
+       case hwmon_in_min:
+               reg = ADM9240_REG_IN_MIN(channel);
+               break;
+       case hwmon_in_max:
+               reg = ADM9240_REG_IN(channel);
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+       return regmap_write(data->regmap, reg, IN_TO_REG(val, channel));
+}
+
+static int adm9240_fan_read(struct device *dev, u32 attr, int channel, long *val)
+{
+       struct adm9240_data *data = dev_get_drvdata(dev);
+       unsigned int regval;
+       int err;
+
+       switch (attr) {
+       case hwmon_fan_input:
+               err = regmap_read(data->regmap, ADM9240_REG_FAN(channel), &regval);
+               if (err < 0)
+                       return err;
+               if (regval == 255 && data->fan_div[channel] < 3) {
+                       /* adjust fan clock divider on overflow */
+                       err = adm9240_write_fan_div(data, channel,
+                                                   ++data->fan_div[channel]);
+                       if (err)
+                               return err;
+               }
+               *val = FAN_FROM_REG(regval, BIT(data->fan_div[channel]));
+               break;
+       case hwmon_fan_div:
+               *val = BIT(data->fan_div[channel]);
+               break;
+       case hwmon_fan_min:
+               err = regmap_read(data->regmap, ADM9240_REG_FAN_MIN(channel), &regval);
+               if (err < 0)
+                       return err;
+               *val = FAN_FROM_REG(regval, BIT(data->fan_div[channel]));
+               break;
+       case hwmon_fan_alarm:
+               err = regmap_read(data->regmap, ADM9240_REG_INT(0), &regval);
+               if (err < 0)
+                       return err;
+               *val = !!(regval & BIT(channel + 6));
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+       return 0;
+}
+
+static int adm9240_fan_write(struct device *dev, u32 attr, int channel, long val)
+{
+       struct adm9240_data *data = dev_get_drvdata(dev);
+       int err;
+
+       switch (attr) {
+       case hwmon_fan_min:
+               err = adm9240_fan_min_write(data, channel, val);
+               if (err < 0)
+                       return err;
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+       return 0;
+}
+
+static int adm9240_temp_read(struct device *dev, u32 attr, int channel, long *val)
+{
+       struct adm9240_data *data = dev_get_drvdata(dev);
+       unsigned int regval;
+       int err, temp;
+
+       switch (attr) {
+       case hwmon_temp_input:
+               err = regmap_read(data->regmap, ADM9240_REG_TEMP, &regval);
+               if (err < 0)
+                       return err;
+               temp = regval << 1;
+               err = regmap_read(data->regmap, ADM9240_REG_TEMP_CONF, &regval);
+               if (err < 0)
+                       return err;
+               temp |= regval >> 7;
+               *val = sign_extend32(temp, 8) * 500;
+               break;
+       case hwmon_temp_max:
+               err = regmap_read(data->regmap, ADM9240_REG_TEMP_MAX(0), &regval);
+               if (err < 0)
+                       return err;
+               *val = (s8)regval * 1000;
+               break;
+       case hwmon_temp_max_hyst:
+               err = regmap_read(data->regmap, ADM9240_REG_TEMP_MAX(1), &regval);
+               if (err < 0)
+                       return err;
+               *val = (s8)regval * 1000;
+               break;
+       case hwmon_temp_alarm:
+               err = regmap_read(data->regmap, ADM9240_REG_INT(0), &regval);
+               if (err < 0)
+                       return err;
+               *val = !!(regval & BIT(4));
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+       return 0;
+}
+
+static int adm9240_temp_write(struct device *dev, u32 attr, int channel, long val)
+{
+       struct adm9240_data *data = dev_get_drvdata(dev);
+       int reg;
+
+       switch (attr) {
+       case hwmon_temp_max:
+               reg = ADM9240_REG_TEMP_MAX(0);
+               break;
+       case hwmon_temp_max_hyst:
+               reg = ADM9240_REG_TEMP_MAX(1);
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+       return regmap_write(data->regmap, reg, TEMP_TO_REG(val));
+}
+
+static int adm9240_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
+                       int channel, long *val)
+{
+       switch (type) {
+       case hwmon_chip:
+               return adm9240_chip_read(dev, attr, val);
+       case hwmon_intrusion:
+               return adm9240_intrusion_read(dev, attr, val);
+       case hwmon_in:
+               return adm9240_in_read(dev, attr, channel, val);
+       case hwmon_fan:
+               return adm9240_fan_read(dev, attr, channel, val);
+       case hwmon_temp:
+               return adm9240_temp_read(dev, attr, channel, val);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static int adm9240_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
+                        int channel, long val)
+{
+       switch (type) {
+       case hwmon_intrusion:
+               return adm9240_intrusion_write(dev, attr, val);
+       case hwmon_in:
+               return adm9240_in_write(dev, attr, channel, val);
+       case hwmon_fan:
+               return adm9240_fan_write(dev, attr, channel, val);
+       case hwmon_temp:
+               return adm9240_temp_write(dev, attr, channel, val);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static umode_t adm9240_is_visible(const void *_data, enum hwmon_sensor_types type,
+                                 u32 attr, int channel)
+{
+       umode_t mode = 0;
+
+       switch (type) {
+       case hwmon_chip:
+               switch (attr) {
+               case hwmon_chip_alarms:
+                       mode = 0444;
+                       break;
+               default:
+                       break;
+               }
+               break;
+       case hwmon_intrusion:
+               switch (attr) {
+               case hwmon_intrusion_alarm:
+                       mode = 0644;
+                       break;
+               default:
+                       break;
+               }
+               break;
+       case hwmon_temp:
+               switch (attr) {
+               case hwmon_temp:
+               case hwmon_temp_alarm:
+                       mode = 0444;
+                       break;
+               case hwmon_temp_max:
+               case hwmon_temp_max_hyst:
+                       mode = 0644;
+                       break;
+               default:
+                       break;
+               }
+               break;
+       case hwmon_fan:
+               switch (attr) {
+               case hwmon_fan_input:
+               case hwmon_fan_div:
+               case hwmon_fan_alarm:
+                       mode = 0444;
+                       break;
+               case hwmon_fan_min:
+                       mode = 0644;
+                       break;
+               default:
+                       break;
+               }
+               break;
+       case hwmon_in:
+               switch (attr) {
+               case hwmon_in_input:
+               case hwmon_in_alarm:
+                       mode = 0444;
+                       break;
+               case hwmon_in_min:
+               case hwmon_in_max:
+                       mode = 0644;
+                       break;
+               default:
+                       break;
+               }
+               break;
+       default:
+               break;
+       }
+       return mode;
+}
+
+static const struct hwmon_ops adm9240_hwmon_ops = {
+       .is_visible = adm9240_is_visible,
+       .read = adm9240_read,
+       .write = adm9240_write,
+};
+
+static const struct hwmon_channel_info *adm9240_info[] = {
+       HWMON_CHANNEL_INFO(chip, HWMON_C_ALARMS),
+       HWMON_CHANNEL_INFO(intrusion, HWMON_INTRUSION_ALARM),
+       HWMON_CHANNEL_INFO(temp,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST | HWMON_T_ALARM),
+       HWMON_CHANNEL_INFO(in,
+                          HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_ALARM,
+                          HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_ALARM,
+                          HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_ALARM,
+                          HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_ALARM,
+                          HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_ALARM,
+                          HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_ALARM),
+       HWMON_CHANNEL_INFO(fan,
+                          HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_DIV | HWMON_F_ALARM,
+                          HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_DIV | HWMON_F_ALARM),
+       NULL
+};
+
+static const struct hwmon_chip_info adm9240_chip_info = {
+       .ops = &adm9240_hwmon_ops,
+       .info = adm9240_info,
+};
+
+static bool adm9240_volatile_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case ADM9240_REG_IN(0) ... ADM9240_REG_IN(5):
+       case ADM9240_REG_FAN(0) ... ADM9240_REG_FAN(1):
+       case ADM9240_REG_INT(0) ... ADM9240_REG_INT(1):
+       case ADM9240_REG_TEMP:
+       case ADM9240_REG_TEMP_CONF:
+       case ADM9240_REG_VID_FAN_DIV:
+       case ADM9240_REG_VID4:
+       case ADM9240_REG_ANALOG_OUT:
+               return true;
+       default:
+               return false;
+       }
+}
+
 static const struct regmap_config adm9240_regmap_config = {
        .reg_bits = 8,
        .val_bits = 8,
        .use_single_read = true,
        .use_single_write = true,
+       .volatile_reg = adm9240_volatile_reg,
 };
 
-static int adm9240_probe(struct i2c_client *new_client)
+static int adm9240_probe(struct i2c_client *client)
 {
-       struct device *dev = &new_client->dev;
+       struct device *dev = &client->dev;
        struct device *hwmon_dev;
        struct adm9240_data *data;
        int err;
@@ -841,20 +784,19 @@ static int adm9240_probe(struct i2c_client *new_client)
        if (!data)
                return -ENOMEM;
 
-       data->client = new_client;
+       data->dev = dev;
        mutex_init(&data->update_lock);
-       data->regmap = devm_regmap_init_i2c(new_client, &adm9240_regmap_config);
+       data->regmap = devm_regmap_init_i2c(client, &adm9240_regmap_config);
        if (IS_ERR(data->regmap))
                return PTR_ERR(data->regmap);
 
-       err = adm9240_init_client(new_client, data);
+       err = adm9240_init_client(data);
        if (err < 0)
                return err;
 
-       hwmon_dev = devm_hwmon_device_register_with_groups(dev,
-                                                          new_client->name,
-                                                          data,
-                                                          adm9240_groups);
+       hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data,
+                                                        &adm9240_chip_info,
+                                                        adm9240_groups);
        return PTR_ERR_OR_ZERO(hwmon_dev);
 }
 
diff --git a/drivers/hwmon/amd_energy.c b/drivers/hwmon/amd_energy.c
deleted file mode 100644 (file)
index a86cc8d..0000000
+++ /dev/null
@@ -1,379 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-
-/*
- * Copyright (C) 2020 Advanced Micro Devices, Inc.
- */
-#include <asm/cpu_device_id.h>
-
-#include <linux/bits.h>
-#include <linux/cpu.h>
-#include <linux/cpumask.h>
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/hwmon.h>
-#include <linux/kernel.h>
-#include <linux/kthread.h>
-#include <linux/list.h>
-#include <linux/module.h>
-#include <linux/mutex.h>
-#include <linux/processor.h>
-#include <linux/platform_device.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/topology.h>
-#include <linux/types.h>
-
-#define DRVNAME                        "amd_energy"
-
-#define ENERGY_PWR_UNIT_MSR    0xC0010299
-#define ENERGY_CORE_MSR                0xC001029A
-#define ENERGY_PKG_MSR         0xC001029B
-
-#define AMD_ENERGY_UNIT_MASK   0x01F00
-#define AMD_ENERGY_MASK                0xFFFFFFFF
-
-struct sensor_accumulator {
-       u64 energy_ctr;
-       u64 prev_value;
-};
-
-struct amd_energy_data {
-       struct hwmon_channel_info energy_info;
-       const struct hwmon_channel_info *info[2];
-       struct hwmon_chip_info chip;
-       struct task_struct *wrap_accumulate;
-       /* Lock around the accumulator */
-       struct mutex lock;
-       /* An accumulator for each core and socket */
-       struct sensor_accumulator *accums;
-       unsigned int timeout_ms;
-       /* Energy Status Units */
-       int energy_units;
-       int nr_cpus;
-       int nr_socks;
-       int core_id;
-       char (*label)[10];
-};
-
-static int amd_energy_read_labels(struct device *dev,
-                                 enum hwmon_sensor_types type,
-                                 u32 attr, int channel,
-                                 const char **str)
-{
-       struct amd_energy_data *data = dev_get_drvdata(dev);
-
-       *str = data->label[channel];
-       return 0;
-}
-
-static void get_energy_units(struct amd_energy_data *data)
-{
-       u64 rapl_units;
-
-       rdmsrl_safe(ENERGY_PWR_UNIT_MSR, &rapl_units);
-       data->energy_units = (rapl_units & AMD_ENERGY_UNIT_MASK) >> 8;
-}
-
-static void accumulate_delta(struct amd_energy_data *data,
-                            int channel, int cpu, u32 reg)
-{
-       struct sensor_accumulator *accum;
-       u64 input;
-
-       mutex_lock(&data->lock);
-       rdmsrl_safe_on_cpu(cpu, reg, &input);
-       input &= AMD_ENERGY_MASK;
-
-       accum = &data->accums[channel];
-       if (input >= accum->prev_value)
-               accum->energy_ctr +=
-                       input - accum->prev_value;
-       else
-               accum->energy_ctr += UINT_MAX -
-                       accum->prev_value + input;
-
-       accum->prev_value = input;
-       mutex_unlock(&data->lock);
-}
-
-static void read_accumulate(struct amd_energy_data *data)
-{
-       int sock, scpu, cpu;
-
-       for (sock = 0; sock < data->nr_socks; sock++) {
-               scpu = cpumask_first_and(cpu_online_mask,
-                                        cpumask_of_node(sock));
-
-               accumulate_delta(data, data->nr_cpus + sock,
-                                scpu, ENERGY_PKG_MSR);
-       }
-
-       if (data->core_id >= data->nr_cpus)
-               data->core_id = 0;
-
-       cpu = data->core_id;
-       if (cpu_online(cpu))
-               accumulate_delta(data, cpu, cpu, ENERGY_CORE_MSR);
-
-       data->core_id++;
-}
-
-static void amd_add_delta(struct amd_energy_data *data, int ch,
-                         int cpu, long *val, u32 reg)
-{
-       struct sensor_accumulator *accum;
-       u64 input;
-
-       mutex_lock(&data->lock);
-       rdmsrl_safe_on_cpu(cpu, reg, &input);
-       input &= AMD_ENERGY_MASK;
-
-       accum = &data->accums[ch];
-       if (input >= accum->prev_value)
-               input += accum->energy_ctr -
-                               accum->prev_value;
-       else
-               input += UINT_MAX - accum->prev_value +
-                               accum->energy_ctr;
-
-       /* Energy consumed = (1/(2^ESU) * RAW * 1000000UL) μJoules */
-       *val = div64_ul(input * 1000000UL, BIT(data->energy_units));
-
-       mutex_unlock(&data->lock);
-}
-
-static int amd_energy_read(struct device *dev,
-                          enum hwmon_sensor_types type,
-                          u32 attr, int channel, long *val)
-{
-       struct amd_energy_data *data = dev_get_drvdata(dev);
-       u32 reg;
-       int cpu;
-
-       if (channel >= data->nr_cpus) {
-               cpu = cpumask_first_and(cpu_online_mask,
-                                       cpumask_of_node
-                                       (channel - data->nr_cpus));
-               reg = ENERGY_PKG_MSR;
-       } else {
-               cpu = channel;
-               if (!cpu_online(cpu))
-                       return -ENODEV;
-
-               reg = ENERGY_CORE_MSR;
-       }
-       amd_add_delta(data, channel, cpu, val, reg);
-
-       return 0;
-}
-
-static umode_t amd_energy_is_visible(const void *_data,
-                                    enum hwmon_sensor_types type,
-                                    u32 attr, int channel)
-{
-       return 0440;
-}
-
-static int energy_accumulator(void *p)
-{
-       struct amd_energy_data *data = (struct amd_energy_data *)p;
-       unsigned int timeout = data->timeout_ms;
-
-       while (!kthread_should_stop()) {
-               /*
-                * Ignoring the conditions such as
-                * cpu being offline or rdmsr failure
-                */
-               read_accumulate(data);
-
-               set_current_state(TASK_INTERRUPTIBLE);
-               if (kthread_should_stop())
-                       break;
-
-               schedule_timeout(msecs_to_jiffies(timeout));
-       }
-       return 0;
-}
-
-static const struct hwmon_ops amd_energy_ops = {
-       .is_visible = amd_energy_is_visible,
-       .read = amd_energy_read,
-       .read_string = amd_energy_read_labels,
-};
-
-static int amd_create_sensor(struct device *dev,
-                            struct amd_energy_data *data,
-                            enum hwmon_sensor_types type, u32 config)
-{
-       struct hwmon_channel_info *info = &data->energy_info;
-       struct sensor_accumulator *accums;
-       int i, num_siblings, cpus, sockets;
-       u32 *s_config;
-       char (*label_l)[10];
-
-       /* Identify the number of siblings per core */
-       num_siblings = ((cpuid_ebx(0x8000001e) >> 8) & 0xff) + 1;
-
-       sockets = num_possible_nodes();
-
-       /*
-        * Energy counter register is accessed at core level.
-        * Hence, filterout the siblings.
-        */
-       cpus = num_present_cpus() / num_siblings;
-
-       s_config = devm_kcalloc(dev, cpus + sockets + 1,
-                               sizeof(u32), GFP_KERNEL);
-       if (!s_config)
-               return -ENOMEM;
-
-       accums = devm_kcalloc(dev, cpus + sockets,
-                             sizeof(struct sensor_accumulator),
-                             GFP_KERNEL);
-       if (!accums)
-               return -ENOMEM;
-
-       label_l = devm_kcalloc(dev, cpus + sockets,
-                              sizeof(*label_l), GFP_KERNEL);
-       if (!label_l)
-               return -ENOMEM;
-
-       info->type = type;
-       info->config = s_config;
-
-       data->nr_cpus = cpus;
-       data->nr_socks = sockets;
-       data->accums = accums;
-       data->label = label_l;
-
-       for (i = 0; i < cpus + sockets; i++) {
-               s_config[i] = config;
-               if (i < cpus)
-                       scnprintf(label_l[i], 10, "Ecore%03u", i);
-               else
-                       scnprintf(label_l[i], 10, "Esocket%u", (i - cpus));
-       }
-
-       s_config[i] = 0;
-       return 0;
-}
-
-static int amd_energy_probe(struct platform_device *pdev)
-{
-       struct device *hwmon_dev;
-       struct amd_energy_data *data;
-       struct device *dev = &pdev->dev;
-       int ret;
-
-       data = devm_kzalloc(dev,
-                           sizeof(struct amd_energy_data), GFP_KERNEL);
-       if (!data)
-               return -ENOMEM;
-
-       data->chip.ops = &amd_energy_ops;
-       data->chip.info = data->info;
-
-       dev_set_drvdata(dev, data);
-       /* Populate per-core energy reporting */
-       data->info[0] = &data->energy_info;
-       ret = amd_create_sensor(dev, data, hwmon_energy,
-                               HWMON_E_INPUT | HWMON_E_LABEL);
-       if (ret)
-               return ret;
-
-       mutex_init(&data->lock);
-       get_energy_units(data);
-
-       hwmon_dev = devm_hwmon_device_register_with_info(dev, DRVNAME,
-                                                        data,
-                                                        &data->chip,
-                                                        NULL);
-       if (IS_ERR(hwmon_dev))
-               return PTR_ERR(hwmon_dev);
-
-       /*
-        * On a system with peak wattage of 250W
-        * timeout = 2 ^ 32 / 2 ^ energy_units / 250 secs
-        */
-       data->timeout_ms = 1000 *
-                          BIT(min(28, 31 - data->energy_units)) / 250;
-
-       data->wrap_accumulate = kthread_run(energy_accumulator, data,
-                                           "%s", dev_name(hwmon_dev));
-       return PTR_ERR_OR_ZERO(data->wrap_accumulate);
-}
-
-static int amd_energy_remove(struct platform_device *pdev)
-{
-       struct amd_energy_data *data = dev_get_drvdata(&pdev->dev);
-
-       if (data && data->wrap_accumulate)
-               kthread_stop(data->wrap_accumulate);
-
-       return 0;
-}
-
-static const struct platform_device_id amd_energy_ids[] = {
-       { .name = DRVNAME, },
-       {}
-};
-MODULE_DEVICE_TABLE(platform, amd_energy_ids);
-
-static struct platform_driver amd_energy_driver = {
-       .probe = amd_energy_probe,
-       .remove = amd_energy_remove,
-       .id_table = amd_energy_ids,
-       .driver = {
-               .name = DRVNAME,
-       },
-};
-
-static struct platform_device *amd_energy_platdev;
-
-static const struct x86_cpu_id cpu_ids[] __initconst = {
-       X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x17, 0x31, NULL),
-       X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x19, 0x01, NULL),
-       X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x19, 0x30, NULL),
-       {}
-};
-MODULE_DEVICE_TABLE(x86cpu, cpu_ids);
-
-static int __init amd_energy_init(void)
-{
-       int ret;
-
-       if (!x86_match_cpu(cpu_ids))
-               return -ENODEV;
-
-       ret = platform_driver_register(&amd_energy_driver);
-       if (ret)
-               return ret;
-
-       amd_energy_platdev = platform_device_alloc(DRVNAME, 0);
-       if (!amd_energy_platdev) {
-               platform_driver_unregister(&amd_energy_driver);
-               return -ENOMEM;
-       }
-
-       ret = platform_device_add(amd_energy_platdev);
-       if (ret) {
-               platform_device_put(amd_energy_platdev);
-               platform_driver_unregister(&amd_energy_driver);
-               return ret;
-       }
-
-       return ret;
-}
-
-static void __exit amd_energy_exit(void)
-{
-       platform_device_unregister(amd_energy_platdev);
-       platform_driver_unregister(&amd_energy_driver);
-}
-
-module_init(amd_energy_init);
-module_exit(amd_energy_exit);
-
-MODULE_DESCRIPTION("Driver for AMD Energy reporting from RAPL MSR via HWMON interface");
-MODULE_AUTHOR("Naveen Krishna Chatradhi <nchatrad@amd.com>");
-MODULE_LICENSE("GPL");
index 28b137e..c317597 100644 (file)
@@ -741,7 +741,7 @@ static void applesmc_idev_poll(struct input_dev *idev)
 static ssize_t applesmc_name_show(struct device *dev,
                                   struct device_attribute *attr, char *buf)
 {
-       return snprintf(buf, PAGE_SIZE, "applesmc\n");
+       return sysfs_emit(buf, "applesmc\n");
 }
 
 static ssize_t applesmc_position_show(struct device *dev,
@@ -763,8 +763,8 @@ static ssize_t applesmc_position_show(struct device *dev,
 out:
        if (ret)
                return ret;
-       else
-               return snprintf(buf, PAGE_SIZE, "(%d,%d,%d)\n", x, y, z);
+
+       return sysfs_emit(buf, "(%d,%d,%d)\n", x, y, z);
 }
 
 static ssize_t applesmc_light_show(struct device *dev,
@@ -804,8 +804,8 @@ static ssize_t applesmc_light_show(struct device *dev,
 out:
        if (ret)
                return ret;
-       else
-               return snprintf(sysfsbuf, PAGE_SIZE, "(%d,%d)\n", left, right);
+
+       return sysfs_emit(sysfsbuf, "(%d,%d)\n", left, right);
 }
 
 /* Displays sensor key as label */
@@ -814,7 +814,7 @@ static ssize_t applesmc_show_sensor_label(struct device *dev,
 {
        const char *key = smcreg.index[to_index(devattr)];
 
-       return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", key);
+       return sysfs_emit(sysfsbuf, "%s\n", key);
 }
 
 /* Displays degree Celsius * 1000 */
@@ -832,7 +832,7 @@ static ssize_t applesmc_show_temperature(struct device *dev,
 
        temp = 250 * (value >> 6);
 
-       return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", temp);
+       return sysfs_emit(sysfsbuf, "%d\n", temp);
 }
 
 static ssize_t applesmc_show_fan_speed(struct device *dev,
@@ -851,7 +851,7 @@ static ssize_t applesmc_show_fan_speed(struct device *dev,
                return ret;
 
        speed = ((buffer[0] << 8 | buffer[1]) >> 2);
-       return snprintf(sysfsbuf, PAGE_SIZE, "%u\n", speed);
+       return sysfs_emit(sysfsbuf, "%u\n", speed);
 }
 
 static ssize_t applesmc_store_fan_speed(struct device *dev,
@@ -891,7 +891,7 @@ static ssize_t applesmc_show_fan_manual(struct device *dev,
                return ret;
 
        manual = ((buffer[0] << 8 | buffer[1]) >> to_index(attr)) & 0x01;
-       return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", manual);
+       return sysfs_emit(sysfsbuf, "%d\n", manual);
 }
 
 static ssize_t applesmc_store_fan_manual(struct device *dev,
@@ -943,14 +943,14 @@ static ssize_t applesmc_show_fan_position(struct device *dev,
 
        if (ret)
                return ret;
-       else
-               return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", buffer+4);
+
+       return sysfs_emit(sysfsbuf, "%s\n", buffer + 4);
 }
 
 static ssize_t applesmc_calibrate_show(struct device *dev,
                                struct device_attribute *attr, char *sysfsbuf)
 {
-       return snprintf(sysfsbuf, PAGE_SIZE, "(%d,%d)\n", rest_x, rest_y);
+       return sysfs_emit(sysfsbuf, "(%d,%d)\n", rest_x, rest_y);
 }
 
 static ssize_t applesmc_calibrate_store(struct device *dev,
@@ -992,7 +992,7 @@ static ssize_t applesmc_key_count_show(struct device *dev,
 
        count = ((u32)buffer[0]<<24) + ((u32)buffer[1]<<16) +
                                                ((u32)buffer[2]<<8) + buffer[3];
-       return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", count);
+       return sysfs_emit(sysfsbuf, "%d\n", count);
 }
 
 static ssize_t applesmc_key_at_index_read_show(struct device *dev,
@@ -1020,7 +1020,7 @@ static ssize_t applesmc_key_at_index_data_length_show(struct device *dev,
        if (IS_ERR(entry))
                return PTR_ERR(entry);
 
-       return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", entry->len);
+       return sysfs_emit(sysfsbuf, "%d\n", entry->len);
 }
 
 static ssize_t applesmc_key_at_index_type_show(struct device *dev,
@@ -1032,7 +1032,7 @@ static ssize_t applesmc_key_at_index_type_show(struct device *dev,
        if (IS_ERR(entry))
                return PTR_ERR(entry);
 
-       return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", entry->type);
+       return sysfs_emit(sysfsbuf, "%s\n", entry->type);
 }
 
 static ssize_t applesmc_key_at_index_name_show(struct device *dev,
@@ -1044,13 +1044,13 @@ static ssize_t applesmc_key_at_index_name_show(struct device *dev,
        if (IS_ERR(entry))
                return PTR_ERR(entry);
 
-       return snprintf(sysfsbuf, PAGE_SIZE, "%s\n", entry->key);
+       return sysfs_emit(sysfsbuf, "%s\n", entry->key);
 }
 
 static ssize_t applesmc_key_at_index_show(struct device *dev,
                                struct device_attribute *attr, char *sysfsbuf)
 {
-       return snprintf(sysfsbuf, PAGE_SIZE, "%d\n", key_at_index);
+       return sysfs_emit(sysfsbuf, "%d\n", key_at_index);
 }
 
 static ssize_t applesmc_key_at_index_store(struct device *dev,
index 9949405..3a5807e 100644 (file)
 #define CMD_TIMEOUT_MS         250
 #define SECONDS_PER_HOUR       (60 * 60)
 #define SECONDS_PER_DAY                (SECONDS_PER_HOUR * 24)
+#define RAIL_COUNT             3 /* 3v3 + 5v + 12v */
+#define TEMP_COUNT             2
 
 #define PSU_CMD_SELECT_RAIL    0x00 /* expects length 2 */
-#define PSU_CMD_IN_VOLTS       0x88 /* the rest of the commands expect length 3 */
+#define PSU_CMD_RAIL_VOLTS_HCRIT 0x40 /* the rest of the commands expect length 3 */
+#define PSU_CMD_RAIL_VOLTS_LCRIT 0x44
+#define PSU_CMD_RAIL_AMPS_HCRIT        0x46
+#define PSU_CMD_TEMP_HCRIT     0x4F
+#define PSU_CMD_IN_VOLTS       0x88
 #define PSU_CMD_IN_AMPS                0x89
-#define PSU_CMD_RAIL_OUT_VOLTS 0x8B
+#define PSU_CMD_RAIL_VOLTS     0x8B
 #define PSU_CMD_RAIL_AMPS      0x8C
 #define PSU_CMD_TEMP0          0x8D
 #define PSU_CMD_TEMP1          0x8E
@@ -116,30 +122,25 @@ struct corsairpsu_data {
        u8 *cmd_buffer;
        char vendor[REPLY_SIZE];
        char product[REPLY_SIZE];
+       long temp_crit[TEMP_COUNT];
+       long in_crit[RAIL_COUNT];
+       long in_lcrit[RAIL_COUNT];
+       long curr_crit[RAIL_COUNT];
+       u8 temp_crit_support;
+       u8 in_crit_support;
+       u8 in_lcrit_support;
+       u8 curr_crit_support;
+       bool in_curr_cmd_support; /* not all commands are supported on every PSU */
 };
 
 /* some values are SMBus LINEAR11 data which need a conversion */
-static int corsairpsu_linear11_to_int(const int val)
+static int corsairpsu_linear11_to_int(const u16 val, const int scale)
 {
-       int exp = (val & 0xFFFF) >> 0x0B;
-       int mant = val & 0x7FF;
-       int i;
-
-       if (exp > 0x0F)
-               exp -= 0x20;
-       if (mant > 0x3FF)
-               mant -= 0x800;
-       if ((mant & 0x01) == 1)
-               ++mant;
-       if (exp < 0) {
-               for (i = 0; i < -exp; ++i)
-                       mant /= 2;
-       } else {
-               for (i = 0; i < exp; ++i)
-                       mant *= 2;
-       }
+       const int exp = ((s16)val) >> 11;
+       const int mant = (((s16)(val & 0x7ff)) << 5) >> 5;
+       const int result = mant * scale;
 
-       return mant;
+       return (exp >= 0) ? (result << exp) : (result >> -exp);
 }
 
 static int corsairpsu_usb_cmd(struct corsairpsu_data *priv, u8 p0, u8 p1, u8 p2, void *data)
@@ -207,7 +208,10 @@ static int corsairpsu_request(struct corsairpsu_data *priv, u8 cmd, u8 rail, voi
 
        mutex_lock(&priv->lock);
        switch (cmd) {
-       case PSU_CMD_RAIL_OUT_VOLTS:
+       case PSU_CMD_RAIL_VOLTS_HCRIT:
+       case PSU_CMD_RAIL_VOLTS_LCRIT:
+       case PSU_CMD_RAIL_AMPS_HCRIT:
+       case PSU_CMD_RAIL_VOLTS:
        case PSU_CMD_RAIL_AMPS:
        case PSU_CMD_RAIL_WATTS:
                ret = corsairpsu_usb_cmd(priv, 2, PSU_CMD_SELECT_RAIL, rail, NULL);
@@ -243,20 +247,24 @@ static int corsairpsu_get_value(struct corsairpsu_data *priv, u8 cmd, u8 rail, l
         */
        tmp = ((long)data[3] << 24) + (data[2] << 16) + (data[1] << 8) + data[0];
        switch (cmd) {
+       case PSU_CMD_RAIL_VOLTS_HCRIT:
+       case PSU_CMD_RAIL_VOLTS_LCRIT:
+       case PSU_CMD_RAIL_AMPS_HCRIT:
+       case PSU_CMD_TEMP_HCRIT:
        case PSU_CMD_IN_VOLTS:
        case PSU_CMD_IN_AMPS:
-       case PSU_CMD_RAIL_OUT_VOLTS:
+       case PSU_CMD_RAIL_VOLTS:
        case PSU_CMD_RAIL_AMPS:
        case PSU_CMD_TEMP0:
        case PSU_CMD_TEMP1:
-               *val = corsairpsu_linear11_to_int(tmp & 0xFFFF) * 1000;
+               *val = corsairpsu_linear11_to_int(tmp & 0xFFFF, 1000);
                break;
        case PSU_CMD_FAN:
-               *val = corsairpsu_linear11_to_int(tmp & 0xFFFF);
+               *val = corsairpsu_linear11_to_int(tmp & 0xFFFF, 1);
                break;
        case PSU_CMD_RAIL_WATTS:
        case PSU_CMD_TOTAL_WATTS:
-               *val = corsairpsu_linear11_to_int(tmp & 0xFFFF) * 1000000;
+               *val = corsairpsu_linear11_to_int(tmp & 0xFFFF, 1000000);
                break;
        case PSU_CMD_TOTAL_UPTIME:
        case PSU_CMD_UPTIME:
@@ -270,75 +278,265 @@ static int corsairpsu_get_value(struct corsairpsu_data *priv, u8 cmd, u8 rail, l
        return ret;
 }
 
-static umode_t corsairpsu_hwmon_ops_is_visible(const void *data, enum hwmon_sensor_types type,
-                                              u32 attr, int channel)
+static void corsairpsu_get_criticals(struct corsairpsu_data *priv)
 {
-       if (type == hwmon_temp && (attr == hwmon_temp_input || attr == hwmon_temp_label))
-               return 0444;
-       else if (type == hwmon_fan && (attr == hwmon_fan_input || attr == hwmon_fan_label))
-               return 0444;
-       else if (type == hwmon_power && (attr == hwmon_power_input || attr == hwmon_power_label))
-               return 0444;
-       else if (type == hwmon_in && (attr == hwmon_in_input || attr == hwmon_in_label))
+       long tmp;
+       int rail;
+
+       for (rail = 0; rail < TEMP_COUNT; ++rail) {
+               if (!corsairpsu_get_value(priv, PSU_CMD_TEMP_HCRIT, rail, &tmp)) {
+                       priv->temp_crit_support |= BIT(rail);
+                       priv->temp_crit[rail] = tmp;
+               }
+       }
+
+       for (rail = 0; rail < RAIL_COUNT; ++rail) {
+               if (!corsairpsu_get_value(priv, PSU_CMD_RAIL_VOLTS_HCRIT, rail, &tmp)) {
+                       priv->in_crit_support |= BIT(rail);
+                       priv->in_crit[rail] = tmp;
+               }
+
+               if (!corsairpsu_get_value(priv, PSU_CMD_RAIL_VOLTS_LCRIT, rail, &tmp)) {
+                       priv->in_lcrit_support |= BIT(rail);
+                       priv->in_lcrit[rail] = tmp;
+               }
+
+               if (!corsairpsu_get_value(priv, PSU_CMD_RAIL_AMPS_HCRIT, rail, &tmp)) {
+                       priv->curr_crit_support |= BIT(rail);
+                       priv->curr_crit[rail] = tmp;
+               }
+       }
+}
+
+static void corsairpsu_check_cmd_support(struct corsairpsu_data *priv)
+{
+       long tmp;
+
+       priv->in_curr_cmd_support = !corsairpsu_get_value(priv, PSU_CMD_IN_AMPS, 0, &tmp);
+}
+
+static umode_t corsairpsu_hwmon_temp_is_visible(const struct corsairpsu_data *priv, u32 attr,
+                                               int channel)
+{
+       umode_t res = 0444;
+
+       switch (attr) {
+       case hwmon_temp_input:
+       case hwmon_temp_label:
+       case hwmon_temp_crit:
+               if (channel > 0 && !(priv->temp_crit_support & BIT(channel - 1)))
+                       res = 0;
+               break;
+       default:
+               break;
+       }
+
+       return res;
+}
+
+static umode_t corsairpsu_hwmon_fan_is_visible(const struct corsairpsu_data *priv, u32 attr,
+                                              int channel)
+{
+       switch (attr) {
+       case hwmon_fan_input:
+       case hwmon_fan_label:
                return 0444;
-       else if (type == hwmon_curr && (attr == hwmon_curr_input || attr == hwmon_curr_label))
+       default:
+               return 0;
+       }
+}
+
+static umode_t corsairpsu_hwmon_power_is_visible(const struct corsairpsu_data *priv, u32 attr,
+                                                int channel)
+{
+       switch (attr) {
+       case hwmon_power_input:
+       case hwmon_power_label:
                return 0444;
+       default:
+               return 0;
+       };
+}
 
-       return 0;
+static umode_t corsairpsu_hwmon_in_is_visible(const struct corsairpsu_data *priv, u32 attr,
+                                             int channel)
+{
+       umode_t res = 0444;
+
+       switch (attr) {
+       case hwmon_in_input:
+       case hwmon_in_label:
+       case hwmon_in_crit:
+               if (channel > 0 && !(priv->in_crit_support & BIT(channel - 1)))
+                       res = 0;
+               break;
+       case hwmon_in_lcrit:
+               if (channel > 0 && !(priv->in_lcrit_support & BIT(channel - 1)))
+                       res = 0;
+               break;
+       default:
+               break;
+       };
+
+       return res;
 }
 
-static int corsairpsu_hwmon_ops_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
-                                    int channel, long *val)
+static umode_t corsairpsu_hwmon_curr_is_visible(const struct corsairpsu_data *priv, u32 attr,
+                                               int channel)
 {
-       struct corsairpsu_data *priv = dev_get_drvdata(dev);
-       int ret;
+       umode_t res = 0444;
+
+       switch (attr) {
+       case hwmon_curr_input:
+               if (channel == 0 && !priv->in_curr_cmd_support)
+                       res = 0;
+               break;
+       case hwmon_curr_label:
+       case hwmon_curr_crit:
+               if (channel > 0 && !(priv->curr_crit_support & BIT(channel - 1)))
+                       res = 0;
+               break;
+       default:
+               break;
+       }
+
+       return res;
+}
+
+static umode_t corsairpsu_hwmon_ops_is_visible(const void *data, enum hwmon_sensor_types type,
+                                              u32 attr, int channel)
+{
+       const struct corsairpsu_data *priv = data;
+
+       switch (type) {
+       case hwmon_temp:
+               return corsairpsu_hwmon_temp_is_visible(priv, attr, channel);
+       case hwmon_fan:
+               return corsairpsu_hwmon_fan_is_visible(priv, attr, channel);
+       case hwmon_power:
+               return corsairpsu_hwmon_power_is_visible(priv, attr, channel);
+       case hwmon_in:
+               return corsairpsu_hwmon_in_is_visible(priv, attr, channel);
+       case hwmon_curr:
+               return corsairpsu_hwmon_curr_is_visible(priv, attr, channel);
+       default:
+               return 0;
+       }
+}
+
+static int corsairpsu_hwmon_temp_read(struct corsairpsu_data *priv, u32 attr, int channel,
+                                     long *val)
+{
+       int err = -EOPNOTSUPP;
+
+       switch (attr) {
+       case hwmon_temp_input:
+               return corsairpsu_get_value(priv, channel ? PSU_CMD_TEMP1 : PSU_CMD_TEMP0,
+                                           channel, val);
+       case hwmon_temp_crit:
+               *val = priv->temp_crit[channel];
+               err = 0;
+               break;
+       default:
+               break;
+       }
 
-       if (type == hwmon_temp && attr == hwmon_temp_input && channel < 2) {
-               ret = corsairpsu_get_value(priv, channel ? PSU_CMD_TEMP1 : PSU_CMD_TEMP0, channel,
-                                          val);
-       } else if (type == hwmon_fan && attr == hwmon_fan_input) {
-               ret = corsairpsu_get_value(priv, PSU_CMD_FAN, 0, val);
-       } else if (type == hwmon_power && attr == hwmon_power_input) {
+       return err;
+}
+
+static int corsairpsu_hwmon_power_read(struct corsairpsu_data *priv, u32 attr, int channel,
+                                      long *val)
+{
+       if (attr == hwmon_power_input) {
                switch (channel) {
                case 0:
-                       ret = corsairpsu_get_value(priv, PSU_CMD_TOTAL_WATTS, 0, val);
-                       break;
+                       return corsairpsu_get_value(priv, PSU_CMD_TOTAL_WATTS, 0, val);
                case 1 ... 3:
-                       ret = corsairpsu_get_value(priv, PSU_CMD_RAIL_WATTS, channel - 1, val);
-                       break;
+                       return corsairpsu_get_value(priv, PSU_CMD_RAIL_WATTS, channel - 1, val);
                default:
-                       return -EOPNOTSUPP;
+                       break;
                }
-       } else if (type == hwmon_in && attr == hwmon_in_input) {
+       }
+
+       return -EOPNOTSUPP;
+}
+
+static int corsairpsu_hwmon_in_read(struct corsairpsu_data *priv, u32 attr, int channel, long *val)
+{
+       int err = -EOPNOTSUPP;
+
+       switch (attr) {
+       case hwmon_in_input:
                switch (channel) {
                case 0:
-                       ret = corsairpsu_get_value(priv, PSU_CMD_IN_VOLTS, 0, val);
-                       break;
+                       return corsairpsu_get_value(priv, PSU_CMD_IN_VOLTS, 0, val);
                case 1 ... 3:
-                       ret = corsairpsu_get_value(priv, PSU_CMD_RAIL_OUT_VOLTS, channel - 1, val);
-                       break;
+                       return corsairpsu_get_value(priv, PSU_CMD_RAIL_VOLTS, channel - 1, val);
                default:
-                       return -EOPNOTSUPP;
+                       break;
                }
-       } else if (type == hwmon_curr && attr == hwmon_curr_input) {
+               break;
+       case hwmon_in_crit:
+               *val = priv->in_crit[channel - 1];
+               err = 0;
+               break;
+       case hwmon_in_lcrit:
+               *val = priv->in_lcrit[channel - 1];
+               err = 0;
+               break;
+       }
+
+       return err;
+}
+
+static int corsairpsu_hwmon_curr_read(struct corsairpsu_data *priv, u32 attr, int channel,
+                                     long *val)
+{
+       int err = -EOPNOTSUPP;
+
+       switch (attr) {
+       case hwmon_curr_input:
                switch (channel) {
                case 0:
-                       ret = corsairpsu_get_value(priv, PSU_CMD_IN_AMPS, 0, val);
-                       break;
+                       return corsairpsu_get_value(priv, PSU_CMD_IN_AMPS, 0, val);
                case 1 ... 3:
-                       ret = corsairpsu_get_value(priv, PSU_CMD_RAIL_AMPS, channel - 1, val);
-                       break;
+                       return corsairpsu_get_value(priv, PSU_CMD_RAIL_AMPS, channel - 1, val);
                default:
-                       return -EOPNOTSUPP;
+                       break;
                }
-       } else {
-               return -EOPNOTSUPP;
+               break;
+       case hwmon_curr_crit:
+               *val = priv->curr_crit[channel - 1];
+               err = 0;
+               break;
+       default:
+               break;
        }
 
-       if (ret < 0)
-               return ret;
+       return err;
+}
 
-       return 0;
+static int corsairpsu_hwmon_ops_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
+                                    int channel, long *val)
+{
+       struct corsairpsu_data *priv = dev_get_drvdata(dev);
+
+       switch (type) {
+       case hwmon_temp:
+               return corsairpsu_hwmon_temp_read(priv, attr, channel, val);
+       case hwmon_fan:
+               if (attr == hwmon_fan_input)
+                       return corsairpsu_get_value(priv, PSU_CMD_FAN, 0, val);
+               return -EOPNOTSUPP;
+       case hwmon_power:
+               return corsairpsu_hwmon_power_read(priv, attr, channel, val);
+       case hwmon_in:
+               return corsairpsu_hwmon_in_read(priv, attr, channel, val);
+       case hwmon_curr:
+               return corsairpsu_hwmon_curr_read(priv, attr, channel, val);
+       default:
+               return -EOPNOTSUPP;
+       }
 }
 
 static int corsairpsu_hwmon_ops_read_string(struct device *dev, enum hwmon_sensor_types type,
@@ -374,8 +572,8 @@ static const struct hwmon_channel_info *corsairpsu_info[] = {
        HWMON_CHANNEL_INFO(chip,
                           HWMON_C_REGISTER_TZ),
        HWMON_CHANNEL_INFO(temp,
-                          HWMON_T_INPUT | HWMON_T_LABEL,
-                          HWMON_T_INPUT | HWMON_T_LABEL),
+                          HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_CRIT,
+                          HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_CRIT),
        HWMON_CHANNEL_INFO(fan,
                           HWMON_F_INPUT | HWMON_F_LABEL),
        HWMON_CHANNEL_INFO(power,
@@ -385,14 +583,14 @@ static const struct hwmon_channel_info *corsairpsu_info[] = {
                           HWMON_P_INPUT | HWMON_P_LABEL),
        HWMON_CHANNEL_INFO(in,
                           HWMON_I_INPUT | HWMON_I_LABEL,
-                          HWMON_I_INPUT | HWMON_I_LABEL,
-                          HWMON_I_INPUT | HWMON_I_LABEL,
-                          HWMON_I_INPUT | HWMON_I_LABEL),
+                          HWMON_I_INPUT | HWMON_I_LABEL | HWMON_I_LCRIT | HWMON_I_CRIT,
+                          HWMON_I_INPUT | HWMON_I_LABEL | HWMON_I_LCRIT | HWMON_I_CRIT,
+                          HWMON_I_INPUT | HWMON_I_LABEL | HWMON_I_LCRIT | HWMON_I_CRIT),
        HWMON_CHANNEL_INFO(curr,
                           HWMON_C_INPUT | HWMON_C_LABEL,
-                          HWMON_C_INPUT | HWMON_C_LABEL,
-                          HWMON_C_INPUT | HWMON_C_LABEL,
-                          HWMON_C_INPUT | HWMON_C_LABEL),
+                          HWMON_C_INPUT | HWMON_C_LABEL | HWMON_C_CRIT,
+                          HWMON_C_INPUT | HWMON_C_LABEL | HWMON_C_CRIT,
+                          HWMON_C_INPUT | HWMON_C_LABEL | HWMON_C_CRIT),
        NULL
 };
 
@@ -527,6 +725,9 @@ static int corsairpsu_probe(struct hid_device *hdev, const struct hid_device_id
                goto fail_and_stop;
        }
 
+       corsairpsu_get_criticals(priv);
+       corsairpsu_check_cmd_support(priv);
+
        priv->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "corsairpsu", priv,
                                                          &corsairpsu_chip_info, 0);
 
index 73b9db9..2970892 100644 (file)
@@ -1210,6 +1210,14 @@ static struct dmi_system_id i8k_whitelist_fan_control[] __initdata = {
                },
                .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3],
        },
+       {
+               .ident = "Dell Latitude E7440",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+                       DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Latitude E7440"),
+               },
+               .driver_data = (void *)&i8k_fan_control_data[I8K_FAN_34A3_35A3],
+       },
        { }
 };
 
index e1d742b..bf1c4b7 100644 (file)
@@ -326,7 +326,7 @@ static struct attribute *ds1621_attributes[] = {
 static umode_t ds1621_attribute_visible(struct kobject *kobj,
                                        struct attribute *attr, int index)
 {
-       struct device *dev = container_of(kobj, struct device, kobj);
+       struct device *dev = kobj_to_dev(kobj);
        struct ds1621_data *data = dev_get_drvdata(dev);
 
        if (attr == &dev_attr_update_interval.attr)
index ef88a15..ceffc76 100644 (file)
@@ -509,7 +509,7 @@ error:
 /* SysFS structs                                                            */
 /*****************************************************************************/
 
-/* Temprature sensors */
+/* Temperature sensors */
 static SENSOR_DEVICE_ATTR_RO(temp1_input, temp_value, 0);
 static SENSOR_DEVICE_ATTR_RO(temp2_input, temp_value, 1);
 static SENSOR_DEVICE_ATTR_RO(temp3_input, temp_value, 2);
@@ -713,7 +713,7 @@ static int fts_detect(struct i2c_client *client,
 {
        int val;
 
-       /* detection works with revsion greater or equal to 0x2b */
+       /* detection works with revision greater or equal to 0x2b */
        val = i2c_smbus_read_byte_data(client, FTS_DEVICE_REVISION_REG);
        if (val < 0x2b)
                return -ENODEV;
index 6c68405..fd47ab4 100644 (file)
@@ -79,7 +79,7 @@ static struct attribute *hwmon_dev_attrs[] = {
 static umode_t hwmon_dev_name_is_visible(struct kobject *kobj,
                                         struct attribute *attr, int n)
 {
-       struct device *dev = container_of(kobj, struct device, kobj);
+       struct device *dev = kobj_to_dev(kobj);
 
        if (to_hwmon_device(dev)->name == NULL)
                return 0;
index f4c7b5f..fc3007c 100644 (file)
@@ -259,7 +259,7 @@ static ssize_t ina209_interval_show(struct device *dev,
 {
        struct ina209_data *data = dev_get_drvdata(dev);
 
-       return snprintf(buf, PAGE_SIZE, "%d\n", data->update_interval);
+       return sysfs_emit(buf, "%d\n", data->update_interval);
 }
 
 /*
@@ -343,7 +343,7 @@ static ssize_t ina209_value_show(struct device *dev,
                return PTR_ERR(data);
 
        val = ina209_from_reg(attr->index, data->regs[attr->index]);
-       return snprintf(buf, PAGE_SIZE, "%ld\n", val);
+       return sysfs_emit(buf, "%ld\n", val);
 }
 
 static ssize_t ina209_alarm_show(struct device *dev,
@@ -363,7 +363,7 @@ static ssize_t ina209_alarm_show(struct device *dev,
         * All alarms are in the INA209_STATUS register. To avoid a long
         * switch statement, the mask is passed in attr->index
         */
-       return snprintf(buf, PAGE_SIZE, "%u\n", !!(status & mask));
+       return sysfs_emit(buf, "%u\n", !!(status & mask));
 }
 
 /* Shunt voltage, history, limits, alarms */
index ca97f9e..00fc703 100644 (file)
@@ -310,8 +310,7 @@ static ssize_t ina2xx_value_show(struct device *dev,
        if (err < 0)
                return err;
 
-       return snprintf(buf, PAGE_SIZE, "%d\n",
-                       ina2xx_get_value(data, attr->index, regval));
+       return sysfs_emit(buf, "%d\n", ina2xx_get_value(data, attr->index, regval));
 }
 
 static int ina226_reg_to_alert(struct ina2xx_data *data, u8 bit, u16 regval)
@@ -386,7 +385,7 @@ static ssize_t ina226_alert_show(struct device *dev,
                val = ina226_reg_to_alert(data, attr->index, regval);
        }
 
-       ret = snprintf(buf, PAGE_SIZE, "%d\n", val);
+       ret = sysfs_emit(buf, "%d\n", val);
 abort:
        mutex_unlock(&data->config_lock);
        return ret;
@@ -450,7 +449,7 @@ static ssize_t ina226_alarm_show(struct device *dev,
 
        alarm = (regval & BIT(attr->index)) &&
                (regval & INA226_ALERT_FUNCTION_FLAG);
-       return snprintf(buf, PAGE_SIZE, "%d\n", alarm);
+       return sysfs_emit(buf, "%d\n", alarm);
 }
 
 /*
@@ -481,7 +480,7 @@ static ssize_t ina2xx_shunt_show(struct device *dev,
 {
        struct ina2xx_data *data = dev_get_drvdata(dev);
 
-       return snprintf(buf, PAGE_SIZE, "%li\n", data->rshunt);
+       return sysfs_emit(buf, "%li\n", data->rshunt);
 }
 
 static ssize_t ina2xx_shunt_store(struct device *dev,
@@ -537,7 +536,7 @@ static ssize_t ina226_interval_show(struct device *dev,
        if (status)
                return status;
 
-       return snprintf(buf, PAGE_SIZE, "%d\n", ina226_reg_to_interval(regval));
+       return sysfs_emit(buf, "%d\n", ina226_reg_to_interval(regval));
 }
 
 /* shunt voltage */
index d80bd3e..c602583 100644 (file)
@@ -698,7 +698,7 @@ static ssize_t ina3221_shunt_show(struct device *dev,
        unsigned int channel = sd_attr->index;
        struct ina3221_input *input = &ina->inputs[channel];
 
-       return snprintf(buf, PAGE_SIZE, "%d\n", input->shunt_resistor);
+       return sysfs_emit(buf, "%d\n", input->shunt_resistor);
 }
 
 static ssize_t ina3221_shunt_store(struct device *dev,
index 17d5e6b..bd7ed2e 100644 (file)
@@ -99,6 +99,50 @@ static const struct hwmon_channel_info *n3000bmc_hinfo[] = {
        NULL
 };
 
+static const struct m10bmc_sdata d5005bmc_temp_tbl[] = {
+       { 0x100, 0x104, 0x108, 0x10c, 0x0, 500, "Board Inlet Air Temperature" },
+       { 0x110, 0x114, 0x118, 0x0, 0x0, 500, "FPGA Core Temperature" },
+       { 0x11c, 0x120, 0x124, 0x128, 0x0, 500, "Board Exhaust Air Temperature" },
+       { 0x12c, 0x130, 0x134, 0x0, 0x0, 500, "FPGA Transceiver Temperature" },
+       { 0x138, 0x13c, 0x140, 0x144, 0x0, 500, "RDIMM0 Temperature" },
+       { 0x148, 0x14c, 0x150, 0x154, 0x0, 500, "RDIMM1 Temperature" },
+       { 0x158, 0x15c, 0x160, 0x164, 0x0, 500, "RDIMM2 Temperature" },
+       { 0x168, 0x16c, 0x170, 0x174, 0x0, 500, "RDIMM3 Temperature" },
+       { 0x178, 0x17c, 0x180, 0x0, 0x0, 500, "QSFP0 Temperature" },
+       { 0x188, 0x18c, 0x190, 0x0, 0x0, 500, "QSFP1 Temperature" },
+       { 0x1a0, 0x1a4, 0x1a8, 0x0, 0x0, 500, "3.3v Temperature" },
+       { 0x1bc, 0x1c0, 0x1c4, 0x0, 0x0, 500, "VCCERAM Temperature" },
+       { 0x1d8, 0x1dc, 0x1e0, 0x0, 0x0, 500, "VCCR Temperature" },
+       { 0x1f4, 0x1f8, 0x1fc, 0x0, 0x0, 500, "VCCT Temperature" },
+       { 0x210, 0x214, 0x218, 0x0, 0x0, 500, "1.8v Temperature" },
+       { 0x22c, 0x230, 0x234, 0x0, 0x0, 500, "12v Backplane Temperature" },
+       { 0x248, 0x24c, 0x250, 0x0, 0x0, 500, "12v AUX Temperature" },
+};
+
+static const struct m10bmc_sdata d5005bmc_in_tbl[] = {
+       { 0x184, 0x0, 0x0, 0x0, 0x0, 1, "QSFP0 Supply Voltage" },
+       { 0x194, 0x0, 0x0, 0x0, 0x0, 1, "QSFP1 Supply Voltage" },
+       { 0x198, 0x0, 0x0, 0x0, 0x0, 1, "FPGA Core Voltage" },
+       { 0x1ac, 0x1b0, 0x1b4, 0x0, 0x0, 1, "3.3v Voltage" },
+       { 0x1c8, 0x1cc, 0x1d0, 0x0, 0x0, 1, "VCCERAM Voltage" },
+       { 0x1e4, 0x1e8, 0x1ec, 0x0, 0x0, 1, "VCCR Voltage" },
+       { 0x200, 0x204, 0x208, 0x0, 0x0, 1, "VCCT Voltage" },
+       { 0x21c, 0x220, 0x224, 0x0, 0x0, 1, "1.8v Voltage" },
+       { 0x238, 0x0, 0x0, 0x0, 0x23c, 1, "12v Backplane Voltage" },
+       { 0x254, 0x0, 0x0, 0x0, 0x258, 1, "12v AUX Voltage" },
+};
+
+static const struct m10bmc_sdata d5005bmc_curr_tbl[] = {
+       { 0x19c, 0x0, 0x0, 0x0, 0x0, 1, "FPGA Core Current" },
+       { 0x1b8, 0x0, 0x0, 0x0, 0x0, 1, "3.3v Current" },
+       { 0x1d4, 0x0, 0x0, 0x0, 0x0, 1, "VCCERAM Current" },
+       { 0x1f0, 0x0, 0x0, 0x0, 0x0, 1, "VCCR Current" },
+       { 0x20c, 0x0, 0x0, 0x0, 0x0, 1, "VCCT Current" },
+       { 0x228, 0x0, 0x0, 0x0, 0x0, 1, "1.8v Current" },
+       { 0x240, 0x244, 0x0, 0x0, 0x0, 1, "12v Backplane Current" },
+       { 0x25c, 0x260, 0x0, 0x0, 0x0, 1, "12v AUX Current" },
+};
+
 static const struct m10bmc_hwmon_board_data n3000bmc_hwmon_bdata = {
        .tables = {
                [hwmon_temp] = n3000bmc_temp_tbl,
@@ -110,6 +154,80 @@ static const struct m10bmc_hwmon_board_data n3000bmc_hwmon_bdata = {
        .hinfo = n3000bmc_hinfo,
 };
 
+static const struct hwmon_channel_info *d5005bmc_hinfo[] = {
+       HWMON_CHANNEL_INFO(temp,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
+                          HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+                          HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
+                          HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+                          HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
+                          HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
+                          HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
+                          HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
+                          HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+                          HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+                          HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+                          HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+                          HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+                          HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+                          HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+                          HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+                          HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
+                          HWMON_T_LABEL),
+       HWMON_CHANNEL_INFO(in,
+                          HWMON_I_INPUT | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_CRIT |
+                          HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_CRIT |
+                          HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_CRIT |
+                          HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_CRIT |
+                          HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_MAX | HWMON_I_CRIT |
+                          HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_LABEL),
+       HWMON_CHANNEL_INFO(curr,
+                          HWMON_C_INPUT | HWMON_C_LABEL,
+                          HWMON_C_INPUT | HWMON_C_LABEL,
+                          HWMON_C_INPUT | HWMON_C_LABEL,
+                          HWMON_C_INPUT | HWMON_C_LABEL,
+                          HWMON_C_INPUT | HWMON_C_LABEL,
+                          HWMON_C_INPUT | HWMON_C_LABEL,
+                          HWMON_C_INPUT | HWMON_C_MAX | HWMON_C_LABEL,
+                          HWMON_C_INPUT | HWMON_C_MAX | HWMON_C_LABEL),
+       NULL
+};
+
+static const struct m10bmc_hwmon_board_data d5005bmc_hwmon_bdata = {
+       .tables = {
+               [hwmon_temp] = d5005bmc_temp_tbl,
+               [hwmon_in] = d5005bmc_in_tbl,
+               [hwmon_curr] = d5005bmc_curr_tbl,
+       },
+
+       .hinfo = d5005bmc_hinfo,
+};
+
 static umode_t
 m10bmc_hwmon_is_visible(const void *data, enum hwmon_sensor_types type,
                        u32 attr, int channel)
@@ -316,6 +434,10 @@ static const struct platform_device_id intel_m10bmc_hwmon_ids[] = {
                .name = "n3000bmc-hwmon",
                .driver_data = (unsigned long)&n3000bmc_hwmon_bdata,
        },
+       {
+               .name = "d5005bmc-hwmon",
+               .driver_data = (unsigned long)&d5005bmc_hwmon_bdata,
+       },
        { }
 };
 
index fac9b5c..1f93134 100644 (file)
@@ -1981,7 +1981,7 @@ static SENSOR_DEVICE_ATTR(in9_label, S_IRUGO, show_label, NULL, 3);
 static umode_t it87_in_is_visible(struct kobject *kobj,
                                  struct attribute *attr, int index)
 {
-       struct device *dev = container_of(kobj, struct device, kobj);
+       struct device *dev = kobj_to_dev(kobj);
        struct it87_data *data = dev_get_drvdata(dev);
        int i = index / 5;      /* voltage index */
        int a = index % 5;      /* attribute index */
@@ -2065,7 +2065,7 @@ static const struct attribute_group it87_group_in = {
 static umode_t it87_temp_is_visible(struct kobject *kobj,
                                    struct attribute *attr, int index)
 {
-       struct device *dev = container_of(kobj, struct device, kobj);
+       struct device *dev = kobj_to_dev(kobj);
        struct it87_data *data = dev_get_drvdata(dev);
        int i = index / 7;      /* temperature index */
        int a = index % 7;      /* attribute index */
@@ -2126,7 +2126,7 @@ static const struct attribute_group it87_group_temp = {
 static umode_t it87_is_visible(struct kobject *kobj,
                               struct attribute *attr, int index)
 {
-       struct device *dev = container_of(kobj, struct device, kobj);
+       struct device *dev = kobj_to_dev(kobj);
        struct it87_data *data = dev_get_drvdata(dev);
 
        if ((index == 2 || index == 3) && !data->has_vid)
@@ -2158,7 +2158,7 @@ static const struct attribute_group it87_group = {
 static umode_t it87_fan_is_visible(struct kobject *kobj,
                                   struct attribute *attr, int index)
 {
-       struct device *dev = container_of(kobj, struct device, kobj);
+       struct device *dev = kobj_to_dev(kobj);
        struct it87_data *data = dev_get_drvdata(dev);
        int i = index / 5;      /* fan index */
        int a = index % 5;      /* attribute index */
@@ -2229,7 +2229,7 @@ static const struct attribute_group it87_group_fan = {
 static umode_t it87_pwm_is_visible(struct kobject *kobj,
                                   struct attribute *attr, int index)
 {
-       struct device *dev = container_of(kobj, struct device, kobj);
+       struct device *dev = kobj_to_dev(kobj);
        struct it87_data *data = dev_get_drvdata(dev);
        int i = index / 4;      /* pwm index */
        int a = index % 4;      /* attribute index */
@@ -2290,7 +2290,7 @@ static const struct attribute_group it87_group_pwm = {
 static umode_t it87_auto_pwm_is_visible(struct kobject *kobj,
                                        struct attribute *attr, int index)
 {
-       struct device *dev = container_of(kobj, struct device, kobj);
+       struct device *dev = kobj_to_dev(kobj);
        struct it87_data *data = dev_get_drvdata(dev);
        int i = index / 11;     /* pwm index */
        int a = index % 11;     /* attribute index */
index c83eb2f..1109fff 100644 (file)
@@ -280,7 +280,7 @@ static ssize_t pem_bool_show(struct device *dev, struct device_attribute *da,
                return PTR_ERR(data);
 
        status = data->data_string[attr->nr] & attr->index;
-       return snprintf(buf, PAGE_SIZE, "%d\n", !!status);
+       return sysfs_emit(buf, "%d\n", !!status);
 }
 
 static ssize_t pem_data_show(struct device *dev, struct device_attribute *da,
@@ -296,7 +296,7 @@ static ssize_t pem_data_show(struct device *dev, struct device_attribute *da,
        value = pem_get_data(data->data_string, sizeof(data->data_string),
                             attr->index);
 
-       return snprintf(buf, PAGE_SIZE, "%ld\n", value);
+       return sysfs_emit(buf, "%ld\n", value);
 }
 
 static ssize_t pem_input_show(struct device *dev, struct device_attribute *da,
@@ -312,7 +312,7 @@ static ssize_t pem_input_show(struct device *dev, struct device_attribute *da,
        value = pem_get_input(data->input_string, sizeof(data->input_string),
                              attr->index);
 
-       return snprintf(buf, PAGE_SIZE, "%ld\n", value);
+       return sysfs_emit(buf, "%ld\n", value);
 }
 
 static ssize_t pem_fan_show(struct device *dev, struct device_attribute *da,
@@ -328,7 +328,7 @@ static ssize_t pem_fan_show(struct device *dev, struct device_attribute *da,
        value = pem_get_fan(data->fan_speed, sizeof(data->fan_speed),
                            attr->index);
 
-       return snprintf(buf, PAGE_SIZE, "%ld\n", value);
+       return sysfs_emit(buf, "%ld\n", value);
 }
 
 /* Voltages */
index 50f6726..c8f93c5 100644 (file)
@@ -931,7 +931,7 @@ static const struct attribute_group lm63_group_extra_lut = {
 static umode_t lm63_attribute_mode(struct kobject *kobj,
                                   struct attribute *attr, int index)
 {
-       struct device *dev = container_of(kobj, struct device, kobj);
+       struct device *dev = kobj_to_dev(kobj);
        struct lm63_data *data = dev_get_drvdata(dev);
 
        if (attr == &sensor_dev_attr_temp2_crit.dev_attr.attr
index ba9c868..9adebb5 100644 (file)
@@ -226,7 +226,7 @@ static ssize_t ltc2945_value_show(struct device *dev,
        value = ltc2945_reg_to_val(dev, attr->index);
        if (value < 0)
                return value;
-       return snprintf(buf, PAGE_SIZE, "%lld\n", value);
+       return sysfs_emit(buf, "%lld\n", value);
 }
 
 static ssize_t ltc2945_value_store(struct device *dev,
@@ -333,7 +333,7 @@ static ssize_t ltc2945_bool_show(struct device *dev,
        if (fault)              /* Clear reported faults in chip register */
                regmap_update_bits(regmap, LTC2945_FAULT, attr->index, 0);
 
-       return snprintf(buf, PAGE_SIZE, "%d\n", !!fault);
+       return sysfs_emit(buf, "%d\n", !!fault);
 }
 
 /* Input voltages */
index 78b191b..689f788 100644 (file)
@@ -147,13 +147,13 @@ static ssize_t ltc2990_value_show(struct device *dev,
        if (unlikely(ret < 0))
                return ret;
 
-       return snprintf(buf, PAGE_SIZE, "%d\n", value);
+       return sysfs_emit(buf, "%d\n", value);
 }
 
 static umode_t ltc2990_attrs_visible(struct kobject *kobj,
                                     struct attribute *a, int n)
 {
-       struct device *dev = container_of(kobj, struct device, kobj);
+       struct device *dev = kobj_to_dev(kobj);
        struct ltc2990_data *data = dev_get_drvdata(dev);
        struct device_attribute *da =
                        container_of(a, struct device_attribute, attr);
index 321f54e..13b8536 100644 (file)
@@ -128,7 +128,7 @@ static ssize_t ltc4151_value_show(struct device *dev,
                return PTR_ERR(data);
 
        value = ltc4151_get_value(data, attr->index);
-       return snprintf(buf, PAGE_SIZE, "%d\n", value);
+       return sysfs_emit(buf, "%d\n", value);
 }
 
 /*
index 7cef3cb..1d18c21 100644 (file)
@@ -139,7 +139,7 @@ static ssize_t ltc4215_voltage_show(struct device *dev,
        struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
        const int voltage = ltc4215_get_voltage(dev, attr->index);
 
-       return snprintf(buf, PAGE_SIZE, "%d\n", voltage);
+       return sysfs_emit(buf, "%d\n", voltage);
 }
 
 static ssize_t ltc4215_current_show(struct device *dev,
@@ -147,7 +147,7 @@ static ssize_t ltc4215_current_show(struct device *dev,
 {
        const unsigned int curr = ltc4215_get_current(dev);
 
-       return snprintf(buf, PAGE_SIZE, "%u\n", curr);
+       return sysfs_emit(buf, "%u\n", curr);
 }
 
 static ssize_t ltc4215_power_show(struct device *dev,
@@ -159,7 +159,7 @@ static ssize_t ltc4215_power_show(struct device *dev,
        /* current in mA * voltage in mV == power in uW */
        const unsigned int power = abs(output_voltage * curr);
 
-       return snprintf(buf, PAGE_SIZE, "%u\n", power);
+       return sysfs_emit(buf, "%u\n", power);
 }
 
 static ssize_t ltc4215_alarm_show(struct device *dev,
@@ -170,7 +170,7 @@ static ssize_t ltc4215_alarm_show(struct device *dev,
        const u8 reg = data->regs[LTC4215_STATUS];
        const u32 mask = attr->index;
 
-       return snprintf(buf, PAGE_SIZE, "%u\n", !!(reg & mask));
+       return sysfs_emit(buf, "%u\n", !!(reg & mask));
 }
 
 /*
index 3efce6d..d2027ca 100644 (file)
@@ -94,7 +94,7 @@ static ssize_t ltc4222_value_show(struct device *dev,
        value = ltc4222_get_value(dev, attr->index);
        if (value < 0)
                return value;
-       return snprintf(buf, PAGE_SIZE, "%d\n", value);
+       return sysfs_emit(buf, "%d\n", value);
 }
 
 static ssize_t ltc4222_bool_show(struct device *dev,
@@ -112,7 +112,7 @@ static ssize_t ltc4222_bool_show(struct device *dev,
        if (fault)              /* Clear reported faults in chip register */
                regmap_update_bits(regmap, attr->nr, attr->index, 0);
 
-       return snprintf(buf, PAGE_SIZE, "%d\n", !!fault);
+       return sysfs_emit(buf, "%d\n", !!fault);
 }
 
 /* Voltages */
index d0beb43..75e89ce 100644 (file)
@@ -79,7 +79,7 @@ static ssize_t ltc4260_value_show(struct device *dev,
        value = ltc4260_get_value(dev, attr->index);
        if (value < 0)
                return value;
-       return snprintf(buf, PAGE_SIZE, "%d\n", value);
+       return sysfs_emit(buf, "%d\n", value);
 }
 
 static ssize_t ltc4260_bool_show(struct device *dev,
@@ -98,7 +98,7 @@ static ssize_t ltc4260_bool_show(struct device *dev,
        if (fault)              /* Clear reported faults in chip register */
                regmap_update_bits(regmap, LTC4260_FAULT, attr->index, 0);
 
-       return snprintf(buf, PAGE_SIZE, "%d\n", !!fault);
+       return sysfs_emit(buf, "%d\n", !!fault);
 }
 
 /* Voltages */
index 1dab84b..b81e9c3 100644 (file)
@@ -130,7 +130,7 @@ static ssize_t ltc4261_value_show(struct device *dev,
                return PTR_ERR(data);
 
        value = ltc4261_get_value(data, attr->index);
-       return snprintf(buf, PAGE_SIZE, "%d\n", value);
+       return sysfs_emit(buf, "%d\n", value);
 }
 
 static ssize_t ltc4261_bool_show(struct device *dev,
@@ -147,7 +147,7 @@ static ssize_t ltc4261_bool_show(struct device *dev,
        if (fault)              /* Clear reported faults in chip register */
                i2c_smbus_write_byte_data(data->client, LTC4261_FAULT, ~fault);
 
-       return snprintf(buf, PAGE_SIZE, "%d\n", fault ? 1 : 0);
+       return sysfs_emit(buf, "%d\n", fault ? 1 : 0);
 }
 
 /*
index a26226e..ae3a6a7 100644 (file)
@@ -187,7 +187,7 @@ static ssize_t max16065_alarm_show(struct device *dev,
                i2c_smbus_write_byte_data(data->client,
                                          MAX16065_FAULT(attr2->nr), val);
 
-       return snprintf(buf, PAGE_SIZE, "%d\n", !!val);
+       return sysfs_emit(buf, "%d\n", !!val);
 }
 
 static ssize_t max16065_input_show(struct device *dev,
@@ -200,8 +200,8 @@ static ssize_t max16065_input_show(struct device *dev,
        if (unlikely(adc < 0))
                return adc;
 
-       return snprintf(buf, PAGE_SIZE, "%d\n",
-                       ADC_TO_MV(adc, data->range[attr->index]));
+       return sysfs_emit(buf, "%d\n",
+                         ADC_TO_MV(adc, data->range[attr->index]));
 }
 
 static ssize_t max16065_current_show(struct device *dev,
@@ -212,8 +212,8 @@ static ssize_t max16065_current_show(struct device *dev,
        if (unlikely(data->curr_sense < 0))
                return data->curr_sense;
 
-       return snprintf(buf, PAGE_SIZE, "%d\n",
-                       ADC_TO_CURR(data->curr_sense, data->curr_gain));
+       return sysfs_emit(buf, "%d\n",
+                         ADC_TO_CURR(data->curr_sense, data->curr_gain));
 }
 
 static ssize_t max16065_limit_store(struct device *dev,
@@ -249,8 +249,8 @@ static ssize_t max16065_limit_show(struct device *dev,
        struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(da);
        struct max16065_data *data = dev_get_drvdata(dev);
 
-       return snprintf(buf, PAGE_SIZE, "%d\n",
-                       data->limit[attr2->nr][attr2->index]);
+       return sysfs_emit(buf, "%d\n",
+                         data->limit[attr2->nr][attr2->index]);
 }
 
 /* Construct a sensor_device_attribute structure for each register */
@@ -454,7 +454,7 @@ static struct attribute *max16065_max_attributes[] = {
 static umode_t max16065_basic_is_visible(struct kobject *kobj,
                                         struct attribute *a, int n)
 {
-       struct device *dev = container_of(kobj, struct device, kobj);
+       struct device *dev = kobj_to_dev(kobj);
        struct max16065_data *data = dev_get_drvdata(dev);
        int index = n / 4;
 
@@ -466,7 +466,7 @@ static umode_t max16065_basic_is_visible(struct kobject *kobj,
 static umode_t max16065_secondary_is_visible(struct kobject *kobj,
                                             struct attribute *a, int index)
 {
-       struct device *dev = container_of(kobj, struct device, kobj);
+       struct device *dev = kobj_to_dev(kobj);
        struct max16065_data *data = dev_get_drvdata(dev);
 
        if (index >= data->num_adc)
index fc32411..2895cea 100644 (file)
@@ -460,7 +460,7 @@ static DEVICE_ATTR(dummy, 0, NULL, NULL);
 static umode_t max6697_is_visible(struct kobject *kobj, struct attribute *attr,
                                  int index)
 {
-       struct device *dev = container_of(kobj, struct device, kobj);
+       struct device *dev = kobj_to_dev(kobj);
        struct max6697_data *data = dev_get_drvdata(dev);
        const struct max6697_chip_data *chip = data->chip;
        int channel = index / 7;        /* channel number */
index ed8d59d..116681f 100644 (file)
  * @connected: indicates if tachometer is connected;
  * @reg: register offset;
  * @mask: fault mask;
+ * @prsnt: present register offset;
  */
 struct mlxreg_fan_tacho {
        bool connected;
        u32 reg;
        u32 mask;
+       u32 prsnt;
 };
 
 /*
@@ -92,6 +94,7 @@ struct mlxreg_fan_pwm {
  * @regmap: register map of parent device;
  * @tacho: tachometer data;
  * @pwm: PWM data;
+ * @tachos_per_drwr - number of tachometers per drawer;
  * @samples: minimum allowed samples per pulse;
  * @divider: divider value for tachometer RPM calculation;
  * @cooling: cooling device levels;
@@ -103,6 +106,7 @@ struct mlxreg_fan {
        struct mlxreg_core_platform_data *pdata;
        struct mlxreg_fan_tacho tacho[MLXREG_FAN_MAX_TACHO];
        struct mlxreg_fan_pwm pwm;
+       int tachos_per_drwr;
        int samples;
        int divider;
        u8 cooling_levels[MLXREG_FAN_MAX_STATE + 1];
@@ -123,6 +127,26 @@ mlxreg_fan_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
                tacho = &fan->tacho[channel];
                switch (attr) {
                case hwmon_fan_input:
+                       /*
+                        * Check FAN presence: FAN related bit in presence register is one,
+                        * if FAN is physically connected, zero - otherwise.
+                        */
+                       if (tacho->prsnt && fan->tachos_per_drwr) {
+                               err = regmap_read(fan->regmap, tacho->prsnt, &regval);
+                               if (err)
+                                       return err;
+
+                               /*
+                                * Map channel to presence bit - drawer can be equipped with
+                                * one or few FANs, while presence is indicated per drawer.
+                                */
+                               if (BIT(channel / fan->tachos_per_drwr) & regval) {
+                                       /* FAN is not connected - return zero for FAN speed. */
+                                       *val = 0;
+                                       return 0;
+                               }
+                       }
+
                        err = regmap_read(fan->regmap, tacho->reg, &regval);
                        if (err)
                                return err;
@@ -389,8 +413,8 @@ static int mlxreg_fan_config(struct mlxreg_fan *fan,
                             struct mlxreg_core_platform_data *pdata)
 {
        struct mlxreg_core_data *data = pdata->data;
+       int tacho_num = 0, tacho_avail = 0, i;
        bool configured = false;
-       int tacho_num = 0, i;
        int err;
 
        fan->samples = MLXREG_FAN_TACHO_SAMPLES_PER_PULSE_DEF;
@@ -415,7 +439,9 @@ static int mlxreg_fan_config(struct mlxreg_fan *fan,
 
                        fan->tacho[tacho_num].reg = data->reg;
                        fan->tacho[tacho_num].mask = data->mask;
+                       fan->tacho[tacho_num].prsnt = data->reg_prsnt;
                        fan->tacho[tacho_num++].connected = true;
+                       tacho_avail++;
                } else if (strnstr(data->label, "pwm", sizeof(data->label))) {
                        if (fan->pwm.connected) {
                                dev_err(fan->dev, "duplicate pwm entry: %s\n",
@@ -453,6 +479,29 @@ static int mlxreg_fan_config(struct mlxreg_fan *fan,
                }
        }
 
+       if (pdata->capability) {
+               int drwr_avail;
+               u32 regval;
+
+               /* Obtain the number of FAN drawers, supported by system. */
+               err = regmap_read(fan->regmap, pdata->capability, &regval);
+               if (err) {
+                       dev_err(fan->dev, "Failed to query capability register 0x%08x\n",
+                               pdata->capability);
+                       return err;
+               }
+
+               drwr_avail = hweight32(regval);
+               if (!tacho_avail || !drwr_avail || tacho_avail < drwr_avail) {
+                       dev_err(fan->dev, "Configuration is invalid: drawers num %d tachos num %d\n",
+                               drwr_avail, tacho_avail);
+                       return -EINVAL;
+               }
+
+               /* Set the number of tachometers per one drawer. */
+               fan->tachos_per_drwr = tacho_avail / drwr_avail;
+       }
+
        /* Init cooling levels per PWM state. */
        for (i = 0; i < MLXREG_FAN_SPEED_MIN_LEVEL; i++)
                fan->cooling_levels[i] = MLXREG_FAN_SPEED_MIN_LEVEL;
index a23047a..35f8635 100644 (file)
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 /*
  * nct6683 - Driver for the hardware monitoring functionality of
- *          Nuvoton NCT6683D/NCT6687D eSIO
+ *          Nuvoton NCT6683D/NCT6686D/NCT6687D eSIO
  *
  * Copyright (C) 2013  Guenter Roeck <linux@roeck-us.net>
  *
@@ -12,6 +12,7 @@
  *
  * Chip        #vin    #fan    #pwm    #temp  chip ID
  * nct6683d     21(1)   16      8       32(1) 0xc730
+ * nct6686d     21(1)   16      8       32(1) 0xd440
  * nct6687d     21(1)   16      8       32(1) 0xd590
  *
  * Notes:
@@ -33,7 +34,7 @@
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 
-enum kinds { nct6683, nct6687 };
+enum kinds { nct6683, nct6686, nct6687 };
 
 static bool force;
 module_param(force, bool, 0);
@@ -41,11 +42,13 @@ MODULE_PARM_DESC(force, "Set to one to enable support for unknown vendors");
 
 static const char * const nct6683_device_names[] = {
        "nct6683",
+       "nct6686",
        "nct6687",
 };
 
 static const char * const nct6683_chip_names[] = {
        "NCT6683D",
+       "NCT6686D",
        "NCT6687D",
 };
 
@@ -66,6 +69,7 @@ static const char * const nct6683_chip_names[] = {
 
 #define SIO_NCT6681_ID         0xb270  /* for later */
 #define SIO_NCT6683_ID         0xc730
+#define SIO_NCT6686_ID         0xd440
 #define SIO_NCT6687_ID         0xd590
 #define SIO_ID_MASK            0xFFF0
 
@@ -488,17 +492,6 @@ static inline long in_from_reg(u16 reg, u8 src)
        return reg * scale;
 }
 
-static inline u16 in_to_reg(u32 val, u8 src)
-{
-       int scale = 16;
-
-       if (src == MON_SRC_VCC || src == MON_SRC_VSB || src == MON_SRC_AVSB ||
-           src == MON_SRC_VBAT)
-               scale <<= 1;
-
-       return clamp_val(DIV_ROUND_CLOSEST(val, scale), 0, 127);
-}
-
 static u16 nct6683_read(struct nct6683_data *data, u16 reg)
 {
        int res;
@@ -1362,6 +1355,9 @@ static int __init nct6683_find(int sioaddr, struct nct6683_sio_data *sio_data)
        case SIO_NCT6683_ID:
                sio_data->kind = nct6683;
                break;
+       case SIO_NCT6686_ID:
+               sio_data->kind = nct6686;
+               break;
        case SIO_NCT6687_ID:
                sio_data->kind = nct6687;
                break;
diff --git a/drivers/hwmon/nzxt-kraken2.c b/drivers/hwmon/nzxt-kraken2.c
new file mode 100644 (file)
index 0000000..89f7ea4
--- /dev/null
@@ -0,0 +1,234 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * nzxt-kraken2.c - hwmon driver for NZXT Kraken X42/X52/X62/X72 coolers
+ *
+ * The device asynchronously sends HID reports (with id 0x04) twice a second to
+ * communicate current fan speed, pump speed and coolant temperature.  The
+ * device does not respond to Get_Report requests for this status report.
+ *
+ * Copyright 2019-2021  Jonas Malaco <jonas@protocubo.io>
+ */
+
+#include <asm/unaligned.h>
+#include <linux/hid.h>
+#include <linux/hwmon.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+
+#define STATUS_REPORT_ID       0x04
+#define STATUS_VALIDITY                2 /* seconds; equivalent to 4 missed updates */
+
+static const char *const kraken2_temp_label[] = {
+       "Coolant",
+};
+
+static const char *const kraken2_fan_label[] = {
+       "Fan",
+       "Pump",
+};
+
+struct kraken2_priv_data {
+       struct hid_device *hid_dev;
+       struct device *hwmon_dev;
+       s32 temp_input[1];
+       u16 fan_input[2];
+       unsigned long updated; /* jiffies */
+};
+
+static umode_t kraken2_is_visible(const void *data,
+                                 enum hwmon_sensor_types type,
+                                 u32 attr, int channel)
+{
+       return 0444;
+}
+
+static int kraken2_read(struct device *dev, enum hwmon_sensor_types type,
+                       u32 attr, int channel, long *val)
+{
+       struct kraken2_priv_data *priv = dev_get_drvdata(dev);
+
+       if (time_after(jiffies, priv->updated + STATUS_VALIDITY * HZ))
+               return -ENODATA;
+
+       switch (type) {
+       case hwmon_temp:
+               *val = priv->temp_input[channel];
+               break;
+       case hwmon_fan:
+               *val = priv->fan_input[channel];
+               break;
+       default:
+               return -EOPNOTSUPP; /* unreachable */
+       }
+
+       return 0;
+}
+
+static int kraken2_read_string(struct device *dev, enum hwmon_sensor_types type,
+                              u32 attr, int channel, const char **str)
+{
+       switch (type) {
+       case hwmon_temp:
+               *str = kraken2_temp_label[channel];
+               break;
+       case hwmon_fan:
+               *str = kraken2_fan_label[channel];
+               break;
+       default:
+               return -EOPNOTSUPP; /* unreachable */
+       }
+       return 0;
+}
+
+static const struct hwmon_ops kraken2_hwmon_ops = {
+       .is_visible = kraken2_is_visible,
+       .read = kraken2_read,
+       .read_string = kraken2_read_string,
+};
+
+static const struct hwmon_channel_info *kraken2_info[] = {
+       HWMON_CHANNEL_INFO(temp,
+                          HWMON_T_INPUT | HWMON_T_LABEL),
+       HWMON_CHANNEL_INFO(fan,
+                          HWMON_F_INPUT | HWMON_F_LABEL,
+                          HWMON_F_INPUT | HWMON_F_LABEL),
+       NULL
+};
+
+static const struct hwmon_chip_info kraken2_chip_info = {
+       .ops = &kraken2_hwmon_ops,
+       .info = kraken2_info,
+};
+
+static int kraken2_raw_event(struct hid_device *hdev,
+                            struct hid_report *report, u8 *data, int size)
+{
+       struct kraken2_priv_data *priv;
+
+       if (size < 7 || report->id != STATUS_REPORT_ID)
+               return 0;
+
+       priv = hid_get_drvdata(hdev);
+
+       /*
+        * The fractional byte of the coolant temperature has been observed to
+        * be in the interval [1,9], but some of these steps are also
+        * consistently skipped for certain integer parts.
+        *
+        * For the lack of a better idea, assume that the resolution is 0.1°C,
+        * and that the missing steps are artifacts of how the firmware
+        * processes the raw sensor data.
+        */
+       priv->temp_input[0] = data[1] * 1000 + data[2] * 100;
+
+       priv->fan_input[0] = get_unaligned_be16(data + 3);
+       priv->fan_input[1] = get_unaligned_be16(data + 5);
+
+       priv->updated = jiffies;
+
+       return 0;
+}
+
+static int kraken2_probe(struct hid_device *hdev,
+                        const struct hid_device_id *id)
+{
+       struct kraken2_priv_data *priv;
+       int ret;
+
+       priv = devm_kzalloc(&hdev->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->hid_dev = hdev;
+       hid_set_drvdata(hdev, priv);
+
+       /*
+        * Initialize ->updated to STATUS_VALIDITY seconds in the past, making
+        * the initial empty data invalid for kraken2_read without the need for
+        * a special case there.
+        */
+       priv->updated = jiffies - STATUS_VALIDITY * HZ;
+
+       ret = hid_parse(hdev);
+       if (ret) {
+               hid_err(hdev, "hid parse failed with %d\n", ret);
+               return ret;
+       }
+
+       /*
+        * Enable hidraw so existing user-space tools can continue to work.
+        */
+       ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
+       if (ret) {
+               hid_err(hdev, "hid hw start failed with %d\n", ret);
+               goto fail_and_stop;
+       }
+
+       ret = hid_hw_open(hdev);
+       if (ret) {
+               hid_err(hdev, "hid hw open failed with %d\n", ret);
+               goto fail_and_close;
+       }
+
+       priv->hwmon_dev = hwmon_device_register_with_info(&hdev->dev, "kraken2",
+                                                         priv, &kraken2_chip_info,
+                                                         NULL);
+       if (IS_ERR(priv->hwmon_dev)) {
+               ret = PTR_ERR(priv->hwmon_dev);
+               hid_err(hdev, "hwmon registration failed with %d\n", ret);
+               goto fail_and_close;
+       }
+
+       return 0;
+
+fail_and_close:
+       hid_hw_close(hdev);
+fail_and_stop:
+       hid_hw_stop(hdev);
+       return ret;
+}
+
+static void kraken2_remove(struct hid_device *hdev)
+{
+       struct kraken2_priv_data *priv = hid_get_drvdata(hdev);
+
+       hwmon_device_unregister(priv->hwmon_dev);
+
+       hid_hw_close(hdev);
+       hid_hw_stop(hdev);
+}
+
+static const struct hid_device_id kraken2_table[] = {
+       { HID_USB_DEVICE(0x1e71, 0x170e) }, /* NZXT Kraken X42/X52/X62/X72 */
+       { }
+};
+
+MODULE_DEVICE_TABLE(hid, kraken2_table);
+
+static struct hid_driver kraken2_driver = {
+       .name = "nzxt-kraken2",
+       .id_table = kraken2_table,
+       .probe = kraken2_probe,
+       .remove = kraken2_remove,
+       .raw_event = kraken2_raw_event,
+};
+
+static int __init kraken2_init(void)
+{
+       return hid_register_driver(&kraken2_driver);
+}
+
+static void __exit kraken2_exit(void)
+{
+       hid_unregister_driver(&kraken2_driver);
+}
+
+/*
+ * When compiled into the kernel, initialize after the hid bus.
+ */
+late_initcall(kraken2_init);
+module_exit(kraken2_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jonas Malaco <jonas@protocubo.io>");
+MODULE_DESCRIPTION("Hwmon driver for NZXT Kraken X42/X52/X62/X72 coolers");
index 7a5e539..f1ac153 100644 (file)
@@ -261,7 +261,7 @@ static ssize_t occ_show_temp_1(struct device *dev,
                return -EINVAL;
        }
 
-       return snprintf(buf, PAGE_SIZE - 1, "%u\n", val);
+       return sysfs_emit(buf, "%u\n", val);
 }
 
 static ssize_t occ_show_temp_2(struct device *dev,
@@ -312,7 +312,7 @@ static ssize_t occ_show_temp_2(struct device *dev,
                return -EINVAL;
        }
 
-       return snprintf(buf, PAGE_SIZE - 1, "%u\n", val);
+       return sysfs_emit(buf, "%u\n", val);
 }
 
 static ssize_t occ_show_temp_10(struct device *dev,
@@ -366,7 +366,7 @@ static ssize_t occ_show_temp_10(struct device *dev,
                return -EINVAL;
        }
 
-       return snprintf(buf, PAGE_SIZE - 1, "%u\n", val);
+       return sysfs_emit(buf, "%u\n", val);
 }
 
 static ssize_t occ_show_freq_1(struct device *dev,
@@ -396,7 +396,7 @@ static ssize_t occ_show_freq_1(struct device *dev,
                return -EINVAL;
        }
 
-       return snprintf(buf, PAGE_SIZE - 1, "%u\n", val);
+       return sysfs_emit(buf, "%u\n", val);
 }
 
 static ssize_t occ_show_freq_2(struct device *dev,
@@ -426,7 +426,7 @@ static ssize_t occ_show_freq_2(struct device *dev,
                return -EINVAL;
        }
 
-       return snprintf(buf, PAGE_SIZE - 1, "%u\n", val);
+       return sysfs_emit(buf, "%u\n", val);
 }
 
 static ssize_t occ_show_power_1(struct device *dev,
@@ -465,7 +465,7 @@ static ssize_t occ_show_power_1(struct device *dev,
                return -EINVAL;
        }
 
-       return snprintf(buf, PAGE_SIZE - 1, "%llu\n", val);
+       return sysfs_emit(buf, "%llu\n", val);
 }
 
 static u64 occ_get_powr_avg(u64 *accum, u32 *samples)
@@ -494,9 +494,9 @@ static ssize_t occ_show_power_2(struct device *dev,
 
        switch (sattr->nr) {
        case 0:
-               return snprintf(buf, PAGE_SIZE - 1, "%u_%u_%u\n",
-                               get_unaligned_be32(&power->sensor_id),
-                               power->function_id, power->apss_channel);
+               return sysfs_emit(buf, "%u_%u_%u\n",
+                                 get_unaligned_be32(&power->sensor_id),
+                                 power->function_id, power->apss_channel);
        case 1:
                val = occ_get_powr_avg(&power->accumulator,
                                       &power->update_tag);
@@ -512,7 +512,7 @@ static ssize_t occ_show_power_2(struct device *dev,
                return -EINVAL;
        }
 
-       return snprintf(buf, PAGE_SIZE - 1, "%llu\n", val);
+       return sysfs_emit(buf, "%llu\n", val);
 }
 
 static ssize_t occ_show_power_a0(struct device *dev,
@@ -533,8 +533,8 @@ static ssize_t occ_show_power_a0(struct device *dev,
 
        switch (sattr->nr) {
        case 0:
-               return snprintf(buf, PAGE_SIZE - 1, "%u_system\n",
-                               get_unaligned_be32(&power->sensor_id));
+               return sysfs_emit(buf, "%u_system\n",
+                                 get_unaligned_be32(&power->sensor_id));
        case 1:
                val = occ_get_powr_avg(&power->system.accumulator,
                                       &power->system.update_tag);
@@ -547,8 +547,8 @@ static ssize_t occ_show_power_a0(struct device *dev,
                val = get_unaligned_be16(&power->system.value) * 1000000ULL;
                break;
        case 4:
-               return snprintf(buf, PAGE_SIZE - 1, "%u_proc\n",
-                               get_unaligned_be32(&power->sensor_id));
+               return sysfs_emit(buf, "%u_proc\n",
+                                 get_unaligned_be32(&power->sensor_id));
        case 5:
                val = occ_get_powr_avg(&power->proc.accumulator,
                                       &power->proc.update_tag);
@@ -561,8 +561,8 @@ static ssize_t occ_show_power_a0(struct device *dev,
                val = get_unaligned_be16(&power->proc.value) * 1000000ULL;
                break;
        case 8:
-               return snprintf(buf, PAGE_SIZE - 1, "%u_vdd\n",
-                               get_unaligned_be32(&power->sensor_id));
+               return sysfs_emit(buf, "%u_vdd\n",
+                                 get_unaligned_be32(&power->sensor_id));
        case 9:
                val = occ_get_powr_avg(&power->vdd.accumulator,
                                       &power->vdd.update_tag);
@@ -575,8 +575,8 @@ static ssize_t occ_show_power_a0(struct device *dev,
                val = get_unaligned_be16(&power->vdd.value) * 1000000ULL;
                break;
        case 12:
-               return snprintf(buf, PAGE_SIZE - 1, "%u_vdn\n",
-                               get_unaligned_be32(&power->sensor_id));
+               return sysfs_emit(buf, "%u_vdn\n",
+                                 get_unaligned_be32(&power->sensor_id));
        case 13:
                val = occ_get_powr_avg(&power->vdn.accumulator,
                                       &power->vdn.update_tag);
@@ -592,7 +592,7 @@ static ssize_t occ_show_power_a0(struct device *dev,
                return -EINVAL;
        }
 
-       return snprintf(buf, PAGE_SIZE - 1, "%llu\n", val);
+       return sysfs_emit(buf, "%llu\n", val);
 }
 
 static ssize_t occ_show_caps_1_2(struct device *dev,
@@ -613,7 +613,7 @@ static ssize_t occ_show_caps_1_2(struct device *dev,
 
        switch (sattr->nr) {
        case 0:
-               return snprintf(buf, PAGE_SIZE - 1, "system\n");
+               return sysfs_emit(buf, "system\n");
        case 1:
                val = get_unaligned_be16(&caps->cap) * 1000000ULL;
                break;
@@ -642,7 +642,7 @@ static ssize_t occ_show_caps_1_2(struct device *dev,
                return -EINVAL;
        }
 
-       return snprintf(buf, PAGE_SIZE - 1, "%llu\n", val);
+       return sysfs_emit(buf, "%llu\n", val);
 }
 
 static ssize_t occ_show_caps_3(struct device *dev,
@@ -663,7 +663,7 @@ static ssize_t occ_show_caps_3(struct device *dev,
 
        switch (sattr->nr) {
        case 0:
-               return snprintf(buf, PAGE_SIZE - 1, "system\n");
+               return sysfs_emit(buf, "system\n");
        case 1:
                val = get_unaligned_be16(&caps->cap) * 1000000ULL;
                break;
@@ -689,7 +689,7 @@ static ssize_t occ_show_caps_3(struct device *dev,
                return -EINVAL;
        }
 
-       return snprintf(buf, PAGE_SIZE - 1, "%llu\n", val);
+       return sysfs_emit(buf, "%llu\n", val);
 }
 
 static ssize_t occ_store_caps_user(struct device *dev,
@@ -732,21 +732,22 @@ static ssize_t occ_show_extended(struct device *dev,
 
        switch (sattr->nr) {
        case 0:
-               if (extn->flags & EXTN_FLAG_SENSOR_ID)
-                       rc = snprintf(buf, PAGE_SIZE - 1, "%u",
-                                     get_unaligned_be32(&extn->sensor_id));
-               else
-                       rc = snprintf(buf, PAGE_SIZE - 1, "%02x%02x%02x%02x\n",
-                                     extn->name[0], extn->name[1],
-                                     extn->name[2], extn->name[3]);
+               if (extn->flags & EXTN_FLAG_SENSOR_ID) {
+                       rc = sysfs_emit(buf, "%u",
+                                       get_unaligned_be32(&extn->sensor_id));
+               } else {
+                       rc = sysfs_emit(buf, "%02x%02x%02x%02x\n",
+                                       extn->name[0], extn->name[1],
+                                       extn->name[2], extn->name[3]);
+               }
                break;
        case 1:
-               rc = snprintf(buf, PAGE_SIZE - 1, "%02x\n", extn->flags);
+               rc = sysfs_emit(buf, "%02x\n", extn->flags);
                break;
        case 2:
-               rc = snprintf(buf, PAGE_SIZE - 1, "%02x%02x%02x%02x%02x%02x\n",
-                             extn->data[0], extn->data[1], extn->data[2],
-                             extn->data[3], extn->data[4], extn->data[5]);
+               rc = sysfs_emit(buf, "%02x%02x%02x%02x%02x%02x\n",
+                               extn->data[0], extn->data[1], extn->data[2],
+                               extn->data[3], extn->data[4], extn->data[5]);
                break;
        default:
                return -EINVAL;
index c73be07..03b16ab 100644 (file)
@@ -67,7 +67,7 @@ static ssize_t occ_sysfs_show(struct device *dev,
                return -EINVAL;
        }
 
-       return snprintf(buf, PAGE_SIZE - 1, "%d\n", val);
+       return sysfs_emit(buf, "%d\n", val);
 }
 
 static ssize_t occ_error_show(struct device *dev,
@@ -77,7 +77,7 @@ static ssize_t occ_error_show(struct device *dev,
 
        occ_update_response(occ);
 
-       return snprintf(buf, PAGE_SIZE - 1, "%d\n", occ->error);
+       return sysfs_emit(buf, "%d\n", occ->error);
 }
 
 static SENSOR_DEVICE_ATTR(occ_master, 0444, occ_sysfs_show, NULL, 0);
index 32d2fc8..37a5c39 100644 (file)
@@ -56,6 +56,25 @@ config SENSORS_BEL_PFE
          This driver can also be built as a module. If so, the module will
          be called bel-pfe.
 
+config SENSORS_BPA_RS600
+       tristate "BluTek BPA-RS600 Power Supplies"
+       help
+         If you say yes here you get hardware monitoring support for BluTek
+         BPA-RS600 Power Supplies.
+
+         This driver can also be built as a module. If so, the module will
+         be called bpa-rs600.
+
+config SENSORS_FSP_3Y
+       tristate "FSP/3Y-Power power supplies"
+       help
+         If you say yes here you get hardware monitoring support for
+         FSP/3Y-Power hot-swap power supplies.
+         Supported models: YH-5151E, YM-2151E
+
+         This driver can also be built as a module. If so, the module will
+         be called fsp-3y.
+
 config SENSORS_IBM_CFFPS
        tristate "IBM Common Form Factor Power Supply"
        depends on LEDS_CLASS
@@ -84,6 +103,15 @@ config SENSORS_IR35221
          This driver can also be built as a module. If so, the module will
          be called ir35221.
 
+config SENSORS_IR36021
+       tristate "Infineon IR36021"
+       help
+         If you say yes here you get hardware monitoring support for Infineon
+         IR36021.
+
+         This driver can also be built as a module. If so, the module will
+         be called ir36021.
+
 config SENSORS_IR38064
        tristate "Infineon IR38064"
        help
@@ -148,6 +176,15 @@ config SENSORS_LTC3815
          This driver can also be built as a module. If so, the module will
          be called ltc3815.
 
+config SENSORS_MAX15301
+       tristate "Maxim MAX15301"
+       help
+         If you say yes here you get hardware monitoring support for Maxim
+         MAX15301, as well as for Flex BMR461.
+
+         This driver can also be built as a module. If so, the module will
+         be called max15301.
+
 config SENSORS_MAX16064
        tristate "Maxim MAX16064"
        help
@@ -247,6 +284,16 @@ config SENSORS_Q54SJ108A2
          This driver can also be built as a module. If so, the module will
          be called q54sj108a2.
 
+config SENSORS_STPDDC60
+       tristate "ST STPDDC60"
+       help
+         If you say yes here you get hardware monitoring support for ST
+         STPDDC60 Universal Digital Multicell Controller, as well as for
+         Flex BMR481.
+
+         This driver can also be built as a module. If so, the module will
+         be called stpddc60.
+
 config SENSORS_TPS40422
        tristate "TI TPS40422"
        help
@@ -257,10 +304,10 @@ config SENSORS_TPS40422
          be called tps40422.
 
 config SENSORS_TPS53679
-       tristate "TI TPS53647, TPS53667, TPS53679, TPS53681, TPS53688"
+       tristate "TI TPS53647, TPS53667, TPS53676, TPS53679, TPS53681, TPS53688"
        help
          If you say yes here you get hardware monitoring support for TI
-         TPS53647, TPS53667, TPS53679, TPS53681, and TPS53688.
+         TPS53647, TPS53667, TPS53676, TPS53679, TPS53681, and TPS53688.
 
          This driver can also be built as a module. If so, the module will
          be called tps53679.
index 6a4ba0f..f8dcc27 100644 (file)
@@ -8,15 +8,19 @@ obj-$(CONFIG_SENSORS_PMBUS)   += pmbus.o
 obj-$(CONFIG_SENSORS_ADM1266)  += adm1266.o
 obj-$(CONFIG_SENSORS_ADM1275)  += adm1275.o
 obj-$(CONFIG_SENSORS_BEL_PFE)  += bel-pfe.o
+obj-$(CONFIG_SENSORS_BPA_RS600)        += bpa-rs600.o
+obj-$(CONFIG_SENSORS_FSP_3Y)   += fsp-3y.o
 obj-$(CONFIG_SENSORS_IBM_CFFPS)        += ibm-cffps.o
 obj-$(CONFIG_SENSORS_INSPUR_IPSPS) += inspur-ipsps.o
 obj-$(CONFIG_SENSORS_IR35221)  += ir35221.o
+obj-$(CONFIG_SENSORS_IR36021)  += ir36021.o
 obj-$(CONFIG_SENSORS_IR38064)  += ir38064.o
 obj-$(CONFIG_SENSORS_IRPS5401) += irps5401.o
 obj-$(CONFIG_SENSORS_ISL68137) += isl68137.o
 obj-$(CONFIG_SENSORS_LM25066)  += lm25066.o
 obj-$(CONFIG_SENSORS_LTC2978)  += ltc2978.o
 obj-$(CONFIG_SENSORS_LTC3815)  += ltc3815.o
+obj-$(CONFIG_SENSORS_MAX15301) += max15301.o
 obj-$(CONFIG_SENSORS_MAX16064) += max16064.o
 obj-$(CONFIG_SENSORS_MAX16601) += max16601.o
 obj-$(CONFIG_SENSORS_MAX20730) += max20730.o
@@ -28,6 +32,7 @@ obj-$(CONFIG_SENSORS_MP2975)  += mp2975.o
 obj-$(CONFIG_SENSORS_PM6764TR) += pm6764tr.o
 obj-$(CONFIG_SENSORS_PXE1610)  += pxe1610.o
 obj-$(CONFIG_SENSORS_Q54SJ108A2)       += q54sj108a2.o
+obj-$(CONFIG_SENSORS_STPDDC60) += stpddc60.o
 obj-$(CONFIG_SENSORS_TPS40422) += tps40422.o
 obj-$(CONFIG_SENSORS_TPS53679) += tps53679.o
 obj-$(CONFIG_SENSORS_UCD9000)  += ucd9000.o
index 4d2e4dd..ec5f932 100644 (file)
@@ -510,3 +510,4 @@ module_i2c_driver(adm1266_driver);
 MODULE_AUTHOR("Alexandru Tachici <alexandru.tachici@analog.com>");
 MODULE_DESCRIPTION("PMBus driver for Analog Devices ADM1266");
 MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(PMBUS);
index 38a6515..980a385 100644 (file)
@@ -805,3 +805,4 @@ module_i2c_driver(adm1275_driver);
 MODULE_AUTHOR("Guenter Roeck");
 MODULE_DESCRIPTION("PMBus driver for Analog Devices ADM1275 and compatibles");
 MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PMBUS);
index aed7542..4100eef 100644 (file)
@@ -129,3 +129,4 @@ module_i2c_driver(pfe_pmbus_driver);
 MODULE_AUTHOR("Tao Ren <rentao.bupt@gmail.com>");
 MODULE_DESCRIPTION("PMBus driver for BEL PFE Family Power Supplies");
 MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PMBUS);
diff --git a/drivers/hwmon/pmbus/bpa-rs600.c b/drivers/hwmon/pmbus/bpa-rs600.c
new file mode 100644 (file)
index 0000000..f6558ee
--- /dev/null
@@ -0,0 +1,173 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Hardware monitoring driver for BluTek BPA-RS600 Power Supplies
+ *
+ * Copyright 2021 Allied Telesis Labs
+ */
+
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pmbus.h>
+#include "pmbus.h"
+
+#define BPARS600_MFR_VIN_MIN   0xa0
+#define BPARS600_MFR_VIN_MAX   0xa1
+#define BPARS600_MFR_IIN_MAX   0xa2
+#define BPARS600_MFR_PIN_MAX   0xa3
+#define BPARS600_MFR_VOUT_MIN  0xa4
+#define BPARS600_MFR_VOUT_MAX  0xa5
+#define BPARS600_MFR_IOUT_MAX  0xa6
+#define BPARS600_MFR_POUT_MAX  0xa7
+
+static int bpa_rs600_read_byte_data(struct i2c_client *client, int page, int reg)
+{
+       int ret;
+
+       if (page > 0)
+               return -ENXIO;
+
+       switch (reg) {
+       case PMBUS_FAN_CONFIG_12:
+               /*
+                * Two fans are reported in PMBUS_FAN_CONFIG_12 but there is
+                * only one fan in the module. Mask out the FAN2 bits.
+                */
+               ret = pmbus_read_byte_data(client, 0, PMBUS_FAN_CONFIG_12);
+               if (ret >= 0)
+                       ret &= ~(PB_FAN_2_INSTALLED | PB_FAN_2_PULSE_MASK);
+               break;
+       default:
+               ret = -ENODATA;
+               break;
+       }
+
+       return ret;
+}
+
+static int bpa_rs600_read_word_data(struct i2c_client *client, int page, int phase, int reg)
+{
+       int ret;
+
+       if (page > 0)
+               return -ENXIO;
+
+       switch (reg) {
+       case PMBUS_VIN_UV_WARN_LIMIT:
+               ret = pmbus_read_word_data(client, 0, 0xff, BPARS600_MFR_VIN_MIN);
+               break;
+       case PMBUS_VIN_OV_WARN_LIMIT:
+               ret = pmbus_read_word_data(client, 0, 0xff, BPARS600_MFR_VIN_MAX);
+               break;
+       case PMBUS_VOUT_UV_WARN_LIMIT:
+               ret = pmbus_read_word_data(client, 0, 0xff, BPARS600_MFR_VOUT_MIN);
+               break;
+       case PMBUS_VOUT_OV_WARN_LIMIT:
+               ret = pmbus_read_word_data(client, 0, 0xff, BPARS600_MFR_VOUT_MAX);
+               break;
+       case PMBUS_IIN_OC_WARN_LIMIT:
+               ret = pmbus_read_word_data(client, 0, 0xff, BPARS600_MFR_IIN_MAX);
+               break;
+       case PMBUS_IOUT_OC_WARN_LIMIT:
+               ret = pmbus_read_word_data(client, 0, 0xff, BPARS600_MFR_IOUT_MAX);
+               break;
+       case PMBUS_PIN_OP_WARN_LIMIT:
+               ret = pmbus_read_word_data(client, 0, 0xff, BPARS600_MFR_PIN_MAX);
+               break;
+       case PMBUS_POUT_OP_WARN_LIMIT:
+               ret = pmbus_read_word_data(client, 0, 0xff, BPARS600_MFR_POUT_MAX);
+               break;
+       case PMBUS_VIN_UV_FAULT_LIMIT:
+       case PMBUS_VIN_OV_FAULT_LIMIT:
+       case PMBUS_VOUT_UV_FAULT_LIMIT:
+       case PMBUS_VOUT_OV_FAULT_LIMIT:
+               /* These commands return data but it is invalid/un-documented */
+               ret = -ENXIO;
+               break;
+       default:
+               if (reg >= PMBUS_VIRT_BASE)
+                       ret = -ENXIO;
+               else
+                       ret = -ENODATA;
+               break;
+       }
+
+       return ret;
+}
+
+static struct pmbus_driver_info bpa_rs600_info = {
+       .pages = 1,
+       .format[PSC_VOLTAGE_IN] = linear,
+       .format[PSC_VOLTAGE_OUT] = linear,
+       .format[PSC_CURRENT_IN] = linear,
+       .format[PSC_CURRENT_OUT] = linear,
+       .format[PSC_POWER] = linear,
+       .format[PSC_TEMPERATURE] = linear,
+       .format[PSC_FAN] = linear,
+       .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT |
+               PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT |
+               PMBUS_HAVE_PIN | PMBUS_HAVE_POUT |
+               PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 |
+               PMBUS_HAVE_FAN12 |
+               PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT |
+               PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP |
+               PMBUS_HAVE_STATUS_FAN12,
+       .read_byte_data = bpa_rs600_read_byte_data,
+       .read_word_data = bpa_rs600_read_word_data,
+};
+
+static int bpa_rs600_probe(struct i2c_client *client)
+{
+       struct device *dev = &client->dev;
+       u8 buf[I2C_SMBUS_BLOCK_MAX + 1];
+       int ret;
+
+       if (!i2c_check_functionality(client->adapter,
+                                    I2C_FUNC_SMBUS_READ_BYTE_DATA
+                                    | I2C_FUNC_SMBUS_READ_WORD_DATA
+                                    | I2C_FUNC_SMBUS_READ_BLOCK_DATA))
+               return -ENODEV;
+
+       ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf);
+       if (ret < 0) {
+               dev_err(dev, "Failed to read Manufacturer Model\n");
+               return ret;
+       }
+
+       if (strncmp(buf, "BPA-RS600", 8)) {
+               buf[ret] = '\0';
+               dev_err(dev, "Unsupported Manufacturer Model '%s'\n", buf);
+               return -ENODEV;
+       }
+
+       return pmbus_do_probe(client, &bpa_rs600_info);
+}
+
+static const struct i2c_device_id bpa_rs600_id[] = {
+       { "bpars600", 0 },
+       {},
+};
+MODULE_DEVICE_TABLE(i2c, bpa_rs600_id);
+
+static const struct of_device_id __maybe_unused bpa_rs600_of_match[] = {
+       { .compatible = "blutek,bpa-rs600" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, bpa_rs600_of_match);
+
+static struct i2c_driver bpa_rs600_driver = {
+       .driver = {
+               .name = "bpa-rs600",
+               .of_match_table = of_match_ptr(bpa_rs600_of_match),
+       },
+       .probe_new = bpa_rs600_probe,
+       .id_table = bpa_rs600_id,
+};
+
+module_i2c_driver(bpa_rs600_driver);
+
+MODULE_AUTHOR("Chris Packham");
+MODULE_DESCRIPTION("PMBus driver for BluTek BPA-RS600");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PMBUS);
diff --git a/drivers/hwmon/pmbus/fsp-3y.c b/drivers/hwmon/pmbus/fsp-3y.c
new file mode 100644 (file)
index 0000000..b177987
--- /dev/null
@@ -0,0 +1,254 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Hardware monitoring driver for FSP 3Y-Power PSUs
+ *
+ * Copyright (c) 2021 Václav Kubernát, CESNET
+ *
+ * This driver is mostly reverse engineered with the help of a tool called pmbus_peek written by
+ * David Brownell (and later adopted by Jan Kundrát). The device has some sort of a timing issue
+ * when switching pages, details are explained in the code. The driver support is limited. It
+ * exposes only the values, that have been tested to work correctly. Unsupported values either
+ * aren't supported by the devices or their encondings are unknown.
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include "pmbus.h"
+
+#define YM2151_PAGE_12V_LOG    0x00
+#define YM2151_PAGE_12V_REAL   0x00
+#define YM2151_PAGE_5VSB_LOG   0x01
+#define YM2151_PAGE_5VSB_REAL  0x20
+#define YH5151E_PAGE_12V_LOG   0x00
+#define YH5151E_PAGE_12V_REAL  0x00
+#define YH5151E_PAGE_5V_LOG    0x01
+#define YH5151E_PAGE_5V_REAL   0x10
+#define YH5151E_PAGE_3V3_LOG   0x02
+#define YH5151E_PAGE_3V3_REAL  0x11
+
+enum chips {
+       ym2151e,
+       yh5151e
+};
+
+struct fsp3y_data {
+       struct pmbus_driver_info info;
+       int chip;
+       int page;
+};
+
+#define to_fsp3y_data(x) container_of(x, struct fsp3y_data, info)
+
+static int page_log_to_page_real(int page_log, enum chips chip)
+{
+       switch (chip) {
+       case ym2151e:
+               switch (page_log) {
+               case YM2151_PAGE_12V_LOG:
+                       return YM2151_PAGE_12V_REAL;
+               case YM2151_PAGE_5VSB_LOG:
+                       return YM2151_PAGE_5VSB_REAL;
+               }
+               return -EINVAL;
+       case yh5151e:
+               switch (page_log) {
+               case YH5151E_PAGE_12V_LOG:
+                       return YH5151E_PAGE_12V_REAL;
+               case YH5151E_PAGE_5V_LOG:
+                       return YH5151E_PAGE_5V_LOG;
+               case YH5151E_PAGE_3V3_LOG:
+                       return YH5151E_PAGE_3V3_REAL;
+               }
+               return -EINVAL;
+       }
+
+       return -EINVAL;
+}
+
+static int set_page(struct i2c_client *client, int page_log)
+{
+       const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+       struct fsp3y_data *data = to_fsp3y_data(info);
+       int rv;
+       int page_real;
+
+       if (page_log < 0)
+               return 0;
+
+       page_real = page_log_to_page_real(page_log, data->chip);
+       if (page_real < 0)
+               return page_real;
+
+       if (data->page != page_real) {
+               rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page_real);
+               if (rv < 0)
+                       return rv;
+
+               data->page = page_real;
+
+               /*
+                * Testing showed that the device has a timing issue. After
+                * setting a page, it takes a while, before the device actually
+                * gives the correct values from the correct page. 20 ms was
+                * tested to be enough to not give wrong values (15 ms wasn't
+                * enough).
+                */
+               usleep_range(20000, 30000);
+       }
+
+       return 0;
+}
+
+static int fsp3y_read_byte_data(struct i2c_client *client, int page, int reg)
+{
+       int rv;
+
+       rv = set_page(client, page);
+       if (rv < 0)
+               return rv;
+
+       return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int fsp3y_read_word_data(struct i2c_client *client, int page, int phase, int reg)
+{
+       int rv;
+
+       /*
+        * This masks commands which weren't tested to work correctly. Some of
+        * the masked commands return 0xFFFF. These would probably get tagged as
+        * invalid by pmbus_core. Other ones do return values which might be
+        * useful (that is, they are not 0xFFFF), but their encoding is unknown,
+        * and so they are unsupported.
+        */
+       switch (reg) {
+       case PMBUS_READ_FAN_SPEED_1:
+       case PMBUS_READ_IIN:
+       case PMBUS_READ_IOUT:
+       case PMBUS_READ_PIN:
+       case PMBUS_READ_POUT:
+       case PMBUS_READ_TEMPERATURE_1:
+       case PMBUS_READ_TEMPERATURE_2:
+       case PMBUS_READ_TEMPERATURE_3:
+       case PMBUS_READ_VIN:
+       case PMBUS_READ_VOUT:
+       case PMBUS_STATUS_WORD:
+               break;
+       default:
+               return -ENXIO;
+       }
+
+       rv = set_page(client, page);
+       if (rv < 0)
+               return rv;
+
+       return i2c_smbus_read_word_data(client, reg);
+}
+
+static struct pmbus_driver_info fsp3y_info[] = {
+       [ym2151e] = {
+               .pages = 2,
+               .func[YM2151_PAGE_12V_LOG] =
+                       PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
+                       PMBUS_HAVE_PIN | PMBUS_HAVE_POUT  |
+                       PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 |
+                       PMBUS_HAVE_VIN | PMBUS_HAVE_IIN |
+                       PMBUS_HAVE_FAN12,
+               .func[YM2151_PAGE_5VSB_LOG] =
+                       PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT,
+                       PMBUS_HAVE_IIN,
+               .read_word_data = fsp3y_read_word_data,
+               .read_byte_data = fsp3y_read_byte_data,
+       },
+       [yh5151e] = {
+               .pages = 3,
+               .func[YH5151E_PAGE_12V_LOG] =
+                       PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
+                       PMBUS_HAVE_POUT  |
+                       PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3,
+               .func[YH5151E_PAGE_5V_LOG] =
+                       PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
+                       PMBUS_HAVE_POUT,
+               .func[YH5151E_PAGE_3V3_LOG] =
+                       PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT |
+                       PMBUS_HAVE_POUT,
+               .read_word_data = fsp3y_read_word_data,
+               .read_byte_data = fsp3y_read_byte_data,
+       }
+};
+
+static int fsp3y_detect(struct i2c_client *client)
+{
+       int rv;
+       u8 buf[I2C_SMBUS_BLOCK_MAX + 1];
+
+       rv = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf);
+       if (rv < 0)
+               return rv;
+
+       buf[rv] = '\0';
+
+       if (rv == 8) {
+               if (!strcmp(buf, "YM-2151E"))
+                       return ym2151e;
+               else if (!strcmp(buf, "YH-5151E"))
+                       return yh5151e;
+       }
+
+       dev_err(&client->dev, "Unsupported model %.*s\n", rv, buf);
+       return -ENODEV;
+}
+
+static const struct i2c_device_id fsp3y_id[] = {
+       {"ym2151e", ym2151e},
+       {"yh5151e", yh5151e},
+       { }
+};
+
+static int fsp3y_probe(struct i2c_client *client)
+{
+       struct fsp3y_data *data;
+       const struct i2c_device_id *id;
+       int rv;
+
+       data = devm_kzalloc(&client->dev, sizeof(struct fsp3y_data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       data->chip = fsp3y_detect(client);
+       if (data->chip < 0)
+               return data->chip;
+
+       id = i2c_match_id(fsp3y_id, client);
+       if (data->chip != id->driver_data)
+               dev_warn(&client->dev, "Device mismatch: Configured %s (%d), detected %d\n",
+                        id->name, (int)id->driver_data, data->chip);
+
+       rv = i2c_smbus_read_byte_data(client, PMBUS_PAGE);
+       if (rv < 0)
+               return rv;
+       data->page = rv;
+
+       data->info = fsp3y_info[data->chip];
+
+       return pmbus_do_probe(client, &data->info);
+}
+
+MODULE_DEVICE_TABLE(i2c, fsp3y_id);
+
+static struct i2c_driver fsp3y_driver = {
+       .driver = {
+                  .name = "fsp3y",
+                  },
+       .probe_new = fsp3y_probe,
+       .id_table = fsp3y_id
+};
+
+module_i2c_driver(fsp3y_driver);
+
+MODULE_AUTHOR("Václav Kubernát");
+MODULE_DESCRIPTION("PMBus driver for FSP/3Y-Power power supplies");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PMBUS);
index ffde5aa..5668d83 100644 (file)
@@ -625,3 +625,4 @@ module_i2c_driver(ibm_cffps_driver);
 MODULE_AUTHOR("Eddie James");
 MODULE_DESCRIPTION("PMBus driver for IBM Common Form Factor power supplies");
 MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PMBUS);
index 88c5865..0f614e8 100644 (file)
@@ -70,7 +70,7 @@ static ssize_t ipsps_string_show(struct device *dev,
        p = memscan(data, '#', rc);
        *p = '\0';
 
-       return snprintf(buf, PAGE_SIZE, "%s\n", data);
+       return sysfs_emit(buf, "%s\n", data);
 }
 
 static ssize_t ipsps_fw_version_show(struct device *dev,
@@ -91,9 +91,9 @@ static ssize_t ipsps_fw_version_show(struct device *dev,
        if (rc != 6)
                return -EPROTO;
 
-       return snprintf(buf, PAGE_SIZE, "%u.%02u%u-%u.%02u\n",
-                       data[1], data[2]/* < 100 */, data[3]/*< 10*/,
-                       data[4], data[5]/* < 100 */);
+       return sysfs_emit(buf, "%u.%02u%u-%u.%02u\n",
+                         data[1], data[2]/* < 100 */, data[3]/*< 10*/,
+                         data[4], data[5]/* < 100 */);
 }
 
 static ssize_t ipsps_mode_show(struct device *dev,
@@ -111,19 +111,19 @@ static ssize_t ipsps_mode_show(struct device *dev,
 
        switch (rc) {
        case MODE_ACTIVE:
-               return snprintf(buf, PAGE_SIZE, "[%s] %s %s\n",
-                               MODE_ACTIVE_STRING,
-                               MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING);
+               return sysfs_emit(buf, "[%s] %s %s\n",
+                                 MODE_ACTIVE_STRING,
+                                 MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING);
        case MODE_STANDBY:
-               return snprintf(buf, PAGE_SIZE, "%s [%s] %s\n",
-                               MODE_ACTIVE_STRING,
-                               MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING);
+               return sysfs_emit(buf, "%s [%s] %s\n",
+                                 MODE_ACTIVE_STRING,
+                                 MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING);
        case MODE_REDUNDANCY:
-               return snprintf(buf, PAGE_SIZE, "%s %s [%s]\n",
-                               MODE_ACTIVE_STRING,
-                               MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING);
+               return sysfs_emit(buf, "%s %s [%s]\n",
+                                 MODE_ACTIVE_STRING,
+                                 MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING);
        default:
-               return snprintf(buf, PAGE_SIZE, "unspecified\n");
+               return sysfs_emit(buf, "unspecified\n");
        }
 }
 
@@ -224,3 +224,4 @@ module_i2c_driver(ipsps_driver);
 MODULE_AUTHOR("John Wang");
 MODULE_DESCRIPTION("PMBus driver for Inspur Power System power supplies");
 MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PMBUS);
index 3aebeb1..a6cf98e 100644 (file)
@@ -145,3 +145,4 @@ module_i2c_driver(ir35221_driver);
 MODULE_AUTHOR("Samuel Mendoza-Jonas <sam@mendozajonas.com");
 MODULE_DESCRIPTION("PMBus driver for IR35221");
 MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PMBUS);
diff --git a/drivers/hwmon/pmbus/ir36021.c b/drivers/hwmon/pmbus/ir36021.c
new file mode 100644 (file)
index 0000000..4dca476
--- /dev/null
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Hardware monitoring driver for Infineon IR36021
+ *
+ * Copyright (c) 2021 Allied Telesis
+ */
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include "pmbus.h"
+
+static struct pmbus_driver_info ir36021_info = {
+       .pages = 1,
+       .format[PSC_VOLTAGE_IN] = linear,
+       .format[PSC_VOLTAGE_OUT] = linear,
+       .format[PSC_CURRENT_IN] = linear,
+       .format[PSC_CURRENT_OUT] = linear,
+       .format[PSC_POWER] = linear,
+       .format[PSC_TEMPERATURE] = linear,
+       .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT
+               | PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT
+               | PMBUS_HAVE_PIN | PMBUS_HAVE_POUT
+               | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2
+               | PMBUS_HAVE_STATUS_TEMP,
+};
+
+static int ir36021_probe(struct i2c_client *client)
+{
+       u8 buf[I2C_SMBUS_BLOCK_MAX];
+       int ret;
+
+       if (!i2c_check_functionality(client->adapter,
+                                    I2C_FUNC_SMBUS_READ_BYTE_DATA
+                                    | I2C_FUNC_SMBUS_READ_WORD_DATA
+                                    | I2C_FUNC_SMBUS_READ_BLOCK_DATA))
+               return -ENODEV;
+
+       ret = i2c_smbus_read_i2c_block_data(client, PMBUS_MFR_MODEL, 2, buf);
+       if (ret < 0) {
+               dev_err(&client->dev, "Failed to read PMBUS_MFR_MODEL\n");
+               return ret;
+       }
+       if (ret != 2 || buf[0] != 0x01 || buf[1] != 0x2d) {
+               dev_err(&client->dev, "MFR_MODEL unrecognised\n");
+               return -ENODEV;
+       }
+
+       return pmbus_do_probe(client, &ir36021_info);
+}
+
+static const struct i2c_device_id ir36021_id[] = {
+       { "ir36021", 0 },
+       {},
+};
+MODULE_DEVICE_TABLE(i2c, ir36021_id);
+
+static const struct of_device_id __maybe_unused ir36021_of_id[] = {
+       { .compatible = "infineon,ir36021" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, ir36021_of_id);
+
+static struct i2c_driver ir36021_driver = {
+       .class = I2C_CLASS_HWMON,
+       .driver = {
+               .name = "ir36021",
+               .of_match_table = of_match_ptr(ir36021_of_id),
+       },
+       .probe_new = ir36021_probe,
+       .id_table = ir36021_id,
+};
+
+module_i2c_driver(ir36021_driver);
+
+MODULE_AUTHOR("Chris Packham <chris.packham@alliedtelesis.co.nz>");
+MODULE_DESCRIPTION("PMBus driver for Infineon IR36021");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PMBUS);
index 46f17c4..1fb7f12 100644 (file)
@@ -61,3 +61,4 @@ module_i2c_driver(ir38064_driver);
 MODULE_AUTHOR("Maxim Sloyko <maxims@google.com>");
 MODULE_DESCRIPTION("PMBus driver for Infineon IR38064");
 MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PMBUS);
index 93ef6d6..de3449e 100644 (file)
@@ -63,3 +63,4 @@ module_i2c_driver(irps5401_driver);
 MODULE_AUTHOR("Robert Hancock");
 MODULE_DESCRIPTION("PMBus driver for Infineon IRPS5401");
 MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PMBUS);
index 2bee930..40597a9 100644 (file)
@@ -332,3 +332,4 @@ module_i2c_driver(isl68137_driver);
 MODULE_AUTHOR("Maxim Sloyko <maxims@google.com>");
 MODULE_DESCRIPTION("PMBus driver for Renesas digital multiphase voltage regulators");
 MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PMBUS);
index e9a66fd..d209e0a 100644 (file)
@@ -511,3 +511,4 @@ module_i2c_driver(lm25066_driver);
 MODULE_AUTHOR("Guenter Roeck");
 MODULE_DESCRIPTION("PMBus driver for LM25066 and compatible chips");
 MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PMBUS);
index 7e53fa9..0127273 100644 (file)
@@ -883,3 +883,4 @@ module_i2c_driver(ltc2978_driver);
 MODULE_AUTHOR("Guenter Roeck");
 MODULE_DESCRIPTION("PMBus driver for LTC2978 and compatible chips");
 MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PMBUS);
index e45e14d..8e13a7d 100644 (file)
@@ -208,3 +208,4 @@ module_i2c_driver(ltc3815_driver);
 MODULE_AUTHOR("Guenter Roeck");
 MODULE_DESCRIPTION("PMBus driver for LTC3815");
 MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PMBUS);
diff --git a/drivers/hwmon/pmbus/max15301.c b/drivers/hwmon/pmbus/max15301.c
new file mode 100644 (file)
index 0000000..0b6f884
--- /dev/null
@@ -0,0 +1,190 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Hardware monitoring driver for Maxim MAX15301
+ *
+ * Copyright (c) 2021 Flextronics International Sweden AB
+ *
+ * Even though the specification does not specifically mention it,
+ * extensive empirical testing has revealed that auto-detection of
+ * limit-registers will fail in a random fashion unless the delay
+ * parameter is set to above about 80us. The default delay is set
+ * to 100us to include some safety margin.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/ktime.h>
+#include <linux/delay.h>
+#include <linux/pmbus.h>
+#include "pmbus.h"
+
+static const struct i2c_device_id max15301_id[] = {
+       {"bmr461", 0},
+       {"max15301", 0},
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, max15301_id);
+
+struct max15301_data {
+       int id;
+       ktime_t access;         /* Chip access time */
+       int delay;              /* Delay between chip accesses in us */
+       struct pmbus_driver_info info;
+};
+
+#define to_max15301_data(x)  container_of(x, struct max15301_data, info)
+
+#define MAX15301_WAIT_TIME             100     /* us   */
+
+static ushort delay = MAX15301_WAIT_TIME;
+module_param(delay, ushort, 0644);
+MODULE_PARM_DESC(delay, "Delay between chip accesses in us");
+
+static struct max15301_data max15301_data = {
+       .info = {
+               .pages = 1,
+               .func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
+                       | PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT
+                       | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2
+                       | PMBUS_HAVE_STATUS_TEMP
+                       | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT,
+       }
+};
+
+/* This chip needs a delay between accesses */
+static inline void max15301_wait(const struct max15301_data *data)
+{
+       if (data->delay) {
+               s64 delta = ktime_us_delta(ktime_get(), data->access);
+
+               if (delta < data->delay)
+                       udelay(data->delay - delta);
+       }
+}
+
+static int max15301_read_word_data(struct i2c_client *client, int page,
+                                  int phase, int reg)
+{
+       const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+       struct max15301_data *data = to_max15301_data(info);
+       int ret;
+
+       if (page > 0)
+               return -ENXIO;
+
+       if (reg >= PMBUS_VIRT_BASE)
+               return -ENXIO;
+
+       max15301_wait(data);
+       ret = pmbus_read_word_data(client, page, phase, reg);
+       data->access = ktime_get();
+
+       return ret;
+}
+
+static int max15301_read_byte_data(struct i2c_client *client, int page, int reg)
+{
+       const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+       struct max15301_data *data = to_max15301_data(info);
+       int ret;
+
+       if (page > 0)
+               return -ENXIO;
+
+       max15301_wait(data);
+       ret = pmbus_read_byte_data(client, page, reg);
+       data->access = ktime_get();
+
+       return ret;
+}
+
+static int max15301_write_word_data(struct i2c_client *client, int page, int reg,
+                                   u16 word)
+{
+       const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+       struct max15301_data *data = to_max15301_data(info);
+       int ret;
+
+       if (page > 0)
+               return -ENXIO;
+
+       if (reg >= PMBUS_VIRT_BASE)
+               return -ENXIO;
+
+       max15301_wait(data);
+       ret = pmbus_write_word_data(client, page, reg, word);
+       data->access = ktime_get();
+
+       return ret;
+}
+
+static int max15301_write_byte(struct i2c_client *client, int page, u8 value)
+{
+       const struct pmbus_driver_info *info = pmbus_get_driver_info(client);
+       struct max15301_data *data = to_max15301_data(info);
+       int ret;
+
+       if (page > 0)
+               return -ENXIO;
+
+       max15301_wait(data);
+       ret = pmbus_write_byte(client, page, value);
+       data->access = ktime_get();
+
+       return ret;
+}
+
+static int max15301_probe(struct i2c_client *client)
+{
+       int status;
+       u8 device_id[I2C_SMBUS_BLOCK_MAX + 1];
+       const struct i2c_device_id *mid;
+       struct pmbus_driver_info *info = &max15301_data.info;
+
+       if (!i2c_check_functionality(client->adapter,
+                                    I2C_FUNC_SMBUS_READ_BYTE_DATA
+                                    | I2C_FUNC_SMBUS_BLOCK_DATA))
+               return -ENODEV;
+
+       status = i2c_smbus_read_block_data(client, PMBUS_IC_DEVICE_ID, device_id);
+       if (status < 0) {
+               dev_err(&client->dev, "Failed to read Device Id\n");
+               return status;
+       }
+       for (mid = max15301_id; mid->name[0]; mid++) {
+               if (!strncasecmp(mid->name, device_id, strlen(mid->name)))
+                       break;
+       }
+       if (!mid->name[0]) {
+               dev_err(&client->dev, "Unsupported device\n");
+               return -ENODEV;
+       }
+
+       max15301_data.delay = delay;
+
+       info->read_byte_data = max15301_read_byte_data;
+       info->read_word_data = max15301_read_word_data;
+       info->write_byte = max15301_write_byte;
+       info->write_word_data = max15301_write_word_data;
+
+       return pmbus_do_probe(client, info);
+}
+
+static struct i2c_driver max15301_driver = {
+       .driver = {
+                  .name = "max15301",
+                  },
+       .probe_new = max15301_probe,
+       .id_table = max15301_id,
+};
+
+module_i2c_driver(max15301_driver);
+
+MODULE_AUTHOR("Erik Rosen <erik.rosen@metormote.com>");
+MODULE_DESCRIPTION("PMBus driver for Maxim MAX15301");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PMBUS);
index d79add9..94f8690 100644 (file)
@@ -111,3 +111,4 @@ module_i2c_driver(max16064_driver);
 MODULE_AUTHOR("Guenter Roeck");
 MODULE_DESCRIPTION("PMBus driver for Maxim MAX16064");
 MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PMBUS);
index 0d1204c..5a226a5 100644 (file)
@@ -359,3 +359,4 @@ module_i2c_driver(max16601_driver);
 MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
 MODULE_DESCRIPTION("PMBus driver for Maxim MAX16601");
 MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(PMBUS);
index 9dd3dd7..ba39f03 100644 (file)
@@ -785,3 +785,4 @@ module_i2c_driver(max20730_driver);
 MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
 MODULE_DESCRIPTION("PMBus driver for Maxim MAX20710 / MAX20730 / MAX20734 / MAX20743");
 MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PMBUS);
index 9d42f82..2272dc8 100644 (file)
@@ -51,3 +51,4 @@ module_i2c_driver(max20751_driver);
 MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
 MODULE_DESCRIPTION("PMBus driver for Maxim MAX20751");
 MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PMBUS);
index 17489ab..95d79a6 100644 (file)
@@ -403,3 +403,4 @@ module_i2c_driver(max31785_driver);
 MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>");
 MODULE_DESCRIPTION("PMBus driver for the Maxim MAX31785");
 MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PMBUS);
index dad66b3..ea76090 100644 (file)
@@ -529,3 +529,4 @@ module_i2c_driver(max34440_driver);
 MODULE_AUTHOR("Guenter Roeck");
 MODULE_DESCRIPTION("PMBus driver for Maxim MAX34440/MAX34441");
 MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PMBUS);
index 329dc85..5e66c28 100644 (file)
@@ -191,3 +191,4 @@ module_i2c_driver(max8688_driver);
 MODULE_AUTHOR("Guenter Roeck");
 MODULE_DESCRIPTION("PMBus driver for Maxim MAX8688");
 MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PMBUS);
index 60fbdb3..eb94bd5 100644 (file)
@@ -766,3 +766,4 @@ module_i2c_driver(mp2975_driver);
 MODULE_AUTHOR("Vadim Pasternak <vadimp@nvidia.com>");
 MODULE_DESCRIPTION("PMBus driver for MPS MP2975 device");
 MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PMBUS);
index d97cb6d..e0bbc8a 100644 (file)
@@ -73,3 +73,4 @@ module_i2c_driver(pm6764tr_driver);
 MODULE_AUTHOR("Charles Hsu");
 MODULE_DESCRIPTION("PMBus driver for  ST PM6764TR");
 MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PMBUS);
index a1b4260..618c377 100644 (file)
@@ -246,3 +246,4 @@ module_i2c_driver(pmbus_driver);
 MODULE_AUTHOR("Guenter Roeck");
 MODULE_DESCRIPTION("Generic PMBus driver");
 MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PMBUS);
index 4c30ec8..3968924 100644 (file)
@@ -475,6 +475,7 @@ extern const struct regulator_ops pmbus_regulator_ops;
 /* Function declarations */
 
 void pmbus_clear_cache(struct i2c_client *client);
+void pmbus_set_update(struct i2c_client *client, u8 reg, bool update);
 int pmbus_set_page(struct i2c_client *client, int page, int phase);
 int pmbus_read_word_data(struct i2c_client *client, int page, int phase,
                         u8 reg);
index aadea85..bbd7451 100644 (file)
@@ -139,7 +139,18 @@ void pmbus_clear_cache(struct i2c_client *client)
        for (sensor = data->sensors; sensor; sensor = sensor->next)
                sensor->data = -ENODATA;
 }
-EXPORT_SYMBOL_GPL(pmbus_clear_cache);
+EXPORT_SYMBOL_NS_GPL(pmbus_clear_cache, PMBUS);
+
+void pmbus_set_update(struct i2c_client *client, u8 reg, bool update)
+{
+       struct pmbus_data *data = i2c_get_clientdata(client);
+       struct pmbus_sensor *sensor;
+
+       for (sensor = data->sensors; sensor; sensor = sensor->next)
+               if (sensor->reg == reg)
+                       sensor->update = update;
+}
+EXPORT_SYMBOL_NS_GPL(pmbus_set_update, PMBUS);
 
 int pmbus_set_page(struct i2c_client *client, int page, int phase)
 {
@@ -175,7 +186,7 @@ int pmbus_set_page(struct i2c_client *client, int page, int phase)
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(pmbus_set_page);
+EXPORT_SYMBOL_NS_GPL(pmbus_set_page, PMBUS);
 
 int pmbus_write_byte(struct i2c_client *client, int page, u8 value)
 {
@@ -187,7 +198,7 @@ int pmbus_write_byte(struct i2c_client *client, int page, u8 value)
 
        return i2c_smbus_write_byte(client, value);
 }
-EXPORT_SYMBOL_GPL(pmbus_write_byte);
+EXPORT_SYMBOL_NS_GPL(pmbus_write_byte, PMBUS);
 
 /*
  * _pmbus_write_byte() is similar to pmbus_write_byte(), but checks if
@@ -218,7 +229,7 @@ int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg,
 
        return i2c_smbus_write_word_data(client, reg, word);
 }
-EXPORT_SYMBOL_GPL(pmbus_write_word_data);
+EXPORT_SYMBOL_NS_GPL(pmbus_write_word_data, PMBUS);
 
 
 static int pmbus_write_virt_reg(struct i2c_client *client, int page, int reg,
@@ -288,7 +299,7 @@ int pmbus_update_fan(struct i2c_client *client, int page, int id,
        return _pmbus_write_word_data(client, page,
                                      pmbus_fan_command_registers[id], command);
 }
-EXPORT_SYMBOL_GPL(pmbus_update_fan);
+EXPORT_SYMBOL_NS_GPL(pmbus_update_fan, PMBUS);
 
 int pmbus_read_word_data(struct i2c_client *client, int page, int phase, u8 reg)
 {
@@ -300,7 +311,7 @@ int pmbus_read_word_data(struct i2c_client *client, int page, int phase, u8 reg)
 
        return i2c_smbus_read_word_data(client, reg);
 }
-EXPORT_SYMBOL_GPL(pmbus_read_word_data);
+EXPORT_SYMBOL_NS_GPL(pmbus_read_word_data, PMBUS);
 
 static int pmbus_read_virt_reg(struct i2c_client *client, int page, int reg)
 {
@@ -359,7 +370,7 @@ int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg)
 
        return i2c_smbus_read_byte_data(client, reg);
 }
-EXPORT_SYMBOL_GPL(pmbus_read_byte_data);
+EXPORT_SYMBOL_NS_GPL(pmbus_read_byte_data, PMBUS);
 
 int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, u8 value)
 {
@@ -371,7 +382,7 @@ int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, u8 value)
 
        return i2c_smbus_write_byte_data(client, reg, value);
 }
-EXPORT_SYMBOL_GPL(pmbus_write_byte_data);
+EXPORT_SYMBOL_NS_GPL(pmbus_write_byte_data, PMBUS);
 
 int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg,
                           u8 mask, u8 value)
@@ -390,7 +401,7 @@ int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg,
 
        return rv;
 }
-EXPORT_SYMBOL_GPL(pmbus_update_byte_data);
+EXPORT_SYMBOL_NS_GPL(pmbus_update_byte_data, PMBUS);
 
 /*
  * _pmbus_read_byte_data() is similar to pmbus_read_byte_data(), but checks if
@@ -463,14 +474,14 @@ int pmbus_get_fan_rate_device(struct i2c_client *client, int page, int id,
 {
        return pmbus_get_fan_rate(client, page, id, mode, false);
 }
-EXPORT_SYMBOL_GPL(pmbus_get_fan_rate_device);
+EXPORT_SYMBOL_NS_GPL(pmbus_get_fan_rate_device, PMBUS);
 
 int pmbus_get_fan_rate_cached(struct i2c_client *client, int page, int id,
                              enum pmbus_fan_mode mode)
 {
        return pmbus_get_fan_rate(client, page, id, mode, true);
 }
-EXPORT_SYMBOL_GPL(pmbus_get_fan_rate_cached);
+EXPORT_SYMBOL_NS_GPL(pmbus_get_fan_rate_cached, PMBUS);
 
 static void pmbus_clear_fault_page(struct i2c_client *client, int page)
 {
@@ -485,7 +496,7 @@ void pmbus_clear_faults(struct i2c_client *client)
        for (i = 0; i < data->info->pages; i++)
                pmbus_clear_fault_page(client, i);
 }
-EXPORT_SYMBOL_GPL(pmbus_clear_faults);
+EXPORT_SYMBOL_NS_GPL(pmbus_clear_faults, PMBUS);
 
 static int pmbus_check_status_cml(struct i2c_client *client)
 {
@@ -537,13 +548,13 @@ bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg)
 {
        return pmbus_check_register(client, _pmbus_read_byte_data, page, reg);
 }
-EXPORT_SYMBOL_GPL(pmbus_check_byte_register);
+EXPORT_SYMBOL_NS_GPL(pmbus_check_byte_register, PMBUS);
 
 bool pmbus_check_word_register(struct i2c_client *client, int page, int reg)
 {
        return pmbus_check_register(client, __pmbus_read_word_data, page, reg);
 }
-EXPORT_SYMBOL_GPL(pmbus_check_word_register);
+EXPORT_SYMBOL_NS_GPL(pmbus_check_word_register, PMBUS);
 
 const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client *client)
 {
@@ -551,7 +562,7 @@ const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client *client)
 
        return data->info;
 }
-EXPORT_SYMBOL_GPL(pmbus_get_driver_info);
+EXPORT_SYMBOL_NS_GPL(pmbus_get_driver_info, PMBUS);
 
 static int pmbus_get_status(struct i2c_client *client, int page, int reg)
 {
@@ -932,7 +943,7 @@ static ssize_t pmbus_show_boolean(struct device *dev,
        val = pmbus_get_boolean(client, boolean, attr->index);
        if (val < 0)
                return val;
-       return snprintf(buf, PAGE_SIZE, "%d\n", val);
+       return sysfs_emit(buf, "%d\n", val);
 }
 
 static ssize_t pmbus_show_sensor(struct device *dev,
@@ -948,7 +959,7 @@ static ssize_t pmbus_show_sensor(struct device *dev,
        if (sensor->data < 0)
                ret = sensor->data;
        else
-               ret = snprintf(buf, PAGE_SIZE, "%lld\n", pmbus_reg2data(data, sensor));
+               ret = sysfs_emit(buf, "%lld\n", pmbus_reg2data(data, sensor));
        mutex_unlock(&data->update_lock);
        return ret;
 }
@@ -984,7 +995,7 @@ static ssize_t pmbus_show_label(struct device *dev,
 {
        struct pmbus_label *label = to_pmbus_label(da);
 
-       return snprintf(buf, PAGE_SIZE, "%s\n", label->label);
+       return sysfs_emit(buf, "%s\n", label->label);
 }
 
 static int pmbus_add_attribute(struct pmbus_data *data, struct attribute *attr)
@@ -2024,7 +2035,7 @@ static ssize_t pmbus_show_samples(struct device *dev,
        if (val < 0)
                return val;
 
-       return snprintf(buf, PAGE_SIZE, "%d\n", val);
+       return sysfs_emit(buf, "%d\n", val);
 }
 
 static ssize_t pmbus_set_samples(struct device *dev,
@@ -2288,7 +2299,7 @@ const struct regulator_ops pmbus_regulator_ops = {
        .disable = pmbus_regulator_disable,
        .is_enabled = pmbus_regulator_is_enabled,
 };
-EXPORT_SYMBOL_GPL(pmbus_regulator_ops);
+EXPORT_SYMBOL_NS_GPL(pmbus_regulator_ops, PMBUS);
 
 static int pmbus_regulator_register(struct pmbus_data *data)
 {
@@ -2557,6 +2568,7 @@ int pmbus_do_probe(struct i2c_client *client, struct pmbus_driver_info *info)
        struct pmbus_data *data;
        size_t groups_num = 0;
        int ret;
+       char *name;
 
        if (!info)
                return -ENODEV;
@@ -2606,10 +2618,15 @@ int pmbus_do_probe(struct i2c_client *client, struct pmbus_driver_info *info)
                return -ENODEV;
        }
 
+       name = devm_kstrdup(dev, client->name, GFP_KERNEL);
+       if (!name)
+               return -ENOMEM;
+       strreplace(name, '-', '_');
+
        data->groups[0] = &data->group;
        memcpy(data->groups + 1, info->groups, sizeof(void *) * groups_num);
        data->hwmon_dev = devm_hwmon_device_register_with_groups(dev,
-                                       client->name, data, data->groups);
+                                       name, data, data->groups);
        if (IS_ERR(data->hwmon_dev)) {
                dev_err(dev, "Failed to register hwmon device\n");
                return PTR_ERR(data->hwmon_dev);
@@ -2625,7 +2642,7 @@ int pmbus_do_probe(struct i2c_client *client, struct pmbus_driver_info *info)
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(pmbus_do_probe);
+EXPORT_SYMBOL_NS_GPL(pmbus_do_probe, PMBUS);
 
 struct dentry *pmbus_get_debugfs_dir(struct i2c_client *client)
 {
@@ -2633,7 +2650,7 @@ struct dentry *pmbus_get_debugfs_dir(struct i2c_client *client)
 
        return data->debugfs;
 }
-EXPORT_SYMBOL_GPL(pmbus_get_debugfs_dir);
+EXPORT_SYMBOL_NS_GPL(pmbus_get_debugfs_dir, PMBUS);
 
 static int __init pmbus_core_init(void)
 {
index da27ce3..52bee5d 100644 (file)
@@ -41,6 +41,15 @@ static int pxe1610_identify(struct i2c_client *client,
                                info->vrm_version[i] = vr13;
                                break;
                        default:
+                               /*
+                                * If prior pages are available limit operation
+                                * to them
+                                */
+                               if (i != 0) {
+                                       info->pages = i;
+                                       return 0;
+                               }
+
                                return -ENODEV;
                        }
                }
@@ -139,3 +148,4 @@ module_i2c_driver(pxe1610_driver);
 MODULE_AUTHOR("Vijay Khemka <vijaykhemka@fb.com>");
 MODULE_DESCRIPTION("PMBus driver for Infineon PXE1610, PXE1110 and PXM1310");
 MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PMBUS);
index aec5127..b6e8b20 100644 (file)
@@ -420,3 +420,4 @@ module_i2c_driver(q54sj108a2_driver);
 MODULE_AUTHOR("Xiao.Ma <xiao.mx.ma@deltaww.com>");
 MODULE_DESCRIPTION("PMBus driver for Delta Q54SJ108A2 series modules");
 MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PMBUS);
diff --git a/drivers/hwmon/pmbus/stpddc60.c b/drivers/hwmon/pmbus/stpddc60.c
new file mode 100644 (file)
index 0000000..357b9d9
--- /dev/null
@@ -0,0 +1,249 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Hardware monitoring driver for the STPDDC60 controller
+ *
+ * Copyright (c) 2021 Flextronics International Sweden AB.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/pmbus.h>
+#include "pmbus.h"
+
+#define STPDDC60_MFR_READ_VOUT         0xd2
+#define STPDDC60_MFR_OV_LIMIT_OFFSET   0xe5
+#define STPDDC60_MFR_UV_LIMIT_OFFSET   0xe6
+
+static const struct i2c_device_id stpddc60_id[] = {
+       {"stpddc60", 0},
+       {"bmr481", 0},
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, stpddc60_id);
+
+static struct pmbus_driver_info stpddc60_info = {
+       .pages = 1,
+       .func[0] = PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT
+               | PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT
+               | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP
+               | PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT
+               | PMBUS_HAVE_POUT,
+};
+
+/*
+ * Calculate the closest absolute offset between commanded vout value
+ * and limit value in steps of 50mv in the range 0 (50mv) to 7 (400mv).
+ * Return 0 if the upper limit is lower than vout or if the lower limit
+ * is higher than vout.
+ */
+static u8 stpddc60_get_offset(int vout, u16 limit, bool over)
+{
+       int offset;
+       long v, l;
+
+       v = 250 + (vout - 1) * 5; /* Convert VID to mv */
+       l = (limit * 1000L) >> 8; /* Convert LINEAR to mv */
+
+       if (over == (l < v))
+               return 0;
+
+       offset = DIV_ROUND_CLOSEST(abs(l - v), 50);
+
+       if (offset > 0)
+               offset--;
+
+       return clamp_val(offset, 0, 7);
+}
+
+/*
+ * Adjust the linear format word to use the given fixed exponent.
+ */
+static u16 stpddc60_adjust_linear(u16 word, s16 fixed)
+{
+       s16 e, m, d;
+
+       e = ((s16)word) >> 11;
+       m = ((s16)((word & 0x7ff) << 5)) >> 5;
+       d = e - fixed;
+
+       if (d >= 0)
+               m <<= d;
+       else
+               m >>= -d;
+
+       return clamp_val(m, 0, 0x3ff) | ((fixed << 11) & 0xf800);
+}
+
+/*
+ * The VOUT_COMMAND register uses the VID format but the vout alarm limit
+ * registers use the LINEAR format so we override VOUT_MODE here to force
+ * LINEAR format for all registers.
+ */
+static int stpddc60_read_byte_data(struct i2c_client *client, int page, int reg)
+{
+       int ret;
+
+       if (page > 0)
+               return -ENXIO;
+
+       switch (reg) {
+       case PMBUS_VOUT_MODE:
+               ret = 0x18;
+               break;
+       default:
+               ret = -ENODATA;
+               break;
+       }
+
+       return ret;
+}
+
+/*
+ * The vout related registers return values in LINEAR11 format when LINEAR16
+ * is expected. Clear the top 5 bits to set the exponent part to zero to
+ * convert the value to LINEAR16 format.
+ */
+static int stpddc60_read_word_data(struct i2c_client *client, int page,
+                                  int phase, int reg)
+{
+       int ret;
+
+       if (page > 0)
+               return -ENXIO;
+
+       switch (reg) {
+       case PMBUS_READ_VOUT:
+               ret = pmbus_read_word_data(client, page, phase,
+                                          STPDDC60_MFR_READ_VOUT);
+               if (ret < 0)
+                       return ret;
+               ret &= 0x7ff;
+               break;
+       case PMBUS_VOUT_OV_FAULT_LIMIT:
+       case PMBUS_VOUT_UV_FAULT_LIMIT:
+               ret = pmbus_read_word_data(client, page, phase, reg);
+               if (ret < 0)
+                       return ret;
+               ret &= 0x7ff;
+               break;
+       default:
+               ret = -ENODATA;
+               break;
+       }
+
+       return ret;
+}
+
+/*
+ * The vout under- and over-voltage limits are set as an offset relative to
+ * the commanded vout voltage. The vin, iout, pout and temp limits must use
+ * the same fixed exponent the chip uses to encode the data when read.
+ */
+static int stpddc60_write_word_data(struct i2c_client *client, int page,
+                                   int reg, u16 word)
+{
+       int ret;
+       u8 offset;
+
+       if (page > 0)
+               return -ENXIO;
+
+       switch (reg) {
+       case PMBUS_VOUT_OV_FAULT_LIMIT:
+               ret = pmbus_read_word_data(client, page, 0xff,
+                                          PMBUS_VOUT_COMMAND);
+               if (ret < 0)
+                       return ret;
+               offset = stpddc60_get_offset(ret, word, true);
+               ret = pmbus_write_byte_data(client, page,
+                                           STPDDC60_MFR_OV_LIMIT_OFFSET,
+                                           offset);
+               break;
+       case PMBUS_VOUT_UV_FAULT_LIMIT:
+               ret = pmbus_read_word_data(client, page, 0xff,
+                                          PMBUS_VOUT_COMMAND);
+               if (ret < 0)
+                       return ret;
+               offset = stpddc60_get_offset(ret, word, false);
+               ret = pmbus_write_byte_data(client, page,
+                                           STPDDC60_MFR_UV_LIMIT_OFFSET,
+                                           offset);
+               break;
+       case PMBUS_VIN_OV_FAULT_LIMIT:
+       case PMBUS_VIN_UV_FAULT_LIMIT:
+       case PMBUS_OT_FAULT_LIMIT:
+       case PMBUS_OT_WARN_LIMIT:
+       case PMBUS_IOUT_OC_FAULT_LIMIT:
+       case PMBUS_IOUT_OC_WARN_LIMIT:
+       case PMBUS_POUT_OP_FAULT_LIMIT:
+               ret = pmbus_read_word_data(client, page, 0xff, reg);
+               if (ret < 0)
+                       return ret;
+               word = stpddc60_adjust_linear(word, ret >> 11);
+               ret = pmbus_write_word_data(client, page, reg, word);
+               break;
+       default:
+               ret = -ENODATA;
+               break;
+       }
+
+       return ret;
+}
+
+static int stpddc60_probe(struct i2c_client *client)
+{
+       int status;
+       u8 device_id[I2C_SMBUS_BLOCK_MAX + 1];
+       const struct i2c_device_id *mid;
+       struct pmbus_driver_info *info = &stpddc60_info;
+
+       if (!i2c_check_functionality(client->adapter,
+                                    I2C_FUNC_SMBUS_READ_BYTE_DATA
+                                    | I2C_FUNC_SMBUS_BLOCK_DATA))
+               return -ENODEV;
+
+       status = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, device_id);
+       if (status < 0) {
+               dev_err(&client->dev, "Failed to read Manufacturer Model\n");
+               return status;
+       }
+       for (mid = stpddc60_id; mid->name[0]; mid++) {
+               if (!strncasecmp(mid->name, device_id, strlen(mid->name)))
+                       break;
+       }
+       if (!mid->name[0]) {
+               dev_err(&client->dev, "Unsupported device\n");
+               return -ENODEV;
+       }
+
+       info->read_byte_data = stpddc60_read_byte_data;
+       info->read_word_data = stpddc60_read_word_data;
+       info->write_word_data = stpddc60_write_word_data;
+
+       status = pmbus_do_probe(client, info);
+       if (status < 0)
+               return status;
+
+       pmbus_set_update(client, PMBUS_VOUT_OV_FAULT_LIMIT, true);
+       pmbus_set_update(client, PMBUS_VOUT_UV_FAULT_LIMIT, true);
+
+       return 0;
+}
+
+static struct i2c_driver stpddc60_driver = {
+       .driver = {
+                  .name = "stpddc60",
+                  },
+       .probe_new = stpddc60_probe,
+       .id_table = stpddc60_id,
+};
+
+module_i2c_driver(stpddc60_driver);
+
+MODULE_AUTHOR("Erik Rosen <erik.rosen@metormote.com>");
+MODULE_DESCRIPTION("PMBus driver for ST STPDDC60");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PMBUS);
index f7f00ab..31bb83c 100644 (file)
@@ -51,3 +51,4 @@ module_i2c_driver(tps40422_driver);
 MODULE_AUTHOR("Zhu Laiwen <richard.zhu@nsn.com>");
 MODULE_DESCRIPTION("PMBus driver for TI TPS40422");
 MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PMBUS);
index ba838fa..81b9d81 100644 (file)
 #include "pmbus.h"
 
 enum chips {
-       tps53647, tps53667, tps53679, tps53681, tps53688
+       tps53647, tps53667, tps53676, tps53679, tps53681, tps53688
 };
 
 #define TPS53647_PAGE_NUM              1
 
+#define TPS53676_USER_DATA_03          0xb3
+#define TPS53676_MAX_PHASES            7
+
 #define TPS53679_PROT_VR12_5MV         0x01 /* VR12.0 mode, 5-mV DAC */
 #define TPS53679_PROT_VR12_5_10MV      0x02 /* VR12.5 mode, 10-mV DAC */
 #define TPS53679_PROT_VR13_10MV                0x04 /* VR13.0 mode, 10-mV DAC */
@@ -143,6 +146,45 @@ static int tps53681_identify(struct i2c_client *client,
                                            TPS53681_DEVICE_ID);
 }
 
+static int tps53676_identify(struct i2c_client *client,
+                            struct pmbus_driver_info *info)
+{
+       u8 buf[I2C_SMBUS_BLOCK_MAX];
+       int phases_a = 0, phases_b = 0;
+       int i, ret;
+
+       ret = i2c_smbus_read_block_data(client, PMBUS_IC_DEVICE_ID, buf);
+       if (ret < 0)
+               return ret;
+       if (strncmp("TI\x53\x67\x60", buf, 5)) {
+               dev_err(&client->dev, "Unexpected device ID: %s\n", buf);
+               return -ENODEV;
+       }
+
+       ret = i2c_smbus_read_block_data(client, TPS53676_USER_DATA_03, buf);
+       if (ret < 0)
+               return ret;
+       if (ret != 24)
+               return -EIO;
+       for (i = 0; i < 2 * TPS53676_MAX_PHASES; i += 2) {
+               if (buf[i + 1] & 0x80) {
+                       if (buf[i] & 0x08)
+                               phases_b++;
+                       else
+                               phases_a++;
+               }
+       }
+
+       info->format[PSC_VOLTAGE_OUT] = linear;
+       info->pages = 1;
+       info->phases[0] = phases_a;
+       if (phases_b > 0) {
+               info->pages = 2;
+               info->phases[1] = phases_b;
+       }
+       return 0;
+}
+
 static int tps53681_read_word_data(struct i2c_client *client, int page,
                                   int phase, int reg)
 {
@@ -183,6 +225,7 @@ static struct pmbus_driver_info tps53679_info = {
        .pfunc[3] = PMBUS_HAVE_IOUT,
        .pfunc[4] = PMBUS_HAVE_IOUT,
        .pfunc[5] = PMBUS_HAVE_IOUT,
+       .pfunc[6] = PMBUS_HAVE_IOUT,
 };
 
 static int tps53679_probe(struct i2c_client *client)
@@ -206,6 +249,9 @@ static int tps53679_probe(struct i2c_client *client)
                info->pages = TPS53647_PAGE_NUM;
                info->identify = tps53679_identify;
                break;
+       case tps53676:
+               info->identify = tps53676_identify;
+               break;
        case tps53679:
        case tps53688:
                info->pages = TPS53679_PAGE_NUM;
@@ -225,8 +271,10 @@ static int tps53679_probe(struct i2c_client *client)
 }
 
 static const struct i2c_device_id tps53679_id[] = {
+       {"bmr474", tps53676},
        {"tps53647", tps53647},
        {"tps53667", tps53667},
+       {"tps53676", tps53676},
        {"tps53679", tps53679},
        {"tps53681", tps53681},
        {"tps53688", tps53688},
@@ -238,6 +286,7 @@ MODULE_DEVICE_TABLE(i2c, tps53679_id);
 static const struct of_device_id __maybe_unused tps53679_of_match[] = {
        {.compatible = "ti,tps53647", .data = (void *)tps53647},
        {.compatible = "ti,tps53667", .data = (void *)tps53667},
+       {.compatible = "ti,tps53676", .data = (void *)tps53676},
        {.compatible = "ti,tps53679", .data = (void *)tps53679},
        {.compatible = "ti,tps53681", .data = (void *)tps53681},
        {.compatible = "ti,tps53688", .data = (void *)tps53688},
@@ -259,3 +308,4 @@ module_i2c_driver(tps53679_driver);
 MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>");
 MODULE_DESCRIPTION("PMBus driver for Texas Instruments TPS53679");
 MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PMBUS);
index a15e6fe..75fc770 100644 (file)
@@ -629,3 +629,4 @@ module_i2c_driver(ucd9000_driver);
 MODULE_AUTHOR("Guenter Roeck");
 MODULE_DESCRIPTION("PMBus driver for TI UCD90xxx");
 MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PMBUS);
index 47cc7ca..6bc3273 100644 (file)
@@ -209,3 +209,4 @@ module_i2c_driver(ucd9200_driver);
 MODULE_AUTHOR("Guenter Roeck");
 MODULE_DESCRIPTION("PMBus driver for TI UCD922x, UCD924x");
 MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PMBUS);
index f8bc0f4..b07da06 100644 (file)
@@ -168,3 +168,4 @@ module_i2c_driver(xdpe122_driver);
 MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>");
 MODULE_DESCRIPTION("PMBus driver for Infineon XDPE122 family");
 MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PMBUS);
index 69120ca..b7d4eac 100644 (file)
@@ -404,3 +404,4 @@ module_i2c_driver(zl6100_driver);
 MODULE_AUTHOR("Guenter Roeck");
 MODULE_DESCRIPTION("PMBus driver for ZL6100 and compatibles");
 MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS(PMBUS);
index f2703c5..70ae665 100644 (file)
@@ -166,7 +166,7 @@ static ssize_t s3c_hwmon_ch_show(struct device *dev,
        ret *= cfg->mult;
        ret = DIV_ROUND_CLOSEST(ret, cfg->div);
 
-       return snprintf(buf, PAGE_SIZE, "%d\n", ret);
+       return sysfs_emit(buf, "%d\n", ret);
 }
 
 /**
@@ -187,7 +187,7 @@ static ssize_t s3c_hwmon_label_show(struct device *dev,
 
        cfg = pdata->in[sen_attr->index];
 
-       return snprintf(buf, PAGE_SIZE, "%s\n", cfg->name);
+       return sysfs_emit(buf, "%s\n", cfg->name);
 }
 
 /**
index 0396442..4324a5d 100644 (file)
@@ -12,7 +12,6 @@
 #include <linux/jiffies.h>
 #include <linux/platform_device.h>
 #include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
 #include <linux/err.h>
 #include <linux/mutex.h>
 #include "sch56xx-common.h"
@@ -65,7 +64,6 @@ static const char * const SCH5627_IN_LABELS[SCH5627_NO_IN] = {
 
 struct sch5627_data {
        unsigned short addr;
-       struct device *hwmon_dev;
        struct sch56xx_watchdog_data *watchdog;
        u8 control;
        u8 temp_max[SCH5627_NO_TEMPS];
@@ -74,66 +72,96 @@ struct sch5627_data {
 
        struct mutex update_lock;
        unsigned long last_battery;     /* In jiffies */
-       char valid;                     /* !=0 if following fields are valid */
-       unsigned long last_updated;     /* In jiffies */
+       char temp_valid;                /* !=0 if following fields are valid */
+       char fan_valid;
+       char in_valid;
+       unsigned long temp_last_updated;        /* In jiffies */
+       unsigned long fan_last_updated;
+       unsigned long in_last_updated;
        u16 temp[SCH5627_NO_TEMPS];
        u16 fan[SCH5627_NO_FANS];
        u16 in[SCH5627_NO_IN];
 };
 
-static struct sch5627_data *sch5627_update_device(struct device *dev)
+static int sch5627_update_temp(struct sch5627_data *data)
 {
-       struct sch5627_data *data = dev_get_drvdata(dev);
-       struct sch5627_data *ret = data;
+       int ret = 0;
        int i, val;
 
        mutex_lock(&data->update_lock);
 
-       /* Trigger a Vbat voltage measurement every 5 minutes */
-       if (time_after(jiffies, data->last_battery + 300 * HZ)) {
-               sch56xx_write_virtual_reg(data->addr, SCH5627_REG_CTRL,
-                                         data->control | 0x10);
-               data->last_battery = jiffies;
-       }
-
        /* Cache the values for 1 second */
-       if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
+       if (time_after(jiffies, data->temp_last_updated + HZ) || !data->temp_valid) {
                for (i = 0; i < SCH5627_NO_TEMPS; i++) {
-                       val = sch56xx_read_virtual_reg12(data->addr,
-                               SCH5627_REG_TEMP_MSB[i],
-                               SCH5627_REG_TEMP_LSN[i],
-                               SCH5627_REG_TEMP_HIGH_NIBBLE[i]);
+                       val = sch56xx_read_virtual_reg12(data->addr, SCH5627_REG_TEMP_MSB[i],
+                                                        SCH5627_REG_TEMP_LSN[i],
+                                                        SCH5627_REG_TEMP_HIGH_NIBBLE[i]);
                        if (unlikely(val < 0)) {
-                               ret = ERR_PTR(val);
+                               ret = val;
                                goto abort;
                        }
                        data->temp[i] = val;
                }
+               data->temp_last_updated = jiffies;
+               data->temp_valid = 1;
+       }
+abort:
+       mutex_unlock(&data->update_lock);
+       return ret;
+}
 
+static int sch5627_update_fan(struct sch5627_data *data)
+{
+       int ret = 0;
+       int i, val;
+
+       mutex_lock(&data->update_lock);
+
+       /* Cache the values for 1 second */
+       if (time_after(jiffies, data->fan_last_updated + HZ) || !data->fan_valid) {
                for (i = 0; i < SCH5627_NO_FANS; i++) {
-                       val = sch56xx_read_virtual_reg16(data->addr,
-                                                        SCH5627_REG_FAN[i]);
+                       val = sch56xx_read_virtual_reg16(data->addr, SCH5627_REG_FAN[i]);
                        if (unlikely(val < 0)) {
-                               ret = ERR_PTR(val);
+                               ret = val;
                                goto abort;
                        }
                        data->fan[i] = val;
                }
+               data->fan_last_updated = jiffies;
+               data->fan_valid = 1;
+       }
+abort:
+       mutex_unlock(&data->update_lock);
+       return ret;
+}
+
+static int sch5627_update_in(struct sch5627_data *data)
+{
+       int ret = 0;
+       int i, val;
+
+       mutex_lock(&data->update_lock);
 
+       /* Trigger a Vbat voltage measurement every 5 minutes */
+       if (time_after(jiffies, data->last_battery + 300 * HZ)) {
+               sch56xx_write_virtual_reg(data->addr, SCH5627_REG_CTRL, data->control | 0x10);
+               data->last_battery = jiffies;
+       }
+
+       /* Cache the values for 1 second */
+       if (time_after(jiffies, data->in_last_updated + HZ) || !data->in_valid) {
                for (i = 0; i < SCH5627_NO_IN; i++) {
-                       val = sch56xx_read_virtual_reg12(data->addr,
-                               SCH5627_REG_IN_MSB[i],
-                               SCH5627_REG_IN_LSN[i],
-                               SCH5627_REG_IN_HIGH_NIBBLE[i]);
+                       val = sch56xx_read_virtual_reg12(data->addr, SCH5627_REG_IN_MSB[i],
+                                                        SCH5627_REG_IN_LSN[i],
+                                                        SCH5627_REG_IN_HIGH_NIBBLE[i]);
                        if (unlikely(val < 0)) {
-                               ret = ERR_PTR(val);
+                               ret = val;
                                goto abort;
                        }
                        data->in[i] = val;
                }
-
-               data->last_updated = jiffies;
-               data->valid = 1;
+               data->in_last_updated = jiffies;
+               data->in_valid = 1;
        }
 abort:
        mutex_unlock(&data->update_lock);
@@ -192,249 +220,141 @@ static int reg_to_rpm(u16 reg)
        return 5400540 / reg;
 }
 
-static ssize_t name_show(struct device *dev, struct device_attribute *devattr,
-       char *buf)
-{
-       return snprintf(buf, PAGE_SIZE, "%s\n", DEVNAME);
-}
-
-static ssize_t temp_show(struct device *dev, struct device_attribute *devattr,
-                        char *buf)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct sch5627_data *data = sch5627_update_device(dev);
-       int val;
-
-       if (IS_ERR(data))
-               return PTR_ERR(data);
-
-       val = reg_to_temp(data->temp[attr->index]);
-       return snprintf(buf, PAGE_SIZE, "%d\n", val);
-}
-
-static ssize_t temp_fault_show(struct device *dev,
-                              struct device_attribute *devattr, char *buf)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct sch5627_data *data = sch5627_update_device(dev);
-
-       if (IS_ERR(data))
-               return PTR_ERR(data);
-
-       return snprintf(buf, PAGE_SIZE, "%d\n", data->temp[attr->index] == 0);
-}
-
-static ssize_t temp_max_show(struct device *dev,
-                            struct device_attribute *devattr, char *buf)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct sch5627_data *data = dev_get_drvdata(dev);
-       int val;
-
-       val = reg_to_temp_limit(data->temp_max[attr->index]);
-       return snprintf(buf, PAGE_SIZE, "%d\n", val);
-}
-
-static ssize_t temp_crit_show(struct device *dev,
-                             struct device_attribute *devattr, char *buf)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct sch5627_data *data = dev_get_drvdata(dev);
-       int val;
-
-       val = reg_to_temp_limit(data->temp_crit[attr->index]);
-       return snprintf(buf, PAGE_SIZE, "%d\n", val);
-}
-
-static ssize_t fan_show(struct device *dev, struct device_attribute *devattr,
-                       char *buf)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct sch5627_data *data = sch5627_update_device(dev);
-       int val;
-
-       if (IS_ERR(data))
-               return PTR_ERR(data);
-
-       val = reg_to_rpm(data->fan[attr->index]);
-       if (val < 0)
-               return val;
-
-       return snprintf(buf, PAGE_SIZE, "%d\n", val);
-}
-
-static ssize_t fan_fault_show(struct device *dev,
-                             struct device_attribute *devattr, char *buf)
+static umode_t sch5627_is_visible(const void *drvdata, enum hwmon_sensor_types type, u32 attr,
+                                 int channel)
 {
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct sch5627_data *data = sch5627_update_device(dev);
-
-       if (IS_ERR(data))
-               return PTR_ERR(data);
-
-       return snprintf(buf, PAGE_SIZE, "%d\n",
-                       data->fan[attr->index] == 0xffff);
+       return 0444;
 }
 
-static ssize_t fan_min_show(struct device *dev,
-                           struct device_attribute *devattr, char *buf)
+static int sch5627_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
+                       long *val)
 {
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
        struct sch5627_data *data = dev_get_drvdata(dev);
-       int val = reg_to_rpm(data->fan_min[attr->index]);
-       if (val < 0)
-               return val;
-
-       return snprintf(buf, PAGE_SIZE, "%d\n", val);
-}
-
-static ssize_t in_show(struct device *dev, struct device_attribute *devattr,
-                      char *buf)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct sch5627_data *data = sch5627_update_device(dev);
-       int val;
-
-       if (IS_ERR(data))
-               return PTR_ERR(data);
+       int ret;
+
+       switch (type) {
+       case hwmon_temp:
+               ret = sch5627_update_temp(data);
+               if (ret < 0)
+                       return ret;
+               switch (attr) {
+               case hwmon_temp_input:
+                       *val = reg_to_temp(data->temp[channel]);
+                       return 0;
+               case hwmon_temp_max:
+                       *val = reg_to_temp_limit(data->temp_max[channel]);
+                       return 0;
+               case hwmon_temp_crit:
+                       *val = reg_to_temp_limit(data->temp_crit[channel]);
+                       return 0;
+               case hwmon_temp_fault:
+                       *val = (data->temp[channel] == 0);
+                       return 0;
+               default:
+                       break;
+               }
+               break;
+       case hwmon_fan:
+               ret = sch5627_update_fan(data);
+               if (ret < 0)
+                       return ret;
+               switch (attr) {
+               case hwmon_fan_input:
+                       ret = reg_to_rpm(data->fan[channel]);
+                       if (ret < 0)
+                               return ret;
+                       *val = ret;
+                       return 0;
+               case hwmon_fan_min:
+                       ret = reg_to_rpm(data->fan_min[channel]);
+                       if (ret < 0)
+                               return ret;
+                       *val = ret;
+                       return 0;
+               case hwmon_fan_fault:
+                       *val = (data->fan[channel] == 0xffff);
+                       return 0;
+               default:
+                       break;
+               }
+               break;
+       case hwmon_in:
+               ret = sch5627_update_in(data);
+               if (ret < 0)
+                       return ret;
+               switch (attr) {
+               case hwmon_in_input:
+                       *val = DIV_ROUND_CLOSEST(data->in[channel] * SCH5627_REG_IN_FACTOR[channel],
+                                                10000);
+                       return 0;
+               default:
+                       break;
+               }
+               break;
+       default:
+               break;
+       }
 
-       val = DIV_ROUND_CLOSEST(
-               data->in[attr->index] * SCH5627_REG_IN_FACTOR[attr->index],
-               10000);
-       return snprintf(buf, PAGE_SIZE, "%d\n", val);
+       return -EOPNOTSUPP;
 }
 
-static ssize_t in_label_show(struct device *dev,
-                            struct device_attribute *devattr, char *buf)
+static int sch5627_read_string(struct device *dev, enum hwmon_sensor_types type, u32 attr,
+                              int channel, const char **str)
 {
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       switch (type) {
+       case hwmon_in:
+               switch (attr) {
+               case hwmon_in_label:
+                       *str = SCH5627_IN_LABELS[channel];
+                       return 0;
+               default:
+                       break;
+               }
+               break;
+       default:
+               break;
+       }
 
-       return snprintf(buf, PAGE_SIZE, "%s\n",
-                       SCH5627_IN_LABELS[attr->index]);
+       return -EOPNOTSUPP;
 }
 
-static DEVICE_ATTR_RO(name);
-static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, 0);
-static SENSOR_DEVICE_ATTR_RO(temp2_input, temp, 1);
-static SENSOR_DEVICE_ATTR_RO(temp3_input, temp, 2);
-static SENSOR_DEVICE_ATTR_RO(temp4_input, temp, 3);
-static SENSOR_DEVICE_ATTR_RO(temp5_input, temp, 4);
-static SENSOR_DEVICE_ATTR_RO(temp6_input, temp, 5);
-static SENSOR_DEVICE_ATTR_RO(temp7_input, temp, 6);
-static SENSOR_DEVICE_ATTR_RO(temp8_input, temp, 7);
-static SENSOR_DEVICE_ATTR_RO(temp1_fault, temp_fault, 0);
-static SENSOR_DEVICE_ATTR_RO(temp2_fault, temp_fault, 1);
-static SENSOR_DEVICE_ATTR_RO(temp3_fault, temp_fault, 2);
-static SENSOR_DEVICE_ATTR_RO(temp4_fault, temp_fault, 3);
-static SENSOR_DEVICE_ATTR_RO(temp5_fault, temp_fault, 4);
-static SENSOR_DEVICE_ATTR_RO(temp6_fault, temp_fault, 5);
-static SENSOR_DEVICE_ATTR_RO(temp7_fault, temp_fault, 6);
-static SENSOR_DEVICE_ATTR_RO(temp8_fault, temp_fault, 7);
-static SENSOR_DEVICE_ATTR_RO(temp1_max, temp_max, 0);
-static SENSOR_DEVICE_ATTR_RO(temp2_max, temp_max, 1);
-static SENSOR_DEVICE_ATTR_RO(temp3_max, temp_max, 2);
-static SENSOR_DEVICE_ATTR_RO(temp4_max, temp_max, 3);
-static SENSOR_DEVICE_ATTR_RO(temp5_max, temp_max, 4);
-static SENSOR_DEVICE_ATTR_RO(temp6_max, temp_max, 5);
-static SENSOR_DEVICE_ATTR_RO(temp7_max, temp_max, 6);
-static SENSOR_DEVICE_ATTR_RO(temp8_max, temp_max, 7);
-static SENSOR_DEVICE_ATTR_RO(temp1_crit, temp_crit, 0);
-static SENSOR_DEVICE_ATTR_RO(temp2_crit, temp_crit, 1);
-static SENSOR_DEVICE_ATTR_RO(temp3_crit, temp_crit, 2);
-static SENSOR_DEVICE_ATTR_RO(temp4_crit, temp_crit, 3);
-static SENSOR_DEVICE_ATTR_RO(temp5_crit, temp_crit, 4);
-static SENSOR_DEVICE_ATTR_RO(temp6_crit, temp_crit, 5);
-static SENSOR_DEVICE_ATTR_RO(temp7_crit, temp_crit, 6);
-static SENSOR_DEVICE_ATTR_RO(temp8_crit, temp_crit, 7);
-
-static SENSOR_DEVICE_ATTR_RO(fan1_input, fan, 0);
-static SENSOR_DEVICE_ATTR_RO(fan2_input, fan, 1);
-static SENSOR_DEVICE_ATTR_RO(fan3_input, fan, 2);
-static SENSOR_DEVICE_ATTR_RO(fan4_input, fan, 3);
-static SENSOR_DEVICE_ATTR_RO(fan1_fault, fan_fault, 0);
-static SENSOR_DEVICE_ATTR_RO(fan2_fault, fan_fault, 1);
-static SENSOR_DEVICE_ATTR_RO(fan3_fault, fan_fault, 2);
-static SENSOR_DEVICE_ATTR_RO(fan4_fault, fan_fault, 3);
-static SENSOR_DEVICE_ATTR_RO(fan1_min, fan_min, 0);
-static SENSOR_DEVICE_ATTR_RO(fan2_min, fan_min, 1);
-static SENSOR_DEVICE_ATTR_RO(fan3_min, fan_min, 2);
-static SENSOR_DEVICE_ATTR_RO(fan4_min, fan_min, 3);
-
-static SENSOR_DEVICE_ATTR_RO(in0_input, in, 0);
-static SENSOR_DEVICE_ATTR_RO(in1_input, in, 1);
-static SENSOR_DEVICE_ATTR_RO(in2_input, in, 2);
-static SENSOR_DEVICE_ATTR_RO(in3_input, in, 3);
-static SENSOR_DEVICE_ATTR_RO(in4_input, in, 4);
-static SENSOR_DEVICE_ATTR_RO(in0_label, in_label, 0);
-static SENSOR_DEVICE_ATTR_RO(in1_label, in_label, 1);
-static SENSOR_DEVICE_ATTR_RO(in2_label, in_label, 2);
-static SENSOR_DEVICE_ATTR_RO(in3_label, in_label, 3);
-
-static struct attribute *sch5627_attributes[] = {
-       &dev_attr_name.attr,
-
-       &sensor_dev_attr_temp1_input.dev_attr.attr,
-       &sensor_dev_attr_temp2_input.dev_attr.attr,
-       &sensor_dev_attr_temp3_input.dev_attr.attr,
-       &sensor_dev_attr_temp4_input.dev_attr.attr,
-       &sensor_dev_attr_temp5_input.dev_attr.attr,
-       &sensor_dev_attr_temp6_input.dev_attr.attr,
-       &sensor_dev_attr_temp7_input.dev_attr.attr,
-       &sensor_dev_attr_temp8_input.dev_attr.attr,
-       &sensor_dev_attr_temp1_fault.dev_attr.attr,
-       &sensor_dev_attr_temp2_fault.dev_attr.attr,
-       &sensor_dev_attr_temp3_fault.dev_attr.attr,
-       &sensor_dev_attr_temp4_fault.dev_attr.attr,
-       &sensor_dev_attr_temp5_fault.dev_attr.attr,
-       &sensor_dev_attr_temp6_fault.dev_attr.attr,
-       &sensor_dev_attr_temp7_fault.dev_attr.attr,
-       &sensor_dev_attr_temp8_fault.dev_attr.attr,
-       &sensor_dev_attr_temp1_max.dev_attr.attr,
-       &sensor_dev_attr_temp2_max.dev_attr.attr,
-       &sensor_dev_attr_temp3_max.dev_attr.attr,
-       &sensor_dev_attr_temp4_max.dev_attr.attr,
-       &sensor_dev_attr_temp5_max.dev_attr.attr,
-       &sensor_dev_attr_temp6_max.dev_attr.attr,
-       &sensor_dev_attr_temp7_max.dev_attr.attr,
-       &sensor_dev_attr_temp8_max.dev_attr.attr,
-       &sensor_dev_attr_temp1_crit.dev_attr.attr,
-       &sensor_dev_attr_temp2_crit.dev_attr.attr,
-       &sensor_dev_attr_temp3_crit.dev_attr.attr,
-       &sensor_dev_attr_temp4_crit.dev_attr.attr,
-       &sensor_dev_attr_temp5_crit.dev_attr.attr,
-       &sensor_dev_attr_temp6_crit.dev_attr.attr,
-       &sensor_dev_attr_temp7_crit.dev_attr.attr,
-       &sensor_dev_attr_temp8_crit.dev_attr.attr,
-
-       &sensor_dev_attr_fan1_input.dev_attr.attr,
-       &sensor_dev_attr_fan2_input.dev_attr.attr,
-       &sensor_dev_attr_fan3_input.dev_attr.attr,
-       &sensor_dev_attr_fan4_input.dev_attr.attr,
-       &sensor_dev_attr_fan1_fault.dev_attr.attr,
-       &sensor_dev_attr_fan2_fault.dev_attr.attr,
-       &sensor_dev_attr_fan3_fault.dev_attr.attr,
-       &sensor_dev_attr_fan4_fault.dev_attr.attr,
-       &sensor_dev_attr_fan1_min.dev_attr.attr,
-       &sensor_dev_attr_fan2_min.dev_attr.attr,
-       &sensor_dev_attr_fan3_min.dev_attr.attr,
-       &sensor_dev_attr_fan4_min.dev_attr.attr,
-
-       &sensor_dev_attr_in0_input.dev_attr.attr,
-       &sensor_dev_attr_in1_input.dev_attr.attr,
-       &sensor_dev_attr_in2_input.dev_attr.attr,
-       &sensor_dev_attr_in3_input.dev_attr.attr,
-       &sensor_dev_attr_in4_input.dev_attr.attr,
-       &sensor_dev_attr_in0_label.dev_attr.attr,
-       &sensor_dev_attr_in1_label.dev_attr.attr,
-       &sensor_dev_attr_in2_label.dev_attr.attr,
-       &sensor_dev_attr_in3_label.dev_attr.attr,
-       /* No in4_label as in4 is a generic input pin */
+static const struct hwmon_ops sch5627_ops = {
+       .is_visible = sch5627_is_visible,
+       .read = sch5627_read,
+       .read_string = sch5627_read_string,
+};
 
+static const struct hwmon_channel_info *sch5627_info[] = {
+       HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ),
+       HWMON_CHANNEL_INFO(temp,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT,
+                          HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT
+                          ),
+       HWMON_CHANNEL_INFO(fan,
+                          HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_FAULT,
+                          HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_FAULT,
+                          HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_FAULT,
+                          HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_FAULT
+                          ),
+       HWMON_CHANNEL_INFO(in,
+                          HWMON_I_INPUT | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_LABEL,
+                          HWMON_I_INPUT | HWMON_I_LABEL,
+                          HWMON_I_INPUT
+                          ),
        NULL
 };
 
-static const struct attribute_group sch5627_group = {
-       .attrs = sch5627_attributes,
+static const struct hwmon_chip_info sch5627_chip_info = {
+       .ops = &sch5627_ops,
+       .info = sch5627_info,
 };
 
 static int sch5627_remove(struct platform_device *pdev)
@@ -444,17 +364,13 @@ static int sch5627_remove(struct platform_device *pdev)
        if (data->watchdog)
                sch56xx_watchdog_unregister(data->watchdog);
 
-       if (data->hwmon_dev)
-               hwmon_device_unregister(data->hwmon_dev);
-
-       sysfs_remove_group(&pdev->dev.kobj, &sch5627_group);
-
        return 0;
 }
 
 static int sch5627_probe(struct platform_device *pdev)
 {
        struct sch5627_data *data;
+       struct device *hwmon_dev;
        int err, build_code, build_id, hwmon_rev, val;
 
        data = devm_kzalloc(&pdev->dev, sizeof(struct sch5627_data),
@@ -467,72 +383,58 @@ static int sch5627_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, data);
 
        val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_HWMON_ID);
-       if (val < 0) {
-               err = val;
-               goto error;
-       }
+       if (val < 0)
+               return val;
+
        if (val != SCH5627_HWMON_ID) {
                pr_err("invalid %s id: 0x%02X (expected 0x%02X)\n", "hwmon",
                       val, SCH5627_HWMON_ID);
-               err = -ENODEV;
-               goto error;
+               return -ENODEV;
        }
 
        val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_COMPANY_ID);
-       if (val < 0) {
-               err = val;
-               goto error;
-       }
+       if (val < 0)
+               return val;
+
        if (val != SCH5627_COMPANY_ID) {
                pr_err("invalid %s id: 0x%02X (expected 0x%02X)\n", "company",
                       val, SCH5627_COMPANY_ID);
-               err = -ENODEV;
-               goto error;
+               return -ENODEV;
        }
 
        val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_PRIMARY_ID);
-       if (val < 0) {
-               err = val;
-               goto error;
-       }
+       if (val < 0)
+               return val;
+
        if (val != SCH5627_PRIMARY_ID) {
                pr_err("invalid %s id: 0x%02X (expected 0x%02X)\n", "primary",
                       val, SCH5627_PRIMARY_ID);
-               err = -ENODEV;
-               goto error;
+               return -ENODEV;
        }
 
        build_code = sch56xx_read_virtual_reg(data->addr,
                                              SCH5627_REG_BUILD_CODE);
-       if (build_code < 0) {
-               err = build_code;
-               goto error;
-       }
+       if (build_code < 0)
+               return build_code;
 
        build_id = sch56xx_read_virtual_reg16(data->addr,
                                              SCH5627_REG_BUILD_ID);
-       if (build_id < 0) {
-               err = build_id;
-               goto error;
-       }
+       if (build_id < 0)
+               return build_id;
 
        hwmon_rev = sch56xx_read_virtual_reg(data->addr,
                                             SCH5627_REG_HWMON_REV);
-       if (hwmon_rev < 0) {
-               err = hwmon_rev;
-               goto error;
-       }
+       if (hwmon_rev < 0)
+               return hwmon_rev;
 
        val = sch56xx_read_virtual_reg(data->addr, SCH5627_REG_CTRL);
-       if (val < 0) {
-               err = val;
-               goto error;
-       }
+       if (val < 0)
+               return val;
+
        data->control = val;
        if (!(data->control & 0x01)) {
                pr_err("hardware monitoring not enabled\n");
-               err = -ENODEV;
-               goto error;
+               return -ENODEV;
        }
        /* Trigger a Vbat voltage measurement, so that we get a valid reading
           the first time we read Vbat */
@@ -546,23 +448,16 @@ static int sch5627_probe(struct platform_device *pdev)
         */
        err = sch5627_read_limits(data);
        if (err)
-               goto error;
+               return err;
 
        pr_info("found %s chip at %#hx\n", DEVNAME, data->addr);
        pr_info("firmware build: code 0x%02X, id 0x%04X, hwmon: rev 0x%02X\n",
                build_code, build_id, hwmon_rev);
 
-       /* Register sysfs interface files */
-       err = sysfs_create_group(&pdev->dev.kobj, &sch5627_group);
-       if (err)
-               goto error;
-
-       data->hwmon_dev = hwmon_device_register(&pdev->dev);
-       if (IS_ERR(data->hwmon_dev)) {
-               err = PTR_ERR(data->hwmon_dev);
-               data->hwmon_dev = NULL;
-               goto error;
-       }
+       hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, DEVNAME, data,
+                                                        &sch5627_chip_info, NULL);
+       if (IS_ERR(hwmon_dev))
+               return PTR_ERR(hwmon_dev);
 
        /* Note failing to register the watchdog is not a fatal error */
        data->watchdog = sch56xx_watchdog_register(&pdev->dev, data->addr,
@@ -570,10 +465,6 @@ static int sch5627_probe(struct platform_device *pdev)
                        &data->update_lock, 1);
 
        return 0;
-
-error:
-       sch5627_remove(pdev);
-       return err;
 }
 
 static struct platform_driver sch5627_driver = {
index 200bb2b..5683a38 100644 (file)
@@ -160,7 +160,7 @@ static int reg_to_rpm(u16 reg)
 static ssize_t name_show(struct device *dev, struct device_attribute *devattr,
                         char *buf)
 {
-       return snprintf(buf, PAGE_SIZE, "%s\n", DEVNAME);
+       return sysfs_emit(buf, "%s\n", DEVNAME);
 }
 
 static ssize_t in_value_show(struct device *dev,
@@ -176,7 +176,7 @@ static ssize_t in_value_show(struct device *dev,
        val = DIV_ROUND_CLOSEST(
                data->in[attr->index] * SCH5636_REG_IN_FACTORS[attr->index],
                255);
-       return snprintf(buf, PAGE_SIZE, "%d\n", val);
+       return sysfs_emit(buf, "%d\n", val);
 }
 
 static ssize_t in_label_show(struct device *dev,
@@ -184,8 +184,8 @@ static ssize_t in_label_show(struct device *dev,
 {
        struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
 
-       return snprintf(buf, PAGE_SIZE, "%s\n",
-                       SCH5636_IN_LABELS[attr->index]);
+       return sysfs_emit(buf, "%s\n",
+                         SCH5636_IN_LABELS[attr->index]);
 }
 
 static ssize_t temp_value_show(struct device *dev,
@@ -199,7 +199,7 @@ static ssize_t temp_value_show(struct device *dev,
                return PTR_ERR(data);
 
        val = (data->temp_val[attr->index] - 64) * 1000;
-       return snprintf(buf, PAGE_SIZE, "%d\n", val);
+       return sysfs_emit(buf, "%d\n", val);
 }
 
 static ssize_t temp_fault_show(struct device *dev,
@@ -213,7 +213,7 @@ static ssize_t temp_fault_show(struct device *dev,
                return PTR_ERR(data);
 
        val = (data->temp_ctrl[attr->index] & SCH5636_TEMP_WORKING) ? 0 : 1;
-       return snprintf(buf, PAGE_SIZE, "%d\n", val);
+       return sysfs_emit(buf, "%d\n", val);
 }
 
 static ssize_t temp_alarm_show(struct device *dev,
@@ -227,7 +227,7 @@ static ssize_t temp_alarm_show(struct device *dev,
                return PTR_ERR(data);
 
        val = (data->temp_ctrl[attr->index] & SCH5636_TEMP_ALARM) ? 1 : 0;
-       return snprintf(buf, PAGE_SIZE, "%d\n", val);
+       return sysfs_emit(buf, "%d\n", val);
 }
 
 static ssize_t fan_value_show(struct device *dev,
@@ -244,7 +244,7 @@ static ssize_t fan_value_show(struct device *dev,
        if (val < 0)
                return val;
 
-       return snprintf(buf, PAGE_SIZE, "%d\n", val);
+       return sysfs_emit(buf, "%d\n", val);
 }
 
 static ssize_t fan_fault_show(struct device *dev,
@@ -258,7 +258,7 @@ static ssize_t fan_fault_show(struct device *dev,
                return PTR_ERR(data);
 
        val = (data->fan_ctrl[attr->index] & SCH5636_FAN_NOT_PRESENT) ? 1 : 0;
-       return snprintf(buf, PAGE_SIZE, "%d\n", val);
+       return sysfs_emit(buf, "%d\n", val);
 }
 
 static ssize_t fan_alarm_show(struct device *dev,
@@ -272,7 +272,7 @@ static ssize_t fan_alarm_show(struct device *dev,
                return PTR_ERR(data);
 
        val = (data->fan_ctrl[attr->index] & SCH5636_FAN_ALARM) ? 1 : 0;
-       return snprintf(buf, PAGE_SIZE, "%d\n", val);
+       return sysfs_emit(buf, "%d\n", val);
 }
 
 static struct sensor_device_attribute sch5636_attr[] = {
index b6cbe98..62906d9 100644 (file)
@@ -351,7 +351,7 @@ static ssize_t smm665_show_crit_alarm(struct device *dev,
        if (data->faults & (1 << attr->index))
                val = 1;
 
-       return snprintf(buf, PAGE_SIZE, "%d\n", val);
+       return sysfs_emit(buf, "%d\n", val);
 }
 
 static ssize_t smm665_show_input(struct device *dev,
@@ -366,7 +366,7 @@ static ssize_t smm665_show_input(struct device *dev,
                return PTR_ERR(data);
 
        val = smm665_convert(data->adc[adc], adc);
-       return snprintf(buf, PAGE_SIZE, "%d\n", val);
+       return sysfs_emit(buf, "%d\n", val);
 }
 
 #define SMM665_SHOW(what) \
index 6928be6..0ed2840 100644 (file)
@@ -387,7 +387,7 @@ static ssize_t max_alarm_show(struct device *dev,
        if (ret < 0)
                return ret;
 
-       return snprintf(buf, PAGE_SIZE, "%d\n", priv->max_alert);
+       return sysfs_emit(buf, "%d\n", priv->max_alert);
 }
 
 static ssize_t min_alarm_show(struct device *dev,
@@ -404,7 +404,7 @@ static ssize_t min_alarm_show(struct device *dev,
        if (ret < 0)
                return ret;
 
-       return snprintf(buf, PAGE_SIZE, "%d\n", priv->min_alert);
+       return sysfs_emit(buf, "%d\n", priv->min_alert);
 }
 
 static ssize_t input_show(struct device *dev, struct device_attribute *attr,
@@ -419,7 +419,7 @@ static ssize_t input_show(struct device *dev, struct device_attribute *attr,
        if (ret < 0)
                return ret;
 
-       return snprintf(buf, PAGE_SIZE, "%d\n", priv->temp);
+       return sysfs_emit(buf, "%d\n", priv->temp);
 }
 
 static ssize_t therm_show(struct device *dev, struct device_attribute *attr,
@@ -427,7 +427,7 @@ static ssize_t therm_show(struct device *dev, struct device_attribute *attr,
 {
        struct stts751_priv *priv = dev_get_drvdata(dev);
 
-       return snprintf(buf, PAGE_SIZE, "%d\n", priv->therm);
+       return sysfs_emit(buf, "%d\n", priv->therm);
 }
 
 static ssize_t therm_store(struct device *dev, struct device_attribute *attr,
@@ -469,7 +469,7 @@ static ssize_t hyst_show(struct device *dev, struct device_attribute *attr,
 {
        struct stts751_priv *priv = dev_get_drvdata(dev);
 
-       return snprintf(buf, PAGE_SIZE, "%d\n", priv->hyst);
+       return sysfs_emit(buf, "%d\n", priv->hyst);
 }
 
 static ssize_t hyst_store(struct device *dev, struct device_attribute *attr,
@@ -509,7 +509,7 @@ static ssize_t therm_trip_show(struct device *dev,
        if (ret < 0)
                return ret;
 
-       return snprintf(buf, PAGE_SIZE, "%d\n", priv->therm_trip);
+       return sysfs_emit(buf, "%d\n", priv->therm_trip);
 }
 
 static ssize_t max_show(struct device *dev, struct device_attribute *attr,
@@ -517,7 +517,7 @@ static ssize_t max_show(struct device *dev, struct device_attribute *attr,
 {
        struct stts751_priv *priv = dev_get_drvdata(dev);
 
-       return snprintf(buf, PAGE_SIZE, "%d\n", priv->event_max);
+       return sysfs_emit(buf, "%d\n", priv->event_max);
 }
 
 static ssize_t max_store(struct device *dev, struct device_attribute *attr,
@@ -551,7 +551,7 @@ static ssize_t min_show(struct device *dev, struct device_attribute *attr,
 {
        struct stts751_priv *priv = dev_get_drvdata(dev);
 
-       return snprintf(buf, PAGE_SIZE, "%d\n", priv->event_min);
+       return sysfs_emit(buf, "%d\n", priv->event_min);
 }
 
 static ssize_t min_store(struct device *dev, struct device_attribute *attr,
@@ -585,8 +585,8 @@ static ssize_t interval_show(struct device *dev,
 {
        struct stts751_priv *priv = dev_get_drvdata(dev);
 
-       return snprintf(buf, PAGE_SIZE, "%d\n",
-                       stts751_intervals[priv->interval]);
+       return sysfs_emit(buf, "%d\n",
+                         stts751_intervals[priv->interval]);
 }
 
 static ssize_t interval_store(struct device *dev,
index e710965..44d798b 100644 (file)
@@ -27,7 +27,7 @@ static ssize_t vexpress_hwmon_label_show(struct device *dev,
 {
        const char *label = of_get_property(dev->of_node, "label", NULL);
 
-       return snprintf(buffer, PAGE_SIZE, "%s\n", label);
+       return sysfs_emit(buffer, "%s\n", label);
 }
 
 static ssize_t vexpress_hwmon_u32_show(struct device *dev,
@@ -41,8 +41,8 @@ static ssize_t vexpress_hwmon_u32_show(struct device *dev,
        if (err)
                return err;
 
-       return snprintf(buffer, PAGE_SIZE, "%u\n", value /
-                       to_sensor_dev_attr(dev_attr)->index);
+       return sysfs_emit(buffer, "%u\n", value /
+                         to_sensor_dev_attr(dev_attr)->index);
 }
 
 static ssize_t vexpress_hwmon_u64_show(struct device *dev,
@@ -60,9 +60,9 @@ static ssize_t vexpress_hwmon_u64_show(struct device *dev,
        if (err)
                return err;
 
-       return snprintf(buffer, PAGE_SIZE, "%llu\n",
-                       div_u64(((u64)value_hi << 32) | value_lo,
-                       to_sensor_dev_attr(dev_attr)->index));
+       return sysfs_emit(buffer, "%llu\n",
+                         div_u64(((u64)value_hi << 32) | value_lo,
+                                 to_sensor_dev_attr(dev_attr)->index));
 }
 
 static umode_t vexpress_hwmon_attr_is_visible(struct kobject *kobj,
index 1489e83..382ef03 100644 (file)
@@ -329,14 +329,14 @@ static ssize_t temp1_input_show(struct device *dev,
 
        temp = sign_extend32(val, TEMP_NEGATIVE_BIT);
 
-       return snprintf(buf, PAGE_SIZE, "%d\n", CELSIUS_TO_mCELSIUS(temp));
+       return sysfs_emit(buf, "%d\n", CELSIUS_TO_mCELSIUS(temp));
 }
 
 static ssize_t temp1_label_show(struct device *dev,
                                struct device_attribute *attr,
                                char *buf)
 {
-       return snprintf(buf, PAGE_SIZE, "SoC Temperature\n");
+       return sysfs_emit(buf, "SoC Temperature\n");
 }
 
 static ssize_t temp1_critical_alarm_show(struct device *dev,
@@ -345,21 +345,21 @@ static ssize_t temp1_critical_alarm_show(struct device *dev,
 {
        struct xgene_hwmon_dev *ctx = dev_get_drvdata(dev);
 
-       return snprintf(buf, PAGE_SIZE, "%d\n", ctx->temp_critical_alarm);
+       return sysfs_emit(buf, "%d\n", ctx->temp_critical_alarm);
 }
 
 static ssize_t power1_label_show(struct device *dev,
                                 struct device_attribute *attr,
                                 char *buf)
 {
-       return snprintf(buf, PAGE_SIZE, "CPU power\n");
+       return sysfs_emit(buf, "CPU power\n");
 }
 
 static ssize_t power2_label_show(struct device *dev,
                                 struct device_attribute *attr,
                                 char *buf)
 {
-       return snprintf(buf, PAGE_SIZE, "IO power\n");
+       return sysfs_emit(buf, "IO power\n");
 }
 
 static ssize_t power1_input_show(struct device *dev,
@@ -374,7 +374,7 @@ static ssize_t power1_input_show(struct device *dev,
        if (rc < 0)
                return rc;
 
-       return snprintf(buf, PAGE_SIZE, "%u\n", mWATT_TO_uWATT(val));
+       return sysfs_emit(buf, "%u\n", mWATT_TO_uWATT(val));
 }
 
 static ssize_t power2_input_show(struct device *dev,
@@ -389,7 +389,7 @@ static ssize_t power2_input_show(struct device *dev,
        if (rc < 0)
                return rc;
 
-       return snprintf(buf, PAGE_SIZE, "%u\n", mWATT_TO_uWATT(val));
+       return sysfs_emit(buf, "%u\n", mWATT_TO_uWATT(val));
 }
 
 static DEVICE_ATTR_RO(temp1_label);
index 06c9775..cb53898 100644 (file)
 
 enum m10bmc_type {
        M10_N3000,
+       M10_D5005
+};
+
+static struct mfd_cell m10bmc_d5005_subdevs[] = {
+       { .name = "d5005bmc-hwmon" },
 };
 
 static struct mfd_cell m10bmc_pacn3000_subdevs[] = {
@@ -173,6 +178,10 @@ static int intel_m10_bmc_spi_probe(struct spi_device *spi)
                cells = m10bmc_pacn3000_subdevs;
                n_cell = ARRAY_SIZE(m10bmc_pacn3000_subdevs);
                break;
+       case M10_D5005:
+               cells = m10bmc_d5005_subdevs;
+               n_cell = ARRAY_SIZE(m10bmc_d5005_subdevs);
+               break;
        default:
                return -ENODEV;
        }
@@ -187,6 +196,7 @@ static int intel_m10_bmc_spi_probe(struct spi_device *spi)
 
 static const struct spi_device_id m10bmc_spi_id[] = {
        { "m10-n3000", M10_N3000 },
+       { "m10-d5005", M10_D5005 },
        { }
 };
 MODULE_DEVICE_TABLE(spi, m10bmc_spi_id);