Re: [RFC PATCH 3/4] pmbus: Allow dynamic fan coefficient values

Guenter Roeck
2017-07-12T03:20:11Z

On 07/11/2017 06:20 PM, Andrew Jeffery wrote:
> On Tue, 2017-07-11 at 06:31 -0700, Guenter Roeck wrote:
>> On 07/10/2017 06:56 AM, Andrew Jeffery wrote:
>>> Some PMBus chips, such as the MAX31785, use different coefficients for
>>> FAN_COMMAND_[1-4] depending on whether the fan is in PWM (percent duty)
>>> or RPM mode. Add a callback to allow the driver to provide the
>>> applicable coefficients to avoid imposing on devices that don't have
>>> this requirement.
>>>
>>
>> Why not just introduce another class, such as PSC_PWM ?
>
> I considered this up front however I wasn't sure where the PMBus sensor
> classes were modelled from. The PMBus spec generally doesn't discuss

The classes are modeled from my brain, so we can do whatever we want with them.

> PMW beyond the concept of fans, and given PSC_FAN already existed I had
> a vague feeling that introducing PSC_PWM might not be the way to go. It
> also means that PSC_FAN is implicitly RPM in some circumstances, or
> both RPM and PWM in others, and wasn't previously contrasted against
> PWM as PWM-specific configuration wasn't an option.
>
> However if it's reasonable it should lead to a more straight forward
> patch. I'll rework and resend if it falls out nicely.
>
Please do.

Thanks,
Guenter

> Thanks,
>
> Andrew
>
>>
>>>>> Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
>>> ---
>>> drivers/hwmon/pmbus/pmbus.h | 18 +++++--
>>> drivers/hwmon/pmbus/pmbus_core.c | 112 ++++++++++++++++++++++++++++++++-------
>>> 2 files changed, 108 insertions(+), 22 deletions(-)
>>>
>>> diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h
>>> index 927eabc1b273..338ecc8b25a4 100644
>>> --- a/drivers/hwmon/pmbus/pmbus.h
>>> +++ b/drivers/hwmon/pmbus/pmbus.h
>>> @@ -345,6 +345,12 @@ enum pmbus_sensor_classes {
>>> enum pmbus_data_format { linear = 0, direct, vid };
>>> enum vrm_version { vr11 = 0, vr12 };
>>>
>>> +struct pmbus_coeffs {
>>>>> + int m; /* mantissa for direct data format */
>>>>> + int b; /* offset */
>>>>> + int R; /* exponent */
>>> +};
>>> +
>>> struct pmbus_driver_info {
>>>>>>> int pages; /* Total number of pages */
>>>>> enum pmbus_data_format format[PSC_NUM_CLASSES];
>>> @@ -353,9 +359,7 @@ struct pmbus_driver_info {
>>>>> * Support one set of coefficients for each sensor type
>>>>> * Used for chips providing data in direct mode.
>>>>> */
>>>>>>> - int m[PSC_NUM_CLASSES]; /* mantissa for direct data format */
>>>>>>> - int b[PSC_NUM_CLASSES]; /* offset */
>>>>>>> - int R[PSC_NUM_CLASSES]; /* exponent */
>>>>> + struct pmbus_coeffs coeffs[PSC_NUM_CLASSES];
>>>
>>>>>>> u32 func[PMBUS_PAGES]; /* Functionality, per page */
>>>>> /*
>>> @@ -382,6 +386,14 @@ struct pmbus_driver_info {
>>>>> int (*identify)(struct i2c_client *client,
>>>>> struct pmbus_driver_info *info);
>>>
>>>>> + /*
>>>>> + * If a fan's coefficents change over time (e.g. between RPM and PWM
>>>>> + * mode), then the driver can provide a function for retrieving the
>>>>> + * currently applicable coefficients.
>>>>> + */
>>>>> + const struct pmbus_coeffs *(*get_fan_coeffs)(
>>>>> + const struct pmbus_driver_info *info, int index,
>>>>> + enum pmbus_fan_mode mode, int command);
>>>>> /* Allow the driver to interpret the fan command value */
>>>>> int (*get_pwm_mode)(int id, u8 fan_config, u16 fan_command);
>>>>> int (*set_pwm_mode)(int id, long mode, u8 *fan_config,
>>> diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
>>> index 3b0a55bbbd2c..4ff6a1fd5cce 100644
>>> --- a/drivers/hwmon/pmbus/pmbus_core.c
>>> +++ b/drivers/hwmon/pmbus/pmbus_core.c
>>> @@ -58,10 +58,11 @@
>>> struct pmbus_sensor {
>>>>> struct pmbus_sensor *next;
>>>>>>> char name[PMBUS_NAME_SIZE]; /* sysfs sensor name */
>>>>> - struct device_attribute attribute;
>>>>> + struct sensor_device_attribute attribute;
>>>>>>> u8 page; /* page number */
>>>>>>> u16 reg; /* register */
>>>>>>> enum pmbus_sensor_classes class; /* sensor class */
>>>>> + const struct pmbus_coeffs *coeffs;
>>>>>>> bool update; /* runtime sensor update needed */
>>>>>>> int data; /* Sensor data.
>>>>> Negative if there was a read error */
>>> @@ -89,6 +90,7 @@ struct pmbus_fan_ctrl {
>>>>> u8 id;
>>>>> u8 config;
>>>>> u16 command;
>>>>> + const struct pmbus_coeffs *coeffs;
>>> };
>>> #define to_pmbus_fan_ctrl_attr(_attr) \
>>>>> container_of(_attr, struct pmbus_fan_ctrl_attr, attribute)
>>> @@ -511,9 +513,15 @@ static long pmbus_reg2data_direct(struct pmbus_data *data,
>>>>> long val = (s16) sensor->data;
>>>>> long m, b, R;
>>>
>>>>> - m = data->info->m[sensor->class];
>>>>> - b = data->info->b[sensor->class];
>>>>> - R = data->info->R[sensor->class];
>>>>> + if (sensor->coeffs) {
>>>>> + m = sensor->coeffs->m;
>>>>> + b = sensor->coeffs->b;
>>>>> + R = sensor->coeffs->R;
>>>>> + } else {
>>>>> + m = data->info->coeffs[sensor->class].m;
>>>>> + b = data->info->coeffs[sensor->class].b;
>>>>> + R = data->info->coeffs[sensor->class].R;
>>>>> + }
>>>
>>>>> if (m == 0)
>>>>> return 0;
>>> @@ -663,9 +671,15 @@ static u16 pmbus_data2reg_direct(struct pmbus_data *data,
>>> {
>>>>> long m, b, R;
>>>
>>>>> - m = data->info->m[sensor->class];
>>>>> - b = data->info->b[sensor->class];
>>>>> - R = data->info->R[sensor->class];
>>>>> + if (sensor->coeffs) {
>>>>> + m = sensor->coeffs->m;
>>>>> + b = sensor->coeffs->b;
>>>>> + R = sensor->coeffs->R;
>>>>> + } else {
>>>>> + m = data->info->coeffs[sensor->class].m;
>>>>> + b = data->info->coeffs[sensor->class].b;
>>>>> + R = data->info->coeffs[sensor->class].R;
>>>>> + }
>>>
>>>>> /* Power is in uW. Adjust R and b. */
>>>>> if (sensor->class == PSC_POWER) {
>>> @@ -796,7 +810,9 @@ static ssize_t pmbus_show_sensor(struct device *dev,
>>>>> struct device_attribute *devattr, char *buf)
>>> {
>>>>> struct pmbus_data *data = pmbus_update_device(dev);
>>>>> - struct pmbus_sensor *sensor = to_pmbus_sensor(devattr);
>>>>> + struct pmbus_sensor *sensor;
>>> +
>>>>> + sensor = to_pmbus_sensor(to_sensor_dev_attr(devattr));
>>>
>>>>> if (sensor->data < 0)
>>>>> return sensor->data;
>>> @@ -810,12 +826,14 @@ static ssize_t pmbus_set_sensor(struct device *dev,
>>> {
>>>>> struct i2c_client *client = to_i2c_client(dev->parent);
>>>>> struct pmbus_data *data = i2c_get_clientdata(client);
>>>>> - struct pmbus_sensor *sensor = to_pmbus_sensor(devattr);
>>>>> + struct pmbus_sensor *sensor;
>>>>> ssize_t rv = count;
>>>>> long val = 0;
>>>>> int ret;
>>>>> u16 regval;
>>>
>>>>> + sensor = to_pmbus_sensor(to_sensor_dev_attr(devattr));
>>> +
>>>>> if (kstrtol(buf, 10, &val) < 0)
>>>>> return -EINVAL;
>>>
>>> @@ -856,6 +874,7 @@ static ssize_t pmbus_show_fan_command(struct device *dev,
>>>>> }
>>>
>>>>> sensor.class = PSC_FAN;
>>>>> + sensor.coeffs = fan->coeffs;
>>>>> if (mode == percent)
>>>>> sensor.data = fan->command * 255 / 100;
>>>>> else
>>> @@ -882,6 +901,29 @@ static ssize_t pmbus_show_pwm(struct device *dev,
>>>>> buf);
>>> }
>>>
>>> +static int pmbus_update_fan_coeffs(struct pmbus_data *data,
>>>>> + struct pmbus_fan_ctrl *fan,
>>>>> + enum pmbus_fan_mode mode)
>>> +{
>>>>> + const struct pmbus_driver_info *info = data->info;
>>>>> + struct pmbus_sensor *curr = data->sensors;
>>> +
>>>>> + fan->coeffs = info->get_fan_coeffs(info, fan->index, mode,
>>>>> + PMBUS_FAN_COMMAND_1 + fan->id);
>>> +
>>>>> + while (curr) {
>>>>> + if (curr->class == PSC_FAN &&
>>>>> + curr->attribute.index == fan->index) {
>>>>> + curr->coeffs = info->get_fan_coeffs(info, fan->index,
>>>>> + mode, curr->reg);
>>>>> + }
>>> +
>>>>> + curr = curr->next;
>>>>> + }
>>> +
>>>>> + return 0;
>>> +}
>>> +
>>> static ssize_t pmbus_set_fan_command(struct device *dev,
>>>>> enum pmbus_fan_mode mode,
>>>>> struct pmbus_fan_ctrl *fan,
>>> @@ -889,6 +931,7 @@ static ssize_t pmbus_set_fan_command(struct device *dev,
>>> {
>>>>> struct i2c_client *client = to_i2c_client(dev->parent);
>>>>> struct pmbus_data *data = i2c_get_clientdata(client);
>>>>> + const struct pmbus_driver_info *info = data->info;
>>>>> int config_addr, command_addr;
>>>>> struct pmbus_sensor sensor;
>>>>> ssize_t rv;
>>> @@ -899,7 +942,13 @@ static ssize_t pmbus_set_fan_command(struct device *dev,
>>>
>>>>> mutex_lock(&data->update_lock);
>>>
>>>>> + if (info->format[PSC_FAN] == direct && info->get_fan_coeffs) {
>>>>> + pmbus_update_fan_coeffs(data, fan, mode);
>>>>> + sensor.coeffs = fan->coeffs;
>>>>> + }
>>> +
>>>>> sensor.class = PSC_FAN;
>>>>> + sensor.attribute.index = fan->index;
>>>
>>>>> val = pmbus_data2reg(data, &sensor, val);
>>>
>>> @@ -968,6 +1017,8 @@ static ssize_t pmbus_show_pwm_enable(struct device *dev,
>>>>> struct pmbus_sensor sensor = {
>>>>> .class = PSC_FAN,
>>>>> .data = fan->command,
>>>>> + .attribute.index = fan->index,
>>>>> + .coeffs = fan->coeffs,
>>>>> };
>>>>> long command;
>>>
>>> @@ -992,6 +1043,7 @@ static ssize_t pmbus_set_pwm_enable(struct device *dev,
>>>>> struct pmbus_fan_ctrl *fan = pwm_enable_to_pmbus_fan_ctrl(da);
>>>>> struct i2c_client *client = to_i2c_client(dev->parent);
>>>>> struct pmbus_data *data = i2c_get_clientdata(client);
>>>>> + const struct pmbus_driver_info *info = data->info;
>>>>> int config_addr, command_addr;
>>>>> struct pmbus_sensor sensor;
>>>>> ssize_t rv = count;
>>> @@ -1003,15 +1055,21 @@ static ssize_t pmbus_set_pwm_enable(struct device *dev,
>>>>> mutex_lock(&data->update_lock);
>>>
>>>>> sensor.class = PSC_FAN;
>>>>> + sensor.attribute.index = fan->index;
>>> +
>>>>> + if (info->format[PSC_FAN] == direct && info->get_fan_coeffs) {
>>>>> + pmbus_update_fan_coeffs(data, fan, percent);
>>>>> + sensor.coeffs = fan->coeffs;
>>>>> + }
>>>
>>>>> config_addr = (fan->id < 2) ? PMBUS_FAN_CONFIG_12 : PMBUS_FAN_CONFIG_34;
>>>>> command_addr = config_addr + 1 + (fan->id & 1);
>>>
>>>>> - if (data->info->set_pwm_mode) {
>>>>> + if (info->set_pwm_mode) {
>>>>> u8 config = PB_FAN_CONFIG_PUT(fan->id, fan->config);
>>>>> u16 command = fan->command;
>>>
>>>>> - rv = data->info->set_pwm_mode(fan->id, mode, &config, &command);
>>>>> + rv = info->set_pwm_mode(fan->id, mode, &config, &command);
>>>>> if (rv < 0)
>>>>> goto done;
>>>
>>> @@ -1138,7 +1196,7 @@ static struct pmbus_sensor *pmbus_add_sensor(struct pmbus_data *data,
>>>>> sensor = devm_kzalloc(data->dev, sizeof(*sensor), GFP_KERNEL);
>>>>> if (!sensor)
>>>>> return NULL;
>>>>> - a = &sensor->attribute;
>>>>> + a = &sensor->attribute.dev_attr;
>>>
>>>>> snprintf(sensor->name, sizeof(sensor->name), "%s%d_%s",
>>>>> name, seq, type);
>>> @@ -1146,9 +1204,9 @@ static struct pmbus_sensor *pmbus_add_sensor(struct pmbus_data *data,
>>>>> sensor->reg = reg;
>>>>> sensor->class = class;
>>>>> sensor->update = update;
>>>>> - pmbus_dev_attr_init(a, sensor->name,
>>>>> + pmbus_attr_init(&sensor->attribute, sensor->name,
>>>>> readonly ? S_IRUGO : S_IRUGO | S_IWUSR,
>>>>> - pmbus_show_sensor, pmbus_set_sensor);
>>>>> + pmbus_show_sensor, pmbus_set_sensor, seq);
>>>
>>>>> if (pmbus_add_attribute(data, &a->attr))
>>>>> return NULL;
>>> @@ -1886,7 +1944,7 @@ static const u32 pmbus_fan_status_flags[] = {
>>> /* Fans */
>>> static int pmbus_add_fan_ctrl(struct i2c_client *client,
>>>>> struct pmbus_data *data, int index, int page, int id,
>>>>> - u8 config)
>>>>> + u8 config, const struct pmbus_coeffs *coeffs)
>>> {
>>>>> struct pmbus_fan_ctrl *fan;
>>>>> int rv;
>>> @@ -1898,6 +1956,7 @@ static int pmbus_add_fan_ctrl(struct i2c_client *client,
>>>>> fan->index = index;
>>>>> fan->page = page;
>>>>> fan->id = id;
>>>>> + fan->coeffs = coeffs;
>>>>> fan->config = config;
>>>
>>>>> rv = _pmbus_read_word_data(client, page,
>>> @@ -1921,6 +1980,8 @@ static int pmbus_add_fan_attributes(struct i2c_client *client,
>>>>> struct pmbus_data *data)
>>> {
>>>>> const struct pmbus_driver_info *info = data->info;
>>>>> + const struct pmbus_coeffs *coeffs = NULL;
>>>>> + enum pmbus_fan_mode mode;
>>>>> int index = 1;
>>>>> int page;
>>>>> int ret;
>>> @@ -1929,6 +1990,7 @@ static int pmbus_add_fan_attributes(struct i2c_client *client,
>>>>> int f;
>>>
>>>>> for (f = 0; f < ARRAY_SIZE(pmbus_fan_registers); f++) {
>>>>> + struct pmbus_sensor *sensor;
>>>>> int regval;
>>>
>>>>> if (!(info->func[page] & pmbus_fan_flags[f]))
>>> @@ -1949,13 +2011,25 @@ static int pmbus_add_fan_attributes(struct i2c_client *client,
>>>>> (!(regval & (PB_FAN_1_INSTALLED >> ((f & 1) * 4)))))
>>>>> continue;
>>>
>>>>> - if (pmbus_add_sensor(data, "fan", "input", index,
>>>>> - page, pmbus_fan_registers[f],
>>>>> - PSC_FAN, true, true) == NULL)
>>>>> + sensor = pmbus_add_sensor(data, "fan", "input", index,
>>>>> + page, pmbus_fan_registers[f],
>>>>> + PSC_FAN, true, true);
>>>>> + if (!sensor)
>>>>> return -ENOMEM;
>>>
>>>>> + /* Add coeffs here as we have access to the fan mode */
>>>>> + if (info->format[PSC_FAN] == direct &&
>>>>> + info->get_fan_coeffs) {
>>>>> + const u16 mask = PB_FAN_1_RPM >> ((f & 1) * 4);
>>> +
>>>>> + mode = (regval & mask) ? rpm : percent;
>>>>> + coeffs = info->get_fan_coeffs(info, index, mode,
>>>>> + pmbus_fan_registers[f]);
>>>>> + sensor->coeffs = coeffs;
>>>>> + }
>>> +
>>>>> ret = pmbus_add_fan_ctrl(client, data, index, page, f,
>>>>> - regval);
>>>>> + regval, coeffs);
>>>>> if (ret < 0)
>>>>> return ret;
>>>
>>>
>>