Commit b17d6561 authored by Nikolaus Schulz's avatar Nikolaus Schulz Committed by Guenter Roeck

hwmon: (f75375s) Properly map the F75387 automatic modes to pwm_enable

The F75387 supports automatic fan control using either PWM duty cycle or
RPM speed values.  Make the driver detect the latter mode, and expose the
different modes in sysfs as per pwm_enable, so that the user can switch
between them.

The interpretation of the pwm_enable attribute for the F75387 is adjusted
to be a superset of those values used for similar Fintek chips which do
not support automatic duty mode, with 2 mapping to automatic speed mode,
and moving automatic duty mode to the new value 4.

Toggling the duty mode via pwm_enable is currently denied for the F75387,
as the chip then simply reinterprets the fan configuration register values
according to the new mode, switching between RPM and PWM units, which
makes this a dangerous operation.

This patch introduces a new pwm mode into the driver. This is necessary
because the new mode (automatic pwm mode, 4) may already be enabled by the
BIOS, and the driver should not break existing functionality. This was seen
on at least one board.
Signed-off-by: default avatarNikolaus Schulz <mail@microschulz.de>
Signed-off-by: default avatarGuenter Roeck <guenter.roeck@ericsson.com>
parent edeea102
...@@ -264,6 +264,21 @@ static inline u16 rpm_to_reg(int rpm) ...@@ -264,6 +264,21 @@ static inline u16 rpm_to_reg(int rpm)
return 1500000 / rpm; return 1500000 / rpm;
} }
static bool duty_mode_enabled(u8 pwm_enable)
{
switch (pwm_enable) {
case 0: /* Manual, duty mode (full speed) */
case 1: /* Manual, duty mode */
case 4: /* Auto, duty mode */
return true;
case 2: /* Auto, speed mode */
case 3: /* Manual, speed mode */
return false;
default:
BUG();
}
}
static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr, static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count) const char *buf, size_t count)
{ {
...@@ -337,11 +352,15 @@ static int set_pwm_enable_direct(struct i2c_client *client, int nr, int val) ...@@ -337,11 +352,15 @@ static int set_pwm_enable_direct(struct i2c_client *client, int nr, int val)
struct f75375_data *data = i2c_get_clientdata(client); struct f75375_data *data = i2c_get_clientdata(client);
u8 fanmode; u8 fanmode;
if (val < 0 || val > 3) if (val < 0 || val > 4)
return -EINVAL; return -EINVAL;
fanmode = f75375_read8(client, F75375_REG_FAN_TIMER); fanmode = f75375_read8(client, F75375_REG_FAN_TIMER);
if (data->kind == f75387) { if (data->kind == f75387) {
/* For now, deny dangerous toggling of duty mode */
if (duty_mode_enabled(data->pwm_enable[nr]) !=
duty_mode_enabled(val))
return -EOPNOTSUPP;
/* clear each fanX_mode bit before setting them properly */ /* clear each fanX_mode bit before setting them properly */
fanmode &= ~(1 << F75387_FAN_DUTY_MODE(nr)); fanmode &= ~(1 << F75387_FAN_DUTY_MODE(nr));
fanmode &= ~(1 << F75387_FAN_MANU_MODE(nr)); fanmode &= ~(1 << F75387_FAN_MANU_MODE(nr));
...@@ -355,12 +374,14 @@ static int set_pwm_enable_direct(struct i2c_client *client, int nr, int val) ...@@ -355,12 +374,14 @@ static int set_pwm_enable_direct(struct i2c_client *client, int nr, int val)
fanmode |= (1 << F75387_FAN_MANU_MODE(nr)); fanmode |= (1 << F75387_FAN_MANU_MODE(nr));
fanmode |= (1 << F75387_FAN_DUTY_MODE(nr)); fanmode |= (1 << F75387_FAN_DUTY_MODE(nr));
break; break;
case 2: /* AUTOMATIC*/ case 2: /* Automatic, speed mode */
fanmode |= (1 << F75387_FAN_DUTY_MODE(nr));
break; break;
case 3: /* fan speed */ case 3: /* fan speed */
fanmode |= (1 << F75387_FAN_MANU_MODE(nr)); fanmode |= (1 << F75387_FAN_MANU_MODE(nr));
break; break;
case 4: /* Automatic, pwm */
fanmode |= (1 << F75387_FAN_DUTY_MODE(nr));
break;
} }
} else { } else {
/* clear each fanX_mode bit before setting them properly */ /* clear each fanX_mode bit before setting them properly */
...@@ -378,6 +399,8 @@ static int set_pwm_enable_direct(struct i2c_client *client, int nr, int val) ...@@ -378,6 +399,8 @@ static int set_pwm_enable_direct(struct i2c_client *client, int nr, int val)
break; break;
case 3: /* fan speed */ case 3: /* fan speed */
break; break;
case 4: /* Automatic pwm */
return -EINVAL;
} }
} }
...@@ -735,14 +758,17 @@ static void f75375_init(struct i2c_client *client, struct f75375_data *data, ...@@ -735,14 +758,17 @@ static void f75375_init(struct i2c_client *client, struct f75375_data *data,
manu = ((mode >> F75387_FAN_MANU_MODE(nr)) & 1); manu = ((mode >> F75387_FAN_MANU_MODE(nr)) & 1);
duty = ((mode >> F75387_FAN_DUTY_MODE(nr)) & 1); duty = ((mode >> F75387_FAN_DUTY_MODE(nr)) & 1);
if (manu && duty) if (!manu && duty)
/* speed */ /* auto, pwm */
data->pwm_enable[nr] = 4;
else if (manu && !duty)
/* manual, speed */
data->pwm_enable[nr] = 3; data->pwm_enable[nr] = 3;
else if (!manu && duty) else if (!manu && !duty)
/* automatic */ /* automatic, speed */
data->pwm_enable[nr] = 2; data->pwm_enable[nr] = 2;
else else
/* manual */ /* manual, pwm */
data->pwm_enable[nr] = 1; data->pwm_enable[nr] = 1;
} else { } else {
if (!(conf & (1 << F75375_FAN_CTRL_LINEAR(nr)))) if (!(conf & (1 << F75375_FAN_CTRL_LINEAR(nr))))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment