Skip to content

Commit

Permalink
hwmon: (lm63) Make fan speed control strategy changeable
Browse files Browse the repository at this point in the history
Let the user switch between automatic and manual fan speed control.
Before switching to automatic fan speed control, we always check that
the lookup table looks sane.

Signed-off-by: Jean Delvare <[email protected]>
Acked-by: Guenter Roeck <[email protected]>
  • Loading branch information
Jean Delvare authored and Jean Delvare committed Mar 23, 2012
1 parent dac27dc commit 817c6cc
Showing 1 changed file with 89 additions and 16 deletions.
105 changes: 89 additions & 16 deletions drivers/hwmon/lm63.c
Original file line number Diff line number Diff line change
Expand Up @@ -205,12 +205,36 @@ static inline int lut_temp_from_reg(struct lm63_data *data, int nr)
return data->temp8[nr] * (data->lut_temp_highres ? 500 : 1000);
}

/*
* Update the lookup table register cache.
* client->update_lock must be held when calling this function.
*/
static void lm63_update_lut(struct i2c_client *client)
{
struct lm63_data *data = i2c_get_clientdata(client);
int i;

if (time_after(jiffies, data->lut_last_updated + 5 * HZ) ||
!data->lut_valid) {
for (i = 0; i < data->lut_size; 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;
}
}

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);

Expand Down Expand Up @@ -278,26 +302,39 @@ 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 < data->lut_size; 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;
}
lm63_update_lut(client);

mutex_unlock(&data->update_lock);

return data;
}

/*
* Trip points in the lookup table should be in ascending order for both
* temperatures and PWM output values.
*/
static int lm63_lut_looks_bad(struct i2c_client *client)
{
struct lm63_data *data = i2c_get_clientdata(client);
int i;

mutex_lock(&data->update_lock);
lm63_update_lut(client);

for (i = 1; i < data->lut_size; i++) {
if (data->pwm1[1 + i - 1] > data->pwm1[1 + i]
|| data->temp8[3 + i - 1] > data->temp8[3 + i]) {
dev_warn(&client->dev,
"Lookup table doesn't look sane (check entries %d and %d)\n",
i, i + 1);
break;
}
}
mutex_unlock(&data->update_lock);

return i == data->lut_size ? 0 : 1;
}

/*
* Sysfs callback functions and files
*/
Expand Down Expand Up @@ -381,6 +418,41 @@ static ssize_t show_pwm1_enable(struct device *dev,
return sprintf(buf, "%d\n", data->config_fan & 0x20 ? 1 : 2);
}

static ssize_t set_pwm1_enable(struct device *dev,
struct device_attribute *dummy,
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;
if (val < 1 || val > 2)
return -EINVAL;

/*
* Only let the user switch to automatic mode if the lookup table
* looks sane.
*/
if (val == 2 && lm63_lut_looks_bad(client))
return -EPERM;

mutex_lock(&data->update_lock);
data->config_fan = i2c_smbus_read_byte_data(client,
LM63_REG_CONFIG_FAN);
if (val == 1)
data->config_fan |= 0x20;
else
data->config_fan &= ~0x20;
i2c_smbus_write_byte_data(client, LM63_REG_CONFIG_FAN,
data->config_fan);
mutex_unlock(&data->update_lock);
return count;
}

/*
* There are 8bit registers for both local(temp1) and remote(temp2) sensor.
* For remote sensor registers temp2_offset has to be considered,
Expand Down Expand Up @@ -669,7 +741,8 @@ static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan,
set_fan, 1);

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 DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO,
show_pwm1_enable, set_pwm1_enable);
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);
Expand Down

0 comments on commit 817c6cc

Please sign in to comment.