diff options
Diffstat (limited to 'drivers/hwmon/adt7470.c')
-rw-r--r-- | drivers/hwmon/adt7470.c | 108 |
1 files changed, 102 insertions, 6 deletions
diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index f5da39a68929..6e60ca53406e 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -32,6 +32,7 @@ #include <linux/log2.h> #include <linux/kthread.h> #include <linux/slab.h> +#include <linux/util_macros.h> /* Addresses to scan */ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; @@ -83,6 +84,7 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; #define ADT7470_REG_PWM_MIN_MAX_ADDR 0x6D #define ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR 0x6E #define ADT7470_REG_PWM_TEMP_MIN_MAX_ADDR 0x71 +#define ADT7470_REG_CFG_2 0x74 #define ADT7470_REG_ACOUSTICS12 0x75 #define ADT7470_REG_ACOUSTICS34 0x76 #define ADT7470_REG_DEVICE 0x3D @@ -142,6 +144,11 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; #define FAN_PERIOD_INVALID 65535 #define FAN_DATA_VALID(x) ((x) && (x) != FAN_PERIOD_INVALID) +/* Config registers 1 and 2 include fields for selecting the PWM frequency */ +#define ADT7470_CFG_LF 0x40 +#define ADT7470_FREQ_MASK 0x70 +#define ADT7470_FREQ_SHIFT 4 + struct adt7470_data { struct i2c_client *client; struct mutex lock; @@ -170,7 +177,6 @@ struct adt7470_data { u8 pwm_auto_temp[ADT7470_PWM_COUNT]; struct task_struct *auto_update; - struct completion auto_update_stop; unsigned int auto_update_interval; }; @@ -266,12 +272,14 @@ static int adt7470_update_thread(void *p) mutex_lock(&data->lock); adt7470_read_temperatures(client, data); mutex_unlock(&data->lock); + + set_current_state(TASK_INTERRUPTIBLE); if (kthread_should_stop()) break; - msleep_interruptible(data->auto_update_interval); + + schedule_timeout(msecs_to_jiffies(data->auto_update_interval)); } - complete_all(&data->auto_update_stop); return 0; } @@ -538,6 +546,28 @@ static ssize_t show_alarm_mask(struct device *dev, return sprintf(buf, "%x\n", data->alarms_mask); } +static ssize_t set_alarm_mask(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct adt7470_data *data = dev_get_drvdata(dev); + long mask; + + if (kstrtoul(buf, 0, &mask)) + return -EINVAL; + + if (mask & ~0xffff) + return -EINVAL; + + mutex_lock(&data->lock); + data->alarms_mask = mask; + adt7470_write_word_data(data->client, ADT7470_REG_ALARM1_MASK, mask); + mutex_unlock(&data->lock); + + return count; +} + static ssize_t show_fan_max(struct device *dev, struct device_attribute *devattr, char *buf) @@ -688,6 +718,70 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr, return count; } +/* These are the valid PWM frequencies to the nearest Hz */ +static const int adt7470_freq_map[] = { + 11, 15, 22, 29, 35, 44, 59, 88, 1400, 22500 +}; + +static ssize_t show_pwm_freq(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct adt7470_data *data = adt7470_update_device(dev); + unsigned char cfg_reg_1; + unsigned char cfg_reg_2; + int index; + + mutex_lock(&data->lock); + cfg_reg_1 = i2c_smbus_read_byte_data(data->client, ADT7470_REG_CFG); + cfg_reg_2 = i2c_smbus_read_byte_data(data->client, ADT7470_REG_CFG_2); + mutex_unlock(&data->lock); + + index = (cfg_reg_2 & ADT7470_FREQ_MASK) >> ADT7470_FREQ_SHIFT; + if (!(cfg_reg_1 & ADT7470_CFG_LF)) + index += 8; + if (index >= ARRAY_SIZE(adt7470_freq_map)) + index = ARRAY_SIZE(adt7470_freq_map) - 1; + + return scnprintf(buf, PAGE_SIZE, "%d\n", adt7470_freq_map[index]); +} + +static ssize_t set_pwm_freq(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct adt7470_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + long freq; + int index; + int low_freq = ADT7470_CFG_LF; + unsigned char val; + + if (kstrtol(buf, 10, &freq)) + return -EINVAL; + + /* Round the user value given to the closest available frequency */ + index = find_closest(freq, adt7470_freq_map, + ARRAY_SIZE(adt7470_freq_map)); + + if (index >= 8) { + index -= 8; + low_freq = 0; + } + + mutex_lock(&data->lock); + /* Configuration Register 1 */ + val = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG); + i2c_smbus_write_byte_data(client, ADT7470_REG_CFG, + (val & ~ADT7470_CFG_LF) | low_freq); + /* Configuration Register 2 */ + val = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG_2); + i2c_smbus_write_byte_data(client, ADT7470_REG_CFG_2, + (val & ~ADT7470_FREQ_MASK) | (index << ADT7470_FREQ_SHIFT)); + mutex_unlock(&data->lock); + + return count; +} + static ssize_t show_pwm_max(struct device *dev, struct device_attribute *devattr, char *buf) @@ -918,7 +1012,8 @@ static ssize_t show_alarm(struct device *dev, return sprintf(buf, "0\n"); } -static DEVICE_ATTR(alarm_mask, S_IRUGO, show_alarm_mask, NULL); +static DEVICE_ATTR(alarm_mask, S_IWUSR | S_IRUGO, show_alarm_mask, + set_alarm_mask); static DEVICE_ATTR(num_temp_sensors, S_IWUSR | S_IRUGO, show_num_temp_sensors, set_num_temp_sensors); static DEVICE_ATTR(auto_update_interval, S_IWUSR | S_IRUGO, @@ -1038,6 +1133,8 @@ static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 1); static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 2); static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 3); +static DEVICE_ATTR(pwm1_freq, S_IWUSR | S_IRUGO, show_pwm_freq, set_pwm_freq); + static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IWUSR | S_IRUGO, show_pwm_min, set_pwm_min, 0); static SENSOR_DEVICE_ATTR(pwm2_auto_point1_pwm, S_IWUSR | S_IRUGO, @@ -1154,6 +1251,7 @@ static struct attribute *adt7470_attrs[] = { &sensor_dev_attr_fan4_alarm.dev_attr.attr, &sensor_dev_attr_force_pwm_max.dev_attr.attr, &sensor_dev_attr_pwm1.dev_attr.attr, + &dev_attr_pwm1_freq.attr, &sensor_dev_attr_pwm2.dev_attr.attr, &sensor_dev_attr_pwm3.dev_attr.attr, &sensor_dev_attr_pwm4.dev_attr.attr, @@ -1256,7 +1354,6 @@ static int adt7470_probe(struct i2c_client *client, if (IS_ERR(hwmon_dev)) return PTR_ERR(hwmon_dev); - init_completion(&data->auto_update_stop); data->auto_update = kthread_run(adt7470_update_thread, client, "%s", dev_name(hwmon_dev)); if (IS_ERR(data->auto_update)) { @@ -1271,7 +1368,6 @@ static int adt7470_remove(struct i2c_client *client) struct adt7470_data *data = i2c_get_clientdata(client); kthread_stop(data->auto_update); - wait_for_completion(&data->auto_update_stop); return 0; } |