From 242dfacf86dec78afdf4abb6ace182741391b25b Mon Sep 17 00:00:00 2001 From: Zi Zhou Date: Mon, 24 Jun 2019 10:57:16 -0700 Subject: [PATCH] add max6620(fan controller) driver patch to kernel 4.14(for s4000/6000) --- .../configs/x86_64-all/x86_64-all.config | 1 + .../driver-hwmon-max6620-fix-rpm-calc.patch | 196 +++++ .../patches/driver-hwmon-max6620-update.patch | 113 +++ .../patches/driver-hwmon-max6620.patch | 753 ++++++++++++++++++ .../base/any/kernels/4.14-lts/patches/series | 3 + 5 files changed, 1066 insertions(+) create mode 100644 packages/base/any/kernels/4.14-lts/patches/driver-hwmon-max6620-fix-rpm-calc.patch create mode 100644 packages/base/any/kernels/4.14-lts/patches/driver-hwmon-max6620-update.patch create mode 100644 packages/base/any/kernels/4.14-lts/patches/driver-hwmon-max6620.patch diff --git a/packages/base/any/kernels/4.14-lts/configs/x86_64-all/x86_64-all.config b/packages/base/any/kernels/4.14-lts/configs/x86_64-all/x86_64-all.config index 81e5a991..9104081c 100755 --- a/packages/base/any/kernels/4.14-lts/configs/x86_64-all/x86_64-all.config +++ b/packages/base/any/kernels/4.14-lts/configs/x86_64-all/x86_64-all.config @@ -2629,6 +2629,7 @@ CONFIG_SENSORS_CORETEMP=y # CONFIG_SENSORS_MAX6639 is not set # CONFIG_SENSORS_MAX6642 is not set # CONFIG_SENSORS_MAX6650 is not set +CONFIG_SENSORS_MAX6620=y # CONFIG_SENSORS_MAX6697 is not set CONFIG_SENSORS_MAX31790=y # CONFIG_SENSORS_MCP3021 is not set diff --git a/packages/base/any/kernels/4.14-lts/patches/driver-hwmon-max6620-fix-rpm-calc.patch b/packages/base/any/kernels/4.14-lts/patches/driver-hwmon-max6620-fix-rpm-calc.patch new file mode 100644 index 00000000..e5401626 --- /dev/null +++ b/packages/base/any/kernels/4.14-lts/patches/driver-hwmon-max6620-fix-rpm-calc.patch @@ -0,0 +1,196 @@ +MAX6620 fix rpm calculation accuracy + +From: Cumulus Networks + +The driver only fills the most significant 8 bits of the fan tach +count (11 bit value). Fixing the driver to use all of 11 bits for +more accuracy. +--- + drivers/hwmon/max6620.c | 105 +++++++++++++++++++++-------------------------- + 1 file changed, 46 insertions(+), 59 deletions(-) + +diff --git a/drivers/hwmon/max6620.c b/drivers/hwmon/max6620.c +index 3c337c7..76c1f7f 100644 +--- a/drivers/hwmon/max6620.c ++++ b/drivers/hwmon/max6620.c +@@ -46,6 +46,8 @@ + + /* clock: The clock frequency of the chip the driver should assume */ + static int clock = 8192; ++static u32 sr = 2; ++static u32 np = 2; + + module_param(clock, int, S_IRUGO); + +@@ -213,22 +215,22 @@ static ssize_t get_fan(struct device *dev, struct device_attribute *devattr, cha + + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct max6620_data *data = max6620_update_device(dev); +- int rpm; +- +- /* +- * Calculation details: +- * +- * Each tachometer counts over an interval given by the "count" +- * register (0.25, 0.5, 1 or 2 seconds). This module assumes +- * that the fans produce two pulses per revolution (this seems +- * to be the most common). +- */ +- if(data->tach[attr->index] == 0 || data->tach[attr->index] == 255) { ++ struct i2c_client *client = to_i2c_client(dev); ++ u32 rpm = 0; ++ u32 tach = 0; ++ u32 tach1 = 0; ++ u32 tach2 = 0; ++ ++ tach1 = i2c_smbus_read_byte_data(client, tach_reg[attr->index]); ++ tach1 = (tach1 << 3) & 0x7f8; ++ tach2 = i2c_smbus_read_byte_data(client, tach_reg[attr->index] + 1); ++ tach2 = (tach2 >> 5) & 0x7; ++ tach = tach1 | tach2; ++ if (tach == 0) { + rpm = 0; + } else { +- rpm = ((clock / (data->tach[attr->index] << 3)) * 30 * DIV_FROM_REG(data->fandyn[attr->index])); ++ rpm = (60 * sr * clock)/(tach * np); + } +- + return sprintf(buf, "%d\n", rpm); + } + +@@ -236,22 +238,21 @@ static ssize_t get_target(struct device *dev, struct device_attribute *devattr, + + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct max6620_data *data = max6620_update_device(dev); +- int kscale, ktach, rpm; +- +- /* +- * Use the datasheet equation: +- * +- * FanSpeed = KSCALE x fCLK / [256 x (KTACH + 1)] +- * +- * then multiply by 60 to give rpm. +- */ +- +- kscale = DIV_FROM_REG(data->fandyn[attr->index]); +- ktach = data->target[attr->index]; +- if(ktach == 0) { ++ struct i2c_client *client = to_i2c_client(dev); ++ u32 rpm; ++ u32 target; ++ u32 target1; ++ u32 target2; ++ ++ target1 = i2c_smbus_read_byte_data(client, target_reg[attr->index]); ++ target1 = (target1 << 3) & 0x7f8; ++ target2 = i2c_smbus_read_byte_data(client, target_reg[attr->index] + 1); ++ target2 = (target2 >> 5) & 0x7; ++ target = target1 | target2; ++ if (target == 0) { + rpm = 0; + } else { +- rpm = ((60 * kscale * clock) / (ktach << 3)); ++ rpm = (60 * sr * clock)/(target * np); + } + return sprintf(buf, "%d\n", rpm); + } +@@ -261,9 +262,11 @@ static ssize_t set_target(struct device *dev, struct device_attribute *devattr, + struct i2c_client *client = to_i2c_client(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct max6620_data *data = i2c_get_clientdata(client); +- int kscale, ktach; +- unsigned long rpm; ++ u32 rpm; + int err; ++ u32 target; ++ u32 target1; ++ u32 target2; + + err = kstrtoul(buf, 10, &rpm); + if (err) +@@ -271,25 +274,13 @@ static ssize_t set_target(struct device *dev, struct device_attribute *devattr, + + rpm = SENSORS_LIMIT(rpm, FAN_RPM_MIN, FAN_RPM_MAX); + +- /* +- * Divide the required speed by 60 to get from rpm to rps, then +- * use the datasheet equation: +- * +- * KTACH = [(fCLK x KSCALE) / (256 x FanSpeed)] - 1 +- */ +- + mutex_lock(&data->update_lock); + +- kscale = DIV_FROM_REG(data->fandyn[attr->index]); +- ktach = ((60 * kscale * clock) / rpm); +- if (ktach < 0) +- ktach = 0; +- if (ktach > 255) +- ktach = 255; +- data->target[attr->index] = ktach; +- +- i2c_smbus_write_byte_data(client, target_reg[attr->index], data->target[attr->index]); +- i2c_smbus_write_byte_data(client, target_reg[attr->index]+0x01, 0x00); ++ target = (60 * sr * 8192)/(rpm * np); ++ target1 = (target >> 3) & 0xff; ++ target2 = (target << 5) & 0xe0; ++ i2c_smbus_write_byte_data(client, target_reg[attr->index], target1); ++ i2c_smbus_write_byte_data(client, target_reg[attr->index] + 1, target2); + + mutex_unlock(&data->update_lock); + +@@ -609,8 +600,11 @@ static int max6620_init_client(struct i2c_client *client) { + } + + +- +- if (i2c_smbus_write_byte_data(client, MAX6620_REG_CONFIG, config)) { ++ /* ++ * Set bit 4, disable other fans from going full speed on a fail ++ * failure. ++ */ ++ if (i2c_smbus_write_byte_data(client, MAX6620_REG_CONFIG, config | 0x10)) { + dev_err(&client->dev, "Config write error, aborting.\n"); + return err; + } +@@ -618,28 +612,21 @@ static int max6620_init_client(struct i2c_client *client) { + data->config = config; + for (i = 0; i < 4; i++) { + data->fancfg[i] = i2c_smbus_read_byte_data(client, config_reg[i]); +- data->fancfg[i] |= 0x80; // enable TACH monitoring ++ data->fancfg[i] |= 0xa8; // enable TACH monitoring + i2c_smbus_write_byte_data(client, config_reg[i], data->fancfg[i]); + data->fandyn[i] = i2c_smbus_read_byte_data(client, dyn_reg[i]); +- data-> fandyn[i] |= 0x1C; ++ /* 2 counts (001) and Rate change 100 (0.125 secs) */ ++ data-> fandyn[i] = 0x30; + i2c_smbus_write_byte_data(client, dyn_reg[i], data->fandyn[i]); + data->tach[i] = i2c_smbus_read_byte_data(client, tach_reg[i]); + data->volt[i] = i2c_smbus_read_byte_data(client, volt_reg[i]); + data->target[i] = i2c_smbus_read_byte_data(client, target_reg[i]); + data->dac[i] = i2c_smbus_read_byte_data(client, dac_reg[i]); + +- +- + } +- +- +- + return 0; + } + +- +- +- + static struct max6620_data *max6620_update_device(struct device *dev) + { + int i; +@@ -678,7 +665,7 @@ static struct max6620_data *max6620_update_device(struct device *dev) + return data; + } + +-module_i2c_driver(max6620_driver); ++// module_i2c_driver(max6620_driver); + + static int __init max6620_init(void) + { diff --git a/packages/base/any/kernels/4.14-lts/patches/driver-hwmon-max6620-update.patch b/packages/base/any/kernels/4.14-lts/patches/driver-hwmon-max6620-update.patch new file mode 100644 index 00000000..b4cfe0cf --- /dev/null +++ b/packages/base/any/kernels/4.14-lts/patches/driver-hwmon-max6620-update.patch @@ -0,0 +1,113 @@ +Update MAX6620 driver to support newer kernel version + +From: Shuotian Cheng + + +--- + drivers/hwmon/max6620.c | 25 +++++++++++-------------- + 1 file changed, 11 insertions(+), 14 deletions(-) + +diff --git a/drivers/hwmon/max6620.c b/drivers/hwmon/max6620.c +index 76c1f7f..fb49195 100644 +--- a/drivers/hwmon/max6620.c ++++ b/drivers/hwmon/max6620.c +@@ -183,7 +183,7 @@ static struct i2c_driver max6620_driver = { + .name = "max6620", + }, + .probe = max6620_probe, +- .remove = __devexit_p(max6620_remove), ++ .remove = max6620_remove, + .id_table = max6620_id, + .address_list = normal_i2c, + }; +@@ -231,6 +231,7 @@ static ssize_t get_fan(struct device *dev, struct device_attribute *devattr, cha + } else { + rpm = (60 * sr * clock)/(tach * np); + } ++ + return sprintf(buf, "%d\n", rpm); + } + +@@ -262,17 +263,17 @@ static ssize_t set_target(struct device *dev, struct device_attribute *devattr, + struct i2c_client *client = to_i2c_client(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct max6620_data *data = i2c_get_clientdata(client); +- u32 rpm; ++ unsigned long rpm; + int err; +- u32 target; +- u32 target1; +- u32 target2; ++ unsigned long target; ++ unsigned long target1; ++ unsigned long target2; + + err = kstrtoul(buf, 10, &rpm); + if (err) + return err; + +- rpm = SENSORS_LIMIT(rpm, FAN_RPM_MIN, FAN_RPM_MAX); ++ rpm = clamp_val(rpm, FAN_RPM_MIN, FAN_RPM_MAX); + + mutex_lock(&data->update_lock); + +@@ -326,7 +327,7 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr, con + if (err) + return err; + +- pwm = SENSORS_LIMIT(pwm, 0, 255); ++ pwm = clamp_val(pwm, 0, 255); + + mutex_lock(&data->update_lock); + +@@ -534,7 +535,7 @@ static struct attribute_group max6620_attr_grp = { + * Real code + */ + +-static int __devinit max6620_probe(struct i2c_client *client, const struct i2c_device_id *id) { ++static int max6620_probe(struct i2c_client *client, const struct i2c_device_id *id) { + + struct max6620_data *data; + int err; +@@ -575,7 +576,7 @@ dev_info(&client->dev, "Sysfs entries created\n"); + return err; + } + +-static int __devexit max6620_remove(struct i2c_client *client) { ++static int max6620_remove(struct i2c_client *client) { + + struct max6620_data *data = i2c_get_clientdata(client); + +@@ -599,7 +600,6 @@ static int max6620_init_client(struct i2c_client *client) { + return err; + } + +- + /* + * Set bit 4, disable other fans from going full speed on a fail + * failure. +@@ -615,14 +615,13 @@ static int max6620_init_client(struct i2c_client *client) { + data->fancfg[i] |= 0xa8; // enable TACH monitoring + i2c_smbus_write_byte_data(client, config_reg[i], data->fancfg[i]); + data->fandyn[i] = i2c_smbus_read_byte_data(client, dyn_reg[i]); +- /* 2 counts (001) and Rate change 100 (0.125 secs) */ ++ /* 2 counts (001) and Rate change 100 (0.125 secs) */ + data-> fandyn[i] = 0x30; + i2c_smbus_write_byte_data(client, dyn_reg[i], data->fandyn[i]); + data->tach[i] = i2c_smbus_read_byte_data(client, tach_reg[i]); + data->volt[i] = i2c_smbus_read_byte_data(client, volt_reg[i]); + data->target[i] = i2c_smbus_read_byte_data(client, target_reg[i]); + data->dac[i] = i2c_smbus_read_byte_data(client, dac_reg[i]); +- + } + return 0; + } +@@ -665,8 +664,6 @@ static struct max6620_data *max6620_update_device(struct device *dev) + return data; + } + +-// module_i2c_driver(max6620_driver); +- + static int __init max6620_init(void) + { + return i2c_add_driver(&max6620_driver); diff --git a/packages/base/any/kernels/4.14-lts/patches/driver-hwmon-max6620.patch b/packages/base/any/kernels/4.14-lts/patches/driver-hwmon-max6620.patch new file mode 100644 index 00000000..119c12ee --- /dev/null +++ b/packages/base/any/kernels/4.14-lts/patches/driver-hwmon-max6620.patch @@ -0,0 +1,753 @@ +Driver for MAX6620 Fan sensor + +From: Cumulus Networks + + +--- + drivers/hwmon/Kconfig | 10 + + drivers/hwmon/Makefile | 1 + drivers/hwmon/max6620.c | 702 +++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 713 insertions(+) + create mode 100644 drivers/hwmon/max6620.c + +diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig +index 02d3d85..ca38e05 100644 +--- a/drivers/hwmon/Kconfig ++++ b/drivers/hwmon/Kconfig +@@ -784,6 +784,16 @@ config SENSORS_MAX6650 + This driver can also be built as a module. If so, the module + will be called max6650. + ++config SENSORS_MAX6620 ++ tristate "Maxim MAX6620 sensor chip" ++ depends on I2C ++ help ++ If you say yes here you get support for the MAX6620 ++ sensor chips. ++ ++ This driver can also be built as a module. If so, the module ++ will be called max6620. ++ + config SENSORS_MAX6697 + tristate "Maxim MAX6697 and compatibles" + depends on I2C +diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile +index 3dc0f02..8837a7b 100644 +--- a/drivers/hwmon/Makefile ++++ b/drivers/hwmon/Makefile +@@ -111,6 +111,7 @@ obj-$(CONFIG_SENSORS_MAX197) += max197.o + obj-$(CONFIG_SENSORS_MAX6639) += max6639.o + obj-$(CONFIG_SENSORS_MAX6642) += max6642.o + obj-$(CONFIG_SENSORS_MAX6650) += max6650.o ++obj-$(CONFIG_SENSORS_MAX6620) += max6620.o + obj-$(CONFIG_SENSORS_MAX6697) += max6697.o + obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o + obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o +diff --git a/drivers/hwmon/max6620.c b/drivers/hwmon/max6620.c +new file mode 100644 +index 0000000..3c337c7 +--- /dev/null ++++ b/drivers/hwmon/max6620.c +@@ -0,0 +1,702 @@ ++/* ++ * max6620.c - Linux Kernel module for hardware monitoring. ++ * ++ * (C) 2012 by L. Grunenberg ++ * ++ * based on code written by : ++ * 2007 by Hans J. Koch ++ * John Morris ++ * Copyright (c) 2003 Spirent Communications ++ * and Claus Gindhart ++ * ++ * This module has only been tested with the MAX6620 chip. ++ * ++ * The datasheet was last seen at: ++ * ++ * http://pdfserv.maxim-ic.com/en/ds/MAX6620.pdf ++ * ++ * 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; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * Insmod parameters ++ */ ++ ++ ++/* clock: The clock frequency of the chip the driver should assume */ ++static int clock = 8192; ++ ++module_param(clock, int, S_IRUGO); ++ ++static const unsigned short normal_i2c[] = {0x0a, 0x1a, 0x2a, I2C_CLIENT_END}; ++ ++/* ++ * MAX 6620 registers ++ */ ++ ++#define MAX6620_REG_CONFIG 0x00 ++#define MAX6620_REG_FAULT 0x01 ++#define MAX6620_REG_CONF_FAN0 0x02 ++#define MAX6620_REG_CONF_FAN1 0x03 ++#define MAX6620_REG_CONF_FAN2 0x04 ++#define MAX6620_REG_CONF_FAN3 0x05 ++#define MAX6620_REG_DYN_FAN0 0x06 ++#define MAX6620_REG_DYN_FAN1 0x07 ++#define MAX6620_REG_DYN_FAN2 0x08 ++#define MAX6620_REG_DYN_FAN3 0x09 ++#define MAX6620_REG_TACH0 0x10 ++#define MAX6620_REG_TACH1 0x12 ++#define MAX6620_REG_TACH2 0x14 ++#define MAX6620_REG_TACH3 0x16 ++#define MAX6620_REG_VOLT0 0x18 ++#define MAX6620_REG_VOLT1 0x1A ++#define MAX6620_REG_VOLT2 0x1C ++#define MAX6620_REG_VOLT3 0x1E ++#define MAX6620_REG_TAR0 0x20 ++#define MAX6620_REG_TAR1 0x22 ++#define MAX6620_REG_TAR2 0x24 ++#define MAX6620_REG_TAR3 0x26 ++#define MAX6620_REG_DAC0 0x28 ++#define MAX6620_REG_DAC1 0x2A ++#define MAX6620_REG_DAC2 0x2C ++#define MAX6620_REG_DAC3 0x2E ++ ++/* ++ * Config register bits ++ */ ++ ++#define MAX6620_CFG_RUN 0x80 ++#define MAX6620_CFG_POR 0x40 ++#define MAX6620_CFG_TIMEOUT 0x20 ++#define MAX6620_CFG_FULLFAN 0x10 ++#define MAX6620_CFG_OSC 0x08 ++#define MAX6620_CFG_WD_MASK 0x06 ++#define MAX6620_CFG_WD_2 0x02 ++#define MAX6620_CFG_WD_6 0x04 ++#define MAX6620_CFG_WD10 0x06 ++#define MAX6620_CFG_WD 0x01 ++ ++ ++/* ++ * Failure status register bits ++ */ ++ ++#define MAX6620_FAIL_TACH0 0x10 ++#define MAX6620_FAIL_TACH1 0x20 ++#define MAX6620_FAIL_TACH2 0x40 ++#define MAX6620_FAIL_TACH3 0x80 ++#define MAX6620_FAIL_MASK0 0x01 ++#define MAX6620_FAIL_MASK1 0x02 ++#define MAX6620_FAIL_MASK2 0x04 ++#define MAX6620_FAIL_MASK3 0x08 ++ ++ ++/* Minimum and maximum values of the FAN-RPM */ ++#define FAN_RPM_MIN 240 ++#define FAN_RPM_MAX 30000 ++ ++#define DIV_FROM_REG(reg) (1 << ((reg & 0xE0) >> 5)) ++ ++static int max6620_probe(struct i2c_client *client, const struct i2c_device_id *id); ++static int max6620_init_client(struct i2c_client *client); ++static int max6620_remove(struct i2c_client *client); ++static struct max6620_data *max6620_update_device(struct device *dev); ++ ++static const u8 config_reg[] = { ++ MAX6620_REG_CONF_FAN0, ++ MAX6620_REG_CONF_FAN1, ++ MAX6620_REG_CONF_FAN2, ++ MAX6620_REG_CONF_FAN3, ++}; ++ ++static const u8 dyn_reg[] = { ++ MAX6620_REG_DYN_FAN0, ++ MAX6620_REG_DYN_FAN1, ++ MAX6620_REG_DYN_FAN2, ++ MAX6620_REG_DYN_FAN3, ++}; ++ ++static const u8 tach_reg[] = { ++ MAX6620_REG_TACH0, ++ MAX6620_REG_TACH1, ++ MAX6620_REG_TACH2, ++ MAX6620_REG_TACH3, ++}; ++ ++static const u8 volt_reg[] = { ++ MAX6620_REG_VOLT0, ++ MAX6620_REG_VOLT1, ++ MAX6620_REG_VOLT2, ++ MAX6620_REG_VOLT3, ++}; ++ ++static const u8 target_reg[] = { ++ MAX6620_REG_TAR0, ++ MAX6620_REG_TAR1, ++ MAX6620_REG_TAR2, ++ MAX6620_REG_TAR3, ++}; ++ ++static const u8 dac_reg[] = { ++ MAX6620_REG_DAC0, ++ MAX6620_REG_DAC1, ++ MAX6620_REG_DAC2, ++ MAX6620_REG_DAC3, ++}; ++ ++/* ++ * Driver data (common to all clients) ++ */ ++ ++static const struct i2c_device_id max6620_id[] = { ++ { "max6620", 0 }, ++ { } ++}; ++MODULE_DEVICE_TABLE(i2c, max6620_id); ++ ++static struct i2c_driver max6620_driver = { ++ .class = I2C_CLASS_HWMON, ++ .driver = { ++ .name = "max6620", ++ }, ++ .probe = max6620_probe, ++ .remove = __devexit_p(max6620_remove), ++ .id_table = max6620_id, ++ .address_list = normal_i2c, ++}; ++ ++/* ++ * Client data (each client gets its own) ++ */ ++ ++struct max6620_data { ++ struct device *hwmon_dev; ++ struct mutex update_lock; ++ int nr_fans; ++ char valid; /* zero until following fields are valid */ ++ unsigned long last_updated; /* in jiffies */ ++ ++ /* register values */ ++ u8 speed[4]; ++ u8 config; ++ u8 fancfg[4]; ++ u8 fandyn[4]; ++ u8 tach[4]; ++ u8 volt[4]; ++ u8 target[4]; ++ u8 dac[4]; ++ u8 fault; ++}; ++ ++static ssize_t get_fan(struct device *dev, struct device_attribute *devattr, char *buf) { ++ ++ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); ++ struct max6620_data *data = max6620_update_device(dev); ++ int rpm; ++ ++ /* ++ * Calculation details: ++ * ++ * Each tachometer counts over an interval given by the "count" ++ * register (0.25, 0.5, 1 or 2 seconds). This module assumes ++ * that the fans produce two pulses per revolution (this seems ++ * to be the most common). ++ */ ++ if(data->tach[attr->index] == 0 || data->tach[attr->index] == 255) { ++ rpm = 0; ++ } else { ++ rpm = ((clock / (data->tach[attr->index] << 3)) * 30 * DIV_FROM_REG(data->fandyn[attr->index])); ++ } ++ ++ return sprintf(buf, "%d\n", rpm); ++} ++ ++static ssize_t get_target(struct device *dev, struct device_attribute *devattr, char *buf) { ++ ++ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); ++ struct max6620_data *data = max6620_update_device(dev); ++ int kscale, ktach, rpm; ++ ++ /* ++ * Use the datasheet equation: ++ * ++ * FanSpeed = KSCALE x fCLK / [256 x (KTACH + 1)] ++ * ++ * then multiply by 60 to give rpm. ++ */ ++ ++ kscale = DIV_FROM_REG(data->fandyn[attr->index]); ++ ktach = data->target[attr->index]; ++ if(ktach == 0) { ++ rpm = 0; ++ } else { ++ rpm = ((60 * kscale * clock) / (ktach << 3)); ++ } ++ return sprintf(buf, "%d\n", rpm); ++} ++ ++static ssize_t set_target(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { ++ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); ++ struct max6620_data *data = i2c_get_clientdata(client); ++ int kscale, ktach; ++ unsigned long rpm; ++ int err; ++ ++ err = kstrtoul(buf, 10, &rpm); ++ if (err) ++ return err; ++ ++ rpm = SENSORS_LIMIT(rpm, FAN_RPM_MIN, FAN_RPM_MAX); ++ ++ /* ++ * Divide the required speed by 60 to get from rpm to rps, then ++ * use the datasheet equation: ++ * ++ * KTACH = [(fCLK x KSCALE) / (256 x FanSpeed)] - 1 ++ */ ++ ++ mutex_lock(&data->update_lock); ++ ++ kscale = DIV_FROM_REG(data->fandyn[attr->index]); ++ ktach = ((60 * kscale * clock) / rpm); ++ if (ktach < 0) ++ ktach = 0; ++ if (ktach > 255) ++ ktach = 255; ++ data->target[attr->index] = ktach; ++ ++ i2c_smbus_write_byte_data(client, target_reg[attr->index], data->target[attr->index]); ++ i2c_smbus_write_byte_data(client, target_reg[attr->index]+0x01, 0x00); ++ ++ mutex_unlock(&data->update_lock); ++ ++ return count; ++} ++ ++/* ++ * Get/set the fan speed in open loop mode using pwm1 sysfs file. ++ * Speed is given as a relative value from 0 to 255, where 255 is maximum ++ * speed. Note that this is done by writing directly to the chip's DAC, ++ * it won't change the closed loop speed set by fan1_target. ++ * Also note that due to rounding errors it is possible that you don't read ++ * back exactly the value you have set. ++ */ ++ ++static ssize_t get_pwm(struct device *dev, struct device_attribute *devattr, char *buf) { ++ ++ int pwm; ++ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); ++ struct max6620_data *data = max6620_update_device(dev); ++ ++ /* ++ * Useful range for dac is 0-180 for 12V fans and 0-76 for 5V fans. ++ * Lower DAC values mean higher speeds. ++ */ ++ pwm = ((int)data->volt[attr->index]); ++ ++ if (pwm < 0) ++ pwm = 0; ++ ++ return sprintf(buf, "%d\n", pwm); ++} ++ ++static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { ++ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); ++ struct max6620_data *data = i2c_get_clientdata(client); ++ unsigned long pwm; ++ int err; ++ ++ err = kstrtoul(buf, 10, &pwm); ++ if (err) ++ return err; ++ ++ pwm = SENSORS_LIMIT(pwm, 0, 255); ++ ++ mutex_lock(&data->update_lock); ++ ++ data->dac[attr->index] = pwm; ++ ++ ++ i2c_smbus_write_byte_data(client, dac_reg[attr->index], data->dac[attr->index]); ++ i2c_smbus_write_byte_data(client, dac_reg[attr->index]+1, 0x00); ++ ++ mutex_unlock(&data->update_lock); ++ ++ return count; ++} ++ ++/* ++ * Get/Set controller mode: ++ * Possible values: ++ * 0 = Fan always on ++ * 1 = Open loop, Voltage is set according to speed, not regulated. ++ * 2 = Closed loop, RPM for all fans regulated by fan1 tachometer ++ */ ++ ++static ssize_t get_enable(struct device *dev, struct device_attribute *devattr, char *buf) { ++ ++ struct max6620_data *data = max6620_update_device(dev); ++ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); ++ int mode = (data->fancfg[attr->index] & 0x80 ) >> 7; ++ int sysfs_modes[2] = {1, 2}; ++ ++ return sprintf(buf, "%d\n", sysfs_modes[mode]); ++} ++ ++static ssize_t set_enable(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { ++ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct max6620_data *data = i2c_get_clientdata(client); ++ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); ++ int max6620_modes[3] = {0, 1, 0}; ++ unsigned long mode; ++ int err; ++ ++ err = kstrtoul(buf, 10, &mode); ++ if (err) ++ return err; ++ ++ if (mode > 2) ++ return -EINVAL; ++ ++ mutex_lock(&data->update_lock); ++ ++ data->fancfg[attr->index] = i2c_smbus_read_byte_data(client, config_reg[attr->index]); ++ data->fancfg[attr->index] = (data->fancfg[attr->index] & ~0x80) ++ | (max6620_modes[mode] << 7); ++ ++ i2c_smbus_write_byte_data(client, config_reg[attr->index], data->fancfg[attr->index]); ++ ++ mutex_unlock(&data->update_lock); ++ ++ return count; ++} ++ ++/* ++ * Read/write functions for fan1_div sysfs file. The MAX6620 has no such ++ * divider. We handle this by converting between divider and counttime: ++ * ++ * (counttime == k) <==> (divider == 2^k), k = 0, 1, 2, 3, 4 or 5 ++ * ++ * Lower values of k allow to connect a faster fan without the risk of ++ * counter overflow. The price is lower resolution. You can also set counttime ++ * using the module parameter. Note that the module parameter "prescaler" also ++ * influences the behaviour. Unfortunately, there's no sysfs attribute ++ * defined for that. See the data sheet for details. ++ */ ++ ++static ssize_t get_div(struct device *dev, struct device_attribute *devattr, char *buf) { ++ ++ struct max6620_data *data = max6620_update_device(dev); ++ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); ++ ++ return sprintf(buf, "%d\n", DIV_FROM_REG(data->fandyn[attr->index])); ++} ++ ++static ssize_t set_div(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { ++ ++ struct i2c_client *client = to_i2c_client(dev); ++ struct max6620_data *data = i2c_get_clientdata(client); ++ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); ++ unsigned long div; ++ int err; ++ u8 div_bin; ++ ++ err = kstrtoul(buf, 10, &div); ++ if (err) ++ return err; ++ ++ mutex_lock(&data->update_lock); ++ switch (div) { ++ case 1: ++ div_bin = 0; ++ break; ++ case 2: ++ div_bin = 1; ++ break; ++ case 4: ++ div_bin = 2; ++ break; ++ case 8: ++ div_bin = 3; ++ break; ++ case 16: ++ div_bin = 4; ++ break; ++ case 32: ++ div_bin = 5; ++ break; ++ default: ++ mutex_unlock(&data->update_lock); ++ return -EINVAL; ++ } ++ data->fandyn[attr->index] &= 0x1F; ++ data->fandyn[attr->index] |= div_bin << 5; ++ i2c_smbus_write_byte_data(client, dyn_reg[attr->index], data->fandyn[attr->index]); ++ mutex_unlock(&data->update_lock); ++ ++ return count; ++} ++ ++/* ++ * Get alarm stati: ++ * Possible values: ++ * 0 = no alarm ++ * 1 = alarm ++ */ ++ ++static ssize_t get_alarm(struct device *dev, struct device_attribute *devattr, char *buf) { ++ ++ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); ++ struct max6620_data *data = max6620_update_device(dev); ++ struct i2c_client *client = to_i2c_client(dev); ++ int alarm = 0; ++ ++ if (data->fault & (1 << attr->index)) { ++ mutex_lock(&data->update_lock); ++ alarm = 1; ++ data->fault &= ~(1 << attr->index); ++ data->fault |= i2c_smbus_read_byte_data(client, ++ MAX6620_REG_FAULT); ++ mutex_unlock(&data->update_lock); ++ } ++ ++ return sprintf(buf, "%d\n", alarm); ++} ++ ++static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, get_fan, NULL, 0); ++static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, get_fan, NULL, 1); ++static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, get_fan, NULL, 2); ++static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, get_fan, NULL, 3); ++static SENSOR_DEVICE_ATTR(fan1_target, S_IWUSR | S_IRUGO, get_target, set_target, 0); ++static SENSOR_DEVICE_ATTR(fan1_div, S_IWUSR | S_IRUGO, get_div, set_div, 0); ++// static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, get_enable, set_enable, 0); ++static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 0); ++static SENSOR_DEVICE_ATTR(fan2_target, S_IWUSR | S_IRUGO, get_target, set_target, 1); ++static SENSOR_DEVICE_ATTR(fan2_div, S_IWUSR | S_IRUGO, get_div, set_div, 1); ++// static SENSOR_DEVICE_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, get_enable, set_enable, 1); ++static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 1); ++static SENSOR_DEVICE_ATTR(fan3_target, S_IWUSR | S_IRUGO, get_target, set_target, 2); ++static SENSOR_DEVICE_ATTR(fan3_div, S_IWUSR | S_IRUGO, get_div, set_div, 2); ++// static SENSOR_DEVICE_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, get_enable, set_enable, 2); ++static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 2); ++static SENSOR_DEVICE_ATTR(fan4_target, S_IWUSR | S_IRUGO, get_target, set_target, 3); ++static SENSOR_DEVICE_ATTR(fan4_div, S_IWUSR | S_IRUGO, get_div, set_div, 3); ++// static SENSOR_DEVICE_ATTR(pwm4_enable, S_IWUSR | S_IRUGO, get_enable, set_enable, 3); ++static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, get_pwm, set_pwm, 3); ++ ++static struct attribute *max6620_attrs[] = { ++ &sensor_dev_attr_fan1_input.dev_attr.attr, ++ &sensor_dev_attr_fan2_input.dev_attr.attr, ++ &sensor_dev_attr_fan3_input.dev_attr.attr, ++ &sensor_dev_attr_fan4_input.dev_attr.attr, ++ &sensor_dev_attr_fan1_target.dev_attr.attr, ++ &sensor_dev_attr_fan1_div.dev_attr.attr, ++// &sensor_dev_attr_pwm1_enable.dev_attr.attr, ++ &sensor_dev_attr_pwm1.dev_attr.attr, ++ &sensor_dev_attr_fan2_target.dev_attr.attr, ++ &sensor_dev_attr_fan2_div.dev_attr.attr, ++// &sensor_dev_attr_pwm2_enable.dev_attr.attr, ++ &sensor_dev_attr_pwm2.dev_attr.attr, ++ &sensor_dev_attr_fan3_target.dev_attr.attr, ++ &sensor_dev_attr_fan3_div.dev_attr.attr, ++// &sensor_dev_attr_pwm3_enable.dev_attr.attr, ++ &sensor_dev_attr_pwm3.dev_attr.attr, ++ &sensor_dev_attr_fan4_target.dev_attr.attr, ++ &sensor_dev_attr_fan4_div.dev_attr.attr, ++// &sensor_dev_attr_pwm4_enable.dev_attr.attr, ++ &sensor_dev_attr_pwm4.dev_attr.attr, ++ NULL ++}; ++ ++static struct attribute_group max6620_attr_grp = { ++ .attrs = max6620_attrs, ++}; ++ ++ ++/* ++ * Real code ++ */ ++ ++static int __devinit max6620_probe(struct i2c_client *client, const struct i2c_device_id *id) { ++ ++ struct max6620_data *data; ++ int err; ++ ++ data = devm_kzalloc(&client->dev, sizeof(struct max6620_data), GFP_KERNEL); ++ if (!data) { ++ dev_err(&client->dev, "out of memory.\n"); ++ return -ENOMEM; ++ } ++ ++ i2c_set_clientdata(client, data); ++ mutex_init(&data->update_lock); ++ data->nr_fans = id->driver_data; ++ ++ /* ++ * Initialize the max6620 chip ++ */ ++ dev_info(&client->dev, "About to initialize module\n"); ++ ++ err = max6620_init_client(client); ++ if (err) ++ return err; ++ dev_info(&client->dev, "Module initialized\n"); ++ ++ err = sysfs_create_group(&client->dev.kobj, &max6620_attr_grp); ++ if (err) ++ return err; ++dev_info(&client->dev, "Sysfs entries created\n"); ++ ++ data->hwmon_dev = hwmon_device_register(&client->dev); ++ if (!IS_ERR(data->hwmon_dev)) ++ return 0; ++ ++ err = PTR_ERR(data->hwmon_dev); ++ dev_err(&client->dev, "error registering hwmon device.\n"); ++ ++ sysfs_remove_group(&client->dev.kobj, &max6620_attr_grp); ++ return err; ++} ++ ++static int __devexit max6620_remove(struct i2c_client *client) { ++ ++ struct max6620_data *data = i2c_get_clientdata(client); ++ ++ hwmon_device_unregister(data->hwmon_dev); ++ ++ sysfs_remove_group(&client->dev.kobj, &max6620_attr_grp); ++ return 0; ++} ++ ++static int max6620_init_client(struct i2c_client *client) { ++ ++ struct max6620_data *data = i2c_get_clientdata(client); ++ int config; ++ int err = -EIO; ++ int i; ++ ++ config = i2c_smbus_read_byte_data(client, MAX6620_REG_CONFIG); ++ ++ if (config < 0) { ++ dev_err(&client->dev, "Error reading config, aborting.\n"); ++ return err; ++ } ++ ++ ++ ++ if (i2c_smbus_write_byte_data(client, MAX6620_REG_CONFIG, config)) { ++ dev_err(&client->dev, "Config write error, aborting.\n"); ++ return err; ++ } ++ ++ data->config = config; ++ for (i = 0; i < 4; i++) { ++ data->fancfg[i] = i2c_smbus_read_byte_data(client, config_reg[i]); ++ data->fancfg[i] |= 0x80; // enable TACH monitoring ++ i2c_smbus_write_byte_data(client, config_reg[i], data->fancfg[i]); ++ data->fandyn[i] = i2c_smbus_read_byte_data(client, dyn_reg[i]); ++ data-> fandyn[i] |= 0x1C; ++ i2c_smbus_write_byte_data(client, dyn_reg[i], data->fandyn[i]); ++ data->tach[i] = i2c_smbus_read_byte_data(client, tach_reg[i]); ++ data->volt[i] = i2c_smbus_read_byte_data(client, volt_reg[i]); ++ data->target[i] = i2c_smbus_read_byte_data(client, target_reg[i]); ++ data->dac[i] = i2c_smbus_read_byte_data(client, dac_reg[i]); ++ ++ ++ ++ } ++ ++ ++ ++ return 0; ++} ++ ++ ++ ++ ++static struct max6620_data *max6620_update_device(struct device *dev) ++{ ++ int i; ++ struct i2c_client *client = to_i2c_client(dev); ++ struct max6620_data *data = i2c_get_clientdata(client); ++ ++ mutex_lock(&data->update_lock); ++ ++ if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { ++ ++ for (i = 0; i < 4; i++) { ++ data->fancfg[i] = i2c_smbus_read_byte_data(client, config_reg[i]); ++ data->fandyn[i] = i2c_smbus_read_byte_data(client, dyn_reg[i]); ++ data->tach[i] = i2c_smbus_read_byte_data(client, tach_reg[i]); ++ data->volt[i] = i2c_smbus_read_byte_data(client, volt_reg[i]); ++ data->target[i] = i2c_smbus_read_byte_data(client, target_reg[i]); ++ data->dac[i] = i2c_smbus_read_byte_data(client, dac_reg[i]); ++ } ++ ++ ++ /* ++ * Alarms are cleared on read in case the condition that ++ * caused the alarm is removed. Keep the value latched here ++ * for providing the register through different alarm files. ++ */ ++ u8 fault_reg; ++ fault_reg = i2c_smbus_read_byte_data(client, MAX6620_REG_FAULT); ++ data->fault |= (fault_reg >> 4) & (fault_reg & 0x0F); ++ ++ data->last_updated = jiffies; ++ data->valid = 1; ++ } ++ ++ mutex_unlock(&data->update_lock); ++ ++ return data; ++} ++ ++module_i2c_driver(max6620_driver); ++ ++static int __init max6620_init(void) ++{ ++ return i2c_add_driver(&max6620_driver); ++} ++module_init(max6620_init); ++ ++/** ++ * sht21_init() - clean up driver ++ * ++ * Called when module is removed. ++ */ ++static void __exit max6620_exit(void) ++{ ++ i2c_del_driver(&max6620_driver); ++} ++module_exit(max6620_exit); ++ ++MODULE_AUTHOR("Lucas Grunenberg"); ++MODULE_DESCRIPTION("MAX6620 sensor driver"); ++MODULE_LICENSE("GPL"); diff --git a/packages/base/any/kernels/4.14-lts/patches/series b/packages/base/any/kernels/4.14-lts/patches/series index 68a83d26..240434b4 100755 --- a/packages/base/any/kernels/4.14-lts/patches/series +++ b/packages/base/any/kernels/4.14-lts/patches/series @@ -5,3 +5,6 @@ drivers-i2c-busses-xgs_iproc_smbus-clk-freq.patch 0002-driver-support-intel-igb-bcm5461S-phy.patch 0003-drivers-net-ethernet-broadcom-tg3.patch driver-ixgbe-external-phy.patch +driver-hwmon-max6620.patch +driver-hwmon-max6620-fix-rpm-calc.patch +driver-hwmon-max6620-update.patch