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

Andrew Jeffery andrew at aj.id.au
Mon Jul 10 23:56:17 AEST 2017


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 at 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



More information about the openbmc mailing list