Skip to content

Commit

Permalink
hwmon/f71805f: Add manual fan speed control
Browse files Browse the repository at this point in the history
pwmN files are writable only in manual fan speed control mode.
In automatic fan speed control modes, they are read-only and
report the duty cycle chosen by the chip.

Signed-off-by: Jean Delvare <khali@linux-fr.org>
  • Loading branch information
Jean Delvare authored and Jean Delvare committed Dec 12, 2006
1 parent 6b14a54 commit 95e3531
Showing 1 changed file with 144 additions and 1 deletion.
145 changes: 144 additions & 1 deletion drivers/hwmon/f71805f.c
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ superio_exit(int base)
#define F71805F_REG_FAN(nr) (0x20 + 2 * (nr))
#define F71805F_REG_FAN_LOW(nr) (0x28 + 2 * (nr))
#define F71805F_REG_FAN_CTRL(nr) (0x60 + 16 * (nr))
#define F71805F_REG_PWM_DUTY(nr) (0x6B + 16 * (nr))
/* temp nr from 0 to 2 (8-bit values) */
#define F71805F_REG_TEMP(nr) (0x1B + (nr))
#define F71805F_REG_TEMP_HIGH(nr) (0x54 + 2 * (nr))
Expand All @@ -124,6 +125,10 @@ superio_exit(int base)

/* individual register bits */
#define FAN_CTRL_SKIP 0x80
#define FAN_CTRL_MODE_MASK 0x03
#define FAN_CTRL_MODE_SPEED 0x00
#define FAN_CTRL_MODE_TEMPERATURE 0x01
#define FAN_CTRL_MODE_MANUAL 0x02

/*
* Data structures and manipulation thereof
Expand All @@ -147,6 +152,7 @@ struct f71805f_data {
u16 fan[3];
u16 fan_low[3];
u8 fan_ctrl[3];
u8 pwm[3];
u8 temp[3];
u8 temp_high[3];
u8 temp_hyst[3];
Expand Down Expand Up @@ -312,6 +318,10 @@ static struct f71805f_data *f71805f_update_device(struct device *dev)
continue;
data->fan[nr] = f71805f_read16(data,
F71805F_REG_FAN(nr));
data->fan_ctrl[nr] = f71805f_read8(data,
F71805F_REG_FAN_CTRL(nr));
data->pwm[nr] = f71805f_read8(data,
F71805F_REG_PWM_DUTY(nr));
}
for (nr = 0; nr < 3; nr++) {
data->temp[nr] = f71805f_read8(data,
Expand Down Expand Up @@ -484,6 +494,104 @@ static ssize_t set_fan_min(struct device *dev, struct device_attribute
return count;
}

static ssize_t show_pwm(struct device *dev, struct device_attribute *devattr,
char *buf)
{
struct f71805f_data *data = f71805f_update_device(dev);
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
int nr = attr->index;

return sprintf(buf, "%d\n", (int)data->pwm[nr]);
}

static ssize_t show_pwm_enable(struct device *dev, struct device_attribute
*devattr, char *buf)
{
struct f71805f_data *data = f71805f_update_device(dev);
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
int nr = attr->index;
int mode;

switch (data->fan_ctrl[nr] & FAN_CTRL_MODE_MASK) {
case FAN_CTRL_MODE_SPEED:
mode = 3;
break;
case FAN_CTRL_MODE_TEMPERATURE:
mode = 2;
break;
default: /* MANUAL */
mode = 1;
}

return sprintf(buf, "%d\n", mode);
}

static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr,
const char *buf, size_t count)
{
struct f71805f_data *data = dev_get_drvdata(dev);
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
int nr = attr->index;
unsigned long val = simple_strtoul(buf, NULL, 10);

if (val > 255)
return -EINVAL;

mutex_lock(&data->update_lock);
data->pwm[nr] = val;
f71805f_write8(data, F71805F_REG_PWM_DUTY(nr), data->pwm[nr]);
mutex_unlock(&data->update_lock);

return count;
}

static struct attribute *f71805f_attr_pwm[];

static ssize_t set_pwm_enable(struct device *dev, struct device_attribute
*devattr, const char *buf, size_t count)
{
struct f71805f_data *data = dev_get_drvdata(dev);
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
int nr = attr->index;
unsigned long val = simple_strtoul(buf, NULL, 10);
u8 reg;

if (val < 1 || val > 3)
return -EINVAL;

if (val > 1) { /* Automatic mode, user can't set PWM value */
if (sysfs_chmod_file(&dev->kobj, f71805f_attr_pwm[nr],
S_IRUGO))
dev_dbg(dev, "chmod -w pwm%d failed\n", nr + 1);
}

mutex_lock(&data->update_lock);
reg = f71805f_read8(data, F71805F_REG_FAN_CTRL(nr))
& ~FAN_CTRL_MODE_MASK;
switch (val) {
case 1:
reg |= FAN_CTRL_MODE_MANUAL;
break;
case 2:
reg |= FAN_CTRL_MODE_TEMPERATURE;
break;
case 3:
reg |= FAN_CTRL_MODE_SPEED;
break;
}
data->fan_ctrl[nr] = reg;
f71805f_write8(data, F71805F_REG_FAN_CTRL(nr), reg);
mutex_unlock(&data->update_lock);

if (val == 1) { /* Manual mode, user can set PWM value */
if (sysfs_chmod_file(&dev->kobj, f71805f_attr_pwm[nr],
S_IRUGO | S_IWUSR))
dev_dbg(dev, "chmod +w pwm%d failed\n", nr + 1);
}

return count;
}

static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
char *buf)
{
Expand Down Expand Up @@ -672,6 +780,18 @@ static SENSOR_DEVICE_ATTR(temp3_max_hyst, S_IRUGO | S_IWUSR,
show_temp_hyst, set_temp_hyst, 2);
static SENSOR_DEVICE_ATTR(temp3_type, S_IRUGO, show_temp_type, NULL, 2);

/* pwm (value) files are created read-only, write permission is
then added or removed dynamically as needed */
static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO, show_pwm, set_pwm, 0);
static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR,
show_pwm_enable, set_pwm_enable, 0);
static SENSOR_DEVICE_ATTR(pwm2, S_IRUGO, show_pwm, set_pwm, 1);
static SENSOR_DEVICE_ATTR(pwm2_enable, S_IRUGO | S_IWUSR,
show_pwm_enable, set_pwm_enable, 1);
static SENSOR_DEVICE_ATTR(pwm3, S_IRUGO, show_pwm, set_pwm, 2);
static SENSOR_DEVICE_ATTR(pwm3_enable, S_IRUGO | S_IWUSR,
show_pwm_enable, set_pwm_enable, 2);

static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0);
static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1);
static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2);
Expand Down Expand Up @@ -759,21 +879,27 @@ static const struct attribute_group f71805f_group = {
.attrs = f71805f_attributes,
};

static struct attribute *f71805f_attributes_fan[3][4] = {
static struct attribute *f71805f_attributes_fan[3][6] = {
{
&sensor_dev_attr_fan1_input.dev_attr.attr,
&sensor_dev_attr_fan1_min.dev_attr.attr,
&sensor_dev_attr_fan1_alarm.dev_attr.attr,
&sensor_dev_attr_pwm1.dev_attr.attr,
&sensor_dev_attr_pwm1_enable.dev_attr.attr,
NULL
}, {
&sensor_dev_attr_fan2_input.dev_attr.attr,
&sensor_dev_attr_fan2_min.dev_attr.attr,
&sensor_dev_attr_fan2_alarm.dev_attr.attr,
&sensor_dev_attr_pwm2.dev_attr.attr,
&sensor_dev_attr_pwm2_enable.dev_attr.attr,
NULL
}, {
&sensor_dev_attr_fan3_input.dev_attr.attr,
&sensor_dev_attr_fan3_min.dev_attr.attr,
&sensor_dev_attr_fan3_alarm.dev_attr.attr,
&sensor_dev_attr_pwm3.dev_attr.attr,
&sensor_dev_attr_pwm3_enable.dev_attr.attr,
NULL
}
};
Expand All @@ -784,6 +910,13 @@ static const struct attribute_group f71805f_group_fan[3] = {
{ .attrs = f71805f_attributes_fan[2] },
};

/* We also need an indexed access to pwmN files to toggle writability */
static struct attribute *f71805f_attr_pwm[] = {
&sensor_dev_attr_pwm1.dev_attr.attr,
&sensor_dev_attr_pwm2.dev_attr.attr,
&sensor_dev_attr_pwm3.dev_attr.attr,
};

/*
* Device registration and initialization
*/
Expand Down Expand Up @@ -840,6 +973,16 @@ static int __devinit f71805f_probe(struct platform_device *pdev)
if ((err = sysfs_create_group(&pdev->dev.kobj,
&f71805f_group_fan[i])))
goto exit_remove_files;
/* If PWM is in manual mode, add write permission */
if (data->fan_ctrl[i] & FAN_CTRL_MODE_MANUAL) {
if ((err = sysfs_chmod_file(&pdev->dev.kobj,
f71805f_attr_pwm[i],
S_IRUGO | S_IWUSR))) {
dev_err(&pdev->dev, "chmod +w pwm%d failed\n",
i + 1);
goto exit_remove_files;
}
}
}

data->class_dev = hwmon_device_register(&pdev->dev);
Expand Down

0 comments on commit 95e3531

Please sign in to comment.