mirror of https://gitee.com/openkylin/linux.git
hwmon: Add fan speed control features to w83627ehf
This patch adds long-awaited support for automatic fan modes. Based on the work of Yuan Mu from Winbond, I finished the support with the great help of David Hubbard. Signed-off-by: Yuan Mu <Ymu@Winbond.com.tw> Signed-off-by: Rudolf Marek <r.marek@sh.cvut.cz> Signed-off-by: David Hubbard <david.c.hubbard@gmail.com> Signed-off-by: Jean Delvare <khali@linux-fr.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
94c12cc7d1
commit
08c79950a0
|
@ -2,6 +2,8 @@
|
||||||
w83627ehf - Driver for the hardware monitoring functionality of
|
w83627ehf - Driver for the hardware monitoring functionality of
|
||||||
the Winbond W83627EHF Super-I/O chip
|
the Winbond W83627EHF Super-I/O chip
|
||||||
Copyright (C) 2005 Jean Delvare <khali@linux-fr.org>
|
Copyright (C) 2005 Jean Delvare <khali@linux-fr.org>
|
||||||
|
Copyright (C) 2006 Yuan Mu <Ymu@Winbond.com.tw>,
|
||||||
|
Rudolf Marek <r.marek@sh.cvut.cz>
|
||||||
|
|
||||||
Shamelessly ripped from the w83627hf driver
|
Shamelessly ripped from the w83627hf driver
|
||||||
Copyright (C) 2003 Mark Studebaker
|
Copyright (C) 2003 Mark Studebaker
|
||||||
|
@ -29,8 +31,8 @@
|
||||||
|
|
||||||
Supports the following chips:
|
Supports the following chips:
|
||||||
|
|
||||||
Chip #vin #fan #pwm #temp chip_id man_id
|
Chip #vin #fan #pwm #temp chip_id man_id
|
||||||
w83627ehf 10 5 - 3 0x88 0x5ca3
|
w83627ehf 10 5 4 3 0x88,0xa1 0x5ca3
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
@ -145,10 +147,44 @@ static const u16 W83627EHF_REG_TEMP_CONFIG[] = { 0x152, 0x252 };
|
||||||
#define W83627EHF_REG_ALARM2 0x45A
|
#define W83627EHF_REG_ALARM2 0x45A
|
||||||
#define W83627EHF_REG_ALARM3 0x45B
|
#define W83627EHF_REG_ALARM3 0x45B
|
||||||
|
|
||||||
|
/* SmartFan registers */
|
||||||
|
/* DC or PWM output fan configuration */
|
||||||
|
static const u8 W83627EHF_REG_PWM_ENABLE[] = {
|
||||||
|
0x04, /* SYS FAN0 output mode and PWM mode */
|
||||||
|
0x04, /* CPU FAN0 output mode and PWM mode */
|
||||||
|
0x12, /* AUX FAN mode */
|
||||||
|
0x62, /* CPU fan1 mode */
|
||||||
|
};
|
||||||
|
|
||||||
|
static const u8 W83627EHF_PWM_MODE_SHIFT[] = { 0, 1, 0, 6 };
|
||||||
|
static const u8 W83627EHF_PWM_ENABLE_SHIFT[] = { 2, 4, 1, 4 };
|
||||||
|
|
||||||
|
/* FAN Duty Cycle, be used to control */
|
||||||
|
static const u8 W83627EHF_REG_PWM[] = { 0x01, 0x03, 0x11, 0x61 };
|
||||||
|
static const u8 W83627EHF_REG_TARGET[] = { 0x05, 0x06, 0x13, 0x63 };
|
||||||
|
static const u8 W83627EHF_REG_TOLERANCE[] = { 0x07, 0x07, 0x14, 0x62 };
|
||||||
|
|
||||||
|
|
||||||
|
/* Advanced Fan control, some values are common for all fans */
|
||||||
|
static const u8 W83627EHF_REG_FAN_MIN_OUTPUT[] = { 0x08, 0x09, 0x15, 0x64 };
|
||||||
|
static const u8 W83627EHF_REG_FAN_STOP_TIME[] = { 0x0C, 0x0D, 0x17, 0x66 };
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Conversions
|
* Conversions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* 1 is PWM mode, output in ms */
|
||||||
|
static inline unsigned int step_time_from_reg(u8 reg, u8 mode)
|
||||||
|
{
|
||||||
|
return mode ? 100 * reg : 400 * reg;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u8 step_time_to_reg(unsigned int msec, u8 mode)
|
||||||
|
{
|
||||||
|
return SENSORS_LIMIT((mode ? (msec + 50) / 100 :
|
||||||
|
(msec + 200) / 400), 1, 255);
|
||||||
|
}
|
||||||
|
|
||||||
static inline unsigned int
|
static inline unsigned int
|
||||||
fan_from_reg(u8 reg, unsigned int div)
|
fan_from_reg(u8 reg, unsigned int div)
|
||||||
{
|
{
|
||||||
|
@ -170,12 +206,12 @@ temp1_from_reg(s8 reg)
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline s8
|
static inline s8
|
||||||
temp1_to_reg(int temp)
|
temp1_to_reg(int temp, int min, int max)
|
||||||
{
|
{
|
||||||
if (temp <= -128000)
|
if (temp <= min)
|
||||||
return -128;
|
return min / 1000;
|
||||||
if (temp >= 127000)
|
if (temp >= max)
|
||||||
return 127;
|
return max / 1000;
|
||||||
if (temp < 0)
|
if (temp < 0)
|
||||||
return (temp - 500) / 1000;
|
return (temp - 500) / 1000;
|
||||||
return (temp + 500) / 1000;
|
return (temp + 500) / 1000;
|
||||||
|
@ -223,6 +259,16 @@ struct w83627ehf_data {
|
||||||
s16 temp_max[2];
|
s16 temp_max[2];
|
||||||
s16 temp_max_hyst[2];
|
s16 temp_max_hyst[2];
|
||||||
u32 alarms;
|
u32 alarms;
|
||||||
|
|
||||||
|
u8 pwm_mode[4]; /* 0->DC variable voltage, 1->PWM variable duty cycle */
|
||||||
|
u8 pwm_enable[4]; /* 1->manual
|
||||||
|
2->thermal cruise (also called SmartFan I) */
|
||||||
|
u8 pwm[4];
|
||||||
|
u8 target_temp[4];
|
||||||
|
u8 tolerance[4];
|
||||||
|
|
||||||
|
u8 fan_min_output[4]; /* minimum fan speed */
|
||||||
|
u8 fan_stop_time[4];
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline int is_word_sized(u16 reg)
|
static inline int is_word_sized(u16 reg)
|
||||||
|
@ -349,6 +395,7 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
|
||||||
{
|
{
|
||||||
struct i2c_client *client = to_i2c_client(dev);
|
struct i2c_client *client = to_i2c_client(dev);
|
||||||
struct w83627ehf_data *data = i2c_get_clientdata(client);
|
struct w83627ehf_data *data = i2c_get_clientdata(client);
|
||||||
|
int pwmcfg = 0, tolerance = 0; /* shut up the compiler */
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
mutex_lock(&data->update_lock);
|
mutex_lock(&data->update_lock);
|
||||||
|
@ -416,6 +463,34 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 4; i++) {
|
||||||
|
/* pwmcfg, tolarance mapped for i=0, i=1 to same reg */
|
||||||
|
if (i != 1) {
|
||||||
|
pwmcfg = w83627ehf_read_value(client,
|
||||||
|
W83627EHF_REG_PWM_ENABLE[i]);
|
||||||
|
tolerance = w83627ehf_read_value(client,
|
||||||
|
W83627EHF_REG_TOLERANCE[i]);
|
||||||
|
}
|
||||||
|
data->pwm_mode[i] =
|
||||||
|
((pwmcfg >> W83627EHF_PWM_MODE_SHIFT[i]) & 1)
|
||||||
|
? 0 : 1;
|
||||||
|
data->pwm_enable[i] =
|
||||||
|
((pwmcfg >> W83627EHF_PWM_ENABLE_SHIFT[i])
|
||||||
|
& 3) + 1;
|
||||||
|
data->pwm[i] = w83627ehf_read_value(client,
|
||||||
|
W83627EHF_REG_PWM[i]);
|
||||||
|
data->fan_min_output[i] = w83627ehf_read_value(client,
|
||||||
|
W83627EHF_REG_FAN_MIN_OUTPUT[i]);
|
||||||
|
data->fan_stop_time[i] = w83627ehf_read_value(client,
|
||||||
|
W83627EHF_REG_FAN_STOP_TIME[i]);
|
||||||
|
data->target_temp[i] =
|
||||||
|
w83627ehf_read_value(client,
|
||||||
|
W83627EHF_REG_TARGET[i]) &
|
||||||
|
(data->pwm_mode[i] == 1 ? 0x7f : 0xff);
|
||||||
|
data->tolerance[i] = (tolerance >> (i == 1 ? 4 : 0))
|
||||||
|
& 0x0f;
|
||||||
|
}
|
||||||
|
|
||||||
/* Measured temperatures and limits */
|
/* Measured temperatures and limits */
|
||||||
data->temp1 = w83627ehf_read_value(client,
|
data->temp1 = w83627ehf_read_value(client,
|
||||||
W83627EHF_REG_TEMP1);
|
W83627EHF_REG_TEMP1);
|
||||||
|
@ -711,7 +786,7 @@ store_temp1_##reg(struct device *dev, struct device_attribute *attr, \
|
||||||
u32 val = simple_strtoul(buf, NULL, 10); \
|
u32 val = simple_strtoul(buf, NULL, 10); \
|
||||||
\
|
\
|
||||||
mutex_lock(&data->update_lock); \
|
mutex_lock(&data->update_lock); \
|
||||||
data->temp1_##reg = temp1_to_reg(val); \
|
data->temp1_##reg = temp1_to_reg(val, -128000, 127000); \
|
||||||
w83627ehf_write_value(client, W83627EHF_REG_TEMP1_##REG, \
|
w83627ehf_write_value(client, W83627EHF_REG_TEMP1_##REG, \
|
||||||
data->temp1_##reg); \
|
data->temp1_##reg); \
|
||||||
mutex_unlock(&data->update_lock); \
|
mutex_unlock(&data->update_lock); \
|
||||||
|
@ -777,6 +852,281 @@ static struct sensor_device_attribute sda_temp[] = {
|
||||||
SENSOR_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 13),
|
SENSOR_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 13),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define show_pwm_reg(reg) \
|
||||||
|
static ssize_t show_##reg (struct device *dev, struct device_attribute *attr, \
|
||||||
|
char *buf) \
|
||||||
|
{ \
|
||||||
|
struct w83627ehf_data *data = w83627ehf_update_device(dev); \
|
||||||
|
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \
|
||||||
|
int nr = sensor_attr->index; \
|
||||||
|
return sprintf(buf, "%d\n", data->reg[nr]); \
|
||||||
|
}
|
||||||
|
|
||||||
|
show_pwm_reg(pwm_mode)
|
||||||
|
show_pwm_reg(pwm_enable)
|
||||||
|
show_pwm_reg(pwm)
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
store_pwm_mode(struct device *dev, struct device_attribute *attr,
|
||||||
|
const char *buf, size_t count)
|
||||||
|
{
|
||||||
|
struct i2c_client *client = to_i2c_client(dev);
|
||||||
|
struct w83627ehf_data *data = i2c_get_clientdata(client);
|
||||||
|
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
|
||||||
|
int nr = sensor_attr->index;
|
||||||
|
u32 val = simple_strtoul(buf, NULL, 10);
|
||||||
|
u16 reg;
|
||||||
|
|
||||||
|
if (val > 1)
|
||||||
|
return -EINVAL;
|
||||||
|
mutex_lock(&data->update_lock);
|
||||||
|
reg = w83627ehf_read_value(client, W83627EHF_REG_PWM_ENABLE[nr]);
|
||||||
|
data->pwm_mode[nr] = val;
|
||||||
|
reg &= ~(1 << W83627EHF_PWM_MODE_SHIFT[nr]);
|
||||||
|
if (!val)
|
||||||
|
reg |= 1 << W83627EHF_PWM_MODE_SHIFT[nr];
|
||||||
|
w83627ehf_write_value(client, W83627EHF_REG_PWM_ENABLE[nr], reg);
|
||||||
|
mutex_unlock(&data->update_lock);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
store_pwm(struct device *dev, struct device_attribute *attr,
|
||||||
|
const char *buf, size_t count)
|
||||||
|
{
|
||||||
|
struct i2c_client *client = to_i2c_client(dev);
|
||||||
|
struct w83627ehf_data *data = i2c_get_clientdata(client);
|
||||||
|
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
|
||||||
|
int nr = sensor_attr->index;
|
||||||
|
u32 val = SENSORS_LIMIT(simple_strtoul(buf, NULL, 10), 0, 255);
|
||||||
|
|
||||||
|
mutex_lock(&data->update_lock);
|
||||||
|
data->pwm[nr] = val;
|
||||||
|
w83627ehf_write_value(client, W83627EHF_REG_PWM[nr], val);
|
||||||
|
mutex_unlock(&data->update_lock);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
store_pwm_enable(struct device *dev, struct device_attribute *attr,
|
||||||
|
const char *buf, size_t count)
|
||||||
|
{
|
||||||
|
struct i2c_client *client = to_i2c_client(dev);
|
||||||
|
struct w83627ehf_data *data = i2c_get_clientdata(client);
|
||||||
|
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
|
||||||
|
int nr = sensor_attr->index;
|
||||||
|
u32 val = simple_strtoul(buf, NULL, 10);
|
||||||
|
u16 reg;
|
||||||
|
|
||||||
|
if (!val || (val > 2)) /* only modes 1 and 2 are supported */
|
||||||
|
return -EINVAL;
|
||||||
|
mutex_lock(&data->update_lock);
|
||||||
|
reg = w83627ehf_read_value(client, W83627EHF_REG_PWM_ENABLE[nr]);
|
||||||
|
data->pwm_enable[nr] = val;
|
||||||
|
reg &= ~(0x03 << W83627EHF_PWM_ENABLE_SHIFT[nr]);
|
||||||
|
reg |= (val - 1) << W83627EHF_PWM_ENABLE_SHIFT[nr];
|
||||||
|
w83627ehf_write_value(client, W83627EHF_REG_PWM_ENABLE[nr], reg);
|
||||||
|
mutex_unlock(&data->update_lock);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define show_tol_temp(reg) \
|
||||||
|
static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \
|
||||||
|
char *buf) \
|
||||||
|
{ \
|
||||||
|
struct w83627ehf_data *data = w83627ehf_update_device(dev); \
|
||||||
|
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \
|
||||||
|
int nr = sensor_attr->index; \
|
||||||
|
return sprintf(buf, "%d\n", temp1_from_reg(data->reg[nr])); \
|
||||||
|
}
|
||||||
|
|
||||||
|
show_tol_temp(tolerance)
|
||||||
|
show_tol_temp(target_temp)
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
store_target_temp(struct device *dev, struct device_attribute *attr,
|
||||||
|
const char *buf, size_t count)
|
||||||
|
{
|
||||||
|
struct i2c_client *client = to_i2c_client(dev);
|
||||||
|
struct w83627ehf_data *data = i2c_get_clientdata(client);
|
||||||
|
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
|
||||||
|
int nr = sensor_attr->index;
|
||||||
|
u8 val = temp1_to_reg(simple_strtoul(buf, NULL, 10), 0, 127000);
|
||||||
|
|
||||||
|
mutex_lock(&data->update_lock);
|
||||||
|
data->target_temp[nr] = val;
|
||||||
|
w83627ehf_write_value(client, W83627EHF_REG_TARGET[nr], val);
|
||||||
|
mutex_unlock(&data->update_lock);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
store_tolerance(struct device *dev, struct device_attribute *attr,
|
||||||
|
const char *buf, size_t count)
|
||||||
|
{
|
||||||
|
struct i2c_client *client = to_i2c_client(dev);
|
||||||
|
struct w83627ehf_data *data = i2c_get_clientdata(client);
|
||||||
|
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
|
||||||
|
int nr = sensor_attr->index;
|
||||||
|
u16 reg;
|
||||||
|
/* Limit the temp to 0C - 15C */
|
||||||
|
u8 val = temp1_to_reg(simple_strtoul(buf, NULL, 10), 0, 15000);
|
||||||
|
|
||||||
|
mutex_lock(&data->update_lock);
|
||||||
|
reg = w83627ehf_read_value(client, W83627EHF_REG_TOLERANCE[nr]);
|
||||||
|
data->tolerance[nr] = val;
|
||||||
|
if (nr == 1)
|
||||||
|
reg = (reg & 0x0f) | (val << 4);
|
||||||
|
else
|
||||||
|
reg = (reg & 0xf0) | val;
|
||||||
|
w83627ehf_write_value(client, W83627EHF_REG_TOLERANCE[nr], reg);
|
||||||
|
mutex_unlock(&data->update_lock);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct sensor_device_attribute sda_pwm[] = {
|
||||||
|
SENSOR_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0),
|
||||||
|
SENSOR_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1),
|
||||||
|
SENSOR_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2),
|
||||||
|
SENSOR_ATTR(pwm4, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 3),
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct sensor_device_attribute sda_pwm_mode[] = {
|
||||||
|
SENSOR_ATTR(pwm1_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
|
||||||
|
store_pwm_mode, 0),
|
||||||
|
SENSOR_ATTR(pwm2_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
|
||||||
|
store_pwm_mode, 1),
|
||||||
|
SENSOR_ATTR(pwm3_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
|
||||||
|
store_pwm_mode, 2),
|
||||||
|
SENSOR_ATTR(pwm4_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
|
||||||
|
store_pwm_mode, 3),
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct sensor_device_attribute sda_pwm_enable[] = {
|
||||||
|
SENSOR_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
|
||||||
|
store_pwm_enable, 0),
|
||||||
|
SENSOR_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
|
||||||
|
store_pwm_enable, 1),
|
||||||
|
SENSOR_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
|
||||||
|
store_pwm_enable, 2),
|
||||||
|
SENSOR_ATTR(pwm4_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
|
||||||
|
store_pwm_enable, 3),
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct sensor_device_attribute sda_target_temp[] = {
|
||||||
|
SENSOR_ATTR(pwm1_target, S_IWUSR | S_IRUGO, show_target_temp,
|
||||||
|
store_target_temp, 0),
|
||||||
|
SENSOR_ATTR(pwm2_target, S_IWUSR | S_IRUGO, show_target_temp,
|
||||||
|
store_target_temp, 1),
|
||||||
|
SENSOR_ATTR(pwm3_target, S_IWUSR | S_IRUGO, show_target_temp,
|
||||||
|
store_target_temp, 2),
|
||||||
|
SENSOR_ATTR(pwm4_target, S_IWUSR | S_IRUGO, show_target_temp,
|
||||||
|
store_target_temp, 3),
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct sensor_device_attribute sda_tolerance[] = {
|
||||||
|
SENSOR_ATTR(pwm1_tolerance, S_IWUSR | S_IRUGO, show_tolerance,
|
||||||
|
store_tolerance, 0),
|
||||||
|
SENSOR_ATTR(pwm2_tolerance, S_IWUSR | S_IRUGO, show_tolerance,
|
||||||
|
store_tolerance, 1),
|
||||||
|
SENSOR_ATTR(pwm3_tolerance, S_IWUSR | S_IRUGO, show_tolerance,
|
||||||
|
store_tolerance, 2),
|
||||||
|
SENSOR_ATTR(pwm4_tolerance, S_IWUSR | S_IRUGO, show_tolerance,
|
||||||
|
store_tolerance, 3),
|
||||||
|
};
|
||||||
|
|
||||||
|
static void device_create_file_pwm(struct device *dev, int i)
|
||||||
|
{
|
||||||
|
device_create_file(dev, &sda_pwm[i].dev_attr);
|
||||||
|
device_create_file(dev, &sda_pwm_mode[i].dev_attr);
|
||||||
|
device_create_file(dev, &sda_pwm_enable[i].dev_attr);
|
||||||
|
device_create_file(dev, &sda_target_temp[i].dev_attr);
|
||||||
|
device_create_file(dev, &sda_tolerance[i].dev_attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Smart Fan registers */
|
||||||
|
|
||||||
|
#define fan_functions(reg, REG) \
|
||||||
|
static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \
|
||||||
|
char *buf) \
|
||||||
|
{ \
|
||||||
|
struct w83627ehf_data *data = w83627ehf_update_device(dev); \
|
||||||
|
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \
|
||||||
|
int nr = sensor_attr->index; \
|
||||||
|
return sprintf(buf, "%d\n", data->reg[nr]); \
|
||||||
|
}\
|
||||||
|
static ssize_t \
|
||||||
|
store_##reg(struct device *dev, struct device_attribute *attr, \
|
||||||
|
const char *buf, size_t count) \
|
||||||
|
{\
|
||||||
|
struct i2c_client *client = to_i2c_client(dev); \
|
||||||
|
struct w83627ehf_data *data = i2c_get_clientdata(client); \
|
||||||
|
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \
|
||||||
|
int nr = sensor_attr->index; \
|
||||||
|
u32 val = SENSORS_LIMIT(simple_strtoul(buf, NULL, 10), 1, 255); \
|
||||||
|
mutex_lock(&data->update_lock); \
|
||||||
|
data->reg[nr] = val; \
|
||||||
|
w83627ehf_write_value(client, W83627EHF_REG_##REG[nr], val); \
|
||||||
|
mutex_unlock(&data->update_lock); \
|
||||||
|
return count; \
|
||||||
|
}
|
||||||
|
|
||||||
|
fan_functions(fan_min_output, FAN_MIN_OUTPUT)
|
||||||
|
|
||||||
|
#define fan_time_functions(reg, REG) \
|
||||||
|
static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \
|
||||||
|
char *buf) \
|
||||||
|
{ \
|
||||||
|
struct w83627ehf_data *data = w83627ehf_update_device(dev); \
|
||||||
|
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \
|
||||||
|
int nr = sensor_attr->index; \
|
||||||
|
return sprintf(buf, "%d\n", \
|
||||||
|
step_time_from_reg(data->reg[nr], data->pwm_mode[nr])); \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
static ssize_t \
|
||||||
|
store_##reg(struct device *dev, struct device_attribute *attr, \
|
||||||
|
const char *buf, size_t count) \
|
||||||
|
{ \
|
||||||
|
struct i2c_client *client = to_i2c_client(dev); \
|
||||||
|
struct w83627ehf_data *data = i2c_get_clientdata(client); \
|
||||||
|
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \
|
||||||
|
int nr = sensor_attr->index; \
|
||||||
|
u8 val = step_time_to_reg(simple_strtoul(buf, NULL, 10), \
|
||||||
|
data->pwm_mode[nr]); \
|
||||||
|
mutex_lock(&data->update_lock); \
|
||||||
|
data->reg[nr] = val; \
|
||||||
|
w83627ehf_write_value(client, W83627EHF_REG_##REG[nr], val); \
|
||||||
|
mutex_unlock(&data->update_lock); \
|
||||||
|
return count; \
|
||||||
|
} \
|
||||||
|
|
||||||
|
fan_time_functions(fan_stop_time, FAN_STOP_TIME)
|
||||||
|
|
||||||
|
|
||||||
|
static struct sensor_device_attribute sda_sf3_arrays_fan4[] = {
|
||||||
|
SENSOR_ATTR(pwm4_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
|
||||||
|
store_fan_stop_time, 3),
|
||||||
|
SENSOR_ATTR(pwm4_min_output, S_IWUSR | S_IRUGO, show_fan_min_output,
|
||||||
|
store_fan_min_output, 3),
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct sensor_device_attribute sda_sf3_arrays[] = {
|
||||||
|
SENSOR_ATTR(pwm1_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
|
||||||
|
store_fan_stop_time, 0),
|
||||||
|
SENSOR_ATTR(pwm2_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
|
||||||
|
store_fan_stop_time, 1),
|
||||||
|
SENSOR_ATTR(pwm3_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
|
||||||
|
store_fan_stop_time, 2),
|
||||||
|
SENSOR_ATTR(pwm1_min_output, S_IWUSR | S_IRUGO, show_fan_min_output,
|
||||||
|
store_fan_min_output, 0),
|
||||||
|
SENSOR_ATTR(pwm2_min_output, S_IWUSR | S_IRUGO, show_fan_min_output,
|
||||||
|
store_fan_min_output, 1),
|
||||||
|
SENSOR_ATTR(pwm3_min_output, S_IWUSR | S_IRUGO, show_fan_min_output,
|
||||||
|
store_fan_min_output, 2),
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Driver and client management
|
* Driver and client management
|
||||||
*/
|
*/
|
||||||
|
@ -810,6 +1160,7 @@ static int w83627ehf_detect(struct i2c_adapter *adapter)
|
||||||
struct i2c_client *client;
|
struct i2c_client *client;
|
||||||
struct w83627ehf_data *data;
|
struct w83627ehf_data *data;
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
|
u8 fan4pin, fan5pin;
|
||||||
int i, err = 0;
|
int i, err = 0;
|
||||||
|
|
||||||
if (!request_region(address + REGION_OFFSET, REGION_LENGTH,
|
if (!request_region(address + REGION_OFFSET, REGION_LENGTH,
|
||||||
|
@ -848,13 +1199,21 @@ static int w83627ehf_detect(struct i2c_adapter *adapter)
|
||||||
data->fan_min[i] = w83627ehf_read_value(client,
|
data->fan_min[i] = w83627ehf_read_value(client,
|
||||||
W83627EHF_REG_FAN_MIN[i]);
|
W83627EHF_REG_FAN_MIN[i]);
|
||||||
|
|
||||||
|
/* fan4 and fan5 share some pins with the GPIO and serial flash */
|
||||||
|
|
||||||
|
superio_enter();
|
||||||
|
fan5pin = superio_inb(0x24) & 0x2;
|
||||||
|
fan4pin = superio_inb(0x29) & 0x6;
|
||||||
|
superio_exit();
|
||||||
|
|
||||||
/* It looks like fan4 and fan5 pins can be alternatively used
|
/* It looks like fan4 and fan5 pins can be alternatively used
|
||||||
as fan on/off switches */
|
as fan on/off switches */
|
||||||
|
|
||||||
data->has_fan = 0x07; /* fan1, fan2 and fan3 */
|
data->has_fan = 0x07; /* fan1, fan2 and fan3 */
|
||||||
i = w83627ehf_read_value(client, W83627EHF_REG_FANDIV1);
|
i = w83627ehf_read_value(client, W83627EHF_REG_FANDIV1);
|
||||||
if (i & (1 << 2))
|
if ((i & (1 << 2)) && (!fan4pin))
|
||||||
data->has_fan |= (1 << 3);
|
data->has_fan |= (1 << 3);
|
||||||
if (i & (1 << 0))
|
if ((i & (1 << 0)) && (!fan5pin))
|
||||||
data->has_fan |= (1 << 4);
|
data->has_fan |= (1 << 4);
|
||||||
|
|
||||||
/* Register sysfs hooks */
|
/* Register sysfs hooks */
|
||||||
|
@ -864,13 +1223,25 @@ static int w83627ehf_detect(struct i2c_adapter *adapter)
|
||||||
goto exit_detach;
|
goto exit_detach;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays); i++)
|
||||||
|
device_create_file(dev, &sda_sf3_arrays[i].dev_attr);
|
||||||
|
|
||||||
|
/* if fan4 is enabled create the sf3 files for it */
|
||||||
|
if (data->has_fan & (1 << 3))
|
||||||
|
for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++)
|
||||||
|
device_create_file(dev, &sda_sf3_arrays_fan4[i].dev_attr);
|
||||||
|
|
||||||
for (i = 0; i < 10; i++)
|
for (i = 0; i < 10; i++)
|
||||||
device_create_file_in(dev, i);
|
device_create_file_in(dev, i);
|
||||||
|
|
||||||
for (i = 0; i < 5; i++) {
|
for (i = 0; i < 5; i++) {
|
||||||
if (data->has_fan & (1 << i))
|
if (data->has_fan & (1 << i)) {
|
||||||
device_create_file_fan(dev, i);
|
device_create_file_fan(dev, i);
|
||||||
|
if (i != 4) /* we have only 4 pwm */
|
||||||
|
device_create_file_pwm(dev, i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(sda_temp); i++)
|
for (i = 0; i < ARRAY_SIZE(sda_temp); i++)
|
||||||
device_create_file(dev, &sda_temp[i].dev_attr);
|
device_create_file(dev, &sda_temp[i].dev_attr);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue