Merge tag 'hwmon-for-v6.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/groec...
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 13 Dec 2022 21:09:38 +0000 (13:09 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 13 Dec 2022 21:09:38 +0000 (13:09 -0800)
Pull hwmon updates from Guenter Roeck:
 "New drivers:

   - Driver for OneXPlayer mini AMD sensors

   - Ampere's Altra smpro-hwmon driver

  New chip and attribute support in existing drivers:

   - nct6775: Support for ASUS CROSSHAIR VIII/TUF/ProArt B550M

   - pmbus/ltc2978: Support for LTC7132

   - aquacomputer_d5next: Support for temperature sensor offsets and
     flow sensor pulses

   - coretemp: Support for dynamic ttarget and tjmax

  Improvements:

   - Use devm_regulator_get_enable() where appropriate

   - Use sysfs_emit() instead of scnprintf()

   - Remove some useless #include <linux/hwmon-vid.h>

   - Include <linux/kstrtox.h> when appropriate

   - Use simple i2c probe

   - it87: Check for a valid chip before using force_id, and new new
     module parameter to ignore ACPI resource conflicts

   - jc42: Use regmap, and restore min/max/critical temperatures on
     resume

   - Add reporting power good and status to PMBus based regulators

  Last minute fixes:

   - emc2305: Fix probing of emc2301/2/3, and fix setting pwm values
     manually if THERMAL is enabled

  And various other minor fixes and improvements"

* tag 'hwmon-for-v6.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (37 commits)
  hwmon: (emc2305) fix pwm never being able to set lower
  hwmon: (emc2305) fix unable to probe emc2301/2/3
  hwmon: (dell-smm) Move error message to make probing silent
  hwmon: use sysfs_emit() to instead of scnprintf()
  hwmon: (oxp-sensors) Fix pwm reading
  hwmon: (aquacomputer_d5next) Add support for Quadro flow sensor pulses
  hwmon: (pmbus/core) Implement regulator get_status
  hwmon: (oxp-sensors) Add AOK ZOE and Mini PRO
  hwmon: (gsc-hwmon) Switch to flexible array to simplify code
  hwmon: (pmbus) Add power good support
  hwmon: (nct6775) add ASUS CROSSHAIR VIII/TUF/ProArt B550M
  hwmon: (coretemp) Add support for dynamic ttarget
  hwmon: (coretemp) Add support for dynamic tjmax
  hwmon: (coretemp) rearrange tjmax handing code
  hwmon: Remove some useless #include <linux/hwmon-vid.h>
  hwmon: (coretemp) Remove obsolete temp_data->valid
  hwmon: add OneXPlayer mini AMD sensors driver
  hwmon: (aquacomputer_d5next) Clear up macros and comments
  hwmon: (it87) Add DMI table for future extensions
  hwmon: Include <linux/kstrtox.h> when appropriate
  ...

42 files changed:
Documentation/hwmon/aquacomputer_d5next.rst
Documentation/hwmon/index.rst
Documentation/hwmon/oxp-sensors.rst [new file with mode: 0644]
Documentation/hwmon/smpro-hwmon.rst [new file with mode: 0644]
MAINTAINERS
drivers/hwmon/Kconfig
drivers/hwmon/Makefile
drivers/hwmon/adm1177.c
drivers/hwmon/aht10.c
drivers/hwmon/aquacomputer_d5next.c
drivers/hwmon/atxp1.c
drivers/hwmon/coretemp.c
drivers/hwmon/dell-smm-hwmon.c
drivers/hwmon/ds1621.c
drivers/hwmon/emc2305.c
drivers/hwmon/fschmd.c
drivers/hwmon/gpio-fan.c
drivers/hwmon/gsc-hwmon.c
drivers/hwmon/hwmon.c
drivers/hwmon/it87.c
drivers/hwmon/jc42.c
drivers/hwmon/lm73.c
drivers/hwmon/lm90.c
drivers/hwmon/ltc2992.c
drivers/hwmon/max127.c
drivers/hwmon/mr75203.c
drivers/hwmon/nct6775-platform.c
drivers/hwmon/occ/Kconfig
drivers/hwmon/oxp-sensors.c [new file with mode: 0644]
drivers/hwmon/pcf8591.c
drivers/hwmon/pmbus/ltc2978.c
drivers/hwmon/pmbus/pmbus_core.c
drivers/hwmon/pmbus/q54sj108a2.c
drivers/hwmon/sbrmi.c
drivers/hwmon/sbtsi_temp.c
drivers/hwmon/sht3x.c
drivers/hwmon/sht4x.c
drivers/hwmon/smpro-hwmon.c [new file with mode: 0644]
drivers/hwmon/vt8231.c
drivers/hwmon/w83l786ng.c
include/linux/hwmon-sysfs.h
include/linux/platform_data/gsc_hwmon.h

index e238533..637bdbc 100644 (file)
@@ -39,7 +39,7 @@ current.
 
 The Quadro exposes four physical and sixteen virtual temperature sensors, a flow
 sensor and four PWM controllable fans, along with their speed (in RPM), power,
-voltage and current.
+voltage and current. Flow sensor pulses are also available.
 
 The Farbwerk and Farbwerk 360 expose four temperature sensors. Additionally,
 sixteen virtual temperature sensors of the Farbwerk 360 are exposed.
@@ -62,7 +62,9 @@ Sysfs entries
 
 ================ ==============================================================
 temp[1-20]_input Physical/virtual temperature sensors (in millidegrees Celsius)
+temp[1-4]_offset Temperature sensor correction offset (in millidegrees Celsius)
 fan[1-8]_input   Pump/fan speed (in RPM) / Flow speed (in dL/h)
+fan5_pulses      Quadro flow sensor pulses
 power[1-8]_input Pump/fan power (in micro Watts)
 in[0-7]_input    Pump/fan voltage (in milli Volts)
 curr[1-8]_input  Pump/fan current (in milli Amperes)
index c1d11cf..fe2cc6b 100644 (file)
@@ -160,6 +160,7 @@ Hardware Monitoring Kernel Drivers
    nzxt-kraken2
    nzxt-smart2
    occ
+   oxp-sensors
    pc87360
    pc87427
    pcf8591
@@ -187,6 +188,7 @@ Hardware Monitoring Kernel Drivers
    sis5595
    sl28cpld
    smm665
+   smpro-hwmon
    smsc47b397
    smsc47m192
    smsc47m1
diff --git a/Documentation/hwmon/oxp-sensors.rst b/Documentation/hwmon/oxp-sensors.rst
new file mode 100644 (file)
index 0000000..39c588e
--- /dev/null
@@ -0,0 +1,44 @@
+.. SPDX-License-Identifier: GPL-2.0-or-later
+
+Kernel driver oxp-sensors
+=========================
+
+Author:
+    - Joaquín Ignacio Aramendía <samsagax@gmail.com>
+
+Description:
+------------
+
+One X Player devices from One Netbook provide fan readings and fan control
+through its Embedded Controller.
+
+Currently only supports AMD boards from the One X Player and AOK ZOE lineup.
+Intel boards could be supported if we could figure out the EC registers and
+values to write to since the EC layout and model is different.
+
+Supported devices
+-----------------
+
+Currently the driver supports the following handhelds:
+
+ - AOK ZOE A1
+ - OneXPlayer AMD
+ - OneXPlayer mini AMD
+ - OneXPlayer mini AMD PRO
+
+Sysfs entries
+-------------
+
+The following attributes are supported:
+
+fan1_input
+  Read Only. Reads current fan RMP.
+
+pwm1_enable
+  Read Write. Enable manual fan control. Write "1" to set to manual, write "0"
+  to let the EC control de fan speed. Read this attribute to see current status.
+
+pwm1
+  Read Write. Read this attribute to see current duty cycle in the range [0-255].
+  When pwm1_enable is set to "1" (manual) write any value in the range [0-255]
+  to set fan speed.
diff --git a/Documentation/hwmon/smpro-hwmon.rst b/Documentation/hwmon/smpro-hwmon.rst
new file mode 100644 (file)
index 0000000..fb7b366
--- /dev/null
@@ -0,0 +1,102 @@
+.. SPDX-License-Identifier: GPL-2.0-only
+
+Kernel driver Ampere(R)'s Altra(R) SMpro hwmon
+==============================================
+
+Supported chips:
+
+  * Ampere(R) Altra(R)
+
+    Prefix: ``smpro``
+
+    Reference: `Altra SoC BMC Interface Specification`
+
+Author: Thu Nguyen <thu@os.amperecomputing.com>
+
+Description
+-----------
+The smpro-hwmon driver supports hardware monitoring for Ampere(R) Altra(R)
+SoCs based on the SMpro co-processor (SMpro).  The following sensor metrics
+are supported by the driver:
+
+  * temperature
+  * voltage
+  * current
+  * power
+
+The interface provides the registers to query the various sensors and
+their values which are then exported to userspace by this driver.
+
+Usage Notes
+-----------
+
+The driver creates at least two sysfs files for each sensor.
+
+* ``<sensor_type><idx>_label`` reports the sensor label.
+* ``<sensor_type><idx>_input`` returns the sensor value.
+
+The sysfs files are allocated in the SMpro rootfs folder, with one root
+directory for each instance.
+
+When the SoC is turned off, the driver will fail to read registers and
+return ``-ENXIO``.
+
+Sysfs entries
+-------------
+
+The following sysfs files are supported:
+
+* Ampere(R) Altra(R):
+
+  ============    =============  ======  ===============================================
+  Name            Unit           Perm    Description
+  ============    =============  ======  ===============================================
+  temp1_input     millicelsius   RO      SoC temperature
+  temp2_input     millicelsius   RO      Max temperature reported among SoC VRDs
+  temp2_crit      millicelsius   RO      SoC VRD HOT Threshold temperature
+  temp3_input     millicelsius   RO      Max temperature reported among DIMM VRDs
+  temp4_input     millicelsius   RO      Max temperature reported among Core VRDs
+  temp5_input     millicelsius   RO      Temperature of DIMM0 on CH0
+  temp5_crit      millicelsius   RO      MEM HOT Threshold for all DIMMs
+  temp6_input     millicelsius   RO      Temperature of DIMM0 on CH1
+  temp6_crit      millicelsius   RO      MEM HOT Threshold for all DIMMs
+  temp7_input     millicelsius   RO      Temperature of DIMM0 on CH2
+  temp7_crit      millicelsius   RO      MEM HOT Threshold for all DIMMs
+  temp8_input     millicelsius   RO      Temperature of DIMM0 on CH3
+  temp8_crit      millicelsius   RO      MEM HOT Threshold for all DIMMs
+  temp9_input     millicelsius   RO      Temperature of DIMM0 on CH4
+  temp9_crit      millicelsius   RO      MEM HOT Threshold for all DIMMs
+  temp10_input    millicelsius   RO      Temperature of DIMM0 on CH5
+  temp10_crit     millicelsius   RO      MEM HOT Threshold for all DIMMs
+  temp11_input    millicelsius   RO      Temperature of DIMM0 on CH6
+  temp11_crit     millicelsius   RO      MEM HOT Threshold for all DIMMs
+  temp12_input    millicelsius   RO      Temperature of DIMM0 on CH7
+  temp12_crit     millicelsius   RO      MEM HOT Threshold for all DIMMs
+  temp13_input    millicelsius   RO      Max temperature reported among RCA VRDs
+  in0_input       millivolts     RO      Core voltage
+  in1_input       millivolts     RO      SoC voltage
+  in2_input       millivolts     RO      DIMM VRD1 voltage
+  in3_input       millivolts     RO      DIMM VRD2 voltage
+  in4_input       millivolts     RO      RCA VRD voltage
+  cur1_input      milliamperes   RO      Core VRD current
+  cur2_input      milliamperes   RO      SoC VRD current
+  cur3_input      milliamperes   RO      DIMM VRD1 current
+  cur4_input      milliamperes   RO      DIMM VRD2 current
+  cur5_input      milliamperes   RO      RCA VRD current
+  power1_input    microwatts     RO      Core VRD power
+  power2_input    microwatts     RO      SoC VRD power
+  power3_input    microwatts     RO      DIMM VRD1 power
+  power4_input    microwatts     RO      DIMM VRD2 power
+  power5_input    microwatts     RO      RCA VRD power
+  ============    =============  ======  ===============================================
+
+  Example::
+
+    # cat in0_input
+    830
+    # cat temp1_input
+    37000
+    # cat curr1_input
+    9000
+    # cat power5_input
+    19500000
index 3900f24..ec69250 100644 (file)
@@ -15452,6 +15452,12 @@ S:     Maintained
 F:     drivers/mtd/nand/onenand/
 F:     include/linux/mtd/onenand*.h
 
+ONEXPLAYER FAN DRIVER
+M:     Joaquín Ignacio Aramendía <samsagax@gmail.com>
+L:     linux-hwmon@vger.kernel.org
+S:     Maintained
+F:     drivers/hwmon/oxp-sensors.c
+
 ONION OMEGA2+ BOARD
 M:     Harvey Hunt <harveyhuntnexus@gmail.com>
 L:     linux-mips@vger.kernel.org
index 7ac3daa..3176c33 100644 (file)
@@ -67,6 +67,14 @@ config SENSORS_ABITUGURU3
          This driver can also be built as a module. If so, the module
          will be called abituguru3.
 
+config SENSORS_SMPRO
+       tristate "Ampere's Altra SMpro hardware monitoring driver"
+       depends on MFD_SMPRO
+       help
+         If you say yes here you get support for the thermal, voltage,
+         current and power sensors of Ampere's Altra processor family SoC
+         with SMpro co-processor.
+
 config SENSORS_AD7314
        tristate "Analog Devices AD7314 and compatibles"
        depends on SPI
@@ -799,6 +807,7 @@ config SENSORS_IT87
 config SENSORS_JC42
        tristate "JEDEC JC42.4 compliant memory module temperature sensors"
        depends on I2C
+       select REGMAP_I2C
        help
          If you say yes here, you get support for JEDEC JC42.4 compliant
          temperature sensors, which are used on many DDR3 memory modules for
@@ -1607,6 +1616,17 @@ config SENSORS_NZXT_SMART2
 
 source "drivers/hwmon/occ/Kconfig"
 
+config SENSORS_OXP
+       tristate "OneXPlayer EC fan control"
+       depends on ACPI
+       depends on X86
+       help
+               If you say yes here you get support for fan readings and control over
+               OneXPlayer handheld devices. Only OneXPlayer mini AMD handheld variant
+               boards are supported.
+
+               Can also be built as a module. In that case it will be called oxp-sensors.
+
 config SENSORS_PCF8591
        tristate "Philips PCF8591 ADC/DAC"
        depends on I2C
index 11d076c..e2e4e87 100644 (file)
@@ -167,6 +167,7 @@ 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_NZXT_SMART2) += nzxt-smart2.o
+obj-$(CONFIG_SENSORS_OXP) += oxp-sensors.o
 obj-$(CONFIG_SENSORS_PC87360)  += pc87360.o
 obj-$(CONFIG_SENSORS_PC87427)  += pc87427.o
 obj-$(CONFIG_SENSORS_PCF8591)  += pcf8591.o
@@ -187,6 +188,7 @@ obj-$(CONFIG_SENSORS_SHT4x) += sht4x.o
 obj-$(CONFIG_SENSORS_SHTC1)    += shtc1.o
 obj-$(CONFIG_SENSORS_SIS5595)  += sis5595.o
 obj-$(CONFIG_SENSORS_SMM665)   += smm665.o
+obj-$(CONFIG_SENSORS_SMPRO)    += smpro-hwmon.o
 obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o
 obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o
 obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o
index 0c5dbc5..be17a26 100644 (file)
 /**
  * struct adm1177_state - driver instance specific data
  * @client:            pointer to i2c client
- * @reg:               regulator info for the power supply of the device
  * @r_sense_uohm:      current sense resistor value
  * @alert_threshold_ua:        current limit for shutdown
  * @vrange_high:       internal voltage divider
  */
 struct adm1177_state {
        struct i2c_client       *client;
-       struct regulator        *reg;
        u32                     r_sense_uohm;
        u32                     alert_threshold_ua;
        bool                    vrange_high;
@@ -189,13 +187,6 @@ static const struct hwmon_chip_info adm1177_chip_info = {
        .info = adm1177_info,
 };
 
-static void adm1177_remove(void *data)
-{
-       struct adm1177_state *st = data;
-
-       regulator_disable(st->reg);
-}
-
 static int adm1177_probe(struct i2c_client *client)
 {
        struct device *dev = &client->dev;
@@ -210,21 +201,9 @@ static int adm1177_probe(struct i2c_client *client)
 
        st->client = client;
 
-       st->reg = devm_regulator_get_optional(&client->dev, "vref");
-       if (IS_ERR(st->reg)) {
-               if (PTR_ERR(st->reg) == -EPROBE_DEFER)
-                       return -EPROBE_DEFER;
-
-               st->reg = NULL;
-       } else {
-               ret = regulator_enable(st->reg);
-               if (ret)
-                       return ret;
-               ret = devm_add_action_or_reset(&client->dev, adm1177_remove,
-                                              st);
-               if (ret)
-                       return ret;
-       }
+       ret = devm_regulator_get_enable_optional(&client->dev, "vref");
+       if (ret == -EPROBE_DEFER)
+               return -EPROBE_DEFER;
 
        if (device_property_read_u32(dev, "shunt-resistor-micro-ohms",
                                     &st->r_sense_uohm))
index 2d9770c..d76f344 100644 (file)
@@ -289,8 +289,7 @@ static const struct hwmon_chip_info aht10_chip_info = {
        .info = aht10_info,
 };
 
-static int aht10_probe(struct i2c_client *client,
-                      const struct i2c_device_id *aht10_id)
+static int aht10_probe(struct i2c_client *client)
 {
        struct device *device = &client->dev;
        struct device *hwmon_dev;
@@ -336,7 +335,7 @@ static struct i2c_driver aht10_driver = {
        .driver = {
                .name = "aht10",
        },
-       .probe      = aht10_probe,
+       .probe_new  = aht10_probe,
        .id_table   = aht10_id,
 };
 
index c51a267..9cc1008 100644 (file)
@@ -59,7 +59,7 @@ static u8 secondary_ctrl_report[] = {
        0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x34, 0xC6
 };
 
-/* Register offsets for all Aquacomputer devices */
+/* Sensor sizes and offsets for all Aquacomputer devices */
 #define AQC_TEMP_SENSOR_SIZE           0x02
 #define AQC_TEMP_SENSOR_DISCONNECTED   0x7FFF
 #define AQC_FAN_PERCENT_OFFSET         0x00
@@ -68,62 +68,81 @@ static u8 secondary_ctrl_report[] = {
 #define AQC_FAN_POWER_OFFSET           0x06
 #define AQC_FAN_SPEED_OFFSET           0x08
 
-/* Register offsets for the D5 Next pump */
-#define D5NEXT_POWER_CYCLES            0x18
-#define D5NEXT_COOLANT_TEMP            0x57
+/* Specs of the D5 Next pump */
 #define D5NEXT_NUM_FANS                        2
 #define D5NEXT_NUM_SENSORS             1
 #define D5NEXT_NUM_VIRTUAL_SENSORS     8
-#define D5NEXT_VIRTUAL_SENSORS_START   0x3f
+#define D5NEXT_CTRL_REPORT_SIZE                0x329
+
+/* Sensor report offsets for the D5 Next pump */
+#define D5NEXT_POWER_CYCLES            0x18
+#define D5NEXT_COOLANT_TEMP            0x57
 #define D5NEXT_PUMP_OFFSET             0x6c
 #define D5NEXT_FAN_OFFSET              0x5f
 #define D5NEXT_5V_VOLTAGE              0x39
 #define D5NEXT_12V_VOLTAGE             0x37
-#define D5NEXT_CTRL_REPORT_SIZE                0x329
+#define D5NEXT_VIRTUAL_SENSORS_START   0x3f
 static u8 d5next_sensor_fan_offsets[] = { D5NEXT_PUMP_OFFSET, D5NEXT_FAN_OFFSET };
 
-/* Pump and fan speed registers in D5 Next control report (from 0-100%) */
-static u16 d5next_ctrl_fan_offsets[] = { 0x97, 0x42 };
+/* Control report offsets for the D5 Next pump */
+#define D5NEXT_TEMP_CTRL_OFFSET                0x2D    /* Temperature sensor offsets location */
+static u16 d5next_ctrl_fan_offsets[] = { 0x97, 0x42 }; /* Pump and fan speed (from 0-100%) */
 
-/* Register offsets for the Farbwerk RGB controller */
+/* Spec and sensor report offset for the Farbwerk RGB controller */
 #define FARBWERK_NUM_SENSORS           4
 #define FARBWERK_SENSOR_START          0x2f
 
-/* Register offsets for the Farbwerk 360 RGB controller */
+/* Specs of the Farbwerk 360 RGB controller */
 #define FARBWERK360_NUM_SENSORS                        4
-#define FARBWERK360_SENSOR_START               0x32
 #define FARBWERK360_NUM_VIRTUAL_SENSORS                16
+#define FARBWERK360_CTRL_REPORT_SIZE           0x682
+
+/* Sensor report offsets for the Farbwerk 360 */
+#define FARBWERK360_SENSOR_START               0x32
 #define FARBWERK360_VIRTUAL_SENSORS_START      0x3a
 
-/* Register offsets for the Octo fan controller */
-#define OCTO_POWER_CYCLES              0x18
+/* Control report offsets for the Farbwerk 360 */
+#define FARBWERK360_TEMP_CTRL_OFFSET           0x8
+
+/* Specs of the Octo fan controller */
 #define OCTO_NUM_FANS                  8
 #define OCTO_NUM_SENSORS               4
-#define OCTO_SENSOR_START              0x3D
 #define OCTO_NUM_VIRTUAL_SENSORS       16
-#define OCTO_VIRTUAL_SENSORS_START     0x45
 #define OCTO_CTRL_REPORT_SIZE          0x65F
+
+/* Sensor report offsets for the Octo */
+#define OCTO_POWER_CYCLES              0x18
+#define OCTO_SENSOR_START              0x3D
+#define OCTO_VIRTUAL_SENSORS_START     0x45
 static u8 octo_sensor_fan_offsets[] = { 0x7D, 0x8A, 0x97, 0xA4, 0xB1, 0xBE, 0xCB, 0xD8 };
 
-/* Fan speed registers in Octo control report (from 0-100%) */
+/* Control report offsets for the Octo */
+#define OCTO_TEMP_CTRL_OFFSET          0xA
+/* Fan speed offsets (0-100%) */
 static u16 octo_ctrl_fan_offsets[] = { 0x5B, 0xB0, 0x105, 0x15A, 0x1AF, 0x204, 0x259, 0x2AE };
 
-/* Register offsets for the Quadro fan controller */
-#define QUADRO_POWER_CYCLES            0x18
+/* Specs of Quadro fan controller */
 #define QUADRO_NUM_FANS                        4
 #define QUADRO_NUM_SENSORS             4
-#define QUADRO_SENSOR_START            0x34
 #define QUADRO_NUM_VIRTUAL_SENSORS     16
-#define QUADRO_VIRTUAL_SENSORS_START   0x3c
 #define QUADRO_CTRL_REPORT_SIZE                0x3c1
+
+/* Sensor report offsets for the Quadro */
+#define QUADRO_POWER_CYCLES            0x18
+#define QUADRO_SENSOR_START            0x34
+#define QUADRO_VIRTUAL_SENSORS_START   0x3c
 #define QUADRO_FLOW_SENSOR_OFFSET      0x6e
 static u8 quadro_sensor_fan_offsets[] = { 0x70, 0x7D, 0x8A, 0x97 };
 
-/* Fan speed registers in Quadro control report (from 0-100%) */
-static u16 quadro_ctrl_fan_offsets[] = { 0x37, 0x8c, 0xe1, 0x136 };
+/* Control report offsets for the Quadro */
+#define QUADRO_TEMP_CTRL_OFFSET                0xA
+#define QUADRO_FLOW_PULSES_CTRL_OFFSET 0x6
+static u16 quadro_ctrl_fan_offsets[] = { 0x37, 0x8c, 0xe1, 0x136 }; /* Fan speed offsets (0-100%) */
 
-/* Register offsets for the High Flow Next */
+/* Specs of High Flow Next flow sensor */
 #define HIGHFLOWNEXT_NUM_SENSORS       2
+
+/* Sensor report offsets for the High Flow Next */
 #define HIGHFLOWNEXT_SENSOR_START      85
 #define HIGHFLOWNEXT_FLOW              81
 #define HIGHFLOWNEXT_WATER_QUALITY     89
@@ -282,8 +301,10 @@ struct aqc_data {
        int temp_sensor_start_offset;
        int num_virtual_temp_sensors;
        int virtual_temp_sensor_start_offset;
+       u16 temp_ctrl_offset;
        u16 power_cycle_count_offset;
        u8 flow_sensor_offset;
+       u8 flow_pulses_ctrl_offset;
 
        /* General info, same across all devices */
        u32 serial_number[2];
@@ -365,8 +386,8 @@ static int aqc_send_ctrl_data(struct aqc_data *priv)
        return ret;
 }
 
-/* Refreshes the control buffer and returns value at offset */
-static int aqc_get_ctrl_val(struct aqc_data *priv, int offset)
+/* Refreshes the control buffer and stores value at offset in val */
+static int aqc_get_ctrl_val(struct aqc_data *priv, int offset, long *val)
 {
        int ret;
 
@@ -376,7 +397,7 @@ static int aqc_get_ctrl_val(struct aqc_data *priv, int offset)
        if (ret < 0)
                goto unlock_and_return;
 
-       ret = get_unaligned_be16(priv->buffer + offset);
+       *val = (s16)get_unaligned_be16(priv->buffer + offset);
 
 unlock_and_return:
        mutex_unlock(&priv->mutex);
@@ -393,7 +414,7 @@ static int aqc_set_ctrl_val(struct aqc_data *priv, int offset, long val)
        if (ret < 0)
                goto unlock_and_return;
 
-       put_unaligned_be16((u16)val, priv->buffer + offset);
+       put_unaligned_be16((s16)val, priv->buffer + offset);
 
        ret = aqc_send_ctrl_data(priv);
 
@@ -408,8 +429,28 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3
 
        switch (type) {
        case hwmon_temp:
+               if (channel < priv->num_temp_sensors) {
+                       switch (attr) {
+                       case hwmon_temp_label:
+                       case hwmon_temp_input:
+                               return 0444;
+                       case hwmon_temp_offset:
+                               if (priv->temp_ctrl_offset != 0)
+                                       return 0644;
+                               break;
+                       default:
+                               break;
+                       }
+               }
+
                if (channel < priv->num_temp_sensors + priv->num_virtual_temp_sensors)
-                       return 0444;
+                       switch (attr) {
+                       case hwmon_temp_label:
+                       case hwmon_temp_input:
+                               return 0444;
+                       default:
+                               break;
+                       }
                break;
        case hwmon_pwm:
                if (priv->fan_ctrl_offsets && channel < priv->num_fans) {
@@ -422,20 +463,34 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3
                }
                break;
        case hwmon_fan:
-               switch (priv->kind) {
-               case highflownext:
-                       /* Special case to support flow sensor, water quality and conductivity */
-                       if (channel < 3)
-                               return 0444;
+               switch (attr) {
+               case hwmon_fan_input:
+               case hwmon_fan_label:
+                       switch (priv->kind) {
+                       case highflownext:
+                               /* Special case to support flow sensor, water quality
+                                * and conductivity
+                                */
+                               if (channel < 3)
+                                       return 0444;
+                               break;
+                       case quadro:
+                               /* Special case to support flow sensor */
+                               if (channel < priv->num_fans + 1)
+                                       return 0444;
+                               break;
+                       default:
+                               if (channel < priv->num_fans)
+                                       return 0444;
+                               break;
+                       }
                        break;
-               case quadro:
-                       /* Special case to support flow sensor */
-                       if (channel < priv->num_fans + 1)
-                               return 0444;
+               case hwmon_fan_pulses:
+                       /* Special case for Quadro flow sensor */
+                       if (priv->kind == quadro && channel == priv->num_fans)
+                               return 0644;
                        break;
                default:
-                       if (channel < priv->num_fans)
-                               return 0444;
                        break;
                }
                break;
@@ -492,20 +547,46 @@ static int aqc_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
 
        switch (type) {
        case hwmon_temp:
-               if (priv->temp_input[channel] == -ENODATA)
-                       return -ENODATA;
+               switch (attr) {
+               case hwmon_temp_input:
+                       if (priv->temp_input[channel] == -ENODATA)
+                               return -ENODATA;
 
-               *val = priv->temp_input[channel];
+                       *val = priv->temp_input[channel];
+                       break;
+               case hwmon_temp_offset:
+                       ret =
+                           aqc_get_ctrl_val(priv, priv->temp_ctrl_offset +
+                                            channel * AQC_TEMP_SENSOR_SIZE, val);
+                       if (ret < 0)
+                               return ret;
+
+                       *val *= 10;
+                       break;
+               default:
+                       break;
+               }
                break;
        case hwmon_fan:
-               *val = priv->speed_input[channel];
+               switch (attr) {
+               case hwmon_fan_input:
+                       *val = priv->speed_input[channel];
+                       break;
+               case hwmon_fan_pulses:
+                       ret = aqc_get_ctrl_val(priv, priv->flow_pulses_ctrl_offset, val);
+                       if (ret < 0)
+                               return ret;
+                       break;
+               default:
+                       break;
+               }
                break;
        case hwmon_power:
                *val = priv->power_input[channel];
                break;
        case hwmon_pwm:
                if (priv->fan_ctrl_offsets) {
-                       ret = aqc_get_ctrl_val(priv, priv->fan_ctrl_offsets[channel]);
+                       ret = aqc_get_ctrl_val(priv, priv->fan_ctrl_offsets[channel], val);
                        if (ret < 0)
                                return ret;
 
@@ -563,6 +644,33 @@ static int aqc_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
        struct aqc_data *priv = dev_get_drvdata(dev);
 
        switch (type) {
+       case hwmon_temp:
+               switch (attr) {
+               case hwmon_temp_offset:
+                       /* Limit temp offset to +/- 15K as in the official software */
+                       val = clamp_val(val, -15000, 15000) / 10;
+                       ret =
+                           aqc_set_ctrl_val(priv, priv->temp_ctrl_offset +
+                                            channel * AQC_TEMP_SENSOR_SIZE, val);
+                       if (ret < 0)
+                               return ret;
+                       break;
+               default:
+                       return -EOPNOTSUPP;
+               }
+               break;
+       case hwmon_fan:
+               switch (attr) {
+               case hwmon_fan_pulses:
+                       val = clamp_val(val, 10, 1000);
+                       ret = aqc_set_ctrl_val(priv, priv->flow_pulses_ctrl_offset, val);
+                       if (ret < 0)
+                               return ret;
+                       break;
+               default:
+                       break;
+               }
+               break;
        case hwmon_pwm:
                switch (attr) {
                case hwmon_pwm_input:
@@ -597,10 +705,10 @@ static const struct hwmon_ops aqc_hwmon_ops = {
 
 static const struct hwmon_channel_info *aqc_info[] = {
        HWMON_CHANNEL_INFO(temp,
-                          HWMON_T_INPUT | HWMON_T_LABEL,
-                          HWMON_T_INPUT | HWMON_T_LABEL,
-                          HWMON_T_INPUT | HWMON_T_LABEL,
-                          HWMON_T_INPUT | HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_OFFSET,
+                          HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_OFFSET,
+                          HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_OFFSET,
+                          HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_OFFSET,
                           HWMON_T_INPUT | HWMON_T_LABEL,
                           HWMON_T_INPUT | HWMON_T_LABEL,
                           HWMON_T_INPUT | HWMON_T_LABEL,
@@ -622,7 +730,7 @@ static const struct hwmon_channel_info *aqc_info[] = {
                           HWMON_F_INPUT | HWMON_F_LABEL,
                           HWMON_F_INPUT | HWMON_F_LABEL,
                           HWMON_F_INPUT | HWMON_F_LABEL,
-                          HWMON_F_INPUT | HWMON_F_LABEL,
+                          HWMON_F_INPUT | HWMON_F_LABEL | HWMON_F_PULSES,
                           HWMON_F_INPUT | HWMON_F_LABEL,
                           HWMON_F_INPUT | HWMON_F_LABEL,
                           HWMON_F_INPUT | HWMON_F_LABEL),
@@ -847,13 +955,17 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
                priv->num_fans = D5NEXT_NUM_FANS;
                priv->fan_sensor_offsets = d5next_sensor_fan_offsets;
                priv->fan_ctrl_offsets = d5next_ctrl_fan_offsets;
+
                priv->num_temp_sensors = D5NEXT_NUM_SENSORS;
                priv->temp_sensor_start_offset = D5NEXT_COOLANT_TEMP;
                priv->num_virtual_temp_sensors = D5NEXT_NUM_VIRTUAL_SENSORS;
                priv->virtual_temp_sensor_start_offset = D5NEXT_VIRTUAL_SENSORS_START;
-               priv->power_cycle_count_offset = D5NEXT_POWER_CYCLES;
+               priv->temp_ctrl_offset = D5NEXT_TEMP_CTRL_OFFSET;
+
                priv->buffer_size = D5NEXT_CTRL_REPORT_SIZE;
 
+               priv->power_cycle_count_offset = D5NEXT_POWER_CYCLES;
+
                priv->temp_label = label_d5next_temp;
                priv->virtual_temp_label = label_virtual_temp_sensors;
                priv->speed_label = label_d5next_speeds;
@@ -865,18 +977,24 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
                priv->kind = farbwerk;
 
                priv->num_fans = 0;
+
                priv->num_temp_sensors = FARBWERK_NUM_SENSORS;
                priv->temp_sensor_start_offset = FARBWERK_SENSOR_START;
+
                priv->temp_label = label_temp_sensors;
                break;
        case USB_PRODUCT_ID_FARBWERK360:
                priv->kind = farbwerk360;
 
                priv->num_fans = 0;
+
                priv->num_temp_sensors = FARBWERK360_NUM_SENSORS;
                priv->temp_sensor_start_offset = FARBWERK360_SENSOR_START;
                priv->num_virtual_temp_sensors = FARBWERK360_NUM_VIRTUAL_SENSORS;
                priv->virtual_temp_sensor_start_offset = FARBWERK360_VIRTUAL_SENSORS_START;
+               priv->temp_ctrl_offset = FARBWERK360_TEMP_CTRL_OFFSET;
+
+               priv->buffer_size = FARBWERK360_CTRL_REPORT_SIZE;
 
                priv->temp_label = label_temp_sensors;
                priv->virtual_temp_label = label_virtual_temp_sensors;
@@ -887,13 +1005,17 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
                priv->num_fans = OCTO_NUM_FANS;
                priv->fan_sensor_offsets = octo_sensor_fan_offsets;
                priv->fan_ctrl_offsets = octo_ctrl_fan_offsets;
+
                priv->num_temp_sensors = OCTO_NUM_SENSORS;
                priv->temp_sensor_start_offset = OCTO_SENSOR_START;
                priv->num_virtual_temp_sensors = OCTO_NUM_VIRTUAL_SENSORS;
                priv->virtual_temp_sensor_start_offset = OCTO_VIRTUAL_SENSORS_START;
-               priv->power_cycle_count_offset = OCTO_POWER_CYCLES;
+               priv->temp_ctrl_offset = OCTO_TEMP_CTRL_OFFSET;
+
                priv->buffer_size = OCTO_CTRL_REPORT_SIZE;
 
+               priv->power_cycle_count_offset = OCTO_POWER_CYCLES;
+
                priv->temp_label = label_temp_sensors;
                priv->virtual_temp_label = label_virtual_temp_sensors;
                priv->speed_label = label_fan_speed;
@@ -907,13 +1029,18 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
                priv->num_fans = QUADRO_NUM_FANS;
                priv->fan_sensor_offsets = quadro_sensor_fan_offsets;
                priv->fan_ctrl_offsets = quadro_ctrl_fan_offsets;
+
                priv->num_temp_sensors = QUADRO_NUM_SENSORS;
                priv->temp_sensor_start_offset = QUADRO_SENSOR_START;
                priv->num_virtual_temp_sensors = QUADRO_NUM_VIRTUAL_SENSORS;
                priv->virtual_temp_sensor_start_offset = QUADRO_VIRTUAL_SENSORS_START;
-               priv->power_cycle_count_offset = QUADRO_POWER_CYCLES;
+               priv->temp_ctrl_offset = QUADRO_TEMP_CTRL_OFFSET;
+
                priv->buffer_size = QUADRO_CTRL_REPORT_SIZE;
+
                priv->flow_sensor_offset = QUADRO_FLOW_SENSOR_OFFSET;
+               priv->flow_pulses_ctrl_offset = QUADRO_FLOW_PULSES_CTRL_OFFSET;
+               priv->power_cycle_count_offset = QUADRO_POWER_CYCLES;
 
                priv->temp_label = label_temp_sensors;
                priv->virtual_temp_label = label_virtual_temp_sensors;
@@ -926,8 +1053,10 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
                priv->kind = highflownext;
 
                priv->num_fans = 0;
+
                priv->num_temp_sensors = HIGHFLOWNEXT_NUM_SENSORS;
                priv->temp_sensor_start_offset = HIGHFLOWNEXT_SENSOR_START;
+
                priv->power_cycle_count_offset = QUADRO_POWER_CYCLES;
 
                priv->temp_label = label_highflownext_temp_sensors;
index 4fd8de8..118297e 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/hwmon.h>
 #include <linux/hwmon-vid.h>
 #include <linux/err.h>
+#include <linux/kstrtox.h>
 #include <linux/mutex.h>
 #include <linux/sysfs.h>
 #include <linux/slab.h>
index 9bee4d3..ca7a9b3 100644 (file)
@@ -55,6 +55,8 @@ MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius");
 
 /*
  * Per-Core Temperature Data
+ * @tjmax: The static tjmax value when tjmax cannot be retrieved from
+ *             IA32_TEMPERATURE_TARGET MSR.
  * @last_updated: The time when the current temperature value was updated
  *             earlier (in jiffies).
  * @cpu_core_id: The CPU Core from which temperature values should be read
@@ -64,11 +66,9 @@ MODULE_PARM_DESC(tjmax, "TjMax value in degrees Celsius");
  * @attr_size:  Total number of pre-core attrs displayed in the sysfs.
  * @is_pkg_data: If this is 1, the temp_data holds pkgtemp data.
  *             Otherwise, temp_data holds coretemp data.
- * @valid: If this is 1, the current temperature is valid.
  */
 struct temp_data {
        int temp;
-       int ttarget;
        int tjmax;
        unsigned long last_updated;
        unsigned int cpu;
@@ -76,7 +76,6 @@ struct temp_data {
        u32 status_reg;
        int attr_size;
        bool is_pkg_data;
-       bool valid;
        struct sensor_device_attribute sd_attrs[TOTAL_ATTRS];
        char attr_name[TOTAL_ATTRS][CORETEMP_NAME_LENGTH];
        struct attribute *attrs[TOTAL_ATTRS + 1];
@@ -95,85 +94,6 @@ struct platform_data {
        struct device_attribute name_attr;
 };
 
-/* Keep track of how many zone pointers we allocated in init() */
-static int max_zones __read_mostly;
-/* Array of zone pointers. Serialized by cpu hotplug lock */
-static struct platform_device **zone_devices;
-
-static ssize_t show_label(struct device *dev,
-                               struct device_attribute *devattr, char *buf)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct platform_data *pdata = dev_get_drvdata(dev);
-       struct temp_data *tdata = pdata->core_data[attr->index];
-
-       if (tdata->is_pkg_data)
-               return sprintf(buf, "Package id %u\n", pdata->pkg_id);
-
-       return sprintf(buf, "Core %u\n", tdata->cpu_core_id);
-}
-
-static ssize_t show_crit_alarm(struct device *dev,
-                               struct device_attribute *devattr, char *buf)
-{
-       u32 eax, edx;
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct platform_data *pdata = dev_get_drvdata(dev);
-       struct temp_data *tdata = pdata->core_data[attr->index];
-
-       mutex_lock(&tdata->update_lock);
-       rdmsr_on_cpu(tdata->cpu, tdata->status_reg, &eax, &edx);
-       mutex_unlock(&tdata->update_lock);
-
-       return sprintf(buf, "%d\n", (eax >> 5) & 1);
-}
-
-static ssize_t show_tjmax(struct device *dev,
-                       struct device_attribute *devattr, char *buf)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct platform_data *pdata = dev_get_drvdata(dev);
-
-       return sprintf(buf, "%d\n", pdata->core_data[attr->index]->tjmax);
-}
-
-static ssize_t show_ttarget(struct device *dev,
-                               struct device_attribute *devattr, char *buf)
-{
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct platform_data *pdata = dev_get_drvdata(dev);
-
-       return sprintf(buf, "%d\n", pdata->core_data[attr->index]->ttarget);
-}
-
-static ssize_t show_temp(struct device *dev,
-                       struct device_attribute *devattr, char *buf)
-{
-       u32 eax, edx;
-       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
-       struct platform_data *pdata = dev_get_drvdata(dev);
-       struct temp_data *tdata = pdata->core_data[attr->index];
-
-       mutex_lock(&tdata->update_lock);
-
-       /* Check whether the time interval has elapsed */
-       if (!tdata->valid || time_after(jiffies, tdata->last_updated + HZ)) {
-               rdmsr_on_cpu(tdata->cpu, tdata->status_reg, &eax, &edx);
-               /*
-                * Ignore the valid bit. In all observed cases the register
-                * value is either low or zero if the valid bit is 0.
-                * Return it instead of reporting an error which doesn't
-                * really help at all.
-                */
-               tdata->temp = tdata->tjmax - ((eax >> 16) & 0x7f) * 1000;
-               tdata->valid = true;
-               tdata->last_updated = jiffies;
-       }
-
-       mutex_unlock(&tdata->update_lock);
-       return sprintf(buf, "%d\n", tdata->temp);
-}
-
 struct tjmax_pci {
        unsigned int device;
        int tjmax;
@@ -340,20 +260,25 @@ static bool cpu_has_tjmax(struct cpuinfo_x86 *c)
               model != 0x36;
 }
 
-static int get_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev)
+static int get_tjmax(struct temp_data *tdata, struct device *dev)
 {
+       struct cpuinfo_x86 *c = &cpu_data(tdata->cpu);
        int err;
        u32 eax, edx;
        u32 val;
 
+       /* use static tjmax once it is set */
+       if (tdata->tjmax)
+               return tdata->tjmax;
+
        /*
         * A new feature of current Intel(R) processors, the
         * IA32_TEMPERATURE_TARGET contains the TjMax value
         */
-       err = rdmsr_safe_on_cpu(id, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
+       err = rdmsr_safe_on_cpu(tdata->cpu, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
        if (err) {
                if (cpu_has_tjmax(c))
-                       dev_warn(dev, "Unable to read TjMax from CPU %u\n", id);
+                       dev_warn(dev, "Unable to read TjMax from CPU %u\n", tdata->cpu);
        } else {
                val = (eax >> 16) & 0xff;
                /*
@@ -369,14 +294,133 @@ static int get_tjmax(struct cpuinfo_x86 *c, u32 id, struct device *dev)
        if (force_tjmax) {
                dev_notice(dev, "TjMax forced to %d degrees C by user\n",
                           force_tjmax);
-               return force_tjmax * 1000;
+               tdata->tjmax = force_tjmax * 1000;
+       } else {
+               /*
+                * An assumption is made for early CPUs and unreadable MSR.
+                * NOTE: the calculated value may not be correct.
+                */
+               tdata->tjmax = adjust_tjmax(c, tdata->cpu, dev);
        }
+       return tdata->tjmax;
+}
+
+static int get_ttarget(struct temp_data *tdata, struct device *dev)
+{
+       u32 eax, edx;
+       int tjmax, ttarget_offset, ret;
 
        /*
-        * An assumption is made for early CPUs and unreadable MSR.
-        * NOTE: the calculated value may not be correct.
+        * ttarget is valid only if tjmax can be retrieved from
+        * MSR_IA32_TEMPERATURE_TARGET
         */
-       return adjust_tjmax(c, id, dev);
+       if (tdata->tjmax)
+               return -ENODEV;
+
+       ret = rdmsr_safe_on_cpu(tdata->cpu, MSR_IA32_TEMPERATURE_TARGET, &eax, &edx);
+       if (ret)
+               return ret;
+
+       tjmax = (eax >> 16) & 0xff;
+
+       /* Read the still undocumented bits 8:15 of IA32_TEMPERATURE_TARGET. */
+       ttarget_offset = (eax >> 8) & 0xff;
+
+       return (tjmax - ttarget_offset) * 1000;
+}
+
+/* Keep track of how many zone pointers we allocated in init() */
+static int max_zones __read_mostly;
+/* Array of zone pointers. Serialized by cpu hotplug lock */
+static struct platform_device **zone_devices;
+
+static ssize_t show_label(struct device *dev,
+                               struct device_attribute *devattr, char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct platform_data *pdata = dev_get_drvdata(dev);
+       struct temp_data *tdata = pdata->core_data[attr->index];
+
+       if (tdata->is_pkg_data)
+               return sprintf(buf, "Package id %u\n", pdata->pkg_id);
+
+       return sprintf(buf, "Core %u\n", tdata->cpu_core_id);
+}
+
+static ssize_t show_crit_alarm(struct device *dev,
+                               struct device_attribute *devattr, char *buf)
+{
+       u32 eax, edx;
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct platform_data *pdata = dev_get_drvdata(dev);
+       struct temp_data *tdata = pdata->core_data[attr->index];
+
+       mutex_lock(&tdata->update_lock);
+       rdmsr_on_cpu(tdata->cpu, tdata->status_reg, &eax, &edx);
+       mutex_unlock(&tdata->update_lock);
+
+       return sprintf(buf, "%d\n", (eax >> 5) & 1);
+}
+
+static ssize_t show_tjmax(struct device *dev,
+                       struct device_attribute *devattr, char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct platform_data *pdata = dev_get_drvdata(dev);
+       struct temp_data *tdata = pdata->core_data[attr->index];
+       int tjmax;
+
+       mutex_lock(&tdata->update_lock);
+       tjmax = get_tjmax(tdata, dev);
+       mutex_unlock(&tdata->update_lock);
+
+       return sprintf(buf, "%d\n", tjmax);
+}
+
+static ssize_t show_ttarget(struct device *dev,
+                               struct device_attribute *devattr, char *buf)
+{
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct platform_data *pdata = dev_get_drvdata(dev);
+       struct temp_data *tdata = pdata->core_data[attr->index];
+       int ttarget;
+
+       mutex_lock(&tdata->update_lock);
+       ttarget = get_ttarget(tdata, dev);
+       mutex_unlock(&tdata->update_lock);
+
+       if (ttarget < 0)
+               return ttarget;
+       return sprintf(buf, "%d\n", ttarget);
+}
+
+static ssize_t show_temp(struct device *dev,
+                       struct device_attribute *devattr, char *buf)
+{
+       u32 eax, edx;
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       struct platform_data *pdata = dev_get_drvdata(dev);
+       struct temp_data *tdata = pdata->core_data[attr->index];
+       int tjmax;
+
+       mutex_lock(&tdata->update_lock);
+
+       tjmax = get_tjmax(tdata, dev);
+       /* Check whether the time interval has elapsed */
+       if (time_after(jiffies, tdata->last_updated + HZ)) {
+               rdmsr_on_cpu(tdata->cpu, tdata->status_reg, &eax, &edx);
+               /*
+                * Ignore the valid bit. In all observed cases the register
+                * value is either low or zero if the valid bit is 0.
+                * Return it instead of reporting an error which doesn't
+                * really help at all.
+                */
+               tdata->temp = tjmax - ((eax >> 16) & 0x7f) * 1000;
+               tdata->last_updated = jiffies;
+       }
+
+       mutex_unlock(&tdata->update_lock);
+       return sprintf(buf, "%d\n", tdata->temp);
 }
 
 static int create_core_attrs(struct temp_data *tdata, struct device *dev,
@@ -490,23 +534,17 @@ static int create_core_data(struct platform_device *pdev, unsigned int cpu,
        if (err)
                goto exit_free;
 
-       /* We can access status register. Get Critical Temperature */
-       tdata->tjmax = get_tjmax(c, cpu, &pdev->dev);
+       /* Make sure tdata->tjmax is a valid indicator for dynamic/static tjmax */
+       get_tjmax(tdata, &pdev->dev);
 
        /*
-        * Read the still undocumented bits 8:15 of IA32_TEMPERATURE_TARGET.
-        * The target temperature is available on older CPUs but not in this
-        * register. Atoms don't have the register at all.
+        * The target temperature is available on older CPUs but not in the
+        * MSR_IA32_TEMPERATURE_TARGET register. Atoms don't have the register
+        * at all.
         */
-       if (c->x86_model > 0xe && c->x86_model != 0x1c) {
-               err = rdmsr_safe_on_cpu(cpu, MSR_IA32_TEMPERATURE_TARGET,
-                                       &eax, &edx);
-               if (!err) {
-                       tdata->ttarget
-                         = tdata->tjmax - ((eax >> 8) & 0xff) * 1000;
+       if (c->x86_model > 0xe && c->x86_model != 0x1c)
+               if (get_ttarget(tdata, &pdev->dev) >= 0)
                        tdata->attr_size++;
-               }
-       }
 
        pdata->core_data[attr_no] = tdata;
 
index 1572b54..7ac778a 100644 (file)
@@ -1447,9 +1447,10 @@ static int __init i8k_init(void)
         */
        if (i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG1) &&
            i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG2)) {
-               pr_err("unable to get SMM Dell signature\n");
                if (!force)
                        return -ENODEV;
+
+               pr_err("Unable to get Dell SMM signature\n");
        }
 
        dell_smm_device = platform_create_bundle(&dell_smm_driver, dell_smm_probe, NULL, 0, NULL,
index 0886abf..e803d63 100644 (file)
@@ -269,7 +269,7 @@ static ssize_t update_interval_show(struct device *dev,
                                    struct device_attribute *da, char *buf)
 {
        struct ds1621_data *data = dev_get_drvdata(dev);
-       return scnprintf(buf, PAGE_SIZE, "%hu\n", data->update_interval);
+       return sysfs_emit(buf, "%hu\n", data->update_interval);
 }
 
 static ssize_t update_interval_store(struct device *dev,
index aa1f25a..6ad055e 100644 (file)
@@ -16,7 +16,6 @@ static const unsigned short
 emc2305_normal_i2c[] = { 0x27, 0x2c, 0x2d, 0x2e, 0x2f, 0x4c, 0x4d, I2C_CLIENT_END };
 
 #define EMC2305_REG_DRIVE_FAIL_STATUS  0x27
-#define EMC2305_REG_DEVICE             0xfd
 #define EMC2305_REG_VENDOR             0xfe
 #define EMC2305_FAN_MAX                        0xff
 #define EMC2305_FAN_MIN                        0x00
@@ -172,22 +171,12 @@ static int emc2305_get_max_state(struct thermal_cooling_device *cdev, unsigned l
        return 0;
 }
 
-static int emc2305_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
+static int __emc2305_set_cur_state(struct emc2305_data *data, int cdev_idx, unsigned long state)
 {
-       int cdev_idx, ret;
-       struct emc2305_data *data = cdev->devdata;
+       int ret;
        struct i2c_client *client = data->client;
        u8 val, i;
 
-       if (state > data->max_state)
-               return -EINVAL;
-
-       cdev_idx =  emc2305_get_cdev_idx(cdev);
-       if (cdev_idx < 0)
-               return cdev_idx;
-
-       /* Save thermal state. */
-       data->cdev_data[cdev_idx].last_thermal_state = state;
        state = max_t(unsigned long, state, data->cdev_data[cdev_idx].last_hwmon_state);
 
        val = EMC2305_PWM_STATE2DUTY(state, data->max_state, EMC2305_FAN_MAX);
@@ -212,6 +201,27 @@ static int emc2305_set_cur_state(struct thermal_cooling_device *cdev, unsigned l
        return 0;
 }
 
+static int emc2305_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
+{
+       int cdev_idx, ret;
+       struct emc2305_data *data = cdev->devdata;
+
+       if (state > data->max_state)
+               return -EINVAL;
+
+       cdev_idx =  emc2305_get_cdev_idx(cdev);
+       if (cdev_idx < 0)
+               return cdev_idx;
+
+       /* Save thermal state. */
+       data->cdev_data[cdev_idx].last_thermal_state = state;
+       ret = __emc2305_set_cur_state(data, cdev_idx, state);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
 static const struct thermal_cooling_device_ops emc2305_cooling_ops = {
        .get_max_state = emc2305_get_max_state,
        .get_cur_state = emc2305_get_cur_state,
@@ -402,7 +412,7 @@ emc2305_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int ch
                                 */
                                if (data->cdev_data[cdev_idx].last_hwmon_state >=
                                    data->cdev_data[cdev_idx].last_thermal_state)
-                                       return emc2305_set_cur_state(data->cdev_data[cdev_idx].cdev,
+                                       return __emc2305_set_cur_state(data, cdev_idx,
                                                        data->cdev_data[cdev_idx].last_hwmon_state);
                                return 0;
                        }
@@ -518,13 +528,13 @@ static int emc2305_identify(struct device *dev)
        return 0;
 }
 
-static int emc2305_probe(struct i2c_client *client, const struct i2c_device_id *id)
+static int emc2305_probe(struct i2c_client *client)
 {
        struct i2c_adapter *adapter = client->adapter;
        struct device *dev = &client->dev;
        struct emc2305_data *data;
        struct emc2305_platform_data *pdata;
-       int vendor, device;
+       int vendor;
        int ret;
        int i;
 
@@ -535,10 +545,6 @@ static int emc2305_probe(struct i2c_client *client, const struct i2c_device_id *
        if (vendor != EMC2305_VENDOR)
                return -ENODEV;
 
-       device = i2c_smbus_read_byte_data(client, EMC2305_REG_DEVICE);
-       if (device != EMC2305_DEVICE)
-               return -ENODEV;
-
        data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
        if (!data)
                return -ENOMEM;
@@ -607,7 +613,7 @@ static struct i2c_driver emc2305_driver = {
        .driver = {
                .name = "emc2305",
        },
-       .probe    = emc2305_probe,
+       .probe_new = emc2305_probe,
        .remove   = emc2305_remove,
        .id_table = emc2305_ids,
        .address_list = emc2305_normal_i2c,
index 0a77d61..e1f426e 100644 (file)
@@ -1083,9 +1083,9 @@ static int fschmd_detect(struct i2c_client *client,
 static int fschmd_probe(struct i2c_client *client)
 {
        struct fschmd_data *data;
-       const char * const names[7] = { "Poseidon", "Hermes", "Scylla",
+       static const char * const names[7] = { "Poseidon", "Hermes", "Scylla",
                                "Heracles", "Heimdall", "Hades", "Syleus" };
-       const int watchdog_minors[] = { WATCHDOG_MINOR, 212, 213, 214, 215 };
+       static const int watchdog_minors[] = { WATCHDOG_MINOR, 212, 213, 214, 215 };
        int i, err;
        enum chips kind = i2c_match_id(fschmd_id, client)->driver_data;
 
index ba40894..e75db6f 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/irq.h>
 #include <linux/platform_device.h>
 #include <linux/err.h>
+#include <linux/kstrtox.h>
 #include <linux/mutex.h>
 #include <linux/hwmon.h>
 #include <linux/gpio/consumer.h>
index b60ec95..73e5d92 100644 (file)
@@ -257,13 +257,10 @@ gsc_hwmon_get_devtree_pdata(struct device *dev)
        if (nchannels == 0)
                return ERR_PTR(-ENODEV);
 
-       pdata = devm_kzalloc(dev,
-                            sizeof(*pdata) + nchannels * sizeof(*ch),
+       pdata = devm_kzalloc(dev, struct_size(pdata, channels, nchannels),
                             GFP_KERNEL);
        if (!pdata)
                return ERR_PTR(-ENOMEM);
-       ch = (struct gsc_hwmon_channel *)(pdata + 1);
-       pdata->channels = ch;
        pdata->nchannels = nchannels;
 
        /* fan controller base address */
@@ -277,6 +274,7 @@ gsc_hwmon_get_devtree_pdata(struct device *dev)
 
        of_node_put(fan);
 
+       ch = pdata->channels;
        /* allocate structures for channels and count instances of each type */
        device_for_each_child_node(dev, child) {
                if (fwnode_property_read_string(child, "label", &ch->name)) {
index 4218750..33edb5c 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/gfp.h>
 #include <linux/hwmon.h>
 #include <linux/idr.h>
+#include <linux/kstrtox.h>
 #include <linux/list.h>
 #include <linux/module.h>
 #include <linux/pci.h>
index 7bd154b..9997f76 100644 (file)
@@ -69,6 +69,10 @@ static unsigned short force_id;
 module_param(force_id, ushort, 0);
 MODULE_PARM_DESC(force_id, "Override the detected device ID");
 
+static bool ignore_resource_conflict;
+module_param(ignore_resource_conflict, bool, 0);
+MODULE_PARM_DESC(ignore_resource_conflict, "Ignore ACPI resource conflict");
+
 static struct platform_device *it87_pdev[2];
 
 #define        REG_2E  0x2e    /* The register to read/write */
@@ -563,6 +567,14 @@ struct it87_data {
        s8 auto_temp[NUM_AUTO_PWM][5];  /* [nr][0] is point1_temp_hyst */
 };
 
+/* Board specific settings from DMI matching */
+struct it87_dmi_data {
+       u8 skip_pwm;            /* pwm channels to skip for this board  */
+};
+
+/* Global for results from DMI matching, if needed */
+static struct it87_dmi_data *dmi_data;
+
 static int adc_lsb(const struct it87_data *data, int nr)
 {
        int lsb;
@@ -2389,7 +2401,6 @@ static int __init it87_find(int sioaddr, unsigned short *address,
 {
        int err;
        u16 chip_type;
-       const char *board_vendor, *board_name;
        const struct it87_devices *config;
 
        err = superio_enter(sioaddr);
@@ -2397,7 +2408,13 @@ static int __init it87_find(int sioaddr, unsigned short *address,
                return err;
 
        err = -ENODEV;
-       chip_type = force_id ? force_id : superio_inw(sioaddr, DEVID);
+       chip_type = superio_inw(sioaddr, DEVID);
+       /* check first for a valid chip before forcing chip id */
+       if (chip_type == 0xffff)
+               goto exit;
+
+       if (force_id)
+               chip_type = force_id;
 
        switch (chip_type) {
        case IT8705F_DEVID:
@@ -2802,24 +2819,9 @@ static int __init it87_find(int sioaddr, unsigned short *address,
        if (sio_data->beep_pin)
                pr_info("Beeping is supported\n");
 
-       /* Disable specific features based on DMI strings */
-       board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR);
-       board_name = dmi_get_system_info(DMI_BOARD_NAME);
-       if (board_vendor && board_name) {
-               if (strcmp(board_vendor, "nVIDIA") == 0 &&
-                   strcmp(board_name, "FN68PT") == 0) {
-                       /*
-                        * On the Shuttle SN68PT, FAN_CTL2 is apparently not
-                        * connected to a fan, but to something else. One user
-                        * has reported instant system power-off when changing
-                        * the PWM2 duty cycle, so we disable it.
-                        * I use the board name string as the trigger in case
-                        * the same board is ever used in other systems.
-                        */
-                       pr_info("Disabling pwm2 due to hardware constraints\n");
-                       sio_data->skip_pwm = BIT(1);
-               }
-       }
+       /* Set values based on DMI matches */
+       if (dmi_data)
+               sio_data->skip_pwm |= dmi_data->skip_pwm;
 
 exit:
        superio_exit(sioaddr);
@@ -3261,8 +3263,10 @@ static int __init it87_device_add(int index, unsigned short address,
        int err;
 
        err = acpi_check_resource_conflict(&res);
-       if (err)
-               return err;
+       if (err) {
+               if (!ignore_resource_conflict)
+                       return err;
+       }
 
        pdev = platform_device_alloc(DRVNAME, address);
        if (!pdev)
@@ -3295,6 +3299,46 @@ exit_device_put:
        return err;
 }
 
+/* callback function for DMI */
+static int it87_dmi_cb(const struct dmi_system_id *dmi_entry)
+{
+       dmi_data = dmi_entry->driver_data;
+
+       if (dmi_data && dmi_data->skip_pwm)
+               pr_info("Disabling pwm2 due to hardware constraints\n");
+
+       return 1;
+}
+
+/*
+ * On the Shuttle SN68PT, FAN_CTL2 is apparently not
+ * connected to a fan, but to something else. One user
+ * has reported instant system power-off when changing
+ * the PWM2 duty cycle, so we disable it.
+ * I use the board name string as the trigger in case
+ * the same board is ever used in other systems.
+ */
+static struct it87_dmi_data nvidia_fn68pt = {
+       .skip_pwm = BIT(1),
+};
+
+#define IT87_DMI_MATCH_VND(vendor, name, cb, data) \
+       { \
+               .callback = cb, \
+               .matches = { \
+                       DMI_EXACT_MATCH(DMI_BOARD_VENDOR, vendor), \
+                       DMI_EXACT_MATCH(DMI_BOARD_NAME, name), \
+               }, \
+               .driver_data = data, \
+       }
+
+static const struct dmi_system_id it87_dmi_table[] __initconst = {
+       IT87_DMI_MATCH_VND("nVIDIA", "FN68PT", it87_dmi_cb, &nvidia_fn68pt),
+       { }
+
+};
+MODULE_DEVICE_TABLE(dmi, it87_dmi_table);
+
 static int __init sm_it87_init(void)
 {
        int sioaddr[2] = { REG_2E, REG_4E };
@@ -3307,6 +3351,8 @@ static int __init sm_it87_init(void)
        if (err)
                return err;
 
+       dmi_check_system(it87_dmi_table);
+
        for (i = 0; i < ARRAY_SIZE(sioaddr); i++) {
                memset(&sio_data, 0, sizeof(struct it87_sio_data));
                isa_address[i] = 0;
index 30888fe..8523bf9 100644 (file)
@@ -10,6 +10,7 @@
  */
 
 #include <linux/bitops.h>
+#include <linux/bitfield.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/slab.h>
@@ -19,6 +20,7 @@
 #include <linux/err.h>
 #include <linux/mutex.h>
 #include <linux/of.h>
+#include <linux/regmap.h>
 
 /* Addresses to scan */
 static const unsigned short normal_i2c[] = {
@@ -36,20 +38,19 @@ static const unsigned short normal_i2c[] = {
 #define JC42_REG_SMBUS         0x22 /* NXP and Atmel, possibly others? */
 
 /* Status bits in temperature register */
-#define JC42_ALARM_CRIT_BIT    15
-#define JC42_ALARM_MAX_BIT     14
-#define JC42_ALARM_MIN_BIT     13
+#define JC42_ALARM_CRIT                BIT(15)
+#define JC42_ALARM_MAX         BIT(14)
+#define JC42_ALARM_MIN         BIT(13)
 
 /* Configuration register defines */
-#define JC42_CFG_CRIT_ONLY     (1 << 2)
-#define JC42_CFG_TCRIT_LOCK    (1 << 6)
-#define JC42_CFG_EVENT_LOCK    (1 << 7)
-#define JC42_CFG_SHUTDOWN      (1 << 8)
-#define JC42_CFG_HYST_SHIFT    9
-#define JC42_CFG_HYST_MASK     (0x03 << 9)
+#define JC42_CFG_CRIT_ONLY     BIT(2)
+#define JC42_CFG_TCRIT_LOCK    BIT(6)
+#define JC42_CFG_EVENT_LOCK    BIT(7)
+#define JC42_CFG_SHUTDOWN      BIT(8)
+#define JC42_CFG_HYST_MASK     GENMASK(10, 9)
 
 /* Capabilities */
-#define JC42_CAP_RANGE         (1 << 2)
+#define JC42_CAP_RANGE         BIT(2)
 
 /* Manufacturer IDs */
 #define ADT_MANID              0x11d4  /* Analog Devices */
@@ -199,31 +200,14 @@ static struct jc42_chips jc42_chips[] = {
        { STM_MANID, STTS3000_DEVID, STTS3000_DEVID_MASK },
 };
 
-enum temp_index {
-       t_input = 0,
-       t_crit,
-       t_min,
-       t_max,
-       t_num_temp
-};
-
-static const u8 temp_regs[t_num_temp] = {
-       [t_input] = JC42_REG_TEMP,
-       [t_crit] = JC42_REG_TEMP_CRITICAL,
-       [t_min] = JC42_REG_TEMP_LOWER,
-       [t_max] = JC42_REG_TEMP_UPPER,
-};
-
 /* Each client has this additional data */
 struct jc42_data {
-       struct i2c_client *client;
        struct mutex    update_lock;    /* protect register access */
+       struct regmap   *regmap;
        bool            extended;       /* true if extended range supported */
        bool            valid;
-       unsigned long   last_updated;   /* In jiffies */
        u16             orig_config;    /* original configuration */
        u16             config;         /* current configuration */
-       u16             temp[t_num_temp];/* Temperatures */
 };
 
 #define JC42_TEMP_MIN_EXTENDED (-40000)
@@ -248,85 +232,102 @@ static int jc42_temp_from_reg(s16 reg)
        return reg * 125 / 2;
 }
 
-static struct jc42_data *jc42_update_device(struct device *dev)
-{
-       struct jc42_data *data = dev_get_drvdata(dev);
-       struct i2c_client *client = data->client;
-       struct jc42_data *ret = data;
-       int i, val;
-
-       mutex_lock(&data->update_lock);
-
-       if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
-               for (i = 0; i < t_num_temp; i++) {
-                       val = i2c_smbus_read_word_swapped(client, temp_regs[i]);
-                       if (val < 0) {
-                               ret = ERR_PTR(val);
-                               goto abort;
-                       }
-                       data->temp[i] = val;
-               }
-               data->last_updated = jiffies;
-               data->valid = true;
-       }
-abort:
-       mutex_unlock(&data->update_lock);
-       return ret;
-}
-
 static int jc42_read(struct device *dev, enum hwmon_sensor_types type,
                     u32 attr, int channel, long *val)
 {
-       struct jc42_data *data = jc42_update_device(dev);
-       int temp, hyst;
+       struct jc42_data *data = dev_get_drvdata(dev);
+       unsigned int regval;
+       int ret, temp, hyst;
 
-       if (IS_ERR(data))
-               return PTR_ERR(data);
+       mutex_lock(&data->update_lock);
 
        switch (attr) {
        case hwmon_temp_input:
-               *val = jc42_temp_from_reg(data->temp[t_input]);
-               return 0;
+               ret = regmap_read(data->regmap, JC42_REG_TEMP, &regval);
+               if (ret)
+                       break;
+
+               *val = jc42_temp_from_reg(regval);
+               break;
        case hwmon_temp_min:
-               *val = jc42_temp_from_reg(data->temp[t_min]);
-               return 0;
+               ret = regmap_read(data->regmap, JC42_REG_TEMP_LOWER, &regval);
+               if (ret)
+                       break;
+
+               *val = jc42_temp_from_reg(regval);
+               break;
        case hwmon_temp_max:
-               *val = jc42_temp_from_reg(data->temp[t_max]);
-               return 0;
+               ret = regmap_read(data->regmap, JC42_REG_TEMP_UPPER, &regval);
+               if (ret)
+                       break;
+
+               *val = jc42_temp_from_reg(regval);
+               break;
        case hwmon_temp_crit:
-               *val = jc42_temp_from_reg(data->temp[t_crit]);
-               return 0;
+               ret = regmap_read(data->regmap, JC42_REG_TEMP_CRITICAL,
+                                 &regval);
+               if (ret)
+                       break;
+
+               *val = jc42_temp_from_reg(regval);
+               break;
        case hwmon_temp_max_hyst:
-               temp = jc42_temp_from_reg(data->temp[t_max]);
-               hyst = jc42_hysteresis[(data->config & JC42_CFG_HYST_MASK)
-                                               >> JC42_CFG_HYST_SHIFT];
+               ret = regmap_read(data->regmap, JC42_REG_TEMP_UPPER, &regval);
+               if (ret)
+                       break;
+
+               temp = jc42_temp_from_reg(regval);
+               hyst = jc42_hysteresis[FIELD_GET(JC42_CFG_HYST_MASK,
+                                                data->config)];
                *val = temp - hyst;
-               return 0;
+               break;
        case hwmon_temp_crit_hyst:
-               temp = jc42_temp_from_reg(data->temp[t_crit]);
-               hyst = jc42_hysteresis[(data->config & JC42_CFG_HYST_MASK)
-                                               >> JC42_CFG_HYST_SHIFT];
+               ret = regmap_read(data->regmap, JC42_REG_TEMP_CRITICAL,
+                                 &regval);
+               if (ret)
+                       break;
+
+               temp = jc42_temp_from_reg(regval);
+               hyst = jc42_hysteresis[FIELD_GET(JC42_CFG_HYST_MASK,
+                                                data->config)];
                *val = temp - hyst;
-               return 0;
+               break;
        case hwmon_temp_min_alarm:
-               *val = (data->temp[t_input] >> JC42_ALARM_MIN_BIT) & 1;
-               return 0;
+               ret = regmap_read(data->regmap, JC42_REG_TEMP, &regval);
+               if (ret)
+                       break;
+
+               *val = FIELD_GET(JC42_ALARM_MIN, regval);
+               break;
        case hwmon_temp_max_alarm:
-               *val = (data->temp[t_input] >> JC42_ALARM_MAX_BIT) & 1;
-               return 0;
+               ret = regmap_read(data->regmap, JC42_REG_TEMP, &regval);
+               if (ret)
+                       break;
+
+               *val = FIELD_GET(JC42_ALARM_MAX, regval);
+               break;
        case hwmon_temp_crit_alarm:
-               *val = (data->temp[t_input] >> JC42_ALARM_CRIT_BIT) & 1;
-               return 0;
+               ret = regmap_read(data->regmap, JC42_REG_TEMP, &regval);
+               if (ret)
+                       break;
+
+               *val = FIELD_GET(JC42_ALARM_CRIT, regval);
+               break;
        default:
-               return -EOPNOTSUPP;
+               ret = -EOPNOTSUPP;
+               break;
        }
+
+       mutex_unlock(&data->update_lock);
+
+       return ret;
 }
 
 static int jc42_write(struct device *dev, enum hwmon_sensor_types type,
                      u32 attr, int channel, long val)
 {
        struct jc42_data *data = dev_get_drvdata(dev);
-       struct i2c_client *client = data->client;
+       unsigned int regval;
        int diff, hyst;
        int ret;
 
@@ -334,21 +335,23 @@ static int jc42_write(struct device *dev, enum hwmon_sensor_types type,
 
        switch (attr) {
        case hwmon_temp_min:
-               data->temp[t_min] = jc42_temp_to_reg(val, data->extended);
-               ret = i2c_smbus_write_word_swapped(client, temp_regs[t_min],
-                                                  data->temp[t_min]);
+               ret = regmap_write(data->regmap, JC42_REG_TEMP_LOWER,
+                                  jc42_temp_to_reg(val, data->extended));
                break;
        case hwmon_temp_max:
-               data->temp[t_max] = jc42_temp_to_reg(val, data->extended);
-               ret = i2c_smbus_write_word_swapped(client, temp_regs[t_max],
-                                                  data->temp[t_max]);
+               ret = regmap_write(data->regmap, JC42_REG_TEMP_UPPER,
+                                  jc42_temp_to_reg(val, data->extended));
                break;
        case hwmon_temp_crit:
-               data->temp[t_crit] = jc42_temp_to_reg(val, data->extended);
-               ret = i2c_smbus_write_word_swapped(client, temp_regs[t_crit],
-                                                  data->temp[t_crit]);
+               ret = regmap_write(data->regmap, JC42_REG_TEMP_CRITICAL,
+                                  jc42_temp_to_reg(val, data->extended));
                break;
        case hwmon_temp_crit_hyst:
+               ret = regmap_read(data->regmap, JC42_REG_TEMP_CRITICAL,
+                                 &regval);
+               if (ret)
+                       break;
+
                /*
                 * JC42.4 compliant chips only support four hysteresis values.
                 * Pick best choice and go from there.
@@ -356,7 +359,7 @@ static int jc42_write(struct device *dev, enum hwmon_sensor_types type,
                val = clamp_val(val, (data->extended ? JC42_TEMP_MIN_EXTENDED
                                                     : JC42_TEMP_MIN) - 6000,
                                JC42_TEMP_MAX);
-               diff = jc42_temp_from_reg(data->temp[t_crit]) - val;
+               diff = jc42_temp_from_reg(regval) - val;
                hyst = 0;
                if (diff > 0) {
                        if (diff < 2250)
@@ -367,10 +370,9 @@ static int jc42_write(struct device *dev, enum hwmon_sensor_types type,
                                hyst = 3;       /* 6.0 degrees C */
                }
                data->config = (data->config & ~JC42_CFG_HYST_MASK) |
-                               (hyst << JC42_CFG_HYST_SHIFT);
-               ret = i2c_smbus_write_word_swapped(data->client,
-                                                  JC42_REG_CONFIG,
-                                                  data->config);
+                               FIELD_PREP(JC42_CFG_HYST_MASK, hyst);
+               ret = regmap_write(data->regmap, JC42_REG_CONFIG,
+                                  data->config);
                break;
        default:
                ret = -EOPNOTSUPP;
@@ -470,51 +472,80 @@ static const struct hwmon_chip_info jc42_chip_info = {
        .info = jc42_info,
 };
 
+static bool jc42_readable_reg(struct device *dev, unsigned int reg)
+{
+       return (reg >= JC42_REG_CAP && reg <= JC42_REG_DEVICEID) ||
+               reg == JC42_REG_SMBUS;
+}
+
+static bool jc42_writable_reg(struct device *dev, unsigned int reg)
+{
+       return (reg >= JC42_REG_CONFIG && reg <= JC42_REG_TEMP_CRITICAL) ||
+               reg == JC42_REG_SMBUS;
+}
+
+static bool jc42_volatile_reg(struct device *dev, unsigned int reg)
+{
+       return reg == JC42_REG_CONFIG || reg == JC42_REG_TEMP;
+}
+
+static const struct regmap_config jc42_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 16,
+       .val_format_endian = REGMAP_ENDIAN_BIG,
+       .max_register = JC42_REG_SMBUS,
+       .writeable_reg = jc42_writable_reg,
+       .readable_reg = jc42_readable_reg,
+       .volatile_reg = jc42_volatile_reg,
+       .cache_type = REGCACHE_RBTREE,
+};
+
 static int jc42_probe(struct i2c_client *client)
 {
        struct device *dev = &client->dev;
        struct device *hwmon_dev;
+       unsigned int config, cap;
        struct jc42_data *data;
-       int config, cap;
+       int ret;
 
        data = devm_kzalloc(dev, sizeof(struct jc42_data), GFP_KERNEL);
        if (!data)
                return -ENOMEM;
 
-       data->client = client;
+       data->regmap = devm_regmap_init_i2c(client, &jc42_regmap_config);
+       if (IS_ERR(data->regmap))
+               return PTR_ERR(data->regmap);
+
        i2c_set_clientdata(client, data);
        mutex_init(&data->update_lock);
 
-       cap = i2c_smbus_read_word_swapped(client, JC42_REG_CAP);
-       if (cap < 0)
-               return cap;
+       ret = regmap_read(data->regmap, JC42_REG_CAP, &cap);
+       if (ret)
+               return ret;
 
        data->extended = !!(cap & JC42_CAP_RANGE);
 
        if (device_property_read_bool(dev, "smbus-timeout-disable")) {
-               int smbus;
-
                /*
                 * Not all chips support this register, but from a
                 * quick read of various datasheets no chip appears
                 * incompatible with the below attempt to disable
                 * the timeout. And the whole thing is opt-in...
                 */
-               smbus = i2c_smbus_read_word_swapped(client, JC42_REG_SMBUS);
-               if (smbus < 0)
-                       return smbus;
-               i2c_smbus_write_word_swapped(client, JC42_REG_SMBUS,
-                                            smbus | SMBUS_STMOUT);
+               ret = regmap_set_bits(data->regmap, JC42_REG_SMBUS,
+                                     SMBUS_STMOUT);
+               if (ret)
+                       return ret;
        }
 
-       config = i2c_smbus_read_word_swapped(client, JC42_REG_CONFIG);
-       if (config < 0)
-               return config;
+       ret = regmap_read(data->regmap, JC42_REG_CONFIG, &config);
+       if (ret)
+               return ret;
 
        data->orig_config = config;
        if (config & JC42_CFG_SHUTDOWN) {
                config &= ~JC42_CFG_SHUTDOWN;
-               i2c_smbus_write_word_swapped(client, JC42_REG_CONFIG, config);
+               regmap_write(data->regmap, JC42_REG_CONFIG, config);
        }
        data->config = config;
 
@@ -535,7 +566,7 @@ static void jc42_remove(struct i2c_client *client)
 
                config = (data->orig_config & ~JC42_CFG_HYST_MASK)
                  | (data->config & JC42_CFG_HYST_MASK);
-               i2c_smbus_write_word_swapped(client, JC42_REG_CONFIG, config);
+               regmap_write(data->regmap, JC42_REG_CONFIG, config);
        }
 }
 
@@ -546,8 +577,11 @@ static int jc42_suspend(struct device *dev)
        struct jc42_data *data = dev_get_drvdata(dev);
 
        data->config |= JC42_CFG_SHUTDOWN;
-       i2c_smbus_write_word_swapped(data->client, JC42_REG_CONFIG,
-                                    data->config);
+       regmap_write(data->regmap, JC42_REG_CONFIG, data->config);
+
+       regcache_cache_only(data->regmap, true);
+       regcache_mark_dirty(data->regmap);
+
        return 0;
 }
 
@@ -555,10 +589,13 @@ static int jc42_resume(struct device *dev)
 {
        struct jc42_data *data = dev_get_drvdata(dev);
 
+       regcache_cache_only(data->regmap, false);
+
        data->config &= ~JC42_CFG_SHUTDOWN;
-       i2c_smbus_write_word_swapped(data->client, JC42_REG_CONFIG,
-                                    data->config);
-       return 0;
+       regmap_write(data->regmap, JC42_REG_CONFIG, data->config);
+
+       /* Restore cached register values to hardware */
+       return regcache_sync(data->regmap);
 }
 
 static const struct dev_pm_ops jc42_dev_pm_ops = {
index 1346b3b..b6433ae 100644 (file)
@@ -92,7 +92,7 @@ static ssize_t temp_show(struct device *dev, struct device_attribute *da,
        /* use integer division instead of equivalent right shift to
           guarantee arithmetic shift and preserve the sign */
        temp = (((s16) err) * 250) / 32;
-       return scnprintf(buf, PAGE_SIZE, "%d\n", temp);
+       return sysfs_emit(buf, "%d\n", temp);
 }
 
 static ssize_t convrate_store(struct device *dev, struct device_attribute *da,
@@ -137,7 +137,7 @@ static ssize_t convrate_show(struct device *dev, struct device_attribute *da,
        int res;
 
        res = (data->ctrl & LM73_CTRL_RES_MASK) >> LM73_CTRL_RES_SHIFT;
-       return scnprintf(buf, PAGE_SIZE, "%hu\n", lm73_convrates[res]);
+       return sysfs_emit(buf, "%hu\n", lm73_convrates[res]);
 }
 
 static ssize_t maxmin_alarm_show(struct device *dev,
@@ -154,7 +154,7 @@ static ssize_t maxmin_alarm_show(struct device *dev,
        data->ctrl = ctrl;
        mutex_unlock(&data->lock);
 
-       return scnprintf(buf, PAGE_SIZE, "%d\n", (ctrl >> attr->index) & 1);
+       return sysfs_emit(buf, "%d\n", (ctrl >> attr->index) & 1);
 
 abort:
        mutex_unlock(&data->lock);
index db595f7..6498d5a 100644 (file)
 #include <linux/interrupt.h>
 #include <linux/jiffies.h>
 #include <linux/hwmon.h>
+#include <linux/kstrtox.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/of_device.h>
@@ -2663,11 +2664,6 @@ static void lm90_remove_pec(void *dev)
        device_remove_file(dev, &dev_attr_pec);
 }
 
-static void lm90_regulator_disable(void *regulator)
-{
-       regulator_disable(regulator);
-}
-
 static int lm90_probe_channel_from_dt(struct i2c_client *client,
                                      struct device_node *child,
                                      struct lm90_data *data)
@@ -2749,24 +2745,13 @@ static int lm90_probe(struct i2c_client *client)
        struct device *dev = &client->dev;
        struct i2c_adapter *adapter = client->adapter;
        struct hwmon_channel_info *info;
-       struct regulator *regulator;
        struct device *hwmon_dev;
        struct lm90_data *data;
        int err;
 
-       regulator = devm_regulator_get(dev, "vcc");
-       if (IS_ERR(regulator))
-               return PTR_ERR(regulator);
-
-       err = regulator_enable(regulator);
-       if (err < 0) {
-               dev_err(dev, "Failed to enable regulator: %d\n", err);
-               return err;
-       }
-
-       err = devm_add_action_or_reset(dev, lm90_regulator_disable, regulator);
+       err = devm_regulator_get_enable(dev, "vcc");
        if (err)
-               return err;
+               return dev_err_probe(dev, err, "Failed to enable regulator\n");
 
        data = devm_kzalloc(dev, sizeof(struct lm90_data), GFP_KERNEL);
        if (!data)
index 72489d5..8851415 100644 (file)
@@ -881,7 +881,7 @@ static int ltc2992_parse_dt(struct ltc2992_state *st)
        return 0;
 }
 
-static int ltc2992_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
+static int ltc2992_i2c_probe(struct i2c_client *client)
 {
        struct device *hwmon_dev;
        struct ltc2992_state *st;
@@ -927,7 +927,7 @@ static struct i2c_driver ltc2992_i2c_driver = {
                .name = "ltc2992",
                .of_match_table = ltc2992_of_match,
        },
-       .probe    = ltc2992_i2c_probe,
+       .probe_new = ltc2992_i2c_probe,
        .id_table = ltc2992_i2c_id,
 };
 
index 402ffdc..0e21e7e 100644 (file)
@@ -303,8 +303,7 @@ static const struct hwmon_chip_info max127_chip_info = {
        .info = max127_info,
 };
 
-static int max127_probe(struct i2c_client *client,
-                       const struct i2c_device_id *id)
+static int max127_probe(struct i2c_client *client)
 {
        int i;
        struct device *hwmon_dev;
@@ -340,7 +339,7 @@ static struct i2c_driver max127_driver = {
        .driver = {
                .name   = "max127",
        },
-       .probe          = max127_probe,
+       .probe_new      = max127_probe,
        .id_table       = max127_id,
 };
 
index 394a4c7..50a8b9c 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/clk.h>
 #include <linux/debugfs.h>
 #include <linux/hwmon.h>
+#include <linux/kstrtox.h>
 #include <linux/module.h>
 #include <linux/mod_devicetable.h>
 #include <linux/mutex.h>
index b347837..bf43f73 100644 (file)
@@ -1043,7 +1043,9 @@ static struct platform_device *pdev[2];
 
 static const char * const asus_wmi_boards[] = {
        "PRO H410T",
+       "ProArt B550-CREATOR",
        "ProArt X570-CREATOR WIFI",
+       "ProArt Z490-CREATOR 10G",
        "Pro B550M-C",
        "Pro WS X570-ACE",
        "PRIME B360-PLUS",
@@ -1055,8 +1057,10 @@ static const char * const asus_wmi_boards[] = {
        "PRIME X570-P",
        "PRIME X570-PRO",
        "ROG CROSSHAIR VIII DARK HERO",
+       "ROG CROSSHAIR VIII EXTREME",
        "ROG CROSSHAIR VIII FORMULA",
        "ROG CROSSHAIR VIII HERO",
+       "ROG CROSSHAIR VIII HERO (WI-FI)",
        "ROG CROSSHAIR VIII IMPACT",
        "ROG STRIX B550-A GAMING",
        "ROG STRIX B550-E GAMING",
@@ -1080,8 +1084,11 @@ static const char * const asus_wmi_boards[] = {
        "ROG STRIX Z490-G GAMING (WI-FI)",
        "ROG STRIX Z490-H GAMING",
        "ROG STRIX Z490-I GAMING",
+       "TUF GAMING B550M-E",
+       "TUF GAMING B550M-E (WI-FI)",
        "TUF GAMING B550M-PLUS",
        "TUF GAMING B550M-PLUS (WI-FI)",
+       "TUF GAMING B550M-PLUS WIFI II",
        "TUF GAMING B550-PLUS",
        "TUF GAMING B550-PLUS WIFI II",
        "TUF GAMING B550-PRO",
index 35a7070..348c211 100644 (file)
@@ -6,7 +6,6 @@
 config SENSORS_OCC_P8_I2C
        tristate "POWER8 OCC through I2C"
        depends on I2C
-       depends on ARM || ARM64 || COMPILE_TEST
        select SENSORS_OCC
        help
         This option enables support for monitoring sensors provided by the
@@ -21,7 +20,6 @@ config SENSORS_OCC_P8_I2C
 config SENSORS_OCC_P9_SBE
        tristate "POWER9 OCC through SBE"
        depends on FSI_OCC
-       depends on ARM || ARM64 || COMPILE_TEST
        select SENSORS_OCC
        help
         This option enables support for monitoring sensors provided by the
diff --git a/drivers/hwmon/oxp-sensors.c b/drivers/hwmon/oxp-sensors.c
new file mode 100644 (file)
index 0000000..f84ec8f
--- /dev/null
@@ -0,0 +1,284 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Platform driver for OXP Handhelds that expose fan reading and control
+ * via hwmon sysfs.
+ *
+ * Old boards have the same DMI strings and they are told appart by the
+ * boot cpu vendor (Intel/AMD). Currently only AMD boards are supported
+ * but the code is made to be simple to add other handheld boards in the
+ * future.
+ * Fan control is provided via pwm interface in the range [0-255].
+ * Old AMD boards use [0-100] as range in the EC, the written value is
+ * scaled to accommodate for that. Newer boards like the mini PRO and
+ * AOK ZOE are not scaled but have the same EC layout.
+ *
+ * Copyright (C) 2022 Joaquín I. Aramendía <samsagax@gmail.com>
+ */
+
+#include <linux/acpi.h>
+#include <linux/dev_printk.h>
+#include <linux/dmi.h>
+#include <linux/hwmon.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/processor.h>
+
+/* Handle ACPI lock mechanism */
+static u32 oxp_mutex;
+
+#define ACPI_LOCK_DELAY_MS     500
+
+static bool lock_global_acpi_lock(void)
+{
+       return ACPI_SUCCESS(acpi_acquire_global_lock(ACPI_LOCK_DELAY_MS, &oxp_mutex));
+}
+
+static bool unlock_global_acpi_lock(void)
+{
+       return ACPI_SUCCESS(acpi_release_global_lock(oxp_mutex));
+}
+
+enum oxp_board {
+       aok_zoe_a1 = 1,
+       oxp_mini_amd,
+       oxp_mini_amd_pro,
+};
+
+static enum oxp_board board;
+
+#define OXP_SENSOR_FAN_REG             0x76 /* Fan reading is 2 registers long */
+#define OXP_SENSOR_PWM_ENABLE_REG      0x4A /* PWM enable is 1 register long */
+#define OXP_SENSOR_PWM_REG             0x4B /* PWM reading is 1 register long */
+
+static const struct dmi_system_id dmi_table[] = {
+       {
+               .matches = {
+                       DMI_MATCH(DMI_BOARD_VENDOR, "AOKZOE"),
+                       DMI_EXACT_MATCH(DMI_BOARD_NAME, "AOKZOE A1 AR07"),
+               },
+               .driver_data = (void *) &(enum oxp_board) {aok_zoe_a1},
+       },
+       {
+               .matches = {
+                       DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
+                       DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONE XPLAYER"),
+               },
+               .driver_data = (void *) &(enum oxp_board) {oxp_mini_amd},
+       },
+       {
+               .matches = {
+                       DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"),
+                       DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER Mini Pro"),
+               },
+               .driver_data = (void *) &(enum oxp_board) {oxp_mini_amd_pro},
+       },
+       {},
+};
+
+/* Helper functions to handle EC read/write */
+static int read_from_ec(u8 reg, int size, long *val)
+{
+       int i;
+       int ret;
+       u8 buffer;
+
+       if (!lock_global_acpi_lock())
+               return -EBUSY;
+
+       *val = 0;
+       for (i = 0; i < size; i++) {
+               ret = ec_read(reg + i, &buffer);
+               if (ret)
+                       return ret;
+               *val <<= i * 8;
+               *val += buffer;
+       }
+
+       if (!unlock_global_acpi_lock())
+               return -EBUSY;
+
+       return 0;
+}
+
+static int write_to_ec(const struct device *dev, u8 reg, u8 value)
+{
+       int ret;
+
+       if (!lock_global_acpi_lock())
+               return -EBUSY;
+
+       ret = ec_write(reg, value);
+
+       if (!unlock_global_acpi_lock())
+               return -EBUSY;
+
+       return ret;
+}
+
+static int oxp_pwm_enable(const struct device *dev)
+{
+       return write_to_ec(dev, OXP_SENSOR_PWM_ENABLE_REG, 0x01);
+}
+
+static int oxp_pwm_disable(const struct device *dev)
+{
+       return write_to_ec(dev, OXP_SENSOR_PWM_ENABLE_REG, 0x00);
+}
+
+/* Callbacks for hwmon interface */
+static umode_t oxp_ec_hwmon_is_visible(const void *drvdata,
+                                      enum hwmon_sensor_types type, u32 attr, int channel)
+{
+       switch (type) {
+       case hwmon_fan:
+               return 0444;
+       case hwmon_pwm:
+               return 0644;
+       default:
+               return 0;
+       }
+}
+
+static int oxp_platform_read(struct device *dev, enum hwmon_sensor_types type,
+                            u32 attr, int channel, long *val)
+{
+       int ret;
+
+       switch (type) {
+       case hwmon_fan:
+               switch (attr) {
+               case hwmon_fan_input:
+                       return read_from_ec(OXP_SENSOR_FAN_REG, 2, val);
+               default:
+                       break;
+               }
+               break;
+       case hwmon_pwm:
+               switch (attr) {
+               case hwmon_pwm_input:
+                       ret = read_from_ec(OXP_SENSOR_PWM_REG, 1, val);
+                       if (ret)
+                               return ret;
+                       if (board == oxp_mini_amd)
+                               *val = (*val * 255) / 100;
+                       return 0;
+               case hwmon_pwm_enable:
+                       return read_from_ec(OXP_SENSOR_PWM_ENABLE_REG, 1, val);
+               default:
+                       break;
+               }
+               break;
+       default:
+               break;
+       }
+       return -EOPNOTSUPP;
+}
+
+static int oxp_platform_write(struct device *dev, enum hwmon_sensor_types type,
+                             u32 attr, int channel, long val)
+{
+       switch (type) {
+       case hwmon_pwm:
+               switch (attr) {
+               case hwmon_pwm_enable:
+                       if (val == 1)
+                               return oxp_pwm_enable(dev);
+                       else if (val == 0)
+                               return oxp_pwm_disable(dev);
+                       return -EINVAL;
+               case hwmon_pwm_input:
+                       if (val < 0 || val > 255)
+                               return -EINVAL;
+                       if (board == oxp_mini_amd)
+                               val = (val * 100) / 255;
+                       return write_to_ec(dev, OXP_SENSOR_PWM_REG, val);
+               default:
+                       break;
+               }
+               break;
+       default:
+               break;
+       }
+       return -EOPNOTSUPP;
+}
+
+/* Known sensors in the OXP EC controllers */
+static const struct hwmon_channel_info *oxp_platform_sensors[] = {
+       HWMON_CHANNEL_INFO(fan,
+                          HWMON_F_INPUT),
+       HWMON_CHANNEL_INFO(pwm,
+                          HWMON_PWM_INPUT | HWMON_PWM_ENABLE),
+       NULL,
+};
+
+static const struct hwmon_ops oxp_ec_hwmon_ops = {
+       .is_visible = oxp_ec_hwmon_is_visible,
+       .read = oxp_platform_read,
+       .write = oxp_platform_write,
+};
+
+static const struct hwmon_chip_info oxp_ec_chip_info = {
+       .ops = &oxp_ec_hwmon_ops,
+       .info = oxp_platform_sensors,
+};
+
+/* Initialization logic */
+static int oxp_platform_probe(struct platform_device *pdev)
+{
+       const struct dmi_system_id *dmi_entry;
+       struct device *dev = &pdev->dev;
+       struct device *hwdev;
+
+       /*
+        * Have to check for AMD processor here because DMI strings are the
+        * same between Intel and AMD boards, the only way to tell them appart
+        * is the CPU.
+        * Intel boards seem to have different EC registers and values to
+        * read/write.
+        */
+       dmi_entry = dmi_first_match(dmi_table);
+       if (!dmi_entry || boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
+               return -ENODEV;
+
+       board = *((enum oxp_board *) dmi_entry->driver_data);
+
+       hwdev = devm_hwmon_device_register_with_info(dev, "oxpec", NULL,
+                                                    &oxp_ec_chip_info, NULL);
+
+       return PTR_ERR_OR_ZERO(hwdev);
+}
+
+static struct platform_driver oxp_platform_driver = {
+       .driver = {
+               .name = "oxp-platform",
+       },
+       .probe = oxp_platform_probe,
+};
+
+static struct platform_device *oxp_platform_device;
+
+static int __init oxp_platform_init(void)
+{
+       oxp_platform_device =
+               platform_create_bundle(&oxp_platform_driver,
+                                      oxp_platform_probe, NULL, 0, NULL, 0);
+
+       return PTR_ERR_OR_ZERO(oxp_platform_device);
+}
+
+static void __exit oxp_platform_exit(void)
+{
+       platform_device_unregister(oxp_platform_device);
+       platform_driver_unregister(&oxp_platform_driver);
+}
+
+MODULE_DEVICE_TABLE(dmi, dmi_table);
+
+module_init(oxp_platform_init);
+module_exit(oxp_platform_exit);
+
+MODULE_AUTHOR("Joaquín Ignacio Aramendía <samsagax@gmail.com>");
+MODULE_DESCRIPTION("Platform driver that handles EC sensors of OneXPlayer devices");
+MODULE_LICENSE("GPL");
index af9614e..1dbe209 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/mutex.h>
 #include <linux/err.h>
 #include <linux/hwmon.h>
+#include <linux/kstrtox.h>
 
 /* Insmod parameters */
 
index 6d25927..79f480b 100644 (file)
@@ -23,7 +23,7 @@ enum chips {
        /* Managers */
        ltc2972, ltc2974, ltc2975, ltc2977, ltc2978, ltc2979, ltc2980,
        /* Controllers */
-       ltc3880, ltc3882, ltc3883, ltc3884, ltc3886, ltc3887, ltc3889, ltc7880,
+       ltc3880, ltc3882, ltc3883, ltc3884, ltc3886, ltc3887, ltc3889, ltc7132, ltc7880,
        /* Modules */
        ltm2987, ltm4664, ltm4675, ltm4676, ltm4677, ltm4678, ltm4680, ltm4686,
        ltm4700,
@@ -45,15 +45,14 @@ enum chips {
 #define LTC2974_MFR_IOUT_PEAK          0xd7
 #define LTC2974_MFR_IOUT_MIN           0xd8
 
-/* LTC3880, LTC3882, LTC3883, LTC3887, LTM4675, and LTM4676 */
+/* LTC3880, LTC3882, LTC3883, LTC3887, LTM4675, LTM4676, LTC7132 */
 #define LTC3880_MFR_IOUT_PEAK          0xd7
 #define LTC3880_MFR_CLEAR_PEAKS                0xe3
 #define LTC3880_MFR_TEMPERATURE2_PEAK  0xf4
 
-/* LTC3883, LTC3884, LTC3886, LTC3889 and LTC7880 only */
+/* LTC3883, LTC3884, LTC3886, LTC3889, LTC7132, LTC7880 */
 #define LTC3883_MFR_IIN_PEAK           0xe1
 
-
 /* LTC2975 only */
 #define LTC2975_MFR_IIN_PEAK           0xc4
 #define LTC2975_MFR_IIN_MIN            0xc5
@@ -79,10 +78,11 @@ enum chips {
 #define LTC3884_ID                     0x4C00
 #define LTC3886_ID                     0x4600
 #define LTC3887_ID                     0x4700
-#define LTM2987_ID_A                   0x8010  /* A/B for two die IDs */
-#define LTM2987_ID_B                   0x8020
 #define LTC3889_ID                     0x4900
+#define LTC7132_ID                     0x4CE0
 #define LTC7880_ID                     0x49E0
+#define LTM2987_ID_A                   0x8010  /* A/B for two die IDs */
+#define LTM2987_ID_B                   0x8020
 #define LTM4664_ID                     0x4120
 #define LTM4675_ID                     0x47a0
 #define LTM4676_ID_REV1                        0x4400
@@ -547,6 +547,7 @@ static const struct i2c_device_id ltc2978_id[] = {
        {"ltc3886", ltc3886},
        {"ltc3887", ltc3887},
        {"ltc3889", ltc3889},
+       {"ltc7132", ltc7132},
        {"ltc7880", ltc7880},
        {"ltm2987", ltm2987},
        {"ltm4664", ltm4664},
@@ -651,6 +652,8 @@ static int ltc2978_get_id(struct i2c_client *client)
                return ltc3887;
        else if (chip_id == LTC3889_ID)
                return ltc3889;
+       else if (chip_id == LTC7132_ID)
+               return ltc7132;
        else if (chip_id == LTC7880_ID)
                return ltc7880;
        else if (chip_id == LTM2987_ID_A || chip_id == LTM2987_ID_B)
@@ -831,6 +834,7 @@ static int ltc2978_probe(struct i2c_client *client)
        case ltc3884:
        case ltc3886:
        case ltc3889:
+       case ltc7132:
        case ltc7880:
        case ltm4664:
        case ltm4678:
@@ -902,6 +906,7 @@ static const struct of_device_id ltc2978_of_match[] = {
        { .compatible = "lltc,ltc3886" },
        { .compatible = "lltc,ltc3887" },
        { .compatible = "lltc,ltc3889" },
+       { .compatible = "lltc,ltc7132" },
        { .compatible = "lltc,ltc7880" },
        { .compatible = "lltc,ltm2987" },
        { .compatible = "lltc,ltm4664" },
index 7ec0493..95e9578 100644 (file)
@@ -2827,9 +2827,13 @@ static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned
        if (status < 0)
                return status;
 
-       if (pmbus_regulator_is_enabled(rdev) && (status & PB_STATUS_OFF))
-               *flags |= REGULATOR_ERROR_FAIL;
+       if (pmbus_regulator_is_enabled(rdev)) {
+               if (status & PB_STATUS_OFF)
+                       *flags |= REGULATOR_ERROR_FAIL;
 
+               if (status & PB_STATUS_POWER_GOOD_N)
+                       *flags |= REGULATOR_ERROR_REGULATION_OUT;
+       }
        /*
         * Unlike most other status bits, PB_STATUS_{IOUT_OC,VOUT_OV} are
         * defined strictly as fault indicators (not warnings).
@@ -2851,6 +2855,49 @@ static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned
        return 0;
 }
 
+static int pmbus_regulator_get_status(struct regulator_dev *rdev)
+{
+       struct device *dev = rdev_get_dev(rdev);
+       struct i2c_client *client = to_i2c_client(dev->parent);
+       struct pmbus_data *data = i2c_get_clientdata(client);
+       u8 page = rdev_get_id(rdev);
+       int status, ret;
+
+       mutex_lock(&data->update_lock);
+       status = pmbus_get_status(client, page, PMBUS_STATUS_WORD);
+       if (status < 0) {
+               ret = status;
+               goto unlock;
+       }
+
+       if (status & PB_STATUS_OFF) {
+               ret = REGULATOR_STATUS_OFF;
+               goto unlock;
+       }
+
+       /* If regulator is ON & reports power good then return ON */
+       if (!(status & PB_STATUS_POWER_GOOD_N)) {
+               ret = REGULATOR_STATUS_ON;
+               goto unlock;
+       }
+
+       ret = pmbus_regulator_get_error_flags(rdev, &status);
+       if (ret)
+               goto unlock;
+
+       if (status & (REGULATOR_ERROR_UNDER_VOLTAGE | REGULATOR_ERROR_OVER_CURRENT |
+          REGULATOR_ERROR_REGULATION_OUT | REGULATOR_ERROR_FAIL | REGULATOR_ERROR_OVER_TEMP)) {
+               ret = REGULATOR_STATUS_ERROR;
+               goto unlock;
+       }
+
+       ret = REGULATOR_STATUS_UNDEFINED;
+
+unlock:
+       mutex_unlock(&data->update_lock);
+       return ret;
+}
+
 static int pmbus_regulator_get_low_margin(struct i2c_client *client, int page)
 {
        struct pmbus_data *data = i2c_get_clientdata(client);
@@ -2991,6 +3038,7 @@ const struct regulator_ops pmbus_regulator_ops = {
        .disable = pmbus_regulator_disable,
        .is_enabled = pmbus_regulator_is_enabled,
        .get_error_flags = pmbus_regulator_get_error_flags,
+       .get_status = pmbus_regulator_get_status,
        .get_voltage = pmbus_regulator_get_voltage,
        .set_voltage = pmbus_regulator_set_voltage,
        .list_voltage = pmbus_regulator_list_voltage,
index fa298b4..d3ba129 100644 (file)
@@ -8,6 +8,7 @@
 
 #include <linux/debugfs.h>
 #include <linux/i2c.h>
+#include <linux/kstrtox.h>
 #include <linux/module.h>
 #include <linux/of_device.h>
 #include "pmbus.h"
index 7bf0c3f..8ea5a4d 100644 (file)
@@ -297,8 +297,7 @@ static int sbrmi_get_max_pwr_limit(struct sbrmi_data *data)
        return ret;
 }
 
-static int sbrmi_probe(struct i2c_client *client,
-                      const struct i2c_device_id *id)
+static int sbrmi_probe(struct i2c_client *client)
 {
        struct device *dev = &client->dev;
        struct device *hwmon_dev;
@@ -348,7 +347,7 @@ static struct i2c_driver sbrmi_driver = {
                .name = "sbrmi",
                .of_match_table = of_match_ptr(sbrmi_of_match),
        },
-       .probe = sbrmi_probe,
+       .probe_new = sbrmi_probe,
        .id_table = sbrmi_id,
 };
 
index e35357c..4c37de8 100644 (file)
@@ -199,8 +199,7 @@ static const struct hwmon_chip_info sbtsi_chip_info = {
        .info = sbtsi_info,
 };
 
-static int sbtsi_probe(struct i2c_client *client,
-                      const struct i2c_device_id *id)
+static int sbtsi_probe(struct i2c_client *client)
 {
        struct device *dev = &client->dev;
        struct device *hwmon_dev;
@@ -239,7 +238,7 @@ static struct i2c_driver sbtsi_driver = {
                .name = "sbtsi",
                .of_match_table = of_match_ptr(sbtsi_of_match),
        },
-       .probe = sbtsi_probe,
+       .probe_new = sbtsi_probe,
        .id_table = sbtsi_id,
 };
 
index 3f279aa..8305e44 100644 (file)
@@ -320,7 +320,7 @@ static ssize_t temp1_limit_show(struct device *dev,
        u8 index = to_sensor_dev_attr(attr)->index;
        int temperature_limit = data->temperature_limits[index];
 
-       return scnprintf(buf, PAGE_SIZE, "%d\n", temperature_limit);
+       return sysfs_emit(buf, "%d\n", temperature_limit);
 }
 
 static ssize_t humidity1_limit_show(struct device *dev,
@@ -331,7 +331,7 @@ static ssize_t humidity1_limit_show(struct device *dev,
        u8 index = to_sensor_dev_attr(attr)->index;
        u32 humidity_limit = data->humidity_limits[index];
 
-       return scnprintf(buf, PAGE_SIZE, "%u\n", humidity_limit);
+       return sysfs_emit(buf, "%u\n", humidity_limit);
 }
 
 /*
@@ -483,7 +483,7 @@ static ssize_t temp1_alarm_show(struct device *dev,
        if (ret)
                return ret;
 
-       return scnprintf(buf, PAGE_SIZE, "%d\n", !!(buffer[0] & 0x04));
+       return sysfs_emit(buf, "%d\n", !!(buffer[0] & 0x04));
 }
 
 static ssize_t humidity1_alarm_show(struct device *dev,
@@ -498,7 +498,7 @@ static ssize_t humidity1_alarm_show(struct device *dev,
        if (ret)
                return ret;
 
-       return scnprintf(buf, PAGE_SIZE, "%d\n", !!(buffer[0] & 0x08));
+       return sysfs_emit(buf, "%d\n", !!(buffer[0] & 0x08));
 }
 
 static ssize_t heater_enable_show(struct device *dev,
@@ -513,7 +513,7 @@ static ssize_t heater_enable_show(struct device *dev,
        if (ret)
                return ret;
 
-       return scnprintf(buf, PAGE_SIZE, "%d\n", !!(buffer[0] & 0x20));
+       return sysfs_emit(buf, "%d\n", !!(buffer[0] & 0x20));
 }
 
 static ssize_t heater_enable_store(struct device *dev,
@@ -550,7 +550,7 @@ static ssize_t update_interval_show(struct device *dev,
 {
        struct sht3x_data *data = dev_get_drvdata(dev);
 
-       return scnprintf(buf, PAGE_SIZE, "%u\n",
+       return sysfs_emit(buf, "%u\n",
                         mode_to_update_interval[data->mode]);
 }
 
index 13ac2d8..13e0429 100644 (file)
@@ -232,8 +232,7 @@ static const struct hwmon_chip_info sht4x_chip_info = {
        .info = sht4x_info,
 };
 
-static int sht4x_probe(struct i2c_client *client,
-                      const struct i2c_device_id *sht4x_id)
+static int sht4x_probe(struct i2c_client *client)
 {
        struct device *device = &client->dev;
        struct device *hwmon_dev;
@@ -292,7 +291,7 @@ static struct i2c_driver sht4x_driver = {
                .name = "sht4x",
                .of_match_table = sht4x_of_match,
        },
-       .probe          = sht4x_probe,
+       .probe_new      = sht4x_probe,
        .id_table       = sht4x_id,
 };
 
diff --git a/drivers/hwmon/smpro-hwmon.c b/drivers/hwmon/smpro-hwmon.c
new file mode 100644 (file)
index 0000000..a76c49d
--- /dev/null
@@ -0,0 +1,466 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Ampere Computing SoC's SMPro Hardware Monitoring Driver
+ *
+ * Copyright (c) 2022, Ampere Computing LLC
+ */
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+
+/* Logical Power Sensor Registers */
+#define SOC_TEMP               0x10
+#define SOC_VRD_TEMP           0x11
+#define DIMM_VRD_TEMP          0x12
+#define CORE_VRD_TEMP          0x13
+#define CH0_DIMM_TEMP          0x14
+#define CH1_DIMM_TEMP          0x15
+#define CH2_DIMM_TEMP          0x16
+#define CH3_DIMM_TEMP          0x17
+#define CH4_DIMM_TEMP          0x18
+#define CH5_DIMM_TEMP          0x19
+#define CH6_DIMM_TEMP          0x1A
+#define CH7_DIMM_TEMP          0x1B
+#define RCA_VRD_TEMP           0x1C
+
+#define CORE_VRD_PWR           0x20
+#define SOC_PWR                        0x21
+#define DIMM_VRD1_PWR          0x22
+#define DIMM_VRD2_PWR          0x23
+#define CORE_VRD_PWR_MW                0x26
+#define SOC_PWR_MW             0x27
+#define DIMM_VRD1_PWR_MW       0x28
+#define DIMM_VRD2_PWR_MW       0x29
+#define RCA_VRD_PWR            0x2A
+#define RCA_VRD_PWR_MW         0x2B
+
+#define MEM_HOT_THRESHOLD      0x32
+#define SOC_VR_HOT_THRESHOLD   0x33
+#define CORE_VRD_VOLT          0x34
+#define SOC_VRD_VOLT           0x35
+#define DIMM_VRD1_VOLT         0x36
+#define DIMM_VRD2_VOLT         0x37
+#define RCA_VRD_VOLT           0x38
+
+#define CORE_VRD_CURR          0x39
+#define SOC_VRD_CURR           0x3A
+#define DIMM_VRD1_CURR         0x3B
+#define DIMM_VRD2_CURR         0x3C
+#define RCA_VRD_CURR           0x3D
+
+struct smpro_hwmon {
+       struct regmap *regmap;
+};
+
+struct smpro_sensor {
+       const u8 reg;
+       const u8 reg_ext;
+       const char *label;
+};
+
+static const struct smpro_sensor temperature[] = {
+       {
+               .reg = SOC_TEMP,
+               .label = "temp1 SoC"
+       },
+       {
+               .reg = SOC_VRD_TEMP,
+               .reg_ext = SOC_VR_HOT_THRESHOLD,
+               .label = "temp2 SoC VRD"
+       },
+       {
+               .reg = DIMM_VRD_TEMP,
+               .label = "temp3 DIMM VRD"
+       },
+       {
+               .reg = CORE_VRD_TEMP,
+               .label = "temp4 CORE VRD"
+       },
+       {
+               .reg = CH0_DIMM_TEMP,
+               .reg_ext = MEM_HOT_THRESHOLD,
+               .label = "temp5 CH0 DIMM"
+       },
+       {
+               .reg = CH1_DIMM_TEMP,
+               .reg_ext = MEM_HOT_THRESHOLD,
+               .label = "temp6 CH1 DIMM"
+       },
+       {
+               .reg = CH2_DIMM_TEMP,
+               .reg_ext = MEM_HOT_THRESHOLD,
+               .label = "temp7 CH2 DIMM"
+       },
+       {
+               .reg = CH3_DIMM_TEMP,
+               .reg_ext = MEM_HOT_THRESHOLD,
+               .label = "temp8 CH3 DIMM"
+       },
+       {
+               .reg = CH4_DIMM_TEMP,
+               .reg_ext = MEM_HOT_THRESHOLD,
+               .label = "temp9 CH4 DIMM"
+       },
+       {
+               .reg = CH5_DIMM_TEMP,
+               .reg_ext = MEM_HOT_THRESHOLD,
+               .label = "temp10 CH5 DIMM"
+       },
+       {
+               .reg = CH6_DIMM_TEMP,
+               .reg_ext = MEM_HOT_THRESHOLD,
+               .label = "temp11 CH6 DIMM"
+       },
+       {
+               .reg = CH7_DIMM_TEMP,
+               .reg_ext = MEM_HOT_THRESHOLD,
+               .label = "temp12 CH7 DIMM"
+       },
+       {
+               .reg = RCA_VRD_TEMP,
+               .label = "temp13 RCA VRD"
+       },
+};
+
+static const struct smpro_sensor voltage[] = {
+       {
+               .reg = CORE_VRD_VOLT,
+               .label = "vout0 CORE VRD"
+       },
+       {
+               .reg = SOC_VRD_VOLT,
+               .label = "vout1 SoC VRD"
+       },
+       {
+               .reg = DIMM_VRD1_VOLT,
+               .label = "vout2 DIMM VRD1"
+       },
+       {
+               .reg = DIMM_VRD2_VOLT,
+               .label = "vout3 DIMM VRD2"
+       },
+       {
+               .reg = RCA_VRD_VOLT,
+               .label = "vout4 RCA VRD"
+       },
+};
+
+static const struct smpro_sensor curr_sensor[] = {
+       {
+               .reg = CORE_VRD_CURR,
+               .label = "iout1 CORE VRD"
+       },
+       {
+               .reg = SOC_VRD_CURR,
+               .label = "iout2 SoC VRD"
+       },
+       {
+               .reg = DIMM_VRD1_CURR,
+               .label = "iout3 DIMM VRD1"
+       },
+       {
+               .reg = DIMM_VRD2_CURR,
+               .label = "iout4 DIMM VRD2"
+       },
+       {
+               .reg = RCA_VRD_CURR,
+               .label = "iout5 RCA VRD"
+       },
+};
+
+static const struct smpro_sensor power[] = {
+       {
+               .reg = CORE_VRD_PWR,
+               .reg_ext = CORE_VRD_PWR_MW,
+               .label = "power1 CORE VRD"
+       },
+       {
+               .reg = SOC_PWR,
+               .reg_ext = SOC_PWR_MW,
+               .label = "power2 SoC"
+       },
+       {
+               .reg = DIMM_VRD1_PWR,
+               .reg_ext = DIMM_VRD1_PWR_MW,
+               .label = "power3 DIMM VRD1"
+       },
+       {
+               .reg = DIMM_VRD2_PWR,
+               .reg_ext = DIMM_VRD2_PWR_MW,
+               .label = "power4 DIMM VRD2"
+       },
+       {
+               .reg = RCA_VRD_PWR,
+               .reg_ext = RCA_VRD_PWR_MW,
+               .label = "power5 RCA VRD"
+       },
+};
+
+static int smpro_read_temp(struct device *dev, u32 attr, int channel, long *val)
+{
+       struct smpro_hwmon *hwmon = dev_get_drvdata(dev);
+       unsigned int value;
+       int ret;
+
+       switch (attr) {
+       case hwmon_temp_input:
+               ret = regmap_read(hwmon->regmap, temperature[channel].reg, &value);
+               if (ret)
+                       return ret;
+               break;
+       case hwmon_temp_crit:
+               ret = regmap_read(hwmon->regmap, temperature[channel].reg_ext, &value);
+               if (ret)
+                       return ret;
+               break;
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       *val = sign_extend32(value, 8) * 1000;
+       return 0;
+}
+
+static int smpro_read_in(struct device *dev, u32 attr, int channel, long *val)
+{
+       struct smpro_hwmon *hwmon = dev_get_drvdata(dev);
+       unsigned int value;
+       int ret;
+
+       switch (attr) {
+       case hwmon_in_input:
+               ret = regmap_read(hwmon->regmap, voltage[channel].reg, &value);
+               if (ret < 0)
+                       return ret;
+               /* 15-bit value in 1mV */
+               *val = value & 0x7fff;
+               return 0;
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static int smpro_read_curr(struct device *dev, u32 attr, int channel, long *val)
+{
+       struct smpro_hwmon *hwmon = dev_get_drvdata(dev);
+       unsigned int value;
+       int ret;
+
+       switch (attr) {
+       case hwmon_curr_input:
+               ret = regmap_read(hwmon->regmap, curr_sensor[channel].reg, &value);
+               if (ret < 0)
+                       return ret;
+               /* Scale reported by the hardware is 1mA */
+               *val = value & 0x7fff;
+               return 0;
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static int smpro_read_power(struct device *dev, u32 attr, int channel, long *val_pwr)
+{
+       struct smpro_hwmon *hwmon = dev_get_drvdata(dev);
+       unsigned int val = 0, val_mw = 0;
+       int ret;
+
+       switch (attr) {
+       case hwmon_power_input:
+               ret = regmap_read(hwmon->regmap, power[channel].reg, &val);
+               if (ret)
+                       return ret;
+
+               ret = regmap_read(hwmon->regmap, power[channel].reg_ext, &val_mw);
+               if (ret)
+                       return ret;
+               /* 10-bit value */
+               *val_pwr = (val & 0x3ff) * 1000000 + (val_mw & 0x3ff) * 1000;
+               return 0;
+
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static int smpro_read(struct device *dev, enum hwmon_sensor_types type,
+                     u32 attr, int channel, long *val)
+{
+       switch (type) {
+       case hwmon_temp:
+               return smpro_read_temp(dev, attr, channel, val);
+       case hwmon_in:
+               return smpro_read_in(dev, attr, channel, val);
+       case hwmon_power:
+               return smpro_read_power(dev, attr, channel, val);
+       case hwmon_curr:
+               return smpro_read_curr(dev, attr, channel, val);
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+static int smpro_read_string(struct device *dev, enum hwmon_sensor_types type,
+                            u32 attr, int channel, const char **str)
+{
+       switch (type) {
+       case hwmon_temp:
+               switch (attr) {
+               case hwmon_temp_label:
+                       *str = temperature[channel].label;
+                       return 0;
+               default:
+                       break;
+               }
+               break;
+
+       case hwmon_in:
+               switch (attr) {
+               case hwmon_in_label:
+                       *str = voltage[channel].label;
+                       return 0;
+               default:
+                       break;
+               }
+               break;
+
+       case hwmon_curr:
+               switch (attr) {
+               case hwmon_curr_label:
+                       *str = curr_sensor[channel].label;
+                       return 0;
+               default:
+                       break;
+               }
+               break;
+
+       case hwmon_power:
+               switch (attr) {
+               case hwmon_power_label:
+                       *str = power[channel].label;
+                       return 0;
+               default:
+                       break;
+               }
+               break;
+       default:
+               break;
+       }
+
+       return -EOPNOTSUPP;
+}
+
+static umode_t smpro_is_visible(const void *data, enum hwmon_sensor_types type,
+                               u32 attr, int channel)
+{
+       const struct smpro_hwmon *hwmon = data;
+       unsigned int value;
+       int ret;
+
+       switch (type) {
+       case hwmon_temp:
+               switch (attr) {
+               case hwmon_temp_input:
+               case hwmon_temp_label:
+               case hwmon_temp_crit:
+                       ret = regmap_read(hwmon->regmap, temperature[channel].reg, &value);
+                       if (ret || value == 0xFFFF)
+                               return 0;
+                       break;
+               default:
+                       break;
+               }
+               break;
+       default:
+               break;
+       }
+
+       return 0444;
+}
+
+static const struct hwmon_channel_info *smpro_info[] = {
+       HWMON_CHANNEL_INFO(temp,
+                          HWMON_T_INPUT | HWMON_T_LABEL,
+                          HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_CRIT,
+                          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_T_INPUT | HWMON_T_LABEL | HWMON_T_CRIT,
+                          HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_CRIT,
+                          HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_CRIT,
+                          HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_CRIT,
+                          HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_CRIT,
+                          HWMON_T_INPUT | HWMON_T_LABEL | HWMON_T_CRIT,
+                          HWMON_T_INPUT | 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_LABEL,
+                          HWMON_I_INPUT | HWMON_I_LABEL),
+       HWMON_CHANNEL_INFO(power,
+                          HWMON_P_INPUT | HWMON_P_LABEL,
+                          HWMON_P_INPUT | HWMON_P_LABEL,
+                          HWMON_P_INPUT | HWMON_P_LABEL,
+                          HWMON_P_INPUT | HWMON_P_LABEL,
+                          HWMON_P_INPUT | HWMON_P_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),
+       NULL
+};
+
+static const struct hwmon_ops smpro_hwmon_ops = {
+       .is_visible = smpro_is_visible,
+       .read = smpro_read,
+       .read_string = smpro_read_string,
+};
+
+static const struct hwmon_chip_info smpro_chip_info = {
+       .ops = &smpro_hwmon_ops,
+       .info = smpro_info,
+};
+
+static int smpro_hwmon_probe(struct platform_device *pdev)
+{
+       struct smpro_hwmon *hwmon;
+       struct device *hwmon_dev;
+
+       hwmon = devm_kzalloc(&pdev->dev, sizeof(struct smpro_hwmon), GFP_KERNEL);
+       if (!hwmon)
+               return -ENOMEM;
+
+       hwmon->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+       if (!hwmon->regmap)
+               return -ENODEV;
+
+       hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, "smpro_hwmon",
+                                                        hwmon, &smpro_chip_info, NULL);
+
+       return PTR_ERR_OR_ZERO(hwmon_dev);
+}
+
+static struct platform_driver smpro_hwmon_driver = {
+       .probe          = smpro_hwmon_probe,
+       .driver = {
+               .name   = "smpro-hwmon",
+       },
+};
+
+module_platform_driver(smpro_hwmon_driver);
+
+MODULE_AUTHOR("Thu Nguyen <thu@os.amperecomputing.com>");
+MODULE_AUTHOR("Quan Nguyen <quan@os.amperecomputing.com>");
+MODULE_DESCRIPTION("Ampere Altra SMPro hwmon driver");
+MODULE_LICENSE("GPL");
index 3b7f892..b7c6392 100644 (file)
@@ -22,7 +22,6 @@
 #include <linux/platform_device.h>
 #include <linux/hwmon.h>
 #include <linux/hwmon-sysfs.h>
-#include <linux/hwmon-vid.h>
 #include <linux/err.h>
 #include <linux/mutex.h>
 #include <linux/acpi.h>
index 2c4646f..5597e1c 100644 (file)
@@ -16,7 +16,6 @@
 #include <linux/slab.h>
 #include <linux/i2c.h>
 #include <linux/hwmon.h>
-#include <linux/hwmon-vid.h>
 #include <linux/hwmon-sysfs.h>
 #include <linux/err.h>
 #include <linux/mutex.h>
index cb26d02..d896713 100644 (file)
@@ -8,6 +8,7 @@
 #define _LINUX_HWMON_SYSFS_H
 
 #include <linux/device.h>
+#include <linux/kstrtox.h>
 
 struct sensor_device_attribute{
        struct device_attribute dev_attr;
index 281f499..f2781aa 100644 (file)
@@ -29,18 +29,17 @@ struct gsc_hwmon_channel {
 
 /**
  * struct gsc_hwmon_platform_data - platform data for gsc_hwmon driver
- * @channels:  pointer to array of gsc_hwmon_channel structures
- *             describing channels
  * @nchannels: number of elements in @channels array
  * @vreference: voltage reference (mV)
  * @resolution: ADC bit resolution
  * @fan_base: register base for FAN controller
+ * @channels:  array of gsc_hwmon_channel structures describing channels
  */
 struct gsc_hwmon_platform_data {
-       const struct gsc_hwmon_channel *channels;
        int nchannels;
        unsigned int resolution;
        unsigned int vreference;
        unsigned int fan_base;
+       struct gsc_hwmon_channel channels[];
 };
 #endif