* [PATCH] hwmon: Driver for Texas Instruments amc6821 chip
@ 2009-08-31 20:24 tomaz.mertelj
2009-09-01 17:56 ` [lm-sensors] " Andre Prendel
` (2 more replies)
0 siblings, 3 replies; 15+ messages in thread
From: tomaz.mertelj @ 2009-08-31 20:24 UTC (permalink / raw)
To: lm-sensors; +Cc: linux-kernel
[-- Attachment #1: Mail message body --]
[-- Type: text/plain, Size: 205 bytes --]
I have crafted a hwmon driver for Texas Instruments amc6821 SMB-bus 2-
channel temperature sensor, pwm controller. This is my first linux project
so some corrections will be necessary.
T. Mertelj
[-- Attachment #2: amc6821.diff --]
[-- Type: application/octet-stream, Size: 31259 bytes --]
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 2d50166..d231a28 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -777,6 +777,16 @@ config SENSORS_ADS7828
This driver can also be built as a module. If so, the module
will be called ads7828.
+config SENSORS_AMC6821
+ tristate "Texas Instruments AMC6821"
+ depends on I2C
+ help
+ If you say yes here you get support for the Texas Instruments
+ AMC6821 hardware monitoring chips.
+
+ This driver can also be build as a module. If so, the module
+ will be called amc6821.
+
config SENSORS_THMC50
tristate "Texas Instruments THMC50 / Analog Devices ADM1022"
depends on I2C && EXPERIMENTAL
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index b793dce..431de76 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -81,6 +81,7 @@ obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o
obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o
obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o
obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o
+obj-$(CONFIG_SENSORS_AMC6821) += amc6821.o
obj-$(CONFIG_SENSORS_THMC50) += thmc50.o
obj-$(CONFIG_SENSORS_TMP401) += tmp401.o
obj-$(CONFIG_SENSORS_VIA686A) += via686a.o
diff --git a/drivers/hwmon/amc6821.c b/drivers/hwmon/amc6821.c
new file mode 100644
index 0000000..75dee4c
--- /dev/null
+++ b/drivers/hwmon/amc6821.c
@@ -0,0 +1,851 @@
+/*
+ amc6821.c - Part of lm_sensors, Linux kernel modules for hardware
+ monitoring
+ Copyright (C) 2009 T. Mertelj <tomaz.mertelj@guest.arnes.si>
+
+ 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/kernel.h> /* Needed for KERN_INFO */
+#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>
+
+
+/*
+ * Addresses to scan.
+ */
+
+static const unsigned short normal_i2c[ ] = {0x18, 0x19, 0x1a, 0x2c,0x2d,0x2e,
+ 0x4c,0x4d,0x4e,I2C_CLIENT_END};
+
+
+
+/*
+ * Insmod parameters
+ */
+
+static int pwminv = 0; /*Inverted PWM output. */
+module_param(pwminv, int, S_IRUGO);
+
+static int init = 1; /*Power-on initialization.*/
+module_param(init, int, S_IRUGO);
+
+
+I2C_CLIENT_INSMOD_1(amc6821);
+
+#define AMC6821_REG_DEV_ID 0x3D
+#define AMC6821_REG_COMP_ID 0x3E
+#define AMC6821_REG_CONF1 0x00
+#define AMC6821_REG_CONF2 0x01
+#define AMC6821_REG_CONF3 0x3F
+#define AMC6821_REG_CONF4 0x04
+#define AMC6821_REG_STAT1 0x02
+#define AMC6821_REG_STAT2 0x03
+#define AMC6821_REG_TDATA_LOW 0x08
+#define AMC6821_REG_TDATA_HI 0x09
+#define AMC6821_REG_LTEMP_HI 0x0A
+#define AMC6821_REG_RTEMP_HI 0x0B
+#define AMC6821_REG_LTEMP_LIMIT_MIN 0x15
+#define AMC6821_REG_LTEMP_LIMIT_MAX 0x14
+#define AMC6821_REG_RTEMP_LIMIT_MIN 0x19
+#define AMC6821_REG_RTEMP_LIMIT_MAX 0x18
+#define AMC6821_REG_LTEMP_CRIT 0x1B
+#define AMC6821_REG_RTEMP_CRIT 0x1D
+#define AMC6821_REG_PSV_TEMP 0x1C
+#define AMC6821_REG_DCY 0x22
+#define AMC6821_REG_LTEMP_FAN_CTRL 0x24
+#define AMC6821_REG_RTEMP_FAN_CTRL 0x25
+#define AMC6821_REG_DCY_LOW_TEMP 0x21
+
+#define AMC6821_REG_TACH_LLIMITL 0x10
+#define AMC6821_REG_TACH_LLIMITH 0x11
+#define AMC6821_REG_TACH_HLIMITL 0x12
+#define AMC6821_REG_TACH_HLIMITH 0x13
+
+#define AMC6821_CONF1_START 0x01
+#define AMC6821_CONF1_FAN_INT_EN 0x02
+#define AMC6821_CONF1_FANIE 0x04
+#define AMC6821_CONF1_PWMINV 0x08
+#define AMC6821_CONF1_FAN_FAULT_EN 0x10
+#define AMC6821_CONF1_FDRC0 0x20
+#define AMC6821_CONF1_FDRC1 0x40
+#define AMC6821_CONF1_THERMOVIE 0x80
+
+#define AMC6821_CONF2_PWM_EN 0x01
+#define AMC6821_CONF2_TACH_MODE 0x02
+#define AMC6821_CONF2_TACH_EN 0x04
+#define AMC6821_CONF2_RTFIE 0x08
+#define AMC6821_CONF2_LTOIE 0x10
+#define AMC6821_CONF2_RTOIE 0x20
+#define AMC6821_CONF2_PSVIE 0x40
+#define AMC6821_CONF2_RST 0x80
+
+#define AMC6821_CONF3_THERM_FAN_EN 0x80
+#define AMC6821_CONF3_REV_MASK 0x0F
+
+#define AMC6821_CONF4_OVREN 0x10
+#define AMC6821_CONF4_TACH_FAST 0x20
+#define AMC6821_CONF4_PSPR 0x40
+#define AMC6821_CONF4_MODE 0x80
+
+#define AMC6821_STAT1_RPM_ALARM 0x01
+#define AMC6821_STAT1_FANS 0x02
+#define AMC6821_STAT1_RTH 0x04
+#define AMC6821_STAT1_RTL 0x08
+#define AMC6821_STAT1_R_THERM 0x10
+#define AMC6821_STAT1_RTF 0x20
+#define AMC6821_STAT1_LTH 0x40
+#define AMC6821_STAT1_LTL 0x80
+
+#define AMC6821_STAT2_RTC 0x08
+#define AMC6821_STAT2_LTC 0x10
+#define AMC6821_STAT2_LPSV 0x20
+#define AMC6821_STAT2_L_THERM 0x40
+#define AMC6821_STAT2_THERM_IN 0x80
+
+
+static int amc6821_probe(struct i2c_client *client,const struct i2c_device_id *id);
+static int amc6821_detect(struct i2c_client *client, int kind,struct i2c_board_info *info);
+static int amc6821_init_client(struct i2c_client *client);
+static int amc6821_remove(struct i2c_client *client);
+static struct amc6821_data *amc6821_update_device(struct device *dev);
+
+/*
+ * Driver data (common to all clients)
+ */
+
+static const struct i2c_device_id amc6821_id[] = {
+ { "amc6821", amc6821 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, amc6821_id);
+
+
+static struct i2c_driver amc6821_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "amc6821",
+ },
+ .probe = amc6821_probe,
+ .remove = amc6821_remove,
+ .id_table = amc6821_id,
+ .detect = amc6821_detect,
+ .address_data = &addr_data,
+};
+
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct amc6821_data
+{
+ struct device *hwmon_dev;
+ struct mutex update_lock;
+ char valid; /* zero until following fields are valid */
+ unsigned long last_updated; /* in jiffies */
+
+ /* register values */
+ int temp1_input;
+ int temp1_min;
+ int temp1_max;
+ int temp1_crit;
+
+ u8 temp1_alarm;
+ int temp2_input;
+ int temp2_min;
+ int temp2_max;
+ int temp2_crit;
+ u8 temp2_alarm;
+
+ u16 fan1_input;
+ u16 fan1_min;
+ u16 fan1_max;
+ u8 fan1_div;
+
+ u8 pwm1;
+ u8 temp1_auto_point_temp[3];
+ u8 temp2_auto_point_temp[3];
+ u8 pwm1_auto_point_pwm[3];
+ u8 pwm1_enable;
+ u8 pwm1_auto_channels_temp;
+
+ u8 stat1;
+ u8 stat2;
+};
+
+
+#define get_temp_para(name) \
+static ssize_t get_##name(struct device *dev, struct device_attribute *devattr,char *buf){ \
+ struct amc6821_data *data = amc6821_update_device(dev);\
+ return sprintf(buf, "%d\n", data->name * 1000);\
+}
+
+get_temp_para(temp1_input);
+get_temp_para(temp1_min);
+get_temp_para(temp1_max);
+get_temp_para(temp2_input);
+get_temp_para(temp2_min);
+get_temp_para(temp2_max);
+get_temp_para(temp1_crit);
+get_temp_para(temp2_crit);
+
+#define set_temp_para(name,reg)\
+static ssize_t set_##name(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \
+{ \
+ struct i2c_client *client = to_i2c_client(dev); \
+ struct amc6821_data *data = i2c_get_clientdata(client); \
+ int val = simple_strtol(buf, NULL, 10); \
+ \
+ mutex_lock(&data->update_lock); \
+ data->name = SENSORS_LIMIT(val / 1000, -128, 127); \
+ if (i2c_smbus_write_byte_data(client, reg, data->name)) {\
+ dev_err(&client->dev, "Register write error, aborting.\n");\
+ count= -EIO;\
+ }\
+ mutex_unlock(&data->update_lock); \
+ return count; \
+}
+
+set_temp_para(temp1_min,AMC6821_REG_LTEMP_LIMIT_MIN);
+set_temp_para(temp1_max,AMC6821_REG_LTEMP_LIMIT_MAX);
+set_temp_para(temp2_min,AMC6821_REG_RTEMP_LIMIT_MIN);
+set_temp_para(temp2_max,AMC6821_REG_RTEMP_LIMIT_MAX);
+set_temp_para(temp1_crit,AMC6821_REG_LTEMP_CRIT);
+set_temp_para(temp2_crit,AMC6821_REG_RTEMP_CRIT);
+
+#define get_temp_alarm(name, reg, mask)\
+static ssize_t get_##name(struct device *dev, struct device_attribute *devattr,char *buf){\
+ struct amc6821_data *data = amc6821_update_device(dev);\
+ if( data->reg & mask)\
+ return sprintf(buf, "1");\
+ else \
+ return sprintf(buf, "0");\
+}\
+
+get_temp_alarm(temp1_min_alarm, stat1, AMC6821_STAT1_LTL)
+get_temp_alarm(temp1_max_alarm, stat1, AMC6821_STAT1_LTH)
+get_temp_alarm(temp2_min_alarm, stat1, AMC6821_STAT1_RTL)
+get_temp_alarm(temp2_max_alarm, stat1, AMC6821_STAT1_RTH)
+get_temp_alarm(temp1_crit_alarm, stat2, AMC6821_STAT2_LTC)
+get_temp_alarm(temp2_crit_alarm, stat2, AMC6821_STAT2_RTC)
+
+
+static ssize_t get_temp2_fault(struct device *dev, struct device_attribute *devattr,char *buf){
+ struct amc6821_data *data = amc6821_update_device(dev);
+ if( data->stat1 & AMC6821_STAT1_RTF)
+ return sprintf(buf, "1");
+ else
+ return sprintf(buf, "0");
+}
+
+static ssize_t get_pwm1(struct device *dev, struct device_attribute *devattr,char *buf){
+ struct amc6821_data *data = amc6821_update_device(dev);
+ return sprintf(buf, "%d\n", data->pwm1);
+}
+
+static ssize_t set_pwm1(struct device *dev, struct device_attribute *devattr,const char *buf, size_t count){
+ struct i2c_client *client = to_i2c_client(dev);
+ struct amc6821_data *data = i2c_get_clientdata(client);
+ int val = simple_strtol(buf, NULL, 10);
+
+ mutex_lock(&data->update_lock); \
+ data->pwm1 = SENSORS_LIMIT(val , 0, 255); \
+ i2c_smbus_write_byte_data(client, AMC6821_REG_DCY, data->pwm1); \
+ mutex_unlock(&data->update_lock); \
+ return count;
+}
+
+static ssize_t get_pwm1_enable(struct device *dev, struct device_attribute *devattr,char *buf){
+ struct amc6821_data *data = amc6821_update_device(dev);
+ return sprintf(buf, "%d\n", data->pwm1_enable);
+}
+
+static ssize_t set_pwm1_enable(struct device *dev, struct device_attribute *attr, const char *buf, size_t count){
+ struct i2c_client *client = to_i2c_client(dev);
+ struct amc6821_data *data = i2c_get_clientdata(client);
+ int val = simple_strtol(buf, NULL, 10);
+
+ int config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF1);
+ if (config < 0) {
+ dev_err(&client->dev, "Error reading configuration register, aborting.\n");
+ return -EIO;
+ }
+
+ switch (val){
+ case 1:
+ config &= ~AMC6821_CONF1_FDRC0;
+ config &= ~AMC6821_CONF1_FDRC1;
+ break;
+ case 2:
+ config &= ~AMC6821_CONF1_FDRC0;
+ config |= AMC6821_CONF1_FDRC1;
+ break;
+ case 3:
+ config |= AMC6821_CONF1_FDRC0;
+ config |= AMC6821_CONF1_FDRC1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ mutex_lock(&data->update_lock);
+ if (i2c_smbus_write_byte_data(client, AMC6821_REG_CONF1, config)) {
+ dev_err(&client->dev, "Configuration register write error, aborting.\n");
+ count= -EIO;
+ }
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+
+static ssize_t get_pwm1_auto_channels_temp(struct device *dev, struct device_attribute *devattr,char *buf){
+ struct amc6821_data *data = amc6821_update_device(dev);
+ return sprintf(buf, "%d\n", data->pwm1_auto_channels_temp);
+}
+
+
+#define get_auto_point(name)\
+static ssize_t get_##name(struct device *dev, struct device_attribute *devattr,char *buf){\
+ int nr = to_sensor_dev_attr(devattr)->index;\
+ struct amc6821_data *data = amc6821_update_device(dev);\
+ return sprintf(buf, "%d\n", data->name[nr] *1000);\
+}
+
+get_auto_point(temp1_auto_point_temp);
+get_auto_point(temp2_auto_point_temp);
+
+static ssize_t get_pwm1_auto_point_pwm(struct device *dev, struct device_attribute *devattr,char *buf){
+ int nr = to_sensor_dev_attr(devattr)->index;
+ struct amc6821_data *data = amc6821_update_device(dev);
+ return sprintf(buf, "%d\n", data->pwm1_auto_point_pwm[nr] );
+}
+
+
+#define set_temp_auto_point_temp(name,reg)\
+static ssize_t set_##name(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \
+{ \
+ struct i2c_client *client = to_i2c_client(dev); \
+ struct amc6821_data *data = amc6821_update_device(dev);\
+ int nr = to_sensor_dev_attr(attr)->index;\
+ int val = simple_strtol(buf, NULL, 10); \
+ u8 tmp;\
+ \
+ mutex_lock(&data->update_lock); \
+ switch(nr){\
+ case 0:\
+ data->name[0] = SENSORS_LIMIT(val / 1000, 0,data->temp1_auto_point_temp[1]-1);\
+ data->name[0] = SENSORS_LIMIT(data->name[0], 0,data->temp2_auto_point_temp[1]-1);\
+ data->name[0] = SENSORS_LIMIT(data->name[0], 0, 63); \
+ data->valid = 0;\
+ if (i2c_smbus_write_byte_data(client, AMC6821_REG_PSV_TEMP, data->name[ 0] )) {\
+ dev_err(&client->dev, "Register write error, aborting.\n");\
+ count = -EIO;\
+ }\
+ goto EXIT;\
+ break;\
+ case 1:\
+ data->name[1] = SENSORS_LIMIT(val / 1000, data->name[0]+1, data->name[ 2]-1); \
+ data->name[1] &= 0x7C;\
+ break;\
+ case 2:\
+ data->name[2] = SENSORS_LIMIT(val / 1000, data->name[1]+1, 127); \
+ break;\
+ }\
+ val = (16*(data->pwm1_auto_point_pwm[2]-data->pwm1_auto_point_pwm[1]))/(data->name[2]-data->name[1]);\
+ for(tmp = 4; tmp >= 0; tmp--){\
+ if( val <= (0x200 >> tmp)) break;\
+ }\
+ tmp |= ((u8) data->name[ 1] & 0x7C) << 1;\
+ if (i2c_smbus_write_byte_data(client, reg, tmp)) {\
+ dev_err(&client->dev, "Register write error, aborting.\n");\
+ count = -EIO;\
+ }\
+ data->valid = 0;\
+EXIT:\
+ mutex_unlock(&data->update_lock); \
+ return count; \
+}
+
+set_temp_auto_point_temp(temp1_auto_point_temp, AMC6821_REG_LTEMP_FAN_CTRL);
+set_temp_auto_point_temp(temp2_auto_point_temp, AMC6821_REG_RTEMP_FAN_CTRL);
+
+static ssize_t set_pwm1_auto_point_pwm(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct amc6821_data *data = i2c_get_clientdata(client);
+ int val = simple_strtol(buf, NULL, 10);
+ u8 tmp;
+
+ mutex_lock(&data->update_lock);
+ data->pwm1_auto_point_pwm[ 1] = SENSORS_LIMIT(val, 0, 255);
+ if (i2c_smbus_write_byte_data(client, AMC6821_REG_DCY_LOW_TEMP, data->pwm1_auto_point_pwm[ 1])) {\
+ dev_err(&client->dev, "Register write error, aborting.\n");
+ count = -EIO;
+ }
+
+ val =(16 * (data->pwm1_auto_point_pwm[2]-data->pwm1_auto_point_pwm[1]))/(data->temp1_auto_point_temp[2]-data->temp1_auto_point_temp[1]);
+ for(tmp = 4; tmp >= 0; tmp--){
+ if( val <= (0x200 >> tmp)) break;
+ }
+ tmp |= ((u8) data->temp1_auto_point_temp[ 1] & 0x7C) << 1;
+ if (i2c_smbus_write_byte_data(client, AMC6821_REG_LTEMP_FAN_CTRL, tmp)) {
+ dev_err(&client->dev, "Register write error, aborting.\n");
+ count = -EIO;
+ }
+ data->valid = 0;
+
+ val = (16 * (data->pwm1_auto_point_pwm[2]-data->pwm1_auto_point_pwm[1]))/(data->temp2_auto_point_temp[2]-data->temp2_auto_point_temp[1]);
+ for(tmp = 4; tmp >= 0; tmp--){
+ if( val <= (0x200 >> tmp)) break;
+ }
+ tmp |= ((u8) data->temp2_auto_point_temp[ 1] & 0x7C) << 1;
+ if (i2c_smbus_write_byte_data(client, AMC6821_REG_RTEMP_FAN_CTRL, tmp)) {
+ dev_err(&client->dev, "Register write error, aborting.\n");
+ count = -EIO;
+ }
+ data->valid = 0;
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+
+#define get_fan_para(name) static ssize_t get_##name(struct device *dev, struct device_attribute *devattr,char *buf){ \
+ struct amc6821_data *data = amc6821_update_device(dev); \
+ if ( 0 == data->name) return sprintf(buf,"0");\
+ return sprintf(buf, "%d\n", (int)(6000000 / data->name)); \
+}
+
+get_fan_para(fan1_input);
+get_fan_para(fan1_min);
+get_fan_para(fan1_max);
+
+static ssize_t get_fan1_fault(struct device *dev, struct device_attribute *devattr,char *buf){
+ struct amc6821_data *data = amc6821_update_device(dev);
+ if( data->stat1 & AMC6821_STAT1_FANS)
+ return sprintf(buf, "1");
+ else
+ return sprintf(buf, "0");
+}
+
+
+#define set_fan_para(name, reg) \
+static ssize_t set_##name(struct device *dev, struct device_attribute *attr, const char *buf, size_t count){ \
+ struct i2c_client *client = to_i2c_client(dev); \
+ struct amc6821_data *data = i2c_get_clientdata(client); \
+ int val = simple_strtol(buf, NULL, 10); \
+ val = 1>val ? 0xFFFF : 6000000/val; \
+\
+ mutex_lock(&data->update_lock); \
+ data->name = (u16) SENSORS_LIMIT(val, 1, 0xFFFF); \
+ if (i2c_smbus_write_byte_data(client, reg, data->name & 0xFF)) { \
+ dev_err(&client->dev, "Register write error, aborting.\n");\
+ count= -EIO;\
+ }\
+ if (i2c_smbus_write_byte_data(client, reg+1, data->name >> 8 )) { \
+ dev_err(&client->dev, "Register write error, aborting.\n");\
+ count= -EIO;\
+ }\
+ mutex_unlock(&data->update_lock); \
+ return count; \
+}
+
+
+set_fan_para(fan1_min, AMC6821_REG_TACH_LLIMITL);
+set_fan_para(fan1_max, AMC6821_REG_TACH_HLIMITL);
+
+
+static ssize_t get_fan1_div(struct device *dev, struct device_attribute *devattr,char *buf){
+ struct amc6821_data *data = amc6821_update_device(dev);
+
+ return sprintf(buf, "%d\n", data->fan1_div);
+}
+
+static ssize_t set_fan1_div(struct device *dev, struct device_attribute *attr, const char *buf, size_t count){
+ struct i2c_client *client = to_i2c_client(dev);
+ struct amc6821_data *data = i2c_get_clientdata(client);
+ int val = simple_strtol(buf, NULL, 10);
+ int config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF4);
+
+ if (config < 0) {
+ dev_err(&client->dev, "Error reading configuration register, aborting.\n");
+ return -EIO;
+ }
+ mutex_lock(&data->update_lock);
+ switch (val){
+ case 2:
+ config &= ~AMC6821_CONF4_PSPR;
+ data->fan1_div=2;
+ break;
+ case 4:
+ config |= AMC6821_CONF4_PSPR;
+ data->fan1_div=4;
+ break;
+ default:
+ mutex_unlock(&data->update_lock);
+ return -EINVAL;
+ }
+ if (i2c_smbus_write_byte_data(client, AMC6821_REG_CONF4, config)) {
+ dev_err(&client->dev, "Configuration register write error, aborting.\n");
+ count= -EIO;
+ }
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, get_temp1_input, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO | S_IWUSR, get_temp1_min, set_temp1_min, 0);
+static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, get_temp1_max, set_temp1_max, 0);
+static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO | S_IWUSR, get_temp1_crit, set_temp1_crit, 0);
+static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, get_temp1_min_alarm, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, get_temp1_max_alarm, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, get_temp1_crit_alarm, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO | S_IWUSR, get_temp2_input, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_min, S_IRUGO | S_IWUSR, get_temp2_min, set_temp2_min, 0);
+static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO | S_IWUSR, get_temp2_max, set_temp2_max, 0);
+static SENSOR_DEVICE_ATTR(temp2_crit, S_IRUGO | S_IWUSR, get_temp2_crit, set_temp2_crit, 0);
+static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, get_temp2_fault, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, get_temp2_min_alarm, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, get_temp2_max_alarm, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, get_temp2_crit_alarm, NULL, 0);
+static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, get_fan1_input, NULL, 0);
+static SENSOR_DEVICE_ATTR(fan1_min, S_IRUGO | S_IWUSR, get_fan1_min, set_fan1_min, 0);
+static SENSOR_DEVICE_ATTR(fan1_max, S_IRUGO | S_IWUSR, get_fan1_max, set_fan1_max, 0);
+static SENSOR_DEVICE_ATTR(fan1_fault, S_IRUGO, get_fan1_fault, NULL, 0);
+static SENSOR_DEVICE_ATTR(fan1_div, S_IRUGO | S_IWUSR, get_fan1_div, set_fan1_div, 0);
+
+static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, get_pwm1, set_pwm1,0);
+static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, get_pwm1_enable, set_pwm1_enable,0);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IRUGO, get_pwm1_auto_point_pwm, NULL,0);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point2_pwm, S_IWUSR | S_IRUGO, get_pwm1_auto_point_pwm, set_pwm1_auto_point_pwm,1);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point3_pwm, S_IRUGO, get_pwm1_auto_point_pwm, NULL,2);
+static SENSOR_DEVICE_ATTR(pwm1_auto_channels_temp, S_IRUGO, get_pwm1_auto_channels_temp, NULL,0);
+static SENSOR_DEVICE_ATTR(temp1_auto_point1_temp, S_IRUGO, get_temp1_auto_point_temp, NULL,0);
+static SENSOR_DEVICE_ATTR(temp1_auto_point2_temp, S_IWUSR | S_IRUGO, get_temp1_auto_point_temp, set_temp1_auto_point_temp,1);
+static SENSOR_DEVICE_ATTR(temp1_auto_point3_temp, S_IWUSR | S_IRUGO, get_temp1_auto_point_temp, set_temp1_auto_point_temp,2);
+
+static SENSOR_DEVICE_ATTR(temp2_auto_point1_temp, S_IWUSR | S_IRUGO, get_temp2_auto_point_temp, set_temp2_auto_point_temp,0);
+static SENSOR_DEVICE_ATTR(temp2_auto_point2_temp, S_IWUSR | S_IRUGO, get_temp2_auto_point_temp, set_temp2_auto_point_temp,1);
+static SENSOR_DEVICE_ATTR(temp2_auto_point3_temp, S_IWUSR | S_IRUGO, get_temp2_auto_point_temp, set_temp2_auto_point_temp,2);
+
+
+
+static struct attribute *amc6821_attrs[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit.dev_attr.attr,
+ &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit.dev_attr.attr,
+ &sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_fault.dev_attr.attr,
+ &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_fault.dev_attr.attr,
+ &sensor_dev_attr_fan1_div.dev_attr.attr,
+ &sensor_dev_attr_pwm1.dev_attr.attr,
+ &sensor_dev_attr_pwm1_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_channels_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point3_pwm.dev_attr.attr,
+ &sensor_dev_attr_temp1_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_temp1_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_temp1_auto_point3_temp.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_point3_temp.dev_attr.attr,
+ NULL
+};
+
+static struct attribute_group amc6821_attr_grp = {
+ .attrs = amc6821_attrs,
+};
+
+
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int amc6821_detect(struct i2c_client *client, int kind,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ int address = client->addr;
+
+ dev_dbg(&adapter->dev, "amc6821_detect called, kind = %d\n", kind);
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_dbg(&adapter->dev, "max6650: I2C bus doesn't support byte read mode, skipping.\n");
+ return -ENODEV;
+ }
+ if ((kind < 0) &&
+ ((i2c_smbus_read_byte_data(client, AMC6821_REG_DEV_ID) & 0xDE) ||
+ (i2c_smbus_read_byte_data(client, AMC6821_REG_COMP_ID) & 0xB6))){
+ dev_dbg(&adapter->dev,"amc6821: detection failed at 0x%02x.\n", address);
+ return -ENODEV;
+ }
+
+ dev_info(&adapter->dev, "amc6821: chip found at 0x%02x.\n", address);
+ strlcpy(info->type, "amc6821", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static int amc6821_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct amc6821_data *data;
+ int err;
+
+ if (!(data = kzalloc(sizeof(struct amc6821_data), GFP_KERNEL))) {
+ dev_err(&client->dev, "out of memory.\n");
+ return -ENOMEM;
+ }
+
+
+ i2c_set_clientdata(client, data);
+ mutex_init(&data->update_lock);
+
+ /*
+ * Initialize the amc6821 chip
+ */
+ err = amc6821_init_client(client);
+ if (err)
+ goto err_free;
+
+ err = sysfs_create_group(&client->dev.kobj, &amc6821_attr_grp);
+ if (err)
+ goto err_free;
+
+ 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, &amc6821_attr_grp);
+err_free:
+ kfree(data);
+ return err;
+}
+
+static int amc6821_remove(struct i2c_client *client)
+{
+ struct amc6821_data *data = i2c_get_clientdata(client);
+
+ hwmon_device_unregister(data->hwmon_dev);
+ sysfs_remove_group(&client->dev.kobj, &amc6821_attr_grp);
+
+ kfree(data);
+
+ return 0;
+}
+
+
+static int amc6821_init_client(struct i2c_client *client)
+{
+ int config;
+ int err = -EIO;
+
+ if( init ){
+ config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF4);
+
+ if (config < 0) {
+ dev_err(&client->dev, "Error reading configuration register, aborting.\n");
+ return err;
+ }
+
+ config |= AMC6821_CONF4_MODE;
+
+ if (i2c_smbus_write_byte_data(client, AMC6821_REG_CONF4, config)) {
+ dev_err(&client->dev, "Configuration register write error, aborting.\n");
+ return err;
+ }
+
+ config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF3);
+
+ if (config < 0) {
+ dev_err(&client->dev, "Error reading configuration register, aborting.\n");
+ return err;
+ }
+
+ config &= ~AMC6821_CONF3_THERM_FAN_EN;
+
+ if (i2c_smbus_write_byte_data(client, AMC6821_REG_CONF3, config)) {
+ dev_err(&client->dev, "Configuration register write error, aborting.\n");
+ return err;
+ }
+
+ config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF2);
+
+ if (config < 0) {
+ dev_err(&client->dev, "Error reading configuration register, aborting.\n");
+ return err;
+ }
+
+ config &= ~AMC6821_CONF2_RTFIE;
+ config &= ~AMC6821_CONF2_LTOIE;
+ config &= ~AMC6821_CONF2_RTOIE;
+ if (i2c_smbus_write_byte_data(client, AMC6821_REG_CONF2, config)) {
+ dev_err(&client->dev, "Configuration register write error, aborting.\n");
+ return err;
+ }
+
+ config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF1);
+
+ if (config < 0) {
+ dev_err(&client->dev, "Error reading configuration register, aborting.\n");
+ return err;
+ }
+
+ config &= ~AMC6821_CONF1_THERMOVIE;
+ config &= ~AMC6821_CONF1_FANIE;
+ config |= AMC6821_CONF1_START;
+ if (pwminv) {
+ config |= AMC6821_CONF1_PWMINV;
+ } else {
+ config &= ~AMC6821_CONF1_PWMINV;
+ }
+
+ if (i2c_smbus_write_byte_data(client, AMC6821_REG_CONF1, config)) {
+ dev_err(&client->dev, "Configuration register write error, aborting.\n");
+ return err;
+ }
+ }
+ return 0;
+}
+
+
+static struct amc6821_data *amc6821_update_device(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct amc6821_data *data = i2c_get_clientdata(client);
+ int timeout = HZ;
+ u8 reg;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + timeout) || !data->valid) {
+ data->temp1_input = i2c_smbus_read_byte_data(client, AMC6821_REG_LTEMP_HI);
+ data->temp1_min = i2c_smbus_read_byte_data(client, AMC6821_REG_LTEMP_LIMIT_MIN);
+ data->temp1_max = i2c_smbus_read_byte_data(client, AMC6821_REG_LTEMP_LIMIT_MAX);
+ data->temp1_crit = i2c_smbus_read_byte_data(client, AMC6821_REG_LTEMP_CRIT);
+
+ data->temp2_input = i2c_smbus_read_byte_data(client, AMC6821_REG_RTEMP_HI);
+ data->temp2_min = i2c_smbus_read_byte_data(client, AMC6821_REG_RTEMP_LIMIT_MIN);
+ data->temp2_max = i2c_smbus_read_byte_data(client, AMC6821_REG_RTEMP_LIMIT_MAX);
+ data->temp2_crit = i2c_smbus_read_byte_data(client, AMC6821_REG_RTEMP_CRIT);
+
+ data->stat1 = i2c_smbus_read_byte_data(client,AMC6821_REG_STAT1);
+ data->stat2 = i2c_smbus_read_byte_data(client,AMC6821_REG_STAT2);
+
+ data->pwm1 = i2c_smbus_read_byte_data(client,AMC6821_REG_DCY);
+ data->fan1_input = i2c_smbus_read_byte_data(client,AMC6821_REG_TDATA_LOW);
+ data->fan1_input += i2c_smbus_read_byte_data(client,AMC6821_REG_TDATA_HI) << 8;
+ data->fan1_min = i2c_smbus_read_byte_data(client,AMC6821_REG_TACH_LLIMITL);
+ data->fan1_min += i2c_smbus_read_byte_data(client,AMC6821_REG_TACH_LLIMITL+1) << 8;
+ data->fan1_max = i2c_smbus_read_byte_data(client,AMC6821_REG_TACH_HLIMITL);
+ data->fan1_max += i2c_smbus_read_byte_data(client,AMC6821_REG_TACH_HLIMITL+1) << 8;
+ data->fan1_div = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF4);
+ data->fan1_div = data->fan1_div & AMC6821_CONF4_PSPR ? 4 :2;
+
+ data->pwm1_auto_point_pwm[0] = 0;
+ data->pwm1_auto_point_pwm[2] = 255;
+ data->pwm1_auto_point_pwm[1] = i2c_smbus_read_byte_data(client, AMC6821_REG_DCY_LOW_TEMP);
+
+ data->temp2_auto_point_temp[0] = data->temp1_auto_point_temp[0]=i2c_smbus_read_byte_data(client, AMC6821_REG_PSV_TEMP);
+ reg = i2c_smbus_read_byte_data(client, AMC6821_REG_LTEMP_FAN_CTRL);
+ data->temp1_auto_point_temp[1] = (reg & 0xF8) >> 1;
+ reg &= 0x07;
+ reg=0x20 >> reg;
+ if(reg>0)
+ data->temp1_auto_point_temp[2] = data->temp1_auto_point_temp[1] + (data->pwm1_auto_point_pwm[2]-data->pwm1_auto_point_pwm[1])/reg;
+ else
+ data->temp1_auto_point_temp[2] = 255;
+
+ reg = i2c_smbus_read_byte_data(client, AMC6821_REG_RTEMP_FAN_CTRL);
+ data->temp2_auto_point_temp[1] = (reg & 0xF8) >> 1;
+ reg &= 0x07;
+ reg=0x20 >> reg;
+ if(reg>0)
+ data->temp2_auto_point_temp[2] = data->temp2_auto_point_temp[1] + (data->pwm1_auto_point_pwm[2]-data->pwm1_auto_point_pwm[1])/reg;
+ else
+ data->temp1_auto_point_temp[2] = 255;
+
+ reg = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF1);
+ reg=(reg >> 5) & 0x3;
+ switch(reg) {
+ case 0:
+ data->pwm1_auto_channels_temp=0;
+ data->pwm1_enable=1;
+ break;
+ case 2:
+ data->pwm1_auto_channels_temp=2;
+ data->pwm1_enable=2;
+ break;
+ case 3:
+ data->pwm1_auto_channels_temp=3;
+ data->pwm1_enable=3;
+ break;
+ case 1:
+ data->pwm1_auto_channels_temp=0;
+ data->pwm1_enable=0;
+ break;
+ }
+
+ data->last_updated = jiffies;
+ data->valid = 1;
+ }
+ mutex_unlock(&data->update_lock);
+ return data;
+}
+
+
+static int __init amc6821_init(void)
+{
+ return i2c_add_driver(&amc6821_driver);
+}
+
+static void __exit amc6821_exit(void)
+{
+ i2c_del_driver(&amc6821_driver);
+}
+
+module_init(amc6821_init);
+module_exit(amc6821_exit);
+
+
+MODULE_LICENSE("GPL");
+
+MODULE_AUTHOR("T. Mertelj <tomaz.mertelj@guest.arnes.si>");
+MODULE_DESCRIPTION("TI amc6821 hwmon driver");
+MODULE_SUPPORTED_DEVICE("amc6821");
^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [lm-sensors] [PATCH] hwmon: Driver for Texas Instruments amc6821 chip
2009-08-31 20:24 [PATCH] hwmon: Driver for Texas Instruments amc6821 chip tomaz.mertelj
@ 2009-09-01 17:56 ` Andre Prendel
2009-09-02 8:20 ` Tomaz Mertelj
2009-09-02 8:45 ` corentin.labbe
2009-09-02 23:55 ` Andrew Morton
2 siblings, 1 reply; 15+ messages in thread
From: Andre Prendel @ 2009-09-01 17:56 UTC (permalink / raw)
To: tomaz.mertelj; +Cc: lm-sensors, linux-kernel
On Mon, Aug 31, 2009 at 10:24:50PM +0200, tomaz.mertelj@guest.arnes.si wrote:
Hello Tomaz,
> I have crafted a hwmon driver for Texas Instruments amc6821 SMB-bus 2-
> channel temperature sensor, pwm controller. This is my first linux project
> so some corrections will be necessary.
First thing, please send the patch inline attached that one can
comment the source code.
> T. Mertelj
>
Regards,
Andre
> _______________________________________________
> lm-sensors mailing list
> lm-sensors@lm-sensors.org
> http://lists.lm-sensors.org/mailman/listinfo/lm-sensors
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [lm-sensors] [PATCH] hwmon: Driver for Texas Instruments amc6821 chip
2009-09-01 17:56 ` [lm-sensors] " Andre Prendel
@ 2009-09-02 8:20 ` Tomaz Mertelj
0 siblings, 0 replies; 15+ messages in thread
From: Tomaz Mertelj @ 2009-09-02 8:20 UTC (permalink / raw)
To: Andre Prendel; +Cc: lm-sensors, linux-kernel
At 19:56 1. 9. 2009 +0200, you wrote:
>First thing, please send the patch inline attached that one can
>comment the source code.
Sorry, it was filtered out due to a wrong attchment format.
Tomaz
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 2d50166..d231a28 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -777,6 +777,16 @@ config SENSORS_ADS7828
This driver can also be built as a module. If so, the module
will be called ads7828.
+config SENSORS_AMC6821
+ tristate "Texas Instruments AMC6821"
+ depends on I2C
+ help
+ If you say yes here you get support for the Texas Instruments
+ AMC6821 hardware monitoring chips.
+
+ This driver can also be build as a module. If so, the module
+ will be called amc6821.
+
config SENSORS_THMC50
tristate "Texas Instruments THMC50 / Analog Devices ADM1022"
depends on I2C && EXPERIMENTAL
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index b793dce..431de76 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -81,6 +81,7 @@ obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o
obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o
obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o
obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o
+obj-$(CONFIG_SENSORS_AMC6821) += amc6821.o
obj-$(CONFIG_SENSORS_THMC50) += thmc50.o
obj-$(CONFIG_SENSORS_TMP401) += tmp401.o
obj-$(CONFIG_SENSORS_VIA686A) += via686a.o
diff --git a/drivers/hwmon/amc6821.c b/drivers/hwmon/amc6821.c
new file mode 100644
index 0000000..75dee4c
--- /dev/null
+++ b/drivers/hwmon/amc6821.c
@@ -0,0 +1,851 @@
+/*
+ amc6821.c - Part of lm_sensors, Linux kernel modules for hardware
+ monitoring
+ Copyright (C) 2009 T. Mertelj <tomaz.mertelj@guest.arnes.si>
+
+ 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/kernel.h> /* Needed for KERN_INFO */
+#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>
+
+
+/*
+ * Addresses to scan.
+ */
+
+static const unsigned short normal_i2c[ ] = {0x18, 0x19, 0x1a, 0x2c,0x2d,0x2e,
+ 0x4c,0x4d,0x4e,I2C_CLIENT_END};
+
+
+
+/*
+ * Insmod parameters
+ */
+
+static int pwminv = 0; /*Inverted PWM output. */
+module_param(pwminv, int, S_IRUGO);
+
+static int init = 1; /*Power-on initialization.*/
+module_param(init, int, S_IRUGO);
+
+
+I2C_CLIENT_INSMOD_1(amc6821);
+
+#define AMC6821_REG_DEV_ID 0x3D
+#define AMC6821_REG_COMP_ID 0x3E
+#define AMC6821_REG_CONF1 0x00
+#define AMC6821_REG_CONF2 0x01
+#define AMC6821_REG_CONF3 0x3F
+#define AMC6821_REG_CONF4 0x04
+#define AMC6821_REG_STAT1 0x02
+#define AMC6821_REG_STAT2 0x03
+#define AMC6821_REG_TDATA_LOW 0x08
+#define AMC6821_REG_TDATA_HI 0x09
+#define AMC6821_REG_LTEMP_HI 0x0A
+#define AMC6821_REG_RTEMP_HI 0x0B
+#define AMC6821_REG_LTEMP_LIMIT_MIN 0x15
+#define AMC6821_REG_LTEMP_LIMIT_MAX 0x14
+#define AMC6821_REG_RTEMP_LIMIT_MIN 0x19
+#define AMC6821_REG_RTEMP_LIMIT_MAX 0x18
+#define AMC6821_REG_LTEMP_CRIT 0x1B
+#define AMC6821_REG_RTEMP_CRIT 0x1D
+#define AMC6821_REG_PSV_TEMP 0x1C
+#define AMC6821_REG_DCY 0x22
+#define AMC6821_REG_LTEMP_FAN_CTRL 0x24
+#define AMC6821_REG_RTEMP_FAN_CTRL 0x25
+#define AMC6821_REG_DCY_LOW_TEMP 0x21
+
+#define AMC6821_REG_TACH_LLIMITL 0x10
+#define AMC6821_REG_TACH_LLIMITH 0x11
+#define AMC6821_REG_TACH_HLIMITL 0x12
+#define AMC6821_REG_TACH_HLIMITH 0x13
+
+#define AMC6821_CONF1_START 0x01
+#define AMC6821_CONF1_FAN_INT_EN 0x02
+#define AMC6821_CONF1_FANIE 0x04
+#define AMC6821_CONF1_PWMINV 0x08
+#define AMC6821_CONF1_FAN_FAULT_EN 0x10
+#define AMC6821_CONF1_FDRC0 0x20
+#define AMC6821_CONF1_FDRC1 0x40
+#define AMC6821_CONF1_THERMOVIE 0x80
+
+#define AMC6821_CONF2_PWM_EN 0x01
+#define AMC6821_CONF2_TACH_MODE 0x02
+#define AMC6821_CONF2_TACH_EN 0x04
+#define AMC6821_CONF2_RTFIE 0x08
+#define AMC6821_CONF2_LTOIE 0x10
+#define AMC6821_CONF2_RTOIE 0x20
+#define AMC6821_CONF2_PSVIE 0x40
+#define AMC6821_CONF2_RST 0x80
+
+#define AMC6821_CONF3_THERM_FAN_EN 0x80
+#define AMC6821_CONF3_REV_MASK 0x0F
+
+#define AMC6821_CONF4_OVREN 0x10
+#define AMC6821_CONF4_TACH_FAST 0x20
+#define AMC6821_CONF4_PSPR 0x40
+#define AMC6821_CONF4_MODE 0x80
+
+#define AMC6821_STAT1_RPM_ALARM 0x01
+#define AMC6821_STAT1_FANS 0x02
+#define AMC6821_STAT1_RTH 0x04
+#define AMC6821_STAT1_RTL 0x08
+#define AMC6821_STAT1_R_THERM 0x10
+#define AMC6821_STAT1_RTF 0x20
+#define AMC6821_STAT1_LTH 0x40
+#define AMC6821_STAT1_LTL 0x80
+
+#define AMC6821_STAT2_RTC 0x08
+#define AMC6821_STAT2_LTC 0x10
+#define AMC6821_STAT2_LPSV 0x20
+#define AMC6821_STAT2_L_THERM 0x40
+#define AMC6821_STAT2_THERM_IN 0x80
+
+
+static int amc6821_probe(struct i2c_client *client,const struct
i2c_device_id *id);
+static int amc6821_detect(struct i2c_client *client, int kind,struct
i2c_board_info *info);
+static int amc6821_init_client(struct i2c_client *client);
+static int amc6821_remove(struct i2c_client *client);
+static struct amc6821_data *amc6821_update_device(struct device *dev);
+
+/*
+ * Driver data (common to all clients)
+ */
+
+static const struct i2c_device_id amc6821_id[] = {
+ { "amc6821", amc6821 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, amc6821_id);
+
+
+static struct i2c_driver amc6821_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "amc6821",
+ },
+ .probe = amc6821_probe,
+ .remove = amc6821_remove,
+ .id_table = amc6821_id,
+ .detect = amc6821_detect,
+ .address_data = &addr_data,
+};
+
+
+/*
+ * Client data (each client gets its own)
+ */
+
+struct amc6821_data
+{
+ struct device *hwmon_dev;
+ struct mutex update_lock;
+ char valid; /* zero until following fields are valid */
+ unsigned long last_updated; /* in jiffies */
+
+ /* register values */
+ int temp1_input;
+ int temp1_min;
+ int temp1_max;
+ int temp1_crit;
+
+ u8 temp1_alarm;
+ int temp2_input;
+ int temp2_min;
+ int temp2_max;
+ int temp2_crit;
+ u8 temp2_alarm;
+
+ u16 fan1_input;
+ u16 fan1_min;
+ u16 fan1_max;
+ u8 fan1_div;
+
+ u8 pwm1;
+ u8 temp1_auto_point_temp[3];
+ u8 temp2_auto_point_temp[3];
+ u8 pwm1_auto_point_pwm[3];
+ u8 pwm1_enable;
+ u8 pwm1_auto_channels_temp;
+
+ u8 stat1;
+ u8 stat2;
+};
+
+
+#define get_temp_para(name) \
+static ssize_t get_##name(struct device *dev, struct device_attribute
*devattr,char *buf){ \
+ struct amc6821_data *data = amc6821_update_device(dev);\
+ return sprintf(buf, "%d\n", data->name * 1000);\
+}
+
+get_temp_para(temp1_input);
+get_temp_para(temp1_min);
+get_temp_para(temp1_max);
+get_temp_para(temp2_input);
+get_temp_para(temp2_min);
+get_temp_para(temp2_max);
+get_temp_para(temp1_crit);
+get_temp_para(temp2_crit);
+
+#define set_temp_para(name,reg)\
+static ssize_t set_##name(struct device *dev, struct device_attribute
*attr, const char *buf, size_t count) \
+{ \
+ struct i2c_client *client = to_i2c_client(dev); \
+ struct amc6821_data *data = i2c_get_clientdata(client); \
+ int val = simple_strtol(buf, NULL, 10); \
+ \
+ mutex_lock(&data->update_lock); \
+ data->name = SENSORS_LIMIT(val / 1000, -128, 127); \
+ if (i2c_smbus_write_byte_data(client, reg, data->name)) {\
+ dev_err(&client->dev, "Register write error, aborting.\n");\
+ count= -EIO;\
+ }\
+ mutex_unlock(&data->update_lock); \
+ return count; \
+}
+
+set_temp_para(temp1_min,AMC6821_REG_LTEMP_LIMIT_MIN);
+set_temp_para(temp1_max,AMC6821_REG_LTEMP_LIMIT_MAX);
+set_temp_para(temp2_min,AMC6821_REG_RTEMP_LIMIT_MIN);
+set_temp_para(temp2_max,AMC6821_REG_RTEMP_LIMIT_MAX);
+set_temp_para(temp1_crit,AMC6821_REG_LTEMP_CRIT);
+set_temp_para(temp2_crit,AMC6821_REG_RTEMP_CRIT);
+
+#define get_temp_alarm(name, reg, mask)\
+static ssize_t get_##name(struct device *dev, struct device_attribute
*devattr,char *buf){\
+ struct amc6821_data *data = amc6821_update_device(dev);\
+ if( data->reg & mask)\
+ return sprintf(buf, "1");\
+ else \
+ return sprintf(buf, "0");\
+}\
+
+get_temp_alarm(temp1_min_alarm, stat1, AMC6821_STAT1_LTL)
+get_temp_alarm(temp1_max_alarm, stat1, AMC6821_STAT1_LTH)
+get_temp_alarm(temp2_min_alarm, stat1, AMC6821_STAT1_RTL)
+get_temp_alarm(temp2_max_alarm, stat1, AMC6821_STAT1_RTH)
+get_temp_alarm(temp1_crit_alarm, stat2, AMC6821_STAT2_LTC)
+get_temp_alarm(temp2_crit_alarm, stat2, AMC6821_STAT2_RTC)
+
+
+static ssize_t get_temp2_fault(struct device *dev, struct device_attribute
*devattr,char *buf){
+ struct amc6821_data *data = amc6821_update_device(dev);
+ if( data->stat1 & AMC6821_STAT1_RTF)
+ return sprintf(buf, "1");
+ else
+ return sprintf(buf, "0");
+}
+
+static ssize_t get_pwm1(struct device *dev, struct device_attribute
*devattr,char *buf){
+ struct amc6821_data *data = amc6821_update_device(dev);
+ return sprintf(buf, "%d\n", data->pwm1);
+}
+
+static ssize_t set_pwm1(struct device *dev, struct device_attribute
*devattr,const char *buf, size_t count){
+ struct i2c_client *client = to_i2c_client(dev);
+ struct amc6821_data *data = i2c_get_clientdata(client);
+ int val = simple_strtol(buf, NULL, 10);
+
+ mutex_lock(&data->update_lock); \
+ data->pwm1 = SENSORS_LIMIT(val , 0, 255); \
+ i2c_smbus_write_byte_data(client, AMC6821_REG_DCY, data->pwm1); \
+ mutex_unlock(&data->update_lock); \
+ return count;
+}
+
+static ssize_t get_pwm1_enable(struct device *dev, struct device_attribute
*devattr,char *buf){
+ struct amc6821_data *data = amc6821_update_device(dev);
+ return sprintf(buf, "%d\n", data->pwm1_enable);
+}
+
+static ssize_t set_pwm1_enable(struct device *dev, struct device_attribute
*attr, const char *buf, size_t count){
+ struct i2c_client *client = to_i2c_client(dev);
+ struct amc6821_data *data = i2c_get_clientdata(client);
+ int val = simple_strtol(buf, NULL, 10);
+
+ int config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF1);
+ if (config < 0) {
+ dev_err(&client->dev, "Error reading configuration
register, aborting.\n");
+ return -EIO;
+ }
+
+ switch (val){
+ case 1:
+ config &= ~AMC6821_CONF1_FDRC0;
+ config &= ~AMC6821_CONF1_FDRC1;
+ break;
+ case 2:
+ config &= ~AMC6821_CONF1_FDRC0;
+ config |= AMC6821_CONF1_FDRC1;
+ break;
+ case 3:
+ config |= AMC6821_CONF1_FDRC0;
+ config |= AMC6821_CONF1_FDRC1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ mutex_lock(&data->update_lock);
+ if (i2c_smbus_write_byte_data(client, AMC6821_REG_CONF1, config)) {
+ dev_err(&client->dev, "Configuration register write
error, aborting.\n");
+ count= -EIO;
+ }
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+
+static ssize_t get_pwm1_auto_channels_temp(struct device *dev, struct
device_attribute *devattr,char *buf){
+ struct amc6821_data *data = amc6821_update_device(dev);
+ return sprintf(buf, "%d\n", data->pwm1_auto_channels_temp);
+}
+
+
+#define get_auto_point(name)\
+static ssize_t get_##name(struct device *dev, struct device_attribute
*devattr,char *buf){\
+ int nr = to_sensor_dev_attr(devattr)->index;\
+ struct amc6821_data *data = amc6821_update_device(dev);\
+ return sprintf(buf, "%d\n", data->name[nr] *1000);\
+}
+
+get_auto_point(temp1_auto_point_temp);
+get_auto_point(temp2_auto_point_temp);
+
+static ssize_t get_pwm1_auto_point_pwm(struct device *dev, struct
device_attribute *devattr,char *buf){
+ int nr = to_sensor_dev_attr(devattr)->index;
+ struct amc6821_data *data = amc6821_update_device(dev);
+ return sprintf(buf, "%d\n", data->pwm1_auto_point_pwm[nr] );
+}
+
+
+#define set_temp_auto_point_temp(name,reg)\
+static ssize_t set_##name(struct device *dev, struct device_attribute
*attr, const char *buf, size_t count) \
+{ \
+ struct i2c_client *client = to_i2c_client(dev); \
+ struct amc6821_data *data = amc6821_update_device(dev);\
+ int nr = to_sensor_dev_attr(attr)->index;\
+ int val = simple_strtol(buf, NULL, 10); \
+ u8 tmp;\
+ \
+ mutex_lock(&data->update_lock); \
+ switch(nr){\
+ case 0:\
+ data->name[0] = SENSORS_LIMIT(val / 1000,
0,data->temp1_auto_point_temp[1]-1);\
+ data->name[0] = SENSORS_LIMIT(data->name[0],
0,data->temp2_auto_point_temp[1]-1);\
+ data->name[0] = SENSORS_LIMIT(data->name[0], 0, 63); \
+ data->valid = 0;\
+ if (i2c_smbus_write_byte_data(client,
AMC6821_REG_PSV_TEMP, data->name[ 0] )) {\
+ dev_err(&client->dev, "Register
write error, aborting.\n");\
+ count = -EIO;\
+ }\
+ goto EXIT;\
+ break;\
+ case 1:\
+ data->name[1] = SENSORS_LIMIT(val / 1000,
data->name[0]+1, data->name[ 2]-1); \
+ data->name[1] &= 0x7C;\
+ break;\
+ case 2:\
+ data->name[2] = SENSORS_LIMIT(val / 1000,
data->name[1]+1, 127); \
+ break;\
+ }\
+ val =
(16*(data->pwm1_auto_point_pwm[2]-data->pwm1_auto_point_pwm[1]))/(data->name[2]-data->name[1]);\
+ for(tmp = 4; tmp >= 0; tmp--){\
+ if( val <= (0x200 >> tmp)) break;\
+ }\
+ tmp |= ((u8) data->name[ 1] & 0x7C) << 1;\
+ if (i2c_smbus_write_byte_data(client, reg, tmp)) {\
+ dev_err(&client->dev, "Register write error, aborting.\n");\
+ count = -EIO;\
+ }\
+ data->valid = 0;\
+EXIT:\
+ mutex_unlock(&data->update_lock); \
+ return count; \
+}
+
+set_temp_auto_point_temp(temp1_auto_point_temp, AMC6821_REG_LTEMP_FAN_CTRL);
+set_temp_auto_point_temp(temp2_auto_point_temp, AMC6821_REG_RTEMP_FAN_CTRL);
+
+static ssize_t set_pwm1_auto_point_pwm(struct device *dev, struct
device_attribute *attr, const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct amc6821_data *data = i2c_get_clientdata(client);
+ int val = simple_strtol(buf, NULL, 10);
+ u8 tmp;
+
+ mutex_lock(&data->update_lock);
+ data->pwm1_auto_point_pwm[ 1] = SENSORS_LIMIT(val, 0, 255);
+ if (i2c_smbus_write_byte_data(client, AMC6821_REG_DCY_LOW_TEMP,
data->pwm1_auto_point_pwm[ 1])) {\
+ dev_err(&client->dev, "Register write error, aborting.\n");
+ count = -EIO;
+ }
+
+ val =(16 *
(data->pwm1_auto_point_pwm[2]-data->pwm1_auto_point_pwm[1]))/(data->temp1_auto_point_temp[2]-data->temp1_auto_point_temp[1]);
+ for(tmp = 4; tmp >= 0; tmp--){
+ if( val <= (0x200 >> tmp)) break;
+ }
+ tmp |= ((u8) data->temp1_auto_point_temp[ 1] & 0x7C) << 1;
+ if (i2c_smbus_write_byte_data(client, AMC6821_REG_LTEMP_FAN_CTRL,
tmp)) {
+ dev_err(&client->dev, "Register write error, aborting.\n");
+ count = -EIO;
+ }
+ data->valid = 0;
+
+ val = (16 *
(data->pwm1_auto_point_pwm[2]-data->pwm1_auto_point_pwm[1]))/(data->temp2_auto_point_temp[2]-data->temp2_auto_point_temp[1]);
+ for(tmp = 4; tmp >= 0; tmp--){
+ if( val <= (0x200 >> tmp)) break;
+ }
+ tmp |= ((u8) data->temp2_auto_point_temp[ 1] & 0x7C) << 1;
+ if (i2c_smbus_write_byte_data(client, AMC6821_REG_RTEMP_FAN_CTRL,
tmp)) {
+ dev_err(&client->dev, "Register write error, aborting.\n");
+ count = -EIO;
+ }
+ data->valid = 0;
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+
+#define get_fan_para(name) static ssize_t get_##name(struct device *dev,
struct device_attribute *devattr,char *buf){ \
+ struct amc6821_data *data = amc6821_update_device(dev); \
+ if ( 0 == data->name) return sprintf(buf,"0");\
+ return sprintf(buf, "%d\n", (int)(6000000 / data->name)); \
+}
+
+get_fan_para(fan1_input);
+get_fan_para(fan1_min);
+get_fan_para(fan1_max);
+
+static ssize_t get_fan1_fault(struct device *dev, struct device_attribute
*devattr,char *buf){
+ struct amc6821_data *data = amc6821_update_device(dev);
+ if( data->stat1 & AMC6821_STAT1_FANS)
+ return sprintf(buf, "1");
+ else
+ return sprintf(buf, "0");
+}
+
+
+#define set_fan_para(name, reg) \
+static ssize_t set_##name(struct device *dev, struct device_attribute
*attr, const char *buf, size_t count){ \
+ struct i2c_client *client = to_i2c_client(dev); \
+ struct amc6821_data *data = i2c_get_clientdata(client); \
+ int val = simple_strtol(buf, NULL, 10); \
+ val = 1>val ? 0xFFFF : 6000000/val; \
+\
+ mutex_lock(&data->update_lock); \
+ data->name = (u16) SENSORS_LIMIT(val, 1, 0xFFFF); \
+ if (i2c_smbus_write_byte_data(client, reg, data->name & 0xFF)) { \
+ dev_err(&client->dev, "Register write error, aborting.\n");\
+ count= -EIO;\
+ }\
+ if (i2c_smbus_write_byte_data(client, reg+1, data->name >> 8 )) { \
+ dev_err(&client->dev, "Register write error, aborting.\n");\
+ count= -EIO;\
+ }\
+ mutex_unlock(&data->update_lock); \
+ return count; \
+}
+
+
+set_fan_para(fan1_min, AMC6821_REG_TACH_LLIMITL);
+set_fan_para(fan1_max, AMC6821_REG_TACH_HLIMITL);
+
+
+static ssize_t get_fan1_div(struct device *dev, struct device_attribute
*devattr,char *buf){
+ struct amc6821_data *data = amc6821_update_device(dev);
+
+ return sprintf(buf, "%d\n", data->fan1_div);
+}
+
+static ssize_t set_fan1_div(struct device *dev, struct device_attribute
*attr, const char *buf, size_t count){
+ struct i2c_client *client = to_i2c_client(dev);
+ struct amc6821_data *data = i2c_get_clientdata(client);
+ int val = simple_strtol(buf, NULL, 10);
+ int config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF4);
+
+ if (config < 0) {
+ dev_err(&client->dev, "Error reading configuration
register, aborting.\n");
+ return -EIO;
+ }
+ mutex_lock(&data->update_lock);
+ switch (val){
+ case 2:
+ config &= ~AMC6821_CONF4_PSPR;
+ data->fan1_div=2;
+ break;
+ case 4:
+ config |= AMC6821_CONF4_PSPR;
+ data->fan1_div=4;
+ break;
+ default:
+ mutex_unlock(&data->update_lock);
+ return -EINVAL;
+ }
+ if (i2c_smbus_write_byte_data(client, AMC6821_REG_CONF4, config)) {
+ dev_err(&client->dev, "Configuration register write error,
aborting.\n");
+ count= -EIO;
+ }
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, get_temp1_input, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO | S_IWUSR, get_temp1_min,
set_temp1_min, 0);
+static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, get_temp1_max,
set_temp1_max, 0);
+static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO | S_IWUSR, get_temp1_crit,
set_temp1_crit, 0);
+static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, get_temp1_min_alarm,
NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, get_temp1_max_alarm,
NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, get_temp1_crit_alarm,
NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO | S_IWUSR, get_temp2_input,
NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_min, S_IRUGO | S_IWUSR, get_temp2_min,
set_temp2_min, 0);
+static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO | S_IWUSR, get_temp2_max,
set_temp2_max, 0);
+static SENSOR_DEVICE_ATTR(temp2_crit, S_IRUGO | S_IWUSR, get_temp2_crit,
set_temp2_crit, 0);
+static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, get_temp2_fault, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, get_temp2_min_alarm,
NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, get_temp2_max_alarm,
NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, get_temp2_crit_alarm,
NULL, 0);
+static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, get_fan1_input, NULL, 0);
+static SENSOR_DEVICE_ATTR(fan1_min, S_IRUGO | S_IWUSR, get_fan1_min,
set_fan1_min, 0);
+static SENSOR_DEVICE_ATTR(fan1_max, S_IRUGO | S_IWUSR, get_fan1_max,
set_fan1_max, 0);
+static SENSOR_DEVICE_ATTR(fan1_fault, S_IRUGO, get_fan1_fault, NULL, 0);
+static SENSOR_DEVICE_ATTR(fan1_div, S_IRUGO | S_IWUSR, get_fan1_div,
set_fan1_div, 0);
+
+static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, get_pwm1, set_pwm1,0);
+static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, get_pwm1_enable,
set_pwm1_enable,0);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IRUGO,
get_pwm1_auto_point_pwm, NULL,0);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point2_pwm, S_IWUSR | S_IRUGO,
get_pwm1_auto_point_pwm, set_pwm1_auto_point_pwm,1);
+static SENSOR_DEVICE_ATTR(pwm1_auto_point3_pwm, S_IRUGO,
get_pwm1_auto_point_pwm, NULL,2);
+static SENSOR_DEVICE_ATTR(pwm1_auto_channels_temp, S_IRUGO,
get_pwm1_auto_channels_temp, NULL,0);
+static SENSOR_DEVICE_ATTR(temp1_auto_point1_temp, S_IRUGO,
get_temp1_auto_point_temp, NULL,0);
+static SENSOR_DEVICE_ATTR(temp1_auto_point2_temp, S_IWUSR | S_IRUGO,
get_temp1_auto_point_temp, set_temp1_auto_point_temp,1);
+static SENSOR_DEVICE_ATTR(temp1_auto_point3_temp, S_IWUSR | S_IRUGO,
get_temp1_auto_point_temp, set_temp1_auto_point_temp,2);
+
+static SENSOR_DEVICE_ATTR(temp2_auto_point1_temp, S_IWUSR | S_IRUGO,
get_temp2_auto_point_temp, set_temp2_auto_point_temp,0);
+static SENSOR_DEVICE_ATTR(temp2_auto_point2_temp, S_IWUSR | S_IRUGO,
get_temp2_auto_point_temp, set_temp2_auto_point_temp,1);
+static SENSOR_DEVICE_ATTR(temp2_auto_point3_temp, S_IWUSR | S_IRUGO,
get_temp2_auto_point_temp, set_temp2_auto_point_temp,2);
+
+
+
+static struct attribute *amc6821_attrs[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit.dev_attr.attr,
+ &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit.dev_attr.attr,
+ &sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_fault.dev_attr.attr,
+ &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_fault.dev_attr.attr,
+ &sensor_dev_attr_fan1_div.dev_attr.attr,
+ &sensor_dev_attr_pwm1.dev_attr.attr,
+ &sensor_dev_attr_pwm1_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_channels_temp.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr,
+ &sensor_dev_attr_pwm1_auto_point3_pwm.dev_attr.attr,
+ &sensor_dev_attr_temp1_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_temp1_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_temp1_auto_point3_temp.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_point3_temp.dev_attr.attr,
+ NULL
+};
+
+static struct attribute_group amc6821_attr_grp = {
+ .attrs = amc6821_attrs,
+};
+
+
+
+/* Return 0 if detection is successful, -ENODEV otherwise */
+static int amc6821_detect(struct i2c_client *client, int kind,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+ int address = client->addr;
+
+ dev_dbg(&adapter->dev, "amc6821_detect called, kind = %d\n", kind);
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_dbg(&adapter->dev, "max6650: I2C bus doesn't support
byte read mode, skipping.\n");
+ return -ENODEV;
+ }
+ if ((kind < 0) &&
+ ((i2c_smbus_read_byte_data(client, AMC6821_REG_DEV_ID) &
0xDE) ||
+ (i2c_smbus_read_byte_data(client, AMC6821_REG_COMP_ID) &
0xB6))){
+ dev_dbg(&adapter->dev,"amc6821: detection failed at
0x%02x.\n", address);
+ return -ENODEV;
+ }
+
+ dev_info(&adapter->dev, "amc6821: chip found at 0x%02x.\n", address);
+ strlcpy(info->type, "amc6821", I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static int amc6821_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct amc6821_data *data;
+ int err;
+
+ if (!(data = kzalloc(sizeof(struct amc6821_data), GFP_KERNEL))) {
+ dev_err(&client->dev, "out of memory.\n");
+ return -ENOMEM;
+ }
+
+
+ i2c_set_clientdata(client, data);
+ mutex_init(&data->update_lock);
+
+ /*
+ * Initialize the amc6821 chip
+ */
+ err = amc6821_init_client(client);
+ if (err)
+ goto err_free;
+
+ err = sysfs_create_group(&client->dev.kobj, &amc6821_attr_grp);
+ if (err)
+ goto err_free;
+
+ 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, &amc6821_attr_grp);
+err_free:
+ kfree(data);
+ return err;
+}
+
+static int amc6821_remove(struct i2c_client *client)
+{
+ struct amc6821_data *data = i2c_get_clientdata(client);
+
+ hwmon_device_unregister(data->hwmon_dev);
+ sysfs_remove_group(&client->dev.kobj, &amc6821_attr_grp);
+
+ kfree(data);
+
+ return 0;
+}
+
+
+static int amc6821_init_client(struct i2c_client *client)
+{
+ int config;
+ int err = -EIO;
+
+ if( init ){
+ config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF4);
+
+ if (config < 0) {
+ dev_err(&client->dev, "Error reading
configuration register, aborting.\n");
+ return err;
+ }
+
+ config |= AMC6821_CONF4_MODE;
+
+ if (i2c_smbus_write_byte_data(client, AMC6821_REG_CONF4,
config)) {
+ dev_err(&client->dev, "Configuration register write
error, aborting.\n");
+ return err;
+ }
+
+ config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF3);
+
+ if (config < 0) {
+ dev_err(&client->dev, "Error reading configuration
register, aborting.\n");
+ return err;
+ }
+
+ config &= ~AMC6821_CONF3_THERM_FAN_EN;
+
+ if (i2c_smbus_write_byte_data(client, AMC6821_REG_CONF3,
config)) {
+ dev_err(&client->dev, "Configuration register write
error, aborting.\n");
+ return err;
+ }
+
+ config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF2);
+
+ if (config < 0) {
+ dev_err(&client->dev, "Error reading configuration
register, aborting.\n");
+ return err;
+ }
+
+ config &= ~AMC6821_CONF2_RTFIE;
+ config &= ~AMC6821_CONF2_LTOIE;
+ config &= ~AMC6821_CONF2_RTOIE;
+ if (i2c_smbus_write_byte_data(client, AMC6821_REG_CONF2,
config)) {
+ dev_err(&client->dev, "Configuration register write
error, aborting.\n");
+ return err;
+ }
+
+ config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF1);
+
+ if (config < 0) {
+ dev_err(&client->dev, "Error reading configuration
register, aborting.\n");
+ return err;
+ }
+
+ config &= ~AMC6821_CONF1_THERMOVIE;
+ config &= ~AMC6821_CONF1_FANIE;
+ config |= AMC6821_CONF1_START;
+ if (pwminv) {
+ config |= AMC6821_CONF1_PWMINV;
+ } else {
+ config &= ~AMC6821_CONF1_PWMINV;
+ }
+
+ if (i2c_smbus_write_byte_data(client, AMC6821_REG_CONF1,
config)) {
+ dev_err(&client->dev, "Configuration register write
error, aborting.\n");
+ return err;
+ }
+ }
+ return 0;
+}
+
+
+static struct amc6821_data *amc6821_update_device(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct amc6821_data *data = i2c_get_clientdata(client);
+ int timeout = HZ;
+ u8 reg;
+
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + timeout) ||
!data->valid) {
+ data->temp1_input = i2c_smbus_read_byte_data(client,
AMC6821_REG_LTEMP_HI);
+ data->temp1_min = i2c_smbus_read_byte_data(client,
AMC6821_REG_LTEMP_LIMIT_MIN);
+ data->temp1_max = i2c_smbus_read_byte_data(client,
AMC6821_REG_LTEMP_LIMIT_MAX);
+ data->temp1_crit = i2c_smbus_read_byte_data(client,
AMC6821_REG_LTEMP_CRIT);
+
+ data->temp2_input = i2c_smbus_read_byte_data(client,
AMC6821_REG_RTEMP_HI);
+ data->temp2_min = i2c_smbus_read_byte_data(client,
AMC6821_REG_RTEMP_LIMIT_MIN);
+ data->temp2_max = i2c_smbus_read_byte_data(client,
AMC6821_REG_RTEMP_LIMIT_MAX);
+ data->temp2_crit = i2c_smbus_read_byte_data(client,
AMC6821_REG_RTEMP_CRIT);
+
+ data->stat1 =
i2c_smbus_read_byte_data(client,AMC6821_REG_STAT1);
+ data->stat2 =
i2c_smbus_read_byte_data(client,AMC6821_REG_STAT2);
+
+ data->pwm1 = i2c_smbus_read_byte_data(client,AMC6821_REG_DCY);
+ data->fan1_input =
i2c_smbus_read_byte_data(client,AMC6821_REG_TDATA_LOW);
+ data->fan1_input +=
i2c_smbus_read_byte_data(client,AMC6821_REG_TDATA_HI) << 8;
+ data->fan1_min =
i2c_smbus_read_byte_data(client,AMC6821_REG_TACH_LLIMITL);
+ data->fan1_min +=
i2c_smbus_read_byte_data(client,AMC6821_REG_TACH_LLIMITL+1) << 8;
+ data->fan1_max =
i2c_smbus_read_byte_data(client,AMC6821_REG_TACH_HLIMITL);
+ data->fan1_max +=
i2c_smbus_read_byte_data(client,AMC6821_REG_TACH_HLIMITL+1) << 8;
+ data->fan1_div = i2c_smbus_read_byte_data(client,
AMC6821_REG_CONF4);
+ data->fan1_div = data->fan1_div & AMC6821_CONF4_PSPR ? 4 :2;
+
+ data->pwm1_auto_point_pwm[0] = 0;
+ data->pwm1_auto_point_pwm[2] = 255;
+ data->pwm1_auto_point_pwm[1] =
i2c_smbus_read_byte_data(client, AMC6821_REG_DCY_LOW_TEMP);
+
+ data->temp2_auto_point_temp[0] =
data->temp1_auto_point_temp[0]=i2c_smbus_read_byte_data(client,
AMC6821_REG_PSV_TEMP);
+ reg = i2c_smbus_read_byte_data(client,
AMC6821_REG_LTEMP_FAN_CTRL);
+ data->temp1_auto_point_temp[1] = (reg & 0xF8) >> 1;
+ reg &= 0x07;
+ reg=0x20 >> reg;
+ if(reg>0)
+ data->temp1_auto_point_temp[2] =
data->temp1_auto_point_temp[1] +
(data->pwm1_auto_point_pwm[2]-data->pwm1_auto_point_pwm[1])/reg;
+ else
+ data->temp1_auto_point_temp[2] = 255;
+
+ reg = i2c_smbus_read_byte_data(client,
AMC6821_REG_RTEMP_FAN_CTRL);
+ data->temp2_auto_point_temp[1] = (reg & 0xF8) >> 1;
+ reg &= 0x07;
+ reg=0x20 >> reg;
+ if(reg>0)
+ data->temp2_auto_point_temp[2] =
data->temp2_auto_point_temp[1] +
(data->pwm1_auto_point_pwm[2]-data->pwm1_auto_point_pwm[1])/reg;
+ else
+ data->temp1_auto_point_temp[2] = 255;
+
+ reg = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF1);
+ reg=(reg >> 5) & 0x3;
+ switch(reg) {
+ case 0:
+ data->pwm1_auto_channels_temp=0;
+ data->pwm1_enable=1;
+ break;
+ case 2:
+ data->pwm1_auto_channels_temp=2;
+ data->pwm1_enable=2;
+ break;
+ case 3:
+ data->pwm1_auto_channels_temp=3;
+ data->pwm1_enable=3;
+ break;
+ case 1:
+ data->pwm1_auto_channels_temp=0;
+ data->pwm1_enable=0;
+ break;
+ }
+
+ data->last_updated = jiffies;
+ data->valid = 1;
+ }
+ mutex_unlock(&data->update_lock);
+ return data;
+}
+
+
+static int __init amc6821_init(void)
+{
+ return i2c_add_driver(&amc6821_driver);
+}
+
+static void __exit amc6821_exit(void)
+{
+ i2c_del_driver(&amc6821_driver);
+}
+
+module_init(amc6821_init);
+module_exit(amc6821_exit);
+
+
+MODULE_LICENSE("GPL");
+
+MODULE_AUTHOR("T. Mertelj <tomaz.mertelj@guest.arnes.si>");
+MODULE_DESCRIPTION("TI amc6821 hwmon driver");
+MODULE_SUPPORTED_DEVICE("amc6821");
***********************************************************************************
Tomaz Mertelj
E-mail: tomaz.mertelj@guest.arnes.si Home page:
http://optlab.ijs.si/tmertelj
Staniceva 14
1000 Ljubljana
Slovenia
***********************************************************************************
^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [lm-sensors] [PATCH] hwmon: Driver for Texas Instruments amc6821 chip
2009-08-31 20:24 [PATCH] hwmon: Driver for Texas Instruments amc6821 chip tomaz.mertelj
2009-09-01 17:56 ` [lm-sensors] " Andre Prendel
@ 2009-09-02 8:45 ` corentin.labbe
2009-09-02 23:55 ` Andrew Morton
2 siblings, 0 replies; 15+ messages in thread
From: corentin.labbe @ 2009-09-02 8:45 UTC (permalink / raw)
To: tomaz.mertelj; +Cc: lm-sensors, linux-kernel
tomaz.mertelj@guest.arnes.si a écrit :
> I have crafted a hwmon driver for Texas Instruments amc6821 SMB-bus 2-
> channel temperature sensor, pwm controller. This is my first linux project
> so some corrections will be necessary.
>
> T. Mertelj
Hello
I have some warnings
CC drivers/hwmon/amc6821.o
drivers/hwmon/amc6821.c: In function `set_temp1_auto_point_temp':
drivers/hwmon/amc6821.c:390: warning: comparison is always true due to limited range of data type
drivers/hwmon/amc6821.c: In function `set_temp2_auto_point_temp':
drivers/hwmon/amc6821.c:391: warning: comparison is always true due to limited range of data type
drivers/hwmon/amc6821.c: In function `set_pwm1_auto_point_pwm':
drivers/hwmon/amc6821.c:408: warning: comparison is always true due to limited range of data type
drivers/hwmon/amc6821.c:419: warning: comparison is always true due to limited range of data type
you use tmp as a unsigned 8 bit so it is always >= 0 (it can't be below 0)
Prefer use:
s8 tmp;
Cordially
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH] hwmon: Driver for Texas Instruments amc6821 chip
2009-08-31 20:24 [PATCH] hwmon: Driver for Texas Instruments amc6821 chip tomaz.mertelj
2009-09-01 17:56 ` [lm-sensors] " Andre Prendel
2009-09-02 8:45 ` corentin.labbe
@ 2009-09-02 23:55 ` Andrew Morton
2 siblings, 0 replies; 15+ messages in thread
From: Andrew Morton @ 2009-09-02 23:55 UTC (permalink / raw)
To: tomaz.mertelj; +Cc: lm-sensors, linux-kernel
On Mon, 31 Aug 2009 22:24:50 +0200
tomaz.mertelj@guest.arnes.si wrote:
> I have crafted a hwmon driver for Texas Instruments amc6821 SMB-bus 2-
> channel temperature sensor, pwm controller. This is my first linux project
> so some corrections will be necessary.
>
The driver looks saneish to me, but...
- The attachment uses \r\n line termination and caused me quite a bit
of trouble before I worked out what was going on.
- The changelog is missing your Signed-off-by:. Please read up on
this in Documentation/SubmittingPatches.
- The driver needs lots and lots of trivial coding-style fixes.
Please take a look at some similar drivers and also use the
scripts/checkpatch.pl tool.
checkpatch says:
total: 153 errors, 110 warnings, 874 lines checked
which must be some sort of record ;)
Also, some C functions have needless `\' line continuations in them.
All pretty simple stuff. Please take a look at all that and then resend?
Thanks.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [lm-sensors] [PATCH] hwmon: Driver for Texas Instruments amc6821 chip
2009-09-09 0:06 ` Andrew Morton
@ 2009-09-09 7:34 ` Jean Delvare
2009-09-09 8:06 ` Andrew Morton
` (2 more replies)
0 siblings, 3 replies; 15+ messages in thread
From: Jean Delvare @ 2009-09-09 7:34 UTC (permalink / raw)
To: Andrew Morton; +Cc: tomaz.mertelj, linux-kernel, lm-sensors
On Tue, 8 Sep 2009 17:06:49 -0700, Andrew Morton wrote:
> On Sat, 5 Sep 2009 14:08:34 +0200
> tomaz.mertelj@guest.arnes.si wrote:
>
> > + int temp1_input;
> > + int temp1_min;
> > + int temp1_max;
> > + int temp1_crit;
> > +
> > + int temp2_input;
> > + int temp2_min;
> > + int temp2_max;
> > + int temp2_crit;
> > +
> > + u16 fan1_input;
> > + u16 fan1_min;
> > + u16 fan1_max;
> > + u8 fan1_div;
> > +
> > + u8 pwm1;
> > + u8 temp1_auto_point_temp[3];
> > + u8 temp2_auto_point_temp[3];
> > + u8 pwm1_auto_point_pwm[3];
> > + u8 pwm1_enable;
> > + u8 pwm1_auto_channels_temp;
> > +
> > + u8 stat1;
> > + u8 stat2;
> > +};
> > +
> > +
> > +#define get_temp_para(name) \
> > +static ssize_t get_##name(\
> > + struct device *dev,\
> > + struct device_attribute *devattr,\
> > + char *buf)\
> > +{\
> > + struct amc6821_data *data = amc6821_update_device(dev);\
> > + return sprintf(buf, "%d\n", data->name * 1000);\
> > +}
> > +
> > +get_temp_para(temp1_input);
> > +get_temp_para(temp1_min);
> > +get_temp_para(temp1_max);
> > +get_temp_para(temp2_input);
> > +get_temp_para(temp2_min);
> > +get_temp_para(temp2_max);
> > +get_temp_para(temp1_crit);
> > +get_temp_para(temp2_crit);
> > +
> > +#define set_temp_para(name, reg)\
> > +static ssize_t set_##name(\
> > + struct device *dev,\
> > + struct device_attribute *attr,\
> > + const char *buf,\
> > + size_t count)\
> > +{ \
> > + struct i2c_client *client = to_i2c_client(dev); \
> > + struct amc6821_data *data = i2c_get_clientdata(client); \
> > + int val = simple_strtol(buf, NULL, 10); \
> > + \
> > + mutex_lock(&data->update_lock); \
> > + data->name = SENSORS_LIMIT(val / 1000, -128, 127); \
> > + if (i2c_smbus_write_byte_data(client, reg, data->name)) {\
> > + dev_err(&client->dev, "Register write error, aborting.\n");\
> > + count = -EIO;\
> > + } \
> > + mutex_unlock(&data->update_lock); \
> > + return count; \
> > +}
>
> I'm wondering if these functions need to be so huge. Couldn't you do
>
> #define set_temp_para(name, reg)\
> static ssize_t set_##name(\
> struct device *dev,\
> struct device_attribute *attr,\
> const char *buf,\
> size_t count)\
> {\
> return set_helper(dev, attr, buf, count, &dev->name);\
> }
>
> And then do all the real work in a common function? Rather than
> expanding tens of copies of the same thing?
Yes please. We got rid of macro-generated callbacks in most hwmon
drivers a couple years ago already.
>
> Also, the checkpatch warning
>
> WARNING: consider using strict_strtol in preference to simple_strtol
> #381: FILE: drivers/hwmon/amc6821.c:228:
> + int val = simple_strtol(buf, NULL, 10); \
>
> is valid. The problem with simple_strtol() is that it will treat input
> of the form "43foo" as "43". Even though the input was invalid. A
> minor thing, but easily fixed too.
Is there any legitimate use of simple_strtol then? I'm wondering why we
don't just get rid of it and rename strict_strtol to just strtol.
--
Jean Delvare
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [lm-sensors] [PATCH] hwmon: Driver for Texas Instruments amc6821 chip
2009-09-09 7:34 ` [lm-sensors] " Jean Delvare
@ 2009-09-09 8:06 ` Andrew Morton
2009-09-09 12:24 ` Tomaz Mertelj
2009-09-21 21:44 ` Andrew Morton
2 siblings, 0 replies; 15+ messages in thread
From: Andrew Morton @ 2009-09-09 8:06 UTC (permalink / raw)
To: Jean Delvare; +Cc: tomaz.mertelj, linux-kernel, lm-sensors
On Wed, 9 Sep 2009 09:34:35 +0200 Jean Delvare <khali@linux-fr.org> wrote:
> > Also, the checkpatch warning
> >
> > WARNING: consider using strict_strtol in preference to simple_strtol
> > #381: FILE: drivers/hwmon/amc6821.c:228:
> > + int val = simple_strtol(buf, NULL, 10); \
> >
> > is valid. The problem with simple_strtol() is that it will treat input
> > of the form "43foo" as "43". Even though the input was invalid. A
> > minor thing, but easily fixed too.
>
> Is there any legitimate use of simple_strtol then?
Probably not, unless it's known that the input is a legit decimal
string.
> I'm wondering why we
> don't just get rid of it and rename strict_strtol to just strtol.
Well. The calling convention is pretty different, the callers need to
be changed to handle errors. But the main problem is that changing
existing interfaces to use strict_strtol() could break existing
userspace.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [lm-sensors] [PATCH] hwmon: Driver for Texas Instruments amc6821 chip
2009-09-09 7:34 ` [lm-sensors] " Jean Delvare
2009-09-09 8:06 ` Andrew Morton
@ 2009-09-09 12:24 ` Tomaz Mertelj
2009-09-09 12:45 ` Jean Delvare
2009-09-21 21:44 ` Andrew Morton
2 siblings, 1 reply; 15+ messages in thread
From: Tomaz Mertelj @ 2009-09-09 12:24 UTC (permalink / raw)
To: Jean Delvare, Andrew Morton; +Cc: linux-kernel, lm-sensors
At 09:34 9. 9. 2009 +0200, Jean Delvare wrote:
> > I'm wondering if these functions need to be so huge. Couldn't you do
> >
> > #define set_temp_para(name, reg)\
> > static ssize_t set_##name(\
> > struct device *dev,\
> > struct device_attribute *attr,\
> > const char *buf,\
> > size_t count)\
> > {\
> > return set_helper(dev, attr, buf, count, &dev->name);\
> > }
> >
> > And then do all the real work in a common function? Rather than
> > expanding tens of copies of the same thing?
>
>Yes please. We got rid of macro-generated callbacks in most hwmon
>drivers a couple years ago already.
I do not like macro-generated callbacks myself as well. However, I was
impatient to get the
driver working and since I have seen similar things in a few other drivers ...
I would prefer a single callback (would require some more work):
static ssize_t set_temp(
struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
struct i2c_client *client = to_i2c_client(dev);
struct amc6821_data *data = i2c_get_clientdata(client);
int nr = to_sensor_dev_attr(attr)->index;
int val = simple_strtol(buf, NULL, 10);
val = SENSORS_LIMIT(val / 1000, -128, 127);
int *pvar;
u8 reg;
switch (nr) {
case GET_SET_TEMP1_MIN:
pvar=&data->temp1_min;
reg=AMC6821_REG_LTEMP_LIMIT_MIN;
break;
case ...
...
default:
dev_dbg(dev, "Unknown attr->index (%d)\n", nr);
return SOME_ERROR;
}
mutex_lock(&data->update_lock);
*pvar=val;
if (i2c_smbus_write_byte_data(client, reg, *pvar)) {
dev_err(&client->dev, "Register write error, aborting.\n");
count = -EIO;
}
mutex_unlock(&data->update_lock);
return count;
}
static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO | S_IWUSR, get_temp,
set_temp, GET_SET_TEMP1_MIN);
...
> >
> > Also, the checkpatch warning
> >
> > WARNING: consider using strict_strtol in preference to simple_strtol
> > #381: FILE: drivers/hwmon/amc6821.c:228:
> > + int val = simple_strtol(buf, NULL, 10); \
> >
> > is valid. The problem with simple_strtol() is that it will treat input
> > of the form "43foo" as "43". Even though the input was invalid. A
> > minor thing, but easily fixed too.
>
>Is there any legitimate use of simple_strtol then? I'm wondering why we
>don't just get rid of it and rename strict_strtol to just strtol.
I have seen simple_strtol in many hwmon drivers so I thought there might be
a reason to do it this way?
***********************************************************************************
Tomaz Mertelj
E-mail: tomaz.mertelj@guest.arnes.si Home page:
http://optlab.ijs.si/tmertelj
Staniceva 14
1000 Ljubljana
Slovenia
***********************************************************************************
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [lm-sensors] [PATCH] hwmon: Driver for Texas Instruments amc6821 chip
2009-09-09 12:24 ` Tomaz Mertelj
@ 2009-09-09 12:45 ` Jean Delvare
0 siblings, 0 replies; 15+ messages in thread
From: Jean Delvare @ 2009-09-09 12:45 UTC (permalink / raw)
To: Tomaz Mertelj; +Cc: Andrew Morton, linux-kernel, lm-sensors
On Wed, 09 Sep 2009 14:24:05 +0200, Tomaz Mertelj wrote:
> At 09:34 9. 9. 2009 +0200, Jean Delvare wrote:
> >Yes please. We got rid of macro-generated callbacks in most hwmon
> >drivers a couple years ago already.
>
> I do not like macro-generated callbacks myself as well. However, I was
> impatient to get the
> driver working and since I have seen similar things in a few other drivers ...
>
> I would prefer a single callback (would require some more work):
>
> static ssize_t set_temp(
> struct device *dev,
> struct device_attribute *attr,
> const char *buf,
> size_t count)
> {
> struct i2c_client *client = to_i2c_client(dev);
> struct amc6821_data *data = i2c_get_clientdata(client);
> int nr = to_sensor_dev_attr(attr)->index;
> int val = simple_strtol(buf, NULL, 10);
> val = SENSORS_LIMIT(val / 1000, -128, 127);
> int *pvar;
> u8 reg;
>
> switch (nr) {
> case GET_SET_TEMP1_MIN:
> pvar=&data->temp1_min;
> reg=AMC6821_REG_LTEMP_LIMIT_MIN;
> break;
> case ...
>
> ...
>
> default:
> dev_dbg(dev, "Unknown attr->index (%d)\n", nr);
> return SOME_ERROR;
> }
> mutex_lock(&data->update_lock);
> *pvar=val;
> if (i2c_smbus_write_byte_data(client, reg, *pvar)) {
> dev_err(&client->dev, "Register write error, aborting.\n");
> count = -EIO;
> }
> mutex_unlock(&data->update_lock);
> return count;
> }
>
>
> static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO | S_IWUSR, get_temp,
> set_temp, GET_SET_TEMP1_MIN);
> ...
Yes, would be much better. Or you can make things even better by
defining arrays of variables in your data structure, and arrays for
register numbers too. And if you need to pass 2 identifiers per entry,
you can take a look at struct sensor_device_attribute_2. So you have a
lot of possibilities to make the code more compact. To which degree you
want that, is up to you.
> > > Also, the checkpatch warning
> > >
> > > WARNING: consider using strict_strtol in preference to simple_strtol
> > > #381: FILE: drivers/hwmon/amc6821.c:228:
> > > + int val = simple_strtol(buf, NULL, 10); \
> > >
> > > is valid. The problem with simple_strtol() is that it will treat input
> > > of the form "43foo" as "43". Even though the input was invalid. A
> > > minor thing, but easily fixed too.
> >
> >Is there any legitimate use of simple_strtol then? I'm wondering why we
> >don't just get rid of it and rename strict_strtol to just strtol.
>
> I have seen simple_strtol in many hwmon drivers so I thought there might be
> a reason to do it this way?
Historical, as I recall, the strict variant did not exist when we
converted the first driver. And then copy-and-paste from driver to
driver, and here we are.
--
Jean Delvare
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [lm-sensors] [PATCH] hwmon: Driver for Texas Instruments amc6821 chip
2009-09-09 7:34 ` [lm-sensors] " Jean Delvare
2009-09-09 8:06 ` Andrew Morton
2009-09-09 12:24 ` Tomaz Mertelj
@ 2009-09-21 21:44 ` Andrew Morton
2009-09-22 5:59 ` Tomaz Mertelj
2 siblings, 1 reply; 15+ messages in thread
From: Andrew Morton @ 2009-09-21 21:44 UTC (permalink / raw)
To: Jean Delvare; +Cc: tomaz.mertelj, linux-kernel, lm-sensors
On Wed, 9 Sep 2009 09:34:35 +0200
Jean Delvare <khali@linux-fr.org> wrote:
> > #define set_temp_para(name, reg)\
> > static ssize_t set_##name(\
> > struct device *dev,\
> > struct device_attribute *attr,\
> > const char *buf,\
> > size_t count)\
> > {\
> > return set_helper(dev, attr, buf, count, &dev->name);\
> > }
> >
> > And then do all the real work in a common function? Rather than
> > expanding tens of copies of the same thing?
>
> Yes please. We got rid of macro-generated callbacks in most hwmon
> drivers a couple years ago already.
I never received an update to this patch so I'm retaining it in my tree
for now.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [lm-sensors] [PATCH] hwmon: Driver for Texas Instruments amc6821 chip
2009-09-21 21:44 ` Andrew Morton
@ 2009-09-22 5:59 ` Tomaz Mertelj
2009-09-22 6:02 ` Andrew Morton
0 siblings, 1 reply; 15+ messages in thread
From: Tomaz Mertelj @ 2009-09-22 5:59 UTC (permalink / raw)
To: Andrew Morton, Jean Delvare; +Cc: linux-kernel, lm-sensors
At 14:44 21. 9. 2009 -0700, Andrew Morton wrote:
>On Wed, 9 Sep 2009 09:34:35 +0200
>Jean Delvare <khali@linux-fr.org> wrote:
>
> > > #define set_temp_para(name, reg)\
> > > static ssize_t set_##name(\
> > > struct device *dev,\
> > > struct device_attribute *attr,\
> > > const char *buf,\
> > > size_t count)\
> > > {\
> > > return set_helper(dev, attr, buf, count, &dev->name);\
> > > }
> > >
> > > And then do all the real work in a common function? Rather than
> > > expanding tens of copies of the same thing?
> >
> > Yes please. We got rid of macro-generated callbacks in most hwmon
> > drivers a couple years ago already.
>
>I never received an update to this patch so I'm retaining it in my tree
>for now.
OK. I have an update almost ready. I only need some time to test it.
Should I post a patch on top of the original patch or a full patch to the
latest kernel tree?
Tomaz Mertelj
***********************************************************************************
Tomaz Mertelj
E-mail: tomaz.mertelj@guest.arnes.si Home page:
http://optlab.ijs.si/tmertelj
Staniceva 14
1000 Ljubljana
Slovenia
***********************************************************************************
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [lm-sensors] [PATCH] hwmon: Driver for Texas Instruments amc6821 chip
2009-09-22 5:59 ` Tomaz Mertelj
@ 2009-09-22 6:02 ` Andrew Morton
0 siblings, 0 replies; 15+ messages in thread
From: Andrew Morton @ 2009-09-22 6:02 UTC (permalink / raw)
To: Tomaz Mertelj; +Cc: Jean Delvare, linux-kernel, lm-sensors
On Tue, 22 Sep 2009 07:59:14 +0200 Tomaz Mertelj <tomaz.mertelj@guest.arnes.si> wrote:
> Should I post a patch on top of the original patch or a full patch to the
> latest kernel tree?
Either is OK for me.
I'll turn it into an incremental patch so I can see what you changed.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [lm-sensors] [PATCH] hwmon: Driver for Texas Instruments amc6821 chip
@ 2009-09-23 9:32 tomaz.mertelj
2009-09-30 19:44 ` Andrew Morton
0 siblings, 1 reply; 15+ messages in thread
From: tomaz.mertelj @ 2009-09-23 9:32 UTC (permalink / raw)
To: Andrew Morton, Jean Delvare; +Cc: linux-kernel, lm-sensors
On Wed, 9 Sep 2009 09:34:35 +0200
Jean Delvare <khali@linux-fr.org> wrote:
> > And then do all the real work in a common function? Rather than
> > expanding tens of copies of the same thing?
>
> Yes please. We got rid of macro-generated callbacks in most hwmon
> drivers a couple years ago already.
Here is an incremental patch to TI amc6821 chip hwmon driver:
I got rid of macro generated callbacks to reduce memory footprint.
simple_strtol was replaced by strict_strtol.
Fixed some errors in documentation.
Tomaz
--
Signed-off-by: Tomaz Mertelj tomaz.mertelj(at)guest.arnes.si
diff --git a/Documentation/hwmon/amc6821 b/Documentation/hwmon/amc6821
index b7cba86..bc3ab6c 100644
--- a/Documentation/hwmon/amc6821
+++ b/Documentation/hwmon/amc6821
@@ -46,12 +46,12 @@ fan1_div rw Fan divisor can be
either 2 or 4.
pwm1 rw pwm1
pwm1_enable rw regulator mode, 1=open loop, 2=fan
controlled
by remote temperature, 3=fan
controlled by
- combination of on-chip temperature and
+ combination of the on-chip
temperature and
remote-sensor temperature,
pwm1_auto_channels_temp ro 1 if pwm_enable==2, 3 if pwm_enable==3
pwm1_auto_point1_pwm ro Hardwired to 0, shared for both
temperature channels.
-pwm1_auto_point2_pwm rw This value, shared for both
temperature
+pwm1_auto_point2_pwm rw This value is shared for both
temperature
channels.
pwm1_auto_point3_pwm rw Hardwired to 255, shared for both
temperature channels.
@@ -61,9 +61,9 @@ temp1_auto_point1_temp ro Hardwired to
temp2_auto_point1_temp
temp1_auto_point2_temp rw The low-temperature limit of the
proportional
range. Below this temperature
pwm1 = pwm1_auto_point2_pwm. It can
go from
- 0 degree C and 124 degree C in steps
of
+ 0 degree C to 124 degree C in steps of
4 degree C. Read it out after writing
to get
- actual value.
+ the actual value.
temp1_auto_point3_temp rw Above this temperature fan runs at
maximum
speed. It can go from
temp1_auto_point2_temp.
It can only have certain discrete
values
@@ -72,13 +72,13 @@ temp1_auto_point3_temp rw Above this
temperature fan runs at maximum
writing to get the actual value.
temp2_auto_point1_temp rw Must be between 0 degree C and 63
degree C and
- it defines passive cooling
temperature.
+ it defines the passive cooling
temperature.
Below this temperature the fan stops
in
the closed loop mode.
temp2_auto_point2_temp rw The low-temperature limit of the
proportional
range. Below this temperature
pwm1 = pwm1_auto_point2_pwm. It can
go from
- 0 degree C and 124 degree C in steps
+ 0 degree C to 124 degree C in steps
of 4 degree C.
temp2_auto_point3_temp rw Above this temperature fan runs at
maximum
diff --git a/drivers/hwmon/amc6821.c b/drivers/hwmon/amc6821.c
index 6905c5e..26e7635 100644
--- a/drivers/hwmon/amc6821.c
+++ b/drivers/hwmon/amc6821.c
@@ -123,6 +123,31 @@ I2C_CLIENT_INSMOD_1(amc6821);
#define AMC6821_STAT2_L_THERM 0x40
#define AMC6821_STAT2_THERM_IN 0x80
+enum {IDX_TEMP1_INPUT = 0, IDX_TEMP1_MIN, IDX_TEMP1_MAX,
+ IDX_TEMP1_CRIT, IDX_TEMP2_INPUT, IDX_TEMP2_MIN,
+ IDX_TEMP2_MAX, IDX_TEMP2_CRIT,
+ TEMP_IDX_LEN, };
+
+static const u8 temp_reg[] = {AMC6821_REG_LTEMP_HI,
+ AMC6821_REG_LTEMP_LIMIT_MIN,
+ AMC6821_REG_LTEMP_LIMIT_MAX,
+ AMC6821_REG_LTEMP_CRIT,
+ AMC6821_REG_RTEMP_HI,
+ AMC6821_REG_RTEMP_LIMIT_MIN,
+ AMC6821_REG_RTEMP_LIMIT_MAX,
+ AMC6821_REG_RTEMP_CRIT, };
+
+enum {IDX_FAN1_INPUT = 0, IDX_FAN1_MIN, IDX_FAN1_MAX,
+ FAN1_IDX_LEN, };
+
+static const u8 fan_reg_low[] = {AMC6821_REG_TDATA_LOW,
+ AMC6821_REG_TACH_LLIMITL,
+ AMC6821_REG_TACH_HLIMITL, };
+
+
+static const u8 fan_reg_hi[] = {AMC6821_REG_TDATA_HI,
+ AMC6821_REG_TACH_LLIMITH,
+ AMC6821_REG_TACH_HLIMITH, };
static int amc6821_probe(
struct i2c_client *client,
@@ -170,19 +195,9 @@ struct amc6821_data {
unsigned long last_updated; /* in jiffies */
/* register values */
- int temp1_input;
- int temp1_min;
- int temp1_max;
- int temp1_crit;
-
- int temp2_input;
- int temp2_min;
- int temp2_max;
- int temp2_crit;
-
- u16 fan1_input;
- u16 fan1_min;
- u16 fan1_max;
+ int temp[TEMP_IDX_LEN];
+
+ u16 fan[FAN1_IDX_LEN];
u8 fan1_div;
u8 pwm1;
@@ -197,72 +212,87 @@ struct amc6821_data {
};
-#define get_temp_para(name) \
-static ssize_t get_##name(\
- struct device *dev,\
- struct device_attribute *devattr,\
- char *buf)\
-{\
- struct amc6821_data *data = amc6821_update_device(dev);\
- return sprintf(buf, "%d\n", data->name * 1000);\
+static ssize_t get_temp(
+ struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct amc6821_data *data = amc6821_update_device(dev);
+ int ix = to_sensor_dev_attr(devattr)->index;
+
+ return sprintf(buf, "%d\n", data->temp[ix] * 1000);
}
-get_temp_para(temp1_input);
-get_temp_para(temp1_min);
-get_temp_para(temp1_max);
-get_temp_para(temp2_input);
-get_temp_para(temp2_min);
-get_temp_para(temp2_max);
-get_temp_para(temp1_crit);
-get_temp_para(temp2_crit);
-
-#define set_temp_para(name, reg)\
-static ssize_t set_##name(\
- struct device *dev,\
- struct device_attribute *attr,\
- const char *buf,\
- size_t count)\
-{ \
- struct i2c_client *client = to_i2c_client(dev); \
- struct amc6821_data *data = i2c_get_clientdata(client); \
- int val = simple_strtol(buf, NULL, 10); \
- \
- mutex_lock(&data->update_lock); \
- data->name = SENSORS_LIMIT(val / 1000, -128, 127); \
- if (i2c_smbus_write_byte_data(client, reg, data->name)) {\
- dev_err(&client->dev, "Register write error, aborting.
\n");\
- count = -EIO;\
- } \
- mutex_unlock(&data->update_lock); \
- return count; \
+
+
+static ssize_t set_temp(
+ struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct amc6821_data *data = i2c_get_clientdata(client);
+ int ix = to_sensor_dev_attr(attr)->index;
+ long val;
+
+ int ret = strict_strtol(buf, 10, &val);
+ if (ret)
+ return ret;
+ val = SENSORS_LIMIT(val / 1000, -128, 127);
+
+ mutex_lock(&data->update_lock);
+ data->temp[ix] = val;
+ if (i2c_smbus_write_byte_data(client, temp_reg[ix], data->temp
[ix])) {
+ dev_err(&client->dev, "Register write error, aborting.
\n");
+ count = -EIO;
+ }
+ mutex_unlock(&data->update_lock);
+ return count;
}
-set_temp_para(temp1_min, AMC6821_REG_LTEMP_LIMIT_MIN);
-set_temp_para(temp1_max, AMC6821_REG_LTEMP_LIMIT_MAX);
-set_temp_para(temp2_min, AMC6821_REG_RTEMP_LIMIT_MIN);
-set_temp_para(temp2_max, AMC6821_REG_RTEMP_LIMIT_MAX);
-set_temp_para(temp1_crit, AMC6821_REG_LTEMP_CRIT);
-set_temp_para(temp2_crit, AMC6821_REG_RTEMP_CRIT);
-
-#define get_temp_alarm(name, reg, mask)\
-static ssize_t get_##name(\
- struct device *dev, \
- struct device_attribute *devattr,\
- char *buf)\
-{\
- struct amc6821_data *data = amc6821_update_device(dev);\
- if (data->reg & mask)\
- return sprintf(buf, "1");\
- else \
- return sprintf(buf, "0");\
-} \
-
-get_temp_alarm(temp1_min_alarm, stat1, AMC6821_STAT1_LTL)
-get_temp_alarm(temp1_max_alarm, stat1, AMC6821_STAT1_LTH)
-get_temp_alarm(temp2_min_alarm, stat1, AMC6821_STAT1_RTL)
-get_temp_alarm(temp2_max_alarm, stat1, AMC6821_STAT1_RTH)
-get_temp_alarm(temp1_crit_alarm, stat2, AMC6821_STAT2_LTC)
-get_temp_alarm(temp2_crit_alarm, stat2, AMC6821_STAT2_RTC)
+
+
+
+static ssize_t get_temp_alarm(
+ struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct amc6821_data *data = amc6821_update_device(dev);
+ int ix = to_sensor_dev_attr(devattr)->index;
+ u8 flag;
+
+ switch (ix) {
+ case IDX_TEMP1_MIN:
+ flag = data->stat1 & AMC6821_STAT1_LTL;
+ break;
+ case IDX_TEMP1_MAX:
+ flag = data->stat1 & AMC6821_STAT1_LTH;
+ break;
+ case IDX_TEMP1_CRIT:
+ flag = data->stat2 & AMC6821_STAT2_LTC;
+ break;
+ case IDX_TEMP2_MIN:
+ flag = data->stat1 & AMC6821_STAT1_RTL;
+ break;
+ case IDX_TEMP2_MAX:
+ flag = data->stat1 & AMC6821_STAT1_RTH;
+ break;
+ case IDX_TEMP2_CRIT:
+ flag = data->stat2 & AMC6821_STAT2_RTC;
+ break;
+ default:
+ dev_dbg(dev, "Unknown attr->index (%d).\n", ix);
+ return -EINVAL;
+ }
+ if (flag)
+ return sprintf(buf, "1");
+ else
+ return sprintf(buf, "0");
+}
+
+
static ssize_t get_temp2_fault(
@@ -294,7 +324,10 @@ static ssize_t set_pwm1(
{
struct i2c_client *client = to_i2c_client(dev);
struct amc6821_data *data = i2c_get_clientdata(client);
- int val = simple_strtol(buf, NULL, 10);
+ long val;
+ int ret = strict_strtol(buf, 10, &val);
+ if (ret)
+ return ret;
mutex_lock(&data->update_lock);
data->pwm1 = SENSORS_LIMIT(val , 0, 255);
@@ -320,9 +353,12 @@ static ssize_t set_pwm1_enable(
{
struct i2c_client *client = to_i2c_client(dev);
struct amc6821_data *data = i2c_get_clientdata(client);
- int val = simple_strtol(buf, NULL, 10);
+ long val;
+ int config = strict_strtol(buf, 10, &val);
+ if (config)
+ return config;
- int config = i2c_smbus_read_byte_data(client,
AMC6821_REG_CONF1);
+ config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF1);
if (config < 0) {
dev_err(&client->dev,
"Error reading configuration register,
aborting.\n");
@@ -366,101 +402,147 @@ static ssize_t get_pwm1_auto_channels_temp(
}
-#define get_auto_point(name)\
-static ssize_t get_##name(\
- struct device *dev,\
- struct device_attribute *devattr,\
- char *buf)\
-{\
- int nr = to_sensor_dev_attr(devattr)->index;\
- struct amc6821_data *data = amc6821_update_device(dev);\
- return sprintf(buf, "%d\n", data->name[nr] * 1000);\
+static ssize_t get_temp_auto_point_temp(
+ struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ int ix = to_sensor_dev_attr_2(devattr)->index;
+ int nr = to_sensor_dev_attr_2(devattr)->nr;
+ struct amc6821_data *data = amc6821_update_device(dev);
+ switch (nr) {
+ case 1:
+ return sprintf(buf, "%d\n",
+ data->temp1_auto_point_temp[ix] * 1000);
+ break;
+ case 2:
+ return sprintf(buf, "%d\n",
+ data->temp2_auto_point_temp[ix] * 1000);
+ break;
+ default:
+ dev_dbg(dev, "Unknown attr->nr (%d).\n", nr);
+ return -EINVAL;
+ }
}
-get_auto_point(temp1_auto_point_temp);
-get_auto_point(temp2_auto_point_temp);
static ssize_t get_pwm1_auto_point_pwm(
struct device *dev,
struct device_attribute *devattr,
char *buf)
{
- int nr = to_sensor_dev_attr(devattr)->index;
+ int ix = to_sensor_dev_attr(devattr)->index;
struct amc6821_data *data = amc6821_update_device(dev);
- return sprintf(buf, "%d\n", data->pwm1_auto_point_pwm[nr]);
+ return sprintf(buf, "%d\n", data->pwm1_auto_point_pwm[ix]);
+}
+
+
+static inline ssize_t set_slope_register(struct i2c_client *client,
+ u8 reg,
+ u8 dpwm,
+ u8 *ptemp)
+{
+ int dt;
+ u8 tmp;
+
+ dt = ptemp[2]-ptemp[1];
+ for (tmp = 4; tmp > 0; tmp--) {
+ if (dt * (0x20 >> tmp) >= dpwm)
+ break;
+ }
+ tmp |= (ptemp[1] & 0x7C) << 1;
+ if (i2c_smbus_write_byte_data(client,
+ reg, tmp)) {
+ dev_err(&client->dev, "Register write error, aborting.
\n");
+ return -EIO;
+ }
+ return 0;
}
-#define set_temp_auto_point_temp(name, reg)\
-static ssize_t set_##name(\
- struct device *dev,\
- struct device_attribute *attr,\
- const char *buf,\
- size_t count)\
-{ \
- struct i2c_client *client = to_i2c_client(dev); \
- struct amc6821_data *data = amc6821_update_device(dev);\
- int nr = to_sensor_dev_attr(attr)->index;\
- int val = simple_strtol(buf, NULL, 10); \
- u8 tmp;\
- int dt;\
- int dpwm;\
- \
- mutex_lock(&data->update_lock); \
- switch (nr) {\
- case 0:\
- data->name[0] = SENSORS_LIMIT(val / 1000, 0,\
- data->temp1_auto_point_temp[1]);\
- data->name[0] = SENSORS_LIMIT(data->name[0], 0,\
- data->temp2_auto_point_temp[1]);\
- data->name[0] = SENSORS_LIMIT(data->name[0], 0, 63); \
- data->valid = 0;\
- if (i2c_smbus_write_byte_data(\
- client,\
- AMC6821_REG_PSV_TEMP,\
- data->name[0])) {\
- dev_err(&client->dev,\
- "Register write error,
aborting.\n");\
- count = -EIO;\
- } \
- goto EXIT;\
- break;\
- case 1:\
- data->name[1] = SENSORS_LIMIT(\
- val / 1000,\
- (data->name[0] & 0x7C) + 4,\
- 124);\
- data->name[1] &= 0x7C;\
- data->name[2] = SENSORS_LIMIT(\
- data->name[2], data->name[1]
+ 1,\
- 255);\
- break;\
- case 2:\
- data->name[2] = SENSORS_LIMIT(\
- val / 1000,\
- data->name[1]+1,\
- 255); \
- break;\
- } \
- dt = data->name[2]-data->name[1];\
- dpwm = data->pwm1_auto_point_pwm[2] - data-
>pwm1_auto_point_pwm[1];\
- for (tmp = 4; tmp > 0; tmp--) {\
- if (dt * (0x20 >> tmp) >= dpwm)\
- break;\
- } \
- tmp |= (data->name[1] & 0x7C) << 1;\
- if (i2c_smbus_write_byte_data(client, reg, tmp)) {\
- dev_err(&client->dev, "Register write error, aborting.
\n");\
- count = -EIO;\
- } \
- data->valid = 0;\
-EXIT:\
- mutex_unlock(&data->update_lock); \
- return count; \
+
+static ssize_t set_temp_auto_point_temp(
+ struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct amc6821_data *data = amc6821_update_device(dev);
+ int ix = to_sensor_dev_attr_2(attr)->index;
+ int nr = to_sensor_dev_attr_2(attr)->nr;
+ u8 *ptemp;
+ u8 reg;
+ int dpwm;
+ long val;
+ int ret = strict_strtol(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ switch (nr) {
+ case 1:
+ ptemp = data->temp1_auto_point_temp;
+ reg = AMC6821_REG_LTEMP_FAN_CTRL;
+ break;
+ case 2:
+ ptemp = data->temp2_auto_point_temp;
+ reg = AMC6821_REG_RTEMP_FAN_CTRL;
+ break;
+ default:
+ dev_dbg(dev, "Unknown attr->nr (%d).\n", nr);
+ return -EINVAL;
+ }
+
+ data->valid = 0;
+ mutex_lock(&data->update_lock);
+ switch (ix) {
+ case 0:
+ ptemp[0] = SENSORS_LIMIT(val / 1000, 0,
+ data->temp1_auto_point_temp[1]);
+ ptemp[0] = SENSORS_LIMIT(ptemp[0], 0,
+ data->temp2_auto_point_temp[1]);
+ ptemp[0] = SENSORS_LIMIT(ptemp[0], 0, 63);
+ if (i2c_smbus_write_byte_data(
+ client,
+ AMC6821_REG_PSV_TEMP,
+ ptemp[0])) {
+ dev_err(&client->dev,
+ "Register write error,
aborting.\n");
+ count = -EIO;
+ }
+ goto EXIT;
+ break;
+ case 1:
+ ptemp[1] = SENSORS_LIMIT(
+ val / 1000,
+ (ptemp[0] & 0x7C) + 4,
+ 124);
+ ptemp[1] &= 0x7C;
+ ptemp[2] = SENSORS_LIMIT(
+ ptemp[2], ptemp[1] + 1,
+ 255);
+ break;
+ case 2:
+ ptemp[2] = SENSORS_LIMIT(
+ val / 1000,
+ ptemp[1]+1,
+ 255);
+ break;
+ default:
+ dev_dbg(dev, "Unknown attr->index (%d).\n", ix);
+ count = -EINVAL;
+ goto EXIT;
+ }
+ dpwm = data->pwm1_auto_point_pwm[2] - data-
>pwm1_auto_point_pwm[1];
+ if (set_slope_register(client, reg, dpwm, ptemp))
+ count = -EIO;
+
+EXIT:
+ mutex_unlock(&data->update_lock);
+ return count;
}
-set_temp_auto_point_temp(temp1_auto_point_temp,
AMC6821_REG_LTEMP_FAN_CTRL);
-set_temp_auto_point_temp(temp2_auto_point_temp,
AMC6821_REG_RTEMP_FAN_CTRL);
+
static ssize_t set_pwm1_auto_point_pwm(
struct device *dev,
@@ -470,10 +552,11 @@ static ssize_t set_pwm1_auto_point_pwm(
{
struct i2c_client *client = to_i2c_client(dev);
struct amc6821_data *data = i2c_get_clientdata(client);
- int val = simple_strtol(buf, NULL, 10);
- u8 tmp;
- int dt;
int dpwm;
+ long val;
+ int ret = strict_strtol(buf, 10, &val);
+ if (ret)
+ return ret;
mutex_lock(&data->update_lock);
data->pwm1_auto_point_pwm[1] = SENSORS_LIMIT(val, 0, 254);
@@ -481,52 +564,39 @@ static ssize_t set_pwm1_auto_point_pwm(
data->pwm1_auto_point_pwm[1])) {
dev_err(&client->dev, "Register write error, aborting.
\n");
count = -EIO;
+ goto EXIT;
}
-
dpwm = data->pwm1_auto_point_pwm[2] - data-
>pwm1_auto_point_pwm[1];
- dt = data->temp1_auto_point_temp[2]-data-
>temp1_auto_point_temp[1];
- for (tmp = 4; tmp > 0; tmp--) {
- if (dt * (0x20 >> tmp) >= dpwm)
- break;
- }
- tmp |= (data->temp1_auto_point_temp[1] & 0x7C) << 1;
- if (i2c_smbus_write_byte_data(client,
- AMC6821_REG_LTEMP_FAN_CTRL, tmp)) {
- dev_err(&client->dev, "Register write error, aborting.
\n");
+ if (set_slope_register(client, AMC6821_REG_LTEMP_FAN_CTRL,
dpwm,
+ data->temp1_auto_point_temp)) {
count = -EIO;
+ goto EXIT;
}
-
- dt = data->temp2_auto_point_temp[2]-data-
>temp2_auto_point_temp[1];
- for (tmp = 4; tmp > 0; tmp--) {
- if (dt * (0x20 >> tmp) >= dpwm)
- break;
- }
- tmp |= (data->temp2_auto_point_temp[1] & 0x7C) << 1;
- if (i2c_smbus_write_byte_data(client,
- AMC6821_REG_RTEMP_FAN_CTRL, tmp)) {
- dev_err(&client->dev, "Register write error, aborting.
\n");
+ if (set_slope_register(client, AMC6821_REG_RTEMP_FAN_CTRL,
dpwm,
+ data->temp2_auto_point_temp)) {
count = -EIO;
+ goto EXIT;
}
+
+EXIT:
data->valid = 0;
mutex_unlock(&data->update_lock);
return count;
}
-
-#define get_fan_para(name) static ssize_t get_##name(\
- struct device *dev,\
- struct device_attribute *devattr,\
- char *buf)\
-{ \
- struct amc6821_data *data = amc6821_update_device(dev); \
- if (0 == data->name)\
- return sprintf(buf, "0");\
- return sprintf(buf, "%d\n", (int)(6000000 / data->name)); \
+static ssize_t get_fan(
+ struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct amc6821_data *data = amc6821_update_device(dev);
+ int ix = to_sensor_dev_attr(devattr)->index;
+ if (0 == data->fan[ix])
+ return sprintf(buf, "0");
+ return sprintf(buf, "%d\n", (int)(6000000 / data->fan[ix]));
}
-get_fan_para(fan1_input);
-get_fan_para(fan1_min);
-get_fan_para(fan1_max);
+
static ssize_t get_fan1_fault(
struct device *dev,
@@ -541,34 +611,39 @@ static ssize_t get_fan1_fault(
}
-#define set_fan_para(name, reg) \
-static ssize_t set_##name(\
- struct device *dev,\
- struct device_attribute *attr,\
- const char *buf, size_t count)\
-{ \
- struct i2c_client *client = to_i2c_client(dev); \
- struct amc6821_data *data = i2c_get_clientdata(client); \
- int val = simple_strtol(buf, NULL, 10); \
- val = 1 > val ? 0xFFFF : 6000000/val; \
-\
- mutex_lock(&data->update_lock); \
- data->name = (u16) SENSORS_LIMIT(val, 1, 0xFFFF); \
- if (i2c_smbus_write_byte_data(client, reg, data->name &
0xFF)) { \
- dev_err(&client->dev, "Register write error, aborting.
\n");\
- count = -EIO;\
- } \
- if (i2c_smbus_write_byte_data(client, reg+1, data->name >>
8)) { \
- dev_err(&client->dev, "Register write error, aborting.
\n");\
- count = -EIO;\
- } \
- mutex_unlock(&data->update_lock); \
- return count; \
-}
+static ssize_t set_fan(
+ struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct amc6821_data *data = i2c_get_clientdata(client);
+ long val;
+ int ix = to_sensor_dev_attr(attr)->index;
+ int ret = strict_strtol(buf, 10, &val);
+ if (ret)
+ return ret;
+ val = 1 > val ? 0xFFFF : 6000000/val;
+
+ mutex_lock(&data->update_lock);
+ data->fan[ix] = (u16) SENSORS_LIMIT(val, 1, 0xFFFF);
+ if (i2c_smbus_write_byte_data(client, fan_reg_low[ix],
+ data->fan[ix] & 0xFF)) {
+ dev_err(&client->dev, "Register write error, aborting.
\n");
+ count = -EIO;
+ goto EXIT;
+ }
+ if (i2c_smbus_write_byte_data(client,
+ fan_reg_hi[ix], data->fan[ix] >> 8)) {
+ dev_err(&client->dev, "Register write error, aborting.
\n");
+ count = -EIO;
+ }
+EXIT:
+ mutex_unlock(&data->update_lock);
+ return count;
+}
-set_fan_para(fan1_min, AMC6821_REG_TACH_LLIMITL);
-set_fan_para(fan1_max, AMC6821_REG_TACH_HLIMITL);
static ssize_t get_fan1_div(
@@ -587,9 +662,12 @@ static ssize_t set_fan1_div(
{
struct i2c_client *client = to_i2c_client(dev);
struct amc6821_data *data = i2c_get_clientdata(client);
- int val = simple_strtol(buf, NULL, 10);
- int config = i2c_smbus_read_byte_data(client,
AMC6821_REG_CONF4);
+ long val;
+ int config = strict_strtol(buf, 10, &val);
+ if (config)
+ return config;
+ config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF4);
if (config < 0) {
dev_err(&client->dev,
"Error reading configuration register,
aborting.\n");
@@ -607,53 +685,56 @@ static ssize_t set_fan1_div(
break;
default:
mutex_unlock(&data->update_lock);
- return -EINVAL;
+ count = -EINVAL;
+ goto EXIT;
}
if (i2c_smbus_write_byte_data(client, AMC6821_REG_CONF4,
config)) {
dev_err(&client->dev,
"Configuration register write error, aborting.
\n");
count = -EIO;
}
+EXIT:
mutex_unlock(&data->update_lock);
return count;
}
-static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, get_temp1_input,
NULL, 0);
-static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO | S_IWUSR,
get_temp1_min,
- set_temp1_min, 0);
-static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR,
get_temp1_max,
- set_temp1_max, 0);
-static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO | S_IWUSR,
get_temp1_crit,
- set_temp1_crit, 0);
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO,
+ get_temp, NULL, IDX_TEMP1_INPUT);
+static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO | S_IWUSR, get_temp,
+ set_temp, IDX_TEMP1_MIN);
+static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, get_temp,
+ set_temp, IDX_TEMP1_MAX);
+static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO | S_IWUSR, get_temp,
+ set_temp, IDX_TEMP1_CRIT);
static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO,
- get_temp1_min_alarm, NULL, 0);
+ get_temp_alarm, NULL, IDX_TEMP1_MIN);
static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO,
- get_temp1_max_alarm, NULL, 0);
+ get_temp_alarm, NULL, IDX_TEMP1_MAX);
static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO,
- get_temp1_crit_alarm, NULL, 0);
+ get_temp_alarm, NULL, IDX_TEMP1_CRIT);
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO | S_IWUSR,
- get_temp2_input, NULL, 0);
-static SENSOR_DEVICE_ATTR(temp2_min, S_IRUGO | S_IWUSR,
get_temp2_min,
- set_temp2_min, 0);
-static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO | S_IWUSR,
get_temp2_max,
- set_temp2_max, 0);
-static SENSOR_DEVICE_ATTR(temp2_crit, S_IRUGO | S_IWUSR,
get_temp2_crit,
- set_temp2_crit, 0);
+ get_temp, NULL, IDX_TEMP2_INPUT);
+static SENSOR_DEVICE_ATTR(temp2_min, S_IRUGO | S_IWUSR, get_temp,
+ set_temp, IDX_TEMP2_MIN);
+static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO | S_IWUSR, get_temp,
+ set_temp, IDX_TEMP2_MAX);
+static SENSOR_DEVICE_ATTR(temp2_crit, S_IRUGO | S_IWUSR, get_temp,
+ set_temp, IDX_TEMP2_CRIT);
static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO,
get_temp2_fault, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO,
- get_temp2_min_alarm, NULL, 0);
+ get_temp_alarm, NULL, IDX_TEMP2_MIN);
static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO,
- get_temp2_max_alarm, NULL, 0);
+ get_temp_alarm, NULL, IDX_TEMP2_MAX);
static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO,
- get_temp2_crit_alarm, NULL, 0);
-static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, get_fan1_input, NULL,
0);
+ get_temp_alarm, NULL, IDX_TEMP2_CRIT);
+static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, get_fan, NULL,
IDX_FAN1_INPUT);
static SENSOR_DEVICE_ATTR(fan1_min, S_IRUGO | S_IWUSR,
- get_fan1_min, set_fan1_min, 0);
+ get_fan, set_fan, IDX_FAN1_MIN);
static SENSOR_DEVICE_ATTR(fan1_max, S_IRUGO | S_IWUSR,
- get_fan1_max, set_fan1_max, 0);
+ get_fan, set_fan, IDX_FAN1_MAX);
static SENSOR_DEVICE_ATTR(fan1_fault, S_IRUGO, get_fan1_fault, NULL,
0);
static SENSOR_DEVICE_ATTR(fan1_div, S_IRUGO | S_IWUSR,
get_fan1_div, set_fan1_div, 0);
@@ -669,19 +750,19 @@ static SENSOR_DEVICE_ATTR(pwm1_auto_point3_pwm,
S_IRUGO,
get_pwm1_auto_point_pwm, NULL, 2);
static SENSOR_DEVICE_ATTR(pwm1_auto_channels_temp, S_IRUGO,
get_pwm1_auto_channels_temp, NULL, 0);
-static SENSOR_DEVICE_ATTR(temp1_auto_point1_temp, S_IRUGO,
- get_temp1_auto_point_temp, NULL, 0);
-static SENSOR_DEVICE_ATTR(temp1_auto_point2_temp, S_IWUSR | S_IRUGO,
- get_temp1_auto_point_temp, set_temp1_auto_point_temp, 1);
-static SENSOR_DEVICE_ATTR(temp1_auto_point3_temp, S_IWUSR | S_IRUGO,
- get_temp1_auto_point_temp, set_temp1_auto_point_temp, 2);
+static SENSOR_DEVICE_ATTR_2(temp1_auto_point1_temp, S_IRUGO,
+ get_temp_auto_point_temp, NULL, 1, 0);
+static SENSOR_DEVICE_ATTR_2(temp1_auto_point2_temp, S_IWUSR |
S_IRUGO,
+ get_temp_auto_point_temp, set_temp_auto_point_temp, 1, 1);
+static SENSOR_DEVICE_ATTR_2(temp1_auto_point3_temp, S_IWUSR |
S_IRUGO,
+ get_temp_auto_point_temp, set_temp_auto_point_temp, 1, 2);
-static SENSOR_DEVICE_ATTR(temp2_auto_point1_temp, S_IWUSR | S_IRUGO,
- get_temp2_auto_point_temp, set_temp2_auto_point_temp, 0);
-static SENSOR_DEVICE_ATTR(temp2_auto_point2_temp, S_IWUSR | S_IRUGO,
- get_temp2_auto_point_temp, set_temp2_auto_point_temp, 1);
-static SENSOR_DEVICE_ATTR(temp2_auto_point3_temp, S_IWUSR | S_IRUGO,
- get_temp2_auto_point_temp, set_temp2_auto_point_temp, 2);
+static SENSOR_DEVICE_ATTR_2(temp2_auto_point1_temp, S_IWUSR |
S_IRUGO,
+ get_temp_auto_point_temp, set_temp_auto_point_temp, 2, 0);
+static SENSOR_DEVICE_ATTR_2(temp2_auto_point2_temp, S_IWUSR |
S_IRUGO,
+ get_temp_auto_point_temp, set_temp_auto_point_temp, 2, 1);
+static SENSOR_DEVICE_ATTR_2(temp2_auto_point3_temp, S_IWUSR |
S_IRUGO,
+ get_temp_auto_point_temp, set_temp_auto_point_temp, 2, 2);
@@ -736,7 +817,7 @@ static int amc6821_detect(
struct i2c_adapter *adapter = client->adapter;
int address = client->addr;
- dev_dbg(&adapter->dev, "amc6821_detect called, kind = %d\n",
kind);
+ dev_dbg(&adapter->dev, "amc6821_detect called, kind = %d.\n",
kind);
if (!i2c_check_functionality(adapter,
I2C_FUNC_SMBUS_BYTE_DATA)) {
dev_dbg(&adapter->dev,
@@ -904,28 +985,16 @@ static struct amc6821_data
*amc6821_update_device(struct device *dev)
struct amc6821_data *data = i2c_get_clientdata(client);
int timeout = HZ;
u8 reg;
+ int i;
mutex_lock(&data->update_lock);
if (time_after(jiffies, data->last_updated + timeout) ||
!data->valid) {
- data->temp1_input = i2c_smbus_read_byte_data(client,
- AMC6821_REG_LTEMP_HI);
- data->temp1_min = i2c_smbus_read_byte_data(client,
- AMC6821_REG_LTEMP_LIMIT_MIN);
- data->temp1_max = i2c_smbus_read_byte_data(client,
- AMC6821_REG_LTEMP_LIMIT_MAX);
- data->temp1_crit = i2c_smbus_read_byte_data(client,
- AMC6821_REG_LTEMP_CRIT);
-
- data->temp2_input = i2c_smbus_read_byte_data(client,
- AMC6821_REG_RTEMP_HI);
- data->temp2_min = i2c_smbus_read_byte_data(client,
- AMC6821_REG_RTEMP_LIMIT_MIN);
- data->temp2_max = i2c_smbus_read_byte_data(client,
- AMC6821_REG_RTEMP_LIMIT_MAX);
- data->temp2_crit = i2c_smbus_read_byte_data(client,
- AMC6821_REG_RTEMP_CRIT);
+
+ for (i = 0; i < TEMP_IDX_LEN; i++)
+ data->temp[i] = i2c_smbus_read_byte_data
(client,
+ temp_reg[i]);
data->stat1 = i2c_smbus_read_byte_data(client,
AMC6821_REG_STAT1);
@@ -934,18 +1003,14 @@ static struct amc6821_data
*amc6821_update_device(struct device *dev)
data->pwm1 = i2c_smbus_read_byte_data(client,
AMC6821_REG_DCY);
- data->fan1_input = i2c_smbus_read_byte_data(client,
- AMC6821_REG_TDATA_LOW);
- data->fan1_input += i2c_smbus_read_byte_data(client,
- AMC6821_REG_TDATA_HI) << 8;
- data->fan1_min = i2c_smbus_read_byte_data(client,
- AMC6821_REG_TACH_LLIMITL);
- data->fan1_min += i2c_smbus_read_byte_data(client,
- AMC6821_REG_TACH_LLIMITL+1) << 8;
- data->fan1_max = i2c_smbus_read_byte_data(client,
- AMC6821_REG_TACH_HLIMITL);
- data->fan1_max += i2c_smbus_read_byte_data(client,
- AMC6821_REG_TACH_HLIMITL+1) << 8;
+ for (i = 0; i < FAN1_IDX_LEN; i++) {
+ data->fan[i] = i2c_smbus_read_byte_data(
+ client,
+ fan_reg_low[i]);
+ data->fan[i] += i2c_smbus_read_byte_data(
+ client,
+ fan_reg_hi[i]) << 8;
+ }
data->fan1_div = i2c_smbus_read_byte_data(client,
AMC6821_REG_CONF4);
data->fan1_div = data->fan1_div &
AMC6821_CONF4_PSPR ? 4 : 2;
^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [lm-sensors] [PATCH] hwmon: Driver for Texas Instruments amc6821 chip
2009-09-23 9:32 tomaz.mertelj
@ 2009-09-30 19:44 ` Andrew Morton
0 siblings, 0 replies; 15+ messages in thread
From: Andrew Morton @ 2009-09-30 19:44 UTC (permalink / raw)
To: tomaz.mertelj; +Cc: khali, linux-kernel, lm-sensors
On Wed, 23 Sep 2009 11:32:47 +0200
tomaz.mertelj@guest.arnes.si wrote:
> On Wed, 9 Sep 2009 09:34:35 +0200
> Jean Delvare <khali@linux-fr.org> wrote:
>
> > > And then do all the real work in a common function? Rather than
> > > expanding tens of copies of the same thing?
> >
> > Yes please. We got rid of macro-generated callbacks in most hwmon
> > drivers a couple years ago already.
>
> Here is an incremental patch to TI amc6821 chip hwmon driver:
The patch is hopelessly wordwrapped. Please fix and resend?
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [lm-sensors] [PATCH] hwmon: Driver for Texas Instruments amc6821 chip
@ 2009-10-01 7:42 Tomaz Mertelj
0 siblings, 0 replies; 15+ messages in thread
From: Tomaz Mertelj @ 2009-10-01 7:42 UTC (permalink / raw)
To: Andrew Morton; +Cc: khali, linux-kernel, lm-sensors
At 12:44 30. 9. 2009 -0700, Andrew Morton wrote:
>On Wed, 23 Sep 2009 11:32:47 +0200
>tomaz.mertelj@guest.arnes.si wrote:
>
> > On Wed, 9 Sep 2009 09:34:35 +0200
> > Jean Delvare <khali@linux-fr.org> wrote:
> >
> > > > And then do all the real work in a common function? Rather than
> > > > expanding tens of copies of the same thing?
> > >
> > > Yes please. We got rid of macro-generated callbacks in most hwmon
> > > drivers a couple years ago already.
> >
> > Here is an incremental patch to TI amc6821 chip hwmon driver:
>
>The patch is hopelessly wordwrapped. Please fix and resend?
Sorry,
I was trying to solve \n\r by posting via a web browser on a linux
desktop and did not noticed the wrapping.
Here I resend the patch from a linux mail program. It should be ok this
time.
---
Here is an incremental patch to TI amc6821 chip hwmon driver:
I got rid of macro generated callbacks to reduce memory footprint.
simple_strtol was replaced by strict_strtol.
Fixed some errors in documentation.
---
Signed-off-by: Tomaz Mertelj tomaz.mertelj(at)guest.arnes.si
diff --git a/Documentation/hwmon/amc6821 b/Documentation/hwmon/amc6821
index b7cba86..bc3ab6c 100644
--- a/Documentation/hwmon/amc6821
+++ b/Documentation/hwmon/amc6821
@@ -46,12 +46,12 @@ fan1_div rw Fan divisor can be either 2 or 4.
pwm1 rw pwm1
pwm1_enable rw regulator mode, 1=open loop, 2=fan controlled
by remote temperature, 3=fan controlled by
- combination of on-chip temperature and
+ combination of the on-chip temperature and
remote-sensor temperature,
pwm1_auto_channels_temp ro 1 if pwm_enable==2, 3 if pwm_enable==3
pwm1_auto_point1_pwm ro Hardwired to 0, shared for both
temperature channels.
-pwm1_auto_point2_pwm rw This value, shared for both temperature
+pwm1_auto_point2_pwm rw This value is shared for both temperature
channels.
pwm1_auto_point3_pwm rw Hardwired to 255, shared for both
temperature channels.
@@ -61,9 +61,9 @@ temp1_auto_point1_temp ro Hardwired to temp2_auto_point1_temp
temp1_auto_point2_temp rw The low-temperature limit of the proportional
range. Below this temperature
pwm1 = pwm1_auto_point2_pwm. It can go from
- 0 degree C and 124 degree C in steps of
+ 0 degree C to 124 degree C in steps of
4 degree C. Read it out after writing to get
- actual value.
+ the actual value.
temp1_auto_point3_temp rw Above this temperature fan runs at maximum
speed. It can go from temp1_auto_point2_temp.
It can only have certain discrete values
@@ -72,13 +72,13 @@ temp1_auto_point3_temp rw Above this temperature fan runs at maximum
writing to get the actual value.
temp2_auto_point1_temp rw Must be between 0 degree C and 63 degree C and
- it defines passive cooling temperature.
+ it defines the passive cooling temperature.
Below this temperature the fan stops in
the closed loop mode.
temp2_auto_point2_temp rw The low-temperature limit of the proportional
range. Below this temperature
pwm1 = pwm1_auto_point2_pwm. It can go from
- 0 degree C and 124 degree C in steps
+ 0 degree C to 124 degree C in steps
of 4 degree C.
temp2_auto_point3_temp rw Above this temperature fan runs at maximum
diff --git a/drivers/hwmon/amc6821.c b/drivers/hwmon/amc6821.c
index 6905c5e..26e7635 100644
--- a/drivers/hwmon/amc6821.c
+++ b/drivers/hwmon/amc6821.c
@@ -123,6 +123,31 @@ I2C_CLIENT_INSMOD_1(amc6821);
#define AMC6821_STAT2_L_THERM 0x40
#define AMC6821_STAT2_THERM_IN 0x80
+enum {IDX_TEMP1_INPUT = 0, IDX_TEMP1_MIN, IDX_TEMP1_MAX,
+ IDX_TEMP1_CRIT, IDX_TEMP2_INPUT, IDX_TEMP2_MIN,
+ IDX_TEMP2_MAX, IDX_TEMP2_CRIT,
+ TEMP_IDX_LEN, };
+
+static const u8 temp_reg[] = {AMC6821_REG_LTEMP_HI,
+ AMC6821_REG_LTEMP_LIMIT_MIN,
+ AMC6821_REG_LTEMP_LIMIT_MAX,
+ AMC6821_REG_LTEMP_CRIT,
+ AMC6821_REG_RTEMP_HI,
+ AMC6821_REG_RTEMP_LIMIT_MIN,
+ AMC6821_REG_RTEMP_LIMIT_MAX,
+ AMC6821_REG_RTEMP_CRIT, };
+
+enum {IDX_FAN1_INPUT = 0, IDX_FAN1_MIN, IDX_FAN1_MAX,
+ FAN1_IDX_LEN, };
+
+static const u8 fan_reg_low[] = {AMC6821_REG_TDATA_LOW,
+ AMC6821_REG_TACH_LLIMITL,
+ AMC6821_REG_TACH_HLIMITL, };
+
+
+static const u8 fan_reg_hi[] = {AMC6821_REG_TDATA_HI,
+ AMC6821_REG_TACH_LLIMITH,
+ AMC6821_REG_TACH_HLIMITH, };
static int amc6821_probe(
struct i2c_client *client,
@@ -170,19 +195,9 @@ struct amc6821_data {
unsigned long last_updated; /* in jiffies */
/* register values */
- int temp1_input;
- int temp1_min;
- int temp1_max;
- int temp1_crit;
-
- int temp2_input;
- int temp2_min;
- int temp2_max;
- int temp2_crit;
-
- u16 fan1_input;
- u16 fan1_min;
- u16 fan1_max;
+ int temp[TEMP_IDX_LEN];
+
+ u16 fan[FAN1_IDX_LEN];
u8 fan1_div;
u8 pwm1;
@@ -197,72 +212,87 @@ struct amc6821_data {
};
-#define get_temp_para(name) \
-static ssize_t get_##name(\
- struct device *dev,\
- struct device_attribute *devattr,\
- char *buf)\
-{\
- struct amc6821_data *data = amc6821_update_device(dev);\
- return sprintf(buf, "%d\n", data->name * 1000);\
+static ssize_t get_temp(
+ struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct amc6821_data *data = amc6821_update_device(dev);
+ int ix = to_sensor_dev_attr(devattr)->index;
+
+ return sprintf(buf, "%d\n", data->temp[ix] * 1000);
}
-get_temp_para(temp1_input);
-get_temp_para(temp1_min);
-get_temp_para(temp1_max);
-get_temp_para(temp2_input);
-get_temp_para(temp2_min);
-get_temp_para(temp2_max);
-get_temp_para(temp1_crit);
-get_temp_para(temp2_crit);
-
-#define set_temp_para(name, reg)\
-static ssize_t set_##name(\
- struct device *dev,\
- struct device_attribute *attr,\
- const char *buf,\
- size_t count)\
-{ \
- struct i2c_client *client = to_i2c_client(dev); \
- struct amc6821_data *data = i2c_get_clientdata(client); \
- int val = simple_strtol(buf, NULL, 10); \
- \
- mutex_lock(&data->update_lock); \
- data->name = SENSORS_LIMIT(val / 1000, -128, 127); \
- if (i2c_smbus_write_byte_data(client, reg, data->name)) {\
- dev_err(&client->dev, "Register write error, aborting.\n");\
- count = -EIO;\
- } \
- mutex_unlock(&data->update_lock); \
- return count; \
+
+
+static ssize_t set_temp(
+ struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct amc6821_data *data = i2c_get_clientdata(client);
+ int ix = to_sensor_dev_attr(attr)->index;
+ long val;
+
+ int ret = strict_strtol(buf, 10, &val);
+ if (ret)
+ return ret;
+ val = SENSORS_LIMIT(val / 1000, -128, 127);
+
+ mutex_lock(&data->update_lock);
+ data->temp[ix] = val;
+ if (i2c_smbus_write_byte_data(client, temp_reg[ix], data->temp[ix])) {
+ dev_err(&client->dev, "Register write error, aborting.\n");
+ count = -EIO;
+ }
+ mutex_unlock(&data->update_lock);
+ return count;
}
-set_temp_para(temp1_min, AMC6821_REG_LTEMP_LIMIT_MIN);
-set_temp_para(temp1_max, AMC6821_REG_LTEMP_LIMIT_MAX);
-set_temp_para(temp2_min, AMC6821_REG_RTEMP_LIMIT_MIN);
-set_temp_para(temp2_max, AMC6821_REG_RTEMP_LIMIT_MAX);
-set_temp_para(temp1_crit, AMC6821_REG_LTEMP_CRIT);
-set_temp_para(temp2_crit, AMC6821_REG_RTEMP_CRIT);
-
-#define get_temp_alarm(name, reg, mask)\
-static ssize_t get_##name(\
- struct device *dev, \
- struct device_attribute *devattr,\
- char *buf)\
-{\
- struct amc6821_data *data = amc6821_update_device(dev);\
- if (data->reg & mask)\
- return sprintf(buf, "1");\
- else \
- return sprintf(buf, "0");\
-} \
-
-get_temp_alarm(temp1_min_alarm, stat1, AMC6821_STAT1_LTL)
-get_temp_alarm(temp1_max_alarm, stat1, AMC6821_STAT1_LTH)
-get_temp_alarm(temp2_min_alarm, stat1, AMC6821_STAT1_RTL)
-get_temp_alarm(temp2_max_alarm, stat1, AMC6821_STAT1_RTH)
-get_temp_alarm(temp1_crit_alarm, stat2, AMC6821_STAT2_LTC)
-get_temp_alarm(temp2_crit_alarm, stat2, AMC6821_STAT2_RTC)
+
+
+
+static ssize_t get_temp_alarm(
+ struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct amc6821_data *data = amc6821_update_device(dev);
+ int ix = to_sensor_dev_attr(devattr)->index;
+ u8 flag;
+
+ switch (ix) {
+ case IDX_TEMP1_MIN:
+ flag = data->stat1 & AMC6821_STAT1_LTL;
+ break;
+ case IDX_TEMP1_MAX:
+ flag = data->stat1 & AMC6821_STAT1_LTH;
+ break;
+ case IDX_TEMP1_CRIT:
+ flag = data->stat2 & AMC6821_STAT2_LTC;
+ break;
+ case IDX_TEMP2_MIN:
+ flag = data->stat1 & AMC6821_STAT1_RTL;
+ break;
+ case IDX_TEMP2_MAX:
+ flag = data->stat1 & AMC6821_STAT1_RTH;
+ break;
+ case IDX_TEMP2_CRIT:
+ flag = data->stat2 & AMC6821_STAT2_RTC;
+ break;
+ default:
+ dev_dbg(dev, "Unknown attr->index (%d).\n", ix);
+ return -EINVAL;
+ }
+ if (flag)
+ return sprintf(buf, "1");
+ else
+ return sprintf(buf, "0");
+}
+
+
static ssize_t get_temp2_fault(
@@ -294,7 +324,10 @@ static ssize_t set_pwm1(
{
struct i2c_client *client = to_i2c_client(dev);
struct amc6821_data *data = i2c_get_clientdata(client);
- int val = simple_strtol(buf, NULL, 10);
+ long val;
+ int ret = strict_strtol(buf, 10, &val);
+ if (ret)
+ return ret;
mutex_lock(&data->update_lock);
data->pwm1 = SENSORS_LIMIT(val , 0, 255);
@@ -320,9 +353,12 @@ static ssize_t set_pwm1_enable(
{
struct i2c_client *client = to_i2c_client(dev);
struct amc6821_data *data = i2c_get_clientdata(client);
- int val = simple_strtol(buf, NULL, 10);
+ long val;
+ int config = strict_strtol(buf, 10, &val);
+ if (config)
+ return config;
- int config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF1);
+ config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF1);
if (config < 0) {
dev_err(&client->dev,
"Error reading configuration register, aborting.\n");
@@ -366,101 +402,147 @@ static ssize_t get_pwm1_auto_channels_temp(
}
-#define get_auto_point(name)\
-static ssize_t get_##name(\
- struct device *dev,\
- struct device_attribute *devattr,\
- char *buf)\
-{\
- int nr = to_sensor_dev_attr(devattr)->index;\
- struct amc6821_data *data = amc6821_update_device(dev);\
- return sprintf(buf, "%d\n", data->name[nr] * 1000);\
+static ssize_t get_temp_auto_point_temp(
+ struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ int ix = to_sensor_dev_attr_2(devattr)->index;
+ int nr = to_sensor_dev_attr_2(devattr)->nr;
+ struct amc6821_data *data = amc6821_update_device(dev);
+ switch (nr) {
+ case 1:
+ return sprintf(buf, "%d\n",
+ data->temp1_auto_point_temp[ix] * 1000);
+ break;
+ case 2:
+ return sprintf(buf, "%d\n",
+ data->temp2_auto_point_temp[ix] * 1000);
+ break;
+ default:
+ dev_dbg(dev, "Unknown attr->nr (%d).\n", nr);
+ return -EINVAL;
+ }
}
-get_auto_point(temp1_auto_point_temp);
-get_auto_point(temp2_auto_point_temp);
static ssize_t get_pwm1_auto_point_pwm(
struct device *dev,
struct device_attribute *devattr,
char *buf)
{
- int nr = to_sensor_dev_attr(devattr)->index;
+ int ix = to_sensor_dev_attr(devattr)->index;
struct amc6821_data *data = amc6821_update_device(dev);
- return sprintf(buf, "%d\n", data->pwm1_auto_point_pwm[nr]);
+ return sprintf(buf, "%d\n", data->pwm1_auto_point_pwm[ix]);
+}
+
+
+static inline ssize_t set_slope_register(struct i2c_client *client,
+ u8 reg,
+ u8 dpwm,
+ u8 *ptemp)
+{
+ int dt;
+ u8 tmp;
+
+ dt = ptemp[2]-ptemp[1];
+ for (tmp = 4; tmp > 0; tmp--) {
+ if (dt * (0x20 >> tmp) >= dpwm)
+ break;
+ }
+ tmp |= (ptemp[1] & 0x7C) << 1;
+ if (i2c_smbus_write_byte_data(client,
+ reg, tmp)) {
+ dev_err(&client->dev, "Register write error, aborting.\n");
+ return -EIO;
+ }
+ return 0;
}
-#define set_temp_auto_point_temp(name, reg)\
-static ssize_t set_##name(\
- struct device *dev,\
- struct device_attribute *attr,\
- const char *buf,\
- size_t count)\
-{ \
- struct i2c_client *client = to_i2c_client(dev); \
- struct amc6821_data *data = amc6821_update_device(dev);\
- int nr = to_sensor_dev_attr(attr)->index;\
- int val = simple_strtol(buf, NULL, 10); \
- u8 tmp;\
- int dt;\
- int dpwm;\
- \
- mutex_lock(&data->update_lock); \
- switch (nr) {\
- case 0:\
- data->name[0] = SENSORS_LIMIT(val / 1000, 0,\
- data->temp1_auto_point_temp[1]);\
- data->name[0] = SENSORS_LIMIT(data->name[0], 0,\
- data->temp2_auto_point_temp[1]);\
- data->name[0] = SENSORS_LIMIT(data->name[0], 0, 63); \
- data->valid = 0;\
- if (i2c_smbus_write_byte_data(\
- client,\
- AMC6821_REG_PSV_TEMP,\
- data->name[0])) {\
- dev_err(&client->dev,\
- "Register write error, aborting.\n");\
- count = -EIO;\
- } \
- goto EXIT;\
- break;\
- case 1:\
- data->name[1] = SENSORS_LIMIT(\
- val / 1000,\
- (data->name[0] & 0x7C) + 4,\
- 124);\
- data->name[1] &= 0x7C;\
- data->name[2] = SENSORS_LIMIT(\
- data->name[2], data->name[1] + 1,\
- 255);\
- break;\
- case 2:\
- data->name[2] = SENSORS_LIMIT(\
- val / 1000,\
- data->name[1]+1,\
- 255); \
- break;\
- } \
- dt = data->name[2]-data->name[1];\
- dpwm = data->pwm1_auto_point_pwm[2] - data->pwm1_auto_point_pwm[1];\
- for (tmp = 4; tmp > 0; tmp--) {\
- if (dt * (0x20 >> tmp) >= dpwm)\
- break;\
- } \
- tmp |= (data->name[1] & 0x7C) << 1;\
- if (i2c_smbus_write_byte_data(client, reg, tmp)) {\
- dev_err(&client->dev, "Register write error, aborting.\n");\
- count = -EIO;\
- } \
- data->valid = 0;\
-EXIT:\
- mutex_unlock(&data->update_lock); \
- return count; \
+
+static ssize_t set_temp_auto_point_temp(
+ struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct amc6821_data *data = amc6821_update_device(dev);
+ int ix = to_sensor_dev_attr_2(attr)->index;
+ int nr = to_sensor_dev_attr_2(attr)->nr;
+ u8 *ptemp;
+ u8 reg;
+ int dpwm;
+ long val;
+ int ret = strict_strtol(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ switch (nr) {
+ case 1:
+ ptemp = data->temp1_auto_point_temp;
+ reg = AMC6821_REG_LTEMP_FAN_CTRL;
+ break;
+ case 2:
+ ptemp = data->temp2_auto_point_temp;
+ reg = AMC6821_REG_RTEMP_FAN_CTRL;
+ break;
+ default:
+ dev_dbg(dev, "Unknown attr->nr (%d).\n", nr);
+ return -EINVAL;
+ }
+
+ data->valid = 0;
+ mutex_lock(&data->update_lock);
+ switch (ix) {
+ case 0:
+ ptemp[0] = SENSORS_LIMIT(val / 1000, 0,
+ data->temp1_auto_point_temp[1]);
+ ptemp[0] = SENSORS_LIMIT(ptemp[0], 0,
+ data->temp2_auto_point_temp[1]);
+ ptemp[0] = SENSORS_LIMIT(ptemp[0], 0, 63);
+ if (i2c_smbus_write_byte_data(
+ client,
+ AMC6821_REG_PSV_TEMP,
+ ptemp[0])) {
+ dev_err(&client->dev,
+ "Register write error, aborting.\n");
+ count = -EIO;
+ }
+ goto EXIT;
+ break;
+ case 1:
+ ptemp[1] = SENSORS_LIMIT(
+ val / 1000,
+ (ptemp[0] & 0x7C) + 4,
+ 124);
+ ptemp[1] &= 0x7C;
+ ptemp[2] = SENSORS_LIMIT(
+ ptemp[2], ptemp[1] + 1,
+ 255);
+ break;
+ case 2:
+ ptemp[2] = SENSORS_LIMIT(
+ val / 1000,
+ ptemp[1]+1,
+ 255);
+ break;
+ default:
+ dev_dbg(dev, "Unknown attr->index (%d).\n", ix);
+ count = -EINVAL;
+ goto EXIT;
+ }
+ dpwm = data->pwm1_auto_point_pwm[2] - data->pwm1_auto_point_pwm[1];
+ if (set_slope_register(client, reg, dpwm, ptemp))
+ count = -EIO;
+
+EXIT:
+ mutex_unlock(&data->update_lock);
+ return count;
}
-set_temp_auto_point_temp(temp1_auto_point_temp, AMC6821_REG_LTEMP_FAN_CTRL);
-set_temp_auto_point_temp(temp2_auto_point_temp, AMC6821_REG_RTEMP_FAN_CTRL);
+
static ssize_t set_pwm1_auto_point_pwm(
struct device *dev,
@@ -470,10 +552,11 @@ static ssize_t set_pwm1_auto_point_pwm(
{
struct i2c_client *client = to_i2c_client(dev);
struct amc6821_data *data = i2c_get_clientdata(client);
- int val = simple_strtol(buf, NULL, 10);
- u8 tmp;
- int dt;
int dpwm;
+ long val;
+ int ret = strict_strtol(buf, 10, &val);
+ if (ret)
+ return ret;
mutex_lock(&data->update_lock);
data->pwm1_auto_point_pwm[1] = SENSORS_LIMIT(val, 0, 254);
@@ -481,52 +564,39 @@ static ssize_t set_pwm1_auto_point_pwm(
data->pwm1_auto_point_pwm[1])) {
dev_err(&client->dev, "Register write error, aborting.\n");
count = -EIO;
+ goto EXIT;
}
-
dpwm = data->pwm1_auto_point_pwm[2] - data->pwm1_auto_point_pwm[1];
- dt = data->temp1_auto_point_temp[2]-data->temp1_auto_point_temp[1];
- for (tmp = 4; tmp > 0; tmp--) {
- if (dt * (0x20 >> tmp) >= dpwm)
- break;
- }
- tmp |= (data->temp1_auto_point_temp[1] & 0x7C) << 1;
- if (i2c_smbus_write_byte_data(client,
- AMC6821_REG_LTEMP_FAN_CTRL, tmp)) {
- dev_err(&client->dev, "Register write error, aborting.\n");
+ if (set_slope_register(client, AMC6821_REG_LTEMP_FAN_CTRL, dpwm,
+ data->temp1_auto_point_temp)) {
count = -EIO;
+ goto EXIT;
}
-
- dt = data->temp2_auto_point_temp[2]-data->temp2_auto_point_temp[1];
- for (tmp = 4; tmp > 0; tmp--) {
- if (dt * (0x20 >> tmp) >= dpwm)
- break;
- }
- tmp |= (data->temp2_auto_point_temp[1] & 0x7C) << 1;
- if (i2c_smbus_write_byte_data(client,
- AMC6821_REG_RTEMP_FAN_CTRL, tmp)) {
- dev_err(&client->dev, "Register write error, aborting.\n");
+ if (set_slope_register(client, AMC6821_REG_RTEMP_FAN_CTRL, dpwm,
+ data->temp2_auto_point_temp)) {
count = -EIO;
+ goto EXIT;
}
+
+EXIT:
data->valid = 0;
mutex_unlock(&data->update_lock);
return count;
}
-
-#define get_fan_para(name) static ssize_t get_##name(\
- struct device *dev,\
- struct device_attribute *devattr,\
- char *buf)\
-{ \
- struct amc6821_data *data = amc6821_update_device(dev); \
- if (0 == data->name)\
- return sprintf(buf, "0");\
- return sprintf(buf, "%d\n", (int)(6000000 / data->name)); \
+static ssize_t get_fan(
+ struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct amc6821_data *data = amc6821_update_device(dev);
+ int ix = to_sensor_dev_attr(devattr)->index;
+ if (0 == data->fan[ix])
+ return sprintf(buf, "0");
+ return sprintf(buf, "%d\n", (int)(6000000 / data->fan[ix]));
}
-get_fan_para(fan1_input);
-get_fan_para(fan1_min);
-get_fan_para(fan1_max);
+
static ssize_t get_fan1_fault(
struct device *dev,
@@ -541,34 +611,39 @@ static ssize_t get_fan1_fault(
}
-#define set_fan_para(name, reg) \
-static ssize_t set_##name(\
- struct device *dev,\
- struct device_attribute *attr,\
- const char *buf, size_t count)\
-{ \
- struct i2c_client *client = to_i2c_client(dev); \
- struct amc6821_data *data = i2c_get_clientdata(client); \
- int val = simple_strtol(buf, NULL, 10); \
- val = 1 > val ? 0xFFFF : 6000000/val; \
-\
- mutex_lock(&data->update_lock); \
- data->name = (u16) SENSORS_LIMIT(val, 1, 0xFFFF); \
- if (i2c_smbus_write_byte_data(client, reg, data->name & 0xFF)) { \
- dev_err(&client->dev, "Register write error, aborting.\n");\
- count = -EIO;\
- } \
- if (i2c_smbus_write_byte_data(client, reg+1, data->name >> 8)) { \
- dev_err(&client->dev, "Register write error, aborting.\n");\
- count = -EIO;\
- } \
- mutex_unlock(&data->update_lock); \
- return count; \
-}
+static ssize_t set_fan(
+ struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct amc6821_data *data = i2c_get_clientdata(client);
+ long val;
+ int ix = to_sensor_dev_attr(attr)->index;
+ int ret = strict_strtol(buf, 10, &val);
+ if (ret)
+ return ret;
+ val = 1 > val ? 0xFFFF : 6000000/val;
+
+ mutex_lock(&data->update_lock);
+ data->fan[ix] = (u16) SENSORS_LIMIT(val, 1, 0xFFFF);
+ if (i2c_smbus_write_byte_data(client, fan_reg_low[ix],
+ data->fan[ix] & 0xFF)) {
+ dev_err(&client->dev, "Register write error, aborting.\n");
+ count = -EIO;
+ goto EXIT;
+ }
+ if (i2c_smbus_write_byte_data(client,
+ fan_reg_hi[ix], data->fan[ix] >> 8)) {
+ dev_err(&client->dev, "Register write error, aborting.\n");
+ count = -EIO;
+ }
+EXIT:
+ mutex_unlock(&data->update_lock);
+ return count;
+}
-set_fan_para(fan1_min, AMC6821_REG_TACH_LLIMITL);
-set_fan_para(fan1_max, AMC6821_REG_TACH_HLIMITL);
static ssize_t get_fan1_div(
@@ -587,9 +662,12 @@ static ssize_t set_fan1_div(
{
struct i2c_client *client = to_i2c_client(dev);
struct amc6821_data *data = i2c_get_clientdata(client);
- int val = simple_strtol(buf, NULL, 10);
- int config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF4);
+ long val;
+ int config = strict_strtol(buf, 10, &val);
+ if (config)
+ return config;
+ config = i2c_smbus_read_byte_data(client, AMC6821_REG_CONF4);
if (config < 0) {
dev_err(&client->dev,
"Error reading configuration register, aborting.\n");
@@ -607,53 +685,56 @@ static ssize_t set_fan1_div(
break;
default:
mutex_unlock(&data->update_lock);
- return -EINVAL;
+ count = -EINVAL;
+ goto EXIT;
}
if (i2c_smbus_write_byte_data(client, AMC6821_REG_CONF4, config)) {
dev_err(&client->dev,
"Configuration register write error, aborting.\n");
count = -EIO;
}
+EXIT:
mutex_unlock(&data->update_lock);
return count;
}
-static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, get_temp1_input, NULL, 0);
-static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO | S_IWUSR, get_temp1_min,
- set_temp1_min, 0);
-static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, get_temp1_max,
- set_temp1_max, 0);
-static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO | S_IWUSR, get_temp1_crit,
- set_temp1_crit, 0);
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO,
+ get_temp, NULL, IDX_TEMP1_INPUT);
+static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO | S_IWUSR, get_temp,
+ set_temp, IDX_TEMP1_MIN);
+static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, get_temp,
+ set_temp, IDX_TEMP1_MAX);
+static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO | S_IWUSR, get_temp,
+ set_temp, IDX_TEMP1_CRIT);
static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO,
- get_temp1_min_alarm, NULL, 0);
+ get_temp_alarm, NULL, IDX_TEMP1_MIN);
static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO,
- get_temp1_max_alarm, NULL, 0);
+ get_temp_alarm, NULL, IDX_TEMP1_MAX);
static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO,
- get_temp1_crit_alarm, NULL, 0);
+ get_temp_alarm, NULL, IDX_TEMP1_CRIT);
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO | S_IWUSR,
- get_temp2_input, NULL, 0);
-static SENSOR_DEVICE_ATTR(temp2_min, S_IRUGO | S_IWUSR, get_temp2_min,
- set_temp2_min, 0);
-static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO | S_IWUSR, get_temp2_max,
- set_temp2_max, 0);
-static SENSOR_DEVICE_ATTR(temp2_crit, S_IRUGO | S_IWUSR, get_temp2_crit,
- set_temp2_crit, 0);
+ get_temp, NULL, IDX_TEMP2_INPUT);
+static SENSOR_DEVICE_ATTR(temp2_min, S_IRUGO | S_IWUSR, get_temp,
+ set_temp, IDX_TEMP2_MIN);
+static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO | S_IWUSR, get_temp,
+ set_temp, IDX_TEMP2_MAX);
+static SENSOR_DEVICE_ATTR(temp2_crit, S_IRUGO | S_IWUSR, get_temp,
+ set_temp, IDX_TEMP2_CRIT);
static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO,
get_temp2_fault, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO,
- get_temp2_min_alarm, NULL, 0);
+ get_temp_alarm, NULL, IDX_TEMP2_MIN);
static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO,
- get_temp2_max_alarm, NULL, 0);
+ get_temp_alarm, NULL, IDX_TEMP2_MAX);
static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO,
- get_temp2_crit_alarm, NULL, 0);
-static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, get_fan1_input, NULL, 0);
+ get_temp_alarm, NULL, IDX_TEMP2_CRIT);
+static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, get_fan, NULL, IDX_FAN1_INPUT);
static SENSOR_DEVICE_ATTR(fan1_min, S_IRUGO | S_IWUSR,
- get_fan1_min, set_fan1_min, 0);
+ get_fan, set_fan, IDX_FAN1_MIN);
static SENSOR_DEVICE_ATTR(fan1_max, S_IRUGO | S_IWUSR,
- get_fan1_max, set_fan1_max, 0);
+ get_fan, set_fan, IDX_FAN1_MAX);
static SENSOR_DEVICE_ATTR(fan1_fault, S_IRUGO, get_fan1_fault, NULL, 0);
static SENSOR_DEVICE_ATTR(fan1_div, S_IRUGO | S_IWUSR,
get_fan1_div, set_fan1_div, 0);
@@ -669,19 +750,19 @@ static SENSOR_DEVICE_ATTR(pwm1_auto_point3_pwm, S_IRUGO,
get_pwm1_auto_point_pwm, NULL, 2);
static SENSOR_DEVICE_ATTR(pwm1_auto_channels_temp, S_IRUGO,
get_pwm1_auto_channels_temp, NULL, 0);
-static SENSOR_DEVICE_ATTR(temp1_auto_point1_temp, S_IRUGO,
- get_temp1_auto_point_temp, NULL, 0);
-static SENSOR_DEVICE_ATTR(temp1_auto_point2_temp, S_IWUSR | S_IRUGO,
- get_temp1_auto_point_temp, set_temp1_auto_point_temp, 1);
-static SENSOR_DEVICE_ATTR(temp1_auto_point3_temp, S_IWUSR | S_IRUGO,
- get_temp1_auto_point_temp, set_temp1_auto_point_temp, 2);
+static SENSOR_DEVICE_ATTR_2(temp1_auto_point1_temp, S_IRUGO,
+ get_temp_auto_point_temp, NULL, 1, 0);
+static SENSOR_DEVICE_ATTR_2(temp1_auto_point2_temp, S_IWUSR | S_IRUGO,
+ get_temp_auto_point_temp, set_temp_auto_point_temp, 1, 1);
+static SENSOR_DEVICE_ATTR_2(temp1_auto_point3_temp, S_IWUSR | S_IRUGO,
+ get_temp_auto_point_temp, set_temp_auto_point_temp, 1, 2);
-static SENSOR_DEVICE_ATTR(temp2_auto_point1_temp, S_IWUSR | S_IRUGO,
- get_temp2_auto_point_temp, set_temp2_auto_point_temp, 0);
-static SENSOR_DEVICE_ATTR(temp2_auto_point2_temp, S_IWUSR | S_IRUGO,
- get_temp2_auto_point_temp, set_temp2_auto_point_temp, 1);
-static SENSOR_DEVICE_ATTR(temp2_auto_point3_temp, S_IWUSR | S_IRUGO,
- get_temp2_auto_point_temp, set_temp2_auto_point_temp, 2);
+static SENSOR_DEVICE_ATTR_2(temp2_auto_point1_temp, S_IWUSR | S_IRUGO,
+ get_temp_auto_point_temp, set_temp_auto_point_temp, 2, 0);
+static SENSOR_DEVICE_ATTR_2(temp2_auto_point2_temp, S_IWUSR | S_IRUGO,
+ get_temp_auto_point_temp, set_temp_auto_point_temp, 2, 1);
+static SENSOR_DEVICE_ATTR_2(temp2_auto_point3_temp, S_IWUSR | S_IRUGO,
+ get_temp_auto_point_temp, set_temp_auto_point_temp, 2, 2);
@@ -736,7 +817,7 @@ static int amc6821_detect(
struct i2c_adapter *adapter = client->adapter;
int address = client->addr;
- dev_dbg(&adapter->dev, "amc6821_detect called, kind = %d\n", kind);
+ dev_dbg(&adapter->dev, "amc6821_detect called, kind = %d.\n", kind);
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
dev_dbg(&adapter->dev,
@@ -904,28 +985,16 @@ static struct amc6821_data *amc6821_update_device(struct device *dev)
struct amc6821_data *data = i2c_get_clientdata(client);
int timeout = HZ;
u8 reg;
+ int i;
mutex_lock(&data->update_lock);
if (time_after(jiffies, data->last_updated + timeout) ||
!data->valid) {
- data->temp1_input = i2c_smbus_read_byte_data(client,
- AMC6821_REG_LTEMP_HI);
- data->temp1_min = i2c_smbus_read_byte_data(client,
- AMC6821_REG_LTEMP_LIMIT_MIN);
- data->temp1_max = i2c_smbus_read_byte_data(client,
- AMC6821_REG_LTEMP_LIMIT_MAX);
- data->temp1_crit = i2c_smbus_read_byte_data(client,
- AMC6821_REG_LTEMP_CRIT);
-
- data->temp2_input = i2c_smbus_read_byte_data(client,
- AMC6821_REG_RTEMP_HI);
- data->temp2_min = i2c_smbus_read_byte_data(client,
- AMC6821_REG_RTEMP_LIMIT_MIN);
- data->temp2_max = i2c_smbus_read_byte_data(client,
- AMC6821_REG_RTEMP_LIMIT_MAX);
- data->temp2_crit = i2c_smbus_read_byte_data(client,
- AMC6821_REG_RTEMP_CRIT);
+
+ for (i = 0; i < TEMP_IDX_LEN; i++)
+ data->temp[i] = i2c_smbus_read_byte_data(client,
+ temp_reg[i]);
data->stat1 = i2c_smbus_read_byte_data(client,
AMC6821_REG_STAT1);
@@ -934,18 +1003,14 @@ static struct amc6821_data *amc6821_update_device(struct device *dev)
data->pwm1 = i2c_smbus_read_byte_data(client,
AMC6821_REG_DCY);
- data->fan1_input = i2c_smbus_read_byte_data(client,
- AMC6821_REG_TDATA_LOW);
- data->fan1_input += i2c_smbus_read_byte_data(client,
- AMC6821_REG_TDATA_HI) << 8;
- data->fan1_min = i2c_smbus_read_byte_data(client,
- AMC6821_REG_TACH_LLIMITL);
- data->fan1_min += i2c_smbus_read_byte_data(client,
- AMC6821_REG_TACH_LLIMITL+1) << 8;
- data->fan1_max = i2c_smbus_read_byte_data(client,
- AMC6821_REG_TACH_HLIMITL);
- data->fan1_max += i2c_smbus_read_byte_data(client,
- AMC6821_REG_TACH_HLIMITL+1) << 8;
+ for (i = 0; i < FAN1_IDX_LEN; i++) {
+ data->fan[i] = i2c_smbus_read_byte_data(
+ client,
+ fan_reg_low[i]);
+ data->fan[i] += i2c_smbus_read_byte_data(
+ client,
+ fan_reg_hi[i]) << 8;
+ }
data->fan1_div = i2c_smbus_read_byte_data(client,
AMC6821_REG_CONF4);
data->fan1_div = data->fan1_div & AMC6821_CONF4_PSPR ? 4 : 2;
^ permalink raw reply related [flat|nested] 15+ messages in thread
end of thread, other threads:[~2009-10-01 7:41 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-08-31 20:24 [PATCH] hwmon: Driver for Texas Instruments amc6821 chip tomaz.mertelj
2009-09-01 17:56 ` [lm-sensors] " Andre Prendel
2009-09-02 8:20 ` Tomaz Mertelj
2009-09-02 8:45 ` corentin.labbe
2009-09-02 23:55 ` Andrew Morton
-- strict thread matches above, loose matches on Subject: below --
2009-09-05 12:08 tomaz.mertelj
2009-09-09 0:06 ` Andrew Morton
2009-09-09 7:34 ` [lm-sensors] " Jean Delvare
2009-09-09 8:06 ` Andrew Morton
2009-09-09 12:24 ` Tomaz Mertelj
2009-09-09 12:45 ` Jean Delvare
2009-09-21 21:44 ` Andrew Morton
2009-09-22 5:59 ` Tomaz Mertelj
2009-09-22 6:02 ` Andrew Morton
2009-09-23 9:32 tomaz.mertelj
2009-09-30 19:44 ` Andrew Morton
2009-10-01 7:42 Tomaz Mertelj
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox