All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v5 0/4] hwmon: (pmbus/ltc2978) Add regulator ops
@ 2022-05-02 11:13 Mårten Lindahl
  2022-05-02 11:13 ` [PATCH v5 1/4] hwmon: (pmbus) Introduce and use write_byte_data callback Mårten Lindahl
                   ` (3 more replies)
  0 siblings, 4 replies; 7+ messages in thread
From: Mårten Lindahl @ 2022-05-02 11:13 UTC (permalink / raw)
  To: Guenter Roeck, Jean Delvare; +Cc: linux-hwmon, kernel, Mårten Lindahl

Hi!

The LTC2978 driver supports a wide range of power regulator chips, but it
has limited functionality for using it in a dynamic regulator framework.
Since standard functions for setting and getting voltage are missing as
pmbus core operations this patchset adds it.

These patches have been tested on an ARTPEC-8 developer board with a group
of LTC2977 power regulators.

Kind regards
Mårten Lindahl

Changes in v2:
 - Add pmbus core _pmbus_write_byte_data to check for driver specific callback
 - Change pmbus_update_byte_data to use _pmbus_read/write_byte_data
 - Change pmbus_regulator_is_enabled to use _pmbus_read_byte_data
 - Export pmbus core functions enable/disable/is_enabled

Changes in v3:
 - Split patch "hwmon: (pmbus/ltc2978) Use driver specific ops if they exist"
   into two patches: (1) pmbus core, (2) ltc2978.
 - Move ltc2978_regulator_get/set_voltage functions to pmbus core.

Changes in v4:
 - Split (and rename) patch "hwmon: (pmbus) Use driver specific ops if they exist"
   into two patches where the first handle _pmbus_write_byte_data, and the
   second handle _pmbus_read_byte_data.
 - Use voltage conversion functions in pmbus_regulator_get/set_voltage.

Changes in v5:
 - Remove PMBUS_VOUT_COMMAND voltage attribute added in v4 and use local
   sensor object for conversion.
 - Try to read MFR_VOUT_MIN and MFR_VOUT_MAX first, or else VOUT_MARGIN_LOW and
   VOUT_MARGIN_HIGH, to get voltage window.

Mårten Lindahl (4):
  hwmon: (pmbus) Introduce and use write_byte_data callback
  hwmon: (pmbus) Use _pmbus_read_byte_data with callback
  hwmon: (pmbus/ltc2978) Add chip specific write_byte_data
  hwmon: (pmbus) Add get_voltage/set_voltage ops

 drivers/hwmon/pmbus/ltc2978.c    |  12 +++
 drivers/hwmon/pmbus/pmbus.h      |   2 +
 drivers/hwmon/pmbus/pmbus_core.c | 133 +++++++++++++++++++++++++------
 3 files changed, 121 insertions(+), 26 deletions(-)

-- 
2.30.2


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v5 1/4] hwmon: (pmbus) Introduce and use write_byte_data callback
  2022-05-02 11:13 [PATCH v5 0/4] hwmon: (pmbus/ltc2978) Add regulator ops Mårten Lindahl
@ 2022-05-02 11:13 ` Mårten Lindahl
  2022-05-02 11:13 ` [PATCH v5 2/4] hwmon: (pmbus) Use _pmbus_read_byte_data with callback Mårten Lindahl
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 7+ messages in thread
From: Mårten Lindahl @ 2022-05-02 11:13 UTC (permalink / raw)
  To: Guenter Roeck, Jean Delvare; +Cc: linux-hwmon, kernel, Mårten Lindahl

Some of the pmbus core functions uses pmbus_write_byte_data, which does
not support driver callbacks for chip specific write operations. This
could potentially influence some specific regulator chips that for
example need a time delay before each data access.

Lets add support for driver callback with _pmbus_write_byte_data.

Signed-off-by: Mårten Lindahl <marten.lindahl@axis.com>
---
 drivers/hwmon/pmbus/pmbus.h      |  2 ++
 drivers/hwmon/pmbus/pmbus_core.c | 24 +++++++++++++++++++++---
 2 files changed, 23 insertions(+), 3 deletions(-)

diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h
index e74b6ef070f3..c031a9700ace 100644
--- a/drivers/hwmon/pmbus/pmbus.h
+++ b/drivers/hwmon/pmbus/pmbus.h
@@ -438,6 +438,8 @@ struct pmbus_driver_info {
 	int (*read_byte_data)(struct i2c_client *client, int page, int reg);
 	int (*read_word_data)(struct i2c_client *client, int page, int phase,
 			      int reg);
+	int (*write_byte_data)(struct i2c_client *client, int page, int reg,
+			      u8 byte);
 	int (*write_word_data)(struct i2c_client *client, int page, int reg,
 			       u16 word);
 	int (*write_byte)(struct i2c_client *client, int page, u8 value);
diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
index b2618b1d529e..98da2db3f831 100644
--- a/drivers/hwmon/pmbus/pmbus_core.c
+++ b/drivers/hwmon/pmbus/pmbus_core.c
@@ -276,6 +276,24 @@ static int _pmbus_write_word_data(struct i2c_client *client, int page, int reg,
 	return pmbus_write_word_data(client, page, reg, word);
 }
 
+/*
+ * _pmbus_write_byte_data() is similar to pmbus_write_byte_data(), but checks if
+ * a device specific mapping function exists and calls it if necessary.
+ */
+static int _pmbus_write_byte_data(struct i2c_client *client, int page, int reg, u8 value)
+{
+	struct pmbus_data *data = i2c_get_clientdata(client);
+	const struct pmbus_driver_info *info = data->info;
+	int status;
+
+	if (info->write_byte_data) {
+		status = info->write_byte_data(client, page, reg, value);
+		if (status != -ENODATA)
+			return status;
+	}
+	return pmbus_write_byte_data(client, page, reg, value);
+}
+
 int pmbus_update_fan(struct i2c_client *client, int page, int id,
 		     u8 config, u8 mask, u16 command)
 {
@@ -290,7 +308,7 @@ int pmbus_update_fan(struct i2c_client *client, int page, int id,
 
 	to = (from & ~mask) | (config & mask);
 	if (to != from) {
-		rv = pmbus_write_byte_data(client, page,
+		rv = _pmbus_write_byte_data(client, page,
 					   pmbus_fan_config_registers[id], to);
 		if (rv < 0)
 			return rv;
@@ -397,7 +415,7 @@ int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg,
 	tmp = (rv & ~mask) | (value & mask);
 
 	if (tmp != rv)
-		rv = pmbus_write_byte_data(client, page, reg, tmp);
+		rv = _pmbus_write_byte_data(client, page, reg, tmp);
 
 	return rv;
 }
@@ -912,7 +930,7 @@ static int pmbus_get_boolean(struct i2c_client *client, struct pmbus_boolean *b,
 
 	regval = status & mask;
 	if (regval) {
-		ret = pmbus_write_byte_data(client, page, reg, regval);
+		ret = _pmbus_write_byte_data(client, page, reg, regval);
 		if (ret)
 			goto unlock;
 	}
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v5 2/4] hwmon: (pmbus) Use _pmbus_read_byte_data with callback
  2022-05-02 11:13 [PATCH v5 0/4] hwmon: (pmbus/ltc2978) Add regulator ops Mårten Lindahl
  2022-05-02 11:13 ` [PATCH v5 1/4] hwmon: (pmbus) Introduce and use write_byte_data callback Mårten Lindahl
@ 2022-05-02 11:13 ` Mårten Lindahl
  2022-05-02 11:13 ` [PATCH v5 3/4] hwmon: (pmbus/ltc2978) Add chip specific write_byte_data Mårten Lindahl
  2022-05-02 11:13 ` [PATCH v5 4/4] hwmon: (pmbus) Add get_voltage/set_voltage ops Mårten Lindahl
  3 siblings, 0 replies; 7+ messages in thread
From: Mårten Lindahl @ 2022-05-02 11:13 UTC (permalink / raw)
  To: Guenter Roeck, Jean Delvare; +Cc: linux-hwmon, kernel, Mårten Lindahl

Some of the pmbus core functions uses pmbus_read_byte_data, which does
not support driver callbacks for chip specific write operations. This
could potentially influence some specific regulator chips that for
example need a time delay before each data access.

Lets use _pmbus_read_byte_data with callback check.

Signed-off-by: Mårten Lindahl <marten.lindahl@axis.com>
---
 drivers/hwmon/pmbus/pmbus_core.c | 46 ++++++++++++++++----------------
 1 file changed, 23 insertions(+), 23 deletions(-)

diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
index 98da2db3f831..bd143ca0c320 100644
--- a/drivers/hwmon/pmbus/pmbus_core.c
+++ b/drivers/hwmon/pmbus/pmbus_core.c
@@ -294,6 +294,24 @@ static int _pmbus_write_byte_data(struct i2c_client *client, int page, int reg,
 	return pmbus_write_byte_data(client, page, reg, value);
 }
 
+/*
+ * _pmbus_read_byte_data() is similar to pmbus_read_byte_data(), but checks if
+ * a device specific mapping function exists and calls it if necessary.
+ */
+static int _pmbus_read_byte_data(struct i2c_client *client, int page, int reg)
+{
+	struct pmbus_data *data = i2c_get_clientdata(client);
+	const struct pmbus_driver_info *info = data->info;
+	int status;
+
+	if (info->read_byte_data) {
+		status = info->read_byte_data(client, page, reg);
+		if (status != -ENODATA)
+			return status;
+	}
+	return pmbus_read_byte_data(client, page, reg);
+}
+
 int pmbus_update_fan(struct i2c_client *client, int page, int id,
 		     u8 config, u8 mask, u16 command)
 {
@@ -301,7 +319,7 @@ int pmbus_update_fan(struct i2c_client *client, int page, int id,
 	int rv;
 	u8 to;
 
-	from = pmbus_read_byte_data(client, page,
+	from = _pmbus_read_byte_data(client, page,
 				    pmbus_fan_config_registers[id]);
 	if (from < 0)
 		return from;
@@ -408,7 +426,7 @@ int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg,
 	unsigned int tmp;
 	int rv;
 
-	rv = pmbus_read_byte_data(client, page, reg);
+	rv = _pmbus_read_byte_data(client, page, reg);
 	if (rv < 0)
 		return rv;
 
@@ -421,24 +439,6 @@ int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg,
 }
 EXPORT_SYMBOL_NS_GPL(pmbus_update_byte_data, PMBUS);
 
-/*
- * _pmbus_read_byte_data() is similar to pmbus_read_byte_data(), but checks if
- * a device specific mapping function exists and calls it if necessary.
- */
-static int _pmbus_read_byte_data(struct i2c_client *client, int page, int reg)
-{
-	struct pmbus_data *data = i2c_get_clientdata(client);
-	const struct pmbus_driver_info *info = data->info;
-	int status;
-
-	if (info->read_byte_data) {
-		status = info->read_byte_data(client, page, reg);
-		if (status != -ENODATA)
-			return status;
-	}
-	return pmbus_read_byte_data(client, page, reg);
-}
-
 static struct pmbus_sensor *pmbus_find_sensor(struct pmbus_data *data, int page,
 					      int reg)
 {
@@ -473,7 +473,7 @@ static int pmbus_get_fan_rate(struct i2c_client *client, int page, int id,
 		return s->data;
 	}
 
-	config = pmbus_read_byte_data(client, page,
+	config = _pmbus_read_byte_data(client, page,
 				      pmbus_fan_config_registers[id]);
 	if (config < 0)
 		return config;
@@ -2414,7 +2414,7 @@ static int pmbus_regulator_is_enabled(struct regulator_dev *rdev)
 	int ret;
 
 	mutex_lock(&data->update_lock);
-	ret = pmbus_read_byte_data(client, page, PMBUS_OPERATION);
+	ret = _pmbus_read_byte_data(client, page, PMBUS_OPERATION);
 	mutex_unlock(&data->update_lock);
 
 	if (ret < 0)
@@ -2513,7 +2513,7 @@ static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned
 		if (!(func & cat->func))
 			continue;
 
-		status = pmbus_read_byte_data(client, page, cat->reg);
+		status = _pmbus_read_byte_data(client, page, cat->reg);
 		if (status < 0) {
 			mutex_unlock(&data->update_lock);
 			return status;
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v5 3/4] hwmon: (pmbus/ltc2978) Add chip specific write_byte_data
  2022-05-02 11:13 [PATCH v5 0/4] hwmon: (pmbus/ltc2978) Add regulator ops Mårten Lindahl
  2022-05-02 11:13 ` [PATCH v5 1/4] hwmon: (pmbus) Introduce and use write_byte_data callback Mårten Lindahl
  2022-05-02 11:13 ` [PATCH v5 2/4] hwmon: (pmbus) Use _pmbus_read_byte_data with callback Mårten Lindahl
@ 2022-05-02 11:13 ` Mårten Lindahl
  2022-05-02 11:13 ` [PATCH v5 4/4] hwmon: (pmbus) Add get_voltage/set_voltage ops Mårten Lindahl
  3 siblings, 0 replies; 7+ messages in thread
From: Mårten Lindahl @ 2022-05-02 11:13 UTC (permalink / raw)
  To: Guenter Roeck, Jean Delvare; +Cc: linux-hwmon, kernel, Mårten Lindahl

Several of the manuals for devices supported by this driver describes
the need for a minimum wait time before the chip is ready to receive
next command.

This wait time is already implemented in the driver as a ltc_wait_ready
function with a driver defined wait time of 100 ms, and is considered
for specific devices before reading/writing data on the pmbus.

Since this driver uses the default pmbus_regulator_ops for the enable/
disable/is_enabled functions we should add a driver specific callback
for write_byte_data to prevent bypassing the wait time recommendations
for the following devices: ltc3880/ltc3882/ltc3883/ltc3884/ltc3886/
ltc3887/ltc3889/ltm4664/ltm4675/ltm4676/ltm4677/ltm4678/ltm4680/ltm4686/
ltm4700/ltc7880.

Signed-off-by: Mårten Lindahl <marten.lindahl@axis.com>
---
 drivers/hwmon/pmbus/ltc2978.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c
index 0127273883f0..531aa674a928 100644
--- a/drivers/hwmon/pmbus/ltc2978.c
+++ b/drivers/hwmon/pmbus/ltc2978.c
@@ -196,6 +196,17 @@ static int ltc_read_byte_data(struct i2c_client *client, int page, int reg)
 	return pmbus_read_byte_data(client, page, reg);
 }
 
+static int ltc_write_byte_data(struct i2c_client *client, int page, int reg, u8 value)
+{
+	int ret;
+
+	ret = ltc_wait_ready(client);
+	if (ret < 0)
+		return ret;
+
+	return pmbus_write_byte_data(client, page, reg, value);
+}
+
 static int ltc_write_byte(struct i2c_client *client, int page, u8 byte)
 {
 	int ret;
@@ -681,6 +692,7 @@ static int ltc2978_probe(struct i2c_client *client)
 	info = &data->info;
 	info->write_word_data = ltc2978_write_word_data;
 	info->write_byte = ltc_write_byte;
+	info->write_byte_data = ltc_write_byte_data;
 	info->read_word_data = ltc_read_word_data;
 	info->read_byte_data = ltc_read_byte_data;
 
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v5 4/4] hwmon: (pmbus) Add get_voltage/set_voltage ops
  2022-05-02 11:13 [PATCH v5 0/4] hwmon: (pmbus/ltc2978) Add regulator ops Mårten Lindahl
                   ` (2 preceding siblings ...)
  2022-05-02 11:13 ` [PATCH v5 3/4] hwmon: (pmbus/ltc2978) Add chip specific write_byte_data Mårten Lindahl
@ 2022-05-02 11:13 ` Mårten Lindahl
  2022-05-02 16:48   ` Guenter Roeck
  3 siblings, 1 reply; 7+ messages in thread
From: Mårten Lindahl @ 2022-05-02 11:13 UTC (permalink / raw)
  To: Guenter Roeck, Jean Delvare; +Cc: linux-hwmon, kernel, Mårten Lindahl

The pmbus core does not have operations for getting or setting voltage.
Add functions get/set voltage for the dynamic regulator framework.

Signed-off-by: Mårten Lindahl <marten.lindahl@axis.com>
---
 drivers/hwmon/pmbus/pmbus_core.c | 63 ++++++++++++++++++++++++++++++++
 1 file changed, 63 insertions(+)

diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
index bd143ca0c320..455d06ba5fdf 100644
--- a/drivers/hwmon/pmbus/pmbus_core.c
+++ b/drivers/hwmon/pmbus/pmbus_core.c
@@ -2563,11 +2563,74 @@ static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned
 	return 0;
 }
 
+static int pmbus_regulator_get_voltage(struct regulator_dev *rdev)
+{
+	struct device *dev = rdev_get_dev(rdev);
+	struct i2c_client *client = to_i2c_client(dev->parent);
+	struct pmbus_data *data = i2c_get_clientdata(client);
+	struct pmbus_sensor s = {
+		.page = rdev_get_id(rdev),
+		.class = PSC_VOLTAGE_OUT,
+		.convert = true,
+	};
+
+	s.data = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_READ_VOUT);
+	if (s.data < 0)
+		return s.data;
+
+	return (int)pmbus_reg2data(data, &s) * 1000; /* unit is uV */
+}
+
+static int pmbus_regulator_set_voltage(struct regulator_dev *rdev, int min_uV,
+					 int max_uV, unsigned int *selector)
+{
+	struct device *dev = rdev_get_dev(rdev);
+	struct i2c_client *client = to_i2c_client(dev->parent);
+	struct pmbus_data *data = i2c_get_clientdata(client);
+	struct pmbus_sensor s = {
+		.page = rdev_get_id(rdev),
+		.class = PSC_VOLTAGE_OUT,
+		.convert = true,
+	};
+	s64 tmp = DIV_ROUND_CLOSEST_ULL(min_uV, 1000); /* convert to mV */
+	int low = -1, high = -1;
+	u16 val;
+	*selector = 0;
+
+	if (pmbus_check_word_register(client, s.page, PMBUS_MFR_VOUT_MIN))
+		low = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_MFR_VOUT_MIN);
+	if (low < 0) {
+		low = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_VOUT_MARGIN_LOW);
+		if (low < 0)
+			return low;
+	}
+
+	if (pmbus_check_word_register(client, s.page, PMBUS_MFR_VOUT_MAX))
+		high = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_MFR_VOUT_MAX);
+	if (high < 0) {
+		high = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_VOUT_MARGIN_HIGH);
+		if (high < 0)
+			return high;
+	}
+
+	val = pmbus_data2reg(data, &s, tmp);
+
+	/* Make sure we are within margins */
+	if (low > val)
+		val = low;
+	if (high < val)
+		val = high;
+
+	return _pmbus_write_word_data(client, s.page, PMBUS_VOUT_COMMAND, val);
+}
+
 const struct regulator_ops pmbus_regulator_ops = {
 	.enable = pmbus_regulator_enable,
 	.disable = pmbus_regulator_disable,
 	.is_enabled = pmbus_regulator_is_enabled,
 	.get_error_flags = pmbus_regulator_get_error_flags,
+	.get_voltage = pmbus_regulator_get_voltage,
+	.set_voltage = pmbus_regulator_set_voltage,
 };
 EXPORT_SYMBOL_NS_GPL(pmbus_regulator_ops, PMBUS);
 
-- 
2.30.2


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH v5 4/4] hwmon: (pmbus) Add get_voltage/set_voltage ops
  2022-05-02 11:13 ` [PATCH v5 4/4] hwmon: (pmbus) Add get_voltage/set_voltage ops Mårten Lindahl
@ 2022-05-02 16:48   ` Guenter Roeck
  2022-05-03 10:14     ` Marten Lindahl
  0 siblings, 1 reply; 7+ messages in thread
From: Guenter Roeck @ 2022-05-02 16:48 UTC (permalink / raw)
  To: Mårten Lindahl, Jean Delvare; +Cc: linux-hwmon, kernel

On 5/2/22 04:13, Mårten Lindahl wrote:
> The pmbus core does not have operations for getting or setting voltage.
> Add functions get/set voltage for the dynamic regulator framework.
> 
> Signed-off-by: Mårten Lindahl <marten.lindahl@axis.com>
> ---
>   drivers/hwmon/pmbus/pmbus_core.c | 63 ++++++++++++++++++++++++++++++++
>   1 file changed, 63 insertions(+)
> 
> diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
> index bd143ca0c320..455d06ba5fdf 100644
> --- a/drivers/hwmon/pmbus/pmbus_core.c
> +++ b/drivers/hwmon/pmbus/pmbus_core.c
> @@ -2563,11 +2563,74 @@ static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned
>   	return 0;
>   }
>   
> +static int pmbus_regulator_get_voltage(struct regulator_dev *rdev)
> +{
> +	struct device *dev = rdev_get_dev(rdev);
> +	struct i2c_client *client = to_i2c_client(dev->parent);
> +	struct pmbus_data *data = i2c_get_clientdata(client);
> +	struct pmbus_sensor s = {
> +		.page = rdev_get_id(rdev),
> +		.class = PSC_VOLTAGE_OUT,
> +		.convert = true,
> +	};
> +
> +	s.data = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_READ_VOUT);
> +	if (s.data < 0)
> +		return s.data;
> +
> +	return (int)pmbus_reg2data(data, &s) * 1000; /* unit is uV */
> +}
> +
> +static int pmbus_regulator_set_voltage(struct regulator_dev *rdev, int min_uV,
> +					 int max_uV, unsigned int *selector)

Just noticed: Please don't use camelCase.

> +{
> +	struct device *dev = rdev_get_dev(rdev);
> +	struct i2c_client *client = to_i2c_client(dev->parent);
> +	struct pmbus_data *data = i2c_get_clientdata(client);
> +	struct pmbus_sensor s = {
> +		.page = rdev_get_id(rdev),
> +		.class = PSC_VOLTAGE_OUT,
> +		.convert = true,
> +	};
> +	s64 tmp = DIV_ROUND_CLOSEST_ULL(min_uV, 1000); /* convert to mV */

min_uV is already an int, so converting it to s64 will never be
necessary.

> +	int low = -1, high = -1;
> +	u16 val;
> +	*selector = 0;
> +
> +	if (pmbus_check_word_register(client, s.page, PMBUS_MFR_VOUT_MIN))
> +		low = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_MFR_VOUT_MIN);
> +	if (low < 0) {
> +		low = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_VOUT_MARGIN_LOW);
> +		if (low < 0)
> +			return low;
> +	}
> +
> +	if (pmbus_check_word_register(client, s.page, PMBUS_MFR_VOUT_MAX))
> +		high = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_MFR_VOUT_MAX);
> +	if (high < 0) {
> +		high = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_VOUT_MARGIN_HIGH);
> +		if (high < 0)
> +			return high;
> +	}
> +
> +	val = pmbus_data2reg(data, &s, tmp);
> +
> +	/* Make sure we are within margins */
> +	if (low > val)
> +		val = low;
> +	if (high < val)
> +		val = high;
> +

The above assumes that register values are directly comparable.
Unfortunately that isn't really the case. It happens to work
for ULINEAR16 and DIRECT mode, but chips could also support
IEEE-754 (maybe in the future) or VID mode.

You need to read the limits from the registers, convert to voltages,
compare and adjust the voltage, and as final step convert the adjusted
voltage to a register value.

Thanks,
Guenter

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v5 4/4] hwmon: (pmbus) Add get_voltage/set_voltage ops
  2022-05-02 16:48   ` Guenter Roeck
@ 2022-05-03 10:14     ` Marten Lindahl
  0 siblings, 0 replies; 7+ messages in thread
From: Marten Lindahl @ 2022-05-03 10:14 UTC (permalink / raw)
  To: Guenter Roeck; +Cc: Mårten Lindahl, Jean Delvare, linux-hwmon, kernel

On Mon, May 02, 2022 at 06:48:25PM +0200, Guenter Roeck wrote:
> On 5/2/22 04:13, Mårten Lindahl wrote:
> > The pmbus core does not have operations for getting or setting voltage.
> > Add functions get/set voltage for the dynamic regulator framework.
> > 
> > Signed-off-by: Mårten Lindahl <marten.lindahl@axis.com>
> > ---
> >   drivers/hwmon/pmbus/pmbus_core.c | 63 ++++++++++++++++++++++++++++++++
> >   1 file changed, 63 insertions(+)
> > 
> > diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
> > index bd143ca0c320..455d06ba5fdf 100644
> > --- a/drivers/hwmon/pmbus/pmbus_core.c
> > +++ b/drivers/hwmon/pmbus/pmbus_core.c
> > @@ -2563,11 +2563,74 @@ static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned
> >   	return 0;
> >   }
> >   
> > +static int pmbus_regulator_get_voltage(struct regulator_dev *rdev)
> > +{
> > +	struct device *dev = rdev_get_dev(rdev);
> > +	struct i2c_client *client = to_i2c_client(dev->parent);
> > +	struct pmbus_data *data = i2c_get_clientdata(client);
> > +	struct pmbus_sensor s = {
> > +		.page = rdev_get_id(rdev),
> > +		.class = PSC_VOLTAGE_OUT,
> > +		.convert = true,
> > +	};
> > +
> > +	s.data = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_READ_VOUT);
> > +	if (s.data < 0)
> > +		return s.data;
> > +
> > +	return (int)pmbus_reg2data(data, &s) * 1000; /* unit is uV */
> > +}
> > +
> > +static int pmbus_regulator_set_voltage(struct regulator_dev *rdev, int min_uV,
> > +					 int max_uV, unsigned int *selector)
> 
> Just noticed: Please don't use camelCase.

Ok, I will change it. But for this I blame include/linux/regulator/driver.h:
struct regulator_ops {
	...
	/* get/set regulator voltage */
	int (*set_voltage) (struct regulator_dev *, int min_uV, int max_uV,
			    unsigned *selector);

> 
> > +{
> > +	struct device *dev = rdev_get_dev(rdev);
> > +	struct i2c_client *client = to_i2c_client(dev->parent);
> > +	struct pmbus_data *data = i2c_get_clientdata(client);
> > +	struct pmbus_sensor s = {
> > +		.page = rdev_get_id(rdev),
> > +		.class = PSC_VOLTAGE_OUT,
> > +		.convert = true,
> > +	};
> > +	s64 tmp = DIV_ROUND_CLOSEST_ULL(min_uV, 1000); /* convert to mV */
> 
> min_uV is already an int, so converting it to s64 will never be
> necessary.

True. Will change.

> 
> > +	int low = -1, high = -1;
> > +	u16 val;
> > +	*selector = 0;
> > +
> > +	if (pmbus_check_word_register(client, s.page, PMBUS_MFR_VOUT_MIN))
> > +		low = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_MFR_VOUT_MIN);
> > +	if (low < 0) {
> > +		low = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_VOUT_MARGIN_LOW);
> > +		if (low < 0)
> > +			return low;
> > +	}
> > +
> > +	if (pmbus_check_word_register(client, s.page, PMBUS_MFR_VOUT_MAX))
> > +		high = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_MFR_VOUT_MAX);
> > +	if (high < 0) {
> > +		high = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_VOUT_MARGIN_HIGH);
> > +		if (high < 0)
> > +			return high;
> > +	}
> > +
> > +	val = pmbus_data2reg(data, &s, tmp);
> > +
> > +	/* Make sure we are within margins */
> > +	if (low > val)
> > +		val = low;
> > +	if (high < val)
> > +		val = high;
> > +
> 
> The above assumes that register values are directly comparable.
> Unfortunately that isn't really the case. It happens to work
> for ULINEAR16 and DIRECT mode, but chips could also support
> IEEE-754 (maybe in the future) or VID mode.
> 
> You need to read the limits from the registers, convert to voltages,
> compare and adjust the voltage, and as final step convert the adjusted
> voltage to a register value.

Thanks. That is a good observation.

Kind regards
Mårten

> 
> Thanks,
> Guenter

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2022-05-03 10:14 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-05-02 11:13 [PATCH v5 0/4] hwmon: (pmbus/ltc2978) Add regulator ops Mårten Lindahl
2022-05-02 11:13 ` [PATCH v5 1/4] hwmon: (pmbus) Introduce and use write_byte_data callback Mårten Lindahl
2022-05-02 11:13 ` [PATCH v5 2/4] hwmon: (pmbus) Use _pmbus_read_byte_data with callback Mårten Lindahl
2022-05-02 11:13 ` [PATCH v5 3/4] hwmon: (pmbus/ltc2978) Add chip specific write_byte_data Mårten Lindahl
2022-05-02 11:13 ` [PATCH v5 4/4] hwmon: (pmbus) Add get_voltage/set_voltage ops Mårten Lindahl
2022-05-02 16:48   ` Guenter Roeck
2022-05-03 10:14     ` Marten Lindahl

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.