From: Cumulus Networks <support@xxxxxxxxxxxxxxxxxxx> Driver for MAX6620 Fan sensor --- 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 e3675377bc5d..31478a55a3be 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1068,6 +1068,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 d712c61c1f5e..923af2e4aecd 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -139,6 +139,7 @@ obj-$(CONFIG_SENSORS_MAX6621) += max6621.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_MAX31790) += max31790.o obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o diff --git a/drivers/hwmon/max6620.c b/drivers/hwmon/max6620.c new file mode 100644 index 000000000000..3c337c7b0f4d --- /dev/null +++ b/drivers/hwmon/max6620.c @@ -0,0 +1,702 @@ +/* + * max6620.c - Linux Kernel module for hardware monitoring. + * + * (C) 2012 by L. Grunenberg <contact@xxxxxxxxxxxxxx> + * + * based on code written by : + * 2007 by Hans J. Koch <hjk@xxxxxxxxxxxx> + * John Morris <john.morris@xxxxxxxxxxxxxx> + * Copyright (c) 2003 Spirent Communications + * and Claus Gindhart <claus.gindhart@xxxxxxxxxxx> + * + * 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 <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> + +/* + * 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"); -- 2.32.0