From 025dfdafe77f20b3890981a394774baab7b9c827 Mon Sep 17 00:00:00 2001 From: Frederik Schwarzer Date: Thu, 16 Oct 2008 19:02:37 +0200 Subject: trivial: fix then -> than typos in comments and documentation - (better, more, bigger ...) then -> (...) than Signed-off-by: Frederik Schwarzer Signed-off-by: Jiri Kosina --- drivers/hwmon/fschmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/fschmd.c b/drivers/hwmon/fschmd.c index 96717036893..8b2d756595d 100644 --- a/drivers/hwmon/fschmd.c +++ b/drivers/hwmon/fschmd.c @@ -75,7 +75,7 @@ static const u8 FSCHMD_REG_VOLT[3] = { 0x45, 0x42, 0x48 }; /* minimum pwm at which the fan is driven (pwm can by increased depending on the temp. Notice that for the scy some fans share there minimum speed. - Also notice that with the scy the sensor order is different then with the + Also notice that with the scy the sensor order is different than with the other chips, this order was in the 2.4 driver and kept for consistency. */ static const u8 FSCHMD_REG_FAN_MIN[5][6] = { { 0x55, 0x65 }, /* pos */ -- cgit v1.2.3 From 739cf3a2691951a2d68baa275201a7e931fd50e9 Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Tue, 6 Jan 2009 10:44:41 -0800 Subject: hwmon: struct device - replace bus_id with dev_name(), dev_set_name() Signed-off-by: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/hwmon.c | 2 +- drivers/hwmon/lm75.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index 076a59cdabe..e15c3e7b07e 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -76,7 +76,7 @@ void hwmon_device_unregister(struct device *dev) { int id; - if (likely(sscanf(dev->bus_id, HWMON_ID_FORMAT, &id) == 1)) { + if (likely(sscanf(dev_name(dev), HWMON_ID_FORMAT, &id) == 1)) { device_unregister(dev); spin_lock(&idr_lock); idr_remove(&hwmon_idr, id); diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c index 8f9595f2fb5..55bd87c15c9 100644 --- a/drivers/hwmon/lm75.c +++ b/drivers/hwmon/lm75.c @@ -190,7 +190,7 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id) } dev_info(&client->dev, "%s: sensor '%s'\n", - data->hwmon_dev->bus_id, client->name); + dev_name(data->hwmon_dev), client->name); return 0; -- cgit v1.2.3 From 8f8c1fb0c829278b889588da211a5a557518b06c Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 6 Jan 2009 14:41:31 -0800 Subject: adt74{62, 70, 73}: Use DIV_ROUND_CLOSEST for rounded division Modify some hwmon drivers to use DIV_ROUND_CLOSEST instead of bloating source with (naughty) macros. Signed-off-by: Darrick J. Wong Cc: Jean Delvare Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/hwmon/adt7462.c | 14 ++++++-------- drivers/hwmon/adt7470.c | 8 +++----- drivers/hwmon/adt7473.c | 10 ++++------ 3 files changed, 13 insertions(+), 19 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/adt7462.c b/drivers/hwmon/adt7462.c index 66107b4dc12..1852f27bac5 100644 --- a/drivers/hwmon/adt7462.c +++ b/drivers/hwmon/adt7462.c @@ -204,8 +204,6 @@ I2C_CLIENT_INSMOD_1(adt7462); #define MASK_AND_SHIFT(value, prefix) \ (((value) & prefix##_MASK) >> prefix##_SHIFT) -#define ROUND_DIV(x, divisor) (((x) + ((divisor) / 2)) / (divisor)) - struct adt7462_data { struct device *hwmon_dev; struct attribute_group attrs; @@ -840,7 +838,7 @@ static ssize_t set_temp_min(struct device *dev, if (strict_strtol(buf, 10, &temp) || !temp_enabled(data, attr->index)) return -EINVAL; - temp = ROUND_DIV(temp, 1000) + 64; + temp = DIV_ROUND_CLOSEST(temp, 1000) + 64; temp = SENSORS_LIMIT(temp, 0, 255); mutex_lock(&data->lock); @@ -878,7 +876,7 @@ static ssize_t set_temp_max(struct device *dev, if (strict_strtol(buf, 10, &temp) || !temp_enabled(data, attr->index)) return -EINVAL; - temp = ROUND_DIV(temp, 1000) + 64; + temp = DIV_ROUND_CLOSEST(temp, 1000) + 64; temp = SENSORS_LIMIT(temp, 0, 255); mutex_lock(&data->lock); @@ -943,7 +941,7 @@ static ssize_t set_volt_max(struct device *dev, return -EINVAL; temp *= 1000; /* convert mV to uV */ - temp = ROUND_DIV(temp, x); + temp = DIV_ROUND_CLOSEST(temp, x); temp = SENSORS_LIMIT(temp, 0, 255); mutex_lock(&data->lock); @@ -985,7 +983,7 @@ static ssize_t set_volt_min(struct device *dev, return -EINVAL; temp *= 1000; /* convert mV to uV */ - temp = ROUND_DIV(temp, x); + temp = DIV_ROUND_CLOSEST(temp, x); temp = SENSORS_LIMIT(temp, 0, 255); mutex_lock(&data->lock); @@ -1250,7 +1248,7 @@ static ssize_t set_pwm_hyst(struct device *dev, if (strict_strtol(buf, 10, &temp)) return -EINVAL; - temp = ROUND_DIV(temp, 1000); + temp = DIV_ROUND_CLOSEST(temp, 1000); temp = SENSORS_LIMIT(temp, 0, 15); /* package things up */ @@ -1337,7 +1335,7 @@ static ssize_t set_pwm_tmin(struct device *dev, if (strict_strtol(buf, 10, &temp)) return -EINVAL; - temp = ROUND_DIV(temp, 1000) + 64; + temp = DIV_ROUND_CLOSEST(temp, 1000) + 64; temp = SENSORS_LIMIT(temp, 0, 255); mutex_lock(&data->lock); diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index 1311a595147..da6c9306ca5 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -137,8 +137,6 @@ I2C_CLIENT_INSMOD_1(adt7470); #define FAN_PERIOD_INVALID 65535 #define FAN_DATA_VALID(x) ((x) && (x) != FAN_PERIOD_INVALID) -#define ROUND_DIV(x, divisor) (((x) + ((divisor) / 2)) / (divisor)) - struct adt7470_data { struct device *hwmon_dev; struct attribute_group attrs; @@ -360,7 +358,7 @@ static ssize_t set_temp_min(struct device *dev, if (strict_strtol(buf, 10, &temp)) return -EINVAL; - temp = ROUND_DIV(temp, 1000); + temp = DIV_ROUND_CLOSEST(temp, 1000); temp = SENSORS_LIMIT(temp, 0, 255); mutex_lock(&data->lock); @@ -394,7 +392,7 @@ static ssize_t set_temp_max(struct device *dev, if (strict_strtol(buf, 10, &temp)) return -EINVAL; - temp = ROUND_DIV(temp, 1000); + temp = DIV_ROUND_CLOSEST(temp, 1000); temp = SENSORS_LIMIT(temp, 0, 255); mutex_lock(&data->lock); @@ -671,7 +669,7 @@ static ssize_t set_pwm_tmin(struct device *dev, if (strict_strtol(buf, 10, &temp)) return -EINVAL; - temp = ROUND_DIV(temp, 1000); + temp = DIV_ROUND_CLOSEST(temp, 1000); temp = SENSORS_LIMIT(temp, 0, 255); mutex_lock(&data->lock); diff --git a/drivers/hwmon/adt7473.c b/drivers/hwmon/adt7473.c index 18aa30866a6..0a6ce2367b4 100644 --- a/drivers/hwmon/adt7473.c +++ b/drivers/hwmon/adt7473.c @@ -129,8 +129,6 @@ I2C_CLIENT_INSMOD_1(adt7473); #define FAN_PERIOD_INVALID 65535 #define FAN_DATA_VALID(x) ((x) && (x) != FAN_PERIOD_INVALID) -#define ROUND_DIV(x, divisor) (((x) + ((divisor) / 2)) / (divisor)) - struct adt7473_data { struct device *hwmon_dev; struct attribute_group attrs; @@ -459,7 +457,7 @@ static ssize_t set_temp_min(struct device *dev, if (strict_strtol(buf, 10, &temp)) return -EINVAL; - temp = ROUND_DIV(temp, 1000); + temp = DIV_ROUND_CLOSEST(temp, 1000); temp = encode_temp(data->temp_twos_complement, temp); mutex_lock(&data->lock); @@ -495,7 +493,7 @@ static ssize_t set_temp_max(struct device *dev, if (strict_strtol(buf, 10, &temp)) return -EINVAL; - temp = ROUND_DIV(temp, 1000); + temp = DIV_ROUND_CLOSEST(temp, 1000); temp = encode_temp(data->temp_twos_complement, temp); mutex_lock(&data->lock); @@ -720,7 +718,7 @@ static ssize_t set_temp_tmax(struct device *dev, if (strict_strtol(buf, 10, &temp)) return -EINVAL; - temp = ROUND_DIV(temp, 1000); + temp = DIV_ROUND_CLOSEST(temp, 1000); temp = encode_temp(data->temp_twos_complement, temp); mutex_lock(&data->lock); @@ -756,7 +754,7 @@ static ssize_t set_temp_tmin(struct device *dev, if (strict_strtol(buf, 10, &temp)) return -EINVAL; - temp = ROUND_DIV(temp, 1000); + temp = DIV_ROUND_CLOSEST(temp, 1000); temp = encode_temp(data->temp_twos_complement, temp); mutex_lock(&data->lock); -- cgit v1.2.3 From 2e75a4b7ae0dd4bf7b34e41c2c3be7ac23b8f1cc Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 6 Jan 2009 14:41:32 -0800 Subject: adt7470: fix pwm at a certain level during temperature sensor scan In the small window that it takes to read the temperature sensors, the pwm outputs momentarily drop to 0. This causes a noticeable hiccup in fan speed, which is slightly annoying. The solution is to manually program the pwm output with whatever the automatic value is and then shift the fans to manual control while reading temperatures. Once that is done, put the fans back to whatever mode of control was there before. Signed-off-by: Darrick J. Wong Cc: Jean Delvare Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/hwmon/adt7470.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index da6c9306ca5..b7a442a80bd 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -74,6 +74,7 @@ I2C_CLIENT_INSMOD_1(adt7470); #define ADT7470_REG_PWM12_CFG 0x68 #define ADT7470_PWM2_AUTO_MASK 0x40 #define ADT7470_PWM1_AUTO_MASK 0x80 +#define ADT7470_PWM_AUTO_MASK 0xC0 #define ADT7470_REG_PWM34_CFG 0x69 #define ADT7470_PWM3_AUTO_MASK 0x40 #define ADT7470_PWM4_AUTO_MASK 0x80 @@ -223,7 +224,7 @@ static struct adt7470_data *adt7470_update_device(struct device *dev) struct i2c_client *client = to_i2c_client(dev); struct adt7470_data *data = i2c_get_clientdata(client); unsigned long local_jiffies = jiffies; - u8 cfg; + u8 cfg, pwm[4], pwm_cfg[2]; int i; mutex_lock(&data->lock); @@ -232,6 +233,24 @@ static struct adt7470_data *adt7470_update_device(struct device *dev) && data->sensors_valid) goto no_sensor_update; + /* save pwm[1-4] config register */ + pwm_cfg[0] = i2c_smbus_read_byte_data(client, ADT7470_REG_PWM_CFG(0)); + pwm_cfg[1] = i2c_smbus_read_byte_data(client, ADT7470_REG_PWM_CFG(2)); + + /* set manual pwm to whatever it is set to now */ + for (i = 0; i < ADT7470_FAN_COUNT; i++) + pwm[i] = i2c_smbus_read_byte_data(client, ADT7470_REG_PWM(i)); + + /* put pwm in manual mode */ + i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_CFG(0), + pwm_cfg[0] & ~(ADT7470_PWM_AUTO_MASK)); + i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_CFG(2), + pwm_cfg[1] & ~(ADT7470_PWM_AUTO_MASK)); + + /* write pwm control to whatever it was */ + for (i = 0; i < ADT7470_FAN_COUNT; i++) + i2c_smbus_write_byte_data(client, ADT7470_REG_PWM(i), pwm[i]); + /* start reading temperature sensors */ cfg = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG); cfg |= 0x80; @@ -249,6 +268,10 @@ static struct adt7470_data *adt7470_update_device(struct device *dev) cfg &= ~0x80; i2c_smbus_write_byte_data(client, ADT7470_REG_CFG, cfg); + /* restore pwm[1-4] config registers */ + i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]); + i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]); + for (i = 0; i < ADT7470_TEMP_COUNT; i++) data->temp[i] = i2c_smbus_read_byte_data(client, ADT7470_TEMP_REG(i)); -- cgit v1.2.3 From 2f22d5dff6f95d777c4cb217b99bfdf1fe306128 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 6 Jan 2009 14:41:33 -0800 Subject: adt7470: observe the number of temperature sensors to shorten update time The adt7470 driver currently assumes that 1s is the proper time to wait to read all temperature sensors. However, the correct time is 200ms * number_of_sensors. This patch sets the default time to provide for 10 sensors and then lowers it based on the number of sensor inputs that have nozero values. Signed-off-by: Darrick J. Wong Cc: Jean Delvare Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/hwmon/adt7470.c | 56 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 8 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index b7a442a80bd..ab8d5ebc9f7 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -129,8 +129,8 @@ I2C_CLIENT_INSMOD_1(adt7470); /* How often do we reread sensor limit values? (In jiffies) */ #define LIMIT_REFRESH_INTERVAL (60 * HZ) -/* sleep 1s while gathering temperature data */ -#define TEMP_COLLECTION_TIME 1000 +/* Wait at least 200ms per sensor for 10 sensors */ +#define TEMP_COLLECTION_TIME 2000 /* datasheet says to divide this number by the fan reading to get fan rpm */ #define FAN_PERIOD_TO_RPM(x) ((90000 * 60) / (x)) @@ -147,6 +147,8 @@ struct adt7470_data { unsigned long sensors_last_updated; /* In jiffies */ unsigned long limits_last_updated; /* In jiffies */ + int num_temp_sensors; /* -1 = probe */ + s8 temp[ADT7470_TEMP_COUNT]; s8 temp_min[ADT7470_TEMP_COUNT]; s8 temp_max[ADT7470_TEMP_COUNT]; @@ -256,12 +258,10 @@ static struct adt7470_data *adt7470_update_device(struct device *dev) cfg |= 0x80; i2c_smbus_write_byte_data(client, ADT7470_REG_CFG, cfg); - /* - * Delay is 200ms * number of tmp05 sensors. Too bad - * there's no way to figure out how many are connected. - * For now, assume 1s will work. - */ - msleep(TEMP_COLLECTION_TIME); + /* Delay is 200ms * number of temp sensors. */ + msleep((data->num_temp_sensors >= 0 ? + data->num_temp_sensors * 200 : + TEMP_COLLECTION_TIME)); /* done reading temperature sensors */ cfg = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG); @@ -276,6 +276,12 @@ static struct adt7470_data *adt7470_update_device(struct device *dev) data->temp[i] = i2c_smbus_read_byte_data(client, ADT7470_TEMP_REG(i)); + /* Figure out the number of temp sensors */ + if (data->num_temp_sensors < 0) + for (i = 0; i < ADT7470_TEMP_COUNT; i++) + if (data->temp[i]) + data->num_temp_sensors = i + 1; + for (i = 0; i < ADT7470_FAN_COUNT; i++) data->fan[i] = adt7470_read_word_data(client, ADT7470_REG_FAN(i)); @@ -359,6 +365,35 @@ out: return data; } +static ssize_t show_num_temp_sensors(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct adt7470_data *data = adt7470_update_device(dev); + return sprintf(buf, "%d\n", data->num_temp_sensors); +} + +static ssize_t set_num_temp_sensors(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adt7470_data *data = i2c_get_clientdata(client); + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = SENSORS_LIMIT(temp, -1, 10); + + mutex_lock(&data->lock); + data->num_temp_sensors = temp; + mutex_unlock(&data->lock); + + return count; +} + static ssize_t show_temp_min(struct device *dev, struct device_attribute *devattr, char *buf) @@ -825,6 +860,8 @@ static ssize_t show_alarm(struct device *dev, } static DEVICE_ATTR(alarm_mask, S_IRUGO, show_alarm_mask, NULL); +static DEVICE_ATTR(num_temp_sensors, S_IWUSR | S_IRUGO, show_num_temp_sensors, + set_num_temp_sensors); static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max, set_temp_max, 0); @@ -997,6 +1034,7 @@ static SENSOR_DEVICE_ATTR(pwm4_auto_channels_temp, S_IWUSR | S_IRUGO, static struct attribute *adt7470_attr[] = { &dev_attr_alarm_mask.attr, + &dev_attr_num_temp_sensors.attr, &sensor_dev_attr_temp1_max.dev_attr.attr, &sensor_dev_attr_temp2_max.dev_attr.attr, &sensor_dev_attr_temp3_max.dev_attr.attr, @@ -1129,6 +1167,8 @@ static int adt7470_probe(struct i2c_client *client, goto exit; } + data->num_temp_sensors = -1; + i2c_set_clientdata(client, data); mutex_init(&data->lock); -- cgit v1.2.3 From 89fac11cb3e7c5860c425dba14845c09ccede39d Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 6 Jan 2009 14:41:34 -0800 Subject: adt7470: make automatic fan control really work It turns out that the adt7470's automatic fan control algorithm only works when the temperature sensors get updated. This in turn happens only when someone tells the chip to read its temperature sensors. Regrettably, this means that we have to drive the chip periodically. Signed-off-by: Darrick J. Wong Cc: Jean Delvare Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/hwmon/adt7470.c | 156 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 134 insertions(+), 22 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index ab8d5ebc9f7..633e1a1e9d7 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -28,6 +28,7 @@ #include #include #include +#include /* Addresses to scan */ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; @@ -132,6 +133,9 @@ I2C_CLIENT_INSMOD_1(adt7470); /* Wait at least 200ms per sensor for 10 sensors */ #define TEMP_COLLECTION_TIME 2000 +/* auto update thing won't fire more than every 2s */ +#define AUTO_UPDATE_INTERVAL 2000 + /* datasheet says to divide this number by the fan reading to get fan rpm */ #define FAN_PERIOD_TO_RPM(x) ((90000 * 60) / (x)) #define FAN_RPM_TO_PERIOD FAN_PERIOD_TO_RPM @@ -148,6 +152,7 @@ struct adt7470_data { unsigned long limits_last_updated; /* In jiffies */ int num_temp_sensors; /* -1 = probe */ + int temperatures_probed; s8 temp[ADT7470_TEMP_COUNT]; s8 temp_min[ADT7470_TEMP_COUNT]; @@ -164,6 +169,10 @@ struct adt7470_data { u8 pwm_min[ADT7470_PWM_COUNT]; s8 pwm_tmin[ADT7470_PWM_COUNT]; u8 pwm_auto_temp[ADT7470_PWM_COUNT]; + + struct task_struct *auto_update; + struct completion auto_update_stop; + unsigned int auto_update_interval; }; static int adt7470_probe(struct i2c_client *client, @@ -221,19 +230,13 @@ static void adt7470_init_client(struct i2c_client *client) } } -static struct adt7470_data *adt7470_update_device(struct device *dev) +/* Probe for temperature sensors. Assumes lock is held */ +static int adt7470_read_temperatures(struct i2c_client *client, + struct adt7470_data *data) { - struct i2c_client *client = to_i2c_client(dev); - struct adt7470_data *data = i2c_get_clientdata(client); - unsigned long local_jiffies = jiffies; - u8 cfg, pwm[4], pwm_cfg[2]; + unsigned long res; int i; - - mutex_lock(&data->lock); - if (time_before(local_jiffies, data->sensors_last_updated + - SENSOR_REFRESH_INTERVAL) - && data->sensors_valid) - goto no_sensor_update; + u8 cfg, pwm[4], pwm_cfg[2]; /* save pwm[1-4] config register */ pwm_cfg[0] = i2c_smbus_read_byte_data(client, ADT7470_REG_PWM_CFG(0)); @@ -259,9 +262,9 @@ static struct adt7470_data *adt7470_update_device(struct device *dev) i2c_smbus_write_byte_data(client, ADT7470_REG_CFG, cfg); /* Delay is 200ms * number of temp sensors. */ - msleep((data->num_temp_sensors >= 0 ? - data->num_temp_sensors * 200 : - TEMP_COLLECTION_TIME)); + res = msleep_interruptible((data->num_temp_sensors >= 0 ? + data->num_temp_sensors * 200 : + TEMP_COLLECTION_TIME)); /* done reading temperature sensors */ cfg = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG); @@ -272,15 +275,81 @@ static struct adt7470_data *adt7470_update_device(struct device *dev) i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]); i2c_smbus_write_byte_data(client, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]); - for (i = 0; i < ADT7470_TEMP_COUNT; i++) + if (res) { + printk(KERN_ERR "ha ha, interrupted"); + return -EAGAIN; + } + + /* Only count fans if we have to */ + if (data->num_temp_sensors >= 0) + return 0; + + for (i = 0; i < ADT7470_TEMP_COUNT; i++) { data->temp[i] = i2c_smbus_read_byte_data(client, ADT7470_TEMP_REG(i)); + if (data->temp[i]) + data->num_temp_sensors = i + 1; + } + data->temperatures_probed = 1; + return 0; +} - /* Figure out the number of temp sensors */ - if (data->num_temp_sensors < 0) +static int adt7470_update_thread(void *p) +{ + struct i2c_client *client = p; + struct adt7470_data *data = i2c_get_clientdata(client); + + while (!kthread_should_stop()) { + mutex_lock(&data->lock); + adt7470_read_temperatures(client, data); + mutex_unlock(&data->lock); + if (kthread_should_stop()) + break; + msleep_interruptible(data->auto_update_interval); + } + + complete_all(&data->auto_update_stop); + return 0; +} + +static struct adt7470_data *adt7470_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adt7470_data *data = i2c_get_clientdata(client); + unsigned long local_jiffies = jiffies; + u8 cfg; + int i; + int need_sensors = 1; + int need_limits = 1; + + /* + * Figure out if we need to update the shadow registers. + * Lockless means that we may occasionally report out of + * date data. + */ + if (time_before(local_jiffies, data->sensors_last_updated + + SENSOR_REFRESH_INTERVAL) && + data->sensors_valid) + need_sensors = 0; + + if (time_before(local_jiffies, data->limits_last_updated + + LIMIT_REFRESH_INTERVAL) && + data->limits_valid) + need_limits = 0; + + if (!need_sensors && !need_limits) + return data; + + mutex_lock(&data->lock); + if (!need_sensors) + goto no_sensor_update; + + if (!data->temperatures_probed) + adt7470_read_temperatures(client, data); + else for (i = 0; i < ADT7470_TEMP_COUNT; i++) - if (data->temp[i]) - data->num_temp_sensors = i + 1; + data->temp[i] = i2c_smbus_read_byte_data(client, + ADT7470_TEMP_REG(i)); for (i = 0; i < ADT7470_FAN_COUNT; i++) data->fan[i] = adt7470_read_word_data(client, @@ -329,9 +398,7 @@ static struct adt7470_data *adt7470_update_device(struct device *dev) data->sensors_valid = 1; no_sensor_update: - if (time_before(local_jiffies, data->limits_last_updated + - LIMIT_REFRESH_INTERVAL) - && data->limits_valid) + if (!need_limits) goto out; for (i = 0; i < ADT7470_TEMP_COUNT; i++) { @@ -365,6 +432,35 @@ out: return data; } +static ssize_t show_auto_update_interval(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct adt7470_data *data = adt7470_update_device(dev); + return sprintf(buf, "%d\n", data->auto_update_interval); +} + +static ssize_t set_auto_update_interval(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adt7470_data *data = i2c_get_clientdata(client); + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = SENSORS_LIMIT(temp, 0, 60000); + + mutex_lock(&data->lock); + data->auto_update_interval = temp; + mutex_unlock(&data->lock); + + return count; +} + static ssize_t show_num_temp_sensors(struct device *dev, struct device_attribute *devattr, char *buf) @@ -389,6 +485,8 @@ static ssize_t set_num_temp_sensors(struct device *dev, mutex_lock(&data->lock); data->num_temp_sensors = temp; + if (temp < 0) + data->temperatures_probed = 0; mutex_unlock(&data->lock); return count; @@ -862,6 +960,8 @@ static ssize_t show_alarm(struct device *dev, static DEVICE_ATTR(alarm_mask, S_IRUGO, show_alarm_mask, NULL); static DEVICE_ATTR(num_temp_sensors, S_IWUSR | S_IRUGO, show_num_temp_sensors, set_num_temp_sensors); +static DEVICE_ATTR(auto_update_interval, S_IWUSR | S_IRUGO, + show_auto_update_interval, set_auto_update_interval); static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max, set_temp_max, 0); @@ -1035,6 +1135,7 @@ static struct attribute *adt7470_attr[] = { &dev_attr_alarm_mask.attr, &dev_attr_num_temp_sensors.attr, + &dev_attr_auto_update_interval.attr, &sensor_dev_attr_temp1_max.dev_attr.attr, &sensor_dev_attr_temp2_max.dev_attr.attr, &sensor_dev_attr_temp3_max.dev_attr.attr, @@ -1168,6 +1269,7 @@ static int adt7470_probe(struct i2c_client *client, } data->num_temp_sensors = -1; + data->auto_update_interval = AUTO_UPDATE_INTERVAL; i2c_set_clientdata(client, data); mutex_init(&data->lock); @@ -1188,8 +1290,16 @@ static int adt7470_probe(struct i2c_client *client, goto exit_remove; } + init_completion(&data->auto_update_stop); + data->auto_update = kthread_run(adt7470_update_thread, client, + dev_name(data->hwmon_dev)); + if (IS_ERR(data->auto_update)) + goto exit_unregister; + return 0; +exit_unregister: + hwmon_device_unregister(data->hwmon_dev); exit_remove: sysfs_remove_group(&client->dev.kobj, &data->attrs); exit_free: @@ -1202,6 +1312,8 @@ static int adt7470_remove(struct i2c_client *client) { struct adt7470_data *data = i2c_get_clientdata(client); + kthread_stop(data->auto_update); + wait_for_completion(&data->auto_update_stop); hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &data->attrs); kfree(data); -- cgit v1.2.3 From 85e0e5ad1ef8cebd010bbd7047418a47ca9c5ead Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Tue, 6 Jan 2009 14:41:36 -0800 Subject: hwmon: applesmc: Add support for MacBook Air 2 Add temperature sensor support for MacBook Air 2. Signed-off-by: Henrik Rydberg Cc: Nicolas Boichat Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/hwmon/applesmc.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c index 086c2a5cef0..dca47a591ba 100644 --- a/drivers/hwmon/applesmc.c +++ b/drivers/hwmon/applesmc.c @@ -131,6 +131,10 @@ static const char* temperature_sensors_sets[][36] = { /* Set 14: iMac 6,1 */ { "TA0P", "TC0D", "TC0H", "TC0P", "TG0D", "TG0H", "TG0P", "TH0P", "TO0P", "Tp0P", NULL }, +/* Set 15: MacBook Air 2,1 */ + { "TB0T", "TB1S", "TB1T", "TB2S", "TB2T", "TC0D", "TN0D", "TTF0", + "TV0P", "TVFP", "TW0P", "Th0P", "Tp0P", "Tp1P", "TpFP", "Ts0P", + "Ts0S", NULL }, }; /* List of keys used to read/write fan speeds */ @@ -1301,11 +1305,17 @@ static __initdata struct dmi_match_data applesmc_dmi_data[] = { { .accelerometer = 0, .light = 0, .temperature_set = 13 }, /* iMac 6: light sensor only, temperature set 14 */ { .accelerometer = 0, .light = 0, .temperature_set = 14 }, +/* MacBook Air 2,1: accelerometer, backlight and temperature set 15 */ + { .accelerometer = 1, .light = 1, .temperature_set = 15 }, }; /* Note that DMI_MATCH(...,"MacBook") will match "MacBookPro1,1". * So we need to put "Apple MacBook Pro" before "Apple MacBook". */ static __initdata struct dmi_system_id applesmc_whitelist[] = { + { applesmc_dmi_match, "Apple MacBook Air 2", { + DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir2") }, + &applesmc_dmi_data[15]}, { applesmc_dmi_match, "Apple MacBook Air", { DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), DMI_MATCH(DMI_PRODUCT_NAME, "MacBookAir") }, -- cgit v1.2.3 From 29041b4b1a3d68192eef7613703669f5c7b01d0c Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Tue, 6 Jan 2009 14:41:37 -0800 Subject: ibmpex: add endian annotation to extract_data() helper Signed-off-by: Harvey Harrison Cc: "Darrick J. Wong" Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/hwmon/ibmpex.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/ibmpex.c b/drivers/hwmon/ibmpex.c index 537d9fb2ff8..a36363312f2 100644 --- a/drivers/hwmon/ibmpex.c +++ b/drivers/hwmon/ibmpex.c @@ -40,7 +40,7 @@ static inline u16 extract_value(const char *data, int offset) { - return be16_to_cpup((u16 *)&data[offset]); + return be16_to_cpup((__be16 *)&data[offset]); } #define TEMP_SENSOR 1 -- cgit v1.2.3 From bc37ae71207ca899efb93be1fe6b000366debc3a Mon Sep 17 00:00:00 2001 From: Mark van Doesburg Date: Wed, 7 Jan 2009 16:37:27 +0100 Subject: hwmon: (f71882fg) Use sensor_device_attribute_2 Convert f71882fg driver from SENSOR_ATTR to SENSOR_ATTR2 use, this is a preparation patch for adding pwm support, which is broken out to make what changes really in the pwm support patch clear. Signed-off-by: Hans de Goede Cc: Mark van Doesburg Signed-off-by: Jean Delvare --- drivers/hwmon/f71882fg.c | 187 ++++++++++++++++++++++++----------------------- 1 file changed, 95 insertions(+), 92 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c index 67067e9a323..e2f3c5a8215 100644 --- a/drivers/hwmon/f71882fg.c +++ b/drivers/hwmon/f71882fg.c @@ -188,79 +188,82 @@ static struct device_attribute f71882fg_dev_attr[] = __ATTR( name, S_IRUGO, show_name, NULL ), }; -static struct sensor_device_attribute f71882fg_in_temp_attr[] = -{ - SENSOR_ATTR(in0_input, S_IRUGO, show_in, NULL, 0), - SENSOR_ATTR(in1_input, S_IRUGO, show_in, NULL, 1), - SENSOR_ATTR(in1_max, S_IRUGO|S_IWUSR, show_in_max, store_in_max, 1), - SENSOR_ATTR(in1_beep, S_IRUGO|S_IWUSR, show_in_beep, store_in_beep, 1), - SENSOR_ATTR(in1_alarm, S_IRUGO, show_in_alarm, NULL, 1), - SENSOR_ATTR(in2_input, S_IRUGO, show_in, NULL, 2), - SENSOR_ATTR(in3_input, S_IRUGO, show_in, NULL, 3), - SENSOR_ATTR(in4_input, S_IRUGO, show_in, NULL, 4), - SENSOR_ATTR(in5_input, S_IRUGO, show_in, NULL, 5), - SENSOR_ATTR(in6_input, S_IRUGO, show_in, NULL, 6), - SENSOR_ATTR(in7_input, S_IRUGO, show_in, NULL, 7), - SENSOR_ATTR(in8_input, S_IRUGO, show_in, NULL, 8), - SENSOR_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0), - SENSOR_ATTR(temp1_max, S_IRUGO|S_IWUSR, show_temp_max, - store_temp_max, 0), - SENSOR_ATTR(temp1_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst, - store_temp_max_hyst, 0), - SENSOR_ATTR(temp1_crit, S_IRUGO|S_IWUSR, show_temp_crit, - store_temp_crit, 0), - SENSOR_ATTR(temp1_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, 0), - SENSOR_ATTR(temp1_type, S_IRUGO, show_temp_type, NULL, 0), - SENSOR_ATTR(temp1_beep, S_IRUGO|S_IWUSR, show_temp_beep, - store_temp_beep, 0), - SENSOR_ATTR(temp1_alarm, S_IRUGO, show_temp_alarm, NULL, 0), - SENSOR_ATTR(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0), - SENSOR_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1), - SENSOR_ATTR(temp2_max, S_IRUGO|S_IWUSR, show_temp_max, - store_temp_max, 1), - SENSOR_ATTR(temp2_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst, - store_temp_max_hyst, 1), - SENSOR_ATTR(temp2_crit, S_IRUGO|S_IWUSR, show_temp_crit, - store_temp_crit, 1), - SENSOR_ATTR(temp2_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, 1), - SENSOR_ATTR(temp2_type, S_IRUGO, show_temp_type, NULL, 1), - SENSOR_ATTR(temp2_beep, S_IRUGO|S_IWUSR, show_temp_beep, - store_temp_beep, 1), - SENSOR_ATTR(temp2_alarm, S_IRUGO, show_temp_alarm, NULL, 1), - SENSOR_ATTR(temp2_fault, S_IRUGO, show_temp_fault, NULL, 1), - SENSOR_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2), - SENSOR_ATTR(temp3_max, S_IRUGO|S_IWUSR, show_temp_max, - store_temp_max, 2), - SENSOR_ATTR(temp3_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst, - store_temp_max_hyst, 2), - SENSOR_ATTR(temp3_crit, S_IRUGO|S_IWUSR, show_temp_crit, - store_temp_crit, 2), - SENSOR_ATTR(temp3_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, 2), - SENSOR_ATTR(temp3_type, S_IRUGO, show_temp_type, NULL, 2), - SENSOR_ATTR(temp3_beep, S_IRUGO|S_IWUSR, show_temp_beep, - store_temp_beep, 2), - SENSOR_ATTR(temp3_alarm, S_IRUGO, show_temp_alarm, NULL, 2), - SENSOR_ATTR(temp3_fault, S_IRUGO, show_temp_fault, NULL, 2) +static struct sensor_device_attribute_2 f71882fg_in_temp_attr[] = { + SENSOR_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0), + SENSOR_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 0, 1), + SENSOR_ATTR_2(in1_max, S_IRUGO|S_IWUSR, show_in_max, store_in_max, + 0, 1), + SENSOR_ATTR_2(in1_beep, S_IRUGO|S_IWUSR, show_in_beep, store_in_beep, + 0, 1), + SENSOR_ATTR_2(in1_alarm, S_IRUGO, show_in_alarm, NULL, 0, 1), + SENSOR_ATTR_2(in2_input, S_IRUGO, show_in, NULL, 0, 2), + SENSOR_ATTR_2(in3_input, S_IRUGO, show_in, NULL, 0, 3), + SENSOR_ATTR_2(in4_input, S_IRUGO, show_in, NULL, 0, 4), + SENSOR_ATTR_2(in5_input, S_IRUGO, show_in, NULL, 0, 5), + SENSOR_ATTR_2(in6_input, S_IRUGO, show_in, NULL, 0, 6), + SENSOR_ATTR_2(in7_input, S_IRUGO, show_in, NULL, 0, 7), + SENSOR_ATTR_2(in8_input, S_IRUGO, show_in, NULL, 0, 8), + SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0), + SENSOR_ATTR_2(temp1_max, S_IRUGO|S_IWUSR, show_temp_max, + store_temp_max, 0, 0), + SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst, + store_temp_max_hyst, 0, 0), + SENSOR_ATTR_2(temp1_crit, S_IRUGO|S_IWUSR, show_temp_crit, + store_temp_crit, 0, 0), + SENSOR_ATTR_2(temp1_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, + 0, 0), + SENSOR_ATTR_2(temp1_type, S_IRUGO, show_temp_type, NULL, 0, 0), + SENSOR_ATTR_2(temp1_beep, S_IRUGO|S_IWUSR, show_temp_beep, + store_temp_beep, 0, 0), + SENSOR_ATTR_2(temp1_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 0), + SENSOR_ATTR_2(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0, 0), + SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0, 1), + SENSOR_ATTR_2(temp2_max, S_IRUGO|S_IWUSR, show_temp_max, + store_temp_max, 0, 1), + SENSOR_ATTR_2(temp2_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst, + store_temp_max_hyst, 0, 1), + SENSOR_ATTR_2(temp2_crit, S_IRUGO|S_IWUSR, show_temp_crit, + store_temp_crit, 0, 1), + SENSOR_ATTR_2(temp2_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, + 0, 1), + SENSOR_ATTR_2(temp2_type, S_IRUGO, show_temp_type, NULL, 0, 1), + SENSOR_ATTR_2(temp2_beep, S_IRUGO|S_IWUSR, show_temp_beep, + store_temp_beep, 0, 1), + SENSOR_ATTR_2(temp2_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 1), + SENSOR_ATTR_2(temp2_fault, S_IRUGO, show_temp_fault, NULL, 0, 1), + SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0, 2), + SENSOR_ATTR_2(temp3_max, S_IRUGO|S_IWUSR, show_temp_max, + store_temp_max, 0, 2), + SENSOR_ATTR_2(temp3_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst, + store_temp_max_hyst, 0, 2), + SENSOR_ATTR_2(temp3_crit, S_IRUGO|S_IWUSR, show_temp_crit, + store_temp_crit, 0, 2), + SENSOR_ATTR_2(temp3_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, + 0, 2), + SENSOR_ATTR_2(temp3_type, S_IRUGO, show_temp_type, NULL, 0, 2), + SENSOR_ATTR_2(temp3_beep, S_IRUGO|S_IWUSR, show_temp_beep, + store_temp_beep, 0, 2), + SENSOR_ATTR_2(temp3_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 2), + SENSOR_ATTR_2(temp3_fault, S_IRUGO, show_temp_fault, NULL, 0, 2), }; -static struct sensor_device_attribute f71882fg_fan_attr[] = -{ - SENSOR_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0), - SENSOR_ATTR(fan1_beep, S_IRUGO|S_IWUSR, show_fan_beep, - store_fan_beep, 0), - SENSOR_ATTR(fan1_alarm, S_IRUGO, show_fan_alarm, NULL, 0), - SENSOR_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1), - SENSOR_ATTR(fan2_beep, S_IRUGO|S_IWUSR, show_fan_beep, - store_fan_beep, 1), - SENSOR_ATTR(fan2_alarm, S_IRUGO, show_fan_alarm, NULL, 1), - SENSOR_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2), - SENSOR_ATTR(fan3_beep, S_IRUGO|S_IWUSR, show_fan_beep, - store_fan_beep, 2), - SENSOR_ATTR(fan3_alarm, S_IRUGO, show_fan_alarm, NULL, 2), - SENSOR_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 3), - SENSOR_ATTR(fan4_beep, S_IRUGO|S_IWUSR, show_fan_beep, - store_fan_beep, 3), - SENSOR_ATTR(fan4_alarm, S_IRUGO, show_fan_alarm, NULL, 3) +static struct sensor_device_attribute_2 f71882fg_fan_attr[] = { + SENSOR_ATTR_2(fan1_input, S_IRUGO, show_fan, NULL, 0, 0), + SENSOR_ATTR_2(fan1_beep, S_IRUGO|S_IWUSR, show_fan_beep, + store_fan_beep, 0, 0), + SENSOR_ATTR_2(fan1_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 0), + SENSOR_ATTR_2(fan2_input, S_IRUGO, show_fan, NULL, 0, 1), + SENSOR_ATTR_2(fan2_beep, S_IRUGO|S_IWUSR, show_fan_beep, + store_fan_beep, 0, 1), + SENSOR_ATTR_2(fan2_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 1), + SENSOR_ATTR_2(fan3_input, S_IRUGO, show_fan, NULL, 0, 2), + SENSOR_ATTR_2(fan3_beep, S_IRUGO|S_IWUSR, show_fan_beep, + store_fan_beep, 0, 2), + SENSOR_ATTR_2(fan3_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 2), + SENSOR_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 0, 3), + SENSOR_ATTR_2(fan4_beep, S_IRUGO|S_IWUSR, show_fan_beep, + store_fan_beep, 0, 3), + SENSOR_ATTR_2(fan4_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 3), }; @@ -417,7 +420,7 @@ static ssize_t show_fan(struct device *dev, struct device_attribute *devattr, char *buf) { struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; int speed = fan_from_reg(data->fan[nr]); if (speed == FAN_MIN_DETECT) @@ -430,7 +433,7 @@ static ssize_t show_fan_beep(struct device *dev, struct device_attribute *devattr, char *buf) { struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; if (data->fan_beep & (1 << nr)) return sprintf(buf, "1\n"); @@ -442,7 +445,7 @@ static ssize_t store_fan_beep(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { struct f71882fg_data *data = dev_get_drvdata(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; int val = simple_strtoul(buf, NULL, 10); mutex_lock(&data->update_lock); @@ -461,7 +464,7 @@ static ssize_t show_fan_alarm(struct device *dev, struct device_attribute *devattr, char *buf) { struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; if (data->fan_status & (1 << nr)) return sprintf(buf, "1\n"); @@ -473,7 +476,7 @@ static ssize_t show_in(struct device *dev, struct device_attribute *devattr, char *buf) { struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; return sprintf(buf, "%d\n", data->in[nr] * 8); } @@ -507,7 +510,7 @@ static ssize_t show_in_beep(struct device *dev, struct device_attribute *devattr, char *buf) { struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; if (data->in_beep & (1 << nr)) return sprintf(buf, "1\n"); @@ -519,7 +522,7 @@ static ssize_t store_in_beep(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { struct f71882fg_data *data = dev_get_drvdata(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; int val = simple_strtoul(buf, NULL, 10); mutex_lock(&data->update_lock); @@ -538,7 +541,7 @@ static ssize_t show_in_alarm(struct device *dev, struct device_attribute *devattr, char *buf) { struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; if (data->in_status & (1 << nr)) return sprintf(buf, "1\n"); @@ -550,7 +553,7 @@ static ssize_t show_temp(struct device *dev, struct device_attribute *devattr, char *buf) { struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; return sprintf(buf, "%d\n", data->temp[nr] * 1000); } @@ -559,7 +562,7 @@ static ssize_t show_temp_max(struct device *dev, struct device_attribute *devattr, char *buf) { struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; return sprintf(buf, "%d\n", data->temp_high[nr] * 1000); } @@ -568,7 +571,7 @@ static ssize_t store_temp_max(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { struct f71882fg_data *data = dev_get_drvdata(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; int val = simple_strtoul(buf, NULL, 10) / 1000; if (val > 255) @@ -586,7 +589,7 @@ static ssize_t show_temp_max_hyst(struct device *dev, struct device_attribute *devattr, char *buf) { struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; return sprintf(buf, "%d\n", (data->temp_high[nr] - data->temp_hyst[nr]) * 1000); @@ -596,7 +599,7 @@ static ssize_t store_temp_max_hyst(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { struct f71882fg_data *data = dev_get_drvdata(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; int val = simple_strtoul(buf, NULL, 10) / 1000; ssize_t ret = count; @@ -636,7 +639,7 @@ static ssize_t show_temp_crit(struct device *dev, struct device_attribute *devattr, char *buf) { struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; return sprintf(buf, "%d\n", data->temp_ovt[nr] * 1000); } @@ -645,7 +648,7 @@ static ssize_t store_temp_crit(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { struct f71882fg_data *data = dev_get_drvdata(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; int val = simple_strtoul(buf, NULL, 10) / 1000; if (val > 255) @@ -663,7 +666,7 @@ static ssize_t show_temp_crit_hyst(struct device *dev, struct device_attribute *devattr, char *buf) { struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; return sprintf(buf, "%d\n", (data->temp_ovt[nr] - data->temp_hyst[nr]) * 1000); @@ -673,7 +676,7 @@ static ssize_t show_temp_type(struct device *dev, struct device_attribute *devattr, char *buf) { struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; return sprintf(buf, "%d\n", data->temp_type[nr]); } @@ -682,7 +685,7 @@ static ssize_t show_temp_beep(struct device *dev, struct device_attribute *devattr, char *buf) { struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; if (data->temp_beep & (1 << (nr + 1))) return sprintf(buf, "1\n"); @@ -694,7 +697,7 @@ static ssize_t store_temp_beep(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { struct f71882fg_data *data = dev_get_drvdata(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; int val = simple_strtoul(buf, NULL, 10); mutex_lock(&data->update_lock); @@ -713,7 +716,7 @@ static ssize_t show_temp_alarm(struct device *dev, struct device_attribute *devattr, char *buf) { struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; if (data->temp_status & (1 << (nr + 1))) return sprintf(buf, "1\n"); @@ -725,7 +728,7 @@ static ssize_t show_temp_fault(struct device *dev, struct device_attribute *devattr, char *buf) { struct f71882fg_data *data = f71882fg_update_device(dev); - int nr = to_sensor_dev_attr(devattr)->index; + int nr = to_sensor_dev_attr_2(devattr)->index; if (data->temp_diode_open & (1 << (nr + 1))) return sprintf(buf, "1\n"); -- cgit v1.2.3 From 77a4a3e2a36aea5896f86653f6b77168d578c962 Mon Sep 17 00:00:00 2001 From: Mark van Doesburg Date: Wed, 7 Jan 2009 16:37:27 +0100 Subject: hwmon: (f71882fg) Misc cleanups A few cleanups that were originally part of a larger patch but are better submitted separately. Signed-off-by: Hans de Goede Cc: Mark van Doesburg Signed-off-by: Jean Delvare --- drivers/hwmon/f71882fg.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c index e2f3c5a8215..c6fa8578bde 100644 --- a/drivers/hwmon/f71882fg.c +++ b/drivers/hwmon/f71882fg.c @@ -27,11 +27,11 @@ #include #include #include -#include +#include #define DRVNAME "f71882fg" -#define SIO_F71882FG_LD_HWM 0x04 /* Hardware monitor logical device*/ +#define SIO_F71882FG_LD_HWM 0x04 /* Hardware monitor logical device */ #define SIO_UNLOCK_KEY 0x87 /* Key to enable Super-I/O */ #define SIO_LOCK_KEY 0xAA /* Key to diasble Super-I/O */ @@ -78,7 +78,7 @@ static unsigned short force_id; module_param(force_id, ushort, 0); MODULE_PARM_DESC(force_id, "Override the detected device ID"); -static struct platform_device *f71882fg_pdev = NULL; +static struct platform_device *f71882fg_pdev; /* Super-I/O Function prototypes */ static inline int superio_inb(int base, int reg); @@ -114,7 +114,7 @@ struct f71882fg_data { u8 temp_diode_open; }; -/* Sysfs in*/ +/* Sysfs in */ static ssize_t show_in(struct device *dev, struct device_attribute *devattr, char *buf); static ssize_t show_in_max(struct device *dev, struct device_attribute @@ -335,7 +335,7 @@ static void f71882fg_write8(struct f71882fg_data *data, u8 reg, u8 val) outb(val, data->addr + DATA_REG_OFFSET); } -static struct f71882fg_data *f71882fg_update_device(struct device * dev) +static struct f71882fg_data *f71882fg_update_device(struct device *dev) { struct f71882fg_data *data = dev_get_drvdata(dev); int nr, reg, reg2; -- cgit v1.2.3 From 9ab796ebe185257013f0ac505ecbe7abf068a608 Mon Sep 17 00:00:00 2001 From: Mark van Doesburg Date: Wed, 7 Jan 2009 16:37:27 +0100 Subject: hwmon: (f71882fg) Add PWM support Add PWM (fan speed control) support to the f71882fg driver. Both manual control and automatic (temperature-based) modes are supported. Additionally, each mode has a PWM-based and an RPM-based variant. By default we use the mode set by the BIOS. Signed-off-by: Hans de Goede Cc: Mark van Doesburg Signed-off-by: Jean Delvare --- drivers/hwmon/f71882fg.c | 656 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 655 insertions(+), 1 deletion(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c index c6fa8578bde..c04108735f3 100644 --- a/drivers/hwmon/f71882fg.c +++ b/drivers/hwmon/f71882fg.c @@ -57,6 +57,8 @@ #define F71882FG_REG_IN1_HIGH 0x32 #define F71882FG_REG_FAN(nr) (0xA0 + (16 * (nr))) +#define F71882FG_REG_FAN_TARGET(nr) (0xA2 + (16 * (nr))) +#define F71882FG_REG_FAN_FULL_SPEED(nr) (0xA4 + (16 * (nr))) #define F71882FG_REG_FAN_STATUS 0x92 #define F71882FG_REG_FAN_BEEP 0x93 @@ -70,6 +72,17 @@ #define F71882FG_REG_TEMP_TYPE 0x6B #define F71882FG_REG_TEMP_DIODE_OPEN 0x6F +#define F71882FG_REG_PWM(nr) (0xA3 + (16 * (nr))) +#define F71882FG_REG_PWM_TYPE 0x94 +#define F71882FG_REG_PWM_ENABLE 0x96 + +#define F71882FG_REG_FAN_HYST0 0x98 +#define F71882FG_REG_FAN_HYST1 0x99 + +#define F71882FG_REG_POINT_PWM(pwm, point) (0xAA + (point) + (16 * (pwm))) +#define F71882FG_REG_POINT_TEMP(pwm, point) (0xA6 + (point) + (16 * (pwm))) +#define F71882FG_REG_POINT_MAPPING(nr) (0xAF + 16 * (nr)) + #define F71882FG_REG_START 0x01 #define FAN_MIN_DETECT 366 /* Lowest detectable fanspeed */ @@ -78,6 +91,12 @@ static unsigned short force_id; module_param(force_id, ushort, 0); MODULE_PARM_DESC(force_id, "Override the detected device ID"); +static int fan_mode[4] = { 0, 0, 0, 0 }; +module_param_array(fan_mode, int, NULL, 0644); +MODULE_PARM_DESC(fan_mode, "List of fan control modes (f71882fg only) " + "(0=don't change, 1=pwm, 2=rpm)\n" + "Note: this needs a write to pwm#_enable to take effect"); + static struct platform_device *f71882fg_pdev; /* Super-I/O Function prototypes */ @@ -102,6 +121,8 @@ struct f71882fg_data { u8 in_status; u8 in_beep; u16 fan[4]; + u16 fan_target[4]; + u16 fan_full_speed[4]; u8 fan_status; u8 fan_beep; u8 temp[3]; @@ -112,6 +133,12 @@ struct f71882fg_data { u8 temp_status; u8 temp_beep; u8 temp_diode_open; + u8 pwm[4]; + u8 pwm_enable; + u8 pwm_auto_point_hyst[2]; + u8 pwm_auto_point_mapping[4]; + u8 pwm_auto_point_pwm[4][5]; + u8 pwm_auto_point_temp[4][4]; }; /* Sysfs in */ @@ -130,6 +157,10 @@ static ssize_t show_in_alarm(struct device *dev, struct device_attribute /* Sysfs Fan */ static ssize_t show_fan(struct device *dev, struct device_attribute *devattr, char *buf); +static ssize_t show_fan_full_speed(struct device *dev, + struct device_attribute *devattr, char *buf); +static ssize_t store_fan_full_speed(struct device *dev, + struct device_attribute *devattr, const char *buf, size_t count); static ssize_t show_fan_beep(struct device *dev, struct device_attribute *devattr, char *buf); static ssize_t store_fan_beep(struct device *dev, struct device_attribute @@ -163,6 +194,35 @@ static ssize_t show_temp_alarm(struct device *dev, struct device_attribute *devattr, char *buf); static ssize_t show_temp_fault(struct device *dev, struct device_attribute *devattr, char *buf); +/* PWM and Auto point control */ +static ssize_t show_pwm(struct device *dev, struct device_attribute *devattr, + char *buf); +static ssize_t store_pwm(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count); +static ssize_t show_pwm_enable(struct device *dev, + struct device_attribute *devattr, char *buf); +static ssize_t store_pwm_enable(struct device *dev, + struct device_attribute *devattr, const char *buf, size_t count); +static ssize_t show_pwm_interpolate(struct device *dev, + struct device_attribute *devattr, char *buf); +static ssize_t store_pwm_interpolate(struct device *dev, + struct device_attribute *devattr, const char *buf, size_t count); +static ssize_t show_pwm_auto_point_channel(struct device *dev, + struct device_attribute *devattr, char *buf); +static ssize_t store_pwm_auto_point_channel(struct device *dev, + struct device_attribute *devattr, const char *buf, size_t count); +static ssize_t show_pwm_auto_point_temp_hyst(struct device *dev, + struct device_attribute *devattr, char *buf); +static ssize_t store_pwm_auto_point_temp_hyst(struct device *dev, + struct device_attribute *devattr, const char *buf, size_t count); +static ssize_t show_pwm_auto_point_pwm(struct device *dev, + struct device_attribute *devattr, char *buf); +static ssize_t store_pwm_auto_point_pwm(struct device *dev, + struct device_attribute *devattr, const char *buf, size_t count); +static ssize_t show_pwm_auto_point_temp(struct device *dev, + struct device_attribute *devattr, char *buf); +static ssize_t store_pwm_auto_point_temp(struct device *dev, + struct device_attribute *devattr, const char *buf, size_t count); /* Sysfs misc */ static ssize_t show_name(struct device *dev, struct device_attribute *devattr, char *buf); @@ -249,21 +309,217 @@ static struct sensor_device_attribute_2 f71882fg_in_temp_attr[] = { static struct sensor_device_attribute_2 f71882fg_fan_attr[] = { SENSOR_ATTR_2(fan1_input, S_IRUGO, show_fan, NULL, 0, 0), + SENSOR_ATTR_2(fan1_full_speed, S_IRUGO|S_IWUSR, + show_fan_full_speed, + store_fan_full_speed, 0, 0), SENSOR_ATTR_2(fan1_beep, S_IRUGO|S_IWUSR, show_fan_beep, store_fan_beep, 0, 0), SENSOR_ATTR_2(fan1_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 0), SENSOR_ATTR_2(fan2_input, S_IRUGO, show_fan, NULL, 0, 1), + SENSOR_ATTR_2(fan2_full_speed, S_IRUGO|S_IWUSR, + show_fan_full_speed, + store_fan_full_speed, 0, 1), SENSOR_ATTR_2(fan2_beep, S_IRUGO|S_IWUSR, show_fan_beep, store_fan_beep, 0, 1), SENSOR_ATTR_2(fan2_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 1), SENSOR_ATTR_2(fan3_input, S_IRUGO, show_fan, NULL, 0, 2), + SENSOR_ATTR_2(fan3_full_speed, S_IRUGO|S_IWUSR, + show_fan_full_speed, + store_fan_full_speed, 0, 2), SENSOR_ATTR_2(fan3_beep, S_IRUGO|S_IWUSR, show_fan_beep, store_fan_beep, 0, 2), SENSOR_ATTR_2(fan3_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 2), SENSOR_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 0, 3), + SENSOR_ATTR_2(fan4_full_speed, S_IRUGO|S_IWUSR, + show_fan_full_speed, + store_fan_full_speed, 0, 3), SENSOR_ATTR_2(fan4_beep, S_IRUGO|S_IWUSR, show_fan_beep, store_fan_beep, 0, 3), SENSOR_ATTR_2(fan4_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 3), + + SENSOR_ATTR_2(pwm1, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 0), + SENSOR_ATTR_2(pwm1_enable, S_IRUGO|S_IWUSR, show_pwm_enable, + store_pwm_enable, 0, 0), + SENSOR_ATTR_2(pwm1_interpolate, S_IRUGO|S_IWUSR, + show_pwm_interpolate, store_pwm_interpolate, 0, 0), + SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 0), + SENSOR_ATTR_2(pwm1_auto_point1_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 0, 0), + SENSOR_ATTR_2(pwm1_auto_point2_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 1, 0), + SENSOR_ATTR_2(pwm1_auto_point3_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 2, 0), + SENSOR_ATTR_2(pwm1_auto_point4_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 3, 0), + SENSOR_ATTR_2(pwm1_auto_point5_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 4, 0), + SENSOR_ATTR_2(pwm1_auto_point1_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 0, 0), + SENSOR_ATTR_2(pwm1_auto_point2_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 1, 0), + SENSOR_ATTR_2(pwm1_auto_point3_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 2, 0), + SENSOR_ATTR_2(pwm1_auto_point4_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 3, 0), + SENSOR_ATTR_2(pwm1_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp_hyst, + store_pwm_auto_point_temp_hyst, + 0, 0), + SENSOR_ATTR_2(pwm1_auto_point2_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 1, 0), + SENSOR_ATTR_2(pwm1_auto_point3_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 2, 0), + SENSOR_ATTR_2(pwm1_auto_point4_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 3, 0), + + SENSOR_ATTR_2(pwm2, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 1), + SENSOR_ATTR_2(pwm2_enable, S_IRUGO|S_IWUSR, show_pwm_enable, + store_pwm_enable, 0, 1), + SENSOR_ATTR_2(pwm2_interpolate, S_IRUGO|S_IWUSR, + show_pwm_interpolate, store_pwm_interpolate, 0, 1), + SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 1), + SENSOR_ATTR_2(pwm2_auto_point1_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 0, 1), + SENSOR_ATTR_2(pwm2_auto_point2_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 1, 1), + SENSOR_ATTR_2(pwm2_auto_point3_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 2, 1), + SENSOR_ATTR_2(pwm2_auto_point4_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 3, 1), + SENSOR_ATTR_2(pwm2_auto_point5_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 4, 1), + SENSOR_ATTR_2(pwm2_auto_point1_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 0, 1), + SENSOR_ATTR_2(pwm2_auto_point2_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 1, 1), + SENSOR_ATTR_2(pwm2_auto_point3_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 2, 1), + SENSOR_ATTR_2(pwm2_auto_point4_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 3, 1), + SENSOR_ATTR_2(pwm2_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp_hyst, + store_pwm_auto_point_temp_hyst, + 0, 1), + SENSOR_ATTR_2(pwm2_auto_point2_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 1, 1), + SENSOR_ATTR_2(pwm2_auto_point3_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 2, 1), + SENSOR_ATTR_2(pwm2_auto_point4_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 3, 1), + + SENSOR_ATTR_2(pwm3, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 2), + SENSOR_ATTR_2(pwm3_enable, S_IRUGO|S_IWUSR, show_pwm_enable, + store_pwm_enable, 0, 2), + SENSOR_ATTR_2(pwm3_interpolate, S_IRUGO|S_IWUSR, + show_pwm_interpolate, store_pwm_interpolate, 0, 2), + SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 2), + SENSOR_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 0, 2), + SENSOR_ATTR_2(pwm3_auto_point2_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 1, 2), + SENSOR_ATTR_2(pwm3_auto_point3_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 2, 2), + SENSOR_ATTR_2(pwm3_auto_point4_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 3, 2), + SENSOR_ATTR_2(pwm3_auto_point5_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 4, 2), + SENSOR_ATTR_2(pwm3_auto_point1_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 0, 2), + SENSOR_ATTR_2(pwm3_auto_point2_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 1, 2), + SENSOR_ATTR_2(pwm3_auto_point3_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 2, 2), + SENSOR_ATTR_2(pwm3_auto_point4_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 3, 2), + SENSOR_ATTR_2(pwm3_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp_hyst, + store_pwm_auto_point_temp_hyst, + 0, 2), + SENSOR_ATTR_2(pwm3_auto_point2_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 1, 2), + SENSOR_ATTR_2(pwm3_auto_point3_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 2, 2), + SENSOR_ATTR_2(pwm3_auto_point4_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 3, 2), + + SENSOR_ATTR_2(pwm4, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 3), + SENSOR_ATTR_2(pwm4_enable, S_IRUGO|S_IWUSR, show_pwm_enable, + store_pwm_enable, 0, 3), + SENSOR_ATTR_2(pwm4_interpolate, S_IRUGO|S_IWUSR, + show_pwm_interpolate, store_pwm_interpolate, 0, 3), + SENSOR_ATTR_2(pwm4_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 3), + SENSOR_ATTR_2(pwm4_auto_point1_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 0, 3), + SENSOR_ATTR_2(pwm4_auto_point2_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 1, 3), + SENSOR_ATTR_2(pwm4_auto_point3_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 2, 3), + SENSOR_ATTR_2(pwm4_auto_point4_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 3, 3), + SENSOR_ATTR_2(pwm4_auto_point5_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 4, 3), + SENSOR_ATTR_2(pwm4_auto_point1_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 0, 3), + SENSOR_ATTR_2(pwm4_auto_point2_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 1, 3), + SENSOR_ATTR_2(pwm4_auto_point3_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 2, 3), + SENSOR_ATTR_2(pwm4_auto_point4_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 3, 3), + SENSOR_ATTR_2(pwm4_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp_hyst, + store_pwm_auto_point_temp_hyst, + 0, 3), + SENSOR_ATTR_2(pwm4_auto_point2_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 1, 3), + SENSOR_ATTR_2(pwm4_auto_point3_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 2, 3), + SENSOR_ATTR_2(pwm4_auto_point4_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 3, 3), }; @@ -307,6 +563,11 @@ static inline u16 fan_from_reg(u16 reg) return reg ? (1500000 / reg) : 0; } +static inline u16 fan_to_reg(u16 fan) +{ + return fan ? (1500000 / fan) : 0; +} + static u8 f71882fg_read8(struct f71882fg_data *data, u8 reg) { u8 val; @@ -335,6 +596,14 @@ static void f71882fg_write8(struct f71882fg_data *data, u8 reg, u8 val) outb(val, data->addr + DATA_REG_OFFSET); } +static void f71882fg_write16(struct f71882fg_data *data, u8 reg, u16 val) +{ + outb(reg++, data->addr + ADDR_REG_OFFSET); + outb(val >> 8, data->addr + DATA_REG_OFFSET); + outb(reg, data->addr + ADDR_REG_OFFSET); + outb(val & 255, data->addr + DATA_REG_OFFSET); +} + static struct f71882fg_data *f71882fg_update_device(struct device *dev) { struct f71882fg_data *data = dev_get_drvdata(dev); @@ -381,6 +650,32 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev) data->fan_beep = f71882fg_read8(data, F71882FG_REG_FAN_BEEP); + data->pwm_enable = f71882fg_read8(data, + F71882FG_REG_PWM_ENABLE); + data->pwm_auto_point_hyst[0] = f71882fg_read8(data, + F71882FG_REG_FAN_HYST0); + data->pwm_auto_point_hyst[1] = f71882fg_read8(data, + F71882FG_REG_FAN_HYST1); + for (nr = 0; nr < 4; nr++) { + int point; + + data->pwm_auto_point_mapping[nr] = + f71882fg_read8(data, + F71882FG_REG_POINT_MAPPING(nr)); + + for (point = 0; point < 5; point++) { + data->pwm_auto_point_pwm[nr][point] = + f71882fg_read8(data, + F71882FG_REG_POINT_PWM + (nr, point)); + } + for (point = 0; point < 4; point++) { + data->pwm_auto_point_temp[nr][point] = + f71882fg_read8(data, + F71882FG_REG_POINT_TEMP + (nr, point)); + } + } data->last_limits = jiffies; } @@ -396,9 +691,17 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev) data->fan_status = f71882fg_read8(data, F71882FG_REG_FAN_STATUS); - for (nr = 0; nr < 4; nr++) + for (nr = 0; nr < 4; nr++) { data->fan[nr] = f71882fg_read16(data, F71882FG_REG_FAN(nr)); + data->fan_target[nr] = + f71882fg_read16(data, F71882FG_REG_FAN_TARGET(nr)); + data->fan_full_speed[nr] = + f71882fg_read16(data, + F71882FG_REG_FAN_FULL_SPEED(nr)); + data->pwm[nr] = + f71882fg_read8(data, F71882FG_REG_PWM(nr)); + } data->in_status = f71882fg_read8(data, F71882FG_REG_IN_STATUS); @@ -429,6 +732,40 @@ static ssize_t show_fan(struct device *dev, struct device_attribute *devattr, return sprintf(buf, "%d\n", speed); } +static ssize_t show_fan_full_speed(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + int speed = fan_from_reg(data->fan_full_speed[nr]); + return sprintf(buf, "%d\n", speed); +} + +static ssize_t store_fan_full_speed(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + long val = simple_strtol(buf, NULL, 10); + + val = SENSORS_LIMIT(val, 23, 1500000); + val = fan_to_reg(val); + + mutex_lock(&data->update_lock); + if (data->pwm_enable & (1 << (2 * nr))) + /* PWM mode */ + count = -EINVAL; + else { + /* RPM mode */ + f71882fg_write16(data, F71882FG_REG_FAN_FULL_SPEED(nr), val); + data->fan_full_speed[nr] = val; + } + mutex_unlock(&data->update_lock); + + return count; +} + static ssize_t show_fan_beep(struct device *dev, struct device_attribute *devattr, char *buf) { @@ -736,6 +1073,323 @@ static ssize_t show_temp_fault(struct device *dev, struct device_attribute return sprintf(buf, "0\n"); } +static ssize_t show_pwm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int val, nr = to_sensor_dev_attr_2(devattr)->index; + if (data->pwm_enable & (1 << (2 * nr))) + /* PWM mode */ + val = data->pwm[nr]; + else { + /* RPM mode */ + mutex_lock(&data->update_lock); + val = 255 * fan_from_reg(data->fan_target[nr]) + / fan_from_reg(data->fan_full_speed[nr]); + mutex_unlock(&data->update_lock); + } + return sprintf(buf, "%d\n", val); +} + +static ssize_t store_pwm(struct device *dev, + struct device_attribute *devattr, const char *buf, + size_t count) +{ + /* struct f71882fg_data *data = dev_get_drvdata(dev); */ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + long val = simple_strtol(buf, NULL, 10); + val = SENSORS_LIMIT(val, 0, 255); + + mutex_lock(&data->update_lock); + if (data->pwm_enable & (1 << (2 * nr))) { + /* PWM mode */ + f71882fg_write8(data, F71882FG_REG_PWM(nr), val); + data->pwm[nr] = val; + } else { + /* RPM mode */ + int target = val * fan_from_reg(data->fan_full_speed[nr]) / 255; + f71882fg_write16(data, F71882FG_REG_FAN_TARGET(nr), + fan_to_reg(target)); + data->fan_target[nr] = fan_to_reg(target); + } + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_pwm_enable(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int result; + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + + if (data->pwm_enable & (2 << (2 * nr))) + result = 1; + else + result = 2; + + return sprintf(buf, "%d\n", result); +} + +static ssize_t store_pwm_enable(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count) +{ + struct f71882fg_data *data = dev_get_drvdata(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + long val = simple_strtol(buf, NULL, 10); + if (val < 1 || val > 2) + return -EINVAL; + + mutex_lock(&data->update_lock); + switch (val) { + case 1: + data->pwm_enable |= 2 << (2 * nr); + break; /* Manual */ + case 2: + data->pwm_enable &= ~(2 << (2 * nr)); + break; /* Temperature ctrl */ + } + switch (fan_mode[nr]) { + case 1: + data->pwm_enable |= 1 << (2 * nr); + break; /* Duty cycle mode */ + case 2: + data->pwm_enable &= ~(1 << (2 * nr)); + break; /* RPM mode */ + } + f71882fg_write8(data, F71882FG_REG_PWM_ENABLE, data->pwm_enable); + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_pwm_auto_point_pwm(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + int result; + struct f71882fg_data *data = f71882fg_update_device(dev); + int pwm = to_sensor_dev_attr_2(devattr)->index; + int point = to_sensor_dev_attr_2(devattr)->nr; + + if (data->pwm_enable & (1 << (2 * pwm))) { + /* PWM mode */ + result = data->pwm_auto_point_pwm[pwm][point]; + } else { + /* RPM mode */ + result = 32 * 255 / (32 + data->pwm_auto_point_pwm[pwm][point]); + } + + return sprintf(buf, "%d\n", result); +} + +static ssize_t store_pwm_auto_point_pwm(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + /* struct f71882fg_data *data = dev_get_drvdata(dev); */ + struct f71882fg_data *data = f71882fg_update_device(dev); + int pwm = to_sensor_dev_attr_2(devattr)->index; + int point = to_sensor_dev_attr_2(devattr)->nr; + int val = simple_strtoul(buf, NULL, 10); + val = SENSORS_LIMIT(val, 0, 255); + + mutex_lock(&data->update_lock); + if (data->pwm_enable & (1 << (2 * pwm))) { + /* PWM mode */ + } else { + /* RPM mode */ + if (val < 29) /* Prevent negative numbers */ + val = 255; + else + val = (255 - val) * 32 / val; + } + f71882fg_write8(data, F71882FG_REG_POINT_PWM(pwm, point), val); + data->pwm_auto_point_pwm[pwm][point] = val; + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_pwm_auto_point_temp_hyst(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + int result = 0; + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + int point = to_sensor_dev_attr_2(devattr)->nr; + + mutex_lock(&data->update_lock); + switch (nr) { + case 0: + result = data->pwm_auto_point_hyst[0] & 0x0f; + break; + case 1: + result = data->pwm_auto_point_hyst[0] >> 4; + break; + case 2: + result = data->pwm_auto_point_hyst[1] & 0x0f; + break; + case 3: + result = data->pwm_auto_point_hyst[1] >> 4; + break; + } + result = 1000 * (data->pwm_auto_point_temp[nr][point] - result); + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%d\n", result); +} + +static ssize_t store_pwm_auto_point_temp_hyst(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + int point = to_sensor_dev_attr_2(devattr)->nr; + long val = simple_strtol(buf, NULL, 10) / 1000; + + mutex_lock(&data->update_lock); + val = SENSORS_LIMIT(val, data->pwm_auto_point_temp[nr][point] - 15, + data->pwm_auto_point_temp[nr][point]); + val = data->pwm_auto_point_temp[nr][point] - val; + + switch (nr) { + case 0: + val = (data->pwm_auto_point_hyst[0] & 0xf0) | val; + break; + case 1: + val = (data->pwm_auto_point_hyst[0] & 0x0f) | (val << 4); + break; + case 2: + val = (data->pwm_auto_point_hyst[1] & 0xf0) | val; + break; + case 3: + val = (data->pwm_auto_point_hyst[1] & 0x0f) | (val << 4); + break; + } + if (nr == 0 || nr == 1) { + f71882fg_write8(data, F71882FG_REG_FAN_HYST0, val); + data->pwm_auto_point_hyst[0] = val; + } else { + f71882fg_write8(data, F71882FG_REG_FAN_HYST1, val); + data->pwm_auto_point_hyst[1] = val; + } + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_pwm_interpolate(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int result; + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + + result = (data->pwm_auto_point_mapping[nr] >> 4) & 1; + + return sprintf(buf, "%d\n", result); +} + +static ssize_t store_pwm_interpolate(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + /* struct f71882fg_data *data = dev_get_drvdata(dev); */ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + int val = simple_strtoul(buf, NULL, 10); + mutex_lock(&data->update_lock); + if (val) + val = data->pwm_auto_point_mapping[nr] | (1 << 4); + else + val = data->pwm_auto_point_mapping[nr] & (~(1 << 4)); + f71882fg_write8(data, F71882FG_REG_POINT_MAPPING(nr), val); + data->pwm_auto_point_mapping[nr] = val; + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_pwm_auto_point_channel(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + int result; + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + + result = 1 << ((data->pwm_auto_point_mapping[nr] & 3) - 1); + + return sprintf(buf, "%d\n", result); +} + +static ssize_t store_pwm_auto_point_channel(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + /* struct f71882fg_data *data = dev_get_drvdata(dev); */ + struct f71882fg_data *data = f71882fg_update_device(dev); + int nr = to_sensor_dev_attr_2(devattr)->index; + long val = simple_strtol(buf, NULL, 10); + switch (val) { + case 1: + val = 1; + break; + case 2: + val = 2; + break; + case 4: + val = 3; + break; + default: + return -EINVAL; + } + mutex_lock(&data->update_lock); + val = (data->pwm_auto_point_mapping[nr] & 0xfc) | val; + f71882fg_write8(data, F71882FG_REG_POINT_MAPPING(nr), val); + data->pwm_auto_point_mapping[nr] = val; + mutex_unlock(&data->update_lock); + + return count; +} + +static ssize_t show_pwm_auto_point_temp(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + int result; + struct f71882fg_data *data = f71882fg_update_device(dev); + int pwm = to_sensor_dev_attr_2(devattr)->index; + int point = to_sensor_dev_attr_2(devattr)->nr; + + result = data->pwm_auto_point_temp[pwm][point]; + return sprintf(buf, "%d\n", 1000 * result); +} + +static ssize_t store_pwm_auto_point_temp(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + /* struct f71882fg_data *data = dev_get_drvdata(dev); */ + struct f71882fg_data *data = f71882fg_update_device(dev); + int pwm = to_sensor_dev_attr_2(devattr)->index; + int point = to_sensor_dev_attr_2(devattr)->nr; + long val = simple_strtol(buf, NULL, 10) / 1000; + val = SENSORS_LIMIT(val, 0, 255); + + mutex_lock(&data->update_lock); + f71882fg_write8(data, F71882FG_REG_POINT_TEMP(pwm, point), val); + data->pwm_auto_point_temp[pwm][point] = val; + mutex_unlock(&data->update_lock); + + return count; +} + static ssize_t show_name(struct device *dev, struct device_attribute *devattr, char *buf) { -- cgit v1.2.3 From c13548c531ff40501aee1c1dd6f474c3c6adfcd5 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 7 Jan 2009 16:37:27 +0100 Subject: hwmon: (f71882fg) Style cleanups and put some repeating code into functions Various small cleanups as preparation for adding f71862fg support to the f71882fg driver. Signed-off-by: Hans de Goede Signed-off-by: Jean Delvare --- drivers/hwmon/f71882fg.c | 89 ++++++++++++++++++++++-------------------------- 1 file changed, 41 insertions(+), 48 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c index c04108735f3..593ed2a0555 100644 --- a/drivers/hwmon/f71882fg.c +++ b/drivers/hwmon/f71882fg.c @@ -1,6 +1,6 @@ /*************************************************************************** * Copyright (C) 2006 by Hans Edgington * - * Copyright (C) 2007 by Hans de Goede * + * Copyright (C) 2007,2008 by Hans de Goede * * * * 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 * @@ -228,11 +228,7 @@ static ssize_t show_name(struct device *dev, struct device_attribute *devattr, char *buf); static int __devinit f71882fg_probe(struct platform_device * pdev); -static int __devexit f71882fg_remove(struct platform_device *pdev); -static int __init f71882fg_init(void); -static int __init f71882fg_find(int sioaddr, unsigned short *address); -static int __init f71882fg_device_add(unsigned short address); -static void __exit f71882fg_exit(void); +static int f71882fg_remove(struct platform_device *pdev); static struct platform_driver f71882fg_driver = { .driver = { @@ -243,10 +239,7 @@ static struct platform_driver f71882fg_driver = { .remove = __devexit_p(f71882fg_remove), }; -static struct device_attribute f71882fg_dev_attr[] = -{ - __ATTR( name, S_IRUGO, show_name, NULL ), -}; +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); static struct sensor_device_attribute_2 f71882fg_in_temp_attr[] = { SENSOR_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0), @@ -1396,14 +1389,27 @@ static ssize_t show_name(struct device *dev, struct device_attribute *devattr, return sprintf(buf, DRVNAME "\n"); } +static int __devinit f71882fg_create_sysfs_files(struct platform_device *pdev, + struct sensor_device_attribute_2 *attr, int count) +{ + int err, i; + + for (i = 0; i < count; i++) { + err = device_create_file(&pdev->dev, &attr[i].dev_attr); + if (err) + return err; + } + return 0; +} -static int __devinit f71882fg_probe(struct platform_device * pdev) +static int __devinit f71882fg_probe(struct platform_device *pdev) { struct f71882fg_data *data; - int err, i; + int err; u8 start_reg; - if (!(data = kzalloc(sizeof(struct f71882fg_data), GFP_KERNEL))) + data = kzalloc(sizeof(struct f71882fg_data), GFP_KERNEL); + if (!data) return -ENOMEM; data->addr = platform_get_resource(pdev, IORESOURCE_IO, 0)->start; @@ -1411,65 +1417,50 @@ static int __devinit f71882fg_probe(struct platform_device * pdev) platform_set_drvdata(pdev, data); /* Register sysfs interface files */ - for (i = 0; i < ARRAY_SIZE(f71882fg_dev_attr); i++) { - err = device_create_file(&pdev->dev, &f71882fg_dev_attr[i]); - if (err) - goto exit_unregister_sysfs; - } + err = device_create_file(&pdev->dev, &dev_attr_name); + if (err) + goto exit_unregister_sysfs; start_reg = f71882fg_read8(data, F71882FG_REG_START); if (start_reg & 0x01) { - for (i = 0; i < ARRAY_SIZE(f71882fg_in_temp_attr); i++) { - err = device_create_file(&pdev->dev, - &f71882fg_in_temp_attr[i].dev_attr); - if (err) - goto exit_unregister_sysfs; - } + err = f71882fg_create_sysfs_files(pdev, f71882fg_in_temp_attr, + ARRAY_SIZE(f71882fg_in_temp_attr)); + if (err) + goto exit_unregister_sysfs; } if (start_reg & 0x02) { - for (i = 0; i < ARRAY_SIZE(f71882fg_fan_attr); i++) { - err = device_create_file(&pdev->dev, - &f71882fg_fan_attr[i].dev_attr); - if (err) - goto exit_unregister_sysfs; - } + err = f71882fg_create_sysfs_files(pdev, f71882fg_fan_attr, + ARRAY_SIZE(f71882fg_fan_attr)); + if (err) + goto exit_unregister_sysfs; } data->hwmon_dev = hwmon_device_register(&pdev->dev); if (IS_ERR(data->hwmon_dev)) { err = PTR_ERR(data->hwmon_dev); + data->hwmon_dev = NULL; goto exit_unregister_sysfs; } return 0; exit_unregister_sysfs: - for (i = 0; i < ARRAY_SIZE(f71882fg_dev_attr); i++) - device_remove_file(&pdev->dev, &f71882fg_dev_attr[i]); - - for (i = 0; i < ARRAY_SIZE(f71882fg_in_temp_attr); i++) - device_remove_file(&pdev->dev, - &f71882fg_in_temp_attr[i].dev_attr); - - for (i = 0; i < ARRAY_SIZE(f71882fg_fan_attr); i++) - device_remove_file(&pdev->dev, &f71882fg_fan_attr[i].dev_attr); - - kfree(data); + f71882fg_remove(pdev); /* Will unregister the sysfs files for us */ return err; } -static int __devexit f71882fg_remove(struct platform_device *pdev) +static int f71882fg_remove(struct platform_device *pdev) { int i; struct f71882fg_data *data = platform_get_drvdata(pdev); platform_set_drvdata(pdev, NULL); - hwmon_device_unregister(data->hwmon_dev); + if (data->hwmon_dev) + hwmon_device_unregister(data->hwmon_dev); - for (i = 0; i < ARRAY_SIZE(f71882fg_dev_attr); i++) - device_remove_file(&pdev->dev, &f71882fg_dev_attr[i]); + device_remove_file(&pdev->dev, &dev_attr_name); for (i = 0; i < ARRAY_SIZE(f71882fg_in_temp_attr); i++) device_remove_file(&pdev->dev, @@ -1577,10 +1568,12 @@ static int __init f71882fg_init(void) if (f71882fg_find(0x2e, &address) && f71882fg_find(0x4e, &address)) goto exit; - if ((err = platform_driver_register(&f71882fg_driver))) + err = platform_driver_register(&f71882fg_driver); + if (err) goto exit; - if ((err = f71882fg_device_add(address))) + err = f71882fg_device_add(address); + if (err) goto exit_driver; return 0; @@ -1598,7 +1591,7 @@ static void __exit f71882fg_exit(void) } MODULE_DESCRIPTION("F71882FG Hardware Monitoring Driver"); -MODULE_AUTHOR("Hans Edgington (hans@edgington.nl)"); +MODULE_AUTHOR("Hans Edgington, Hans de Goede (hdegoede@redhat.com)"); MODULE_LICENSE("GPL"); module_init(f71882fg_init); -- cgit v1.2.3 From 498be96834bf88a44db2f4a3115688c882e6f3e3 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 7 Jan 2009 16:37:28 +0100 Subject: hwmon: (f71882fg) Add support for the F71862FG superio sensors This patch adds support for the Fintek f71862fg superio monitoring functions to the f71882fg driver. This support has been tested without problems on a Jetway J9F2 by Tony McConnell. Signed-off-by: Hans de Goede Signed-off-by: Jean Delvare --- drivers/hwmon/f71882fg.c | 296 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 224 insertions(+), 72 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c index 593ed2a0555..cdd16d4966c 100644 --- a/drivers/hwmon/f71882fg.c +++ b/drivers/hwmon/f71882fg.c @@ -43,6 +43,7 @@ #define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */ #define SIO_FINTEK_ID 0x1934 /* Manufacturers ID */ +#define SIO_F71862_ID 0x0601 /* Chipset ID */ #define SIO_F71882_ID 0x0541 /* Chipset ID */ #define REGION_LENGTH 8 @@ -51,10 +52,10 @@ #define F71882FG_REG_PECI 0x0A -#define F71882FG_REG_IN_STATUS 0x12 -#define F71882FG_REG_IN_BEEP 0x13 +#define F71882FG_REG_IN_STATUS 0x12 /* f71882fg only */ +#define F71882FG_REG_IN_BEEP 0x13 /* f71882fg only */ #define F71882FG_REG_IN(nr) (0x20 + (nr)) -#define F71882FG_REG_IN1_HIGH 0x32 +#define F71882FG_REG_IN1_HIGH 0x32 /* f71882fg only */ #define F71882FG_REG_FAN(nr) (0xA0 + (16 * (nr))) #define F71882FG_REG_FAN_TARGET(nr) (0xA2 + (16 * (nr))) @@ -97,6 +98,13 @@ MODULE_PARM_DESC(fan_mode, "List of fan control modes (f71882fg only) " "(0=don't change, 1=pwm, 2=rpm)\n" "Note: this needs a write to pwm#_enable to take effect"); +enum chips { f71862fg, f71882fg }; + +static const char *f71882fg_names[] = { + "f71862fg", + "f71882fg", +}; + static struct platform_device *f71882fg_pdev; /* Super-I/O Function prototypes */ @@ -106,8 +114,13 @@ static inline void superio_enter(int base); static inline void superio_select(int base, int ld); static inline void superio_exit(int base); +struct f71882fg_sio_data { + enum chips type; +}; + struct f71882fg_data { unsigned short addr; + enum chips type; struct device *hwmon_dev; struct mutex update_lock; @@ -241,14 +254,9 @@ static struct platform_driver f71882fg_driver = { static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); -static struct sensor_device_attribute_2 f71882fg_in_temp_attr[] = { +static struct sensor_device_attribute_2 f718x2fg_in_temp_attr[] = { SENSOR_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0), SENSOR_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 0, 1), - SENSOR_ATTR_2(in1_max, S_IRUGO|S_IWUSR, show_in_max, store_in_max, - 0, 1), - SENSOR_ATTR_2(in1_beep, S_IRUGO|S_IWUSR, show_in_beep, store_in_beep, - 0, 1), - SENSOR_ATTR_2(in1_alarm, S_IRUGO, show_in_alarm, NULL, 0, 1), SENSOR_ATTR_2(in2_input, S_IRUGO, show_in, NULL, 0, 2), SENSOR_ATTR_2(in3_input, S_IRUGO, show_in, NULL, 0, 3), SENSOR_ATTR_2(in4_input, S_IRUGO, show_in, NULL, 0, 4), @@ -300,7 +308,15 @@ static struct sensor_device_attribute_2 f71882fg_in_temp_attr[] = { SENSOR_ATTR_2(temp3_fault, S_IRUGO, show_temp_fault, NULL, 0, 2), }; -static struct sensor_device_attribute_2 f71882fg_fan_attr[] = { +static struct sensor_device_attribute_2 f71882fg_in_temp_attr[] = { + SENSOR_ATTR_2(in1_max, S_IRUGO|S_IWUSR, show_in_max, store_in_max, + 0, 1), + SENSOR_ATTR_2(in1_beep, S_IRUGO|S_IWUSR, show_in_beep, store_in_beep, + 0, 1), + SENSOR_ATTR_2(in1_alarm, S_IRUGO, show_in_alarm, NULL, 0, 1), +}; + +static struct sensor_device_attribute_2 f718x2fg_fan_attr[] = { SENSOR_ATTR_2(fan1_input, S_IRUGO, show_fan, NULL, 0, 0), SENSOR_ATTR_2(fan1_full_speed, S_IRUGO|S_IWUSR, show_fan_full_speed, @@ -322,13 +338,6 @@ static struct sensor_device_attribute_2 f71882fg_fan_attr[] = { SENSOR_ATTR_2(fan3_beep, S_IRUGO|S_IWUSR, show_fan_beep, store_fan_beep, 0, 2), SENSOR_ATTR_2(fan3_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 2), - SENSOR_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 0, 3), - SENSOR_ATTR_2(fan4_full_speed, S_IRUGO|S_IWUSR, - show_fan_full_speed, - store_fan_full_speed, 0, 3), - SENSOR_ATTR_2(fan4_beep, S_IRUGO|S_IWUSR, show_fan_beep, - store_fan_beep, 0, 3), - SENSOR_ATTR_2(fan4_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 3), SENSOR_ATTR_2(pwm1, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 0), SENSOR_ATTR_2(pwm1_enable, S_IRUGO|S_IWUSR, show_pwm_enable, @@ -338,6 +347,75 @@ static struct sensor_device_attribute_2 f71882fg_fan_attr[] = { SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR, show_pwm_auto_point_channel, store_pwm_auto_point_channel, 0, 0), + + SENSOR_ATTR_2(pwm2, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 1), + SENSOR_ATTR_2(pwm2_enable, S_IRUGO|S_IWUSR, show_pwm_enable, + store_pwm_enable, 0, 1), + SENSOR_ATTR_2(pwm2_interpolate, S_IRUGO|S_IWUSR, + show_pwm_interpolate, store_pwm_interpolate, 0, 1), + SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 1), + + SENSOR_ATTR_2(pwm3, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 2), + SENSOR_ATTR_2(pwm3_enable, S_IRUGO|S_IWUSR, show_pwm_enable, + store_pwm_enable, 0, 2), + SENSOR_ATTR_2(pwm3_interpolate, S_IRUGO|S_IWUSR, + show_pwm_interpolate, store_pwm_interpolate, 0, 2), + SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 2), +}; + +static struct sensor_device_attribute_2 f71862fg_fan_attr[] = { + SENSOR_ATTR_2(pwm1_auto_point1_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 1, 0), + SENSOR_ATTR_2(pwm1_auto_point2_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 4, 0), + SENSOR_ATTR_2(pwm1_auto_point1_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 0, 0), + SENSOR_ATTR_2(pwm1_auto_point2_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 3, 0), + SENSOR_ATTR_2(pwm1_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp_hyst, + store_pwm_auto_point_temp_hyst, + 0, 0), + SENSOR_ATTR_2(pwm1_auto_point2_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 3, 0), + + SENSOR_ATTR_2(pwm2_auto_point1_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 1, 1), + SENSOR_ATTR_2(pwm2_auto_point2_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 4, 1), + SENSOR_ATTR_2(pwm2_auto_point1_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 0, 1), + SENSOR_ATTR_2(pwm2_auto_point2_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 3, 1), + SENSOR_ATTR_2(pwm2_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp_hyst, + store_pwm_auto_point_temp_hyst, + 0, 1), + SENSOR_ATTR_2(pwm2_auto_point2_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 3, 1), +}; + +static struct sensor_device_attribute_2 f71882fg_fan_attr[] = { + SENSOR_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 0, 3), + SENSOR_ATTR_2(fan4_full_speed, S_IRUGO|S_IWUSR, + show_fan_full_speed, + store_fan_full_speed, 0, 3), + SENSOR_ATTR_2(fan4_beep, S_IRUGO|S_IWUSR, show_fan_beep, + store_fan_beep, 0, 3), + SENSOR_ATTR_2(fan4_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 3), + SENSOR_ATTR_2(pwm1_auto_point1_pwm, S_IRUGO|S_IWUSR, show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 0, 0), @@ -376,14 +454,6 @@ static struct sensor_device_attribute_2 f71882fg_fan_attr[] = { SENSOR_ATTR_2(pwm1_auto_point4_temp_hyst, S_IRUGO, show_pwm_auto_point_temp_hyst, NULL, 3, 0), - SENSOR_ATTR_2(pwm2, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 1), - SENSOR_ATTR_2(pwm2_enable, S_IRUGO|S_IWUSR, show_pwm_enable, - store_pwm_enable, 0, 1), - SENSOR_ATTR_2(pwm2_interpolate, S_IRUGO|S_IWUSR, - show_pwm_interpolate, store_pwm_interpolate, 0, 1), - SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_channel, - store_pwm_auto_point_channel, 0, 1), SENSOR_ATTR_2(pwm2_auto_point1_pwm, S_IRUGO|S_IWUSR, show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 0, 1), @@ -422,14 +492,6 @@ static struct sensor_device_attribute_2 f71882fg_fan_attr[] = { SENSOR_ATTR_2(pwm2_auto_point4_temp_hyst, S_IRUGO, show_pwm_auto_point_temp_hyst, NULL, 3, 1), - SENSOR_ATTR_2(pwm3, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 2), - SENSOR_ATTR_2(pwm3_enable, S_IRUGO|S_IWUSR, show_pwm_enable, - store_pwm_enable, 0, 2), - SENSOR_ATTR_2(pwm3_interpolate, S_IRUGO|S_IWUSR, - show_pwm_interpolate, store_pwm_interpolate, 0, 2), - SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_channel, - store_pwm_auto_point_channel, 0, 2), SENSOR_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO|S_IWUSR, show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 0, 2), @@ -601,14 +663,19 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev) { struct f71882fg_data *data = dev_get_drvdata(dev); int nr, reg, reg2; + int nr_fans = (data->type == f71862fg) ? 3 : 4; mutex_lock(&data->update_lock); /* Update once every 60 seconds */ if ( time_after(jiffies, data->last_limits + 60 * HZ ) || !data->valid) { - data->in1_max = f71882fg_read8(data, F71882FG_REG_IN1_HIGH); - data->in_beep = f71882fg_read8(data, F71882FG_REG_IN_BEEP); + if (data->type == f71882fg) { + data->in1_max = + f71882fg_read8(data, F71882FG_REG_IN1_HIGH); + data->in_beep = + f71882fg_read8(data, F71882FG_REG_IN_BEEP); + } /* Get High & boundary temps*/ for (nr = 0; nr < 3; nr++) { @@ -649,24 +716,42 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev) F71882FG_REG_FAN_HYST0); data->pwm_auto_point_hyst[1] = f71882fg_read8(data, F71882FG_REG_FAN_HYST1); - for (nr = 0; nr < 4; nr++) { - int point; - + for (nr = 0; nr < nr_fans; nr++) { data->pwm_auto_point_mapping[nr] = f71882fg_read8(data, F71882FG_REG_POINT_MAPPING(nr)); - for (point = 0; point < 5; point++) { - data->pwm_auto_point_pwm[nr][point] = - f71882fg_read8(data, - F71882FG_REG_POINT_PWM - (nr, point)); - } - for (point = 0; point < 4; point++) { - data->pwm_auto_point_temp[nr][point] = - f71882fg_read8(data, - F71882FG_REG_POINT_TEMP - (nr, point)); + if (data->type == f71882fg) { + int point; + for (point = 0; point < 5; point++) { + data->pwm_auto_point_pwm[nr][point] = + f71882fg_read8(data, + F71882FG_REG_POINT_PWM + (nr, point)); + } + for (point = 0; point < 4; point++) { + data->pwm_auto_point_temp[nr][point] = + f71882fg_read8(data, + F71882FG_REG_POINT_TEMP + (nr, point)); + } + } else { + data->pwm_auto_point_pwm[nr][1] = + f71882fg_read8(data, + F71882FG_REG_POINT_PWM + (nr, 1)); + data->pwm_auto_point_pwm[nr][4] = + f71882fg_read8(data, + F71882FG_REG_POINT_PWM + (nr, 4)); + data->pwm_auto_point_temp[nr][0] = + f71882fg_read8(data, + F71882FG_REG_POINT_TEMP + (nr, 0)); + data->pwm_auto_point_temp[nr][3] = + f71882fg_read8(data, + F71882FG_REG_POINT_TEMP + (nr, 3)); } } data->last_limits = jiffies; @@ -684,7 +769,7 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev) data->fan_status = f71882fg_read8(data, F71882FG_REG_FAN_STATUS); - for (nr = 0; nr < 4; nr++) { + for (nr = 0; nr < nr_fans; nr++) { data->fan[nr] = f71882fg_read16(data, F71882FG_REG_FAN(nr)); data->fan_target[nr] = @@ -696,7 +781,8 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev) f71882fg_read8(data, F71882FG_REG_PWM(nr)); } - data->in_status = f71882fg_read8(data, + if (data->type == f71882fg) + data->in_status = f71882fg_read8(data, F71882FG_REG_IN_STATUS); for (nr = 0; nr < 9; nr++) data->in[nr] = f71882fg_read8(data, @@ -1144,13 +1230,15 @@ static ssize_t store_pwm_enable(struct device *dev, struct device_attribute data->pwm_enable &= ~(2 << (2 * nr)); break; /* Temperature ctrl */ } - switch (fan_mode[nr]) { - case 1: - data->pwm_enable |= 1 << (2 * nr); - break; /* Duty cycle mode */ - case 2: - data->pwm_enable &= ~(1 << (2 * nr)); - break; /* RPM mode */ + if (data->type == f71882fg) { + switch (fan_mode[nr]) { + case 1: + data->pwm_enable |= 1 << (2 * nr); + break; /* Duty cycle mode */ + case 2: + data->pwm_enable &= ~(1 << (2 * nr)); + break; /* RPM mode */ + } } f71882fg_write8(data, F71882FG_REG_PWM_ENABLE, data->pwm_enable); mutex_unlock(&data->update_lock); @@ -1386,7 +1474,8 @@ static ssize_t store_pwm_auto_point_temp(struct device *dev, static ssize_t show_name(struct device *dev, struct device_attribute *devattr, char *buf) { - return sprintf(buf, DRVNAME "\n"); + struct f71882fg_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%s\n", f71882fg_names[data->type]); } static int __devinit f71882fg_create_sysfs_files(struct platform_device *pdev, @@ -1405,6 +1494,7 @@ static int __devinit f71882fg_create_sysfs_files(struct platform_device *pdev, static int __devinit f71882fg_probe(struct platform_device *pdev) { struct f71882fg_data *data; + struct f71882fg_sio_data *sio_data = pdev->dev.platform_data; int err; u8 start_reg; @@ -1413,6 +1503,7 @@ static int __devinit f71882fg_probe(struct platform_device *pdev) return -ENOMEM; data->addr = platform_get_resource(pdev, IORESOURCE_IO, 0)->start; + data->type = sio_data->type; mutex_init(&data->update_lock); platform_set_drvdata(pdev, data); @@ -1423,15 +1514,35 @@ static int __devinit f71882fg_probe(struct platform_device *pdev) start_reg = f71882fg_read8(data, F71882FG_REG_START); if (start_reg & 0x01) { - err = f71882fg_create_sysfs_files(pdev, f71882fg_in_temp_attr, - ARRAY_SIZE(f71882fg_in_temp_attr)); + err = f71882fg_create_sysfs_files(pdev, f718x2fg_in_temp_attr, + ARRAY_SIZE(f718x2fg_in_temp_attr)); if (err) goto exit_unregister_sysfs; + + if (data->type == f71882fg) { + err = f71882fg_create_sysfs_files(pdev, + f71882fg_in_temp_attr, + ARRAY_SIZE(f71882fg_in_temp_attr)); + if (err) + goto exit_unregister_sysfs; + } } if (start_reg & 0x02) { - err = f71882fg_create_sysfs_files(pdev, f71882fg_fan_attr, + err = f71882fg_create_sysfs_files(pdev, f718x2fg_fan_attr, + ARRAY_SIZE(f718x2fg_fan_attr)); + if (err) + goto exit_unregister_sysfs; + + if (data->type == f71862fg) { + err = f71882fg_create_sysfs_files(pdev, + f71862fg_fan_attr, + ARRAY_SIZE(f71862fg_fan_attr)); + } else { + err = f71882fg_create_sysfs_files(pdev, + f71882fg_fan_attr, ARRAY_SIZE(f71882fg_fan_attr)); + } if (err) goto exit_unregister_sysfs; } @@ -1462,10 +1573,20 @@ static int f71882fg_remove(struct platform_device *pdev) device_remove_file(&pdev->dev, &dev_attr_name); + for (i = 0; i < ARRAY_SIZE(f718x2fg_in_temp_attr); i++) + device_remove_file(&pdev->dev, + &f718x2fg_in_temp_attr[i].dev_attr); + for (i = 0; i < ARRAY_SIZE(f71882fg_in_temp_attr); i++) device_remove_file(&pdev->dev, &f71882fg_in_temp_attr[i].dev_attr); + for (i = 0; i < ARRAY_SIZE(f718x2fg_fan_attr); i++) + device_remove_file(&pdev->dev, &f718x2fg_fan_attr[i].dev_attr); + + for (i = 0; i < ARRAY_SIZE(f71862fg_fan_attr); i++) + device_remove_file(&pdev->dev, &f71862fg_fan_attr[i].dev_attr); + for (i = 0; i < ARRAY_SIZE(f71882fg_fan_attr); i++) device_remove_file(&pdev->dev, &f71882fg_fan_attr[i].dev_attr); @@ -1474,11 +1595,12 @@ static int f71882fg_remove(struct platform_device *pdev) return 0; } -static int __init f71882fg_find(int sioaddr, unsigned short *address) +static int __init f71882fg_find(int sioaddr, unsigned short *address, + struct f71882fg_sio_data *sio_data) { int err = -ENODEV; u16 devid; - u8 start_reg; + u8 reg; struct f71882fg_data data; superio_enter(sioaddr); @@ -1490,7 +1612,14 @@ static int __init f71882fg_find(int sioaddr, unsigned short *address) } devid = force_id ? force_id : superio_inw(sioaddr, SIO_REG_DEVID); - if (devid != SIO_F71882_ID) { + switch (devid) { + case SIO_F71862_ID: + sio_data->type = f71862fg; + break; + case SIO_F71882_ID: + sio_data->type = f71882fg; + break; + default: printk(KERN_INFO DRVNAME ": Unsupported Fintek device\n"); goto exit; } @@ -1510,23 +1639,35 @@ static int __init f71882fg_find(int sioaddr, unsigned short *address) *address &= ~(REGION_LENGTH - 1); /* Ignore 3 LSB */ data.addr = *address; - start_reg = f71882fg_read8(&data, F71882FG_REG_START); - if (!(start_reg & 0x03)) { + reg = f71882fg_read8(&data, F71882FG_REG_START); + if (!(reg & 0x03)) { printk(KERN_WARNING DRVNAME ": Hardware monitoring not activated\n"); goto exit; } + /* If it is a 71862 and the fan / pwm part is enabled sanity check + the pwm settings */ + if (sio_data->type == f71862fg && (reg & 0x02)) { + reg = f71882fg_read8(&data, F71882FG_REG_PWM_ENABLE); + if ((reg & 0x15) != 0x15) { + printk(KERN_ERR DRVNAME + ": Invalid (reserved) pwm settings: 0x%02x\n", + (unsigned int)reg); + goto exit; + } + } err = 0; - printk(KERN_INFO DRVNAME ": Found F71882FG chip at %#x, revision %d\n", - (unsigned int)*address, + printk(KERN_INFO DRVNAME ": Found %s chip at %#x, revision %d\n", + f71882fg_names[sio_data->type], (unsigned int)*address, (int)superio_inb(sioaddr, SIO_REG_DEVREV)); exit: superio_exit(sioaddr); return err; } -static int __init f71882fg_device_add(unsigned short address) +static int __init f71882fg_device_add(unsigned short address, + const struct f71882fg_sio_data *sio_data) { struct resource res = { .start = address, @@ -1546,6 +1687,13 @@ static int __init f71882fg_device_add(unsigned short address) goto exit_device_put; } + err = platform_device_add_data(f71882fg_pdev, sio_data, + sizeof(struct f71882fg_sio_data)); + if (err) { + printk(KERN_ERR DRVNAME ": Platform data allocation failed\n"); + goto exit_device_put; + } + err = platform_device_add(f71882fg_pdev); if (err) { printk(KERN_ERR DRVNAME ": Device addition failed\n"); @@ -1564,15 +1712,19 @@ static int __init f71882fg_init(void) { int err = -ENODEV; unsigned short address; + struct f71882fg_sio_data sio_data; + + memset(&sio_data, 0, sizeof(sio_data)); - if (f71882fg_find(0x2e, &address) && f71882fg_find(0x4e, &address)) + if (f71882fg_find(0x2e, &address, &sio_data) && + f71882fg_find(0x4e, &address, &sio_data)) goto exit; err = platform_driver_register(&f71882fg_driver); if (err) goto exit; - err = f71882fg_device_add(address); + err = f71882fg_device_add(address, &sio_data); if (err) goto exit_driver; -- cgit v1.2.3 From 3cc74758a667c5ad46fa5d6810ce701095370d35 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 7 Jan 2009 16:37:28 +0100 Subject: hwmon: (f71882fg) Move some io access from the detect to the probe function The f71882fg driver did some io to ioports it hadn't reserved yet in its find (detect) function, this patches moves this io to the probe function where these ports are reserved and this io belongs. Signed-off-by: Hans de Goede Signed-off-by: Jean Delvare --- drivers/hwmon/f71882fg.c | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c index cdd16d4966c..1cfd2231677 100644 --- a/drivers/hwmon/f71882fg.c +++ b/drivers/hwmon/f71882fg.c @@ -1507,12 +1507,31 @@ static int __devinit f71882fg_probe(struct platform_device *pdev) mutex_init(&data->update_lock); platform_set_drvdata(pdev, data); + start_reg = f71882fg_read8(data, F71882FG_REG_START); + if (!(start_reg & 0x03)) { + dev_warn(&pdev->dev, "Hardware monitoring not activated\n"); + err = -ENODEV; + goto exit_free; + } + + /* If it is a 71862 and the fan / pwm part is enabled sanity check + the pwm settings */ + if (data->type == f71862fg && (start_reg & 0x02)) { + u8 reg = f71882fg_read8(data, F71882FG_REG_PWM_ENABLE); + if ((reg & 0x15) != 0x15) { + dev_err(&pdev->dev, + "Invalid (reserved) pwm settings: 0x%02x\n", + (unsigned int)reg); + err = -ENODEV; + goto exit_free; + } + } + /* Register sysfs interface files */ err = device_create_file(&pdev->dev, &dev_attr_name); if (err) goto exit_unregister_sysfs; - start_reg = f71882fg_read8(data, F71882FG_REG_START); if (start_reg & 0x01) { err = f71882fg_create_sysfs_files(pdev, f718x2fg_in_temp_attr, ARRAY_SIZE(f718x2fg_in_temp_attr)); @@ -1558,7 +1577,9 @@ static int __devinit f71882fg_probe(struct platform_device *pdev) exit_unregister_sysfs: f71882fg_remove(pdev); /* Will unregister the sysfs files for us */ - + return err; /* f71882fg_remove() also frees our data */ +exit_free: + kfree(data); return err; } @@ -1600,8 +1621,6 @@ static int __init f71882fg_find(int sioaddr, unsigned short *address, { int err = -ENODEV; u16 devid; - u8 reg; - struct f71882fg_data data; superio_enter(sioaddr); @@ -1638,25 +1657,6 @@ static int __init f71882fg_find(int sioaddr, unsigned short *address, } *address &= ~(REGION_LENGTH - 1); /* Ignore 3 LSB */ - data.addr = *address; - reg = f71882fg_read8(&data, F71882FG_REG_START); - if (!(reg & 0x03)) { - printk(KERN_WARNING DRVNAME - ": Hardware monitoring not activated\n"); - goto exit; - } - - /* If it is a 71862 and the fan / pwm part is enabled sanity check - the pwm settings */ - if (sio_data->type == f71862fg && (reg & 0x02)) { - reg = f71882fg_read8(&data, F71882FG_REG_PWM_ENABLE); - if ((reg & 0x15) != 0x15) { - printk(KERN_ERR DRVNAME - ": Invalid (reserved) pwm settings: 0x%02x\n", - (unsigned int)reg); - goto exit; - } - } err = 0; printk(KERN_INFO DRVNAME ": Found %s chip at %#x, revision %d\n", f71882fg_names[sio_data->type], (unsigned int)*address, -- cgit v1.2.3 From 7567a0435520fe61420ff2cdc4cec1b5399a5134 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 7 Jan 2009 16:37:28 +0100 Subject: hwmon: (f71882fg) Prepare for adding F8000 support This patch is a preparation patch for adding F8000 support to the f71882fg driver. If you look at the register addresses and esp, the bits used for the temperature channels, then you will notice that it appears that they start at 1 in a system meant to start at 0. As the F8000 actually uses the 0 addresses and bits, this patch changes the f71882fg driver to take 4 temperatures numbered 0-3 in to account, using 1-3 in this new scheme for the temperatures actually present in the F718x2FG. Signed-off-by: Hans de Goede Signed-off-by: Jean Delvare --- drivers/hwmon/f71882fg.c | 117 ++++++++++++++++++++++++----------------------- 1 file changed, 60 insertions(+), 57 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c index 1cfd2231677..2604c6d7ea5 100644 --- a/drivers/hwmon/f71882fg.c +++ b/drivers/hwmon/f71882fg.c @@ -63,9 +63,9 @@ #define F71882FG_REG_FAN_STATUS 0x92 #define F71882FG_REG_FAN_BEEP 0x93 -#define F71882FG_REG_TEMP(nr) (0x72 + 2 * (nr)) -#define F71882FG_REG_TEMP_OVT(nr) (0x82 + 2 * (nr)) -#define F71882FG_REG_TEMP_HIGH(nr) (0x83 + 2 * (nr)) +#define F71882FG_REG_TEMP(nr) (0x70 + 2 * (nr)) +#define F71882FG_REG_TEMP_OVT(nr) (0x80 + 2 * (nr)) +#define F71882FG_REG_TEMP_HIGH(nr) (0x81 + 2 * (nr)) #define F71882FG_REG_TEMP_STATUS 0x62 #define F71882FG_REG_TEMP_BEEP 0x63 #define F71882FG_REG_TEMP_HYST1 0x6C @@ -138,11 +138,14 @@ struct f71882fg_data { u16 fan_full_speed[4]; u8 fan_status; u8 fan_beep; - u8 temp[3]; - u8 temp_ovt[3]; - u8 temp_high[3]; - u8 temp_hyst[3]; - u8 temp_type[3]; + /* Note: all models have only 3 temperature channels, but on some + they are addressed as 0-2 and on others as 1-3, so for coding + convenience we reserve space for 4 channels */ + u8 temp[4]; + u8 temp_ovt[4]; + u8 temp_high[4]; + u8 temp_hyst[4]; + u8 temp_type[4]; u8 temp_status; u8 temp_beep; u8 temp_diode_open; @@ -264,48 +267,48 @@ static struct sensor_device_attribute_2 f718x2fg_in_temp_attr[] = { SENSOR_ATTR_2(in6_input, S_IRUGO, show_in, NULL, 0, 6), SENSOR_ATTR_2(in7_input, S_IRUGO, show_in, NULL, 0, 7), SENSOR_ATTR_2(in8_input, S_IRUGO, show_in, NULL, 0, 8), - SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0), + SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 1), SENSOR_ATTR_2(temp1_max, S_IRUGO|S_IWUSR, show_temp_max, - store_temp_max, 0, 0), + store_temp_max, 0, 1), SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst, - store_temp_max_hyst, 0, 0), + store_temp_max_hyst, 0, 1), SENSOR_ATTR_2(temp1_crit, S_IRUGO|S_IWUSR, show_temp_crit, - store_temp_crit, 0, 0), + store_temp_crit, 0, 1), SENSOR_ATTR_2(temp1_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, - 0, 0), - SENSOR_ATTR_2(temp1_type, S_IRUGO, show_temp_type, NULL, 0, 0), + 0, 1), + SENSOR_ATTR_2(temp1_type, S_IRUGO, show_temp_type, NULL, 0, 1), SENSOR_ATTR_2(temp1_beep, S_IRUGO|S_IWUSR, show_temp_beep, - store_temp_beep, 0, 0), - SENSOR_ATTR_2(temp1_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 0), - SENSOR_ATTR_2(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0, 0), - SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0, 1), + store_temp_beep, 0, 1), + SENSOR_ATTR_2(temp1_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 1), + SENSOR_ATTR_2(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0, 1), + SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0, 2), SENSOR_ATTR_2(temp2_max, S_IRUGO|S_IWUSR, show_temp_max, - store_temp_max, 0, 1), + store_temp_max, 0, 2), SENSOR_ATTR_2(temp2_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst, - store_temp_max_hyst, 0, 1), + store_temp_max_hyst, 0, 2), SENSOR_ATTR_2(temp2_crit, S_IRUGO|S_IWUSR, show_temp_crit, - store_temp_crit, 0, 1), + store_temp_crit, 0, 2), SENSOR_ATTR_2(temp2_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, - 0, 1), - SENSOR_ATTR_2(temp2_type, S_IRUGO, show_temp_type, NULL, 0, 1), + 0, 2), + SENSOR_ATTR_2(temp2_type, S_IRUGO, show_temp_type, NULL, 0, 2), SENSOR_ATTR_2(temp2_beep, S_IRUGO|S_IWUSR, show_temp_beep, - store_temp_beep, 0, 1), - SENSOR_ATTR_2(temp2_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 1), - SENSOR_ATTR_2(temp2_fault, S_IRUGO, show_temp_fault, NULL, 0, 1), - SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0, 2), + store_temp_beep, 0, 2), + SENSOR_ATTR_2(temp2_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 2), + SENSOR_ATTR_2(temp2_fault, S_IRUGO, show_temp_fault, NULL, 0, 2), + SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0, 3), SENSOR_ATTR_2(temp3_max, S_IRUGO|S_IWUSR, show_temp_max, - store_temp_max, 0, 2), + store_temp_max, 0, 3), SENSOR_ATTR_2(temp3_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst, - store_temp_max_hyst, 0, 2), + store_temp_max_hyst, 0, 3), SENSOR_ATTR_2(temp3_crit, S_IRUGO|S_IWUSR, show_temp_crit, - store_temp_crit, 0, 2), + store_temp_crit, 0, 3), SENSOR_ATTR_2(temp3_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, - 0, 2), - SENSOR_ATTR_2(temp3_type, S_IRUGO, show_temp_type, NULL, 0, 2), + 0, 3), + SENSOR_ATTR_2(temp3_type, S_IRUGO, show_temp_type, NULL, 0, 3), SENSOR_ATTR_2(temp3_beep, S_IRUGO|S_IWUSR, show_temp_beep, - store_temp_beep, 0, 2), - SENSOR_ATTR_2(temp3_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 2), - SENSOR_ATTR_2(temp3_fault, S_IRUGO, show_temp_fault, NULL, 0, 2), + store_temp_beep, 0, 3), + SENSOR_ATTR_2(temp3_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 3), + SENSOR_ATTR_2(temp3_fault, S_IRUGO, show_temp_fault, NULL, 0, 3), }; static struct sensor_device_attribute_2 f71882fg_in_temp_attr[] = { @@ -678,7 +681,7 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev) } /* Get High & boundary temps*/ - for (nr = 0; nr < 3; nr++) { + for (nr = 1; nr < 4; nr++) { data->temp_ovt[nr] = f71882fg_read8(data, F71882FG_REG_TEMP_OVT(nr)); data->temp_high[nr] = f71882fg_read8(data, @@ -686,25 +689,25 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev) } /* Have to hardcode hyst*/ - data->temp_hyst[0] = f71882fg_read8(data, + data->temp_hyst[1] = f71882fg_read8(data, F71882FG_REG_TEMP_HYST1) >> 4; /* Hyst temps 2 & 3 stored in same register */ reg = f71882fg_read8(data, F71882FG_REG_TEMP_HYST23); - data->temp_hyst[1] = reg & 0x0F; - data->temp_hyst[2] = reg >> 4; + data->temp_hyst[2] = reg & 0x0F; + data->temp_hyst[3] = reg >> 4; /* Have to hardcode type, because temp1 is special */ reg = f71882fg_read8(data, F71882FG_REG_TEMP_TYPE); reg2 = f71882fg_read8(data, F71882FG_REG_PECI); if ((reg2 & 0x03) == 0x01) - data->temp_type[0] = 6 /* PECI */; + data->temp_type[1] = 6 /* PECI */; else if ((reg2 & 0x03) == 0x02) - data->temp_type[0] = 5 /* AMDSI */; + data->temp_type[1] = 5 /* AMDSI */; else - data->temp_type[0] = (reg & 0x02) ? 2 : 4; + data->temp_type[1] = (reg & 0x02) ? 2 : 4; - data->temp_type[1] = (reg & 0x04) ? 2 : 4; - data->temp_type[2] = (reg & 0x08) ? 2 : 4; + data->temp_type[2] = (reg & 0x04) ? 2 : 4; + data->temp_type[3] = (reg & 0x08) ? 2 : 4; data->temp_beep = f71882fg_read8(data, F71882FG_REG_TEMP_BEEP); @@ -763,7 +766,7 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev) F71882FG_REG_TEMP_STATUS); data->temp_diode_open = f71882fg_read8(data, F71882FG_REG_TEMP_DIODE_OPEN); - for (nr = 0; nr < 3; nr++) + for (nr = 1; nr < 4; nr++) data->temp[nr] = f71882fg_read8(data, F71882FG_REG_TEMP(nr)); @@ -1032,19 +1035,19 @@ static ssize_t store_temp_max_hyst(struct device *dev, struct device_attribute /* convert value to register contents */ switch (nr) { - case 0: - val = val << 4; - break; case 1: - val = val | (data->temp_hyst[2] << 4); + val = val << 4; break; case 2: - val = data->temp_hyst[1] | (val << 4); + val = val | (data->temp_hyst[3] << 4); + break; + case 3: + val = data->temp_hyst[2] | (val << 4); break; } - f71882fg_write8(data, nr ? F71882FG_REG_TEMP_HYST23 : - F71882FG_REG_TEMP_HYST1, val); + f71882fg_write8(data, (nr <= 1) ? F71882FG_REG_TEMP_HYST1 : + F71882FG_REG_TEMP_HYST23, val); store_temp_max_hyst_exit: mutex_unlock(&data->update_lock); @@ -1103,7 +1106,7 @@ static ssize_t show_temp_beep(struct device *dev, struct device_attribute struct f71882fg_data *data = f71882fg_update_device(dev); int nr = to_sensor_dev_attr_2(devattr)->index; - if (data->temp_beep & (1 << (nr + 1))) + if (data->temp_beep & (1 << nr)) return sprintf(buf, "1\n"); else return sprintf(buf, "0\n"); @@ -1118,9 +1121,9 @@ static ssize_t store_temp_beep(struct device *dev, struct device_attribute mutex_lock(&data->update_lock); if (val) - data->temp_beep |= 1 << (nr + 1); + data->temp_beep |= 1 << nr; else - data->temp_beep &= ~(1 << (nr + 1)); + data->temp_beep &= ~(1 << nr); f71882fg_write8(data, F71882FG_REG_TEMP_BEEP, data->temp_beep); mutex_unlock(&data->update_lock); @@ -1134,7 +1137,7 @@ static ssize_t show_temp_alarm(struct device *dev, struct device_attribute struct f71882fg_data *data = f71882fg_update_device(dev); int nr = to_sensor_dev_attr_2(devattr)->index; - if (data->temp_status & (1 << (nr + 1))) + if (data->temp_status & (1 << nr)) return sprintf(buf, "1\n"); else return sprintf(buf, "0\n"); @@ -1146,7 +1149,7 @@ static ssize_t show_temp_fault(struct device *dev, struct device_attribute struct f71882fg_data *data = f71882fg_update_device(dev); int nr = to_sensor_dev_attr_2(devattr)->index; - if (data->temp_diode_open & (1 << (nr + 1))) + if (data->temp_diode_open & (1 << nr)) return sprintf(buf, "1\n"); else return sprintf(buf, "0\n"); -- cgit v1.2.3 From ce0bfa5ee25ddbe4072b16054e809f552bf72320 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 7 Jan 2009 16:37:28 +0100 Subject: hwmon: (f71882fg) Fix various sysfs callback function issues While working on adding F8000 support I noticed that various of the store sysfs functions (and a few of the show also) had issues. This patch fixes the following issues in these functions: * store: storing the result of strto[u]l in an int, resulting in a possible overflow before boundary checking * store: use of f71882fg_update_device(), we don't want to read the whole device in store functions, just the registers we need * store: use of cached register values instead of reading the needed regs in the store function, including cases where f71882fg_update_device() was not used, this could cause real isues * show: shown value is a calculation of 2 or more cached register reads, without locking the data struct. Signed-off-by: Hans de Goede Signed-off-by: Jean Delvare --- drivers/hwmon/f71882fg.c | 122 ++++++++++++++++++++++++++++------------------- 1 file changed, 73 insertions(+), 49 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c index 2604c6d7ea5..345f465aa28 100644 --- a/drivers/hwmon/f71882fg.c +++ b/drivers/hwmon/f71882fg.c @@ -835,6 +835,7 @@ static ssize_t store_fan_full_speed(struct device *dev, val = fan_to_reg(val); mutex_lock(&data->update_lock); + data->pwm_enable = f71882fg_read8(data, F71882FG_REG_PWM_ENABLE); if (data->pwm_enable & (1 << (2 * nr))) /* PWM mode */ count = -EINVAL; @@ -865,9 +866,10 @@ static ssize_t store_fan_beep(struct device *dev, struct device_attribute { struct f71882fg_data *data = dev_get_drvdata(dev); int nr = to_sensor_dev_attr_2(devattr)->index; - int val = simple_strtoul(buf, NULL, 10); + unsigned long val = simple_strtoul(buf, NULL, 10); mutex_lock(&data->update_lock); + data->fan_beep = f71882fg_read8(data, F71882FG_REG_FAN_BEEP); if (val) data->fan_beep |= 1 << nr; else @@ -912,10 +914,8 @@ static ssize_t store_in_max(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { struct f71882fg_data *data = dev_get_drvdata(dev); - int val = simple_strtoul(buf, NULL, 10) / 8; - - if (val > 255) - val = 255; + long val = simple_strtol(buf, NULL, 10) / 8; + val = SENSORS_LIMIT(val, 0, 255); mutex_lock(&data->update_lock); f71882fg_write8(data, F71882FG_REG_IN1_HIGH, val); @@ -942,9 +942,10 @@ static ssize_t store_in_beep(struct device *dev, struct device_attribute { struct f71882fg_data *data = dev_get_drvdata(dev); int nr = to_sensor_dev_attr_2(devattr)->index; - int val = simple_strtoul(buf, NULL, 10); + unsigned long val = simple_strtoul(buf, NULL, 10); mutex_lock(&data->update_lock); + data->in_beep = f71882fg_read8(data, F71882FG_REG_IN_BEEP); if (val) data->in_beep |= 1 << nr; else @@ -991,10 +992,8 @@ static ssize_t store_temp_max(struct device *dev, struct device_attribute { struct f71882fg_data *data = dev_get_drvdata(dev); int nr = to_sensor_dev_attr_2(devattr)->index; - int val = simple_strtoul(buf, NULL, 10) / 1000; - - if (val > 255) - val = 255; + long val = simple_strtol(buf, NULL, 10) / 1000; + val = SENSORS_LIMIT(val, 0, 255); mutex_lock(&data->update_lock); f71882fg_write8(data, F71882FG_REG_TEMP_HIGH(nr), val); @@ -1009,9 +1008,13 @@ static ssize_t show_temp_max_hyst(struct device *dev, struct device_attribute { struct f71882fg_data *data = f71882fg_update_device(dev); int nr = to_sensor_dev_attr_2(devattr)->index; + int temp_max_hyst; - return sprintf(buf, "%d\n", - (data->temp_high[nr] - data->temp_hyst[nr]) * 1000); + mutex_lock(&data->update_lock); + temp_max_hyst = (data->temp_high[nr] - data->temp_hyst[nr]) * 1000; + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%d\n", temp_max_hyst); } static ssize_t store_temp_max_hyst(struct device *dev, struct device_attribute @@ -1019,37 +1022,38 @@ static ssize_t store_temp_max_hyst(struct device *dev, struct device_attribute { struct f71882fg_data *data = dev_get_drvdata(dev); int nr = to_sensor_dev_attr_2(devattr)->index; - int val = simple_strtoul(buf, NULL, 10) / 1000; + long val = simple_strtol(buf, NULL, 10) / 1000; ssize_t ret = count; + u8 reg; mutex_lock(&data->update_lock); /* convert abs to relative and check */ + data->temp_high[nr] = f71882fg_read8(data, F71882FG_REG_TEMP_HIGH(nr)); + val = SENSORS_LIMIT(val, data->temp_high[nr] - 15, + data->temp_high[nr]); val = data->temp_high[nr] - val; - if (val < 0 || val > 15) { - ret = -EINVAL; - goto store_temp_max_hyst_exit; - } - data->temp_hyst[nr] = val; /* convert value to register contents */ switch (nr) { case 1: - val = val << 4; + reg = f71882fg_read8(data, F71882FG_REG_TEMP_HYST1); + reg = (reg & 0x0f) | (val << 4); break; case 2: - val = val | (data->temp_hyst[3] << 4); + reg = f71882fg_read8(data, F71882FG_REG_TEMP_HYST23); + reg = (reg & 0xf0) | val; break; case 3: - val = data->temp_hyst[2] | (val << 4); + reg = f71882fg_read8(data, F71882FG_REG_TEMP_HYST23); + reg = (reg & 0x0f) | (val << 4); break; } f71882fg_write8(data, (nr <= 1) ? F71882FG_REG_TEMP_HYST1 : - F71882FG_REG_TEMP_HYST23, val); + F71882FG_REG_TEMP_HYST23, reg); -store_temp_max_hyst_exit: mutex_unlock(&data->update_lock); return ret; } @@ -1068,10 +1072,8 @@ static ssize_t store_temp_crit(struct device *dev, struct device_attribute { struct f71882fg_data *data = dev_get_drvdata(dev); int nr = to_sensor_dev_attr_2(devattr)->index; - int val = simple_strtoul(buf, NULL, 10) / 1000; - - if (val > 255) - val = 255; + long val = simple_strtol(buf, NULL, 10) / 1000; + val = SENSORS_LIMIT(val, 0, 255); mutex_lock(&data->update_lock); f71882fg_write8(data, F71882FG_REG_TEMP_OVT(nr), val); @@ -1086,9 +1088,13 @@ static ssize_t show_temp_crit_hyst(struct device *dev, struct device_attribute { struct f71882fg_data *data = f71882fg_update_device(dev); int nr = to_sensor_dev_attr_2(devattr)->index; + int temp_crit_hyst; - return sprintf(buf, "%d\n", - (data->temp_ovt[nr] - data->temp_hyst[nr]) * 1000); + mutex_lock(&data->update_lock); + temp_crit_hyst = (data->temp_ovt[nr] - data->temp_hyst[nr]) * 1000; + mutex_unlock(&data->update_lock); + + return sprintf(buf, "%d\n", temp_crit_hyst); } static ssize_t show_temp_type(struct device *dev, struct device_attribute @@ -1117,9 +1123,10 @@ static ssize_t store_temp_beep(struct device *dev, struct device_attribute { struct f71882fg_data *data = dev_get_drvdata(dev); int nr = to_sensor_dev_attr_2(devattr)->index; - int val = simple_strtoul(buf, NULL, 10); + unsigned long val = simple_strtoul(buf, NULL, 10); mutex_lock(&data->update_lock); + data->temp_beep = f71882fg_read8(data, F71882FG_REG_TEMP_BEEP); if (val) data->temp_beep |= 1 << nr; else @@ -1160,16 +1167,16 @@ static ssize_t show_pwm(struct device *dev, { struct f71882fg_data *data = f71882fg_update_device(dev); int val, nr = to_sensor_dev_attr_2(devattr)->index; + mutex_lock(&data->update_lock); if (data->pwm_enable & (1 << (2 * nr))) /* PWM mode */ val = data->pwm[nr]; else { /* RPM mode */ - mutex_lock(&data->update_lock); val = 255 * fan_from_reg(data->fan_target[nr]) / fan_from_reg(data->fan_full_speed[nr]); - mutex_unlock(&data->update_lock); } + mutex_unlock(&data->update_lock); return sprintf(buf, "%d\n", val); } @@ -1177,23 +1184,26 @@ static ssize_t store_pwm(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { - /* struct f71882fg_data *data = dev_get_drvdata(dev); */ - struct f71882fg_data *data = f71882fg_update_device(dev); + struct f71882fg_data *data = dev_get_drvdata(dev); int nr = to_sensor_dev_attr_2(devattr)->index; long val = simple_strtol(buf, NULL, 10); val = SENSORS_LIMIT(val, 0, 255); mutex_lock(&data->update_lock); + data->pwm_enable = f71882fg_read8(data, F71882FG_REG_PWM_ENABLE); if (data->pwm_enable & (1 << (2 * nr))) { /* PWM mode */ f71882fg_write8(data, F71882FG_REG_PWM(nr), val); data->pwm[nr] = val; } else { /* RPM mode */ - int target = val * fan_from_reg(data->fan_full_speed[nr]) / 255; - f71882fg_write16(data, F71882FG_REG_FAN_TARGET(nr), - fan_to_reg(target)); - data->fan_target[nr] = fan_to_reg(target); + int target, full_speed; + full_speed = f71882fg_read16(data, + F71882FG_REG_FAN_FULL_SPEED(nr)); + target = fan_to_reg(val * fan_from_reg(full_speed) / 255); + f71882fg_write16(data, F71882FG_REG_FAN_TARGET(nr), target); + data->fan_target[nr] = target; + data->fan_full_speed[nr] = full_speed; } mutex_unlock(&data->update_lock); @@ -1225,6 +1235,7 @@ static ssize_t store_pwm_enable(struct device *dev, struct device_attribute return -EINVAL; mutex_lock(&data->update_lock); + data->pwm_enable = f71882fg_read8(data, F71882FG_REG_PWM_ENABLE); switch (val) { case 1: data->pwm_enable |= 2 << (2 * nr); @@ -1258,6 +1269,7 @@ static ssize_t show_pwm_auto_point_pwm(struct device *dev, int pwm = to_sensor_dev_attr_2(devattr)->index; int point = to_sensor_dev_attr_2(devattr)->nr; + mutex_lock(&data->update_lock); if (data->pwm_enable & (1 << (2 * pwm))) { /* PWM mode */ result = data->pwm_auto_point_pwm[pwm][point]; @@ -1265,6 +1277,7 @@ static ssize_t show_pwm_auto_point_pwm(struct device *dev, /* RPM mode */ result = 32 * 255 / (32 + data->pwm_auto_point_pwm[pwm][point]); } + mutex_unlock(&data->update_lock); return sprintf(buf, "%d\n", result); } @@ -1273,14 +1286,14 @@ static ssize_t store_pwm_auto_point_pwm(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { - /* struct f71882fg_data *data = dev_get_drvdata(dev); */ - struct f71882fg_data *data = f71882fg_update_device(dev); + struct f71882fg_data *data = dev_get_drvdata(dev); int pwm = to_sensor_dev_attr_2(devattr)->index; int point = to_sensor_dev_attr_2(devattr)->nr; - int val = simple_strtoul(buf, NULL, 10); + long val = simple_strtol(buf, NULL, 10); val = SENSORS_LIMIT(val, 0, 255); mutex_lock(&data->update_lock); + data->pwm_enable = f71882fg_read8(data, F71882FG_REG_PWM_ENABLE); if (data->pwm_enable & (1 << (2 * pwm))) { /* PWM mode */ } else { @@ -1331,16 +1344,25 @@ static ssize_t store_pwm_auto_point_temp_hyst(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { - struct f71882fg_data *data = f71882fg_update_device(dev); + struct f71882fg_data *data = dev_get_drvdata(dev); int nr = to_sensor_dev_attr_2(devattr)->index; int point = to_sensor_dev_attr_2(devattr)->nr; long val = simple_strtol(buf, NULL, 10) / 1000; mutex_lock(&data->update_lock); + data->pwm_auto_point_temp[nr][point] = + f71882fg_read8(data, F71882FG_REG_POINT_TEMP(nr, point)); val = SENSORS_LIMIT(val, data->pwm_auto_point_temp[nr][point] - 15, data->pwm_auto_point_temp[nr][point]); val = data->pwm_auto_point_temp[nr][point] - val; + if (nr == 0 || nr == 1) { + data->pwm_auto_point_hyst[0] = + f71882fg_read8(data, F71882FG_REG_FAN_HYST0); + } else { + data->pwm_auto_point_hyst[1] = + f71882fg_read8(data, F71882FG_REG_FAN_HYST1); + } switch (nr) { case 0: val = (data->pwm_auto_point_hyst[0] & 0xf0) | val; @@ -1383,11 +1405,13 @@ static ssize_t store_pwm_interpolate(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { - /* struct f71882fg_data *data = dev_get_drvdata(dev); */ - struct f71882fg_data *data = f71882fg_update_device(dev); + struct f71882fg_data *data = dev_get_drvdata(dev); int nr = to_sensor_dev_attr_2(devattr)->index; - int val = simple_strtoul(buf, NULL, 10); + unsigned long val = simple_strtoul(buf, NULL, 10); + mutex_lock(&data->update_lock); + data->pwm_auto_point_mapping[nr] = + f71882fg_read8(data, F71882FG_REG_POINT_MAPPING(nr)); if (val) val = data->pwm_auto_point_mapping[nr] | (1 << 4); else @@ -1416,8 +1440,7 @@ static ssize_t store_pwm_auto_point_channel(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { - /* struct f71882fg_data *data = dev_get_drvdata(dev); */ - struct f71882fg_data *data = f71882fg_update_device(dev); + struct f71882fg_data *data = dev_get_drvdata(dev); int nr = to_sensor_dev_attr_2(devattr)->index; long val = simple_strtol(buf, NULL, 10); switch (val) { @@ -1434,6 +1457,8 @@ static ssize_t store_pwm_auto_point_channel(struct device *dev, return -EINVAL; } mutex_lock(&data->update_lock); + data->pwm_auto_point_mapping[nr] = + f71882fg_read8(data, F71882FG_REG_POINT_MAPPING(nr)); val = (data->pwm_auto_point_mapping[nr] & 0xfc) | val; f71882fg_write8(data, F71882FG_REG_POINT_MAPPING(nr), val); data->pwm_auto_point_mapping[nr] = val; @@ -1459,8 +1484,7 @@ static ssize_t store_pwm_auto_point_temp(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { - /* struct f71882fg_data *data = dev_get_drvdata(dev); */ - struct f71882fg_data *data = f71882fg_update_device(dev); + struct f71882fg_data *data = dev_get_drvdata(dev); int pwm = to_sensor_dev_attr_2(devattr)->index; int point = to_sensor_dev_attr_2(devattr)->nr; long val = simple_strtol(buf, NULL, 10) / 1000; -- cgit v1.2.3 From bc27490f9164281b9e1768a5df8e0951541f90a1 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 7 Jan 2009 16:37:29 +0100 Subject: hwmon: (f71882fg) Cleanup fan and temp hyst functions Simplify fan and temp hyst. handling by treating the registers as an array of nibbles instead of using switch cases. Also unify the way hysts are handled between temp and fans, the temp code was storing the actual per temp hyst values in 4 u8's, where as the fan code was storing actual register values. Signed-off-by: Hans de Goede Signed-off-by: Jean Delvare --- drivers/hwmon/f71882fg.c | 121 +++++++++++++++++------------------------------ 1 file changed, 43 insertions(+), 78 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c index 345f465aa28..de559923454 100644 --- a/drivers/hwmon/f71882fg.c +++ b/drivers/hwmon/f71882fg.c @@ -68,8 +68,7 @@ #define F71882FG_REG_TEMP_HIGH(nr) (0x81 + 2 * (nr)) #define F71882FG_REG_TEMP_STATUS 0x62 #define F71882FG_REG_TEMP_BEEP 0x63 -#define F71882FG_REG_TEMP_HYST1 0x6C -#define F71882FG_REG_TEMP_HYST23 0x6D +#define F71882FG_REG_TEMP_HYST(nr) (0x6C + (nr)) #define F71882FG_REG_TEMP_TYPE 0x6B #define F71882FG_REG_TEMP_DIODE_OPEN 0x6F @@ -77,8 +76,7 @@ #define F71882FG_REG_PWM_TYPE 0x94 #define F71882FG_REG_PWM_ENABLE 0x96 -#define F71882FG_REG_FAN_HYST0 0x98 -#define F71882FG_REG_FAN_HYST1 0x99 +#define F71882FG_REG_FAN_HYST(nr) (0x98 + (nr)) #define F71882FG_REG_POINT_PWM(pwm, point) (0xAA + (point) + (16 * (pwm))) #define F71882FG_REG_POINT_TEMP(pwm, point) (0xA6 + (point) + (16 * (pwm))) @@ -144,7 +142,7 @@ struct f71882fg_data { u8 temp[4]; u8 temp_ovt[4]; u8 temp_high[4]; - u8 temp_hyst[4]; + u8 temp_hyst[2]; /* 2 hysts stored per reg */ u8 temp_type[4]; u8 temp_status; u8 temp_beep; @@ -688,13 +686,11 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev) F71882FG_REG_TEMP_HIGH(nr)); } - /* Have to hardcode hyst*/ - data->temp_hyst[1] = f71882fg_read8(data, - F71882FG_REG_TEMP_HYST1) >> 4; - /* Hyst temps 2 & 3 stored in same register */ - reg = f71882fg_read8(data, F71882FG_REG_TEMP_HYST23); - data->temp_hyst[2] = reg & 0x0F; - data->temp_hyst[3] = reg >> 4; + /* hyst */ + data->temp_hyst[0] = + f71882fg_read8(data, F71882FG_REG_TEMP_HYST(0)); + data->temp_hyst[1] = + f71882fg_read8(data, F71882FG_REG_TEMP_HYST(1)); /* Have to hardcode type, because temp1 is special */ reg = f71882fg_read8(data, F71882FG_REG_TEMP_TYPE); @@ -715,10 +711,11 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev) data->pwm_enable = f71882fg_read8(data, F71882FG_REG_PWM_ENABLE); - data->pwm_auto_point_hyst[0] = f71882fg_read8(data, - F71882FG_REG_FAN_HYST0); - data->pwm_auto_point_hyst[1] = f71882fg_read8(data, - F71882FG_REG_FAN_HYST1); + data->pwm_auto_point_hyst[0] = + f71882fg_read8(data, F71882FG_REG_FAN_HYST(0)); + data->pwm_auto_point_hyst[1] = + f71882fg_read8(data, F71882FG_REG_FAN_HYST(1)); + for (nr = 0; nr < nr_fans; nr++) { data->pwm_auto_point_mapping[nr] = f71882fg_read8(data, @@ -1011,7 +1008,11 @@ static ssize_t show_temp_max_hyst(struct device *dev, struct device_attribute int temp_max_hyst; mutex_lock(&data->update_lock); - temp_max_hyst = (data->temp_high[nr] - data->temp_hyst[nr]) * 1000; + if (nr & 1) + temp_max_hyst = data->temp_hyst[nr / 2] >> 4; + else + temp_max_hyst = data->temp_hyst[nr / 2] & 0x0f; + temp_max_hyst = (data->temp_high[nr] - temp_max_hyst) * 1000; mutex_unlock(&data->update_lock); return sprintf(buf, "%d\n", temp_max_hyst); @@ -1033,26 +1034,15 @@ static ssize_t store_temp_max_hyst(struct device *dev, struct device_attribute val = SENSORS_LIMIT(val, data->temp_high[nr] - 15, data->temp_high[nr]); val = data->temp_high[nr] - val; - data->temp_hyst[nr] = val; /* convert value to register contents */ - switch (nr) { - case 1: - reg = f71882fg_read8(data, F71882FG_REG_TEMP_HYST1); - reg = (reg & 0x0f) | (val << 4); - break; - case 2: - reg = f71882fg_read8(data, F71882FG_REG_TEMP_HYST23); - reg = (reg & 0xf0) | val; - break; - case 3: - reg = f71882fg_read8(data, F71882FG_REG_TEMP_HYST23); - reg = (reg & 0x0f) | (val << 4); - break; - } - - f71882fg_write8(data, (nr <= 1) ? F71882FG_REG_TEMP_HYST1 : - F71882FG_REG_TEMP_HYST23, reg); + reg = f71882fg_read8(data, F71882FG_REG_TEMP_HYST(nr / 2)); + if (nr & 1) + reg = (reg & 0x0f) | (val << 4); + else + reg = (reg & 0xf0) | val; + f71882fg_write8(data, F71882FG_REG_TEMP_HYST(nr / 2), reg); + data->temp_hyst[nr / 2] = reg; mutex_unlock(&data->update_lock); return ret; @@ -1091,7 +1081,11 @@ static ssize_t show_temp_crit_hyst(struct device *dev, struct device_attribute int temp_crit_hyst; mutex_lock(&data->update_lock); - temp_crit_hyst = (data->temp_ovt[nr] - data->temp_hyst[nr]) * 1000; + if (nr & 1) + temp_crit_hyst = data->temp_hyst[nr / 2] >> 4; + else + temp_crit_hyst = data->temp_hyst[nr / 2] & 0x0f; + temp_crit_hyst = (data->temp_ovt[nr] - temp_crit_hyst) * 1000; mutex_unlock(&data->update_lock); return sprintf(buf, "%d\n", temp_crit_hyst); @@ -1320,20 +1314,10 @@ static ssize_t show_pwm_auto_point_temp_hyst(struct device *dev, int point = to_sensor_dev_attr_2(devattr)->nr; mutex_lock(&data->update_lock); - switch (nr) { - case 0: - result = data->pwm_auto_point_hyst[0] & 0x0f; - break; - case 1: - result = data->pwm_auto_point_hyst[0] >> 4; - break; - case 2: - result = data->pwm_auto_point_hyst[1] & 0x0f; - break; - case 3: - result = data->pwm_auto_point_hyst[1] >> 4; - break; - } + if (nr & 1) + result = data->pwm_auto_point_hyst[nr / 2] >> 4; + else + result = data->pwm_auto_point_hyst[nr / 2] & 0x0f; result = 1000 * (data->pwm_auto_point_temp[nr][point] - result); mutex_unlock(&data->update_lock); @@ -1348,6 +1332,7 @@ static ssize_t store_pwm_auto_point_temp_hyst(struct device *dev, int nr = to_sensor_dev_attr_2(devattr)->index; int point = to_sensor_dev_attr_2(devattr)->nr; long val = simple_strtol(buf, NULL, 10) / 1000; + u8 reg; mutex_lock(&data->update_lock); data->pwm_auto_point_temp[nr][point] = @@ -1356,34 +1341,14 @@ static ssize_t store_pwm_auto_point_temp_hyst(struct device *dev, data->pwm_auto_point_temp[nr][point]); val = data->pwm_auto_point_temp[nr][point] - val; - if (nr == 0 || nr == 1) { - data->pwm_auto_point_hyst[0] = - f71882fg_read8(data, F71882FG_REG_FAN_HYST0); - } else { - data->pwm_auto_point_hyst[1] = - f71882fg_read8(data, F71882FG_REG_FAN_HYST1); - } - switch (nr) { - case 0: - val = (data->pwm_auto_point_hyst[0] & 0xf0) | val; - break; - case 1: - val = (data->pwm_auto_point_hyst[0] & 0x0f) | (val << 4); - break; - case 2: - val = (data->pwm_auto_point_hyst[1] & 0xf0) | val; - break; - case 3: - val = (data->pwm_auto_point_hyst[1] & 0x0f) | (val << 4); - break; - } - if (nr == 0 || nr == 1) { - f71882fg_write8(data, F71882FG_REG_FAN_HYST0, val); - data->pwm_auto_point_hyst[0] = val; - } else { - f71882fg_write8(data, F71882FG_REG_FAN_HYST1, val); - data->pwm_auto_point_hyst[1] = val; - } + reg = f71882fg_read8(data, F71882FG_REG_FAN_HYST(nr / 2)); + if (nr & 1) + reg = (reg & 0x0f) | (val << 4); + else + reg = (reg & 0xf0) | val; + + f71882fg_write8(data, F71882FG_REG_FAN_HYST(nr / 2), reg); + data->pwm_auto_point_hyst[nr / 2] = reg; mutex_unlock(&data->update_lock); return count; -- cgit v1.2.3 From 12d66e840b605265d6adf4b800cc3fc5fb410903 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 7 Jan 2009 16:37:29 +0100 Subject: hwmon: (f71882fg) Check for hwmon powerdown state More F8000 prep work. Take over the checking if the hwmon part is not powered down from the standalone f8000 driver. This check is valid for all supported models. Signed-off-by: Hans de Goede Signed-off-by: Jean Delvare --- drivers/hwmon/f71882fg.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c index de559923454..03a4f84b4e5 100644 --- a/drivers/hwmon/f71882fg.c +++ b/drivers/hwmon/f71882fg.c @@ -1500,6 +1500,11 @@ static int __devinit f71882fg_probe(struct platform_device *pdev) platform_set_drvdata(pdev, data); start_reg = f71882fg_read8(data, F71882FG_REG_START); + if (start_reg & 0x04) { + dev_warn(&pdev->dev, "Hardware monitor is powered down\n"); + err = -ENODEV; + goto exit_free; + } if (!(start_reg & 0x03)) { dev_warn(&pdev->dev, "Hardware monitoring not activated\n"); err = -ENODEV; -- cgit v1.2.3 From 754a5907b01687089382e362753dcceaca58ee66 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 7 Jan 2009 16:37:29 +0100 Subject: hwmon: (f71882fg) Separate max and crit alarm and beep While studying the datasheets for adding F8000 support, I noticed that the F718x2 has separate alarms (and beep control) for its max and crit limits. We keep the temp#_alarm attributes as they are, even though it would be more logical to rename them to temp#_max_alarm. Because lm_sensors v2 depends on them. Signed-off-by: Hans de Goede Signed-off-by: Jean Delvare --- drivers/hwmon/f71882fg.c | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c index 03a4f84b4e5..95a279f7026 100644 --- a/drivers/hwmon/f71882fg.c +++ b/drivers/hwmon/f71882fg.c @@ -270,42 +270,56 @@ static struct sensor_device_attribute_2 f718x2fg_in_temp_attr[] = { store_temp_max, 0, 1), SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst, store_temp_max_hyst, 0, 1), + /* Should really be temp1_max_alarm, but older versions did not handle + the max and crit alarms separately and lm_sensors v2 depends on the + presence of temp#_alarm files. The same goes for temp2/3 _alarm. */ + SENSOR_ATTR_2(temp1_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 1), + SENSOR_ATTR_2(temp1_max_beep, S_IRUGO|S_IWUSR, show_temp_beep, + store_temp_beep, 0, 1), SENSOR_ATTR_2(temp1_crit, S_IRUGO|S_IWUSR, show_temp_crit, store_temp_crit, 0, 1), SENSOR_ATTR_2(temp1_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, 0, 1), + SENSOR_ATTR_2(temp1_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 5), + SENSOR_ATTR_2(temp1_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep, + store_temp_beep, 0, 5), SENSOR_ATTR_2(temp1_type, S_IRUGO, show_temp_type, NULL, 0, 1), - SENSOR_ATTR_2(temp1_beep, S_IRUGO|S_IWUSR, show_temp_beep, - store_temp_beep, 0, 1), - SENSOR_ATTR_2(temp1_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 1), SENSOR_ATTR_2(temp1_fault, S_IRUGO, show_temp_fault, NULL, 0, 1), SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0, 2), SENSOR_ATTR_2(temp2_max, S_IRUGO|S_IWUSR, show_temp_max, store_temp_max, 0, 2), SENSOR_ATTR_2(temp2_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst, store_temp_max_hyst, 0, 2), + /* Should be temp2_max_alarm, see temp1_alarm note */ + SENSOR_ATTR_2(temp2_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 2), + SENSOR_ATTR_2(temp2_max_beep, S_IRUGO|S_IWUSR, show_temp_beep, + store_temp_beep, 0, 2), SENSOR_ATTR_2(temp2_crit, S_IRUGO|S_IWUSR, show_temp_crit, store_temp_crit, 0, 2), SENSOR_ATTR_2(temp2_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, 0, 2), + SENSOR_ATTR_2(temp2_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 6), + SENSOR_ATTR_2(temp2_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep, + store_temp_beep, 0, 6), SENSOR_ATTR_2(temp2_type, S_IRUGO, show_temp_type, NULL, 0, 2), - SENSOR_ATTR_2(temp2_beep, S_IRUGO|S_IWUSR, show_temp_beep, - store_temp_beep, 0, 2), - SENSOR_ATTR_2(temp2_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 2), SENSOR_ATTR_2(temp2_fault, S_IRUGO, show_temp_fault, NULL, 0, 2), SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0, 3), SENSOR_ATTR_2(temp3_max, S_IRUGO|S_IWUSR, show_temp_max, store_temp_max, 0, 3), SENSOR_ATTR_2(temp3_max_hyst, S_IRUGO|S_IWUSR, show_temp_max_hyst, store_temp_max_hyst, 0, 3), + /* Should be temp3_max_alarm, see temp1_alarm note */ + SENSOR_ATTR_2(temp3_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 3), + SENSOR_ATTR_2(temp3_max_beep, S_IRUGO|S_IWUSR, show_temp_beep, + store_temp_beep, 0, 3), SENSOR_ATTR_2(temp3_crit, S_IRUGO|S_IWUSR, show_temp_crit, store_temp_crit, 0, 3), SENSOR_ATTR_2(temp3_crit_hyst, S_IRUGO, show_temp_crit_hyst, NULL, 0, 3), + SENSOR_ATTR_2(temp3_crit_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 7), + SENSOR_ATTR_2(temp3_crit_beep, S_IRUGO|S_IWUSR, show_temp_beep, + store_temp_beep, 0, 7), SENSOR_ATTR_2(temp3_type, S_IRUGO, show_temp_type, NULL, 0, 3), - SENSOR_ATTR_2(temp3_beep, S_IRUGO|S_IWUSR, show_temp_beep, - store_temp_beep, 0, 3), - SENSOR_ATTR_2(temp3_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 3), SENSOR_ATTR_2(temp3_fault, S_IRUGO, show_temp_fault, NULL, 0, 3), }; -- cgit v1.2.3 From 4c82c38ae29a01338b5104b0111cecefaf3a1025 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 7 Jan 2009 16:37:30 +0100 Subject: hwmon: (f71882fg) Remove the fan_mode module option Remove the fan_mode module option it was a monstrosity to begin with, and when adding support for the F8000 it becomes a real pain! Signed-off-by: Hans de Goede Signed-off-by: Jean Delvare --- drivers/hwmon/f71882fg.c | 27 ++------------------------- 1 file changed, 2 insertions(+), 25 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c index 95a279f7026..842592fe5aa 100644 --- a/drivers/hwmon/f71882fg.c +++ b/drivers/hwmon/f71882fg.c @@ -90,12 +90,6 @@ static unsigned short force_id; module_param(force_id, ushort, 0); MODULE_PARM_DESC(force_id, "Override the detected device ID"); -static int fan_mode[4] = { 0, 0, 0, 0 }; -module_param_array(fan_mode, int, NULL, 0644); -MODULE_PARM_DESC(fan_mode, "List of fan control modes (f71882fg only) " - "(0=don't change, 1=pwm, 2=rpm)\n" - "Note: this needs a write to pwm#_enable to take effect"); - enum chips { f71862fg, f71882fg }; static const char *f71882fg_names[] = { @@ -846,15 +840,8 @@ static ssize_t store_fan_full_speed(struct device *dev, val = fan_to_reg(val); mutex_lock(&data->update_lock); - data->pwm_enable = f71882fg_read8(data, F71882FG_REG_PWM_ENABLE); - if (data->pwm_enable & (1 << (2 * nr))) - /* PWM mode */ - count = -EINVAL; - else { - /* RPM mode */ - f71882fg_write16(data, F71882FG_REG_FAN_FULL_SPEED(nr), val); - data->fan_full_speed[nr] = val; - } + f71882fg_write16(data, F71882FG_REG_FAN_FULL_SPEED(nr), val); + data->fan_full_speed[nr] = val; mutex_unlock(&data->update_lock); return count; @@ -1252,16 +1239,6 @@ static ssize_t store_pwm_enable(struct device *dev, struct device_attribute data->pwm_enable &= ~(2 << (2 * nr)); break; /* Temperature ctrl */ } - if (data->type == f71882fg) { - switch (fan_mode[nr]) { - case 1: - data->pwm_enable |= 1 << (2 * nr); - break; /* Duty cycle mode */ - case 2: - data->pwm_enable &= ~(1 << (2 * nr)); - break; /* RPM mode */ - } - } f71882fg_write8(data, F71882FG_REG_PWM_ENABLE, data->pwm_enable); mutex_unlock(&data->update_lock); -- cgit v1.2.3 From ed4f7c20b346294959a16d35443def922e5e1e59 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 7 Jan 2009 16:37:30 +0100 Subject: hwmon: (f71882fg) Add F8000 support And (finally) the patch actually adding f8000 support. Signed-off-by: Hans de Goede Signed-off-by: Jean Delvare --- drivers/hwmon/f71882fg.c | 353 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 291 insertions(+), 62 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c index 842592fe5aa..a833797e96d 100644 --- a/drivers/hwmon/f71882fg.c +++ b/drivers/hwmon/f71882fg.c @@ -45,6 +45,7 @@ #define SIO_FINTEK_ID 0x1934 /* Manufacturers ID */ #define SIO_F71862_ID 0x0601 /* Chipset ID */ #define SIO_F71882_ID 0x0541 /* Chipset ID */ +#define SIO_F8000_ID 0x0581 /* Chipset ID */ #define REGION_LENGTH 8 #define ADDR_REG_OFFSET 5 @@ -90,11 +91,12 @@ static unsigned short force_id; module_param(force_id, ushort, 0); MODULE_PARM_DESC(force_id, "Override the detected device ID"); -enum chips { f71862fg, f71882fg }; +enum chips { f71862fg, f71882fg, f8000 }; static const char *f71882fg_names[] = { "f71862fg", "f71882fg", + "f8000", }; static struct platform_device *f71882fg_pdev; @@ -249,6 +251,7 @@ static struct platform_driver f71882fg_driver = { static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); +/* Temp and in attr common to both the f71862fg and f71882fg */ static struct sensor_device_attribute_2 f718x2fg_in_temp_attr[] = { SENSOR_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0), SENSOR_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 0, 1), @@ -317,6 +320,7 @@ static struct sensor_device_attribute_2 f718x2fg_in_temp_attr[] = { SENSOR_ATTR_2(temp3_fault, S_IRUGO, show_temp_fault, NULL, 0, 3), }; +/* Temp and in attr found only on the f71882fg */ static struct sensor_device_attribute_2 f71882fg_in_temp_attr[] = { SENSOR_ATTR_2(in1_max, S_IRUGO|S_IWUSR, show_in_max, store_in_max, 0, 1), @@ -325,27 +329,51 @@ static struct sensor_device_attribute_2 f71882fg_in_temp_attr[] = { SENSOR_ATTR_2(in1_alarm, S_IRUGO, show_in_alarm, NULL, 0, 1), }; -static struct sensor_device_attribute_2 f718x2fg_fan_attr[] = { +/* Temp and in attr for the f8000 + Note on the f8000 temp_ovt (crit) is used as max, and temp_high (max) + is used as hysteresis value to clear alarms + */ +static struct sensor_device_attribute_2 f8000_in_temp_attr[] = { + SENSOR_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0), + SENSOR_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 0, 1), + SENSOR_ATTR_2(in2_input, S_IRUGO, show_in, NULL, 0, 2), + SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0), + SENSOR_ATTR_2(temp1_max, S_IRUGO|S_IWUSR, show_temp_crit, + store_temp_crit, 0, 0), + SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO|S_IWUSR, show_temp_max, + store_temp_max, 0, 0), + SENSOR_ATTR_2(temp1_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 4), + SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0, 1), + SENSOR_ATTR_2(temp2_max, S_IRUGO|S_IWUSR, show_temp_crit, + store_temp_crit, 0, 1), + SENSOR_ATTR_2(temp2_max_hyst, S_IRUGO|S_IWUSR, show_temp_max, + store_temp_max, 0, 1), + SENSOR_ATTR_2(temp2_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 5), + SENSOR_ATTR_2(temp2_type, S_IRUGO, show_temp_type, NULL, 0, 1), + SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0, 2), + SENSOR_ATTR_2(temp3_max, S_IRUGO|S_IWUSR, show_temp_crit, + store_temp_crit, 0, 2), + SENSOR_ATTR_2(temp3_max_hyst, S_IRUGO|S_IWUSR, show_temp_max, + store_temp_max, 0, 2), + SENSOR_ATTR_2(temp3_alarm, S_IRUGO, show_temp_alarm, NULL, 0, 6), +}; + +/* Fan / PWM attr common to all models */ +static struct sensor_device_attribute_2 fxxxx_fan_attr[] = { SENSOR_ATTR_2(fan1_input, S_IRUGO, show_fan, NULL, 0, 0), SENSOR_ATTR_2(fan1_full_speed, S_IRUGO|S_IWUSR, show_fan_full_speed, store_fan_full_speed, 0, 0), - SENSOR_ATTR_2(fan1_beep, S_IRUGO|S_IWUSR, show_fan_beep, - store_fan_beep, 0, 0), SENSOR_ATTR_2(fan1_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 0), SENSOR_ATTR_2(fan2_input, S_IRUGO, show_fan, NULL, 0, 1), SENSOR_ATTR_2(fan2_full_speed, S_IRUGO|S_IWUSR, show_fan_full_speed, store_fan_full_speed, 0, 1), - SENSOR_ATTR_2(fan2_beep, S_IRUGO|S_IWUSR, show_fan_beep, - store_fan_beep, 0, 1), SENSOR_ATTR_2(fan2_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 1), SENSOR_ATTR_2(fan3_input, S_IRUGO, show_fan, NULL, 0, 2), SENSOR_ATTR_2(fan3_full_speed, S_IRUGO|S_IWUSR, show_fan_full_speed, store_fan_full_speed, 0, 2), - SENSOR_ATTR_2(fan3_beep, S_IRUGO|S_IWUSR, show_fan_beep, - store_fan_beep, 0, 2), SENSOR_ATTR_2(fan3_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 2), SENSOR_ATTR_2(pwm1, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 0), @@ -366,9 +394,6 @@ static struct sensor_device_attribute_2 f718x2fg_fan_attr[] = { show_pwm_auto_point_channel, store_pwm_auto_point_channel, 0, 1), - SENSOR_ATTR_2(pwm3, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 2), - SENSOR_ATTR_2(pwm3_enable, S_IRUGO|S_IWUSR, show_pwm_enable, - store_pwm_enable, 0, 2), SENSOR_ATTR_2(pwm3_interpolate, S_IRUGO|S_IWUSR, show_pwm_interpolate, store_pwm_interpolate, 0, 2), SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR, @@ -376,7 +401,16 @@ static struct sensor_device_attribute_2 f718x2fg_fan_attr[] = { store_pwm_auto_point_channel, 0, 2), }; +/* Fan / PWM attr for the f71862fg, less pwms and less zones per pwm than the + f71882fg */ static struct sensor_device_attribute_2 f71862fg_fan_attr[] = { + SENSOR_ATTR_2(fan1_beep, S_IRUGO|S_IWUSR, show_fan_beep, + store_fan_beep, 0, 0), + SENSOR_ATTR_2(fan2_beep, S_IRUGO|S_IWUSR, show_fan_beep, + store_fan_beep, 0, 1), + SENSOR_ATTR_2(fan3_beep, S_IRUGO|S_IWUSR, show_fan_beep, + store_fan_beep, 0, 2), + SENSOR_ATTR_2(pwm1_auto_point1_pwm, S_IRUGO|S_IWUSR, show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 1, 0), @@ -416,7 +450,14 @@ static struct sensor_device_attribute_2 f71862fg_fan_attr[] = { show_pwm_auto_point_temp_hyst, NULL, 3, 1), }; +/* Fan / PWM attr for the f71882fg */ static struct sensor_device_attribute_2 f71882fg_fan_attr[] = { + SENSOR_ATTR_2(fan1_beep, S_IRUGO|S_IWUSR, show_fan_beep, + store_fan_beep, 0, 0), + SENSOR_ATTR_2(fan2_beep, S_IRUGO|S_IWUSR, show_fan_beep, + store_fan_beep, 0, 1), + SENSOR_ATTR_2(fan3_beep, S_IRUGO|S_IWUSR, show_fan_beep, + store_fan_beep, 0, 2), SENSOR_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 0, 3), SENSOR_ATTR_2(fan4_full_speed, S_IRUGO|S_IWUSR, show_fan_full_speed, @@ -501,6 +542,9 @@ static struct sensor_device_attribute_2 f71882fg_fan_attr[] = { SENSOR_ATTR_2(pwm2_auto_point4_temp_hyst, S_IRUGO, show_pwm_auto_point_temp_hyst, NULL, 3, 1), + SENSOR_ATTR_2(pwm3, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 2), + SENSOR_ATTR_2(pwm3_enable, S_IRUGO|S_IWUSR, show_pwm_enable, + store_pwm_enable, 0, 2), SENSOR_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO|S_IWUSR, show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 0, 2), @@ -586,6 +630,128 @@ static struct sensor_device_attribute_2 f71882fg_fan_attr[] = { show_pwm_auto_point_temp_hyst, NULL, 3, 3), }; +/* Fan / PWM attr for the f8000, zones mapped to temp instead of to pwm! + Also the register block at offset A0 maps to TEMP1 (so our temp2, as the + F8000 starts counting temps at 0), B0 maps the TEMP2 and C0 maps to TEMP0 */ +static struct sensor_device_attribute_2 f8000_fan_attr[] = { + SENSOR_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 0, 3), + + SENSOR_ATTR_2(pwm3, S_IRUGO, show_pwm, NULL, 0, 2), + + SENSOR_ATTR_2(temp1_auto_point1_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 0, 2), + SENSOR_ATTR_2(temp1_auto_point2_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 1, 2), + SENSOR_ATTR_2(temp1_auto_point3_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 2, 2), + SENSOR_ATTR_2(temp1_auto_point4_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 3, 2), + SENSOR_ATTR_2(temp1_auto_point5_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 4, 2), + SENSOR_ATTR_2(temp1_auto_point1_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 0, 2), + SENSOR_ATTR_2(temp1_auto_point2_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 1, 2), + SENSOR_ATTR_2(temp1_auto_point3_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 2, 2), + SENSOR_ATTR_2(temp1_auto_point4_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 3, 2), + SENSOR_ATTR_2(temp1_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp_hyst, + store_pwm_auto_point_temp_hyst, + 0, 2), + SENSOR_ATTR_2(temp1_auto_point2_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 1, 2), + SENSOR_ATTR_2(temp1_auto_point3_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 2, 2), + SENSOR_ATTR_2(temp1_auto_point4_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 3, 2), + + SENSOR_ATTR_2(temp2_auto_point1_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 0, 0), + SENSOR_ATTR_2(temp2_auto_point2_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 1, 0), + SENSOR_ATTR_2(temp2_auto_point3_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 2, 0), + SENSOR_ATTR_2(temp2_auto_point4_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 3, 0), + SENSOR_ATTR_2(temp2_auto_point5_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 4, 0), + SENSOR_ATTR_2(temp2_auto_point1_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 0, 0), + SENSOR_ATTR_2(temp2_auto_point2_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 1, 0), + SENSOR_ATTR_2(temp2_auto_point3_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 2, 0), + SENSOR_ATTR_2(temp2_auto_point4_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 3, 0), + SENSOR_ATTR_2(temp2_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp_hyst, + store_pwm_auto_point_temp_hyst, + 0, 0), + SENSOR_ATTR_2(temp2_auto_point2_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 1, 0), + SENSOR_ATTR_2(temp2_auto_point3_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 2, 0), + SENSOR_ATTR_2(temp2_auto_point4_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 3, 0), + + SENSOR_ATTR_2(temp3_auto_point1_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 0, 1), + SENSOR_ATTR_2(temp3_auto_point2_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 1, 1), + SENSOR_ATTR_2(temp3_auto_point3_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 2, 1), + SENSOR_ATTR_2(temp3_auto_point4_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 3, 1), + SENSOR_ATTR_2(temp3_auto_point5_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 4, 1), + SENSOR_ATTR_2(temp3_auto_point1_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 0, 1), + SENSOR_ATTR_2(temp3_auto_point2_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 1, 1), + SENSOR_ATTR_2(temp3_auto_point3_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 2, 1), + SENSOR_ATTR_2(temp3_auto_point4_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 3, 1), + SENSOR_ATTR_2(temp3_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp_hyst, + store_pwm_auto_point_temp_hyst, + 0, 1), + SENSOR_ATTR_2(temp3_auto_point2_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 1, 1), + SENSOR_ATTR_2(temp3_auto_point3_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 2, 1), + SENSOR_ATTR_2(temp3_auto_point4_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 3, 1), +}; /* Super I/O functions */ static inline int superio_inb(int base, int reg) @@ -671,8 +837,10 @@ static void f71882fg_write16(struct f71882fg_data *data, u8 reg, u16 val) static struct f71882fg_data *f71882fg_update_device(struct device *dev) { struct f71882fg_data *data = dev_get_drvdata(dev); - int nr, reg, reg2; - int nr_fans = (data->type == f71862fg) ? 3 : 4; + int nr, reg = 0, reg2; + int nr_fans = (data->type == f71882fg) ? 4 : 3; + int nr_ins = (data->type == f8000) ? 3 : 9; + int temp_start = (data->type == f8000) ? 0 : 1; mutex_lock(&data->update_lock); @@ -687,35 +855,36 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev) } /* Get High & boundary temps*/ - for (nr = 1; nr < 4; nr++) { + for (nr = temp_start; nr < 3 + temp_start; nr++) { data->temp_ovt[nr] = f71882fg_read8(data, F71882FG_REG_TEMP_OVT(nr)); data->temp_high[nr] = f71882fg_read8(data, F71882FG_REG_TEMP_HIGH(nr)); } - /* hyst */ - data->temp_hyst[0] = - f71882fg_read8(data, F71882FG_REG_TEMP_HYST(0)); - data->temp_hyst[1] = - f71882fg_read8(data, F71882FG_REG_TEMP_HYST(1)); - - /* Have to hardcode type, because temp1 is special */ - reg = f71882fg_read8(data, F71882FG_REG_TEMP_TYPE); + if (data->type != f8000) { + data->fan_beep = f71882fg_read8(data, + F71882FG_REG_FAN_BEEP); + data->temp_beep = f71882fg_read8(data, + F71882FG_REG_TEMP_BEEP); + data->temp_hyst[0] = f71882fg_read8(data, + F71882FG_REG_TEMP_HYST(0)); + data->temp_hyst[1] = f71882fg_read8(data, + F71882FG_REG_TEMP_HYST(1)); + /* Have to hardcode type, because temp1 is special */ + reg = f71882fg_read8(data, F71882FG_REG_TEMP_TYPE); + data->temp_type[2] = (reg & 0x04) ? 2 : 4; + data->temp_type[3] = (reg & 0x08) ? 2 : 4; + } reg2 = f71882fg_read8(data, F71882FG_REG_PECI); if ((reg2 & 0x03) == 0x01) data->temp_type[1] = 6 /* PECI */; else if ((reg2 & 0x03) == 0x02) data->temp_type[1] = 5 /* AMDSI */; - else + else if (data->type != f8000) data->temp_type[1] = (reg & 0x02) ? 2 : 4; - - data->temp_type[2] = (reg & 0x04) ? 2 : 4; - data->temp_type[3] = (reg & 0x08) ? 2 : 4; - - data->temp_beep = f71882fg_read8(data, F71882FG_REG_TEMP_BEEP); - - data->fan_beep = f71882fg_read8(data, F71882FG_REG_FAN_BEEP); + else + data->temp_type[1] = 2; /* F8000 only supports BJT */ data->pwm_enable = f71882fg_read8(data, F71882FG_REG_PWM_ENABLE); @@ -729,7 +898,7 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev) f71882fg_read8(data, F71882FG_REG_POINT_MAPPING(nr)); - if (data->type == f71882fg) { + if (data->type != f71862fg) { int point; for (point = 0; point < 5; point++) { data->pwm_auto_point_pwm[nr][point] = @@ -771,7 +940,7 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev) F71882FG_REG_TEMP_STATUS); data->temp_diode_open = f71882fg_read8(data, F71882FG_REG_TEMP_DIODE_OPEN); - for (nr = 1; nr < 4; nr++) + for (nr = temp_start; nr < 3 + temp_start; nr++) data->temp[nr] = f71882fg_read8(data, F71882FG_REG_TEMP(nr)); @@ -789,10 +958,14 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev) f71882fg_read8(data, F71882FG_REG_PWM(nr)); } + /* The f8000 can monitor 1 more fan, but has no pwm for it */ + if (data->type == f8000) + data->fan[3] = f71882fg_read16(data, + F71882FG_REG_FAN(3)); if (data->type == f71882fg) data->in_status = f71882fg_read8(data, F71882FG_REG_IN_STATUS); - for (nr = 0; nr < 9; nr++) + for (nr = 0; nr < nr_ins; nr++) data->in[nr] = f71882fg_read8(data, F71882FG_REG_IN(nr)); @@ -1186,6 +1359,11 @@ static ssize_t store_pwm(struct device *dev, mutex_lock(&data->update_lock); data->pwm_enable = f71882fg_read8(data, F71882FG_REG_PWM_ENABLE); + if ((data->type == f8000 && ((data->pwm_enable >> 2 * nr) & 3) != 2) || + (data->type != f8000 && !((data->pwm_enable >> 2 * nr) & 2))) { + count = -EROFS; + goto leave; + } if (data->pwm_enable & (1 << (2 * nr))) { /* PWM mode */ f71882fg_write8(data, F71882FG_REG_PWM(nr), val); @@ -1200,6 +1378,7 @@ static ssize_t store_pwm(struct device *dev, data->fan_target[nr] = target; data->fan_full_speed[nr] = full_speed; } +leave: mutex_unlock(&data->update_lock); return count; @@ -1208,14 +1387,25 @@ static ssize_t store_pwm(struct device *dev, static ssize_t show_pwm_enable(struct device *dev, struct device_attribute *devattr, char *buf) { - int result; + int result = 0; struct f71882fg_data *data = f71882fg_update_device(dev); int nr = to_sensor_dev_attr_2(devattr)->index; - if (data->pwm_enable & (2 << (2 * nr))) - result = 1; - else - result = 2; + switch ((data->pwm_enable >> 2 * nr) & 3) { + case 0: + case 1: + result = 2; /* Normal auto mode */ + break; + case 2: + result = 1; /* Manual mode */ + break; + case 3: + if (data->type == f8000) + result = 3; /* Thermostat mode */ + else + result = 1; /* Manual mode */ + break; + } return sprintf(buf, "%d\n", result); } @@ -1226,20 +1416,37 @@ static ssize_t store_pwm_enable(struct device *dev, struct device_attribute struct f71882fg_data *data = dev_get_drvdata(dev); int nr = to_sensor_dev_attr_2(devattr)->index; long val = simple_strtol(buf, NULL, 10); - if (val < 1 || val > 2) - return -EINVAL; mutex_lock(&data->update_lock); data->pwm_enable = f71882fg_read8(data, F71882FG_REG_PWM_ENABLE); - switch (val) { - case 1: - data->pwm_enable |= 2 << (2 * nr); - break; /* Manual */ - case 2: - data->pwm_enable &= ~(2 << (2 * nr)); - break; /* Temperature ctrl */ + /* Special case for F8000 auto PWM mode / Thermostat mode */ + if (data->type == f8000 && ((data->pwm_enable >> 2 * nr) & 1)) { + switch (val) { + case 2: + data->pwm_enable &= ~(2 << (2 * nr)); + break; /* Normal auto mode */ + case 3: + data->pwm_enable |= 2 << (2 * nr); + break; /* Thermostat mode */ + default: + count = -EINVAL; + goto leave; + } + } else { + switch (val) { + case 1: + data->pwm_enable |= 2 << (2 * nr); + break; /* Manual */ + case 2: + data->pwm_enable &= ~(2 << (2 * nr)); + break; /* Normal auto mode */ + default: + count = -EINVAL; + goto leave; + } } f71882fg_write8(data, F71882FG_REG_PWM_ENABLE, data->pwm_enable); +leave: mutex_unlock(&data->update_lock); return count; @@ -1521,34 +1728,51 @@ static int __devinit f71882fg_probe(struct platform_device *pdev) goto exit_unregister_sysfs; if (start_reg & 0x01) { - err = f71882fg_create_sysfs_files(pdev, f718x2fg_in_temp_attr, - ARRAY_SIZE(f718x2fg_in_temp_attr)); - if (err) - goto exit_unregister_sysfs; - - if (data->type == f71882fg) { + switch (data->type) { + case f71882fg: err = f71882fg_create_sysfs_files(pdev, f71882fg_in_temp_attr, ARRAY_SIZE(f71882fg_in_temp_attr)); if (err) goto exit_unregister_sysfs; + /* fall through! */ + case f71862fg: + err = f71882fg_create_sysfs_files(pdev, + f718x2fg_in_temp_attr, + ARRAY_SIZE(f718x2fg_in_temp_attr)); + break; + case f8000: + err = f71882fg_create_sysfs_files(pdev, + f8000_in_temp_attr, + ARRAY_SIZE(f8000_in_temp_attr)); + break; } + if (err) + goto exit_unregister_sysfs; } if (start_reg & 0x02) { - err = f71882fg_create_sysfs_files(pdev, f718x2fg_fan_attr, - ARRAY_SIZE(f718x2fg_fan_attr)); + err = f71882fg_create_sysfs_files(pdev, fxxxx_fan_attr, + ARRAY_SIZE(fxxxx_fan_attr)); if (err) goto exit_unregister_sysfs; - if (data->type == f71862fg) { + switch (data->type) { + case f71862fg: err = f71882fg_create_sysfs_files(pdev, f71862fg_fan_attr, ARRAY_SIZE(f71862fg_fan_attr)); - } else { + break; + case f71882fg: err = f71882fg_create_sysfs_files(pdev, f71882fg_fan_attr, ARRAY_SIZE(f71882fg_fan_attr)); + break; + case f8000: + err = f71882fg_create_sysfs_files(pdev, + f8000_fan_attr, + ARRAY_SIZE(f8000_fan_attr)); + break; } if (err) goto exit_unregister_sysfs; @@ -1580,6 +1804,8 @@ static int f71882fg_remove(struct platform_device *pdev) if (data->hwmon_dev) hwmon_device_unregister(data->hwmon_dev); + /* Note we are not looping over all attr arrays we have as the ones + below are supersets of the ones skipped. */ device_remove_file(&pdev->dev, &dev_attr_name); for (i = 0; i < ARRAY_SIZE(f718x2fg_in_temp_attr); i++) @@ -1590,15 +1816,15 @@ static int f71882fg_remove(struct platform_device *pdev) device_remove_file(&pdev->dev, &f71882fg_in_temp_attr[i].dev_attr); - for (i = 0; i < ARRAY_SIZE(f718x2fg_fan_attr); i++) - device_remove_file(&pdev->dev, &f718x2fg_fan_attr[i].dev_attr); - - for (i = 0; i < ARRAY_SIZE(f71862fg_fan_attr); i++) - device_remove_file(&pdev->dev, &f71862fg_fan_attr[i].dev_attr); + for (i = 0; i < ARRAY_SIZE(fxxxx_fan_attr); i++) + device_remove_file(&pdev->dev, &fxxxx_fan_attr[i].dev_attr); for (i = 0; i < ARRAY_SIZE(f71882fg_fan_attr); i++) device_remove_file(&pdev->dev, &f71882fg_fan_attr[i].dev_attr); + for (i = 0; i < ARRAY_SIZE(f8000_fan_attr); i++) + device_remove_file(&pdev->dev, &f8000_fan_attr[i].dev_attr); + kfree(data); return 0; @@ -1626,6 +1852,9 @@ static int __init f71882fg_find(int sioaddr, unsigned short *address, case SIO_F71882_ID: sio_data->type = f71882fg; break; + case SIO_F8000_ID: + sio_data->type = f8000; + break; default: printk(KERN_INFO DRVNAME ": Unsupported Fintek device\n"); goto exit; -- cgit v1.2.3 From 4901062f78401f09ef0296466172905c93575ddd Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 7 Jan 2009 16:37:30 +0100 Subject: hwmon: (f71882fg) Add missing pwm3 attr for f71862fg For some reason the fan_attr array for the f71862fg was missing the attr for the 3th pwm output. This patch fixes this. Signed-off-by: Hans de Goede Signed-off-by: Jean Delvare --- drivers/hwmon/f71882fg.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c index a833797e96d..f4998bf82ca 100644 --- a/drivers/hwmon/f71882fg.c +++ b/drivers/hwmon/f71882fg.c @@ -448,6 +448,28 @@ static struct sensor_device_attribute_2 f71862fg_fan_attr[] = { 0, 1), SENSOR_ATTR_2(pwm2_auto_point2_temp_hyst, S_IRUGO, show_pwm_auto_point_temp_hyst, NULL, 3, 1), + + SENSOR_ATTR_2(pwm3, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 2), + SENSOR_ATTR_2(pwm3_enable, S_IRUGO|S_IWUSR, show_pwm_enable, + store_pwm_enable, 0, 2), + SENSOR_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 1, 2), + SENSOR_ATTR_2(pwm3_auto_point2_pwm, S_IRUGO|S_IWUSR, + show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, + 4, 2), + SENSOR_ATTR_2(pwm3_auto_point1_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 0, 2), + SENSOR_ATTR_2(pwm3_auto_point2_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp, store_pwm_auto_point_temp, + 3, 2), + SENSOR_ATTR_2(pwm3_auto_point1_temp_hyst, S_IRUGO|S_IWUSR, + show_pwm_auto_point_temp_hyst, + store_pwm_auto_point_temp_hyst, + 0, 2), + SENSOR_ATTR_2(pwm3_auto_point2_temp_hyst, S_IRUGO, + show_pwm_auto_point_temp_hyst, NULL, 3, 2), }; /* Fan / PWM attr for the f71882fg */ -- cgit v1.2.3 From 30453018655a3acd5f59e793da55a2f969ed9c32 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 7 Jan 2009 16:37:30 +0100 Subject: hwmon: (f71882fg) Fix auto_channels_temp temp numbering with f8000 Adjust auto_channels_temp show and store functions for different numbering of temps between f8000 and other supported models. Signed-off-by: Hans de Goede Signed-off-by: Jean Delvare --- drivers/hwmon/f71882fg.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c index f4998bf82ca..0ef7265336b 100644 --- a/drivers/hwmon/f71882fg.c +++ b/drivers/hwmon/f71882fg.c @@ -1615,8 +1615,9 @@ static ssize_t show_pwm_auto_point_channel(struct device *dev, int result; struct f71882fg_data *data = f71882fg_update_device(dev); int nr = to_sensor_dev_attr_2(devattr)->index; + int temp_start = (data->type == f8000) ? 0 : 1; - result = 1 << ((data->pwm_auto_point_mapping[nr] & 3) - 1); + result = 1 << ((data->pwm_auto_point_mapping[nr] & 3) - temp_start); return sprintf(buf, "%d\n", result); } @@ -1627,20 +1628,23 @@ static ssize_t store_pwm_auto_point_channel(struct device *dev, { struct f71882fg_data *data = dev_get_drvdata(dev); int nr = to_sensor_dev_attr_2(devattr)->index; + int temp_start = (data->type == f8000) ? 0 : 1; long val = simple_strtol(buf, NULL, 10); + switch (val) { case 1: - val = 1; + val = 0; break; case 2: - val = 2; + val = 1; break; case 4: - val = 3; + val = 2; break; default: return -EINVAL; } + val += temp_start; mutex_lock(&data->update_lock); data->pwm_auto_point_mapping[nr] = f71882fg_read8(data, F71882FG_REG_POINT_MAPPING(nr)); -- cgit v1.2.3 From 3b02d332b6f15cc8f7b6a04757c86034669600e0 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 7 Jan 2009 16:37:31 +0100 Subject: hwmon: (f71882fg) Add documentation Add some documentation about the f71882fg driver, and update the Kconfig documentation to report the new supported models. Signed-off-by: Hans de Goede Signed-off-by: Jean Delvare --- drivers/hwmon/Kconfig | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index c709e821f04..cc611e4b789 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -284,11 +284,12 @@ config SENSORS_F71805F will be called f71805f. config SENSORS_F71882FG - tristate "Fintek F71882FG and F71883FG" + tristate "Fintek F71862FG, F71882FG and F8000" depends on EXPERIMENTAL help If you say yes here you get support for hardware monitoring - features of the Fintek F71882FG and F71883FG Super-I/O chips. + features of the Fintek F71882FG/F71883FG, F71862FG/71863FG + and F8000 Super-I/O chips. This driver can also be built as a module. If so, the module will be called f71882fg. -- cgit v1.2.3 From 28ba858798d5c70513cd8b9742841fd1bd49a074 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 7 Jan 2009 16:37:31 +0100 Subject: hwmon: (f71882fg) Printout fan modes Print the mode (duty-cycle or RPM) of each fan on driver load. Signed-off-by: Hans de Goede Signed-off-by: Jean Delvare --- drivers/hwmon/f71882fg.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c index 0ef7265336b..a6dc3c7787f 100644 --- a/drivers/hwmon/f71882fg.c +++ b/drivers/hwmon/f71882fg.c @@ -1711,7 +1711,7 @@ static int __devinit f71882fg_probe(struct platform_device *pdev) { struct f71882fg_data *data; struct f71882fg_sio_data *sio_data = pdev->dev.platform_data; - int err; + int err, i, nr_fans = (sio_data->type == f71882fg) ? 4 : 3; u8 start_reg; data = kzalloc(sizeof(struct f71882fg_data), GFP_KERNEL); @@ -1735,14 +1735,14 @@ static int __devinit f71882fg_probe(struct platform_device *pdev) goto exit_free; } + data->pwm_enable = f71882fg_read8(data, F71882FG_REG_PWM_ENABLE); /* If it is a 71862 and the fan / pwm part is enabled sanity check the pwm settings */ if (data->type == f71862fg && (start_reg & 0x02)) { - u8 reg = f71882fg_read8(data, F71882FG_REG_PWM_ENABLE); - if ((reg & 0x15) != 0x15) { + if ((data->pwm_enable & 0x15) != 0x15) { dev_err(&pdev->dev, "Invalid (reserved) pwm settings: 0x%02x\n", - (unsigned int)reg); + (unsigned int)data->pwm_enable); err = -ENODEV; goto exit_free; } @@ -1802,6 +1802,11 @@ static int __devinit f71882fg_probe(struct platform_device *pdev) } if (err) goto exit_unregister_sysfs; + + for (i = 0; i < nr_fans; i++) + dev_info(&pdev->dev, "Fan: %d is in %s mode\n", i + 1, + (data->pwm_enable & (1 << 2 * i)) ? + "duty-cycle" : "RPM"); } data->hwmon_dev = hwmon_device_register(&pdev->dev); -- cgit v1.2.3 From 2f650631b3710622666367474b5475ff81ba486e Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 7 Jan 2009 16:37:31 +0100 Subject: hwmon: (f71882fg) Fix fan_to/from_reg prototypes The RPM after conversion from / before conversion to a register value can be much more than 65535 (up to 1500000), so putting this into an u16 can cause overflows. This changes the functions to use an int to store / get RPM instead. Signed-off-by: Hans de Goede Signed-off-by: Jean Delvare --- drivers/hwmon/f71882fg.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c index a6dc3c7787f..d867b377d4e 100644 --- a/drivers/hwmon/f71882fg.c +++ b/drivers/hwmon/f71882fg.c @@ -810,12 +810,12 @@ static inline void superio_exit(int base) outb(SIO_LOCK_KEY, base); } -static inline u16 fan_from_reg(u16 reg) +static inline int fan_from_reg(u16 reg) { return reg ? (1500000 / reg) : 0; } -static inline u16 fan_to_reg(u16 fan) +static inline u16 fan_to_reg(int fan) { return fan ? (1500000 / fan) : 0; } -- cgit v1.2.3 From 6e34b187bc216fc632769fb8b906d3a29ccd8f14 Mon Sep 17 00:00:00 2001 From: Ira Snyder Date: Wed, 7 Jan 2009 16:37:32 +0100 Subject: hwmon: Add LTC4245 driver Add Linux support for the Linear Technology LTC4245 Multiple Supply Hot Swap controller I2C monitoring interface. Signed-off-by: Ira W. Snyder Acked-by: Hans de Goede Signed-off-by: Jean Delvare --- drivers/hwmon/Kconfig | 11 + drivers/hwmon/Makefile | 1 + drivers/hwmon/ltc4245.c | 567 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 579 insertions(+) create mode 100644 drivers/hwmon/ltc4245.c (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index cc611e4b789..1ef1205b4e8 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -549,6 +549,17 @@ config SENSORS_LM93 This driver can also be built as a module. If so, the module will be called lm93. +config SENSORS_LTC4245 + tristate "Linear Technology LTC4245" + depends on I2C && EXPERIMENTAL + default n + help + If you say yes here you get support for Linear Technology LTC4245 + Multiple Supply Hot Swap Controller I2C interface. + + This driver can also be built as a module. If so, the module will + be called ltc4245. + config SENSORS_MAX1111 tristate "Maxim MAX1111 Multichannel, Serial 8-bit ADC chip" depends on SPI_MASTER diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 58fc5be5355..8fd124eff64 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -62,6 +62,7 @@ obj-$(CONFIG_SENSORS_LM87) += lm87.o obj-$(CONFIG_SENSORS_LM90) += lm90.o obj-$(CONFIG_SENSORS_LM92) += lm92.o obj-$(CONFIG_SENSORS_LM93) += lm93.o +obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o obj-$(CONFIG_SENSORS_MAX1111) += max1111.o obj-$(CONFIG_SENSORS_MAX1619) += max1619.o obj-$(CONFIG_SENSORS_MAX6650) += max6650.o diff --git a/drivers/hwmon/ltc4245.c b/drivers/hwmon/ltc4245.c new file mode 100644 index 00000000000..034b2c51584 --- /dev/null +++ b/drivers/hwmon/ltc4245.c @@ -0,0 +1,567 @@ +/* + * Driver for Linear Technology LTC4245 I2C Multiple Supply Hot Swap Controller + * + * Copyright (C) 2008 Ira W. Snyder + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This driver is based on the ds1621 and ina209 drivers. + * + * Datasheet: + * http://www.linear.com/pc/downloadDocument.do?navId=H0,C1,C1003,C1006,C1140,P19392,D13517 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Valid addresses are 0x20 - 0x3f + * + * For now, we do not probe, since some of these addresses + * are known to be unfriendly to probing */ +static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; + +/* Insmod parameters */ +I2C_CLIENT_INSMOD_1(ltc4245); + +/* Here are names of the chip's registers (a.k.a. commands) */ +enum ltc4245_cmd { + LTC4245_STATUS = 0x00, /* readonly */ + LTC4245_ALERT = 0x01, + LTC4245_CONTROL = 0x02, + LTC4245_ON = 0x03, + LTC4245_FAULT1 = 0x04, + LTC4245_FAULT2 = 0x05, + LTC4245_GPIO = 0x06, + LTC4245_ADCADR = 0x07, + + LTC4245_12VIN = 0x10, + LTC4245_12VSENSE = 0x11, + LTC4245_12VOUT = 0x12, + LTC4245_5VIN = 0x13, + LTC4245_5VSENSE = 0x14, + LTC4245_5VOUT = 0x15, + LTC4245_3VIN = 0x16, + LTC4245_3VSENSE = 0x17, + LTC4245_3VOUT = 0x18, + LTC4245_VEEIN = 0x19, + LTC4245_VEESENSE = 0x1a, + LTC4245_VEEOUT = 0x1b, + LTC4245_GPIOADC1 = 0x1c, + LTC4245_GPIOADC2 = 0x1d, + LTC4245_GPIOADC3 = 0x1e, +}; + +struct ltc4245_data { + struct device *hwmon_dev; + + struct mutex update_lock; + bool valid; + unsigned long last_updated; /* in jiffies */ + + /* Control registers */ + u8 cregs[0x08]; + + /* Voltage registers */ + u8 vregs[0x0f]; +}; + +static struct ltc4245_data *ltc4245_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ltc4245_data *data = i2c_get_clientdata(client); + s32 val; + int i; + + mutex_lock(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { + + dev_dbg(&client->dev, "Starting ltc4245 update\n"); + + /* Read control registers -- 0x00 to 0x07 */ + for (i = 0; i < ARRAY_SIZE(data->cregs); i++) { + val = i2c_smbus_read_byte_data(client, i); + if (unlikely(val < 0)) + data->cregs[i] = 0; + else + data->cregs[i] = val; + } + + /* Read voltage registers -- 0x10 to 0x1f */ + for (i = 0; i < ARRAY_SIZE(data->vregs); i++) { + val = i2c_smbus_read_byte_data(client, i+0x10); + if (unlikely(val < 0)) + data->vregs[i] = 0; + else + data->vregs[i] = val; + } + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + +/* Return the voltage from the given register in millivolts */ +static int ltc4245_get_voltage(struct device *dev, u8 reg) +{ + struct ltc4245_data *data = ltc4245_update_device(dev); + const u8 regval = data->vregs[reg - 0x10]; + u32 voltage = 0; + + switch (reg) { + case LTC4245_12VIN: + case LTC4245_12VOUT: + voltage = regval * 55; + break; + case LTC4245_5VIN: + case LTC4245_5VOUT: + voltage = regval * 22; + break; + case LTC4245_3VIN: + case LTC4245_3VOUT: + voltage = regval * 15; + break; + case LTC4245_VEEIN: + case LTC4245_VEEOUT: + voltage = regval * -55; + break; + case LTC4245_GPIOADC1: + case LTC4245_GPIOADC2: + case LTC4245_GPIOADC3: + voltage = regval * 10; + break; + default: + /* If we get here, the developer messed up */ + WARN_ON_ONCE(1); + break; + } + + return voltage; +} + +/* Return the current in the given sense register in milliAmperes */ +static unsigned int ltc4245_get_current(struct device *dev, u8 reg) +{ + struct ltc4245_data *data = ltc4245_update_device(dev); + const u8 regval = data->vregs[reg - 0x10]; + unsigned int voltage; + unsigned int curr; + + /* The strange looking conversions that follow are fixed-point + * math, since we cannot do floating point in the kernel. + * + * Step 1: convert sense register to microVolts + * Step 2: convert voltage to milliAmperes + * + * If you play around with the V=IR equation, you come up with + * the following: X uV / Y mOhm == Z mA + * + * With the resistors that are fractions of a milliOhm, we multiply + * the voltage and resistance by 10, to shift the decimal point. + * Now we can use the normal division operator again. + */ + + switch (reg) { + case LTC4245_12VSENSE: + voltage = regval * 250; /* voltage in uV */ + curr = voltage / 50; /* sense resistor 50 mOhm */ + break; + case LTC4245_5VSENSE: + voltage = regval * 125; /* voltage in uV */ + curr = (voltage * 10) / 35; /* sense resistor 3.5 mOhm */ + break; + case LTC4245_3VSENSE: + voltage = regval * 125; /* voltage in uV */ + curr = (voltage * 10) / 25; /* sense resistor 2.5 mOhm */ + break; + case LTC4245_VEESENSE: + voltage = regval * 250; /* voltage in uV */ + curr = voltage / 100; /* sense resistor 100 mOhm */ + break; + default: + /* If we get here, the developer messed up */ + WARN_ON_ONCE(1); + curr = 0; + break; + } + + return curr; +} + +static ssize_t ltc4245_show_voltage(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + const int voltage = ltc4245_get_voltage(dev, attr->index); + + return snprintf(buf, PAGE_SIZE, "%d\n", voltage); +} + +static ssize_t ltc4245_show_current(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + const unsigned int curr = ltc4245_get_current(dev, attr->index); + + return snprintf(buf, PAGE_SIZE, "%u\n", curr); +} + +static ssize_t ltc4245_show_power(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + const unsigned int curr = ltc4245_get_current(dev, attr->index); + const int output_voltage = ltc4245_get_voltage(dev, attr->index+1); + + /* current in mA * voltage in mV == power in uW */ + const unsigned int power = abs(output_voltage * curr); + + return snprintf(buf, PAGE_SIZE, "%u\n", power); +} + +static ssize_t ltc4245_show_alarm(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(da); + struct ltc4245_data *data = ltc4245_update_device(dev); + const u8 reg = data->cregs[attr->index]; + const u32 mask = attr->nr; + + return snprintf(buf, PAGE_SIZE, "%u\n", (reg & mask) ? 1 : 0); +} + +/* These macros are used below in constructing device attribute objects + * for use with sysfs_create_group() to make a sysfs device file + * for each register. + */ + +#define LTC4245_VOLTAGE(name, ltc4245_cmd_idx) \ + static SENSOR_DEVICE_ATTR(name, S_IRUGO, \ + ltc4245_show_voltage, NULL, ltc4245_cmd_idx) + +#define LTC4245_CURRENT(name, ltc4245_cmd_idx) \ + static SENSOR_DEVICE_ATTR(name, S_IRUGO, \ + ltc4245_show_current, NULL, ltc4245_cmd_idx) + +#define LTC4245_POWER(name, ltc4245_cmd_idx) \ + static SENSOR_DEVICE_ATTR(name, S_IRUGO, \ + ltc4245_show_power, NULL, ltc4245_cmd_idx) + +#define LTC4245_ALARM(name, mask, reg) \ + static SENSOR_DEVICE_ATTR_2(name, S_IRUGO, \ + ltc4245_show_alarm, NULL, (mask), reg) + +/* Construct a sensor_device_attribute structure for each register */ + +/* Input voltages */ +LTC4245_VOLTAGE(in1_input, LTC4245_12VIN); +LTC4245_VOLTAGE(in2_input, LTC4245_5VIN); +LTC4245_VOLTAGE(in3_input, LTC4245_3VIN); +LTC4245_VOLTAGE(in4_input, LTC4245_VEEIN); + +/* Input undervoltage alarms */ +LTC4245_ALARM(in1_min_alarm, (1 << 0), LTC4245_FAULT1); +LTC4245_ALARM(in2_min_alarm, (1 << 1), LTC4245_FAULT1); +LTC4245_ALARM(in3_min_alarm, (1 << 2), LTC4245_FAULT1); +LTC4245_ALARM(in4_min_alarm, (1 << 3), LTC4245_FAULT1); + +/* Currents (via sense resistor) */ +LTC4245_CURRENT(curr1_input, LTC4245_12VSENSE); +LTC4245_CURRENT(curr2_input, LTC4245_5VSENSE); +LTC4245_CURRENT(curr3_input, LTC4245_3VSENSE); +LTC4245_CURRENT(curr4_input, LTC4245_VEESENSE); + +/* Overcurrent alarms */ +LTC4245_ALARM(curr1_max_alarm, (1 << 4), LTC4245_FAULT1); +LTC4245_ALARM(curr2_max_alarm, (1 << 5), LTC4245_FAULT1); +LTC4245_ALARM(curr3_max_alarm, (1 << 6), LTC4245_FAULT1); +LTC4245_ALARM(curr4_max_alarm, (1 << 7), LTC4245_FAULT1); + +/* Output voltages */ +LTC4245_VOLTAGE(in5_input, LTC4245_12VOUT); +LTC4245_VOLTAGE(in6_input, LTC4245_5VOUT); +LTC4245_VOLTAGE(in7_input, LTC4245_3VOUT); +LTC4245_VOLTAGE(in8_input, LTC4245_VEEOUT); + +/* Power Bad alarms */ +LTC4245_ALARM(in5_min_alarm, (1 << 0), LTC4245_FAULT2); +LTC4245_ALARM(in6_min_alarm, (1 << 1), LTC4245_FAULT2); +LTC4245_ALARM(in7_min_alarm, (1 << 2), LTC4245_FAULT2); +LTC4245_ALARM(in8_min_alarm, (1 << 3), LTC4245_FAULT2); + +/* GPIO voltages */ +LTC4245_VOLTAGE(in9_input, LTC4245_GPIOADC1); +LTC4245_VOLTAGE(in10_input, LTC4245_GPIOADC2); +LTC4245_VOLTAGE(in11_input, LTC4245_GPIOADC3); + +/* Power Consumption (virtual) */ +LTC4245_POWER(power1_input, LTC4245_12VSENSE); +LTC4245_POWER(power2_input, LTC4245_5VSENSE); +LTC4245_POWER(power3_input, LTC4245_3VSENSE); +LTC4245_POWER(power4_input, LTC4245_VEESENSE); + +/* Finally, construct an array of pointers to members of the above objects, + * as required for sysfs_create_group() + */ +static struct attribute *ltc4245_attributes[] = { + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_in4_input.dev_attr.attr, + + &sensor_dev_attr_in1_min_alarm.dev_attr.attr, + &sensor_dev_attr_in2_min_alarm.dev_attr.attr, + &sensor_dev_attr_in3_min_alarm.dev_attr.attr, + &sensor_dev_attr_in4_min_alarm.dev_attr.attr, + + &sensor_dev_attr_curr1_input.dev_attr.attr, + &sensor_dev_attr_curr2_input.dev_attr.attr, + &sensor_dev_attr_curr3_input.dev_attr.attr, + &sensor_dev_attr_curr4_input.dev_attr.attr, + + &sensor_dev_attr_curr1_max_alarm.dev_attr.attr, + &sensor_dev_attr_curr2_max_alarm.dev_attr.attr, + &sensor_dev_attr_curr3_max_alarm.dev_attr.attr, + &sensor_dev_attr_curr4_max_alarm.dev_attr.attr, + + &sensor_dev_attr_in5_input.dev_attr.attr, + &sensor_dev_attr_in6_input.dev_attr.attr, + &sensor_dev_attr_in7_input.dev_attr.attr, + &sensor_dev_attr_in8_input.dev_attr.attr, + + &sensor_dev_attr_in5_min_alarm.dev_attr.attr, + &sensor_dev_attr_in6_min_alarm.dev_attr.attr, + &sensor_dev_attr_in7_min_alarm.dev_attr.attr, + &sensor_dev_attr_in8_min_alarm.dev_attr.attr, + + &sensor_dev_attr_in9_input.dev_attr.attr, + &sensor_dev_attr_in10_input.dev_attr.attr, + &sensor_dev_attr_in11_input.dev_attr.attr, + + &sensor_dev_attr_power1_input.dev_attr.attr, + &sensor_dev_attr_power2_input.dev_attr.attr, + &sensor_dev_attr_power3_input.dev_attr.attr, + &sensor_dev_attr_power4_input.dev_attr.attr, + + NULL, +}; + +static const struct attribute_group ltc4245_group = { + .attrs = ltc4245_attributes, +}; + +static int ltc4245_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ltc4245_data *data; + int ret; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) { + ret = -ENOMEM; + goto out_kzalloc; + } + + i2c_set_clientdata(client, data); + mutex_init(&data->update_lock); + + /* Initialize the LTC4245 chip */ + /* TODO */ + + /* Register sysfs hooks */ + ret = sysfs_create_group(&client->dev.kobj, <c4245_group); + if (ret) + goto out_sysfs_create_group; + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + ret = PTR_ERR(data->hwmon_dev); + goto out_hwmon_device_register; + } + + return 0; + +out_hwmon_device_register: + sysfs_remove_group(&client->dev.kobj, <c4245_group); +out_sysfs_create_group: + kfree(data); +out_kzalloc: + return ret; +} + +static int ltc4245_remove(struct i2c_client *client) +{ + struct ltc4245_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, <c4245_group); + + kfree(data); + + return 0; +} + +/* Check that some bits in a control register appear at all possible + * locations without changing value + * + * @client: the i2c client to use + * @reg: the register to read + * @bits: the bits to check (0xff checks all bits, + * 0x03 checks only the last two bits) + * + * return -ERRNO if the register read failed + * return -ENODEV if the register value doesn't stay constant at all + * possible addresses + * + * return 0 for success + */ +static int ltc4245_check_control_reg(struct i2c_client *client, u8 reg, u8 bits) +{ + int i; + s32 v, voff1, voff2; + + /* Read register and check for error */ + v = i2c_smbus_read_byte_data(client, reg); + if (v < 0) + return v; + + v &= bits; + + for (i = 0x00; i < 0xff; i += 0x20) { + + voff1 = i2c_smbus_read_byte_data(client, reg + i); + if (voff1 < 0) + return voff1; + + voff2 = i2c_smbus_read_byte_data(client, reg + i + 0x08); + if (voff2 < 0) + return voff2; + + voff1 &= bits; + voff2 &= bits; + + if (v != voff1 || v != voff2) + return -ENODEV; + } + + return 0; +} + +static int ltc4245_detect(struct i2c_client *client, + int kind, + struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + if (kind < 0) { /* probed detection - check the chip type */ + s32 v; /* 8 bits from the chip, or -ERRNO */ + + /* Chip registers 0x00-0x07 are control registers + * Chip registers 0x10-0x1f are data registers + * + * Address bits b7-b5 are ignored. This makes the chip "repeat" + * in steps of 0x20. Any control registers should appear with + * the same values across all duplicated addresses. + * + * Register 0x02 bit b2 is reserved, expect 0 + * Register 0x07 bits b7 to b4 are reserved, expect 0 + * + * Registers 0x01, 0x02 are control registers and should not + * change on their own. + * + * Register 0x06 bits b6 and b7 are control bits, and should + * not change on their own. + * + * Register 0x07 bits b3 to b0 are control bits, and should + * not change on their own. + */ + + /* read register 0x02 reserved bit, expect 0 */ + v = i2c_smbus_read_byte_data(client, LTC4245_CONTROL); + if (v < 0 || (v & 0x04) != 0) + return -ENODEV; + + /* read register 0x07 reserved bits, expect 0 */ + v = i2c_smbus_read_byte_data(client, LTC4245_ADCADR); + if (v < 0 || (v & 0xf0) != 0) + return -ENODEV; + + /* check that the alert register appears at all locations */ + if (ltc4245_check_control_reg(client, LTC4245_ALERT, 0xff)) + return -ENODEV; + + /* check that the control register appears at all locations */ + if (ltc4245_check_control_reg(client, LTC4245_CONTROL, 0xff)) + return -ENODEV; + + /* check that register 0x06 bits b6 and b7 stay constant */ + if (ltc4245_check_control_reg(client, LTC4245_GPIO, 0xc0)) + return -ENODEV; + + /* check that register 0x07 bits b3-b0 stay constant */ + if (ltc4245_check_control_reg(client, LTC4245_ADCADR, 0x0f)) + return -ENODEV; + } + + strlcpy(info->type, "ltc4245", I2C_NAME_SIZE); + dev_info(&adapter->dev, "ltc4245 %s at address 0x%02x\n", + kind < 0 ? "probed" : "forced", + client->addr); + + return 0; +} + +static const struct i2c_device_id ltc4245_id[] = { + { "ltc4245", ltc4245 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ltc4245_id); + +/* This is the driver that will be inserted */ +static struct i2c_driver ltc4245_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "ltc4245", + }, + .probe = ltc4245_probe, + .remove = ltc4245_remove, + .id_table = ltc4245_id, + .detect = ltc4245_detect, + .address_data = &addr_data, +}; + +static int __init ltc4245_init(void) +{ + return i2c_add_driver(<c4245_driver); +} + +static void __exit ltc4245_exit(void) +{ + i2c_del_driver(<c4245_driver); +} + +MODULE_AUTHOR("Ira W. Snyder "); +MODULE_DESCRIPTION("LTC4245 driver"); +MODULE_LICENSE("GPL"); + +module_init(ltc4245_init); +module_exit(ltc4245_exit); -- cgit v1.2.3 From 3aed198c35567e5a721f52c0bde23167867e6af6 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 7 Jan 2009 16:37:32 +0100 Subject: hwmon: Don't overuse I2C_CLIENT_MODULE_PARM I2C_CLIENT_MODULE_PARM is overkill for force_subclients. We really only use 4 out of the 48 slots, so we're better defining a custom variable instead. This change saves 92 bytes of data for each of the five drivers affected. Signed-off-by: Jean Delvare Cc: Wolfgang Grandegger Acked-by: Marc Hulsman Cc: Mark M. Hoffman --- drivers/hwmon/asb100.c | 5 ++++- drivers/hwmon/w83781d.c | 5 ++++- drivers/hwmon/w83791d.c | 5 ++++- drivers/hwmon/w83792d.c | 5 ++++- drivers/hwmon/w83793.c | 5 ++++- 5 files changed, 20 insertions(+), 5 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/asb100.c b/drivers/hwmon/asb100.c index 8a45a2e6ba8..8acf82977e7 100644 --- a/drivers/hwmon/asb100.c +++ b/drivers/hwmon/asb100.c @@ -53,7 +53,10 @@ static const unsigned short normal_i2c[] = { 0x2d, I2C_CLIENT_END }; /* Insmod parameters */ I2C_CLIENT_INSMOD_1(asb100); -I2C_CLIENT_MODULE_PARM(force_subclients, "List of subclient addresses: " + +static unsigned short force_subclients[4]; +module_param_array(force_subclients, short, NULL, 0); +MODULE_PARM_DESC(force_subclients, "List of subclient addresses: " "{bus, clientaddr, subclientaddr1, subclientaddr2}"); /* Voltage IN registers 0-6 */ diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c index fc12bd412e3..dbfb30c588d 100644 --- a/drivers/hwmon/w83781d.c +++ b/drivers/hwmon/w83781d.c @@ -58,7 +58,10 @@ static const unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END }; /* Insmod parameters */ I2C_CLIENT_INSMOD_4(w83781d, w83782d, w83783s, as99127f); -I2C_CLIENT_MODULE_PARM(force_subclients, "List of subclient addresses: " + +static unsigned short force_subclients[4]; +module_param_array(force_subclients, short, NULL, 0); +MODULE_PARM_DESC(force_subclients, "List of subclient addresses: " "{bus, clientaddr, subclientaddr1, subclientaddr2}"); static int reset; diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c index 5768def8a4f..97851c5ba3a 100644 --- a/drivers/hwmon/w83791d.c +++ b/drivers/hwmon/w83791d.c @@ -53,7 +53,10 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f, /* Insmod parameters */ I2C_CLIENT_INSMOD_1(w83791d); -I2C_CLIENT_MODULE_PARM(force_subclients, "List of subclient addresses: " + +static unsigned short force_subclients[4]; +module_param_array(force_subclients, short, NULL, 0); +MODULE_PARM_DESC(force_subclients, "List of subclient addresses: " "{bus, clientaddr, subclientaddr1, subclientaddr2}"); static int reset; diff --git a/drivers/hwmon/w83792d.c b/drivers/hwmon/w83792d.c index cf94c5b0c87..2be16194ddf 100644 --- a/drivers/hwmon/w83792d.c +++ b/drivers/hwmon/w83792d.c @@ -51,7 +51,10 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f, /* Insmod parameters */ I2C_CLIENT_INSMOD_1(w83792d); -I2C_CLIENT_MODULE_PARM(force_subclients, "List of subclient addresses: " + +static unsigned short force_subclients[4]; +module_param_array(force_subclients, short, NULL, 0); +MODULE_PARM_DESC(force_subclients, "List of subclient addresses: " "{bus, clientaddr, subclientaddr1, subclientaddr2}"); static int init; diff --git a/drivers/hwmon/w83793.c b/drivers/hwmon/w83793.c index 0a739f1c69b..47dd398f725 100644 --- a/drivers/hwmon/w83793.c +++ b/drivers/hwmon/w83793.c @@ -42,7 +42,10 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f, /* Insmod parameters */ I2C_CLIENT_INSMOD_1(w83793); -I2C_CLIENT_MODULE_PARM(force_subclients, "List of subclient addresses: " + +static unsigned short force_subclients[4]; +module_param_array(force_subclients, short, NULL, 0); +MODULE_PARM_DESC(force_subclients, "List of subclient addresses: " "{bus, clientaddr, subclientaddr1, subclientaddr2}"); static int reset; -- cgit v1.2.3 From b4da93e4b0ffc261c3530fe938aefd52854aa84c Mon Sep 17 00:00:00 2001 From: Jean-Marc Spaggiari Date: Wed, 7 Jan 2009 16:37:32 +0100 Subject: hwmon: (it87) Add support for the ITE IT8720F Allow it87.c to handle IT8720 chipset like IT8718 in order to retrieve voltage, temperatures and fans speed from sensors tools. Also updating the related documentation. Signed-off-by: Jean-Marc Spaggiari Signed-off-by: Jean Delvare --- drivers/hwmon/Kconfig | 3 ++- drivers/hwmon/it87.c | 30 +++++++++++++++++++++--------- 2 files changed, 23 insertions(+), 10 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 1ef1205b4e8..aba01b4ceca 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -400,7 +400,8 @@ config SENSORS_IT87 select HWMON_VID help If you say yes here you get support for ITE IT8705F, IT8712F, - IT8716F, IT8718F and IT8726F sensor chips, and the SiS960 clone. + IT8716F, IT8718F, IT8720F and IT8726F sensor chips, and the + SiS960 clone. This driver can also be built as a module. If so, the module will be called it87. diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index b74c95735f9..0e0d692f0c9 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -14,6 +14,7 @@ IT8712F Super I/O chip w/LPC interface IT8716F Super I/O chip w/LPC interface IT8718F Super I/O chip w/LPC interface + IT8720F Super I/O chip w/LPC interface IT8726F Super I/O chip w/LPC interface Sis950 A clone of the IT8705F @@ -52,7 +53,7 @@ #define DRVNAME "it87" -enum chips { it87, it8712, it8716, it8718 }; +enum chips { it87, it8712, it8716, it8718, it8720 }; static unsigned short force_id; module_param(force_id, ushort, 0); @@ -64,7 +65,10 @@ static struct platform_device *pdev; #define DEV 0x07 /* Register: Logical device select */ #define VAL 0x2f /* The value to read/write */ #define PME 0x04 /* The device with the fan registers in it */ -#define GPIO 0x07 /* The device with the IT8718F VID value in it */ + +/* The device with the IT8718F/IT8720F VID value in it */ +#define GPIO 0x07 + #define DEVID 0x20 /* Register: Device ID */ #define DEVREV 0x22 /* Register: Device Revision */ @@ -113,6 +117,7 @@ superio_exit(void) #define IT8705F_DEVID 0x8705 #define IT8716F_DEVID 0x8716 #define IT8718F_DEVID 0x8718 +#define IT8720F_DEVID 0x8720 #define IT8726F_DEVID 0x8726 #define IT87_ACT_REG 0x30 #define IT87_BASE_REG 0x60 @@ -150,8 +155,8 @@ static int fix_pwm_polarity; #define IT87_REG_ALARM2 0x02 #define IT87_REG_ALARM3 0x03 -/* The IT8718F has the VID value in a different register, in Super-I/O - configuration space. */ +/* The IT8718F and IT8720F have the VID value in a different register, in + Super-I/O configuration space. */ #define IT87_REG_VID 0x0a /* The IT8705F and IT8712F earlier than revision 0x08 use register 0x0b for fan divisors. Later IT8712F revisions must use 16-bit tachometer @@ -282,7 +287,8 @@ static inline int has_16bit_fans(const struct it87_data *data) return (data->type == it87 && data->revision >= 0x03) || (data->type == it8712 && data->revision >= 0x08) || data->type == it8716 - || data->type == it8718; + || data->type == it8718 + || data->type == it8720; } static int it87_probe(struct platform_device *pdev); @@ -992,6 +998,9 @@ static int __init it87_find(unsigned short *address, case IT8718F_DEVID: sio_data->type = it8718; break; + case IT8720F_DEVID: + sio_data->type = it8720; + break; case 0xffff: /* No device at all */ goto exit; default: @@ -1022,7 +1031,8 @@ static int __init it87_find(unsigned short *address, int reg; superio_select(GPIO); - if (chip_type == it8718) + if ((chip_type == it8718) || + (chip_type == it8720)) sio_data->vid_value = superio_inb(IT87_SIO_VID_REG); reg = superio_inb(IT87_SIO_PINX2_REG); @@ -1068,6 +1078,7 @@ static int __devinit it87_probe(struct platform_device *pdev) "it8712", "it8716", "it8718", + "it8720", }; res = platform_get_resource(pdev, IORESOURCE_IO, 0); @@ -1226,7 +1237,7 @@ static int __devinit it87_probe(struct platform_device *pdev) } if (data->type == it8712 || data->type == it8716 - || data->type == it8718) { + || data->type == it8718 || data->type == it8720) { data->vrm = vid_which_vrm(); /* VID reading from Super-I/O config space if available */ data->vid = sio_data->vid_value; @@ -1513,7 +1524,8 @@ static struct it87_data *it87_update_device(struct device *dev) data->sensor = it87_read_value(data, IT87_REG_TEMP_ENABLE); /* The 8705 does not have VID capability. - The 8718 does not use IT87_REG_VID for the same purpose. */ + The 8718 and the 8720 don't use IT87_REG_VID for the + same purpose. */ if (data->type == it8712 || data->type == it8716) { data->vid = it87_read_value(data, IT87_REG_VID); /* The older IT8712F revisions had only 5 VID pins, @@ -1608,7 +1620,7 @@ static void __exit sm_it87_exit(void) MODULE_AUTHOR("Chris Gauthron, " "Jean Delvare "); -MODULE_DESCRIPTION("IT8705F/8712F/8716F/8718F/8726F, SiS950 driver"); +MODULE_DESCRIPTION("IT8705F/8712F/8716F/8718F/8720F/8726F, SiS950 driver"); module_param(update_vbat, bool, 0); MODULE_PARM_DESC(update_vbat, "Update vbat if set else return powerup value"); module_param(fix_pwm_polarity, bool, 0); -- cgit v1.2.3 From 6e31eb2b297c7b6678e6e77393bb8d01b5228bda Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Wed, 7 Jan 2009 16:37:33 +0100 Subject: hwmon: (i5k_amb) Load automatically on all 5000/5400 chipsets It turns out that we cannot create a pci_driver in this driver because PCI will not call this module's probe function if the i5000-edac driver is already loaded. That said, we only want one value (AMBASE) from the PCI config space. Neither driver alters this value, so it's safe to read it. However, we still want the module aliases, so provide that. Signed-off-by: Darrick J. Wong Signed-off-by: Jean Delvare --- drivers/hwmon/i5k_amb.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/i5k_amb.c b/drivers/hwmon/i5k_amb.c index 2ede9388096..27d7f72a5f1 100644 --- a/drivers/hwmon/i5k_amb.c +++ b/drivers/hwmon/i5k_amb.c @@ -490,6 +490,13 @@ static unsigned long chipset_ids[] = { 0 }; +static struct pci_device_id i5k_amb_ids[] __devinitdata = { + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5000_ERR) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5400_ERR) }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, i5k_amb_ids); + static int __devinit i5k_amb_probe(struct platform_device *pdev) { struct i5k_amb_data *data; -- cgit v1.2.3 From 453e308d773979f6bbdf4109df27101072f6524b Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 7 Jan 2009 16:37:33 +0100 Subject: hwmon: (fschmd) Cleanups for watchdog support Various small cleanups in preparation of adding watchdog support, mostly removing _MASK postfix from defines which are not masks. Signed-off-by: Hans de Goede Signed-off-by: Jean Delvare --- drivers/hwmon/fschmd.c | 47 ++++++++++++++++++++++------------------------- 1 file changed, 22 insertions(+), 25 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/fschmd.c b/drivers/hwmon/fschmd.c index 96717036893..c188b713502 100644 --- a/drivers/hwmon/fschmd.c +++ b/drivers/hwmon/fschmd.c @@ -1,6 +1,6 @@ /* fschmd.c * - * Copyright (C) 2007 Hans de Goede + * Copyright (C) 2007,2008 Hans de Goede * * 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 @@ -63,7 +63,7 @@ I2C_CLIENT_INSMOD_5(fscpos, fscher, fscscy, fschrc, fschmd); #define FSCHMD_REG_EVENT_STATE 0x04 #define FSCHMD_REG_CONTROL 0x05 -#define FSCHMD_CONTROL_ALERT_LED_MASK 0x01 +#define FSCHMD_CONTROL_ALERT_LED 0x01 /* watchdog (support to be implemented) */ #define FSCHMD_REG_WDOG_PRESET 0x28 @@ -115,8 +115,8 @@ static const u8 FSCHMD_REG_FAN_RIPPLE[5][6] = { static const int FSCHMD_NO_FAN_SENSORS[5] = { 3, 3, 6, 4, 5 }; /* Fan status register bitmasks */ -#define FSCHMD_FAN_ALARM_MASK 0x04 /* called fault by FSC! */ -#define FSCHMD_FAN_NOT_PRESENT_MASK 0x08 /* not documented */ +#define FSCHMD_FAN_ALARM 0x04 /* called fault by FSC! */ +#define FSCHMD_FAN_NOT_PRESENT 0x08 /* not documented */ /* actual temperature registers */ @@ -158,14 +158,11 @@ static const u8 FSCHER_REG_TEMP_AUTOP2[] = { 0x75, 0x85, 0x95 }; */ static const int FSCHMD_NO_TEMP_SENSORS[5] = { 3, 3, 4, 3, 5 }; /* temp status register bitmasks */ -#define FSCHMD_TEMP_WORKING_MASK 0x01 -#define FSCHMD_TEMP_ALERT_MASK 0x02 +#define FSCHMD_TEMP_WORKING 0x01 +#define FSCHMD_TEMP_ALERT 0x02 /* there only really is an alarm if the sensor is working and alert == 1 */ #define FSCHMD_TEMP_ALARM_MASK \ - (FSCHMD_TEMP_WORKING_MASK | FSCHMD_TEMP_ALERT_MASK) - -/* our driver name */ -#define FSCHMD_NAME "fschmd" + (FSCHMD_TEMP_WORKING | FSCHMD_TEMP_ALERT) /* * Functions declarations @@ -195,7 +192,7 @@ MODULE_DEVICE_TABLE(i2c, fschmd_id); static struct i2c_driver fschmd_driver = { .class = I2C_CLASS_HWMON, .driver = { - .name = FSCHMD_NAME, + .name = "fschmd", }, .probe = fschmd_probe, .remove = fschmd_remove, @@ -300,7 +297,7 @@ static ssize_t show_temp_fault(struct device *dev, struct fschmd_data *data = fschmd_update_device(dev); /* bit 0 set means sensor working ok, so no fault! */ - if (data->temp_status[index] & FSCHMD_TEMP_WORKING_MASK) + if (data->temp_status[index] & FSCHMD_TEMP_WORKING) return sprintf(buf, "0\n"); else return sprintf(buf, "1\n"); @@ -385,7 +382,7 @@ static ssize_t show_fan_alarm(struct device *dev, int index = to_sensor_dev_attr(devattr)->index; struct fschmd_data *data = fschmd_update_device(dev); - if (data->fan_status[index] & FSCHMD_FAN_ALARM_MASK) + if (data->fan_status[index] & FSCHMD_FAN_ALARM) return sprintf(buf, "1\n"); else return sprintf(buf, "0\n"); @@ -397,7 +394,7 @@ static ssize_t show_fan_fault(struct device *dev, int index = to_sensor_dev_attr(devattr)->index; struct fschmd_data *data = fschmd_update_device(dev); - if (data->fan_status[index] & FSCHMD_FAN_NOT_PRESENT_MASK) + if (data->fan_status[index] & FSCHMD_FAN_NOT_PRESENT) return sprintf(buf, "1\n"); else return sprintf(buf, "0\n"); @@ -449,7 +446,7 @@ static ssize_t show_alert_led(struct device *dev, { struct fschmd_data *data = fschmd_update_device(dev); - if (data->global_control & FSCHMD_CONTROL_ALERT_LED_MASK) + if (data->global_control & FSCHMD_CONTROL_ALERT_LED) return sprintf(buf, "1\n"); else return sprintf(buf, "0\n"); @@ -467,9 +464,9 @@ static ssize_t store_alert_led(struct device *dev, reg = i2c_smbus_read_byte_data(to_i2c_client(dev), FSCHMD_REG_CONTROL); if (v) - reg |= FSCHMD_CONTROL_ALERT_LED_MASK; + reg |= FSCHMD_CONTROL_ALERT_LED; else - reg &= ~FSCHMD_CONTROL_ALERT_LED_MASK; + reg &= ~FSCHMD_CONTROL_ALERT_LED; i2c_smbus_write_byte_data(to_i2c_client(dev), FSCHMD_REG_CONTROL, reg); @@ -683,11 +680,11 @@ static int fschmd_probe(struct i2c_client *client, } /* Read the special DMI table for fscher and newer chips */ - if (kind == fscher || kind >= fschrc) { + if ((kind == fscher || kind >= fschrc) && dmi_vref == -1) { dmi_walk(fschmd_dmi_decode); if (dmi_vref == -1) { - printk(KERN_WARNING FSCHMD_NAME - ": Couldn't get voltage scaling factors from " + dev_warn(&client->dev, + "Couldn't get voltage scaling factors from " "BIOS DMI table, using builtin defaults\n"); dmi_vref = 33; } @@ -736,7 +733,7 @@ static int fschmd_probe(struct i2c_client *client, } revision = i2c_smbus_read_byte_data(client, FSCHMD_REG_REVISION); - printk(KERN_INFO FSCHMD_NAME ": Detected FSC %s chip, revision: %d\n", + dev_info(&client->dev, "Detected FSC %s chip, revision: %d\n", names[data->kind], (int) revision); return 0; @@ -798,7 +795,7 @@ static struct fschmd_data *fschmd_update_device(struct device *dev) data->temp_act[i] < data->temp_max[i]) i2c_smbus_write_byte_data(client, FSCHMD_REG_TEMP_STATE[data->kind][i], - FSCHMD_TEMP_ALERT_MASK); + FSCHMD_TEMP_ALERT); } for (i = 0; i < FSCHMD_NO_FAN_SENSORS[data->kind]; i++) { @@ -816,11 +813,11 @@ static struct fschmd_data *fschmd_update_device(struct device *dev) FSCHMD_REG_FAN_MIN[data->kind][i]); /* reset fan status if speed is back to > 0 */ - if ((data->fan_status[i] & FSCHMD_FAN_ALARM_MASK) && + if ((data->fan_status[i] & FSCHMD_FAN_ALARM) && data->fan_act[i]) i2c_smbus_write_byte_data(client, FSCHMD_REG_FAN_STATE[data->kind][i], - FSCHMD_FAN_ALARM_MASK); + FSCHMD_FAN_ALARM); } for (i = 0; i < 3; i++) @@ -857,7 +854,7 @@ static void __exit fschmd_exit(void) i2c_del_driver(&fschmd_driver); } -MODULE_AUTHOR("Hans de Goede "); +MODULE_AUTHOR("Hans de Goede "); MODULE_DESCRIPTION("FSC Poseidon, Hermes, Scylla, Heracles and " "Heimdall driver"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 97950c3d423e474ef887749b238ee67731b532fe Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 7 Jan 2009 16:37:33 +0100 Subject: hwmon: (fschmd) Add watchdog support This patch adds support for the watchdog part found in _all_ supported FSC sensor chips. Signed-off-by: Hans de Goede Signed-off-by: Jean Delvare --- drivers/hwmon/Kconfig | 5 +- drivers/hwmon/fschmd.c | 401 ++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 386 insertions(+), 20 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index aba01b4ceca..91975148f4b 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -329,10 +329,11 @@ config SENSORS_FSCHMD depends on X86 && I2C && EXPERIMENTAL help If you say yes here you get support for various Fujitsu Siemens - Computers sensor chips. + Computers sensor chips, including support for the integrated + watchdog. This is a new merged driver for FSC sensor chips which is intended - as a replacment for the fscpos, fscscy and fscher drivers and adds + as a replacement for the fscpos, fscscy and fscher drivers and adds support for several other FCS sensor chips. This driver can also be built as a module. If so, the module diff --git a/drivers/hwmon/fschmd.c b/drivers/hwmon/fschmd.c index c188b713502..8bb1a44a1de 100644 --- a/drivers/hwmon/fschmd.c +++ b/drivers/hwmon/fschmd.c @@ -42,11 +42,20 @@ #include #include #include +#include +#include +#include +#include +#include /* Addresses to scan */ static const unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END }; /* Insmod parameters */ +static int nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); I2C_CLIENT_INSMOD_5(fscpos, fscher, fscscy, fschrc, fschmd); /* @@ -65,11 +74,18 @@ I2C_CLIENT_INSMOD_5(fscpos, fscher, fscscy, fschrc, fschmd); #define FSCHMD_CONTROL_ALERT_LED 0x01 -/* watchdog (support to be implemented) */ +/* watchdog */ #define FSCHMD_REG_WDOG_PRESET 0x28 #define FSCHMD_REG_WDOG_STATE 0x23 #define FSCHMD_REG_WDOG_CONTROL 0x21 +#define FSCHMD_WDOG_CONTROL_TRIGGER 0x10 +#define FSCHMD_WDOG_CONTROL_STARTED 0x10 /* the same as trigger */ +#define FSCHMD_WDOG_CONTROL_STOP 0x20 +#define FSCHMD_WDOG_CONTROL_RESOLUTION 0x40 + +#define FSCHMD_WDOG_STATE_CARDRESET 0x02 + /* voltages, weird order is to keep the same order as the old drivers */ static const u8 FSCHMD_REG_VOLT[3] = { 0x45, 0x42, 0x48 }; @@ -206,14 +222,26 @@ static struct i2c_driver fschmd_driver = { */ struct fschmd_data { + struct i2c_client *client; struct device *hwmon_dev; struct mutex update_lock; + struct mutex watchdog_lock; + struct list_head list; /* member of the watchdog_data_list */ + struct kref kref; + struct miscdevice watchdog_miscdev; int kind; + unsigned long watchdog_is_open; + char watchdog_expect_close; + char watchdog_name[10]; /* must be unique to avoid sysfs conflict */ char valid; /* zero until following fields are valid */ unsigned long last_updated; /* in jiffies */ /* register values */ + u8 revision; /* chip revision */ u8 global_control; /* global control register */ + u8 watchdog_control; /* watchdog control register */ + u8 watchdog_state; /* watchdog status register */ + u8 watchdog_preset; /* watchdog counter preset on trigger val */ u8 volt[3]; /* 12, 5, battery voltage */ u8 temp_act[5]; /* temperature */ u8 temp_status[5]; /* status of sensor */ @@ -225,11 +253,28 @@ struct fschmd_data { }; /* Global variables to hold information read from special DMI tables, which are - available on FSC machines with an fscher or later chip. */ + available on FSC machines with an fscher or later chip. There is no need to + protect these with a lock as they are only modified from our attach function + which always gets called with the i2c-core lock held and never accessed + before the attach function is done with them. */ static int dmi_mult[3] = { 490, 200, 100 }; static int dmi_offset[3] = { 0, 0, 0 }; static int dmi_vref = -1; +/* Somewhat ugly :( global data pointer list with all fschmd devices, so that + we can find our device data as when using misc_register there is no other + method to get to ones device data from the open fop. */ +static LIST_HEAD(watchdog_data_list); +/* Note this lock not only protect list access, but also data.kref access */ +static DEFINE_MUTEX(watchdog_data_mutex); + +/* Release our data struct when we're detached from the i2c client *and* all + references to our watchdog device are released */ +static void fschmd_release_resources(struct kref *ref) +{ + struct fschmd_data *data = container_of(ref, struct fschmd_data, kref); + kfree(data); +} /* * Sysfs attr show / store functions @@ -548,7 +593,265 @@ static struct sensor_device_attribute fschmd_fan_attr[] = { /* - * Real code + * Watchdog routines + */ + +static int watchdog_set_timeout(struct fschmd_data *data, int timeout) +{ + int ret, resolution; + int kind = data->kind + 1; /* 0-x array index -> 1-x module param */ + + /* 2 second or 60 second resolution? */ + if (timeout <= 510 || kind == fscpos || kind == fscscy) + resolution = 2; + else + resolution = 60; + + if (timeout < resolution || timeout > (resolution * 255)) + return -EINVAL; + + mutex_lock(&data->watchdog_lock); + if (!data->client) { + ret = -ENODEV; + goto leave; + } + + if (resolution == 2) + data->watchdog_control &= ~FSCHMD_WDOG_CONTROL_RESOLUTION; + else + data->watchdog_control |= FSCHMD_WDOG_CONTROL_RESOLUTION; + + data->watchdog_preset = DIV_ROUND_UP(timeout, resolution); + + /* Write new timeout value */ + i2c_smbus_write_byte_data(data->client, FSCHMD_REG_WDOG_PRESET, + data->watchdog_preset); + /* Write new control register, do not trigger! */ + i2c_smbus_write_byte_data(data->client, FSCHMD_REG_WDOG_CONTROL, + data->watchdog_control & ~FSCHMD_WDOG_CONTROL_TRIGGER); + + ret = data->watchdog_preset * resolution; + +leave: + mutex_unlock(&data->watchdog_lock); + return ret; +} + +static int watchdog_get_timeout(struct fschmd_data *data) +{ + int timeout; + + mutex_lock(&data->watchdog_lock); + if (data->watchdog_control & FSCHMD_WDOG_CONTROL_RESOLUTION) + timeout = data->watchdog_preset * 60; + else + timeout = data->watchdog_preset * 2; + mutex_unlock(&data->watchdog_lock); + + return timeout; +} + +static int watchdog_trigger(struct fschmd_data *data) +{ + int ret = 0; + + mutex_lock(&data->watchdog_lock); + if (!data->client) { + ret = -ENODEV; + goto leave; + } + + data->watchdog_control |= FSCHMD_WDOG_CONTROL_TRIGGER; + i2c_smbus_write_byte_data(data->client, FSCHMD_REG_WDOG_CONTROL, + data->watchdog_control); +leave: + mutex_unlock(&data->watchdog_lock); + return ret; +} + +static int watchdog_stop(struct fschmd_data *data) +{ + int ret = 0; + + mutex_lock(&data->watchdog_lock); + if (!data->client) { + ret = -ENODEV; + goto leave; + } + + data->watchdog_control &= ~FSCHMD_WDOG_CONTROL_STARTED; + /* Don't store the stop flag in our watchdog control register copy, as + its a write only bit (read always returns 0) */ + i2c_smbus_write_byte_data(data->client, FSCHMD_REG_WDOG_CONTROL, + data->watchdog_control | FSCHMD_WDOG_CONTROL_STOP); +leave: + mutex_unlock(&data->watchdog_lock); + return ret; +} + +static int watchdog_open(struct inode *inode, struct file *filp) +{ + struct fschmd_data *pos, *data = NULL; + + /* We get called from drivers/char/misc.c with misc_mtx hold, and we + call misc_register() from fschmd_probe() with watchdog_data_mutex + hold, as misc_register() takes the misc_mtx lock, this is a possible + deadlock, so we use mutex_trylock here. */ + if (!mutex_trylock(&watchdog_data_mutex)) + return -ERESTARTSYS; + list_for_each_entry(pos, &watchdog_data_list, list) { + if (pos->watchdog_miscdev.minor == iminor(inode)) { + data = pos; + break; + } + } + /* Note we can never not have found data, so we don't check for this */ + kref_get(&data->kref); + mutex_unlock(&watchdog_data_mutex); + + if (test_and_set_bit(0, &data->watchdog_is_open)) + return -EBUSY; + + /* Start the watchdog */ + watchdog_trigger(data); + filp->private_data = data; + + return nonseekable_open(inode, filp); +} + +static int watchdog_release(struct inode *inode, struct file *filp) +{ + struct fschmd_data *data = filp->private_data; + + if (data->watchdog_expect_close) { + watchdog_stop(data); + data->watchdog_expect_close = 0; + } else { + watchdog_trigger(data); + dev_crit(&data->client->dev, + "unexpected close, not stopping watchdog!\n"); + } + + clear_bit(0, &data->watchdog_is_open); + + mutex_lock(&watchdog_data_mutex); + kref_put(&data->kref, fschmd_release_resources); + mutex_unlock(&watchdog_data_mutex); + + return 0; +} + +static ssize_t watchdog_write(struct file *filp, const char __user *buf, + size_t count, loff_t *offset) +{ + size_t ret; + struct fschmd_data *data = filp->private_data; + + if (count) { + if (!nowayout) { + size_t i; + + /* Clear it in case it was set with a previous write */ + data->watchdog_expect_close = 0; + + for (i = 0; i != count; i++) { + char c; + if (get_user(c, buf + i)) + return -EFAULT; + if (c == 'V') + data->watchdog_expect_close = 1; + } + } + ret = watchdog_trigger(data); + if (ret < 0) + return ret; + } + return count; +} + +static int watchdog_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + static struct watchdog_info ident = { + .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | + WDIOF_CARDRESET, + .identity = "FSC watchdog" + }; + int i, ret = 0; + struct fschmd_data *data = filp->private_data; + + switch (cmd) { + case WDIOC_GETSUPPORT: + ident.firmware_version = data->revision; + if (!nowayout) + ident.options |= WDIOF_MAGICCLOSE; + if (copy_to_user((void __user *)arg, &ident, sizeof(ident))) + ret = -EFAULT; + break; + + case WDIOC_GETSTATUS: + ret = put_user(0, (int __user *)arg); + break; + + case WDIOC_GETBOOTSTATUS: + if (data->watchdog_state & FSCHMD_WDOG_STATE_CARDRESET) + ret = put_user(WDIOF_CARDRESET, (int __user *)arg); + else + ret = put_user(0, (int __user *)arg); + break; + + case WDIOC_KEEPALIVE: + ret = watchdog_trigger(data); + break; + + case WDIOC_GETTIMEOUT: + i = watchdog_get_timeout(data); + ret = put_user(i, (int __user *)arg); + break; + + case WDIOC_SETTIMEOUT: + if (get_user(i, (int __user *)arg)) { + ret = -EFAULT; + break; + } + ret = watchdog_set_timeout(data, i); + if (ret > 0) + ret = put_user(ret, (int __user *)arg); + break; + + case WDIOC_SETOPTIONS: + if (get_user(i, (int __user *)arg)) { + ret = -EFAULT; + break; + } + + if (i & WDIOS_DISABLECARD) + ret = watchdog_stop(data); + else if (i & WDIOS_ENABLECARD) + ret = watchdog_trigger(data); + else + ret = -EINVAL; + + break; + default: + ret = -ENOTTY; + } + + return ret; +} + +static struct file_operations watchdog_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .open = watchdog_open, + .release = watchdog_release, + .write = watchdog_write, + .ioctl = watchdog_ioctl, +}; + + +/* + * Detect, register, unregister and update device functions */ /* DMI decode routine to read voltage scaling factors from special DMI tables, @@ -658,9 +961,9 @@ static int fschmd_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct fschmd_data *data; - u8 revision; const char * const names[5] = { "Poseidon", "Hermes", "Scylla", "Heracles", "Heimdall" }; + const int watchdog_minors[] = { WATCHDOG_MINOR, 212, 213, 214, 215 }; int i, err; enum chips kind = id->driver_data; @@ -670,6 +973,13 @@ static int fschmd_probe(struct i2c_client *client, i2c_set_clientdata(client, data); mutex_init(&data->update_lock); + mutex_init(&data->watchdog_lock); + INIT_LIST_HEAD(&data->list); + kref_init(&data->kref); + /* Store client pointer in our data struct for watchdog usage + (where the client is found through a data ptr instead of the + otherway around) */ + data->client = client; if (kind == fscpos) { /* The Poseidon has hardwired temp limits, fill these @@ -690,6 +1000,17 @@ static int fschmd_probe(struct i2c_client *client, } } + /* Read in some never changing registers */ + data->revision = i2c_smbus_read_byte_data(client, FSCHMD_REG_REVISION); + data->global_control = i2c_smbus_read_byte_data(client, + FSCHMD_REG_CONTROL); + data->watchdog_control = i2c_smbus_read_byte_data(client, + FSCHMD_REG_WDOG_CONTROL); + data->watchdog_state = i2c_smbus_read_byte_data(client, + FSCHMD_REG_WDOG_STATE); + data->watchdog_preset = i2c_smbus_read_byte_data(client, + FSCHMD_REG_WDOG_PRESET); + /* i2c kind goes from 1-5, we want from 0-4 to address arrays */ data->kind = kind - 1; @@ -732,9 +1053,43 @@ static int fschmd_probe(struct i2c_client *client, goto exit_detach; } - revision = i2c_smbus_read_byte_data(client, FSCHMD_REG_REVISION); + /* We take the data_mutex lock early so that watchdog_open() cannot + run when misc_register() has completed, but we've not yet added + our data to the watchdog_data_list (and set the default timeout) */ + mutex_lock(&watchdog_data_mutex); + for (i = 0; i < ARRAY_SIZE(watchdog_minors); i++) { + /* Register our watchdog part */ + snprintf(data->watchdog_name, sizeof(data->watchdog_name), + "watchdog%c", (i == 0) ? '\0' : ('0' + i)); + data->watchdog_miscdev.name = data->watchdog_name; + data->watchdog_miscdev.fops = &watchdog_fops; + data->watchdog_miscdev.minor = watchdog_minors[i]; + err = misc_register(&data->watchdog_miscdev); + if (err == -EBUSY) + continue; + if (err) { + data->watchdog_miscdev.minor = 0; + dev_err(&client->dev, + "Registering watchdog chardev: %d\n", err); + break; + } + + list_add(&data->list, &watchdog_data_list); + watchdog_set_timeout(data, 60); + dev_info(&client->dev, + "Registered watchdog chardev major 10, minor: %d\n", + watchdog_minors[i]); + break; + } + if (i == ARRAY_SIZE(watchdog_minors)) { + data->watchdog_miscdev.minor = 0; + dev_warn(&client->dev, "Couldn't register watchdog chardev " + "(due to no free minor)\n"); + } + mutex_unlock(&watchdog_data_mutex); + dev_info(&client->dev, "Detected FSC %s chip, revision: %d\n", - names[data->kind], (int) revision); + names[data->kind], (int) data->revision); return 0; @@ -748,6 +1103,24 @@ static int fschmd_remove(struct i2c_client *client) struct fschmd_data *data = i2c_get_clientdata(client); int i; + /* Unregister the watchdog (if registered) */ + if (data->watchdog_miscdev.minor) { + misc_deregister(&data->watchdog_miscdev); + if (data->watchdog_is_open) { + dev_warn(&client->dev, + "i2c client detached with watchdog open! " + "Stopping watchdog.\n"); + watchdog_stop(data); + } + mutex_lock(&watchdog_data_mutex); + list_del(&data->list); + mutex_unlock(&watchdog_data_mutex); + /* Tell the watchdog code the client is gone */ + mutex_lock(&data->watchdog_lock); + data->client = NULL; + mutex_unlock(&data->watchdog_lock); + } + /* Check if registered in case we're called from fschmd_detect to cleanup after an error */ if (data->hwmon_dev) @@ -762,7 +1135,10 @@ static int fschmd_remove(struct i2c_client *client) device_remove_file(&client->dev, &fschmd_fan_attr[i].dev_attr); - kfree(data); + mutex_lock(&watchdog_data_mutex); + kref_put(&data->kref, fschmd_release_resources); + mutex_unlock(&watchdog_data_mutex); + return 0; } @@ -824,17 +1200,6 @@ static struct fschmd_data *fschmd_update_device(struct device *dev) data->volt[i] = i2c_smbus_read_byte_data(client, FSCHMD_REG_VOLT[i]); - data->global_control = i2c_smbus_read_byte_data(client, - FSCHMD_REG_CONTROL); - - /* To be implemented in the future - data->watchdog[0] = i2c_smbus_read_byte_data(client, - FSCHMD_REG_WDOG_PRESET); - data->watchdog[1] = i2c_smbus_read_byte_data(client, - FSCHMD_REG_WDOG_STATE); - data->watchdog[2] = i2c_smbus_read_byte_data(client, - FSCHMD_REG_WDOG_CONTROL); */ - data->last_updated = jiffies; data->valid = 1; } -- cgit v1.2.3 From 0589c2de643ef71a684ba6d219532f9e2a3e554b Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 7 Jan 2009 16:37:33 +0100 Subject: hwmon: Deprecate the fscher and fscpos drivers Now that the new merged fschmd driver has gained support for the watchdog integrated into these IC's, there is no more reason to keep the old fscher and fscpos drivers around, so mark them as deprecated. Signed-off-by: Hans de Goede Signed-off-by: Jean Delvare --- drivers/hwmon/Kconfig | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 91975148f4b..3c34fb5e419 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -305,9 +305,13 @@ config SENSORS_F75375S will be called f75375s. config SENSORS_FSCHER - tristate "FSC Hermes" + tristate "FSC Hermes (DEPRECATED)" depends on X86 && I2C help + This driver is DEPRECATED please use the new merged fschmd + ("FSC Poseidon, Scylla, Hermes, Heimdall and Heracles") driver + instead. + If you say yes here you get support for Fujitsu Siemens Computers Hermes sensor chips. @@ -315,9 +319,13 @@ config SENSORS_FSCHER will be called fscher. config SENSORS_FSCPOS - tristate "FSC Poseidon" + tristate "FSC Poseidon (DEPRECATED)" depends on X86 && I2C help + This driver is DEPRECATED please use the new merged fschmd + ("FSC Poseidon, Scylla, Hermes, Heimdall and Heracles") driver + instead. + If you say yes here you get support for Fujitsu Siemens Computers Poseidon sensor chips. @@ -326,15 +334,15 @@ config SENSORS_FSCPOS config SENSORS_FSCHMD tristate "FSC Poseidon, Scylla, Hermes, Heimdall and Heracles" - depends on X86 && I2C && EXPERIMENTAL + depends on X86 && I2C help If you say yes here you get support for various Fujitsu Siemens Computers sensor chips, including support for the integrated watchdog. - This is a new merged driver for FSC sensor chips which is intended - as a replacement for the fscpos, fscscy and fscher drivers and adds - support for several other FCS sensor chips. + This is a merged driver for FSC sensor chips replacing the fscpos, + fscscy and fscher drivers and adding support for several other FSC + sensor chips. This driver can also be built as a module. If so, the module will be called fschmd. -- cgit v1.2.3 From 2b7300513b98e05058a803de3beb8a1c0a0c61d9 Mon Sep 17 00:00:00 2001 From: Kaiwan N Billimoria Date: Wed, 7 Jan 2009 16:37:34 +0100 Subject: hwmon: (lm70) Code streamlining and cleanup This fixes a byteswap bug in the LM70 temperature sensor driver, which was previously covered up by a converse bug in the driver for the LM70EVAL-LLP board (which is also fixed). Other fixes: doc updates, remove an annoying msleep(), and improve three-wire protocol handling. Signed-off-by: Kaiwan N Billimoria [ dbrownell@users.sourceforge.net: doc and whitespace tweaks ] Signed-off-by: David Brownell Signed-off-by: Jean Delvare --- drivers/hwmon/lm70.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/lm70.c b/drivers/hwmon/lm70.c index d435f003292..9f9741b1d2b 100644 --- a/drivers/hwmon/lm70.c +++ b/drivers/hwmon/lm70.c @@ -65,10 +65,9 @@ static ssize_t lm70_sense_temp(struct device *dev, "spi_write_then_read failed with status %d\n", status); goto out; } - dev_dbg(dev, "rxbuf[1] : 0x%x rxbuf[0] : 0x%x\n", rxbuf[1], rxbuf[0]); - - raw = (rxbuf[1] << 8) + rxbuf[0]; - dev_dbg(dev, "raw=0x%x\n", raw); + raw = (rxbuf[0] << 8) + rxbuf[1]; + dev_dbg(dev, "rxbuf[0] : 0x%02x rxbuf[1] : 0x%02x raw=0x%04x\n", + rxbuf[0], rxbuf[1], raw); /* * The "raw" temperature read into rxbuf[] is a 16-bit signed 2's @@ -109,6 +108,8 @@ static int __devinit lm70_probe(struct spi_device *spi) if ((spi->mode & (SPI_CPOL|SPI_CPHA)) || !(spi->mode & SPI_3WIRE)) return -EINVAL; + /* NOTE: we assume 8-bit words, and convert to 16 bits manually */ + p_lm70 = kzalloc(sizeof *p_lm70, GFP_KERNEL); if (!p_lm70) return -ENOMEM; -- cgit v1.2.3 From c8ac32e4711639c81e5f4d4cd78c8f21675a2bae Mon Sep 17 00:00:00 2001 From: Manuel Lauss Date: Wed, 7 Jan 2009 16:37:34 +0100 Subject: hwmon: (lm70) Add TI TMP121 support The Texas Instruments TMP121 is a SPI temperature sensor very similar to the LM70, with slightly higher resolution. This patch extends the LM70 driver to support the TMP121. The TMP123 differs in pin assign- ment. Signed-off-by: Manuel Lauss Signed-off-by: Jean Delvare --- drivers/hwmon/Kconfig | 5 +-- drivers/hwmon/lm70.c | 84 +++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 77 insertions(+), 12 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 3c34fb5e419..4b33bc82cc2 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -428,11 +428,12 @@ config SENSORS_LM63 will be called lm63. config SENSORS_LM70 - tristate "National Semiconductor LM70" + tristate "National Semiconductor LM70 / Texas Instruments TMP121" depends on SPI_MASTER && EXPERIMENTAL help If you say yes here you get support for the National Semiconductor - LM70 digital temperature sensor chip. + LM70 and Texas Instruments TMP121/TMP123 digital temperature + sensor chips. This driver can also be built as a module. If so, the module will be called lm70. diff --git a/drivers/hwmon/lm70.c b/drivers/hwmon/lm70.c index 9f9741b1d2b..ae6204f3321 100644 --- a/drivers/hwmon/lm70.c +++ b/drivers/hwmon/lm70.c @@ -37,9 +37,13 @@ #define DRVNAME "lm70" +#define LM70_CHIP_LM70 0 /* original NS LM70 */ +#define LM70_CHIP_TMP121 1 /* TI TMP121/TMP123 */ + struct lm70 { struct device *hwmon_dev; struct mutex lock; + unsigned int chip; }; /* sysfs hook function */ @@ -47,7 +51,7 @@ static ssize_t lm70_sense_temp(struct device *dev, struct device_attribute *attr, char *buf) { struct spi_device *spi = to_spi_device(dev); - int status, val; + int status, val = 0; u8 rxbuf[2]; s16 raw=0; struct lm70 *p_lm70 = dev_get_drvdata(&spi->dev); @@ -70,6 +74,7 @@ static ssize_t lm70_sense_temp(struct device *dev, rxbuf[0], rxbuf[1], raw); /* + * LM70: * The "raw" temperature read into rxbuf[] is a 16-bit signed 2's * complement value. Only the MSB 11 bits (1 sign + 10 temperature * bits) are meaningful; the LSB 5 bits are to be discarded. @@ -79,8 +84,21 @@ static ssize_t lm70_sense_temp(struct device *dev, * by 0.25. Also multiply by 1000 to represent in millidegrees * Celsius. * So it's equivalent to multiplying by 0.25 * 1000 = 250. + * + * TMP121/TMP123: + * 13 bits of 2's complement data, discard LSB 3 bits, + * resolution 0.0625 degrees celsius. */ - val = ((int)raw/32) * 250; + switch (p_lm70->chip) { + case LM70_CHIP_LM70: + val = ((int)raw / 32) * 250; + break; + + case LM70_CHIP_TMP121: + val = ((int)raw / 8) * 625 / 10; + break; + } + status = sprintf(buf, "%d\n", val); /* millidegrees Celsius */ out: mutex_unlock(&p_lm70->lock); @@ -92,22 +110,31 @@ static DEVICE_ATTR(temp1_input, S_IRUGO, lm70_sense_temp, NULL); static ssize_t lm70_show_name(struct device *dev, struct device_attribute *devattr, char *buf) { - return sprintf(buf, "lm70\n"); + struct lm70 *p_lm70 = dev_get_drvdata(dev); + int ret; + + switch (p_lm70->chip) { + case LM70_CHIP_LM70: + ret = sprintf(buf, "lm70\n"); + break; + case LM70_CHIP_TMP121: + ret = sprintf(buf, "tmp121\n"); + break; + default: + ret = -EINVAL; + } + return ret; } static DEVICE_ATTR(name, S_IRUGO, lm70_show_name, NULL); /*----------------------------------------------------------------------*/ -static int __devinit lm70_probe(struct spi_device *spi) +static int __devinit common_probe(struct spi_device *spi, int chip) { struct lm70 *p_lm70; int status; - /* signaling is SPI_MODE_0 on a 3-wire link (shared SI/SO) */ - if ((spi->mode & (SPI_CPOL|SPI_CPHA)) || !(spi->mode & SPI_3WIRE)) - return -EINVAL; - /* NOTE: we assume 8-bit words, and convert to 16 bits manually */ p_lm70 = kzalloc(sizeof *p_lm70, GFP_KERNEL); @@ -115,6 +142,7 @@ static int __devinit lm70_probe(struct spi_device *spi) return -ENOMEM; mutex_init(&p_lm70->lock); + p_lm70->chip = chip; /* sysfs hook */ p_lm70->hwmon_dev = hwmon_device_register(&spi->dev); @@ -142,6 +170,24 @@ out_dev_reg_failed: return status; } +static int __devinit lm70_probe(struct spi_device *spi) +{ + /* signaling is SPI_MODE_0 on a 3-wire link (shared SI/SO) */ + if ((spi->mode & (SPI_CPOL | SPI_CPHA)) || !(spi->mode & SPI_3WIRE)) + return -EINVAL; + + return common_probe(spi, LM70_CHIP_LM70); +} + +static int __devinit tmp121_probe(struct spi_device *spi) +{ + /* signaling is SPI_MODE_0 with only MISO connected */ + if (spi->mode & (SPI_CPOL | SPI_CPHA)) + return -EINVAL; + + return common_probe(spi, LM70_CHIP_TMP121); +} + static int __devexit lm70_remove(struct spi_device *spi) { struct lm70 *p_lm70 = dev_get_drvdata(&spi->dev); @@ -155,6 +201,15 @@ static int __devexit lm70_remove(struct spi_device *spi) return 0; } +static struct spi_driver tmp121_driver = { + .driver = { + .name = "tmp121", + .owner = THIS_MODULE, + }, + .probe = tmp121_probe, + .remove = __devexit_p(lm70_remove), +}; + static struct spi_driver lm70_driver = { .driver = { .name = "lm70", @@ -166,17 +221,26 @@ static struct spi_driver lm70_driver = { static int __init init_lm70(void) { - return spi_register_driver(&lm70_driver); + int ret = spi_register_driver(&lm70_driver); + if (ret) + return ret; + + ret = spi_register_driver(&tmp121_driver); + if (ret) + spi_unregister_driver(&lm70_driver); + + return ret; } static void __exit cleanup_lm70(void) { spi_unregister_driver(&lm70_driver); + spi_unregister_driver(&tmp121_driver); } module_init(init_lm70); module_exit(cleanup_lm70); MODULE_AUTHOR("Kaiwan N Billimoria"); -MODULE_DESCRIPTION("National Semiconductor LM70 Linux driver"); +MODULE_DESCRIPTION("NS LM70 / TI TMP121/TMP123 Linux driver"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From b9acb64a385c5b26fc392e0d58ac7b8e0a2cd812 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 7 Jan 2009 16:37:35 +0100 Subject: hwmon: Check for ACPI resource conflicts Check for ACPI resource conflicts in hwmon drivers. I've included all Super-I/O and PCI drivers. I've voluntarily left out: * Vendor-specific drivers: if they conflicted on any system, this would pretty much mean that they conflict on all systems, and we would know by now. * Legacy ISA drivers (lm78 and w83781d): they only support chips found on old designs were ACPI either wasn't supported or didn't deal with thermal management. * Drivers accessing the I/O resources indirectly (e.g. through SMBus): the checks are already done where they belong, i.e. in the bus drivers. Signed-off-by: Jean Delvare Acked-by: David Hubbard --- drivers/hwmon/dme1737.c | 5 +++++ drivers/hwmon/f71805f.c | 5 +++++ drivers/hwmon/f71882fg.c | 5 +++++ drivers/hwmon/it87.c | 5 +++++ drivers/hwmon/pc87360.c | 6 ++++++ drivers/hwmon/pc87427.c | 5 +++++ drivers/hwmon/sis5595.c | 5 +++++ drivers/hwmon/smsc47b397.c | 5 +++++ drivers/hwmon/smsc47m1.c | 5 +++++ drivers/hwmon/via686a.c | 5 +++++ drivers/hwmon/vt1211.c | 5 +++++ drivers/hwmon/vt8231.c | 5 +++++ drivers/hwmon/w83627ehf.c | 6 ++++++ drivers/hwmon/w83627hf.c | 5 +++++ 14 files changed, 72 insertions(+) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/dme1737.c b/drivers/hwmon/dme1737.c index 27a5d397f9a..3df202a9ad7 100644 --- a/drivers/hwmon/dme1737.c +++ b/drivers/hwmon/dme1737.c @@ -34,6 +34,7 @@ #include #include #include +#include #include /* ISA device, if found */ @@ -2361,6 +2362,10 @@ static int __init dme1737_isa_device_add(unsigned short addr) }; int err; + err = acpi_check_resource_conflict(&res); + if (err) + goto exit; + if (!(pdev = platform_device_alloc("dme1737", addr))) { printk(KERN_ERR "dme1737: Failed to allocate device.\n"); err = -ENOMEM; diff --git a/drivers/hwmon/f71805f.c b/drivers/hwmon/f71805f.c index 7a14a2dbb75..89987657925 100644 --- a/drivers/hwmon/f71805f.c +++ b/drivers/hwmon/f71805f.c @@ -39,6 +39,7 @@ #include #include #include +#include #include static unsigned short force_id; @@ -1455,6 +1456,10 @@ static int __init f71805f_device_add(unsigned short address, } res.name = pdev->name; + err = acpi_check_resource_conflict(&res); + if (err) + goto exit_device_put; + err = platform_device_add_resources(pdev, &res, 1); if (err) { printk(KERN_ERR DRVNAME ": Device resource addition failed " diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c index d867b377d4e..609cafff86b 100644 --- a/drivers/hwmon/f71882fg.c +++ b/drivers/hwmon/f71882fg.c @@ -28,6 +28,7 @@ #include #include #include +#include #define DRVNAME "f71882fg" @@ -1929,6 +1930,10 @@ static int __init f71882fg_device_add(unsigned short address, return -ENOMEM; res.name = f71882fg_pdev->name; + err = acpi_check_resource_conflict(&res); + if (err) + return err; + err = platform_device_add_resources(f71882fg_pdev, &res, 1); if (err) { printk(KERN_ERR DRVNAME ": Device resource addition failed\n"); diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 0e0d692f0c9..88e71f195ec 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -49,6 +49,7 @@ #include #include #include +#include #include #define DRVNAME "it87" @@ -1552,6 +1553,10 @@ static int __init it87_device_add(unsigned short address, }; int err; + err = acpi_check_resource_conflict(&res); + if (err) + goto exit; + pdev = platform_device_alloc(DRVNAME, address); if (!pdev) { err = -ENOMEM; diff --git a/drivers/hwmon/pc87360.c b/drivers/hwmon/pc87360.c index 5fbfa34c110..fb052fea374 100644 --- a/drivers/hwmon/pc87360.c +++ b/drivers/hwmon/pc87360.c @@ -43,6 +43,7 @@ #include #include #include +#include #include static u8 devid; @@ -1627,6 +1628,11 @@ static int __init pc87360_device_add(unsigned short address) continue; res.start = extra_isa[i]; res.end = extra_isa[i] + PC87360_EXTENT - 1; + + err = acpi_check_resource_conflict(&res); + if (err) + goto exit_device_put; + err = platform_device_add_resources(pdev, &res, 1); if (err) { printk(KERN_ERR "pc87360: Device resource[%d] " diff --git a/drivers/hwmon/pc87427.c b/drivers/hwmon/pc87427.c index 7265f22ae5c..3a8a0f7a773 100644 --- a/drivers/hwmon/pc87427.c +++ b/drivers/hwmon/pc87427.c @@ -32,6 +32,7 @@ #include #include #include +#include #include static unsigned short force_id; @@ -524,6 +525,10 @@ static int __init pc87427_device_add(unsigned short address) }; int err; + err = acpi_check_resource_conflict(&res); + if (err) + goto exit; + pdev = platform_device_alloc(DRVNAME, address); if (!pdev) { err = -ENOMEM; diff --git a/drivers/hwmon/sis5595.c b/drivers/hwmon/sis5595.c index a276806f3d5..aa2e8318f16 100644 --- a/drivers/hwmon/sis5595.c +++ b/drivers/hwmon/sis5595.c @@ -62,6 +62,7 @@ #include #include #include +#include #include @@ -727,6 +728,10 @@ static int __devinit sis5595_device_add(unsigned short address) }; int err; + err = acpi_check_resource_conflict(&res); + if (err) + goto exit; + pdev = platform_device_alloc("sis5595", address); if (!pdev) { err = -ENOMEM; diff --git a/drivers/hwmon/smsc47b397.c b/drivers/hwmon/smsc47b397.c index eb03544c731..6f6d52b4fb6 100644 --- a/drivers/hwmon/smsc47b397.c +++ b/drivers/hwmon/smsc47b397.c @@ -36,6 +36,7 @@ #include #include #include +#include #include static unsigned short force_id; @@ -303,6 +304,10 @@ static int __init smsc47b397_device_add(unsigned short address) }; int err; + err = acpi_check_resource_conflict(&res); + if (err) + goto exit; + pdev = platform_device_alloc(DRVNAME, address); if (!pdev) { err = -ENOMEM; diff --git a/drivers/hwmon/smsc47m1.c b/drivers/hwmon/smsc47m1.c index d1b49854873..a92dbb97ee9 100644 --- a/drivers/hwmon/smsc47m1.c +++ b/drivers/hwmon/smsc47m1.c @@ -37,6 +37,7 @@ #include #include #include +#include #include static unsigned short force_id; @@ -705,6 +706,10 @@ static int __init smsc47m1_device_add(unsigned short address, }; int err; + err = acpi_check_resource_conflict(&res); + if (err) + goto exit; + pdev = platform_device_alloc(DRVNAME, address); if (!pdev) { err = -ENOMEM; diff --git a/drivers/hwmon/via686a.c b/drivers/hwmon/via686a.c index f1ee5e73196..a022aedcaac 100644 --- a/drivers/hwmon/via686a.c +++ b/drivers/hwmon/via686a.c @@ -41,6 +41,7 @@ #include #include #include +#include #include @@ -783,6 +784,10 @@ static int __devinit via686a_device_add(unsigned short address) }; int err; + err = acpi_check_resource_conflict(&res); + if (err) + goto exit; + pdev = platform_device_alloc("via686a", address); if (!pdev) { err = -ENOMEM; diff --git a/drivers/hwmon/vt1211.c b/drivers/hwmon/vt1211.c index 12b43590fa5..b0ce3785228 100644 --- a/drivers/hwmon/vt1211.c +++ b/drivers/hwmon/vt1211.c @@ -32,6 +32,7 @@ #include #include #include +#include #include static int uch_config = -1; @@ -1259,6 +1260,10 @@ static int __init vt1211_device_add(unsigned short address) } res.name = pdev->name; + err = acpi_check_resource_conflict(&res); + if (err) + goto EXIT; + err = platform_device_add_resources(pdev, &res, 1); if (err) { printk(KERN_ERR DRVNAME ": Device resource addition failed " diff --git a/drivers/hwmon/vt8231.c b/drivers/hwmon/vt8231.c index 5bc57275cae..9982b45fbb1 100644 --- a/drivers/hwmon/vt8231.c +++ b/drivers/hwmon/vt8231.c @@ -35,6 +35,7 @@ #include #include #include +#include #include static int force_addr; @@ -894,6 +895,10 @@ static int __devinit vt8231_device_add(unsigned short address) }; int err; + err = acpi_check_resource_conflict(&res); + if (err) + goto exit; + pdev = platform_device_alloc("vt8231", address); if (!pdev) { err = -ENOMEM; diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c index 075164dd65a..a3a01dc35a3 100644 --- a/drivers/hwmon/w83627ehf.c +++ b/drivers/hwmon/w83627ehf.c @@ -48,6 +48,7 @@ #include #include #include +#include #include #include "lm75.h" @@ -1544,6 +1545,11 @@ static int __init sensors_w83627ehf_init(void) res.start = address + IOREGION_OFFSET; res.end = address + IOREGION_OFFSET + IOREGION_LENGTH - 1; res.flags = IORESOURCE_IO; + + err = acpi_check_resource_conflict(&res); + if (err) + goto exit; + err = platform_device_add_resources(pdev, &res, 1); if (err) { printk(KERN_ERR DRVNAME ": Device resource addition failed " diff --git a/drivers/hwmon/w83627hf.c b/drivers/hwmon/w83627hf.c index b30e5796cb2..389150ba30d 100644 --- a/drivers/hwmon/w83627hf.c +++ b/drivers/hwmon/w83627hf.c @@ -50,6 +50,7 @@ #include #include #include +#include #include #include "lm75.h" @@ -1793,6 +1794,10 @@ static int __init w83627hf_device_add(unsigned short address, }; int err; + err = acpi_check_resource_conflict(&res); + if (err) + goto exit; + pdev = platform_device_alloc(DRVNAME, address); if (!pdev) { err = -ENOMEM; -- cgit v1.2.3 From 77fa49d94a75b5f9702c70b4fbe27b08b21317b9 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 7 Jan 2009 16:37:35 +0100 Subject: hwmon: Fix various typos Signed-off-by: Jean Delvare Acked-by: Hans de Goede Acked-by: David Hubbard --- drivers/hwmon/it87.c | 2 +- drivers/hwmon/w83627ehf.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 88e71f195ec..95a99c590da 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -1386,7 +1386,7 @@ static void __devinit it87_init_device(struct platform_device *pdev) it87_write_value(data, IT87_REG_TEMP_HIGH(i), 127); } - /* Check if temperature channnels are reset manually or by some reason */ + /* Check if temperature channels are reset manually or by some reason */ tmp = it87_read_value(data, IT87_REG_TEMP_ENABLE); if ((tmp & 0x3f) == 0) { /* Temp1,Temp3=thermistor; Temp2=thermal diode */ diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c index a3a01dc35a3..cb808d01536 100644 --- a/drivers/hwmon/w83627ehf.c +++ b/drivers/hwmon/w83627ehf.c @@ -503,7 +503,7 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev) } for (i = 0; i < 4; i++) { - /* pwmcfg, tolarance mapped for i=0, i=1 to same reg */ + /* pwmcfg, tolerance mapped for i=0, i=1 to same reg */ if (i != 1) { pwmcfg = w83627ehf_read_value(data, W83627EHF_REG_PWM_ENABLE[i]); -- cgit v1.2.3 From cfce41a6d643c001d416ead960caf04fae2d609a Mon Sep 17 00:00:00 2001 From: Eric Piel Date: Fri, 9 Jan 2009 16:41:01 -0800 Subject: LIS3LV02D: separate the core from HP ACPI API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sensor can be accessed via various buses. In particular, SPI, I²C and, on HP laptops, via a specific ACPI API (the only one currently supported). Separate this latest platform from the core of the sensor driver to allow support for the other bus type. The second, and more direct goal is actually to be able to merge this part with the hp-disk-leds driver, which has the same ACPI PNP number. Signed-off-by: Pavel Machek Signed-off-by: Eric Piel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/hwmon/Makefile | 2 +- drivers/hwmon/hp_accel.c | 265 ++++++++++++++++++++++++++++++++++++++++++++ drivers/hwmon/lis3lv02d.c | 273 ++++------------------------------------------ drivers/hwmon/lis3lv02d.h | 35 +++++- 4 files changed, 324 insertions(+), 251 deletions(-) create mode 100644 drivers/hwmon/hp_accel.c (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 8fd124eff64..19cb1ace3eb 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -49,7 +49,7 @@ obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o obj-$(CONFIG_SENSORS_IT87) += it87.o obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o -obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o +obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o hp_accel.o obj-$(CONFIG_SENSORS_LM63) += lm63.o obj-$(CONFIG_SENSORS_LM70) += lm70.o obj-$(CONFIG_SENSORS_LM75) += lm75.o diff --git a/drivers/hwmon/hp_accel.c b/drivers/hwmon/hp_accel.c new file mode 100644 index 00000000000..bf8d4058057 --- /dev/null +++ b/drivers/hwmon/hp_accel.c @@ -0,0 +1,265 @@ +/* + * hp_accel.c - Interface between LIS3LV02DL driver and HP ACPI BIOS + * + * Copyright (C) 2007-2008 Yan Burman + * Copyright (C) 2008 Eric Piel + * Copyright (C) 2008 Pavel Machek + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "lis3lv02d.h" + +#define DRIVER_NAME "lis3lv02d" +#define ACPI_MDPS_CLASS "accelerometer" + + +/* For automatic insertion of the module */ +static struct acpi_device_id lis3lv02d_device_ids[] = { + {"HPQ0004", 0}, /* HP Mobile Data Protection System PNP */ + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, lis3lv02d_device_ids); + + +/** + * lis3lv02d_acpi_init - ACPI _INI method: initialize the device. + * @handle: the handle of the device + * + * Returns AE_OK on success. + */ +acpi_status lis3lv02d_acpi_init(acpi_handle handle) +{ + return acpi_evaluate_object(handle, METHOD_NAME__INI, NULL, NULL); +} + +/** + * lis3lv02d_acpi_read - ACPI ALRD method: read a register + * @handle: the handle of the device + * @reg: the register to read + * @ret: result of the operation + * + * Returns AE_OK on success. + */ +acpi_status lis3lv02d_acpi_read(acpi_handle handle, int reg, u8 *ret) +{ + union acpi_object arg0 = { ACPI_TYPE_INTEGER }; + struct acpi_object_list args = { 1, &arg0 }; + unsigned long long lret; + acpi_status status; + + arg0.integer.value = reg; + + status = acpi_evaluate_integer(handle, "ALRD", &args, &lret); + *ret = lret; + return status; +} + +/** + * lis3lv02d_acpi_write - ACPI ALWR method: write to a register + * @handle: the handle of the device + * @reg: the register to write to + * @val: the value to write + * + * Returns AE_OK on success. + */ +acpi_status lis3lv02d_acpi_write(acpi_handle handle, int reg, u8 val) +{ + unsigned long long ret; /* Not used when writting */ + union acpi_object in_obj[2]; + struct acpi_object_list args = { 2, in_obj }; + + in_obj[0].type = ACPI_TYPE_INTEGER; + in_obj[0].integer.value = reg; + in_obj[1].type = ACPI_TYPE_INTEGER; + in_obj[1].integer.value = val; + + return acpi_evaluate_integer(handle, "ALWR", &args, &ret); +} + +static int lis3lv02d_dmi_matched(const struct dmi_system_id *dmi) +{ + adev.ac = *((struct axis_conversion *)dmi->driver_data); + printk(KERN_INFO DRIVER_NAME ": hardware type %s found.\n", dmi->ident); + + return 1; +} + +/* Represents, for each axis seen by userspace, the corresponding hw axis (+1). + * If the value is negative, the opposite of the hw value is used. */ +static struct axis_conversion lis3lv02d_axis_normal = {1, 2, 3}; +static struct axis_conversion lis3lv02d_axis_y_inverted = {1, -2, 3}; +static struct axis_conversion lis3lv02d_axis_x_inverted = {-1, 2, 3}; +static struct axis_conversion lis3lv02d_axis_z_inverted = {1, 2, -3}; +static struct axis_conversion lis3lv02d_axis_xy_rotated_left = {-2, 1, 3}; +static struct axis_conversion lis3lv02d_axis_xy_swap_inverted = {-2, -1, 3}; + +#define AXIS_DMI_MATCH(_ident, _name, _axis) { \ + .ident = _ident, \ + .callback = lis3lv02d_dmi_matched, \ + .matches = { \ + DMI_MATCH(DMI_PRODUCT_NAME, _name) \ + }, \ + .driver_data = &lis3lv02d_axis_##_axis \ +} +static struct dmi_system_id lis3lv02d_dmi_ids[] = { + /* product names are truncated to match all kinds of a same model */ + AXIS_DMI_MATCH("NC64x0", "HP Compaq nc64", x_inverted), + AXIS_DMI_MATCH("NC84x0", "HP Compaq nc84", z_inverted), + AXIS_DMI_MATCH("NX9420", "HP Compaq nx9420", x_inverted), + AXIS_DMI_MATCH("NW9440", "HP Compaq nw9440", x_inverted), + AXIS_DMI_MATCH("NC2510", "HP Compaq 2510", y_inverted), + AXIS_DMI_MATCH("NC8510", "HP Compaq 8510", xy_swap_inverted), + AXIS_DMI_MATCH("HP2133", "HP 2133", xy_rotated_left), + { NULL, } +/* Laptop models without axis info (yet): + * "NC651xx" "HP Compaq 651" + * "NC671xx" "HP Compaq 671" + * "NC6910" "HP Compaq 6910" + * HP Compaq 8710x Notebook PC / Mobile Workstation + * "NC2400" "HP Compaq nc2400" + * "NX74x0" "HP Compaq nx74" + * "NX6325" "HP Compaq nx6325" + * "NC4400" "HP Compaq nc4400" + */ +}; + + +static int lis3lv02d_add(struct acpi_device *device) +{ + u8 val; + + if (!device) + return -EINVAL; + + adev.device = device; + adev.init = lis3lv02d_acpi_init; + adev.read = lis3lv02d_acpi_read; + adev.write = lis3lv02d_acpi_write; + strcpy(acpi_device_name(device), DRIVER_NAME); + strcpy(acpi_device_class(device), ACPI_MDPS_CLASS); + device->driver_data = &adev; + + lis3lv02d_acpi_read(device->handle, WHO_AM_I, &val); + if ((val != LIS3LV02DL_ID) && (val != LIS302DL_ID)) { + printk(KERN_ERR DRIVER_NAME + ": Accelerometer chip not LIS3LV02D{L,Q}\n"); + } + + /* If possible use a "standard" axes order */ + if (dmi_check_system(lis3lv02d_dmi_ids) == 0) { + printk(KERN_INFO DRIVER_NAME ": laptop model unknown, " + "using default axes configuration\n"); + adev.ac = lis3lv02d_axis_normal; + } + + return lis3lv02d_init_device(&adev); +} + +static int lis3lv02d_remove(struct acpi_device *device, int type) +{ + if (!device) + return -EINVAL; + + lis3lv02d_joystick_disable(); + lis3lv02d_poweroff(device->handle); + + return lis3lv02d_remove_fs(); +} + + +#ifdef CONFIG_PM +static int lis3lv02d_suspend(struct acpi_device *device, pm_message_t state) +{ + /* make sure the device is off when we suspend */ + lis3lv02d_poweroff(device->handle); + return 0; +} + +static int lis3lv02d_resume(struct acpi_device *device) +{ + /* put back the device in the right state (ACPI might turn it on) */ + mutex_lock(&adev.lock); + if (adev.usage > 0) + lis3lv02d_poweron(device->handle); + else + lis3lv02d_poweroff(device->handle); + mutex_unlock(&adev.lock); + return 0; +} +#else +#define lis3lv02d_suspend NULL +#define lis3lv02d_resume NULL +#endif + +/* For the HP MDPS aka 3D Driveguard */ +static struct acpi_driver lis3lv02d_driver = { + .name = DRIVER_NAME, + .class = ACPI_MDPS_CLASS, + .ids = lis3lv02d_device_ids, + .ops = { + .add = lis3lv02d_add, + .remove = lis3lv02d_remove, + .suspend = lis3lv02d_suspend, + .resume = lis3lv02d_resume, + } +}; + +static int __init lis3lv02d_init_module(void) +{ + int ret; + + if (acpi_disabled) + return -ENODEV; + + ret = acpi_bus_register_driver(&lis3lv02d_driver); + if (ret < 0) + return ret; + + printk(KERN_INFO DRIVER_NAME " driver loaded.\n"); + + return 0; +} + +static void __exit lis3lv02d_exit_module(void) +{ + acpi_bus_unregister_driver(&lis3lv02d_driver); +} + +MODULE_DESCRIPTION("Glue between LIS3LV02Dx and HP ACPI BIOS"); +MODULE_AUTHOR("Yan Burman, Eric Piel, Pavel Machek"); +MODULE_LICENSE("GPL"); + +module_init(lis3lv02d_init_module); +module_exit(lis3lv02d_exit_module); + diff --git a/drivers/hwmon/lis3lv02d.c b/drivers/hwmon/lis3lv02d.c index c002144c76b..219d2d0d5a6 100644 --- a/drivers/hwmon/lis3lv02d.c +++ b/drivers/hwmon/lis3lv02d.c @@ -3,6 +3,7 @@ * * Copyright (C) 2007-2008 Yan Burman * Copyright (C) 2008 Eric Piel + * Copyright (C) 2008 Pavel Machek * * 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 @@ -39,7 +40,6 @@ #include "lis3lv02d.h" #define DRIVER_NAME "lis3lv02d" -#define ACPI_MDPS_CLASS "accelerometer" /* joystick device poll interval in milliseconds */ #define MDPS_POLL_INTERVAL 50 @@ -55,100 +55,17 @@ /* Maximum value our axis may get for the input device (signed 12 bits) */ #define MDPS_MAX_VAL 2048 -struct axis_conversion { - s8 x; - s8 y; - s8 z; -}; - -struct acpi_lis3lv02d { - struct acpi_device *device; /* The ACPI device */ - struct input_dev *idev; /* input device */ - struct task_struct *kthread; /* kthread for input */ - struct mutex lock; - struct platform_device *pdev; /* platform device */ - atomic_t count; /* interrupt count after last read */ - int xcalib; /* calibrated null value for x */ - int ycalib; /* calibrated null value for y */ - int zcalib; /* calibrated null value for z */ - unsigned char is_on; /* whether the device is on or off */ - unsigned char usage; /* usage counter */ - struct axis_conversion ac; /* hw -> logical axis */ -}; +struct acpi_lis3lv02d adev; +EXPORT_SYMBOL_GPL(adev); -static struct acpi_lis3lv02d adev; - -static int lis3lv02d_remove_fs(void); static int lis3lv02d_add_fs(struct acpi_device *device); -/* For automatic insertion of the module */ -static struct acpi_device_id lis3lv02d_device_ids[] = { - {"HPQ0004", 0}, /* HP Mobile Data Protection System PNP */ - {"", 0}, -}; -MODULE_DEVICE_TABLE(acpi, lis3lv02d_device_ids); - -/** - * lis3lv02d_acpi_init - ACPI _INI method: initialize the device. - * @handle: the handle of the device - * - * Returns AE_OK on success. - */ -static inline acpi_status lis3lv02d_acpi_init(acpi_handle handle) -{ - return acpi_evaluate_object(handle, METHOD_NAME__INI, NULL, NULL); -} - -/** - * lis3lv02d_acpi_read - ACPI ALRD method: read a register - * @handle: the handle of the device - * @reg: the register to read - * @ret: result of the operation - * - * Returns AE_OK on success. - */ -static acpi_status lis3lv02d_acpi_read(acpi_handle handle, int reg, u8 *ret) -{ - union acpi_object arg0 = { ACPI_TYPE_INTEGER }; - struct acpi_object_list args = { 1, &arg0 }; - unsigned long long lret; - acpi_status status; - - arg0.integer.value = reg; - - status = acpi_evaluate_integer(handle, "ALRD", &args, &lret); - *ret = lret; - return status; -} - -/** - * lis3lv02d_acpi_write - ACPI ALWR method: write to a register - * @handle: the handle of the device - * @reg: the register to write to - * @val: the value to write - * - * Returns AE_OK on success. - */ -static acpi_status lis3lv02d_acpi_write(acpi_handle handle, int reg, u8 val) -{ - unsigned long long ret; /* Not used when writting */ - union acpi_object in_obj[2]; - struct acpi_object_list args = { 2, in_obj }; - - in_obj[0].type = ACPI_TYPE_INTEGER; - in_obj[0].integer.value = reg; - in_obj[1].type = ACPI_TYPE_INTEGER; - in_obj[1].integer.value = val; - - return acpi_evaluate_integer(handle, "ALWR", &args, &ret); -} - static s16 lis3lv02d_read_16(acpi_handle handle, int reg) { u8 lo, hi; - lis3lv02d_acpi_read(handle, reg, &lo); - lis3lv02d_acpi_read(handle, reg + 1, &hi); + adev.read(handle, reg, &lo); + adev.read(handle, reg + 1, &hi); /* In "12 bit right justified" mode, bit 6, bit 7, bit 8 = bit 5 */ return (s16)((hi << 8) | lo); } @@ -190,54 +107,31 @@ static void lis3lv02d_get_xyz(acpi_handle handle, int *x, int *y, int *z) *z = lis3lv02d_get_axis(adev.ac.z, position); } -static inline void lis3lv02d_poweroff(acpi_handle handle) +void lis3lv02d_poweroff(acpi_handle handle) { adev.is_on = 0; /* disable X,Y,Z axis and power down */ - lis3lv02d_acpi_write(handle, CTRL_REG1, 0x00); + adev.write(handle, CTRL_REG1, 0x00); } +EXPORT_SYMBOL_GPL(lis3lv02d_poweroff); -static void lis3lv02d_poweron(acpi_handle handle) +void lis3lv02d_poweron(acpi_handle handle) { u8 val; adev.is_on = 1; - lis3lv02d_acpi_init(handle); - lis3lv02d_acpi_write(handle, FF_WU_CFG, 0); + adev.init(handle); + adev.write(handle, FF_WU_CFG, 0); /* * BDU: LSB and MSB values are not updated until both have been read. * So the value read will always be correct. * IEN: Interrupt for free-fall and DD, not for data-ready. */ - lis3lv02d_acpi_read(handle, CTRL_REG2, &val); + adev.read(handle, CTRL_REG2, &val); val |= CTRL2_BDU | CTRL2_IEN; - lis3lv02d_acpi_write(handle, CTRL_REG2, val); -} - -#ifdef CONFIG_PM -static int lis3lv02d_suspend(struct acpi_device *device, pm_message_t state) -{ - /* make sure the device is off when we suspend */ - lis3lv02d_poweroff(device->handle); - return 0; -} - -static int lis3lv02d_resume(struct acpi_device *device) -{ - /* put back the device in the right state (ACPI might turn it on) */ - mutex_lock(&adev.lock); - if (adev.usage > 0) - lis3lv02d_poweron(device->handle); - else - lis3lv02d_poweroff(device->handle); - mutex_unlock(&adev.lock); - return 0; + adev.write(handle, CTRL_REG2, val); } -#else -#define lis3lv02d_suspend NULL -#define lis3lv02d_resume NULL -#endif - +EXPORT_SYMBOL_GPL(lis3lv02d_poweron); /* * To be called before starting to use the device. It makes sure that the @@ -315,7 +209,7 @@ static inline void lis3lv02d_calibrate_joystick(void) lis3lv02d_get_xyz(adev.device->handle, &adev.xcalib, &adev.ycalib, &adev.zcalib); } -static int lis3lv02d_joystick_enable(void) +int lis3lv02d_joystick_enable(void) { int err; @@ -349,8 +243,9 @@ static int lis3lv02d_joystick_enable(void) return err; } +EXPORT_SYMBOL_GPL(lis3lv02d_joystick_enable); -static void lis3lv02d_joystick_disable(void) +void lis3lv02d_joystick_disable(void) { if (!adev.idev) return; @@ -358,13 +253,13 @@ static void lis3lv02d_joystick_disable(void) input_unregister_device(adev.idev); adev.idev = NULL; } - +EXPORT_SYMBOL_GPL(lis3lv02d_joystick_disable); /* * Initialise the accelerometer and the various subsystems. * Should be rather independant of the bus system. */ -static int lis3lv02d_init_device(struct acpi_lis3lv02d *dev) +int lis3lv02d_init_device(struct acpi_lis3lv02d *dev) { mutex_init(&dev->lock); lis3lv02d_add_fs(dev->device); @@ -376,93 +271,7 @@ static int lis3lv02d_init_device(struct acpi_lis3lv02d *dev) lis3lv02d_decrease_use(dev); return 0; } - -static int lis3lv02d_dmi_matched(const struct dmi_system_id *dmi) -{ - adev.ac = *((struct axis_conversion *)dmi->driver_data); - printk(KERN_INFO DRIVER_NAME ": hardware type %s found.\n", dmi->ident); - - return 1; -} - -/* Represents, for each axis seen by userspace, the corresponding hw axis (+1). - * If the value is negative, the opposite of the hw value is used. */ -static struct axis_conversion lis3lv02d_axis_normal = {1, 2, 3}; -static struct axis_conversion lis3lv02d_axis_y_inverted = {1, -2, 3}; -static struct axis_conversion lis3lv02d_axis_x_inverted = {-1, 2, 3}; -static struct axis_conversion lis3lv02d_axis_z_inverted = {1, 2, -3}; -static struct axis_conversion lis3lv02d_axis_xy_rotated_left = {-2, 1, 3}; -static struct axis_conversion lis3lv02d_axis_xy_swap_inverted = {-2, -1, 3}; - -#define AXIS_DMI_MATCH(_ident, _name, _axis) { \ - .ident = _ident, \ - .callback = lis3lv02d_dmi_matched, \ - .matches = { \ - DMI_MATCH(DMI_PRODUCT_NAME, _name) \ - }, \ - .driver_data = &lis3lv02d_axis_##_axis \ -} -static struct dmi_system_id lis3lv02d_dmi_ids[] = { - /* product names are truncated to match all kinds of a same model */ - AXIS_DMI_MATCH("NC64x0", "HP Compaq nc64", x_inverted), - AXIS_DMI_MATCH("NC84x0", "HP Compaq nc84", z_inverted), - AXIS_DMI_MATCH("NX9420", "HP Compaq nx9420", x_inverted), - AXIS_DMI_MATCH("NW9440", "HP Compaq nw9440", x_inverted), - AXIS_DMI_MATCH("NC2510", "HP Compaq 2510", y_inverted), - AXIS_DMI_MATCH("NC8510", "HP Compaq 8510", xy_swap_inverted), - AXIS_DMI_MATCH("HP2133", "HP 2133", xy_rotated_left), - { NULL, } -/* Laptop models without axis info (yet): - * "NC651xx" "HP Compaq 651" - * "NC671xx" "HP Compaq 671" - * "NC6910" "HP Compaq 6910" - * HP Compaq 8710x Notebook PC / Mobile Workstation - * "NC2400" "HP Compaq nc2400" - * "NX74x0" "HP Compaq nx74" - * "NX6325" "HP Compaq nx6325" - * "NC4400" "HP Compaq nc4400" - */ -}; - -static int lis3lv02d_add(struct acpi_device *device) -{ - u8 val; - - if (!device) - return -EINVAL; - - adev.device = device; - strcpy(acpi_device_name(device), DRIVER_NAME); - strcpy(acpi_device_class(device), ACPI_MDPS_CLASS); - device->driver_data = &adev; - - lis3lv02d_acpi_read(device->handle, WHO_AM_I, &val); - if ((val != LIS3LV02DL_ID) && (val != LIS302DL_ID)) { - printk(KERN_ERR DRIVER_NAME - ": Accelerometer chip not LIS3LV02D{L,Q}\n"); - } - - /* If possible use a "standard" axes order */ - if (dmi_check_system(lis3lv02d_dmi_ids) == 0) { - printk(KERN_INFO DRIVER_NAME ": laptop model unknown, " - "using default axes configuration\n"); - adev.ac = lis3lv02d_axis_normal; - } - - return lis3lv02d_init_device(&adev); -} - -static int lis3lv02d_remove(struct acpi_device *device, int type) -{ - if (!device) - return -EINVAL; - - lis3lv02d_joystick_disable(); - lis3lv02d_poweroff(device->handle); - - return lis3lv02d_remove_fs(); -} - +EXPORT_SYMBOL_GPL(lis3lv02d_init_device); /* Sysfs stuff */ static ssize_t lis3lv02d_position_show(struct device *dev, @@ -501,7 +310,7 @@ static ssize_t lis3lv02d_rate_show(struct device *dev, int val; lis3lv02d_increase_use(&adev); - lis3lv02d_acpi_read(adev.device->handle, CTRL_REG1, &ctrl); + adev.read(adev.device->handle, CTRL_REG1, &ctrl); lis3lv02d_decrease_use(&adev); val = (ctrl & (CTRL1_DF0 | CTRL1_DF1)) >> 4; return sprintf(buf, "%d\n", lis3lv02dl_df_val[val]); @@ -523,6 +332,7 @@ static struct attribute_group lis3lv02d_attribute_group = { .attrs = lis3lv02d_attributes }; + static int lis3lv02d_add_fs(struct acpi_device *device) { adev.pdev = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0); @@ -532,50 +342,15 @@ static int lis3lv02d_add_fs(struct acpi_device *device) return sysfs_create_group(&adev.pdev->dev.kobj, &lis3lv02d_attribute_group); } -static int lis3lv02d_remove_fs(void) +int lis3lv02d_remove_fs(void) { sysfs_remove_group(&adev.pdev->dev.kobj, &lis3lv02d_attribute_group); platform_device_unregister(adev.pdev); return 0; } - -/* For the HP MDPS aka 3D Driveguard */ -static struct acpi_driver lis3lv02d_driver = { - .name = DRIVER_NAME, - .class = ACPI_MDPS_CLASS, - .ids = lis3lv02d_device_ids, - .ops = { - .add = lis3lv02d_add, - .remove = lis3lv02d_remove, - .suspend = lis3lv02d_suspend, - .resume = lis3lv02d_resume, - } -}; - -static int __init lis3lv02d_init_module(void) -{ - int ret; - - if (acpi_disabled) - return -ENODEV; - - ret = acpi_bus_register_driver(&lis3lv02d_driver); - if (ret < 0) - return ret; - - printk(KERN_INFO DRIVER_NAME " driver loaded.\n"); - - return 0; -} - -static void __exit lis3lv02d_exit_module(void) -{ - acpi_bus_unregister_driver(&lis3lv02d_driver); -} +EXPORT_SYMBOL_GPL(lis3lv02d_remove_fs); MODULE_DESCRIPTION("ST LIS3LV02Dx three-axis digital accelerometer driver"); MODULE_AUTHOR("Yan Burman and Eric Piel"); MODULE_LICENSE("GPL"); -module_init(lis3lv02d_init_module); -module_exit(lis3lv02d_exit_module); diff --git a/drivers/hwmon/lis3lv02d.h b/drivers/hwmon/lis3lv02d.h index 330cfc60e94..223f1c0763b 100644 --- a/drivers/hwmon/lis3lv02d.h +++ b/drivers/hwmon/lis3lv02d.h @@ -23,7 +23,7 @@ * The actual chip is STMicroelectronics LIS3LV02DL or LIS3LV02DQ that seems to * be connected via SPI. There exists also several similar chips (such as LIS302DL or * LIS3L02DQ) but not in the HP laptops and they have slightly different registers. - * They can also be connected via I²C. + * They can also be connected via I²C. */ #define LIS3LV02DL_ID 0x3A /* Also the LIS3LV02DQ */ @@ -147,3 +147,36 @@ enum lis3lv02d_dd_src { DD_SRC_IA = 0x40, }; +struct axis_conversion { + s8 x; + s8 y; + s8 z; +}; + +struct acpi_lis3lv02d { + struct acpi_device *device; /* The ACPI device */ + acpi_status (*init) (acpi_handle handle); + acpi_status (*write) (acpi_handle handle, int reg, u8 val); + acpi_status (*read) (acpi_handle handle, int reg, u8 *ret); + + struct input_dev *idev; /* input device */ + struct task_struct *kthread; /* kthread for input */ + struct mutex lock; + struct platform_device *pdev; /* platform device */ + atomic_t count; /* interrupt count after last read */ + int xcalib; /* calibrated null value for x */ + int ycalib; /* calibrated null value for y */ + int zcalib; /* calibrated null value for z */ + unsigned char is_on; /* whether the device is on or off */ + unsigned char usage; /* usage counter */ + struct axis_conversion ac; /* hw -> logical axis */ +}; + +int lis3lv02d_init_device(struct acpi_lis3lv02d *dev); +int lis3lv02d_joystick_enable(void); +void lis3lv02d_joystick_disable(void); +void lis3lv02d_poweroff(acpi_handle handle); +void lis3lv02d_poweron(acpi_handle handle); +int lis3lv02d_remove_fs(void); + +extern struct acpi_lis3lv02d adev; -- cgit v1.2.3 From bb9a35f293a3c8b5d57253cdfe2f29fa2627e1b9 Mon Sep 17 00:00:00 2001 From: Andreas Herrmann Date: Thu, 15 Jan 2009 22:27:46 +0100 Subject: hwmon: (k8temp) Warn about fam F rev F errata Add warning about wrong CPU temperature readouts on all fam F rev F. The allowed combinations of processors ensure that all processors in a multisocket system have similar characteristics, e.g. (1) provide temperature sensor interface (>=RevC && =RevF) Thus it is sufficient to check the revision of the boot CPU. For "mixed silicon support" refer to "Revision Guide for AMD Athlon 64 and AMD Opteron Processors" (RevA-E) and "Revision Guide for AMD NPT Family 0Fh Processors" (RefF-G). Cc: Rudolf Marek Signed-off-by: Andreas Herrmann Signed-off-by: Jean Delvare --- drivers/hwmon/k8temp.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/k8temp.c b/drivers/hwmon/k8temp.c index bd2bde0ef95..ca56f2e26fd 100644 --- a/drivers/hwmon/k8temp.c +++ b/drivers/hwmon/k8temp.c @@ -31,6 +31,7 @@ #include #include #include +#include #define TEMP_FROM_REG(val) (((((val) >> 16) & 0xff) - 49) * 1000) #define REG_TEMP 0xe4 @@ -141,20 +142,34 @@ static int __devinit k8temp_probe(struct pci_dev *pdev, int err; u8 scfg; u32 temp; + u8 model, stepping; struct k8temp_data *data; - u32 cpuid = cpuid_eax(1); - - /* this feature should be available since SH-C0 core */ - if ((cpuid == 0xf40) || (cpuid == 0xf50) || (cpuid == 0xf51)) { - err = -ENODEV; - goto exit; - } if (!(data = kzalloc(sizeof(struct k8temp_data), GFP_KERNEL))) { err = -ENOMEM; goto exit; } + model = boot_cpu_data.x86_model; + stepping = boot_cpu_data.x86_mask; + + switch (boot_cpu_data.x86) { + case 0xf: + /* feature available since SH-C0, exclude older revisions */ + if (((model == 4) && (stepping == 0)) || + ((model == 5) && (stepping <= 1))) { + err = -ENODEV; + goto exit_free; + } + + if (model >= 0x40) { + dev_warn(&pdev->dev, "Temperature readouts might be " + "wrong - check erratum #141\n"); + } + + break; + } + pci_read_config_byte(pdev, REG_TEMP, &scfg); scfg &= ~(SEL_PLACE | SEL_CORE); /* Select sensor 0, core0 */ pci_write_config_byte(pdev, REG_TEMP, scfg); -- cgit v1.2.3 From a2e066bba2aad6583e3ff648bf28339d6c9f0898 Mon Sep 17 00:00:00 2001 From: Andreas Herrmann Date: Thu, 15 Jan 2009 22:27:47 +0100 Subject: hwmon: (k8temp) Fix wrong sensor selection for AMD K8 RevF/RevG CPUs Meaning of ThermSenseCoreSel bit was inverted beginning with K8 RevF. That means with current driver temp1/temp2 belong to core 1 and temp3/temp4 belong to core 0 on a K8 RevF/RevG CPU. This patch ensures that temp1/temp2 always belong to core 0 and temp3/temp4 to core 1 for all K8 revisions. Cc: Rudolf Marek Signed-off-by: Andreas Herrmann Signed-off-by: Jean Delvare --- drivers/hwmon/k8temp.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/k8temp.c b/drivers/hwmon/k8temp.c index ca56f2e26fd..a6381bc9189 100644 --- a/drivers/hwmon/k8temp.c +++ b/drivers/hwmon/k8temp.c @@ -48,6 +48,7 @@ struct k8temp_data { /* registers values */ u8 sensorsp; /* sensor presence bits - SEL_CORE & SEL_PLACE */ u32 temp[2][2]; /* core, place */ + u8 swap_core_select; /* meaning of SEL_CORE is inverted */ }; static struct k8temp_data *k8temp_update_device(struct device *dev) @@ -117,6 +118,9 @@ static ssize_t show_temp(struct device *dev, int place = attr->index; struct k8temp_data *data = k8temp_update_device(dev); + if (data->swap_core_select) + core = core ? 0 : 1; + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[core][place])); } @@ -162,7 +166,12 @@ static int __devinit k8temp_probe(struct pci_dev *pdev, goto exit_free; } + /* + * AMD NPT family 0fh, i.e. RevF and RevG: + * meaning of SEL_CORE bit is inverted + */ if (model >= 0x40) { + data->swap_core_select = 1; dev_warn(&pdev->dev, "Temperature readouts might be " "wrong - check erratum #141\n"); } -- cgit v1.2.3 From 76ff08da34196cfa308fcd3552bb9ea20888e745 Mon Sep 17 00:00:00 2001 From: Andreas Herrmann Date: Thu, 15 Jan 2009 22:27:47 +0100 Subject: hwmon: (k8temp) Fix temperature reporting for (most) K8 RevG CPUs Current Temperature for K8 RevG desktop CPUs is a "normalized value" which can be below ambient temperature. As a consequence lots of RevG systems report temperatures like: $ sensors k8temp-pci-00c3 Adapter: PCI adapter Core0 Temp: +17 C Core0 Temp: +3 C Core1 Temp: +21 C Core1 Temp: +5 C being quite below ambient temperature. There are even reports of negative temperature values. This patch corrects the temperature reporting of k8temp for RevG desktop CPUs. Cc: Rudolf Marek Signed-off-by: Andreas Herrmann Signed-off-by: Jean Delvare --- drivers/hwmon/k8temp.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/k8temp.c b/drivers/hwmon/k8temp.c index a6381bc9189..1fe99511184 100644 --- a/drivers/hwmon/k8temp.c +++ b/drivers/hwmon/k8temp.c @@ -49,6 +49,7 @@ struct k8temp_data { u8 sensorsp; /* sensor presence bits - SEL_CORE & SEL_PLACE */ u32 temp[2][2]; /* core, place */ u8 swap_core_select; /* meaning of SEL_CORE is inverted */ + u32 temp_offset; }; static struct k8temp_data *k8temp_update_device(struct device *dev) @@ -116,13 +117,15 @@ static ssize_t show_temp(struct device *dev, to_sensor_dev_attr_2(devattr); int core = attr->nr; int place = attr->index; + int temp; struct k8temp_data *data = k8temp_update_device(dev); if (data->swap_core_select) core = core ? 0 : 1; - return sprintf(buf, "%d\n", - TEMP_FROM_REG(data->temp[core][place])); + temp = TEMP_FROM_REG(data->temp[core][place]) + data->temp_offset; + + return sprintf(buf, "%d\n", temp); } /* core, place */ @@ -176,6 +179,16 @@ static int __devinit k8temp_probe(struct pci_dev *pdev, "wrong - check erratum #141\n"); } + if ((model >= 0x69) && + !(model == 0xc1 || model == 0x6c || model == 0x7c)) { + /* + * RevG desktop CPUs (i.e. no socket S1G1 parts) + * need additional offset, otherwise reported + * temperature is below ambient temperature + */ + data->temp_offset = 21000; + } + break; } -- cgit v1.2.3 From 1c301fc5394f7e1aa4c201e6e03d55d9c08b3bdf Mon Sep 17 00:00:00 2001 From: Jordan Crouse Date: Thu, 15 Jan 2009 22:27:47 +0100 Subject: hwmon: Add a driver for the ADT7475 hardware monitoring chip Hwmon driver for the ADT7475 chip. Signed-off-by: Jordan Crouse Signed-off-by: Hans de Goede Signed-off-by: Jean Delvare --- drivers/hwmon/Kconfig | 10 + drivers/hwmon/Makefile | 2 + drivers/hwmon/adt7475.c | 1221 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1233 insertions(+) create mode 100644 drivers/hwmon/adt7475.c (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 4b33bc82cc2..5c349a19a3a 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -189,6 +189,16 @@ config SENSORS_ADT7473 This driver can also be built as a module. If so, the module will be called adt7473. +config SENSORS_ADT7475 + tristate "Analog Devices ADT7475" + depends on I2C && EXPERIMENTAL + help + If you say yes here you get support for the Analog Devices + ADT7475 hardware monitoring chips. + + This driver can also be build as a module. If so, the module + will be called adt7475. + config SENSORS_K8TEMP tristate "AMD Athlon64/FX or Opteron temperature sensor" depends on X86 && PCI && EXPERIMENTAL diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 19cb1ace3eb..2e80f37f39e 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -28,6 +28,8 @@ obj-$(CONFIG_SENSORS_ADS7828) += ads7828.o obj-$(CONFIG_SENSORS_ADT7462) += adt7462.o obj-$(CONFIG_SENSORS_ADT7470) += adt7470.o obj-$(CONFIG_SENSORS_ADT7473) += adt7473.o +obj-$(CONFIG_SENSORS_ADT7475) += adt7475.o + obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o obj-$(CONFIG_SENSORS_AMS) += ams/ obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c new file mode 100644 index 00000000000..d39877a7da6 --- /dev/null +++ b/drivers/hwmon/adt7475.c @@ -0,0 +1,1221 @@ +/* + * adt7475 - Thermal sensor driver for the ADT7475 chip and derivatives + * Copyright (C) 2007-2008, Advanced Micro Devices, Inc. + * Copyright (C) 2008 Jordan Crouse + * Copyright (C) 2008 Hans de Goede + + * Derived from the lm83 driver by Jean Delvare + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* Indexes for the sysfs hooks */ + +#define INPUT 0 +#define MIN 1 +#define MAX 2 +#define CONTROL 3 +#define OFFSET 3 +#define AUTOMIN 4 +#define THERM 5 +#define HYSTERSIS 6 + +/* These are unique identifiers for the sysfs functions - unlike the + numbers above, these are not also indexes into an array +*/ + +#define ALARM 9 +#define FAULT 10 + +/* 7475 Common Registers */ + +#define REG_VOLTAGE_BASE 0x21 +#define REG_TEMP_BASE 0x25 +#define REG_TACH_BASE 0x28 +#define REG_PWM_BASE 0x30 +#define REG_PWM_MAX_BASE 0x38 + +#define REG_DEVID 0x3D +#define REG_VENDID 0x3E + +#define REG_STATUS1 0x41 +#define REG_STATUS2 0x42 + +#define REG_VOLTAGE_MIN_BASE 0x46 +#define REG_VOLTAGE_MAX_BASE 0x47 + +#define REG_TEMP_MIN_BASE 0x4E +#define REG_TEMP_MAX_BASE 0x4F + +#define REG_TACH_MIN_BASE 0x54 + +#define REG_PWM_CONFIG_BASE 0x5C + +#define REG_TEMP_TRANGE_BASE 0x5F + +#define REG_PWM_MIN_BASE 0x64 + +#define REG_TEMP_TMIN_BASE 0x67 +#define REG_TEMP_THERM_BASE 0x6A + +#define REG_REMOTE1_HYSTERSIS 0x6D +#define REG_REMOTE2_HYSTERSIS 0x6E + +#define REG_TEMP_OFFSET_BASE 0x70 + +#define REG_EXTEND1 0x76 +#define REG_EXTEND2 0x77 +#define REG_CONFIG5 0x7C + +#define CONFIG5_TWOSCOMP 0x01 +#define CONFIG5_TEMPOFFSET 0x02 + +/* ADT7475 Settings */ + +#define ADT7475_VOLTAGE_COUNT 2 +#define ADT7475_TEMP_COUNT 3 +#define ADT7475_TACH_COUNT 4 +#define ADT7475_PWM_COUNT 3 + +/* Macro to read the registers */ + +#define adt7475_read(reg) i2c_smbus_read_byte_data(client, (reg)) + +/* Macros to easily index the registers */ + +#define TACH_REG(idx) (REG_TACH_BASE + ((idx) * 2)) +#define TACH_MIN_REG(idx) (REG_TACH_MIN_BASE + ((idx) * 2)) + +#define PWM_REG(idx) (REG_PWM_BASE + (idx)) +#define PWM_MAX_REG(idx) (REG_PWM_MAX_BASE + (idx)) +#define PWM_MIN_REG(idx) (REG_PWM_MIN_BASE + (idx)) +#define PWM_CONFIG_REG(idx) (REG_PWM_CONFIG_BASE + (idx)) + +#define VOLTAGE_REG(idx) (REG_VOLTAGE_BASE + (idx)) +#define VOLTAGE_MIN_REG(idx) (REG_VOLTAGE_MIN_BASE + ((idx) * 2)) +#define VOLTAGE_MAX_REG(idx) (REG_VOLTAGE_MAX_BASE + ((idx) * 2)) + +#define TEMP_REG(idx) (REG_TEMP_BASE + (idx)) +#define TEMP_MIN_REG(idx) (REG_TEMP_MIN_BASE + ((idx) * 2)) +#define TEMP_MAX_REG(idx) (REG_TEMP_MAX_BASE + ((idx) * 2)) +#define TEMP_TMIN_REG(idx) (REG_TEMP_TMIN_BASE + (idx)) +#define TEMP_THERM_REG(idx) (REG_TEMP_THERM_BASE + (idx)) +#define TEMP_OFFSET_REG(idx) (REG_TEMP_OFFSET_BASE + (idx)) +#define TEMP_TRANGE_REG(idx) (REG_TEMP_TRANGE_BASE + (idx)) + +static unsigned short normal_i2c[] = { 0x2e, I2C_CLIENT_END }; + +I2C_CLIENT_INSMOD_1(adt7475); + +static const struct i2c_device_id adt7475_id[] = { + { "adt7475", adt7475 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adt7475_id); + +struct adt7475_data { + struct device *hwmon_dev; + struct mutex lock; + + unsigned long measure_updated; + unsigned long limits_updated; + char valid; + + u8 config5; + u16 alarms; + u16 voltage[3][3]; + u16 temp[7][3]; + u16 tach[2][4]; + u8 pwm[4][3]; + u8 range[3]; + u8 pwmctl[3]; + u8 pwmchan[3]; +}; + +static struct i2c_driver adt7475_driver; +static struct adt7475_data *adt7475_update_device(struct device *dev); +static void adt7475_read_hystersis(struct i2c_client *client); +static void adt7475_read_pwm(struct i2c_client *client, int index); + +/* Given a temp value, convert it to register value */ + +static inline u16 temp2reg(struct adt7475_data *data, long val) +{ + u16 ret; + + if (!(data->config5 & CONFIG5_TWOSCOMP)) { + val = SENSORS_LIMIT(val, -64000, 191000); + ret = (val + 64500) / 1000; + } else { + val = SENSORS_LIMIT(val, -128000, 127000); + if (val < -500) + ret = (256500 + val) / 1000; + else + ret = (val + 500) / 1000; + } + + return ret << 2; +} + +/* Given a register value, convert it to a real temp value */ + +static inline int reg2temp(struct adt7475_data *data, u16 reg) +{ + if (data->config5 & CONFIG5_TWOSCOMP) { + if (reg >= 512) + return (reg - 1024) * 250; + else + return reg * 250; + } else + return (reg - 256) * 250; +} + +static inline int tach2rpm(u16 tach) +{ + if (tach == 0 || tach == 0xFFFF) + return 0; + + return (90000 * 60) / tach; +} + +static inline u16 rpm2tach(unsigned long rpm) +{ + if (rpm == 0) + return 0; + + return SENSORS_LIMIT((90000 * 60) / rpm, 1, 0xFFFF); +} + +static inline int reg2vcc(u16 reg) +{ + return (4296 * reg) / 1000; +} + +static inline int reg2vccp(u16 reg) +{ + return (2929 * reg) / 1000; +} + +static inline u16 vcc2reg(long vcc) +{ + vcc = SENSORS_LIMIT(vcc, 0, 4396); + return (vcc * 1000) / 4296; +} + +static inline u16 vccp2reg(long vcc) +{ + vcc = SENSORS_LIMIT(vcc, 0, 2998); + return (vcc * 1000) / 2929; +} + +static u16 adt7475_read_word(struct i2c_client *client, int reg) +{ + u16 val; + + val = i2c_smbus_read_byte_data(client, reg); + val |= (i2c_smbus_read_byte_data(client, reg + 1) << 8); + + return val; +} + +static void adt7475_write_word(struct i2c_client *client, int reg, u16 val) +{ + i2c_smbus_write_byte_data(client, reg + 1, val >> 8); + i2c_smbus_write_byte_data(client, reg, val & 0xFF); +} + +/* Find the nearest value in a table - used for pwm frequency and + auto temp range */ +static int find_nearest(long val, const int *array, int size) +{ + int i; + + if (val < array[0]) + return 0; + + if (val > array[size - 1]) + return size - 1; + + for (i = 0; i < size - 1; i++) { + int a, b; + + if (val > array[i + 1]) + continue; + + a = val - array[i]; + b = array[i + 1] - val; + + return (a <= b) ? i : i + 1; + } + + return 0; +} + +static ssize_t show_voltage(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct adt7475_data *data = adt7475_update_device(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + unsigned short val; + + switch (sattr->nr) { + case ALARM: + return sprintf(buf, "%d\n", + (data->alarms >> (sattr->index + 1)) & 1); + default: + val = data->voltage[sattr->nr][sattr->index]; + return sprintf(buf, "%d\n", + sattr->index == + 0 ? reg2vccp(val) : reg2vcc(val)); + } +} + +static ssize_t set_voltage(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7475_data *data = i2c_get_clientdata(client); + unsigned char reg; + long val; + + if (strict_strtol(buf, 10, &val)) + return -EINVAL; + + mutex_lock(&data->lock); + + data->voltage[sattr->nr][sattr->index] = + sattr->index ? vcc2reg(val) : vccp2reg(val); + + if (sattr->nr == MIN) + reg = VOLTAGE_MIN_REG(sattr->index); + else + reg = VOLTAGE_MAX_REG(sattr->index); + + i2c_smbus_write_byte_data(client, reg, + data->voltage[sattr->nr][sattr->index] >> 2); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_temp(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct adt7475_data *data = adt7475_update_device(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int out; + + switch (sattr->nr) { + case HYSTERSIS: + mutex_lock(&data->lock); + out = data->temp[sattr->nr][sattr->index]; + if (sattr->index != 1) + out = (out >> 4) & 0xF; + else + out = (out & 0xF); + /* Show the value as an absolute number tied to + * THERM */ + out = reg2temp(data, data->temp[THERM][sattr->index]) - + out * 1000; + mutex_unlock(&data->lock); + break; + + case OFFSET: + /* Offset is always 2's complement, regardless of the + * setting in CONFIG5 */ + mutex_lock(&data->lock); + out = (s8)data->temp[sattr->nr][sattr->index]; + if (data->config5 & CONFIG5_TEMPOFFSET) + out *= 1000; + else + out *= 500; + mutex_unlock(&data->lock); + break; + + case ALARM: + out = (data->alarms >> (sattr->index + 4)) & 1; + break; + + case FAULT: + /* Note - only for remote1 and remote2 */ + out = data->alarms & (sattr->index ? 0x8000 : 0x4000); + out = out ? 0 : 1; + break; + + default: + /* All other temp values are in the configured format */ + out = reg2temp(data, data->temp[sattr->nr][sattr->index]); + } + + return sprintf(buf, "%d\n", out); +} + +static ssize_t set_temp(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7475_data *data = i2c_get_clientdata(client); + unsigned char reg = 0; + u8 out; + int temp; + long val; + + if (strict_strtol(buf, 10, &val)) + return -EINVAL; + + mutex_lock(&data->lock); + + /* We need the config register in all cases for temp <-> reg conv. */ + data->config5 = adt7475_read(REG_CONFIG5); + + switch (sattr->nr) { + case OFFSET: + if (data->config5 & CONFIG5_TEMPOFFSET) { + val = SENSORS_LIMIT(val, -63000, 127000); + out = data->temp[OFFSET][sattr->index] = val / 1000; + } else { + val = SENSORS_LIMIT(val, -63000, 64000); + out = data->temp[OFFSET][sattr->index] = val / 500; + } + break; + + case HYSTERSIS: + /* The value will be given as an absolute value, turn it + into an offset based on THERM */ + + /* Read fresh THERM and HYSTERSIS values from the chip */ + data->temp[THERM][sattr->index] = + adt7475_read(TEMP_THERM_REG(sattr->index)) << 2; + adt7475_read_hystersis(client); + + temp = reg2temp(data, data->temp[THERM][sattr->index]); + val = SENSORS_LIMIT(val, temp - 15000, temp); + val = (temp - val) / 1000; + + if (sattr->index != 1) { + data->temp[HYSTERSIS][sattr->index] &= 0xF0; + data->temp[HYSTERSIS][sattr->index] |= (val & 0xF) << 4; + } else { + data->temp[HYSTERSIS][sattr->index] &= 0x0F; + data->temp[HYSTERSIS][sattr->index] |= (val & 0xF); + } + + out = data->temp[HYSTERSIS][sattr->index]; + break; + + default: + data->temp[sattr->nr][sattr->index] = temp2reg(data, val); + + /* We maintain an extra 2 digits of precision for simplicity + * - shift those back off before writing the value */ + out = (u8) (data->temp[sattr->nr][sattr->index] >> 2); + } + + switch (sattr->nr) { + case MIN: + reg = TEMP_MIN_REG(sattr->index); + break; + case MAX: + reg = TEMP_MAX_REG(sattr->index); + break; + case OFFSET: + reg = TEMP_OFFSET_REG(sattr->index); + break; + case AUTOMIN: + reg = TEMP_TMIN_REG(sattr->index); + break; + case THERM: + reg = TEMP_THERM_REG(sattr->index); + break; + case HYSTERSIS: + if (sattr->index != 2) + reg = REG_REMOTE1_HYSTERSIS; + else + reg = REG_REMOTE2_HYSTERSIS; + + break; + } + + i2c_smbus_write_byte_data(client, reg, out); + + mutex_unlock(&data->lock); + return count; +} + +/* Table of autorange values - the user will write the value in millidegrees, + and we'll convert it */ +static const int autorange_table[] = { + 2000, 2500, 3330, 4000, 5000, 6670, 8000, + 10000, 13330, 16000, 20000, 26670, 32000, 40000, + 53330, 80000 +}; + +static ssize_t show_point2(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct adt7475_data *data = adt7475_update_device(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int out, val; + + mutex_lock(&data->lock); + out = (data->range[sattr->index] >> 4) & 0x0F; + val = reg2temp(data, data->temp[AUTOMIN][sattr->index]); + mutex_unlock(&data->lock); + + return sprintf(buf, "%d\n", val + autorange_table[out]); +} + +static ssize_t set_point2(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adt7475_data *data = i2c_get_clientdata(client); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int temp; + long val; + + if (strict_strtol(buf, 10, &val)) + return -EINVAL; + + mutex_lock(&data->lock); + + /* Get a fresh copy of the needed registers */ + data->config5 = adt7475_read(REG_CONFIG5); + data->temp[AUTOMIN][sattr->index] = + adt7475_read(TEMP_TMIN_REG(sattr->index)) << 2; + data->range[sattr->index] = + adt7475_read(TEMP_TRANGE_REG(sattr->index)); + + /* The user will write an absolute value, so subtract the start point + to figure the range */ + temp = reg2temp(data, data->temp[AUTOMIN][sattr->index]); + val = SENSORS_LIMIT(val, temp + autorange_table[0], + temp + autorange_table[ARRAY_SIZE(autorange_table) - 1]); + val -= temp; + + /* Find the nearest table entry to what the user wrote */ + val = find_nearest(val, autorange_table, ARRAY_SIZE(autorange_table)); + + data->range[sattr->index] &= ~0xF0; + data->range[sattr->index] |= val << 4; + + i2c_smbus_write_byte_data(client, TEMP_TRANGE_REG(sattr->index), + data->range[sattr->index]); + + mutex_unlock(&data->lock); + return count; +} + +static ssize_t show_tach(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct adt7475_data *data = adt7475_update_device(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int out; + + if (sattr->nr == ALARM) + out = (data->alarms >> (sattr->index + 10)) & 1; + else + out = tach2rpm(data->tach[sattr->nr][sattr->index]); + + return sprintf(buf, "%d\n", out); +} + +static ssize_t set_tach(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7475_data *data = i2c_get_clientdata(client); + unsigned long val; + + if (strict_strtoul(buf, 10, &val)) + return -EINVAL; + + mutex_lock(&data->lock); + + data->tach[MIN][sattr->index] = rpm2tach(val); + + adt7475_write_word(client, TACH_MIN_REG(sattr->index), + data->tach[MIN][sattr->index]); + + mutex_unlock(&data->lock); + return count; +} + +static ssize_t show_pwm(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct adt7475_data *data = adt7475_update_device(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + + return sprintf(buf, "%d\n", data->pwm[sattr->nr][sattr->index]); +} + +static ssize_t show_pwmchan(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct adt7475_data *data = adt7475_update_device(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + + return sprintf(buf, "%d\n", data->pwmchan[sattr->index]); +} + +static ssize_t show_pwmctrl(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct adt7475_data *data = adt7475_update_device(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + + return sprintf(buf, "%d\n", data->pwmctl[sattr->index]); +} + +static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7475_data *data = i2c_get_clientdata(client); + unsigned char reg = 0; + long val; + + if (strict_strtol(buf, 10, &val)) + return -EINVAL; + + mutex_lock(&data->lock); + + switch (sattr->nr) { + case INPUT: + /* Get a fresh value for CONTROL */ + data->pwm[CONTROL][sattr->index] = + adt7475_read(PWM_CONFIG_REG(sattr->index)); + + /* If we are not in manual mode, then we shouldn't allow + * the user to set the pwm speed */ + if (((data->pwm[CONTROL][sattr->index] >> 5) & 7) != 7) { + mutex_unlock(&data->lock); + return count; + } + + reg = PWM_REG(sattr->index); + break; + + case MIN: + reg = PWM_MIN_REG(sattr->index); + break; + + case MAX: + reg = PWM_MAX_REG(sattr->index); + break; + } + + data->pwm[sattr->nr][sattr->index] = SENSORS_LIMIT(val, 0, 0xFF); + i2c_smbus_write_byte_data(client, reg, + data->pwm[sattr->nr][sattr->index]); + + mutex_unlock(&data->lock); + + return count; +} + +/* Called by set_pwmctrl and set_pwmchan */ + +static int hw_set_pwm(struct i2c_client *client, int index, + unsigned int pwmctl, unsigned int pwmchan) +{ + struct adt7475_data *data = i2c_get_clientdata(client); + long val = 0; + + switch (pwmctl) { + case 0: + val = 0x03; /* Run at full speed */ + break; + case 1: + val = 0x07; /* Manual mode */ + break; + case 2: + switch (pwmchan) { + case 1: + /* Remote1 controls PWM */ + val = 0x00; + break; + case 2: + /* local controls PWM */ + val = 0x01; + break; + case 4: + /* remote2 controls PWM */ + val = 0x02; + break; + case 6: + /* local/remote2 control PWM */ + val = 0x05; + break; + case 7: + /* All three control PWM */ + val = 0x06; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + data->pwmctl[index] = pwmctl; + data->pwmchan[index] = pwmchan; + + data->pwm[CONTROL][index] &= ~0xE0; + data->pwm[CONTROL][index] |= (val & 7) << 5; + + i2c_smbus_write_byte_data(client, PWM_CONFIG_REG(index), + data->pwm[CONTROL][index]); + + return 0; +} + +static ssize_t set_pwmchan(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7475_data *data = i2c_get_clientdata(client); + int r; + long val; + + if (strict_strtol(buf, 10, &val)) + return -EINVAL; + + mutex_lock(&data->lock); + /* Read Modify Write PWM values */ + adt7475_read_pwm(client, sattr->index); + r = hw_set_pwm(client, sattr->index, data->pwmctl[sattr->index], val); + if (r) + count = r; + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t set_pwmctrl(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7475_data *data = i2c_get_clientdata(client); + int r; + long val; + + if (strict_strtol(buf, 10, &val)) + return -EINVAL; + + mutex_lock(&data->lock); + /* Read Modify Write PWM values */ + adt7475_read_pwm(client, sattr->index); + r = hw_set_pwm(client, sattr->index, val, data->pwmchan[sattr->index]); + if (r) + count = r; + mutex_unlock(&data->lock); + + return count; +} + +/* List of frequencies for the PWM */ +static const int pwmfreq_table[] = { + 11, 14, 22, 29, 35, 44, 58, 88 +}; + +static ssize_t show_pwmfreq(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct adt7475_data *data = adt7475_update_device(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + + return sprintf(buf, "%d\n", + pwmfreq_table[data->range[sattr->index] & 7]); +} + +static ssize_t set_pwmfreq(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7475_data *data = i2c_get_clientdata(client); + int out; + long val; + + if (strict_strtol(buf, 10, &val)) + return -EINVAL; + + out = find_nearest(val, pwmfreq_table, ARRAY_SIZE(pwmfreq_table)); + + mutex_lock(&data->lock); + + data->range[sattr->index] = + adt7475_read(TEMP_TRANGE_REG(sattr->index)); + data->range[sattr->index] &= ~7; + data->range[sattr->index] |= out; + + i2c_smbus_write_byte_data(client, TEMP_TRANGE_REG(sattr->index), + data->range[sattr->index]); + + mutex_unlock(&data->lock); + return count; +} + +static SENSOR_DEVICE_ATTR_2(in1_input, S_IRUGO, show_voltage, NULL, INPUT, 0); +static SENSOR_DEVICE_ATTR_2(in1_max, S_IRUGO | S_IWUSR, show_voltage, + set_voltage, MAX, 0); +static SENSOR_DEVICE_ATTR_2(in1_min, S_IRUGO | S_IWUSR, show_voltage, + set_voltage, MIN, 0); +static SENSOR_DEVICE_ATTR_2(in1_alarm, S_IRUGO, show_voltage, NULL, ALARM, 0); +static SENSOR_DEVICE_ATTR_2(in2_input, S_IRUGO, show_voltage, NULL, INPUT, 1); +static SENSOR_DEVICE_ATTR_2(in2_max, S_IRUGO | S_IWUSR, show_voltage, + set_voltage, MAX, 1); +static SENSOR_DEVICE_ATTR_2(in2_min, S_IRUGO | S_IWUSR, show_voltage, + set_voltage, MIN, 1); +static SENSOR_DEVICE_ATTR_2(in2_alarm, S_IRUGO, show_voltage, NULL, ALARM, 1); +static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, INPUT, 0); +static SENSOR_DEVICE_ATTR_2(temp1_alarm, S_IRUGO, show_temp, NULL, ALARM, 0); +static SENSOR_DEVICE_ATTR_2(temp1_fault, S_IRUGO, show_temp, NULL, FAULT, 0); +static SENSOR_DEVICE_ATTR_2(temp1_max, S_IRUGO | S_IWUSR, show_temp, set_temp, + MAX, 0); +static SENSOR_DEVICE_ATTR_2(temp1_min, S_IRUGO | S_IWUSR, show_temp, set_temp, + MIN, 0); +static SENSOR_DEVICE_ATTR_2(temp1_offset, S_IRUGO | S_IWUSR, show_temp, + set_temp, OFFSET, 0); +static SENSOR_DEVICE_ATTR_2(temp1_auto_point1_temp, S_IRUGO | S_IWUSR, + show_temp, set_temp, AUTOMIN, 0); +static SENSOR_DEVICE_ATTR_2(temp1_auto_point2_temp, S_IRUGO | S_IWUSR, + show_point2, set_point2, 0, 0); +static SENSOR_DEVICE_ATTR_2(temp1_crit, S_IRUGO | S_IWUSR, show_temp, set_temp, + THERM, 0); +static SENSOR_DEVICE_ATTR_2(temp1_crit_hyst, S_IRUGO | S_IWUSR, show_temp, + set_temp, HYSTERSIS, 0); +static SENSOR_DEVICE_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, INPUT, 1); +static SENSOR_DEVICE_ATTR_2(temp2_alarm, S_IRUGO, show_temp, NULL, ALARM, 1); +static SENSOR_DEVICE_ATTR_2(temp2_max, S_IRUGO | S_IWUSR, show_temp, set_temp, + MAX, 1); +static SENSOR_DEVICE_ATTR_2(temp2_min, S_IRUGO | S_IWUSR, show_temp, set_temp, + MIN, 1); +static SENSOR_DEVICE_ATTR_2(temp2_offset, S_IRUGO | S_IWUSR, show_temp, + set_temp, OFFSET, 1); +static SENSOR_DEVICE_ATTR_2(temp2_auto_point1_temp, S_IRUGO | S_IWUSR, + show_temp, set_temp, AUTOMIN, 1); +static SENSOR_DEVICE_ATTR_2(temp2_auto_point2_temp, S_IRUGO | S_IWUSR, + show_point2, set_point2, 0, 1); +static SENSOR_DEVICE_ATTR_2(temp2_crit, S_IRUGO | S_IWUSR, show_temp, set_temp, + THERM, 1); +static SENSOR_DEVICE_ATTR_2(temp2_crit_hyst, S_IRUGO | S_IWUSR, show_temp, + set_temp, HYSTERSIS, 1); +static SENSOR_DEVICE_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, INPUT, 2); +static SENSOR_DEVICE_ATTR_2(temp3_alarm, S_IRUGO, show_temp, NULL, ALARM, 2); +static SENSOR_DEVICE_ATTR_2(temp3_fault, S_IRUGO, show_temp, NULL, FAULT, 2); +static SENSOR_DEVICE_ATTR_2(temp3_max, S_IRUGO | S_IWUSR, show_temp, set_temp, + MAX, 2); +static SENSOR_DEVICE_ATTR_2(temp3_min, S_IRUGO | S_IWUSR, show_temp, set_temp, + MIN, 2); +static SENSOR_DEVICE_ATTR_2(temp3_offset, S_IRUGO | S_IWUSR, show_temp, + set_temp, OFFSET, 2); +static SENSOR_DEVICE_ATTR_2(temp3_auto_point1_temp, S_IRUGO | S_IWUSR, + show_temp, set_temp, AUTOMIN, 2); +static SENSOR_DEVICE_ATTR_2(temp3_auto_point2_temp, S_IRUGO | S_IWUSR, + show_point2, set_point2, 0, 2); +static SENSOR_DEVICE_ATTR_2(temp3_crit, S_IRUGO | S_IWUSR, show_temp, set_temp, + THERM, 2); +static SENSOR_DEVICE_ATTR_2(temp3_crit_hyst, S_IRUGO | S_IWUSR, show_temp, + set_temp, HYSTERSIS, 2); +static SENSOR_DEVICE_ATTR_2(fan1_input, S_IRUGO, show_tach, NULL, INPUT, 0); +static SENSOR_DEVICE_ATTR_2(fan1_min, S_IRUGO | S_IWUSR, show_tach, set_tach, + MIN, 0); +static SENSOR_DEVICE_ATTR_2(fan1_alarm, S_IRUGO, show_tach, NULL, ALARM, 0); +static SENSOR_DEVICE_ATTR_2(fan2_input, S_IRUGO, show_tach, NULL, INPUT, 1); +static SENSOR_DEVICE_ATTR_2(fan2_min, S_IRUGO | S_IWUSR, show_tach, set_tach, + MIN, 1); +static SENSOR_DEVICE_ATTR_2(fan2_alarm, S_IRUGO, show_tach, NULL, ALARM, 1); +static SENSOR_DEVICE_ATTR_2(fan3_input, S_IRUGO, show_tach, NULL, INPUT, 2); +static SENSOR_DEVICE_ATTR_2(fan3_min, S_IRUGO | S_IWUSR, show_tach, set_tach, + MIN, 2); +static SENSOR_DEVICE_ATTR_2(fan3_alarm, S_IRUGO, show_tach, NULL, ALARM, 2); +static SENSOR_DEVICE_ATTR_2(fan4_input, S_IRUGO, show_tach, NULL, INPUT, 3); +static SENSOR_DEVICE_ATTR_2(fan4_min, S_IRUGO | S_IWUSR, show_tach, set_tach, + MIN, 3); +static SENSOR_DEVICE_ATTR_2(fan4_alarm, S_IRUGO, show_tach, NULL, ALARM, 3); +static SENSOR_DEVICE_ATTR_2(pwm1, S_IRUGO | S_IWUSR, show_pwm, set_pwm, INPUT, + 0); +static SENSOR_DEVICE_ATTR_2(pwm1_freq, S_IRUGO | S_IWUSR, show_pwmfreq, + set_pwmfreq, INPUT, 0); +static SENSOR_DEVICE_ATTR_2(pwm1_enable, S_IRUGO | S_IWUSR, show_pwmctrl, + set_pwmctrl, INPUT, 0); +static SENSOR_DEVICE_ATTR_2(pwm1_auto_channel_temp, S_IRUGO | S_IWUSR, + show_pwmchan, set_pwmchan, INPUT, 0); +static SENSOR_DEVICE_ATTR_2(pwm1_auto_point1_pwm, S_IRUGO | S_IWUSR, show_pwm, + set_pwm, MIN, 0); +static SENSOR_DEVICE_ATTR_2(pwm1_auto_point2_pwm, S_IRUGO | S_IWUSR, show_pwm, + set_pwm, MAX, 0); +static SENSOR_DEVICE_ATTR_2(pwm2, S_IRUGO | S_IWUSR, show_pwm, set_pwm, INPUT, + 1); +static SENSOR_DEVICE_ATTR_2(pwm2_freq, S_IRUGO | S_IWUSR, show_pwmfreq, + set_pwmfreq, INPUT, 1); +static SENSOR_DEVICE_ATTR_2(pwm2_enable, S_IRUGO | S_IWUSR, show_pwmctrl, + set_pwmctrl, INPUT, 1); +static SENSOR_DEVICE_ATTR_2(pwm2_auto_channel_temp, S_IRUGO | S_IWUSR, + show_pwmchan, set_pwmchan, INPUT, 1); +static SENSOR_DEVICE_ATTR_2(pwm2_auto_point1_pwm, S_IRUGO | S_IWUSR, show_pwm, + set_pwm, MIN, 1); +static SENSOR_DEVICE_ATTR_2(pwm2_auto_point2_pwm, S_IRUGO | S_IWUSR, show_pwm, + set_pwm, MAX, 1); +static SENSOR_DEVICE_ATTR_2(pwm3, S_IRUGO | S_IWUSR, show_pwm, set_pwm, INPUT, + 2); +static SENSOR_DEVICE_ATTR_2(pwm3_freq, S_IRUGO | S_IWUSR, show_pwmfreq, + set_pwmfreq, INPUT, 2); +static SENSOR_DEVICE_ATTR_2(pwm3_enable, S_IRUGO | S_IWUSR, show_pwmctrl, + set_pwmctrl, INPUT, 2); +static SENSOR_DEVICE_ATTR_2(pwm3_auto_channel_temp, S_IRUGO | S_IWUSR, + show_pwmchan, set_pwmchan, INPUT, 2); +static SENSOR_DEVICE_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO | S_IWUSR, show_pwm, + set_pwm, MIN, 2); +static SENSOR_DEVICE_ATTR_2(pwm3_auto_point2_pwm, S_IRUGO | S_IWUSR, show_pwm, + set_pwm, MAX, 2); + +static struct attribute *adt7475_attrs[] = { + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in1_max.dev_attr.attr, + &sensor_dev_attr_in1_min.dev_attr.attr, + &sensor_dev_attr_in1_alarm.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in2_max.dev_attr.attr, + &sensor_dev_attr_in2_min.dev_attr.attr, + &sensor_dev_attr_in2_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_fault.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_min.dev_attr.attr, + &sensor_dev_attr_temp1_offset.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_crit.dev_attr.attr, + &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp2_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp2_min.dev_attr.attr, + &sensor_dev_attr_temp2_offset.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_crit.dev_attr.attr, + &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr, + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp3_fault.dev_attr.attr, + &sensor_dev_attr_temp3_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_max.dev_attr.attr, + &sensor_dev_attr_temp3_min.dev_attr.attr, + &sensor_dev_attr_temp3_offset.dev_attr.attr, + &sensor_dev_attr_temp3_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_temp3_auto_point2_temp.dev_attr.attr, + &sensor_dev_attr_temp3_crit.dev_attr.attr, + &sensor_dev_attr_temp3_crit_hyst.dev_attr.attr, + &sensor_dev_attr_fan1_input.dev_attr.attr, + &sensor_dev_attr_fan1_min.dev_attr.attr, + &sensor_dev_attr_fan1_alarm.dev_attr.attr, + &sensor_dev_attr_fan2_input.dev_attr.attr, + &sensor_dev_attr_fan2_min.dev_attr.attr, + &sensor_dev_attr_fan2_alarm.dev_attr.attr, + &sensor_dev_attr_fan3_input.dev_attr.attr, + &sensor_dev_attr_fan3_min.dev_attr.attr, + &sensor_dev_attr_fan3_alarm.dev_attr.attr, + &sensor_dev_attr_fan4_input.dev_attr.attr, + &sensor_dev_attr_fan4_min.dev_attr.attr, + &sensor_dev_attr_fan4_alarm.dev_attr.attr, + &sensor_dev_attr_pwm1.dev_attr.attr, + &sensor_dev_attr_pwm1_freq.dev_attr.attr, + &sensor_dev_attr_pwm1_enable.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_channel_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_pwm2.dev_attr.attr, + &sensor_dev_attr_pwm2_freq.dev_attr.attr, + &sensor_dev_attr_pwm2_enable.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_channel_temp.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr, + &sensor_dev_attr_pwm3.dev_attr.attr, + &sensor_dev_attr_pwm3_freq.dev_attr.attr, + &sensor_dev_attr_pwm3_enable.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_channel_temp.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point2_pwm.dev_attr.attr, + NULL, +}; + +struct attribute_group adt7475_attr_group = { .attrs = adt7475_attrs }; + +static int adt7475_detect(struct i2c_client *client, int kind, + struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + if (kind <= 0) { + if (adt7475_read(REG_VENDID) != 0x41 || + adt7475_read(REG_DEVID) != 0x75) { + dev_err(&adapter->dev, + "Couldn't detect a adt7475 part at 0x%02x\n", + (unsigned int)client->addr); + return -ENODEV; + } + } + + strlcpy(info->type, adt7475_id[0].name, I2C_NAME_SIZE); + + return 0; +} + +static int adt7475_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adt7475_data *data; + int i, ret = 0; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + + mutex_init(&data->lock); + i2c_set_clientdata(client, data); + + /* Call adt7475_read_pwm for all pwm's as this will reprogram any + pwm's which are disabled to manual mode with 0% duty cycle */ + for (i = 0; i < ADT7475_PWM_COUNT; i++) + adt7475_read_pwm(client, i); + + ret = sysfs_create_group(&client->dev.kobj, &adt7475_attr_group); + if (ret) + goto efree; + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + ret = PTR_ERR(data->hwmon_dev); + goto eremove; + } + + return 0; + +eremove: + sysfs_remove_group(&client->dev.kobj, &adt7475_attr_group); +efree: + kfree(data); + return ret; +} + +static int adt7475_remove(struct i2c_client *client) +{ + struct adt7475_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &adt7475_attr_group); + kfree(data); + + return 0; +} + +static struct i2c_driver adt7475_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "adt7475", + }, + .probe = adt7475_probe, + .remove = adt7475_remove, + .id_table = adt7475_id, + .detect = adt7475_detect, + .address_data = &addr_data, +}; + +static void adt7475_read_hystersis(struct i2c_client *client) +{ + struct adt7475_data *data = i2c_get_clientdata(client); + + data->temp[HYSTERSIS][0] = (u16) adt7475_read(REG_REMOTE1_HYSTERSIS); + data->temp[HYSTERSIS][1] = data->temp[HYSTERSIS][0]; + data->temp[HYSTERSIS][2] = (u16) adt7475_read(REG_REMOTE2_HYSTERSIS); +} + +static void adt7475_read_pwm(struct i2c_client *client, int index) +{ + struct adt7475_data *data = i2c_get_clientdata(client); + unsigned int v; + + data->pwm[CONTROL][index] = adt7475_read(PWM_CONFIG_REG(index)); + + /* Figure out the internal value for pwmctrl and pwmchan + based on the current settings */ + v = (data->pwm[CONTROL][index] >> 5) & 7; + + if (v == 3) + data->pwmctl[index] = 0; + else if (v == 7) + data->pwmctl[index] = 1; + else if (v == 4) { + /* The fan is disabled - we don't want to + support that, so change to manual mode and + set the duty cycle to 0 instead + */ + data->pwm[INPUT][index] = 0; + data->pwm[CONTROL][index] &= ~0xE0; + data->pwm[CONTROL][index] |= (7 << 5); + + i2c_smbus_write_byte_data(client, PWM_CONFIG_REG(index), + data->pwm[INPUT][index]); + + i2c_smbus_write_byte_data(client, PWM_CONFIG_REG(index), + data->pwm[CONTROL][index]); + + data->pwmctl[index] = 1; + } else { + data->pwmctl[index] = 2; + + switch (v) { + case 0: + data->pwmchan[index] = 1; + break; + case 1: + data->pwmchan[index] = 2; + break; + case 2: + data->pwmchan[index] = 4; + break; + case 5: + data->pwmchan[index] = 6; + break; + case 6: + data->pwmchan[index] = 7; + break; + } + } +} + +static struct adt7475_data *adt7475_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adt7475_data *data = i2c_get_clientdata(client); + u8 ext; + int i; + + mutex_lock(&data->lock); + + /* Measurement values update every 2 seconds */ + if (time_after(jiffies, data->measure_updated + HZ * 2) || + !data->valid) { + data->alarms = adt7475_read(REG_STATUS2) << 8; + data->alarms |= adt7475_read(REG_STATUS1); + + ext = adt7475_read(REG_EXTEND1); + for (i = 0; i < ADT7475_VOLTAGE_COUNT; i++) + data->voltage[INPUT][i] = + (adt7475_read(VOLTAGE_REG(i)) << 2) | + ((ext >> ((i + 1) * 2)) & 3); + + ext = adt7475_read(REG_EXTEND2); + for (i = 0; i < ADT7475_TEMP_COUNT; i++) + data->temp[INPUT][i] = + (adt7475_read(TEMP_REG(i)) << 2) | + ((ext >> ((i + 1) * 2)) & 3); + + for (i = 0; i < ADT7475_TACH_COUNT; i++) + data->tach[INPUT][i] = + adt7475_read_word(client, TACH_REG(i)); + + /* Updated by hw when in auto mode */ + for (i = 0; i < ADT7475_PWM_COUNT; i++) + data->pwm[INPUT][i] = adt7475_read(PWM_REG(i)); + + data->measure_updated = jiffies; + } + + /* Limits and settings, should never change update every 60 seconds */ + if (time_after(jiffies, data->limits_updated + HZ * 2) || + !data->valid) { + data->config5 = adt7475_read(REG_CONFIG5); + + for (i = 0; i < ADT7475_VOLTAGE_COUNT; i++) { + /* Adjust values so they match the input precision */ + data->voltage[MIN][i] = + adt7475_read(VOLTAGE_MIN_REG(i)) << 2; + data->voltage[MAX][i] = + adt7475_read(VOLTAGE_MAX_REG(i)) << 2; + } + + for (i = 0; i < ADT7475_TEMP_COUNT; i++) { + /* Adjust values so they match the input precision */ + data->temp[MIN][i] = + adt7475_read(TEMP_MIN_REG(i)) << 2; + data->temp[MAX][i] = + adt7475_read(TEMP_MAX_REG(i)) << 2; + data->temp[AUTOMIN][i] = + adt7475_read(TEMP_TMIN_REG(i)) << 2; + data->temp[THERM][i] = + adt7475_read(TEMP_THERM_REG(i)) << 2; + data->temp[OFFSET][i] = + adt7475_read(TEMP_OFFSET_REG(i)); + } + adt7475_read_hystersis(client); + + for (i = 0; i < ADT7475_TACH_COUNT; i++) + data->tach[MIN][i] = + adt7475_read_word(client, TACH_MIN_REG(i)); + + for (i = 0; i < ADT7475_PWM_COUNT; i++) { + data->pwm[MAX][i] = adt7475_read(PWM_MAX_REG(i)); + data->pwm[MIN][i] = adt7475_read(PWM_MIN_REG(i)); + /* Set the channel and control information */ + adt7475_read_pwm(client, i); + } + + data->range[0] = adt7475_read(TEMP_TRANGE_REG(0)); + data->range[1] = adt7475_read(TEMP_TRANGE_REG(1)); + data->range[2] = adt7475_read(TEMP_TRANGE_REG(2)); + + data->limits_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->lock); + + return data; +} + +static int __init sensors_adt7475_init(void) +{ + return i2c_add_driver(&adt7475_driver); +} + +static void __exit sensors_adt7475_exit(void) +{ + i2c_del_driver(&adt7475_driver); +} + +MODULE_AUTHOR("Advanced Micro Devices, Inc"); +MODULE_DESCRIPTION("adt7475 driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_adt7475_init); +module_exit(sensors_adt7475_exit); -- cgit v1.2.3 From 058943ddcb7cb307a0c406088c1e61f203d8b66f Mon Sep 17 00:00:00 2001 From: Alistair John Strachan Date: Thu, 15 Jan 2009 22:27:47 +0100 Subject: hwmon: (abituguru3) Match partial DMI board name strings The switch-over to using DMI board strings to identify abituguru3 compatible mainboards works most of the time, but sometimes the vendor has substantially modified the board string between BIOS revisions. We have found that the vendor chipset identification string (provided in brackets) changes frequently and is of no use to us. The rest of the board string sometimes changes in subtle ways, e.g. whitespace or variations in capitalization. The new comparison code checks only a part of the supplied DMI board string, trimming the bracketed content, whitespace, and ignoring case as necessary. This fixes a bug where an IP35 Pro running an early BIOS would not be detected without the force=1 module parameter, and also speculatively fixes other similiar issues. Signed-off-by: Alistair John Strachan Reported-by: Nick Pasich Cc: Hans de Goede Signed-off-by: Jean Delvare --- drivers/hwmon/abituguru3.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/abituguru3.c b/drivers/hwmon/abituguru3.c index 70bb854086d..4914b34e6bc 100644 --- a/drivers/hwmon/abituguru3.c +++ b/drivers/hwmon/abituguru3.c @@ -279,7 +279,7 @@ static const struct abituguru3_motherboard_info abituguru3_motherboards[] = { { "OTES1 Fan", 36, 2, 60, 1, 0 }, { NULL, 0, 0, 0, 0, 0 } } }, - { 0x0011, "AT8 32X(ATI RD580-ULI M1575)", { + { 0x0011, "AT8 32X", { { "CPU Core", 0, 0, 10, 1, 0 }, { "DDR", 1, 0, 20, 1, 0 }, { "DDR VTT", 2, 0, 10, 1, 0 }, @@ -402,7 +402,7 @@ static const struct abituguru3_motherboard_info abituguru3_motherboards[] = { { "AUX3 Fan", 36, 2, 60, 1, 0 }, { NULL, 0, 0, 0, 0, 0 } } }, - { 0x0016, "AW9D-MAX (Intel i975-ICH7)", { + { 0x0016, "AW9D-MAX", { { "CPU Core", 0, 0, 10, 1, 0 }, { "DDR2", 1, 0, 20, 1, 0 }, { "DDR2 VTT", 2, 0, 10, 1, 0 }, @@ -509,7 +509,7 @@ static const struct abituguru3_motherboard_info abituguru3_motherboards[] = { { "AUX3 FAN", 36, 2, 60, 1, 0 }, { NULL, 0, 0, 0, 0, 0 } } }, - { 0x001A, "IP35 Pro(Intel P35-ICH9R)", { + { 0x001A, "IP35 Pro", { { "CPU Core", 0, 0, 10, 1, 0 }, { "DDR2", 1, 0, 20, 1, 0 }, { "DDR2 VTT", 2, 0, 10, 1, 0 }, @@ -1128,6 +1128,7 @@ static int __init abituguru3_dmi_detect(void) { const char *board_vendor, *board_name; int i, err = (force) ? 1 : -ENODEV; + size_t sublen; board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR); if (!board_vendor || strcmp(board_vendor, "http://www.abit.com.tw/")) @@ -1137,9 +1138,20 @@ static int __init abituguru3_dmi_detect(void) if (!board_name) return err; + /* At the moment, we don't care about the part of the vendor + * DMI string contained in brackets. Truncate the string at + * the first occurrence of a bracket. Trim any trailing space + * from the substring. + */ + sublen = strcspn(board_name, "("); + while (sublen > 0 && board_name[sublen - 1] == ' ') + sublen--; + for (i = 0; abituguru3_motherboards[i].id; i++) { const char *dmi_name = abituguru3_motherboards[i].dmi_name; - if (dmi_name && !strcmp(dmi_name, board_name)) + if (!dmi_name || strlen(dmi_name) != sublen) + continue; + if (!strncasecmp(board_name, dmi_name, sublen)) break; } -- cgit v1.2.3 From 3907a8def78a15cd91985c23a3e76b563f36929a Mon Sep 17 00:00:00 2001 From: Alistair John Strachan Date: Thu, 15 Jan 2009 22:27:48 +0100 Subject: hwmon: (abituguru3) Enable DMI probing feature on IN9 32X MAX Switch the IN9 32X MAX over from port probing to the preferred DMI probe method. Signed-off-by: Alistair John Strachan Tested-by: Paul Hartman Cc: Hans de Goede Signed-off-by: Jean Delvare --- drivers/hwmon/abituguru3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/abituguru3.c b/drivers/hwmon/abituguru3.c index 4914b34e6bc..f948ed16e96 100644 --- a/drivers/hwmon/abituguru3.c +++ b/drivers/hwmon/abituguru3.c @@ -482,7 +482,7 @@ static const struct abituguru3_motherboard_info abituguru3_motherboards[] = { { "AUX3 Fan", 36, 2, 60, 1, 0 }, { NULL, 0, 0, 0, 0, 0 } } }, - { 0x0019, NULL /* Unknown, need DMI string */, { + { 0x0019, "IN9 32X MAX", { { "CPU Core", 7, 0, 10, 1, 0 }, { "DDR2", 13, 0, 20, 1, 0 }, { "DDR2 VTT", 14, 0, 10, 1, 0 }, -- cgit v1.2.3 From 46a5f173fc88ffc22651162033696d8a9fbcdc5c Mon Sep 17 00:00:00 2001 From: Alistair John Strachan Date: Thu, 15 Jan 2009 22:27:48 +0100 Subject: hwmon: (abituguru3) Fix CONFIG_DMI=n fallback to probe When CONFIG_DMI is not enabled, dmi detection should flag that no board could be detected (err=1) rather than another error condition (err<0). This fixes the fallback to manual probing for all motherboards, even those without DMI strings, when CONFIG_DMI=n. Signed-off-by: Alistair John Strachan Cc: Hans de Goede Signed-off-by: Jean Delvare --- drivers/hwmon/abituguru3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/abituguru3.c b/drivers/hwmon/abituguru3.c index f948ed16e96..e52b38806d0 100644 --- a/drivers/hwmon/abituguru3.c +++ b/drivers/hwmon/abituguru3.c @@ -1165,7 +1165,7 @@ static int __init abituguru3_dmi_detect(void) static inline int abituguru3_dmi_detect(void) { - return -ENODEV; + return 1; } #endif /* CONFIG_DMI */ -- cgit v1.2.3 From c3d6362b8717759de7f2086f9665a4d96cacbc51 Mon Sep 17 00:00:00 2001 From: Alex Murray Date: Thu, 15 Jan 2009 13:51:08 -0800 Subject: hwmon: applesmc: fix light sensor readings on newer MacBooks The light sensors ALV0 and ALV1 on newer MacBooks (early 2008 and later) changed to report 10 bytes instead the earlier 6, and the sensor encoding subsequently changed. As a result, the reported light sensors readings are much too low. Via experiments leading up to this patch, it seems only the ALV0 is reporting data, and the most useful value therein is a 10-bit big-endian value at offset 6. This suggests that a new protocol was added as a backward-compatible replacement on top of the old one. This patch makes applesmc report the improved light sensor reading for the new machines, on a scale in conformance with earlier ones. Signed-off-by: Alex Murray Signed-off-by: Henrik Rydberg Cc: Nicolas Boichat Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/hwmon/applesmc.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c index dca47a591ba..e3018623658 100644 --- a/drivers/hwmon/applesmc.c +++ b/drivers/hwmon/applesmc.c @@ -590,6 +590,11 @@ static ssize_t applesmc_light_show(struct device *dev, } ret = applesmc_read_key(LIGHT_SENSOR_LEFT_KEY, buffer, data_length); + /* newer macbooks report a single 10-bit bigendian value */ + if (data_length == 10) { + left = be16_to_cpu(*(__be16 *)(buffer + 6)) >> 2; + goto out; + } left = buffer[2]; if (ret) goto out; -- cgit v1.2.3 From 9e0c79782143a816ba7d7f0f6e195091a97053f6 Mon Sep 17 00:00:00 2001 From: Eric Piel Date: Thu, 15 Jan 2009 13:51:23 -0800 Subject: lis3lv02d: merge with leds hp disk Move the second part of the HP laptop disk protection functionality (a red led) to the same driver. From a purely Linux developer's point of view, the led and the accelerometer have nothing related. However, they correspond to the same ACPI functionality, and so will always be used together, moreover as they share the same ACPI PNP alias, there is no other simple to allow to have same loaded at the same time if they are not in the same module. Also make it requires the led class to compile and update the Kconfig text. Signed-off-by: Pavel Machek Signed-off-by: Eric Piel Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/hwmon/Kconfig | 14 +++++++++++--- drivers/hwmon/hp_accel.c | 43 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 52 insertions(+), 5 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 4b33bc82cc2..54b43bea5e4 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -861,6 +861,8 @@ config SENSORS_HDAPS config SENSORS_LIS3LV02D tristate "STMicroeletronics LIS3LV02Dx three-axis digital accelerometer" depends on ACPI && INPUT + select NEW_LEDS + select LEDS_CLASS default n help This driver provides support for the LIS3LV02Dx accelerometer. In @@ -872,10 +874,16 @@ config SENSORS_LIS3LV02D /sys/devices/platform/lis3lv02d. This driver also provides an absolute input class device, allowing - the laptop to act as a pinball machine-esque joystick. + the laptop to act as a pinball machine-esque joystick. On HP laptops, + if the led infrastructure is activated, support for a led indicating + disk protection will be provided as hp:red:hddprotection. - This driver can also be built as a module. If so, the module - will be called lis3lv02d. + This driver can also be built as modules. If so, the core module + will be called lis3lv02d and a specific module for HP laptops will be + called hp_accel. + + Say Y here if you have an applicable laptop and want to experience + the awesome power of lis3lv02d. config SENSORS_APPLESMC tristate "Apple SMC (Motion sensor, light sensor, keyboard backlight)" diff --git a/drivers/hwmon/hp_accel.c b/drivers/hwmon/hp_accel.c index bf8d4058057..86a0f51d99d 100644 --- a/drivers/hwmon/hp_accel.c +++ b/drivers/hwmon/hp_accel.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include "lis3lv02d.h" @@ -154,10 +155,34 @@ static struct dmi_system_id lis3lv02d_dmi_ids[] = { */ }; +static acpi_status hpled_acpi_write(acpi_handle handle, int reg) +{ + unsigned long long ret; /* Not used when writing */ + union acpi_object in_obj[1]; + struct acpi_object_list args = { 1, in_obj }; + + in_obj[0].type = ACPI_TYPE_INTEGER; + in_obj[0].integer.value = reg; + + return acpi_evaluate_integer(handle, "ALED", &args, &ret); +} + +static void hpled_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + hpled_acpi_write(adev.device->handle, !!value); +} + +static struct led_classdev hpled_led = { + .name = "hp:red:hddprotection", + .default_trigger = "none", + .brightness_set = hpled_set, +}; static int lis3lv02d_add(struct acpi_device *device) { u8 val; + int ret; if (!device) return -EINVAL; @@ -183,7 +208,17 @@ static int lis3lv02d_add(struct acpi_device *device) adev.ac = lis3lv02d_axis_normal; } - return lis3lv02d_init_device(&adev); + ret = led_classdev_register(NULL, &hpled_led); + if (ret) + return ret; + + ret = lis3lv02d_init_device(&adev); + if (ret) { + led_classdev_unregister(&hpled_led); + return ret; + } + + return ret; } static int lis3lv02d_remove(struct acpi_device *device, int type) @@ -194,6 +229,8 @@ static int lis3lv02d_remove(struct acpi_device *device, int type) lis3lv02d_joystick_disable(); lis3lv02d_poweroff(device->handle); + led_classdev_unregister(&hpled_led); + return lis3lv02d_remove_fs(); } @@ -203,6 +240,7 @@ static int lis3lv02d_suspend(struct acpi_device *device, pm_message_t state) { /* make sure the device is off when we suspend */ lis3lv02d_poweroff(device->handle); + led_classdev_suspend(&hpled_led); return 0; } @@ -215,6 +253,7 @@ static int lis3lv02d_resume(struct acpi_device *device) else lis3lv02d_poweroff(device->handle); mutex_unlock(&adev.lock); + led_classdev_resume(&hpled_led); return 0; } #else @@ -256,7 +295,7 @@ static void __exit lis3lv02d_exit_module(void) acpi_bus_unregister_driver(&lis3lv02d_driver); } -MODULE_DESCRIPTION("Glue between LIS3LV02Dx and HP ACPI BIOS"); +MODULE_DESCRIPTION("Glue between LIS3LV02Dx and HP ACPI BIOS and support for disk protection LED."); MODULE_AUTHOR("Yan Burman, Eric Piel, Pavel Machek"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 9e1c9d865543593ee92ec3a5075f064dec981a96 Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Thu, 15 Jan 2009 13:51:24 -0800 Subject: hp_accel: do not call ACPI from invalid context MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The LED on HP notebooks is connected through ACPI. That unfortunately means that it needs to be delayed by using schedule_work() to avoid calling the ACPI interpreter from an invalid context. [akpm@linux-foundation.org: use flush_work() rather than sort-of reimplementing it] Signed-off-by: Pavel Machek Cc: Éric Piel Cc: Len Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/hwmon/hp_accel.c | 68 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 49 insertions(+), 19 deletions(-) (limited to 'drivers/hwmon') diff --git a/drivers/hwmon/hp_accel.c b/drivers/hwmon/hp_accel.c index 86a0f51d99d..03705240000 100644 --- a/drivers/hwmon/hp_accel.c +++ b/drivers/hwmon/hp_accel.c @@ -3,7 +3,7 @@ * * Copyright (C) 2007-2008 Yan Burman * Copyright (C) 2008 Eric Piel - * Copyright (C) 2008 Pavel Machek + * Copyright (C) 2008-2009 Pavel Machek * * 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 @@ -44,6 +44,36 @@ #define DRIVER_NAME "lis3lv02d" #define ACPI_MDPS_CLASS "accelerometer" +/* Delayed LEDs infrastructure ------------------------------------ */ + +/* Special LED class that can defer work */ +struct delayed_led_classdev { + struct led_classdev led_classdev; + struct work_struct work; + enum led_brightness new_brightness; + + unsigned int led; /* For driver */ + void (*set_brightness)(struct delayed_led_classdev *data, enum led_brightness value); +}; + +static inline void delayed_set_status_worker(struct work_struct *work) +{ + struct delayed_led_classdev *data = + container_of(work, struct delayed_led_classdev, work); + + data->set_brightness(data, data->new_brightness); +} + +static inline void delayed_sysfs_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct delayed_led_classdev *data = container_of(led_cdev, + struct delayed_led_classdev, led_classdev); + data->new_brightness = brightness; + schedule_work(&data->work); +} + +/* HP-specific accelerometer driver ------------------------------------ */ /* For automatic insertion of the module */ static struct acpi_device_id lis3lv02d_device_ids[] = { @@ -155,28 +185,27 @@ static struct dmi_system_id lis3lv02d_dmi_ids[] = { */ }; -static acpi_status hpled_acpi_write(acpi_handle handle, int reg) +static void hpled_set(struct delayed_led_classdev *led_cdev, enum led_brightness value) { + acpi_handle handle = adev.device->handle; unsigned long long ret; /* Not used when writing */ union acpi_object in_obj[1]; struct acpi_object_list args = { 1, in_obj }; in_obj[0].type = ACPI_TYPE_INTEGER; - in_obj[0].integer.value = reg; + in_obj[0].integer.value = !!value; - return acpi_evaluate_integer(handle, "ALED", &args, &ret); -} - -static void hpled_set(struct led_classdev *led_cdev, - enum led_brightness value) -{ - hpled_acpi_write(adev.device->handle, !!value); + acpi_evaluate_integer(handle, "ALED", &args, &ret); } -static struct led_classdev hpled_led = { - .name = "hp:red:hddprotection", - .default_trigger = "none", - .brightness_set = hpled_set, +static struct delayed_led_classdev hpled_led = { + .led_classdev = { + .name = "hp::hddprotect", + .default_trigger = "none", + .brightness_set = delayed_sysfs_set, + .flags = LED_CORE_SUSPENDRESUME, + }, + .set_brightness = hpled_set, }; static int lis3lv02d_add(struct acpi_device *device) @@ -208,13 +237,15 @@ static int lis3lv02d_add(struct acpi_device *device) adev.ac = lis3lv02d_axis_normal; } - ret = led_classdev_register(NULL, &hpled_led); + INIT_WORK(&hpled_led.work, delayed_set_status_worker); + ret = led_classdev_register(NULL, &hpled_led.led_classdev); if (ret) return ret; ret = lis3lv02d_init_device(&adev); if (ret) { - led_classdev_unregister(&hpled_led); + flush_work(&hpled_led.work); + led_classdev_unregister(&hpled_led.led_classdev); return ret; } @@ -229,7 +260,8 @@ static int lis3lv02d_remove(struct acpi_device *device, int type) lis3lv02d_joystick_disable(); lis3lv02d_poweroff(device->handle); - led_classdev_unregister(&hpled_led); + flush_work(&hpled_led.work); + led_classdev_unregister(&hpled_led.led_classdev); return lis3lv02d_remove_fs(); } @@ -240,7 +272,6 @@ static int lis3lv02d_suspend(struct acpi_device *device, pm_message_t state) { /* make sure the device is off when we suspend */ lis3lv02d_poweroff(device->handle); - led_classdev_suspend(&hpled_led); return 0; } @@ -253,7 +284,6 @@ static int lis3lv02d_resume(struct acpi_device *device) else lis3lv02d_poweroff(device->handle); mutex_unlock(&adev.lock); - led_classdev_resume(&hpled_led); return 0; } #else -- cgit v1.2.3