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/hwmon/lm63') 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 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/hwmon/lm63') 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 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/hwmon/lm63') 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 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/hwmon/lm63') 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 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/hwmon/lm63') 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