Commit 04738b2b authored by Guenter Roeck's avatar Guenter Roeck Committed by Jean Delvare

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: default avatarGuenter Roeck <linux@roeck-us.net>
Signed-off-by: default avatarJean Delvare <khali@linux-fr.org>
parent 94e55df4
...@@ -61,9 +61,9 @@ PWM modes: manual and automatic. Automatic mode is not fully implemented yet ...@@ -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 (you cannot define your custom PWM/temperature curve), and mode change isn't
supported either. supported either.
The lm63 driver will not update its values more frequently than every The lm63 driver will not update its values more frequently than configured with
second; reading them more often will do no harm, but will return 'old' the update_interval sysfs attribute; reading them more often will do no harm,
values. but will return 'old' values.
The LM64 is effectively an LM63 with GPIO lines. The driver does not The LM64 is effectively an LM63 with GPIO lines. The driver does not
support these GPIO lines at present. support these GPIO lines at present.
......
...@@ -61,6 +61,7 @@ static const unsigned short normal_i2c[] = { 0x18, 0x4c, 0x4e, I2C_CLIENT_END }; ...@@ -61,6 +61,7 @@ static const unsigned short normal_i2c[] = { 0x18, 0x4c, 0x4e, I2C_CLIENT_END };
*/ */
#define LM63_REG_CONFIG1 0x03 #define LM63_REG_CONFIG1 0x03
#define LM63_REG_CONVRATE 0x04
#define LM63_REG_CONFIG2 0xBF #define LM63_REG_CONFIG2 0xBF
#define LM63_REG_CONFIG_FAN 0x4A #define LM63_REG_CONFIG_FAN 0x4A
...@@ -96,6 +97,11 @@ static const unsigned short normal_i2c[] = { 0x18, 0x4c, 0x4e, I2C_CLIENT_END }; ...@@ -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_REMOTE_TEMP_U_LSB 0x32
#define LM96163_REG_CONFIG_ENHANCED 0x45 #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 * Conversions and various macros
* For tachometer counts, the LM63 uses 16-bit values. * 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 }; ...@@ -132,6 +138,9 @@ static const unsigned short normal_i2c[] = { 0x18, 0x4c, 0x4e, I2C_CLIENT_END };
(val) >= 127000 ? 127 : \ (val) >= 127000 ? 127 : \
((val) + 500) / 1000) ((val) + 500) / 1000)
#define UPDATE_INTERVAL(max, rate) \
((1000 << (LM63_MAX_CONVRATE - (rate))) / (max))
/* /*
* Functions declaration * Functions declaration
*/ */
...@@ -180,9 +189,12 @@ struct lm63_data { ...@@ -180,9 +189,12 @@ struct lm63_data {
struct mutex update_lock; struct mutex update_lock;
char valid; /* zero until following fields are valid */ char valid; /* zero until following fields are valid */
unsigned long last_updated; /* in jiffies */ unsigned long last_updated; /* in jiffies */
int kind; enum chips kind;
int temp2_offset; int temp2_offset;
int update_interval; /* in milliseconds */
int max_convrate_hz;
/* registers values */ /* registers values */
u8 config, config_fan; u8 config, config_fan;
u16 fan[2]; /* 0: input u16 fan[2]; /* 0: input
...@@ -449,6 +461,58 @@ static ssize_t set_temp2_crit_hyst(struct device *dev, ...@@ -449,6 +461,58 @@ static ssize_t set_temp2_crit_hyst(struct device *dev,
return count; 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, static ssize_t show_alarms(struct device *dev, struct device_attribute *dummy,
char *buf) char *buf)
{ {
...@@ -499,6 +563,9 @@ static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 6); ...@@ -499,6 +563,9 @@ static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 6);
/* Raw alarm file for compatibility */ /* Raw alarm file for compatibility */
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); 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[] = { static struct attribute *lm63_attributes[] = {
&dev_attr_pwm1.attr, &dev_attr_pwm1.attr,
&dev_attr_pwm1_enable.attr, &dev_attr_pwm1_enable.attr,
...@@ -517,6 +584,7 @@ static struct attribute *lm63_attributes[] = { ...@@ -517,6 +584,7 @@ static struct attribute *lm63_attributes[] = {
&sensor_dev_attr_temp2_max_alarm.dev_attr.attr, &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
&sensor_dev_attr_temp1_max_alarm.dev_attr.attr, &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
&dev_attr_alarms.attr, &dev_attr_alarms.attr,
&dev_attr_update_interval.attr,
NULL NULL
}; };
...@@ -669,6 +737,7 @@ static int lm63_probe(struct i2c_client *new_client, ...@@ -669,6 +737,7 @@ static int lm63_probe(struct i2c_client *new_client,
static void lm63_init_client(struct i2c_client *client) static void lm63_init_client(struct i2c_client *client)
{ {
struct lm63_data *data = i2c_get_clientdata(client); struct lm63_data *data = i2c_get_clientdata(client);
u8 convrate;
data->config = i2c_smbus_read_byte_data(client, LM63_REG_CONFIG1); data->config = i2c_smbus_read_byte_data(client, LM63_REG_CONFIG1);
data->config_fan = i2c_smbus_read_byte_data(client, data->config_fan = i2c_smbus_read_byte_data(client,
...@@ -687,6 +756,21 @@ static void lm63_init_client(struct i2c_client *client) ...@@ -687,6 +756,21 @@ static void lm63_init_client(struct i2c_client *client)
if (data->pwm1_freq == 0) if (data->pwm1_freq == 0)
data->pwm1_freq = 1; 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 * For LM96163, check if high resolution PWM
* and unsigned temperature format is enabled. * and unsigned temperature format is enabled.
...@@ -730,10 +814,14 @@ static struct lm63_data *lm63_update_device(struct device *dev) ...@@ -730,10 +814,14 @@ static struct lm63_data *lm63_update_device(struct device *dev)
{ {
struct i2c_client *client = to_i2c_client(dev); struct i2c_client *client = to_i2c_client(dev);
struct lm63_data *data = i2c_get_clientdata(client); struct lm63_data *data = i2c_get_clientdata(client);
unsigned long next_update;
mutex_lock(&data->update_lock); 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 */ if (data->config & 0x04) { /* tachometer enabled */
/* order matters for fan1_input */ /* order matters for fan1_input */
data->fan[0] = i2c_smbus_read_byte_data(client, data->fan[0] = i2c_smbus_read_byte_data(client,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment