From 210961c436d5c552a816ae9c6b38cbc8b993395a Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 16 Jan 2012 22:51:45 +0100 Subject: hwmon: (lm63) Add support for LM96163 LM96163 is an enhanced version of LM63 with improved PWM resolution. Add chip detection code as well as support for improved PWM resolution if the chip is configured to use it. Signed-off-by: Guenter Roeck Tested-by: Thierry Reding Signed-off-by: Jean Delvare --- Documentation/hwmon/lm63 | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'Documentation') diff --git a/Documentation/hwmon/lm63 b/Documentation/hwmon/lm63 index b9843eab1afb..af3e8b0ad9c4 100644 --- a/Documentation/hwmon/lm63 +++ b/Documentation/hwmon/lm63 @@ -12,6 +12,11 @@ Supported chips: Addresses scanned: I2C 0x18 and 0x4e Datasheet: Publicly available at the National Semiconductor website http://www.national.com/pf/LM/LM64.html + * National Semiconductor LM96163 + Prefix: 'lm96163' + Addresses scanned: I2C 0x4c + Datasheet: Publicly available at the National Semiconductor website + http://www.national.com/pf/LM/LM96163.html Author: Jean Delvare @@ -62,3 +67,6 @@ values. The LM64 is effectively an LM63 with GPIO lines. The driver does not support these GPIO lines at present. + +The LM96163 is an enhanced version of LM63 with improved temperature accuracy +and better PWM resolution. -- cgit v1.2.3-59-g8ed1b From 04738b2b2f37c13bbe37b7695fec6c1c60d79c7a Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 16 Jan 2012 22:51:46 +0100 Subject: hwmon: (lm63) Add support for update_interval sysfs attribute The update interval is configurable on LM63 and compatibles. Add support for it. Signed-off-by: Guenter Roeck Signed-off-by: Jean Delvare --- Documentation/hwmon/lm63 | 6 ++-- drivers/hwmon/lm63.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 93 insertions(+), 5 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/lm63 b/Documentation/hwmon/lm63 index af3e8b0ad9c4..0e695d3567eb 100644 --- a/Documentation/hwmon/lm63 +++ b/Documentation/hwmon/lm63 @@ -61,9 +61,9 @@ PWM modes: manual and automatic. Automatic mode is not fully implemented yet (you cannot define your custom PWM/temperature curve), and mode change isn't supported either. -The lm63 driver will not update its values more frequently than every -second; reading them more often will do no harm, but will return 'old' -values. +The lm63 driver will not update its values more frequently than configured with +the update_interval sysfs attribute; reading them more often will do no harm, +but will return 'old' values. The LM64 is effectively an LM63 with GPIO lines. The driver does not support these GPIO lines at present. diff --git a/drivers/hwmon/lm63.c b/drivers/hwmon/lm63.c index a9e6212ed54a..5f6da52a7219 100644 --- a/drivers/hwmon/lm63.c +++ b/drivers/hwmon/lm63.c @@ -61,6 +61,7 @@ static const unsigned short normal_i2c[] = { 0x18, 0x4c, 0x4e, I2C_CLIENT_END }; */ #define LM63_REG_CONFIG1 0x03 +#define LM63_REG_CONVRATE 0x04 #define LM63_REG_CONFIG2 0xBF #define LM63_REG_CONFIG_FAN 0x4A @@ -96,6 +97,11 @@ static const unsigned short normal_i2c[] = { 0x18, 0x4c, 0x4e, I2C_CLIENT_END }; #define LM96163_REG_REMOTE_TEMP_U_LSB 0x32 #define LM96163_REG_CONFIG_ENHANCED 0x45 +#define LM63_MAX_CONVRATE 9 + +#define LM63_MAX_CONVRATE_HZ 32 +#define LM96163_MAX_CONVRATE_HZ 26 + /* * Conversions and various macros * For tachometer counts, the LM63 uses 16-bit values. @@ -132,6 +138,9 @@ static const unsigned short normal_i2c[] = { 0x18, 0x4c, 0x4e, I2C_CLIENT_END }; (val) >= 127000 ? 127 : \ ((val) + 500) / 1000) +#define UPDATE_INTERVAL(max, rate) \ + ((1000 << (LM63_MAX_CONVRATE - (rate))) / (max)) + /* * Functions declaration */ @@ -180,9 +189,12 @@ struct lm63_data { struct mutex update_lock; char valid; /* zero until following fields are valid */ unsigned long last_updated; /* in jiffies */ - int kind; + enum chips kind; int temp2_offset; + int update_interval; /* in milliseconds */ + int max_convrate_hz; + /* registers values */ u8 config, config_fan; u16 fan[2]; /* 0: input @@ -449,6 +461,58 @@ static ssize_t set_temp2_crit_hyst(struct device *dev, return count; } +/* + * Set conversion rate. + * client->update_lock must be held when calling this function. + */ +static void lm63_set_convrate(struct i2c_client *client, struct lm63_data *data, + unsigned int interval) +{ + int i; + unsigned int update_interval; + + /* Shift calculations to avoid rounding errors */ + interval <<= 6; + + /* find the nearest update rate */ + update_interval = (1 << (LM63_MAX_CONVRATE + 6)) * 1000 + / data->max_convrate_hz; + for (i = 0; i < LM63_MAX_CONVRATE; i++, update_interval >>= 1) + if (interval >= update_interval * 3 / 4) + break; + + i2c_smbus_write_byte_data(client, LM63_REG_CONVRATE, i); + data->update_interval = UPDATE_INTERVAL(data->max_convrate_hz, i); +} + +static ssize_t show_update_interval(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct lm63_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", data->update_interval); +} + +static ssize_t set_update_interval(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm63_data *data = i2c_get_clientdata(client); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err) + return err; + + mutex_lock(&data->update_lock); + lm63_set_convrate(client, data, SENSORS_LIMIT(val, 0, 100000)); + mutex_unlock(&data->update_lock); + + return count; +} + static ssize_t show_alarms(struct device *dev, struct device_attribute *dummy, char *buf) { @@ -499,6 +563,9 @@ static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 6); /* Raw alarm file for compatibility */ static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); +static DEVICE_ATTR(update_interval, S_IRUGO | S_IWUSR, show_update_interval, + set_update_interval); + static struct attribute *lm63_attributes[] = { &dev_attr_pwm1.attr, &dev_attr_pwm1_enable.attr, @@ -517,6 +584,7 @@ static struct attribute *lm63_attributes[] = { &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, &dev_attr_alarms.attr, + &dev_attr_update_interval.attr, NULL }; @@ -669,6 +737,7 @@ exit: static void lm63_init_client(struct i2c_client *client) { struct lm63_data *data = i2c_get_clientdata(client); + u8 convrate; data->config = i2c_smbus_read_byte_data(client, LM63_REG_CONFIG1); data->config_fan = i2c_smbus_read_byte_data(client, @@ -687,6 +756,21 @@ static void lm63_init_client(struct i2c_client *client) if (data->pwm1_freq == 0) data->pwm1_freq = 1; + switch (data->kind) { + case lm63: + case lm64: + data->max_convrate_hz = LM63_MAX_CONVRATE_HZ; + break; + case lm96163: + data->max_convrate_hz = LM96163_MAX_CONVRATE_HZ; + break; + } + convrate = i2c_smbus_read_byte_data(client, LM63_REG_CONVRATE); + if (unlikely(convrate > LM63_MAX_CONVRATE)) + convrate = LM63_MAX_CONVRATE; + data->update_interval = UPDATE_INTERVAL(data->max_convrate_hz, + convrate); + /* * For LM96163, check if high resolution PWM * and unsigned temperature format is enabled. @@ -730,10 +814,14 @@ static struct lm63_data *lm63_update_device(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct lm63_data *data = i2c_get_clientdata(client); + unsigned long next_update; mutex_lock(&data->update_lock); - if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { + next_update = data->last_updated + + msecs_to_jiffies(data->update_interval) + 1; + + if (time_after(jiffies, next_update) || !data->valid) { if (data->config & 0x04) { /* tachometer enabled */ /* order matters for fan1_input */ data->fan[0] = i2c_smbus_read_byte_data(client, -- cgit v1.2.3-59-g8ed1b From f496b2d4f181fa5fcdf24016b11caaa33eb12477 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 16 Jan 2012 22:51:46 +0100 Subject: hwmon: (lm63) Add sensor type attribute for external sensor on LM96163 On LM96163, the external temperature sensor type is configurable to either a thermal diode or a 3904 transistor. The chip reports a wrong temperature if misconfigured. Add writable attribute to support it. Signed-off-by: Guenter Roeck Signed-off-by: Jean Delvare --- Documentation/hwmon/lm63 | 3 ++- drivers/hwmon/lm63.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/lm63 b/Documentation/hwmon/lm63 index 0e695d3567eb..8202825cd095 100644 --- a/Documentation/hwmon/lm63 +++ b/Documentation/hwmon/lm63 @@ -69,4 +69,5 @@ The LM64 is effectively an LM63 with GPIO lines. The driver does not support these GPIO lines at present. The LM96163 is an enhanced version of LM63 with improved temperature accuracy -and better PWM resolution. +and better PWM resolution. For LM96163, the external temperature sensor type is +configurable as CPU embedded diode(1) or 3904 transistor(2). diff --git a/drivers/hwmon/lm63.c b/drivers/hwmon/lm63.c index 5f6da52a7219..75e3a15c2f61 100644 --- a/drivers/hwmon/lm63.c +++ b/drivers/hwmon/lm63.c @@ -93,6 +93,7 @@ static const unsigned short normal_i2c[] = { 0x18, 0x4c, 0x4e, I2C_CLIENT_END }; #define LM63_REG_MAN_ID 0xFE #define LM63_REG_CHIP_ID 0xFF +#define LM96163_REG_TRUTHERM 0x30 #define LM96163_REG_REMOTE_TEMP_U_MSB 0x31 #define LM96163_REG_REMOTE_TEMP_U_LSB 0x32 #define LM96163_REG_CONFIG_ENHANCED 0x45 @@ -213,6 +214,7 @@ struct lm63_data { u8 alarms; bool pwm_highres; bool remote_unsigned; /* true if unsigned remote upper limits */ + bool trutherm; }; static inline int temp8_from_reg(struct lm63_data *data, int nr) @@ -513,6 +515,41 @@ static ssize_t set_update_interval(struct device *dev, return count; } +static ssize_t show_type(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm63_data *data = i2c_get_clientdata(client); + + return sprintf(buf, data->trutherm ? "1\n" : "2\n"); +} + +static ssize_t set_type(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm63_data *data = i2c_get_clientdata(client); + unsigned long val; + int ret; + u8 reg; + + ret = kstrtoul(buf, 10, &val); + if (ret < 0) + return ret; + if (val != 1 && val != 2) + return -EINVAL; + + mutex_lock(&data->update_lock); + data->trutherm = val == 1; + reg = i2c_smbus_read_byte_data(client, LM96163_REG_TRUTHERM) & ~0x02; + i2c_smbus_write_byte_data(client, LM96163_REG_TRUTHERM, + reg | (data->trutherm ? 0x02 : 0x00)); + data->valid = 0; + mutex_unlock(&data->update_lock); + + return count; +} + static ssize_t show_alarms(struct device *dev, struct device_attribute *dummy, char *buf) { @@ -553,6 +590,8 @@ static SENSOR_DEVICE_ATTR(temp2_crit, S_IRUGO, show_remote_temp8, static DEVICE_ATTR(temp2_crit_hyst, S_IWUSR | S_IRUGO, show_temp2_crit_hyst, set_temp2_crit_hyst); +static DEVICE_ATTR(temp2_type, S_IWUSR | S_IRUGO, show_type, set_type); + /* Individual alarm files */ static SENSOR_DEVICE_ATTR(fan1_min_alarm, S_IRUGO, show_alarm, NULL, 0); static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, 1); @@ -712,6 +751,12 @@ static int lm63_probe(struct i2c_client *new_client, if (err) goto exit_remove_files; } + if (data->kind == lm96163) { + err = device_create_file(&new_client->dev, + &dev_attr_temp2_type); + if (err) + goto exit_remove_files; + } data->hwmon_dev = hwmon_device_register(&new_client->dev); if (IS_ERR(data->hwmon_dev)) { @@ -722,6 +767,7 @@ static int lm63_probe(struct i2c_client *new_client, return 0; exit_remove_files: + device_remove_file(&new_client->dev, &dev_attr_temp2_type); sysfs_remove_group(&new_client->dev.kobj, &lm63_group); sysfs_remove_group(&new_client->dev.kobj, &lm63_group_fan1); exit_free: @@ -763,6 +809,9 @@ static void lm63_init_client(struct i2c_client *client) break; case lm96163: data->max_convrate_hz = LM96163_MAX_CONVRATE_HZ; + data->trutherm + = i2c_smbus_read_byte_data(client, + LM96163_REG_TRUTHERM) & 0x02; break; } convrate = i2c_smbus_read_byte_data(client, LM63_REG_CONVRATE); @@ -803,6 +852,7 @@ static int lm63_remove(struct i2c_client *client) struct lm63_data *data = i2c_get_clientdata(client); hwmon_device_unregister(data->hwmon_dev); + device_remove_file(&client->dev, &dev_attr_temp2_type); sysfs_remove_group(&client->dev.kobj, &lm63_group); sysfs_remove_group(&client->dev.kobj, &lm63_group_fan1); -- cgit v1.2.3-59-g8ed1b From 409c0b5bdf7d80e61380ce6b226b98405576d7cc Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Mon, 16 Jan 2012 22:51:46 +0100 Subject: hwmon: (lm63) LM64 has a dedicated pin for tachometer On the LM64, the tachometer function has a dedicated pin and fan speed monitoring is always enabled. Signed-off-by: Jean Delvare Acked-by: Guenter Roeck --- Documentation/hwmon/lm63 | 5 ++++- drivers/hwmon/lm63.c | 10 +++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/lm63 b/Documentation/hwmon/lm63 index 8202825cd095..df3e1ae42f39 100644 --- a/Documentation/hwmon/lm63 +++ b/Documentation/hwmon/lm63 @@ -54,7 +54,10 @@ value for measuring the speed of the fan. It can measure fan speeds down to Note that the pin used for fan monitoring is shared with an alert out function. Depending on how the board designer wanted to use the chip, fan speed monitoring will or will not be possible. The proper chip configuration -is left to the BIOS, and the driver will blindly trust it. +is left to the BIOS, and the driver will blindly trust it. Only the original +LM63 suffers from this limitation, the LM64 and LM96163 have separate pins +for fan monitoring and alert out. On the LM64, monitoring is always enabled; +on the LM96163 it can be disabled. A PWM output can be used to control the speed of the fan. The LM63 has two PWM modes: manual and automatic. Automatic mode is not fully implemented yet diff --git a/drivers/hwmon/lm63.c b/drivers/hwmon/lm63.c index 75e3a15c2f61..0c4fff0a00a4 100644 --- a/drivers/hwmon/lm63.c +++ b/drivers/hwmon/lm63.c @@ -796,6 +796,9 @@ static void lm63_init_client(struct i2c_client *client) i2c_smbus_write_byte_data(client, LM63_REG_CONFIG1, data->config); } + /* Tachometer is always enabled on LM64 */ + if (data->kind == lm64) + data->config |= 0x04; /* We may need pwm1_freq before ever updating the client data */ data->pwm1_freq = i2c_smbus_read_byte_data(client, LM63_REG_PWM_FREQ); @@ -836,9 +839,10 @@ static void lm63_init_client(struct i2c_client *client) } /* Show some debug info about the LM63 configuration */ - dev_dbg(&client->dev, "Alert/tach pin configured for %s\n", - (data->config & 0x04) ? "tachometer input" : - "alert output"); + if (data->kind == lm63) + dev_dbg(&client->dev, "Alert/tach pin configured for %s\n", + (data->config & 0x04) ? "tachometer input" : + "alert output"); dev_dbg(&client->dev, "PWM clock %s kHz, output frequency %u Hz\n", (data->config_fan & 0x08) ? "1.4" : "360", ((data->config_fan & 0x08) ? 700 : 180000) / data->pwm1_freq); -- cgit v1.2.3-59-g8ed1b From d216f6809eb690b9a888c286cde68cda4d0c4cfa Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Mon, 16 Jan 2012 22:51:47 +0100 Subject: hwmon: (lm63) Expose automatic fan speed control lookup table The LM63 and compatible devices have a lookup table to control the fan speed automatically. Expose it in sysfs. Values are cached for 5 seconds, independently of the other register values to avoid slowing down "sensors". We might make the table values writable in the future. Signed-off-by: Jean Delvare Tested-by: Guenter Roeck Acked-by: Guenter Roeck --- Documentation/hwmon/lm63 | 3 +- drivers/hwmon/lm63.c | 148 ++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 136 insertions(+), 15 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/lm63 b/Documentation/hwmon/lm63 index df3e1ae42f39..4d30d209881a 100644 --- a/Documentation/hwmon/lm63 +++ b/Documentation/hwmon/lm63 @@ -66,7 +66,8 @@ supported either. The lm63 driver will not update its values more frequently than configured with the update_interval sysfs attribute; reading them more often will do no harm, -but will return 'old' values. +but will return 'old' values. Values in the automatic fan control lookup table +(attributes pwm1_auto_*) have their own independent lifetime of 5 seconds. The LM64 is effectively an LM63 with GPIO lines. The driver does not support these GPIO lines at present. diff --git a/drivers/hwmon/lm63.c b/drivers/hwmon/lm63.c index a5e4ba82af17..1c06a333ba20 100644 --- a/drivers/hwmon/lm63.c +++ b/drivers/hwmon/lm63.c @@ -75,6 +75,9 @@ static const unsigned short normal_i2c[] = { 0x18, 0x4c, 0x4e, I2C_CLIENT_END }; #define LM63_REG_PWM_VALUE 0x4C #define LM63_REG_PWM_FREQ 0x4D +#define LM63_REG_LUT_TEMP_HYST 0x4F +#define LM63_REG_LUT_TEMP(nr) (0x50 + 2 * (nr)) +#define LM63_REG_LUT_PWM(nr) (0x51 + 2 * (nr)) #define LM63_REG_LOCAL_TEMP 0x00 #define LM63_REG_LOCAL_HIGH 0x05 @@ -192,7 +195,9 @@ struct lm63_data { struct device *hwmon_dev; struct mutex update_lock; char valid; /* zero until following fields are valid */ + char lut_valid; /* zero until lut fields are valid */ unsigned long last_updated; /* in jiffies */ + unsigned long lut_last_updated; /* in jiffies */ enum chips kind; int temp2_offset; @@ -204,18 +209,22 @@ struct lm63_data { u16 fan[2]; /* 0: input 1: low limit */ u8 pwm1_freq; - u8 pwm1_value; - s8 temp8[3]; /* 0: local input + u8 pwm1[9]; /* 0: current output + 1-8: lookup table */ + s8 temp8[11]; /* 0: local input 1: local high limit - 2: remote critical limit */ + 2: remote critical limit + 3-10: lookup table */ s16 temp11[4]; /* 0: remote input 1: remote low limit 2: remote high limit 3: remote offset */ u16 temp11u; /* remote input (unsigned) */ u8 temp2_crit_hyst; + u8 lut_temp_hyst; u8 alarms; bool pwm_highres; + bool lut_temp_highres; bool remote_unsigned; /* true if unsigned remote upper limits */ bool trutherm; }; @@ -227,6 +236,11 @@ static inline int temp8_from_reg(struct lm63_data *data, int nr) return TEMP8_FROM_REG(data->temp8[nr]); } +static inline int lut_temp_from_reg(struct lm63_data *data, int nr) +{ + return data->temp8[nr] * (data->lut_temp_highres ? 500 : 1000); +} + /* * Sysfs callback functions and files */ @@ -261,17 +275,19 @@ static ssize_t set_fan(struct device *dev, struct device_attribute *dummy, return count; } -static ssize_t show_pwm1(struct device *dev, struct device_attribute *dummy, +static ssize_t show_pwm1(struct device *dev, struct device_attribute *devattr, char *buf) { + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct lm63_data *data = lm63_update_device(dev); + int nr = attr->index; int pwm; if (data->pwm_highres) - pwm = data->pwm1_value; + pwm = data->pwm1[nr]; else - pwm = data->pwm1_value >= 2 * data->pwm1_freq ? - 255 : (data->pwm1_value * 255 + data->pwm1_freq) / + pwm = data->pwm1[nr] >= 2 * data->pwm1_freq ? + 255 : (data->pwm1[nr] * 255 + data->pwm1_freq) / (2 * data->pwm1_freq); return sprintf(buf, "%d\n", pwm); @@ -294,9 +310,9 @@ static ssize_t set_pwm1(struct device *dev, struct device_attribute *dummy, val = SENSORS_LIMIT(val, 0, 255); mutex_lock(&data->update_lock); - data->pwm1_value = data->pwm_highres ? val : - (val * data->pwm1_freq * 2 + 127) / 255; - i2c_smbus_write_byte_data(client, LM63_REG_PWM_VALUE, data->pwm1_value); + data->pwm1[0] = data->pwm_highres ? val : + (val * data->pwm1_freq * 2 + 127) / 255; + i2c_smbus_write_byte_data(client, LM63_REG_PWM_VALUE, data->pwm1[0]); mutex_unlock(&data->update_lock); return count; } @@ -333,6 +349,16 @@ static ssize_t show_remote_temp8(struct device *dev, + data->temp2_offset); } +static ssize_t show_lut_temp(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct lm63_data *data = lm63_update_device(dev); + return sprintf(buf, "%d\n", lut_temp_from_reg(data, attr->index) + + data->temp2_offset); +} + static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { @@ -440,6 +466,17 @@ static ssize_t show_temp2_crit_hyst(struct device *dev, - TEMP8_FROM_REG(data->temp2_crit_hyst)); } +static ssize_t show_lut_temp_hyst(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct lm63_data *data = lm63_update_device(dev); + + return sprintf(buf, "%d\n", lut_temp_from_reg(data, attr->index) + + data->temp2_offset + - TEMP8_FROM_REG(data->lut_temp_hyst)); +} + /* * And now the other way around, user-space provides an absolute * hysteresis value and we have to store a relative one @@ -574,8 +611,48 @@ static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0); static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan, set_fan, 1); -static DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm1, set_pwm1); +static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm1, set_pwm1, 0); static DEVICE_ATTR(pwm1_enable, S_IRUGO, show_pwm1_enable, NULL); +static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IRUGO, show_pwm1, NULL, 1); +static SENSOR_DEVICE_ATTR(pwm1_auto_point1_temp, S_IRUGO, + show_lut_temp, NULL, 3); +static SENSOR_DEVICE_ATTR(pwm1_auto_point1_temp_hyst, S_IRUGO, + show_lut_temp_hyst, NULL, 3); +static SENSOR_DEVICE_ATTR(pwm1_auto_point2_pwm, S_IRUGO, show_pwm1, NULL, 2); +static SENSOR_DEVICE_ATTR(pwm1_auto_point2_temp, S_IRUGO, + show_lut_temp, NULL, 4); +static SENSOR_DEVICE_ATTR(pwm1_auto_point2_temp_hyst, S_IRUGO, + show_lut_temp_hyst, NULL, 4); +static SENSOR_DEVICE_ATTR(pwm1_auto_point3_pwm, S_IRUGO, show_pwm1, NULL, 3); +static SENSOR_DEVICE_ATTR(pwm1_auto_point3_temp, S_IRUGO, + show_lut_temp, NULL, 5); +static SENSOR_DEVICE_ATTR(pwm1_auto_point3_temp_hyst, S_IRUGO, + show_lut_temp_hyst, NULL, 5); +static SENSOR_DEVICE_ATTR(pwm1_auto_point4_pwm, S_IRUGO, show_pwm1, NULL, 4); +static SENSOR_DEVICE_ATTR(pwm1_auto_point4_temp, S_IRUGO, + show_lut_temp, NULL, 6); +static SENSOR_DEVICE_ATTR(pwm1_auto_point4_temp_hyst, S_IRUGO, + show_lut_temp_hyst, NULL, 6); +static SENSOR_DEVICE_ATTR(pwm1_auto_point5_pwm, S_IRUGO, show_pwm1, NULL, 5); +static SENSOR_DEVICE_ATTR(pwm1_auto_point5_temp, S_IRUGO, + show_lut_temp, NULL, 7); +static SENSOR_DEVICE_ATTR(pwm1_auto_point5_temp_hyst, S_IRUGO, + show_lut_temp_hyst, NULL, 7); +static SENSOR_DEVICE_ATTR(pwm1_auto_point6_pwm, S_IRUGO, show_pwm1, NULL, 6); +static SENSOR_DEVICE_ATTR(pwm1_auto_point6_temp, S_IRUGO, + show_lut_temp, NULL, 8); +static SENSOR_DEVICE_ATTR(pwm1_auto_point6_temp_hyst, S_IRUGO, + show_lut_temp_hyst, NULL, 8); +static SENSOR_DEVICE_ATTR(pwm1_auto_point7_pwm, S_IRUGO, show_pwm1, NULL, 7); +static SENSOR_DEVICE_ATTR(pwm1_auto_point7_temp, S_IRUGO, + show_lut_temp, NULL, 9); +static SENSOR_DEVICE_ATTR(pwm1_auto_point7_temp_hyst, S_IRUGO, + show_lut_temp_hyst, NULL, 9); +static SENSOR_DEVICE_ATTR(pwm1_auto_point8_pwm, S_IRUGO, show_pwm1, NULL, 8); +static SENSOR_DEVICE_ATTR(pwm1_auto_point8_temp, S_IRUGO, + show_lut_temp, NULL, 10); +static SENSOR_DEVICE_ATTR(pwm1_auto_point8_temp_hyst, S_IRUGO, + show_lut_temp_hyst, NULL, 10); static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_local_temp8, NULL, 0); static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_local_temp8, @@ -609,8 +686,33 @@ static DEVICE_ATTR(update_interval, S_IRUGO | S_IWUSR, show_update_interval, set_update_interval); static struct attribute *lm63_attributes[] = { - &dev_attr_pwm1.attr, + &sensor_dev_attr_pwm1.dev_attr.attr, &dev_attr_pwm1_enable.attr, + &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point1_temp_hyst.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point2_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point2_temp_hyst.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point3_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point3_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point3_temp_hyst.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point4_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point4_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point4_temp_hyst.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point5_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point5_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point5_temp_hyst.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point6_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point6_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point6_temp_hyst.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point7_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point7_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point7_temp_hyst.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point8_pwm.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point8_temp.dev_attr.attr, + &sensor_dev_attr_pwm1_auto_point8_temp_hyst.dev_attr.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, &sensor_dev_attr_temp2_input.dev_attr.attr, &sensor_dev_attr_temp2_min.dev_attr.attr, @@ -834,6 +936,8 @@ static void lm63_init_client(struct i2c_client *client) u8 config_enhanced = i2c_smbus_read_byte_data(client, LM96163_REG_CONFIG_ENHANCED); + if (config_enhanced & 0x20) + data->lut_temp_highres = true; if ((config_enhanced & 0x10) && !(data->config_fan & 0x08) && data->pwm1_freq == 8) data->pwm_highres = true; @@ -872,6 +976,7 @@ static struct lm63_data *lm63_update_device(struct device *dev) struct i2c_client *client = to_i2c_client(dev); struct lm63_data *data = i2c_get_clientdata(client); unsigned long next_update; + int i; mutex_lock(&data->update_lock); @@ -895,8 +1000,8 @@ static struct lm63_data *lm63_update_device(struct device *dev) LM63_REG_PWM_FREQ); if (data->pwm1_freq == 0) data->pwm1_freq = 1; - data->pwm1_value = i2c_smbus_read_byte_data(client, - LM63_REG_PWM_VALUE); + data->pwm1[0] = i2c_smbus_read_byte_data(client, + LM63_REG_PWM_VALUE); data->temp8[0] = i2c_smbus_read_byte_data(client, LM63_REG_LOCAL_TEMP); @@ -939,6 +1044,21 @@ static struct lm63_data *lm63_update_device(struct device *dev) data->valid = 1; } + if (time_after(jiffies, data->lut_last_updated + 5 * HZ) || + !data->lut_valid) { + for (i = 0; i < 8; i++) { + data->pwm1[1 + i] = i2c_smbus_read_byte_data(client, + LM63_REG_LUT_PWM(i)); + data->temp8[3 + i] = i2c_smbus_read_byte_data(client, + LM63_REG_LUT_TEMP(i)); + } + data->lut_temp_hyst = i2c_smbus_read_byte_data(client, + LM63_REG_LUT_TEMP_HYST); + + data->lut_last_updated = jiffies; + data->lut_valid = 1; + } + mutex_unlock(&data->update_lock); return data; -- cgit v1.2.3-59-g8ed1b From 16b5dda22e3798e61bb008d2329d4f4d90ef764e Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Mon, 16 Jan 2012 22:51:48 +0100 Subject: hwmon: (it87) Add IT8728F support Until we get a datasheet for the IT8728F, treat it as fully compatible with the IT8721F, as it seems to work reasonably well. This closes kernel bug #27262. Signed-off-by: Jean Delvare Acked-by: Guenter Roeck --- Documentation/hwmon/it87 | 13 ++++++++--- drivers/hwmon/Kconfig | 4 ++-- drivers/hwmon/it87.c | 61 ++++++++++++++++++++++++++++++++++++------------ 3 files changed, 58 insertions(+), 20 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/it87 b/Documentation/hwmon/it87 index 6f496a586732..23b7def21ba8 100644 --- a/Documentation/hwmon/it87 +++ b/Documentation/hwmon/it87 @@ -26,6 +26,10 @@ Supported chips: Prefix: 'it8721' Addresses scanned: from Super I/O config space (8 I/O ports) Datasheet: Not publicly available + * IT8728F + Prefix: 'it8728' + Addresses scanned: from Super I/O config space (8 I/O ports) + Datasheet: Not publicly available * SiS950 [clone of IT8705F] Prefix: 'it87' Addresses scanned: from Super I/O config space (8 I/O ports) @@ -71,7 +75,7 @@ Description ----------- This driver implements support for the IT8705F, IT8712F, IT8716F, -IT8718F, IT8720F, IT8721F, IT8726F, IT8758E and SiS950 chips. +IT8718F, IT8720F, IT8721F, IT8726F, IT8728F, IT8758E and SiS950 chips. These chips are 'Super I/O chips', supporting floppy disks, infrared ports, joysticks and other miscellaneous stuff. For hardware monitoring, they @@ -105,6 +109,9 @@ The IT8726F is just bit enhanced IT8716F with additional hardware for AMD power sequencing. Therefore the chip will appear as IT8716F to userspace applications. +The IT8728F is considered compatible with the IT8721F, until a datasheet +becomes available (hopefully.) + Temperatures are measured in degrees Celsius. An alarm is triggered once when the Overtemperature Shutdown limit is crossed. @@ -121,8 +128,8 @@ alarm is triggered if the voltage has crossed a programmable minimum or maximum limit. Note that minimum in this case always means 'closest to zero'; this is important for negative voltage measurements. All voltage inputs can measure voltages between 0 and 4.08 volts, with a resolution of -0.016 volt (except IT8721F/IT8758E: 0.012 volt.) The battery voltage in8 does -not have limit registers. +0.016 volt (except IT8721F/IT8758E and IT8728F: 0.012 volt.) The battery +voltage in8 does not have limit registers. On the IT8721F/IT8758E, some voltage inputs are internal and scaled inside the chip (in7, in8 and optionally in3). The driver handles this transparently diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index f468bbb6357a..02260406b9e4 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -474,8 +474,8 @@ config SENSORS_IT87 select HWMON_VID help If you say yes here you get support for ITE IT8705F, IT8712F, - IT8716F, IT8718F, IT8720F, IT8721F, IT8726F and IT8758E sensor - chips, and the SiS960 clone. + IT8716F, IT8718F, IT8720F, IT8721F, IT8726F, IT8728F and IT8758E + 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 603ef2af2707..0054d6f9cec9 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -17,6 +17,7 @@ * IT8720F Super I/O chip w/LPC interface * IT8721F Super I/O chip w/LPC interface * IT8726F Super I/O chip w/LPC interface + * IT8728F Super I/O chip w/LPC interface * IT8758E Super I/O chip w/LPC interface * Sis950 A clone of the IT8705F * @@ -58,7 +59,7 @@ #define DRVNAME "it87" -enum chips { it87, it8712, it8716, it8718, it8720, it8721 }; +enum chips { it87, it8712, it8716, it8718, it8720, it8721, it8728 }; static unsigned short force_id; module_param(force_id, ushort, 0); @@ -135,6 +136,7 @@ static inline void superio_exit(void) #define IT8720F_DEVID 0x8720 #define IT8721F_DEVID 0x8721 #define IT8726F_DEVID 0x8726 +#define IT8728F_DEVID 0x8728 #define IT87_ACT_REG 0x30 #define IT87_BASE_REG 0x60 @@ -274,11 +276,31 @@ struct it87_data { s8 auto_temp[3][5]; /* [nr][0] is point1_temp_hyst */ }; +static inline int has_12mv_adc(const struct it87_data *data) +{ + /* + * IT8721F and later have a 12 mV ADC, also with internal scaling + * on selected inputs. + */ + return data->type == it8721 + || data->type == it8728; +} + +static inline int has_newer_autopwm(const struct it87_data *data) +{ + /* + * IT8721F and later have separate registers for the temperature + * mapping and the manual duty cycle. + */ + return data->type == it8721 + || data->type == it8728; +} + static u8 in_to_reg(const struct it87_data *data, int nr, long val) { long lsb; - if (data->type == it8721) { + if (has_12mv_adc(data)) { if (data->in_scaled & (1 << nr)) lsb = 24; else @@ -292,7 +314,7 @@ static u8 in_to_reg(const struct it87_data *data, int nr, long val) static int in_from_reg(const struct it87_data *data, int nr, int val) { - if (data->type == it8721) { + if (has_12mv_adc(data)) { if (data->in_scaled & (1 << nr)) return val * 24; else @@ -329,7 +351,7 @@ static inline u16 FAN16_TO_REG(long rpm) static u8 pwm_to_reg(const struct it87_data *data, long val) { - if (data->type == it8721) + if (has_newer_autopwm(data)) return val; else return val >> 1; @@ -337,7 +359,7 @@ static u8 pwm_to_reg(const struct it87_data *data, long val) static int pwm_from_reg(const struct it87_data *data, u8 reg) { - if (data->type == it8721) + if (has_newer_autopwm(data)) return reg; else return (reg & 0x7f) << 1; @@ -374,7 +396,8 @@ static inline int has_16bit_fans(const struct it87_data *data) || data->type == it8716 || data->type == it8718 || data->type == it8720 - || data->type == it8721; + || data->type == it8721 + || data->type == it8728; } static inline int has_old_autopwm(const struct it87_data *data) @@ -842,7 +865,7 @@ static ssize_t set_pwm_enable(struct device *dev, data->fan_main_ctrl); } else { if (val == 1) /* Manual mode */ - data->pwm_ctrl[nr] = data->type == it8721 ? + data->pwm_ctrl[nr] = has_newer_autopwm(data) ? data->pwm_temp_map[nr] : data->pwm_duty[nr]; else /* Automatic mode */ @@ -870,7 +893,7 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, return -EINVAL; mutex_lock(&data->update_lock); - if (data->type == it8721) { + if (has_newer_autopwm(data)) { /* If we are in automatic mode, the PWM duty cycle register * is read-only so we can't write the value */ if (data->pwm_ctrl[nr] & 0x80) { @@ -1311,8 +1334,8 @@ static ssize_t show_label(struct device *dev, struct device_attribute *attr, struct it87_data *data = dev_get_drvdata(dev); int nr = to_sensor_dev_attr(attr)->index; - return sprintf(buf, "%s\n", data->type == it8721 ? labels_it8721[nr] - : labels[nr]); + return sprintf(buf, "%s\n", has_12mv_adc(data) ? labels_it8721[nr] + : labels[nr]); } static SENSOR_DEVICE_ATTR(in3_label, S_IRUGO, show_label, NULL, 0); static SENSOR_DEVICE_ATTR(in7_label, S_IRUGO, show_label, NULL, 1); @@ -1605,6 +1628,9 @@ static int __init it87_find(unsigned short *address, case IT8721F_DEVID: sio_data->type = it8721; break; + case IT8728F_DEVID: + sio_data->type = it8728; + break; case 0xffff: /* No device at all */ goto exit; default: @@ -1646,8 +1672,11 @@ static int __init it87_find(unsigned short *address, superio_select(GPIO); reg = superio_inb(IT87_SIO_GPIO3_REG); - if (sio_data->type == it8721) { - /* The IT8721F/IT8758E doesn't have VID pins at all */ + if (sio_data->type == it8721 || sio_data->type == it8728) { + /* + * The IT8721F/IT8758E doesn't have VID pins at all, + * not sure about the IT8728F. + */ sio_data->skip_vid = 1; } else { /* We need at least 4 VID pins */ @@ -1692,7 +1721,8 @@ static int __init it87_find(unsigned short *address, } if (reg & (1 << 0)) sio_data->internal |= (1 << 0); - if ((reg & (1 << 1)) || sio_data->type == it8721) + if ((reg & (1 << 1)) || sio_data->type == it8721 || + sio_data->type == it8728) sio_data->internal |= (1 << 1); sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f; @@ -1770,6 +1800,7 @@ static int __devinit it87_probe(struct platform_device *pdev) "it8718", "it8720", "it8721", + "it8728", }; res = platform_get_resource(pdev, IORESOURCE_IO, 0); @@ -1807,7 +1838,7 @@ static int __devinit it87_probe(struct platform_device *pdev) enable_pwm_interface = it87_check_pwm(dev); /* Starting with IT8721F, we handle scaling of internal voltages */ - if (data->type == it8721) { + if (has_12mv_adc(data)) { if (sio_data->internal & (1 << 0)) data->in_scaled |= (1 << 3); /* in3 is AVCC */ if (sio_data->internal & (1 << 1)) @@ -2093,7 +2124,7 @@ static void __devinit it87_init_device(struct platform_device *pdev) static void it87_update_pwm_ctrl(struct it87_data *data, int nr) { data->pwm_ctrl[nr] = it87_read_value(data, IT87_REG_PWM(nr)); - if (data->type == it8721) { + if (has_newer_autopwm(data)) { data->pwm_temp_map[nr] = data->pwm_ctrl[nr] & 0x03; data->pwm_duty[nr] = it87_read_value(data, IT87_REG_PWM_DUTY(nr)); -- cgit v1.2.3-59-g8ed1b From 5f8b1f877e0212bfde8cb950725391f4a50c9396 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 16 Jan 2012 22:51:48 +0100 Subject: hwmon: (sysfs-interface) Update tempX_type attribute to be more generic The temp[1-*]_type attribute reports the temperature sensor type. Sensor type 1 is described as "PII/Celeron Diode", which is quite restrictive; other CPUs may also have an embedded temperature sensor diode with similar characteristics. Change description to "CPU embedded diode" to be more generic. Signed-off-by: Guenter Roeck Signed-off-by: Jean Delvare --- Documentation/hwmon/sysfs-interface | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/sysfs-interface b/Documentation/hwmon/sysfs-interface index a4aa8f600e09..1f4dd855a299 100644 --- a/Documentation/hwmon/sysfs-interface +++ b/Documentation/hwmon/sysfs-interface @@ -304,7 +304,7 @@ value (fastest fan speed) wins. temp[1-*]_type Sensor type selection. Integers 1 to 6 RW - 1: PII/Celeron Diode + 1: CPU embedded diode 2: 3904 transistor 3: thermal diode 4: thermistor -- cgit v1.2.3-59-g8ed1b