diff options
Diffstat (limited to 'target/linux/apm821xx/patches-4.4/901-hwmon-add-driver-for-Microchip-TC654-TC655-PWM-fan-c.patch')
-rw-r--r-- | target/linux/apm821xx/patches-4.4/901-hwmon-add-driver-for-Microchip-TC654-TC655-PWM-fan-c.patch | 1027 |
1 files changed, 0 insertions, 1027 deletions
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 index 41f3370008..0000000000 --- a/target/linux/apm821xx/patches-4.4/901-hwmon-add-driver-for-Microchip-TC654-TC655-PWM-fan-c.patch +++ /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"); |