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 <linux@roeck-us.net>
Signed-off-by: Jean Delvare <khali@linux-fr.org>
This commit is contained in:
Guenter Roeck
2012-01-16 22:51:46 +01:00
committed by Jean Delvare
parent 94e55df48a
commit 04738b2b2f
2 changed files with 93 additions and 5 deletions

View File

@@ -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,