apm821xx: remove 4.4 kernel support
[openwrt/openwrt.git] / target / linux / apm821xx / patches-4.4 / 901-hwmon-add-driver-for-Microchip-TC654-TC655-PWM-fan-c.patch
diff --git a/target/linux/apm821xx/patches-4.4/901-hwmon-add-driver-for-Microchip-TC654-TC655-PWM-fan-c.patch b/target/linux/apm821xx/patches-4.4/901-hwmon-add-driver-for-Microchip-TC654-TC655-PWM-fan-c.patch
deleted file mode 100644 (file)
index 41f3370..0000000
+++ /dev/null
@@ -1,1027 +0,0 @@
-From 5ea2e152d846bf60901107fefd81a58f792f3bc2 Mon Sep 17 00:00:00 2001
-From: Christian Lamparter <chunkeey@gmail.com>
-Date: Fri, 10 Jun 2016 03:00:46 +0200
-Subject: [PATCH] hwmon: add driver for Microchip TC654/TC655 PWM fan
- controllers
-
-This patch adds a hwmon driver for the Microchip TC654 and TC655
-Dual SMBus PWM Fan Speed Controllers with Fan Fault detection.
-
-The chip is described in the DS2001734C Spec Document from Microchip.
-It supports:
-       - Shared PWM Fan Drive for two fans
-       - Provides RPM
-       - automatic PWM controller (needs additional
-         NTC/PTC Thermistors.)
-       - Overtemperature alarm (when using NTC/PTC
-         Thermistors)
-
-Signed-off-by: Christian Lamparter <chunkeey@gmail.com>
----
- drivers/hwmon/Kconfig  |  10 +
- drivers/hwmon/Makefile |   1 +
- drivers/hwmon/tc654.c  | 969 +++++++++++++++++++++++++++++++++++++++++++++++++
- 3 files changed, 980 insertions(+)
- create mode 100644 drivers/hwmon/tc654.c
-
---- a/drivers/hwmon/Kconfig
-+++ b/drivers/hwmon/Kconfig
-@@ -1484,6 +1484,16 @@ config SENSORS_INA2XX
-         This driver can also be built as a module.  If so, the module
-         will be called ina2xx.
-+config SENSORS_TC654
-+      tristate "Microchip TC654 and TC655"
-+      depends on I2C
-+      help
-+        If you say yes here you get support for Microchip TC655 and TC654
-+        Dual PWM Fan Speed Controllers and sensor chips.
-+
-+        This driver can also be built as a module.  If so, the module
-+        will be called tc654.
-+
- config SENSORS_TC74
-       tristate "Microchip TC74"
-       depends on I2C
---- a/drivers/hwmon/Makefile
-+++ b/drivers/hwmon/Makefile
-@@ -143,6 +143,7 @@ obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc4
- obj-$(CONFIG_SENSORS_SMSC47M1)        += smsc47m1.o
- obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o
- obj-$(CONFIG_SENSORS_AMC6821) += amc6821.o
-+obj-$(CONFIG_SENSORS_TC654)   += tc654.o
- obj-$(CONFIG_SENSORS_TC74)    += tc74.o
- obj-$(CONFIG_SENSORS_THMC50)  += thmc50.o
- obj-$(CONFIG_SENSORS_TMP102)  += tmp102.o
---- /dev/null
-+++ b/drivers/hwmon/tc654.c
-@@ -0,0 +1,969 @@
-+/*
-+ * tc654.c - Support for Microchip TC654/TC655
-+ * "A Dual SMBus PWM FAN Speed Controllers with Fan Fault Detection"
-+ *
-+ * Copyright (c) 2016 Christian Lamparter <chunkeey@gmail.com>
-+ *
-+ * This program is free software; you can redistribute it and/or modify
-+ * it under the terms of the GNU General Public License as published by
-+ * the Free Software Foundation version 2 of the License.
-+ *
-+ * This program is distributed in the hope that it will be useful,
-+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
-+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+ * GNU General Public License for more details.
-+ *
-+ * The chip is described in the DS2001734C Spec Document from Microchip.
-+ */
-+
-+#include <linux/module.h>
-+#include <linux/init.h>
-+#include <linux/slab.h>
-+#include <linux/jiffies.h>
-+#include <linux/i2c.h>
-+#include <linux/hwmon.h>
-+#include <linux/hwmon-sysfs.h>
-+#include <linux/err.h>
-+#include <linux/mutex.h>
-+#include <linux/thermal.h>
-+
-+/* Hardware definitions */
-+/* 5.1.4 Address Byte stats that TC654/TC655 are fixed at 0x1b */
-+static const unsigned short normal_i2c[] = { 0x1b, I2C_CLIENT_END };
-+
-+enum TC654_REGS {
-+      TC654_REG_RPM1 = 0x00,
-+      TC654_REG_RPM2,
-+      TC654_REG_FAN1_FAULT_THRESH,
-+      TC654_REG_FAN2_FAULT_THRESH,
-+      TC654_REG_CONFIG,
-+      TC654_REG_STATUS,
-+      TC654_REG_DUTY_CYCLE,
-+      TC654_REG_MFR_ID,
-+      TC654_REG_VER_ID,
-+
-+      /* keep last */
-+      __TC654_REG_NUM,
-+};
-+
-+#define TC654_MFR_ID_MICROCHIP                0x84
-+#define TC654_VER_ID                  0x00
-+#define TC655_VER_ID                  0x01
-+
-+enum TC654_CONTROL_BITS {
-+      TC654_CTRL_SDM          = BIT(0),
-+      TC654_CTRL_F1PPR_S      = 1,
-+      TC654_CTRL_F1PPR_M      = (BIT(1) | BIT(2)),
-+      TC654_CTRL_F2PPR_S      = 3,
-+      TC654_CTRL_F2PPR_M      = (BIT(3) | BIT(4)),
-+      TC654_CTRL_DUTYC        = BIT(5),
-+      TC654_CTRL_RES          = BIT(6),
-+      TC654_CTRL_FFCLR        = BIT(7),
-+};
-+
-+enum TC654_STATUS_BITS {
-+      TC654_STATUS_F1F        = BIT(0),
-+      TC654_STATUS_F2F        = BIT(1),
-+      TC654_STATUS_VSTAT      = BIT(2),
-+      TC654_STATUS_R1CO       = BIT(3),
-+      TC654_STATUS_R2CO       = BIT(4),
-+      TC654_STATUS_OTF        = BIT(5),
-+};
-+
-+enum TC654_FAN {
-+      TC654_FAN1 = 0,
-+      TC654_FAN2,
-+
-+      /* keep last */
-+      __NUM_TC654_FAN,
-+};
-+
-+enum TC654_FAN_MODE {
-+      TC654_PWM_OFF,  /* Shutdown Mode - switch of both fans */
-+      TC654_PWM_VIN,  /* Fans will be controlled via V_in analog input pin */
-+      TC654_PWM_3000, /* sets fans to 30% duty cycle */
-+      TC654_PWM_3467,
-+      TC654_PWM_3933, /* default case - if V_in pin is open */
-+      TC654_PWM_4400,
-+      TC654_PWM_4867,
-+      TC654_PWM_5333,
-+      TC654_PWM_5800,
-+      TC654_PWM_6267,
-+      TC654_PWM_6733,
-+      TC654_PWM_7200,
-+      TC654_PWM_7667,
-+      TC654_PWM_8133,
-+      TC654_PWM_8600,
-+      TC654_PWM_9067,
-+      TC654_PWM_9533,
-+      TC654_PWM_10000, /* sets fans to 100% duty cycle */
-+};
-+
-+enum TC654_ALARMS {
-+      TC654_ALARM_FAN1_FAULT,
-+      TC654_ALARM_FAN2_FAULT,
-+      TC654_ALARM_FAN1_COUNTER_OVERFLOW,
-+      TC654_ALARM_FAN2_COUNTER_OVERFLOW,
-+      TC654_ALARM_OVER_TEMPERATURE,
-+
-+      /* KEEP LAST */
-+      __NUM_TC654_ALARMS,
-+};
-+
-+static const struct pwm_table_entry {
-+      u8 min;
-+      enum TC654_FAN_MODE mode;
-+} pwm_table[] = {
-+      {  0, TC654_PWM_OFF  },
-+      {  1, TC654_PWM_3000 },
-+      { 88, TC654_PWM_3467 },
-+      {101, TC654_PWM_3933 },
-+      {113, TC654_PWM_4400 },
-+      {125, TC654_PWM_4867 },
-+      {137, TC654_PWM_5333 },
-+      {148, TC654_PWM_5800 },
-+      {160, TC654_PWM_6267 },
-+      {172, TC654_PWM_6733 },
-+      {184, TC654_PWM_7200 },
-+      {196, TC654_PWM_7667 },
-+      {208, TC654_PWM_8133 },
-+      {220, TC654_PWM_8600 },
-+      {232, TC654_PWM_9067 },
-+      {244, TC654_PWM_9533 },
-+      {255, TC654_PWM_10000 },
-+};
-+
-+/* driver context */
-+struct tc654 {
-+      struct i2c_client       *client;
-+
-+      struct mutex            update_lock;
-+
-+      unsigned long           last_updated;   /* in jiffies */
-+      u8                      cached_regs[__TC654_REG_NUM];
-+
-+      bool                    valid;  /* monitored registers are valid */
-+      u16                     fan_input[__NUM_TC654_FAN];
-+      bool                    alarms[__NUM_TC654_ALARMS];
-+      bool                    vin_status;
-+      bool                    pwm_manual;
-+
-+      /* optional cooling device */
-+      struct thermal_cooling_device *cdev;
-+};
-+
-+/* hardware accessors and functions */
-+static int read_tc(struct tc654 *tc, u8 reg)
-+{
-+      s32 status;
-+
-+      if (reg <= TC654_REG_VER_ID) {
-+              /* Table 6.1 states that all registers are readable */
-+              status = i2c_smbus_read_byte_data(tc->client, reg);
-+      } else
-+              status = -EINVAL;
-+
-+      if (status < 0) {
-+              dev_warn(&tc->client->dev, "can't read register 0x%02x due to error (%d)",
-+                       reg, status);
-+      } else {
-+              tc->cached_regs[reg] = status;
-+      }
-+
-+      return status;
-+}
-+
-+static int write_tc(struct tc654 *tc, u8 i2c_reg, u8 val)
-+{
-+      s32 status;
-+
-+      /*
-+       * Table 6.1 states that both fan threshold registers,
-+       * the Config and Duty Cycle are writeable.
-+       */
-+      switch (i2c_reg) {
-+      case TC654_REG_FAN1_FAULT_THRESH:
-+      case TC654_REG_FAN2_FAULT_THRESH:
-+      case TC654_REG_DUTY_CYCLE:
-+      case TC654_REG_CONFIG:
-+              status = i2c_smbus_write_byte_data(tc->client, i2c_reg, val);
-+              break;
-+
-+      default:
-+              return -EINVAL;
-+      }
-+
-+      if (status < 0) {
-+              dev_warn(&tc->client->dev, "can't write register 0x%02x with value 0x%02x due to error (%d)",
-+                       i2c_reg, val, status);
-+      } else {
-+              tc->cached_regs[i2c_reg] = val;
-+      }
-+
-+      return status;
-+}
-+
-+static int mod_config(struct tc654 *tc, u8 set, u8 clear)
-+{
-+      u8 val = 0;
-+
-+      /* a bit can't be set and cleared on the same time. */
-+      if (set & clear)
-+              return -EINVAL;
-+
-+      /* invalidate data to force re-read from hardware */
-+      tc->valid = false;
-+      val = (tc->cached_regs[TC654_REG_CONFIG] | set) & (~clear);
-+      return write_tc(tc, TC654_REG_CONFIG, val);
-+}
-+
-+static int read_fan_rpm(struct tc654 *tc, enum TC654_FAN fan)
-+{
-+      int ret;
-+
-+      /* 6.1 RPM-OUTPUT1 and RPM-OUTPUT2 registers */
-+      ret = read_tc(tc, fan == TC654_FAN1 ? TC654_REG_RPM1 : TC654_REG_RPM2);
-+      if (ret < 0)
-+              return ret;
-+
-+      /*
-+       * The Resolution Selection Bit in 6.3 CONFIGURATION REGISTER
-+       * is needed to convert the raw value to the RPM.
-+       * 0 = RPM1 and RPM2 use (8-Bit) resolution => * 50 RPM
-+       * 1 = RPM1 and RPM2 use (9-Bit) resolution => * 25 RPM
-+       */
-+      return ret * (25 <<
-+              !(tc->cached_regs[TC654_REG_CONFIG] & TC654_CTRL_RES));
-+}
-+
-+static int write_fan_fault_thresh(struct tc654 *tc, enum TC654_FAN fan,
-+                                u16 rpm)
-+{
-+      u8 converted_rpm;
-+
-+      if (rpm > 12750)
-+              return -EINVAL;
-+
-+      /*
-+       * 6.2 FAN_FAULT1 and FAN_FAULT2 Threshold registers
-+       *
-+       * Both registers operate in 50 RPM mode exclusively.
-+       */
-+      converted_rpm = rpm / 50;
-+
-+      /* invalidate data to force re-read from hardware */
-+      tc->valid = false;
-+      return write_tc(tc, fan == TC654_FAN1 ? TC654_REG_FAN1_FAULT_THRESH :
-+              TC654_REG_FAN2_FAULT_THRESH, converted_rpm);
-+}
-+
-+
-+static int read_fan_fault_thresh(struct tc654 *tc, enum TC654_FAN fan)
-+{
-+      /*
-+       * 6.2 FAN_FAULT1 and FAN_FAULT2 Threshold registers
-+       *
-+       * Both registers operate in 50 RPM mode exclusively.
-+       */
-+      return read_tc(tc, fan == TC654_FAN1 ? TC654_REG_FAN1_FAULT_THRESH :
-+              TC654_REG_FAN2_FAULT_THRESH) * 50;
-+}
-+
-+static enum TC654_FAN_MODE get_fan_mode(struct tc654 *tc)
-+{
-+      if (tc->cached_regs[TC654_REG_CONFIG] & TC654_CTRL_SDM) {
-+              return TC654_PWM_OFF;
-+      } else if (tc->cached_regs[TC654_REG_CONFIG] & TC654_CTRL_DUTYC) {
-+              return TC654_PWM_3000 + tc->cached_regs[TC654_REG_DUTY_CYCLE];
-+      } else if (tc->vin_status == 0)
-+              return TC654_PWM_VIN;
-+
-+      return -EINVAL;
-+}
-+
-+static int write_fan_mode(struct tc654 *tc, enum TC654_FAN_MODE mode)
-+{
-+      int err;
-+      u8 pwm_mode;
-+      bool in_sdm;
-+
-+      in_sdm = !!(tc->cached_regs[TC654_REG_CONFIG] &
-+              TC654_CTRL_SDM);
-+
-+      switch (mode) {
-+      case TC654_PWM_OFF:
-+              if (in_sdm)
-+                      return 0;
-+
-+              /* Enter Shutdown Mode - Switches off all fans */
-+              err = mod_config(tc, TC654_CTRL_SDM, TC654_CTRL_DUTYC);
-+              if (err)
-+                      return err;
-+
-+              return 0;
-+
-+      case TC654_PWM_VIN:
-+              if (tc->vin_status) {
-+                      dev_err(&tc->client->dev,
-+                              "V_in pin is open, can't enable automatic mode.");
-+                      return -EINVAL;
-+              }
-+
-+              err = mod_config(tc, 0, TC654_CTRL_SDM | TC654_CTRL_DUTYC);
-+              if (err)
-+                      return err;
-+
-+              tc->pwm_manual = false;
-+              return 0;
-+
-+      case TC654_PWM_3000:
-+      case TC654_PWM_3467:
-+      case TC654_PWM_3933:
-+      case TC654_PWM_4400:
-+      case TC654_PWM_4867:
-+      case TC654_PWM_5333:
-+      case TC654_PWM_5800:
-+      case TC654_PWM_6267:
-+      case TC654_PWM_6733:
-+      case TC654_PWM_7200:
-+      case TC654_PWM_7667:
-+      case TC654_PWM_8133:
-+      case TC654_PWM_8600:
-+      case TC654_PWM_9067:
-+      case TC654_PWM_9533:
-+      case TC654_PWM_10000:
-+              pwm_mode = mode - TC654_PWM_3000;
-+              if (!in_sdm) {
-+                      err = write_tc(tc, TC654_REG_DUTY_CYCLE, pwm_mode);
-+                      if (err)
-+                              return err;
-+              }
-+
-+              err = mod_config(tc, TC654_CTRL_DUTYC, TC654_CTRL_SDM);
-+              if (err)
-+                      return err;
-+
-+              tc->pwm_manual = true;
-+
-+              if (in_sdm) {
-+                      /*
-+                       * In case the TC654/TC655 was in SDM mode, the write
-+                       * above into the TC654_REG_DUTY_CYCLE register will
-+                       * have no effect because the chip was switched off.
-+                       *
-+                       * Note: The TC654/TC655 have a special "power-on"
-+                       * feature where the PWM will be forced to 100% for
-+                       * one full second in order to spin-up a resting fan.
-+                       */
-+                      err = write_tc(tc, TC654_REG_DUTY_CYCLE, pwm_mode);
-+                      if (err)
-+                              return err;
-+              }
-+
-+              return 0;
-+
-+      default:
-+              return -EINVAL;
-+      }
-+}
-+
-+static struct tc654 *tc654_update_device(struct device *dev)
-+{
-+      struct tc654 *tc = dev_get_drvdata(dev);
-+
-+      mutex_lock(&tc->update_lock);
-+
-+      /*
-+       * In Chapter "1.0 Electrical Characteristics",
-+       * the "Fault Output Response Time" is specified as 2.4 seconds.
-+       */
-+      if (time_after(jiffies, tc->last_updated + 2 * HZ + (HZ * 2) / 5)
-+          || !tc->valid) {
-+              size_t i;
-+              int ret;
-+              bool alarm_triggered;
-+
-+              tc->valid = false;
-+
-+              for (i = 0; i < __NUM_TC654_FAN; i++) {
-+                      ret = read_fan_rpm(tc, i);
-+                      if (ret < 0)
-+                              goto out;
-+
-+                      tc->fan_input[i] = ret;
-+              }
-+
-+              ret = read_tc(tc, TC654_REG_STATUS);
-+              if (ret < 0)
-+                      goto out;
-+
-+              alarm_triggered = !!(ret & (TC654_STATUS_F1F |
-+                      TC654_STATUS_F2F | TC654_STATUS_R1CO |
-+                      TC654_STATUS_R2CO | TC654_STATUS_OTF));
-+
-+              tc->alarms[TC654_ALARM_FAN1_FAULT] = !!(ret & TC654_STATUS_F1F);
-+              tc->alarms[TC654_ALARM_FAN2_FAULT] = !!(ret & TC654_STATUS_F2F);
-+              tc->alarms[TC654_ALARM_FAN1_COUNTER_OVERFLOW] =
-+                      !!(ret & TC654_STATUS_R1CO);
-+              tc->alarms[TC654_ALARM_FAN2_COUNTER_OVERFLOW] =
-+                      !!(ret & TC654_STATUS_R2CO);
-+              tc->alarms[TC654_ALARM_OVER_TEMPERATURE] =
-+                      !!(ret & TC654_STATUS_OTF);
-+              tc->vin_status = !!(ret & TC654_STATUS_VSTAT);
-+
-+              /*
-+               * From 4.5 and 6.3
-+               *
-+               * ... "If the V_in pin is open when TC654_CTRL_DUTYC is not
-+               * selected, then V_out duty cycle will default to 39.33%.".
-+               *
-+               * and most importantly 6.5:
-+               * ... "V_in pin is open, the duty cycle will go to the default
-+               * setting of this register, which is 0010 (39.33%)."
-+               */
-+              tc->pwm_manual |= tc->vin_status &&
-+                      (tc->cached_regs[TC654_REG_CONFIG] &
-+                       TC654_CTRL_DUTYC);
-+
-+              if (alarm_triggered) {
-+                      /*
-+                       * as the name implies, this FLAG needs to be
-+                       * set in order to clear the FAN Fault error.
-+                       */
-+                      ret = mod_config(tc, TC654_CTRL_FFCLR, 0);
-+                      if (ret < 0)
-+                              goto out;
-+              }
-+
-+              tc->last_updated = jiffies;
-+              tc->valid = true;
-+      }
-+
-+out:
-+      mutex_unlock(&tc->update_lock);
-+      return tc;
-+}
-+
-+static u8 get_fan_pulse(struct tc654 *tc, enum TC654_FAN fan)
-+{
-+      u8 fan_pulse_mask = fan == TC654_FAN1 ?
-+              TC654_CTRL_F1PPR_M : TC654_CTRL_F2PPR_M;
-+      u8 fan_pulse_shift = fan  == TC654_FAN1 ?
-+              TC654_CTRL_F1PPR_S : TC654_CTRL_F2PPR_S;
-+
-+      return 1 << ((tc->cached_regs[TC654_REG_CONFIG] & fan_pulse_mask) >>
-+                   fan_pulse_shift);
-+}
-+
-+static int
-+set_fan_pulse(struct tc654 *tc, enum TC654_FAN fan, int pulses)
-+{
-+      int old_pulses;
-+      int err;
-+      u8 new_pulse_per_rotation;
-+      u8 fan_pulse_mask = fan == TC654_FAN1 ?
-+              TC654_CTRL_F1PPR_M : TC654_CTRL_F2PPR_M;
-+      u8 fan_pulse_shift = fan  == TC654_FAN1 ?
-+              TC654_CTRL_F1PPR_S : TC654_CTRL_F2PPR_S;
-+
-+      switch (pulses) {
-+      case 1:
-+              new_pulse_per_rotation = 0;
-+              break;
-+      case 2:
-+              new_pulse_per_rotation = 1;
-+              break;
-+      case 4:
-+              new_pulse_per_rotation = 2;
-+              break;
-+      case 8:
-+              new_pulse_per_rotation = 3;
-+              break;
-+      default:
-+              return -EINVAL;
-+      }
-+
-+      new_pulse_per_rotation <<= fan_pulse_shift;
-+      new_pulse_per_rotation &= fan_pulse_mask;
-+
-+      old_pulses = tc->cached_regs[TC654_REG_CONFIG];
-+      old_pulses &= fan_pulse_mask;
-+
-+      if (new_pulse_per_rotation == old_pulses)
-+              return 0;
-+
-+      mutex_lock(&tc->update_lock);
-+      err = mod_config(tc, new_pulse_per_rotation,
-+                          old_pulses & (~new_pulse_per_rotation));
-+      mutex_unlock(&tc->update_lock);
-+
-+      /* invalidate RPM data to force re-read from hardware */
-+      tc->valid = false;
-+
-+      return err;
-+}
-+
-+static int get_fan_speed(struct tc654 *tc)
-+{
-+      enum TC654_FAN_MODE mode;
-+      size_t i;
-+
-+      mode = get_fan_mode(tc);
-+      for (i = 0; i < ARRAY_SIZE(pwm_table); i++) {
-+              if (mode == pwm_table[i].mode)
-+                      return pwm_table[i].min;
-+      }
-+
-+      return -EINVAL;
-+}
-+
-+static int set_fan_speed(struct tc654 *tc, int new_value)
-+{
-+      int result;
-+      size_t i;
-+
-+      if (new_value > pwm_table[ARRAY_SIZE(pwm_table) - 1].min ||
-+          new_value < pwm_table[0].min)
-+              return -EINVAL;
-+
-+      for (i = 0; i < ARRAY_SIZE(pwm_table); i++) {
-+              /* exact match */
-+              if (pwm_table[i].min == new_value)
-+                      break;
-+
-+              /* a little bit too big - go with the previous entry */
-+              if (pwm_table[i].min > new_value) {
-+                      --i;
-+                      break;
-+              }
-+      }
-+
-+      mutex_lock(&tc->update_lock);
-+      result = write_fan_mode(tc, pwm_table[i].mode);
-+      mutex_unlock(&tc->update_lock);
-+      if (result < 0)
-+              return result;
-+
-+      return 0;
-+}
-+
-+/* sysfs */
-+
-+static ssize_t
-+show_fan_input(struct device *dev, struct device_attribute *da, char *buf)
-+{
-+      struct tc654 *tc = tc654_update_device(dev);
-+      int nr = to_sensor_dev_attr(da)->index;
-+
-+      return sprintf(buf, "%d\n", tc->fan_input[nr]);
-+}
-+
-+static ssize_t
-+show_fan_min(struct device *dev, struct device_attribute *da, char *buf)
-+{
-+      struct tc654 *tc = dev_get_drvdata(dev);
-+      int nr = to_sensor_dev_attr(da)->index;
-+
-+      return sprintf(buf, "%d\n", read_fan_fault_thresh(tc, nr));
-+}
-+
-+static ssize_t
-+show_fan_min_alarm(struct device *dev, struct device_attribute *da, char *buf)
-+{
-+      struct tc654 *tc = tc654_update_device(dev);
-+      int nr = to_sensor_dev_attr(da)->index;
-+
-+      return sprintf(buf, "%d\n", nr == TC654_FAN1 ?
-+                     tc->alarms[TC654_ALARM_FAN1_FAULT] :
-+                     tc->alarms[TC654_ALARM_FAN2_FAULT]);
-+}
-+
-+static ssize_t
-+show_fan_max_alarm(struct device *dev, struct device_attribute *da, char *buf)
-+{
-+      struct tc654 *tc = tc654_update_device(dev);
-+      int nr = to_sensor_dev_attr(da)->index;
-+
-+      return sprintf(buf, "%d\n", nr == TC654_FAN1 ?
-+                     tc->alarms[TC654_ALARM_FAN1_COUNTER_OVERFLOW] :
-+                     tc->alarms[TC654_ALARM_FAN2_COUNTER_OVERFLOW]);
-+}
-+
-+static ssize_t
-+set_fan_min(struct device *dev, struct device_attribute *da,
-+          const char *buf, size_t count)
-+{
-+      struct tc654 *tc = dev_get_drvdata(dev);
-+      long new_min;
-+      int nr = to_sensor_dev_attr(da)->index;
-+      int old_min = read_fan_fault_thresh(tc, nr);
-+      int status = kstrtol(buf, 10, &new_min);
-+
-+      if (status < 0)
-+              return status;
-+
-+      new_min = (new_min / 50) * 50;
-+      if (new_min == old_min) /* No change */
-+              return count;
-+
-+      if (new_min < 0 || new_min > 12750)
-+              return -EINVAL;
-+
-+      mutex_lock(&tc->update_lock);
-+      status = write_fan_fault_thresh(tc, nr, new_min);
-+      mutex_unlock(&tc->update_lock);
-+      return count;
-+}
-+
-+static ssize_t
-+show_fan_max(struct device *dev, struct device_attribute *da, char *buf)
-+{
-+      struct tc654 *tc = dev_get_drvdata(dev);
-+      int max_rpm = tc->cached_regs[TC654_REG_CONFIG] & TC654_CTRL_RES ?
-+              (((1 << 9) - 1) * 25) /* ((2**9) - 1) * 25 RPM */:
-+              (((1 << 8) - 1) * 50) /* ((2**8) - 1) * 50 RPM */;
-+
-+      return sprintf(buf, "%d\n", max_rpm);
-+}
-+
-+static ssize_t
-+show_fan_fault(struct device *dev, struct device_attribute *da, char *buf)
-+{
-+      struct tc654 *tc = tc654_update_device(dev);
-+      int nr = to_sensor_dev_attr(da)->index;
-+      u8 fan_fault_mask = nr == TC654_FAN1 ?
-+              TC654_STATUS_F1F : TC654_STATUS_F2F;
-+
-+      return sprintf(buf, "%d\n",
-+              !!(tc->cached_regs[TC654_REG_STATUS] & fan_fault_mask));
-+}
-+
-+static ssize_t
-+show_fan_pulses(struct device *dev, struct device_attribute *da, char *buf)
-+{
-+      struct tc654 *tc = dev_get_drvdata(dev);
-+      int nr = to_sensor_dev_attr(da)->index;
-+
-+      return sprintf(buf, "%d\n", get_fan_pulse(tc, nr));
-+}
-+
-+static ssize_t
-+set_fan_pulses(struct device *dev, struct device_attribute *da,
-+            const char *buf, size_t count)
-+{
-+      struct tc654 *tc = dev_get_drvdata(dev);
-+      long new_pulse;
-+      int nr = to_sensor_dev_attr(da)->index;
-+      int status = kstrtol(buf, 10, &new_pulse);
-+
-+      if (status < 0)
-+              return status;
-+
-+      status = set_fan_pulse(tc, nr, new_pulse);
-+      if (status < 0)
-+              return status;
-+
-+      return count;
-+}
-+
-+static ssize_t
-+show_pwm_enable(struct device *dev, struct device_attribute *da, char *buf)
-+{
-+      struct tc654 *tc = tc654_update_device(dev);
-+      int pwm_enabled;
-+
-+      if ((tc->cached_regs[TC654_REG_CONFIG] & TC654_CTRL_SDM) &&
-+          !tc->pwm_manual) {
-+              pwm_enabled = 0; /* full off */
-+      } else {
-+              if (tc->valid && tc->vin_status == 0)
-+                      pwm_enabled = 2; /* automatic fan speed control */
-+
-+              pwm_enabled = 1; /* PWM Mode */
-+      }
-+
-+      return sprintf(buf, "%d\n", pwm_enabled);
-+}
-+
-+static ssize_t
-+set_pwm_enable(struct device *dev, struct device_attribute *da,
-+             const char *buf, size_t count)
-+{
-+      struct tc654 *tc = dev_get_drvdata(dev);
-+      long new_value;
-+
-+      int result = kstrtol(buf, 10, &new_value);
-+
-+      if (result < 0)
-+              return result;
-+
-+      mutex_lock(&tc->update_lock);
-+      switch (new_value) {
-+      case 0: /* no fan control (i.e. is OFF) */
-+              result = write_fan_mode(tc, TC654_PWM_OFF);
-+              tc->pwm_manual = false;
-+              break;
-+
-+      case 1: /* manual fan control enabled (using pwm) */
-+              result = write_fan_mode(tc, TC654_PWM_10000);
-+              break;
-+
-+      case 2: /* automatic fan speed control enabled */
-+              result = write_fan_mode(tc, TC654_PWM_VIN);
-+              break;
-+
-+      default:
-+              result = -EINVAL;
-+      }
-+
-+      mutex_unlock(&tc->update_lock);
-+      return result < 0 ? result : count;
-+}
-+
-+static ssize_t
-+show_pwm(struct device *dev, struct device_attribute *da, char *buf)
-+{
-+      struct tc654 *tc = tc654_update_device(dev);
-+      int ret;
-+
-+      ret = get_fan_speed(tc);
-+      if (ret < 0)
-+              return ret;
-+
-+      return sprintf(buf, "%d\n", ret);
-+}
-+
-+static ssize_t
-+set_pwm(struct device *dev, struct device_attribute *da,
-+      const char *buf, size_t count)
-+{
-+      struct tc654 *tc = dev_get_drvdata(dev);
-+      long new_value = -1;
-+      int result = kstrtol(buf, 10, &new_value);
-+
-+      if (result < 0)
-+              return result;
-+
-+      if (new_value < 0 || new_value > INT_MAX)
-+              return -EINVAL;
-+
-+      if (!tc->pwm_manual)
-+              return -EINVAL;
-+
-+      result = set_fan_speed(tc, new_value);
-+      if (result < 0)
-+              return result;
-+
-+      return count;
-+}
-+
-+static ssize_t
-+show_temp_alarm_otf(struct device *dev, struct device_attribute *da, char *buf)
-+{
-+      struct tc654 *tc = tc654_update_device(dev);
-+
-+      return sprintf(buf, "%d\n", tc->alarms[TC654_ALARM_OVER_TEMPERATURE]);
-+}
-+
-+static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan_input,
-+                        NULL, TC654_FAN1);
-+static SENSOR_DEVICE_ATTR(fan1_min, S_IRUGO | S_IWUSR, show_fan_min,
-+                        set_fan_min, TC654_FAN1);
-+static SENSOR_DEVICE_ATTR(fan1_min_alarm, S_IRUGO, show_fan_min_alarm,
-+                        NULL, TC654_FAN1);
-+static SENSOR_DEVICE_ATTR(fan1_max_alarm, S_IRUGO, show_fan_max_alarm,
-+                        NULL, TC654_FAN1);
-+static SENSOR_DEVICE_ATTR(fan1_max, S_IRUGO, show_fan_max, NULL, TC654_FAN1);
-+static SENSOR_DEVICE_ATTR(fan1_fault, S_IRUGO, show_fan_fault,
-+                        NULL, TC654_FAN1);
-+static SENSOR_DEVICE_ATTR(fan1_pulses, S_IRUGO | S_IWUSR, show_fan_pulses,
-+                        set_fan_pulses, TC654_FAN1);
-+static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan_input,
-+                        NULL, TC654_FAN2);
-+static SENSOR_DEVICE_ATTR(fan2_min, S_IRUGO | S_IWUSR, show_fan_min,
-+                        set_fan_min, TC654_FAN2);
-+static SENSOR_DEVICE_ATTR(fan2_max, S_IRUGO, show_fan_max,
-+                        NULL, TC654_FAN2);
-+static SENSOR_DEVICE_ATTR(fan2_min_alarm, S_IRUGO, show_fan_min_alarm,
-+                        NULL, TC654_FAN2);
-+static SENSOR_DEVICE_ATTR(fan2_max_alarm, S_IRUGO, show_fan_max_alarm,
-+                        NULL, TC654_FAN2);
-+static SENSOR_DEVICE_ATTR(fan2_fault, S_IRUGO, show_fan_fault,
-+                        NULL, TC654_FAN2);
-+static SENSOR_DEVICE_ATTR(fan2_pulses, S_IRUGO | S_IWUSR, show_fan_pulses,
-+                        set_fan_pulses, TC654_FAN2);
-+
-+static DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, show_pwm_enable,
-+      set_pwm_enable);
-+static DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm, set_pwm);
-+
-+static DEVICE_ATTR(temp1_emergency_alarm, S_IRUGO, show_temp_alarm_otf, NULL);
-+
-+/* sensors present on all models */
-+static struct attribute *tc654_attrs[] = {
-+      &sensor_dev_attr_fan1_input.dev_attr.attr,
-+      &sensor_dev_attr_fan1_min.dev_attr.attr,
-+      &sensor_dev_attr_fan1_max.dev_attr.attr,
-+      &sensor_dev_attr_fan1_min_alarm.dev_attr.attr,
-+      &sensor_dev_attr_fan1_max_alarm.dev_attr.attr,
-+      &sensor_dev_attr_fan1_fault.dev_attr.attr,
-+      &sensor_dev_attr_fan1_pulses.dev_attr.attr,
-+      &sensor_dev_attr_fan2_input.dev_attr.attr,
-+      &sensor_dev_attr_fan2_min.dev_attr.attr,
-+      &sensor_dev_attr_fan2_max.dev_attr.attr,
-+      &sensor_dev_attr_fan2_min_alarm.dev_attr.attr,
-+      &sensor_dev_attr_fan2_max_alarm.dev_attr.attr,
-+      &sensor_dev_attr_fan2_fault.dev_attr.attr,
-+      &sensor_dev_attr_fan2_pulses.dev_attr.attr,
-+
-+      &dev_attr_pwm1_enable.attr,
-+      &dev_attr_pwm1.attr,
-+
-+      &dev_attr_temp1_emergency_alarm.attr,
-+      NULL
-+};
-+
-+ATTRIBUTE_GROUPS(tc654);
-+
-+/* cooling device */
-+
-+static int tc654_get_max_state(struct thermal_cooling_device *cdev,
-+                             unsigned long *state)
-+{
-+      *state = 255;
-+      return 0;
-+}
-+
-+static int tc654_get_cur_state(struct thermal_cooling_device *cdev,
-+                             unsigned long *state)
-+{
-+      struct tc654 *tc = cdev->devdata;
-+      int ret;
-+
-+      if (!tc)
-+              return -EINVAL;
-+
-+      ret = get_fan_speed(tc);
-+      if (ret < 0)
-+              return ret;
-+
-+      *state = ret;
-+      return 0;
-+}
-+
-+static int tc654_set_cur_state(struct thermal_cooling_device *cdev,
-+                             unsigned long state)
-+{
-+      struct tc654 *tc = cdev->devdata;
-+
-+      if (!tc)
-+              return -EINVAL;
-+
-+      if (state > INT_MAX)
-+              return -EINVAL;
-+
-+      return set_fan_speed(tc, state);
-+}
-+
-+static const struct thermal_cooling_device_ops tc654_fan_cool_ops = {
-+      .get_max_state = tc654_get_max_state,
-+      .get_cur_state = tc654_get_cur_state,
-+      .set_cur_state = tc654_set_cur_state,
-+};
-+
-+
-+/* hardware probe and detection */
-+
-+static int
-+tc654_probe(struct i2c_client *client, const struct i2c_device_id *id)
-+{
-+      struct tc654 *tc;
-+      struct device *hwmon_dev;
-+      int ret, i;
-+
-+      if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
-+              return -EIO;
-+
-+      tc = devm_kzalloc(&client->dev, sizeof(*tc), GFP_KERNEL);
-+      if (!tc)
-+              return -ENOMEM;
-+
-+      i2c_set_clientdata(client, tc);
-+      tc->client = client;
-+      mutex_init(&tc->update_lock);
-+
-+      /* cache all 8 registers */
-+      for (i = 0; i < __TC654_REG_NUM; i++) {
-+              ret = read_tc(tc, i);
-+              if (ret < 0)
-+                      return ret;
-+      }
-+
-+      /* sysfs hooks */
-+      hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev,
-+                                                         client->name, tc,
-+                                                         tc654_groups);
-+      if (IS_ERR(hwmon_dev))
-+              return PTR_ERR(hwmon_dev);
-+
-+#if IS_ENABLED(CONFIG_OF)
-+      /* Optional cooling device register for Device tree platforms */
-+      tc->cdev = thermal_of_cooling_device_register(client->dev.of_node,
-+                                                    "tc654", tc,
-+                                                    &tc654_fan_cool_ops);
-+#else /* CONFIG_OF */
-+      /* Optional cooling device register for non Device tree platforms */
-+      tc->cdev = thermal_cooling_device_register("tc654", tc,
-+                                                 &tc654_fan_cool_ops);
-+#endif /* CONFIG_OF */
-+
-+      dev_info(&client->dev, "%s: sensor '%s'\n",
-+               dev_name(hwmon_dev), client->name);
-+
-+      return 0;
-+}
-+
-+static const struct i2c_device_id tc654_ids[] = {
-+      { "tc654", 0, },
-+      { }
-+};
-+MODULE_DEVICE_TABLE(i2c, tc654_ids);
-+
-+/* Return 0 if detection is successful, -ENODEV otherwise */
-+static int
-+tc654_detect(struct i2c_client *new_client, struct i2c_board_info *info)
-+{
-+      struct i2c_adapter *adapter = new_client->adapter;
-+      int manufacturer, product;
-+
-+      if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
-+              return -ENODEV;
-+
-+      manufacturer = i2c_smbus_read_byte_data(new_client, TC654_REG_MFR_ID);
-+      if (manufacturer != TC654_MFR_ID_MICROCHIP)
-+              return -ENODEV;
-+
-+      product = i2c_smbus_read_byte_data(new_client, TC654_REG_VER_ID);
-+      if (!((product == TC654_VER_ID) || (product == TC655_VER_ID)))
-+              return -ENODEV;
-+
-+      strlcpy(info->type, "tc654", I2C_NAME_SIZE);
-+      return 0;
-+}
-+
-+static struct i2c_driver tc654_driver = {
-+      .class          = I2C_CLASS_HWMON,
-+      .driver = {
-+              .name   = "tc654",
-+      },
-+      .probe          = tc654_probe,
-+      .id_table       = tc654_ids,
-+      .detect         = tc654_detect,
-+      .address_list   = normal_i2c,
-+};
-+
-+module_i2c_driver(tc654_driver);
-+
-+MODULE_AUTHOR("Christian Lamparter <chunkeey@gmail.com>");
-+MODULE_DESCRIPTION("Microchip TC654/TC655 hwmon driver");
-+MODULE_LICENSE("GPL");