Re: [v6,2/4] pmbus (max31785): Add fan control

From: Guenter Roeck
Date: Wed Nov 29 2017 - 16:13:16 EST


On Mon, Nov 20, 2017 at 03:12:04PM +1030, Andrew Jeffery wrote:
> The implementation makes use of the new fan control virtual registers
> exposed by the pmbus core. It mixes use of the default implementations
> with some overrides via the read/write handlers to handle FAN_COMMAND_1
> on the MAX31785, whose definition breaks the value range into various
> control bands dependent on RPM or PWM mode.
>
> Signed-off-by: Andrew Jeffery <andrew@xxxxxxxx>

Applied.

Thanks,
Guenter

> ---
> Documentation/hwmon/max31785 | 7 ++-
> drivers/hwmon/pmbus/max31785.c | 138 +++++++++++++++++++++++++++++++++-
> 2 files changed, 144 insertions(+), 1 deletion(-)
>
> diff --git a/Documentation/hwmon/max31785 b/Documentation/hwmon/max31785
> index 45fb6093dec2..7b0a0a8cdb6b 100644
> --- a/Documentation/hwmon/max31785
> +++ b/Documentation/hwmon/max31785
> @@ -32,6 +32,7 @@ Sysfs attributes
> fan[1-4]_alarm Fan alarm.
> fan[1-4]_fault Fan fault.
> fan[1-4]_input Fan RPM.
> +fan[1-4]_target Fan input target
>
> in[1-6]_crit Critical maximum output voltage
> in[1-6]_crit_alarm Output voltage critical high alarm
> @@ -44,6 +45,12 @@ in[1-6]_max_alarm Output voltage high alarm
> in[1-6]_min Minimum output voltage
> in[1-6]_min_alarm Output voltage low alarm
>
> +pwm[1-4] Fan target duty cycle (0..255)
> +pwm[1-4]_enable 0: Full-speed
> + 1: Manual PWM control
> + 2: Automatic PWM (tach-feedback RPM fan-control)
> + 3: Automatic closed-loop (temp-feedback fan-control)
> +
> temp[1-11]_crit Critical high temperature
> temp[1-11]_crit_alarm Chip temperature critical high alarm
> temp[1-11]_input Measured temperature
> diff --git a/drivers/hwmon/pmbus/max31785.c b/drivers/hwmon/pmbus/max31785.c
> index 9313849d5160..8706a696c89a 100644
> --- a/drivers/hwmon/pmbus/max31785.c
> +++ b/drivers/hwmon/pmbus/max31785.c
> @@ -20,8 +20,136 @@ enum max31785_regs {
>
> #define MAX31785_NR_PAGES 23
>
> +static int max31785_get_pwm(struct i2c_client *client, int page)
> +{
> + int rv;
> +
> + rv = pmbus_get_fan_rate_device(client, page, 0, percent);
> + if (rv < 0)
> + return rv;
> + else if (rv >= 0x8000)
> + return 0;
> + else if (rv >= 0x2711)
> + return 0x2710;
> +
> + return rv;
> +}
> +
> +static int max31785_get_pwm_mode(struct i2c_client *client, int page)
> +{
> + int config;
> + int command;
> +
> + config = pmbus_read_byte_data(client, page, PMBUS_FAN_CONFIG_12);
> + if (config < 0)
> + return config;
> +
> + command = pmbus_read_word_data(client, page, PMBUS_FAN_COMMAND_1);
> + if (command < 0)
> + return command;
> +
> + if (config & PB_FAN_1_RPM)
> + return (command >= 0x8000) ? 3 : 2;
> +
> + if (command >= 0x8000)
> + return 3;
> + else if (command >= 0x2711)
> + return 0;
> +
> + return 1;
> +}
> +
> +static int max31785_read_word_data(struct i2c_client *client, int page,
> + int reg)
> +{
> + int rv;
> +
> + switch (reg) {
> + case PMBUS_VIRT_PWM_1:
> + rv = max31785_get_pwm(client, page);
> + break;
> + case PMBUS_VIRT_PWM_ENABLE_1:
> + rv = max31785_get_pwm_mode(client, page);
> + break;
> + default:
> + rv = -ENODATA;
> + break;
> + }
> +
> + return rv;
> +}
> +
> +static inline u32 max31785_scale_pwm(u32 sensor_val)
> +{
> + /*
> + * The datasheet describes the accepted value range for manual PWM as
> + * [0, 0x2710], while the hwmon pwmX sysfs interface accepts values in
> + * [0, 255]. The MAX31785 uses DIRECT mode to scale the FAN_COMMAND
> + * registers and in PWM mode the coefficients are m=1, b=0, R=2. The
> + * important observation here is that 0x2710 == 10000 == 100 * 100.
> + *
> + * R=2 (== 10^2 == 100) accounts for scaling the value provided at the
> + * sysfs interface into the required hardware resolution, but it does
> + * not yet yield a value that we can write to the device (this initial
> + * scaling is handled by pmbus_data2reg()). Multiplying by 100 below
> + * translates the parameter value into the percentage units required by
> + * PMBus, and then we scale back by 255 as required by the hwmon pwmX
> + * interface to yield the percentage value at the appropriate
> + * resolution for hardware.
> + */
> + return (sensor_val * 100) / 255;
> +}
> +
> +static int max31785_pwm_enable(struct i2c_client *client, int page,
> + u16 word)
> +{
> + int config = 0;
> + int rate;
> +
> + switch (word) {
> + case 0:
> + rate = 0x7fff;
> + break;
> + case 1:
> + rate = pmbus_get_fan_rate_cached(client, page, 0, percent);
> + if (rate < 0)
> + return rate;
> + rate = max31785_scale_pwm(rate);
> + break;
> + case 2:
> + config = PB_FAN_1_RPM;
> + rate = pmbus_get_fan_rate_cached(client, page, 0, rpm);
> + if (rate < 0)
> + return rate;
> + break;
> + case 3:
> + rate = 0xffff;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return pmbus_update_fan(client, page, 0, config, PB_FAN_1_RPM, rate);
> +}
> +
> +static int max31785_write_word_data(struct i2c_client *client, int page,
> + int reg, u16 word)
> +{
> + switch (reg) {
> + case PMBUS_VIRT_PWM_1:
> + return pmbus_update_fan(client, page, 0, 0, PB_FAN_1_RPM,
> + max31785_scale_pwm(word));
> + case PMBUS_VIRT_PWM_ENABLE_1:
> + return max31785_pwm_enable(client, page, word);
> + default:
> + break;
> + }
> +
> + return -ENODATA;
> +}
> +
> #define MAX31785_FAN_FUNCS \
> - (PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12)
> + (PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12 | PMBUS_HAVE_PWM12)
>
> #define MAX31785_TEMP_FUNCS \
> (PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP)
> @@ -32,11 +160,19 @@ enum max31785_regs {
> static const struct pmbus_driver_info max31785_info = {
> .pages = MAX31785_NR_PAGES,
>
> + .write_word_data = max31785_write_word_data,
> + .read_word_data = max31785_read_word_data,
> +
> /* RPM */
> .format[PSC_FAN] = direct,
> .m[PSC_FAN] = 1,
> .b[PSC_FAN] = 0,
> .R[PSC_FAN] = 0,
> + /* PWM */
> + .format[PSC_PWM] = direct,
> + .m[PSC_PWM] = 1,
> + .b[PSC_PWM] = 0,
> + .R[PSC_PWM] = 2,
> .func[0] = MAX31785_FAN_FUNCS,
> .func[1] = MAX31785_FAN_FUNCS,
> .func[2] = MAX31785_FAN_FUNCS,