lkml.org 
[lkml]   [2017]   [Jul]   [10]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[RFC PATCH 3/4] pmbus: Allow dynamic fan coefficient values
    Date
    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.

    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;

    --
    2.11.0
    \
     
     \ /
      Last update: 2017-07-10 15:58    [W:6.714 / U:0.068 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site