linux-hwmon.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/3]  hwmon: (pmbus/ltc2978) Set voltage resolution
@ 2022-06-10 11:47 Mårten Lindahl
  2022-06-10 11:47 ` [PATCH v2 1/3] hwmon: (pmbus) Introduce and use cached vout margins Mårten Lindahl
                   ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: Mårten Lindahl @ 2022-06-10 11:47 UTC (permalink / raw)
  To: Guenter Roeck, Jean Delvare; +Cc: linux-hwmon, kernel, Mårten Lindahl

Hi!

When checking if a regulator supports a voltage range, the regulator
needs to have support for listing the range or else -EINVAL will be
returned.

This support does not exist for the LTC2977 regulator, so this change
adds support for list voltage to the pmbus regulators by adding
regulator_list_voltage_linear to the pmbus_regulator_ops. It also
defines the voltage resolution for regulators LTC2972/LTC2974/LTC2975/
LTC2977/LTC2978/LTC2979/LTC2980/LTM2987 based on that they all have the
same stepwise 122.07uV resolution, and scales the resolution to a 1mV
resolution which is easier to handle.

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

Kind regards
Mårten Lindahl

Mårten Lindahl (3):
  hwmon: (pmbus) Introduce and use cached vout margins
  hwmon: (pmbus) Add list_voltage to pmbus ops
  hwmon: (pmbus/ltc2978) Set voltage resolution

 drivers/hwmon/pmbus/ltc2978.c    |  56 +++++++++++++++--
 drivers/hwmon/pmbus/pmbus_core.c | 104 ++++++++++++++++++++++++-------
 2 files changed, 134 insertions(+), 26 deletions(-)

-- 
2.30.2


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

* [PATCH v2 1/3] hwmon: (pmbus) Introduce and use cached vout margins
  2022-06-10 11:47 [PATCH v2 0/3] hwmon: (pmbus/ltc2978) Set voltage resolution Mårten Lindahl
@ 2022-06-10 11:47 ` Mårten Lindahl
  2022-06-10 11:47 ` [PATCH v2 2/3] hwmon: (pmbus) Add list_voltage to pmbus ops Mårten Lindahl
  2022-06-10 11:47 ` [PATCH v2 3/3] hwmon: (pmbus/ltc2978) Set voltage resolution Mårten Lindahl
  2 siblings, 0 replies; 8+ messages in thread
From: Mårten Lindahl @ 2022-06-10 11:47 UTC (permalink / raw)
  To: Guenter Roeck, Jean Delvare; +Cc: linux-hwmon, kernel, Mårten Lindahl

When setting a new voltage the voltage boundaries are read every time to
check that the new voltage is within the proper range. Checking these
voltage boundaries consists of reading one of PMBUS_MFR_VOUT_MIN/
PMBUS_VOUT_MARGIN_LOW registers and then PMBUS_MFR_VOUT_MAX/
PMBUS_VOUT_MARGIN_HIGH together with writing the PMBUS_CLEAR_FAULTS
register.

Since these boundaries are never being changed, it can be cached and
thus saving unnecessary smbus transmissions.

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

diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
index 02912022853d..478dda49a45f 100644
--- a/drivers/hwmon/pmbus/pmbus_core.c
+++ b/drivers/hwmon/pmbus/pmbus_core.c
@@ -104,6 +104,9 @@ struct pmbus_data {
 
 	s16 currpage;	/* current page, -1 for unknown/unset */
 	s16 currphase;	/* current phase, 0xff for all, -1 for unknown/unset */
+
+	int vout_low[PMBUS_PAGES];	/* voltage low margin */
+	int vout_high[PMBUS_PAGES];	/* voltage high margin */
 };
 
 struct pmbus_debugfs_entry {
@@ -2667,34 +2670,41 @@ static int pmbus_regulator_set_voltage(struct regulator_dev *rdev, int min_uv,
 		.data = -1,
 	};
 	int val = DIV_ROUND_CLOSEST(min_uv, 1000); /* convert to mV */
-	int low, high;
 
 	*selector = 0;
 
-	if (pmbus_check_word_register(client, s.page, PMBUS_MFR_VOUT_MIN))
-		s.data = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_MFR_VOUT_MIN);
-	if (s.data < 0) {
-		s.data = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_VOUT_MARGIN_LOW);
-		if (s.data < 0)
-			return s.data;
-	}
-	low = pmbus_reg2data(data, &s);
-
-	s.data = -1;
-	if (pmbus_check_word_register(client, s.page, PMBUS_MFR_VOUT_MAX))
-		s.data = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_MFR_VOUT_MAX);
-	if (s.data < 0) {
-		s.data = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_VOUT_MARGIN_HIGH);
-		if (s.data < 0)
-			return s.data;
+	if (!data->vout_low[s.page]) {
+		if (pmbus_check_word_register(client, s.page, PMBUS_MFR_VOUT_MIN))
+			s.data = _pmbus_read_word_data(client, s.page, 0xff,
+						       PMBUS_MFR_VOUT_MIN);
+		if (s.data < 0) {
+			s.data = _pmbus_read_word_data(client, s.page, 0xff,
+						       PMBUS_VOUT_MARGIN_LOW);
+			if (s.data < 0)
+				return s.data;
+		}
+		data->vout_low[s.page] = pmbus_reg2data(data, &s);
+	}
+
+	if (!data->vout_high[s.page]) {
+		s.data = -1;
+		if (pmbus_check_word_register(client, s.page, PMBUS_MFR_VOUT_MAX))
+			s.data = _pmbus_read_word_data(client, s.page, 0xff,
+						       PMBUS_MFR_VOUT_MAX);
+		if (s.data < 0) {
+			s.data = _pmbus_read_word_data(client, s.page, 0xff,
+						       PMBUS_VOUT_MARGIN_HIGH);
+			if (s.data < 0)
+				return s.data;
+		}
+		data->vout_high[s.page] = pmbus_reg2data(data, &s);
 	}
-	high = pmbus_reg2data(data, &s);
 
 	/* Make sure we are within margins */
-	if (low > val)
-		val = low;
-	if (high < val)
-		val = high;
+	if (data->vout_low[s.page] > val)
+		val = data->vout_low[s.page];
+	if (data->vout_high[s.page] < val)
+		val = data->vout_high[s.page];
 
 	val = pmbus_data2reg(data, &s, val);
 
-- 
2.30.2


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

* [PATCH v2 2/3] hwmon: (pmbus) Add list_voltage to pmbus ops
  2022-06-10 11:47 [PATCH v2 0/3] hwmon: (pmbus/ltc2978) Set voltage resolution Mårten Lindahl
  2022-06-10 11:47 ` [PATCH v2 1/3] hwmon: (pmbus) Introduce and use cached vout margins Mårten Lindahl
@ 2022-06-10 11:47 ` Mårten Lindahl
  2022-06-10 14:16   ` Guenter Roeck
  2022-06-10 11:47 ` [PATCH v2 3/3] hwmon: (pmbus/ltc2978) Set voltage resolution Mårten Lindahl
  2 siblings, 1 reply; 8+ messages in thread
From: Mårten Lindahl @ 2022-06-10 11:47 UTC (permalink / raw)
  To: Guenter Roeck, Jean Delvare; +Cc: linux-hwmon, kernel, Mårten Lindahl

When checking if a regulator supports a voltage range, the regulator
needs to have a list_voltage callback set to the regulator_ops or else
-EINVAL will be returned. This support does not exist for the pmbus
regulators, so this patch adds pmbus_regulator_list_voltage to the
pmbus_regulator_ops.

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

diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
index 478dda49a45f..24ba4b2b03d4 100644
--- a/drivers/hwmon/pmbus/pmbus_core.c
+++ b/drivers/hwmon/pmbus/pmbus_core.c
@@ -2711,6 +2711,55 @@ static int pmbus_regulator_set_voltage(struct regulator_dev *rdev, int min_uv,
 	return _pmbus_write_word_data(client, s.page, PMBUS_VOUT_COMMAND, (u16)val);
 }
 
+static int pmbus_regulator_list_voltage(struct regulator_dev *rdev,
+					 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,
+		.data = -1,
+	};
+	int val = DIV_ROUND_CLOSEST(rdev->desc->min_uV +
+				    (rdev->desc->uV_step * selector),
+				    1000); /* convert to mV */
+
+	if (!data->vout_low[s.page]) {
+		if (pmbus_check_word_register(client, s.page, PMBUS_MFR_VOUT_MIN))
+			s.data = _pmbus_read_word_data(client, s.page, 0xff,
+						       PMBUS_MFR_VOUT_MIN);
+		if (s.data < 0) {
+			s.data = _pmbus_read_word_data(client, s.page, 0xff,
+						       PMBUS_VOUT_MARGIN_LOW);
+			if (s.data < 0)
+				return s.data;
+		}
+		data->vout_low[s.page] = pmbus_reg2data(data, &s);
+	}
+
+	if (!data->vout_high[s.page]) {
+		s.data = -1;
+		if (pmbus_check_word_register(client, s.page, PMBUS_MFR_VOUT_MAX))
+			s.data = _pmbus_read_word_data(client, s.page, 0xff,
+						       PMBUS_MFR_VOUT_MAX);
+		if (s.data < 0) {
+			s.data = _pmbus_read_word_data(client, s.page, 0xff,
+						       PMBUS_VOUT_MARGIN_HIGH);
+			if (s.data < 0)
+				return s.data;
+		}
+		data->vout_high[s.page] = pmbus_reg2data(data, &s);
+	}
+
+	if (val >= data->vout_low[s.page] && val <= data->vout_high[s.page])
+		return val * 1000; /* unit is uV */
+
+	return 0;
+}
+
 const struct regulator_ops pmbus_regulator_ops = {
 	.enable = pmbus_regulator_enable,
 	.disable = pmbus_regulator_disable,
@@ -2718,6 +2767,7 @@ const struct regulator_ops pmbus_regulator_ops = {
 	.get_error_flags = pmbus_regulator_get_error_flags,
 	.get_voltage = pmbus_regulator_get_voltage,
 	.set_voltage = pmbus_regulator_set_voltage,
+	.list_voltage = pmbus_regulator_list_voltage,
 };
 EXPORT_SYMBOL_NS_GPL(pmbus_regulator_ops, PMBUS);
 
-- 
2.30.2


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

* [PATCH v2 3/3] hwmon: (pmbus/ltc2978) Set voltage resolution
  2022-06-10 11:47 [PATCH v2 0/3] hwmon: (pmbus/ltc2978) Set voltage resolution Mårten Lindahl
  2022-06-10 11:47 ` [PATCH v2 1/3] hwmon: (pmbus) Introduce and use cached vout margins Mårten Lindahl
  2022-06-10 11:47 ` [PATCH v2 2/3] hwmon: (pmbus) Add list_voltage to pmbus ops Mårten Lindahl
@ 2022-06-10 11:47 ` Mårten Lindahl
  2022-06-10 14:27   ` Guenter Roeck
  2 siblings, 1 reply; 8+ messages in thread
From: Mårten Lindahl @ 2022-06-10 11:47 UTC (permalink / raw)
  To: Guenter Roeck, Jean Delvare; +Cc: linux-hwmon, kernel, Mårten Lindahl

The LTC2977 regulator does not set the regulator_desc .n_voltages value
which is needed in order to let the regulator core list the regulator
voltage range.

This patch defines a regulator_desc with a voltage range, and uses it
for defining voltage resolution for regulators LTC2972/LTC2974/LTC2975/
LTC2977/LTC2978/LTC2979/LTC2980/LTM2987 based on that they all have a 16
bit ADC with the same stepwise 122.07uV resolution. It also scales the
resolution to a 1mV resolution which is easier to handle.

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

diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c
index 531aa674a928..7d44e64c61c1 100644
--- a/drivers/hwmon/pmbus/ltc2978.c
+++ b/drivers/hwmon/pmbus/ltc2978.c
@@ -562,7 +562,36 @@ static const struct i2c_device_id ltc2978_id[] = {
 MODULE_DEVICE_TABLE(i2c, ltc2978_id);
 
 #if IS_ENABLED(CONFIG_SENSORS_LTC2978_REGULATOR)
+#define LTC2978_ADC_RES	0xFFFF
+#define LTC2978_N_ADC	122
+#define LTC2978_MAX_UV	(LTC2978_ADC_RES * LTC2978_N_ADC)
+#define LTC2978_UV_STEP	1000
+
+#define PMBUS_LTC2978_REGULATOR(_name, _id)               \
+	[_id] = {                                               \
+		.name = (_name # _id),                                \
+		.id = (_id),                                          \
+		.of_match = of_match_ptr(_name # _id),                \
+		.regulators_node = of_match_ptr("regulators"),        \
+		.ops = &pmbus_regulator_ops,                          \
+		.type = REGULATOR_VOLTAGE,                            \
+		.owner = THIS_MODULE,                                 \
+		.n_voltages = (LTC2978_MAX_UV / LTC2978_UV_STEP) + 1, \
+		.uV_step = LTC2978_UV_STEP,                           \
+	}
+
 static const struct regulator_desc ltc2978_reg_desc[] = {
+	PMBUS_LTC2978_REGULATOR("vout", 0),
+	PMBUS_LTC2978_REGULATOR("vout", 1),
+	PMBUS_LTC2978_REGULATOR("vout", 2),
+	PMBUS_LTC2978_REGULATOR("vout", 3),
+	PMBUS_LTC2978_REGULATOR("vout", 4),
+	PMBUS_LTC2978_REGULATOR("vout", 5),
+	PMBUS_LTC2978_REGULATOR("vout", 6),
+	PMBUS_LTC2978_REGULATOR("vout", 7),
+};
+
+static const struct regulator_desc ltc2978_reg_desc_default[] = {
 	PMBUS_REGULATOR("vout", 0),
 	PMBUS_REGULATOR("vout", 1),
 	PMBUS_REGULATOR("vout", 2),
@@ -839,10 +868,29 @@ static int ltc2978_probe(struct i2c_client *client)
 
 #if IS_ENABLED(CONFIG_SENSORS_LTC2978_REGULATOR)
 	info->num_regulators = info->pages;
-	info->reg_desc = ltc2978_reg_desc;
-	if (info->num_regulators > ARRAY_SIZE(ltc2978_reg_desc)) {
-		dev_err(&client->dev, "num_regulators too large!");
-		info->num_regulators = ARRAY_SIZE(ltc2978_reg_desc);
+	switch (data->id) {
+	case ltc2972:
+	case ltc2974:
+	case ltc2975:
+	case ltc2977:
+	case ltc2978:
+	case ltc2979:
+	case ltc2980:
+	case ltm2987:
+		info->reg_desc = ltc2978_reg_desc;
+		if (info->num_regulators > ARRAY_SIZE(ltc2978_reg_desc)) {
+			dev_warn(&client->dev, "num_regulators too large!");
+			info->num_regulators = ARRAY_SIZE(ltc2978_reg_desc);
+		}
+		break;
+	default:
+		info->reg_desc = ltc2978_reg_desc_default;
+		if (info->num_regulators > ARRAY_SIZE(ltc2978_reg_desc_default)) {
+			dev_warn(&client->dev, "num_regulators too large!");
+			info->num_regulators =
+			    ARRAY_SIZE(ltc2978_reg_desc_default);
+		}
+		break;
 	}
 #endif
 
-- 
2.30.2


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

* Re: [PATCH v2 2/3] hwmon: (pmbus) Add list_voltage to pmbus ops
  2022-06-10 11:47 ` [PATCH v2 2/3] hwmon: (pmbus) Add list_voltage to pmbus ops Mårten Lindahl
@ 2022-06-10 14:16   ` Guenter Roeck
  2022-06-13 20:27     ` Marten Lindahl
  0 siblings, 1 reply; 8+ messages in thread
From: Guenter Roeck @ 2022-06-10 14:16 UTC (permalink / raw)
  To: Mårten Lindahl, Jean Delvare; +Cc: linux-hwmon, kernel

On 6/10/22 04:47, Mårten Lindahl wrote:
> When checking if a regulator supports a voltage range, the regulator
> needs to have a list_voltage callback set to the regulator_ops or else
> -EINVAL will be returned. This support does not exist for the pmbus
> regulators, so this patch adds pmbus_regulator_list_voltage to the
> pmbus_regulator_ops.
> 
> Signed-off-by: Mårten Lindahl <marten.lindahl@axis.com>
> ---
>   drivers/hwmon/pmbus/pmbus_core.c | 50 ++++++++++++++++++++++++++++++++
>   1 file changed, 50 insertions(+)
> 
> diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
> index 478dda49a45f..24ba4b2b03d4 100644
> --- a/drivers/hwmon/pmbus/pmbus_core.c
> +++ b/drivers/hwmon/pmbus/pmbus_core.c
> @@ -2711,6 +2711,55 @@ static int pmbus_regulator_set_voltage(struct regulator_dev *rdev, int min_uv,
>   	return _pmbus_write_word_data(client, s.page, PMBUS_VOUT_COMMAND, (u16)val);
>   }
>   
> +static int pmbus_regulator_list_voltage(struct regulator_dev *rdev,
> +					 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,
> +		.data = -1,
> +	};
> +	int val = DIV_ROUND_CLOSEST(rdev->desc->min_uV +
> +				    (rdev->desc->uV_step * selector),
> +				    1000); /* convert to mV */
> +
> +	if (!data->vout_low[s.page]) {
> +		if (pmbus_check_word_register(client, s.page, PMBUS_MFR_VOUT_MIN))
> +			s.data = _pmbus_read_word_data(client, s.page, 0xff,
> +						       PMBUS_MFR_VOUT_MIN);
> +		if (s.data < 0) {
> +			s.data = _pmbus_read_word_data(client, s.page, 0xff,
> +						       PMBUS_VOUT_MARGIN_LOW);
> +			if (s.data < 0)
> +				return s.data;
> +		}
> +		data->vout_low[s.page] = pmbus_reg2data(data, &s);
> +	}
> +
> +	if (!data->vout_high[s.page]) {
> +		s.data = -1;
> +		if (pmbus_check_word_register(client, s.page, PMBUS_MFR_VOUT_MAX))
> +			s.data = _pmbus_read_word_data(client, s.page, 0xff,
> +						       PMBUS_MFR_VOUT_MAX);
> +		if (s.data < 0) {
> +			s.data = _pmbus_read_word_data(client, s.page, 0xff,
> +						       PMBUS_VOUT_MARGIN_HIGH);
> +			if (s.data < 0)
> +				return s.data;
> +		}
> +		data->vout_high[s.page] = pmbus_reg2data(data, &s);
> +	}
> +

The code above is similar to the same code in the first patch. Please
move it into a function (in the first patch).

> +	if (val >= data->vout_low[s.page] && val <= data->vout_high[s.page])
> +		return val * 1000; /* unit is uV */
> +
> +	return 0;

Other drivers return -EINVAL here. Should this be returned as well
if rdev->desc->min_uV or rdev->desc->uV_step is 0, if selector
is out of range, or if data->vout_low[s.page] / data->vout_high[s.page]
is 0 ?

Thanks,
Guenter

> +}
> +
>   const struct regulator_ops pmbus_regulator_ops = {
>   	.enable = pmbus_regulator_enable,
>   	.disable = pmbus_regulator_disable,
> @@ -2718,6 +2767,7 @@ const struct regulator_ops pmbus_regulator_ops = {
>   	.get_error_flags = pmbus_regulator_get_error_flags,
>   	.get_voltage = pmbus_regulator_get_voltage,
>   	.set_voltage = pmbus_regulator_set_voltage,
> +	.list_voltage = pmbus_regulator_list_voltage,
>   };
>   EXPORT_SYMBOL_NS_GPL(pmbus_regulator_ops, PMBUS);
>   


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

* Re: [PATCH v2 3/3] hwmon: (pmbus/ltc2978) Set voltage resolution
  2022-06-10 11:47 ` [PATCH v2 3/3] hwmon: (pmbus/ltc2978) Set voltage resolution Mårten Lindahl
@ 2022-06-10 14:27   ` Guenter Roeck
  2022-06-13 20:34     ` Marten Lindahl
  0 siblings, 1 reply; 8+ messages in thread
From: Guenter Roeck @ 2022-06-10 14:27 UTC (permalink / raw)
  To: Mårten Lindahl, Jean Delvare; +Cc: linux-hwmon, kernel

On 6/10/22 04:47, Mårten Lindahl wrote:
> The LTC2977 regulator does not set the regulator_desc .n_voltages value
> which is needed in order to let the regulator core list the regulator
> voltage range.
> 
> This patch defines a regulator_desc with a voltage range, and uses it
> for defining voltage resolution for regulators LTC2972/LTC2974/LTC2975/
> LTC2977/LTC2978/LTC2979/LTC2980/LTM2987 based on that they all have a 16
> bit ADC with the same stepwise 122.07uV resolution. It also scales the
> resolution to a 1mV resolution which is easier to handle.
> 
> Signed-off-by: Mårten Lindahl <marten.lindahl@axis.com>
> ---
>   drivers/hwmon/pmbus/ltc2978.c | 56 ++++++++++++++++++++++++++++++++---
>   1 file changed, 52 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c
> index 531aa674a928..7d44e64c61c1 100644
> --- a/drivers/hwmon/pmbus/ltc2978.c
> +++ b/drivers/hwmon/pmbus/ltc2978.c
> @@ -562,7 +562,36 @@ static const struct i2c_device_id ltc2978_id[] = {
>   MODULE_DEVICE_TABLE(i2c, ltc2978_id);
>   
>   #if IS_ENABLED(CONFIG_SENSORS_LTC2978_REGULATOR)
> +#define LTC2978_ADC_RES	0xFFFF
> +#define LTC2978_N_ADC	122
> +#define LTC2978_MAX_UV	(LTC2978_ADC_RES * LTC2978_N_ADC)
> +#define LTC2978_UV_STEP	1000
> +
> +#define PMBUS_LTC2978_REGULATOR(_name, _id)               \
> +	[_id] = {                                               \
> +		.name = (_name # _id),                                \

This needs 'supply_name = "vin"'. See commit 54cc3dbfc10dc ("hwmon:
(pmbus) Add regulator supply into macro").

> +		.id = (_id),                                          \
> +		.of_match = of_match_ptr(_name # _id),                \
> +		.regulators_node = of_match_ptr("regulators"),        \
> +		.ops = &pmbus_regulator_ops,                          \
> +		.type = REGULATOR_VOLTAGE,                            \
> +		.owner = THIS_MODULE,                                 \
> +		.n_voltages = (LTC2978_MAX_UV / LTC2978_UV_STEP) + 1, \
> +		.uV_step = LTC2978_UV_STEP,                           \
> +	}

Please introduce a new generic macro PMBUS_REGULATOR_STEP()
with additional parameters n_voltages and uV_step and use it here.
PMBUS_REGULATOR() can then be redefined as

#define PMBUS_REGULATOR(n, i)	PMBUS_REGULATOR_STEP(n, i, 0, 0)

Thanks,
Guenter

> +
>   static const struct regulator_desc ltc2978_reg_desc[] = {
> +	PMBUS_LTC2978_REGULATOR("vout", 0),
> +	PMBUS_LTC2978_REGULATOR("vout", 1),
> +	PMBUS_LTC2978_REGULATOR("vout", 2),
> +	PMBUS_LTC2978_REGULATOR("vout", 3),
> +	PMBUS_LTC2978_REGULATOR("vout", 4),
> +	PMBUS_LTC2978_REGULATOR("vout", 5),
> +	PMBUS_LTC2978_REGULATOR("vout", 6),
> +	PMBUS_LTC2978_REGULATOR("vout", 7),
> +};
> +
> +static const struct regulator_desc ltc2978_reg_desc_default[] = {
>   	PMBUS_REGULATOR("vout", 0),
>   	PMBUS_REGULATOR("vout", 1),
>   	PMBUS_REGULATOR("vout", 2),
> @@ -839,10 +868,29 @@ static int ltc2978_probe(struct i2c_client *client)
>   
>   #if IS_ENABLED(CONFIG_SENSORS_LTC2978_REGULATOR)
>   	info->num_regulators = info->pages;
> -	info->reg_desc = ltc2978_reg_desc;
> -	if (info->num_regulators > ARRAY_SIZE(ltc2978_reg_desc)) {
> -		dev_err(&client->dev, "num_regulators too large!");
> -		info->num_regulators = ARRAY_SIZE(ltc2978_reg_desc);
> +	switch (data->id) {
> +	case ltc2972:
> +	case ltc2974:
> +	case ltc2975:
> +	case ltc2977:
> +	case ltc2978:
> +	case ltc2979:
> +	case ltc2980:
> +	case ltm2987:
> +		info->reg_desc = ltc2978_reg_desc;
> +		if (info->num_regulators > ARRAY_SIZE(ltc2978_reg_desc)) {
> +			dev_warn(&client->dev, "num_regulators too large!");
> +			info->num_regulators = ARRAY_SIZE(ltc2978_reg_desc);
> +		}
> +		break;
> +	default:
> +		info->reg_desc = ltc2978_reg_desc_default;
> +		if (info->num_regulators > ARRAY_SIZE(ltc2978_reg_desc_default)) {
> +			dev_warn(&client->dev, "num_regulators too large!");
> +			info->num_regulators =
> +			    ARRAY_SIZE(ltc2978_reg_desc_default);
> +		}
> +		break;
>   	}
>   #endif
>   


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

* Re: [PATCH v2 2/3] hwmon: (pmbus) Add list_voltage to pmbus ops
  2022-06-10 14:16   ` Guenter Roeck
@ 2022-06-13 20:27     ` Marten Lindahl
  0 siblings, 0 replies; 8+ messages in thread
From: Marten Lindahl @ 2022-06-13 20:27 UTC (permalink / raw)
  To: Guenter Roeck; +Cc: Mårten Lindahl, Jean Delvare, linux-hwmon, kernel

On Fri, Jun 10, 2022 at 04:16:46PM +0200, Guenter Roeck wrote:
> On 6/10/22 04:47, Mårten Lindahl wrote:
> > When checking if a regulator supports a voltage range, the regulator
> > needs to have a list_voltage callback set to the regulator_ops or else
> > -EINVAL will be returned. This support does not exist for the pmbus
> > regulators, so this patch adds pmbus_regulator_list_voltage to the
> > pmbus_regulator_ops.
> > 
> > Signed-off-by: Mårten Lindahl <marten.lindahl@axis.com>
> > ---
> >   drivers/hwmon/pmbus/pmbus_core.c | 50 ++++++++++++++++++++++++++++++++
> >   1 file changed, 50 insertions(+)
> > 
> > diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c
> > index 478dda49a45f..24ba4b2b03d4 100644
> > --- a/drivers/hwmon/pmbus/pmbus_core.c
> > +++ b/drivers/hwmon/pmbus/pmbus_core.c
> > @@ -2711,6 +2711,55 @@ static int pmbus_regulator_set_voltage(struct regulator_dev *rdev, int min_uv,
> >   	return _pmbus_write_word_data(client, s.page, PMBUS_VOUT_COMMAND, (u16)val);
> >   }
> >   
> > +static int pmbus_regulator_list_voltage(struct regulator_dev *rdev,
> > +					 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,
> > +		.data = -1,
> > +	};
> > +	int val = DIV_ROUND_CLOSEST(rdev->desc->min_uV +
> > +				    (rdev->desc->uV_step * selector),
> > +				    1000); /* convert to mV */
> > +
> > +	if (!data->vout_low[s.page]) {
> > +		if (pmbus_check_word_register(client, s.page, PMBUS_MFR_VOUT_MIN))
> > +			s.data = _pmbus_read_word_data(client, s.page, 0xff,
> > +						       PMBUS_MFR_VOUT_MIN);
> > +		if (s.data < 0) {
> > +			s.data = _pmbus_read_word_data(client, s.page, 0xff,
> > +						       PMBUS_VOUT_MARGIN_LOW);
> > +			if (s.data < 0)
> > +				return s.data;
> > +		}
> > +		data->vout_low[s.page] = pmbus_reg2data(data, &s);
> > +	}
> > +
> > +	if (!data->vout_high[s.page]) {
> > +		s.data = -1;
> > +		if (pmbus_check_word_register(client, s.page, PMBUS_MFR_VOUT_MAX))
> > +			s.data = _pmbus_read_word_data(client, s.page, 0xff,
> > +						       PMBUS_MFR_VOUT_MAX);
> > +		if (s.data < 0) {
> > +			s.data = _pmbus_read_word_data(client, s.page, 0xff,
> > +						       PMBUS_VOUT_MARGIN_HIGH);
> > +			if (s.data < 0)
> > +				return s.data;
> > +		}
> > +		data->vout_high[s.page] = pmbus_reg2data(data, &s);
> > +	}
> > +

Hi Guenter!
> 
> The code above is similar to the same code in the first patch. Please
> move it into a function (in the first patch).

Ok, I will do that. I think it will be one function for low_margin and
one for high_margin.

> 
> > +	if (val >= data->vout_low[s.page] && val <= data->vout_high[s.page])
> > +		return val * 1000; /* unit is uV */
> > +
> > +	return 0;
> 
> Other drivers return -EINVAL here. Should this be returned as well
> if rdev->desc->min_uV or rdev->desc->uV_step is 0, if selector
> is out of range, or if data->vout_low[s.page] / data->vout_high[s.page]
> is 0 ?

I will add some more checks. I will try to follow what the description
of list_voltages says in include/linux/regulator/driver.h, but it's a
bit free for interpretation, thinking of what counts as unusable
voltages :)

* @list_voltage: Return one of the supported voltages, in microvolts; zero
*	if the selector indicates a voltage that is unusable on this system;
*	or negative errno.  Selectors range from zero to one less than
*	regulator_desc.n_voltages.  Voltages may be reported in any order.

I guess, as long as a voltage is inside of the low/high margins it is a
valid voltage (even though high and/or low is 0).

Kind regards
Mårten

> 
> Thanks,
> Guenter
> 
> > +}
> > +
> >   const struct regulator_ops pmbus_regulator_ops = {
> >   	.enable = pmbus_regulator_enable,
> >   	.disable = pmbus_regulator_disable,
> > @@ -2718,6 +2767,7 @@ const struct regulator_ops pmbus_regulator_ops = {
> >   	.get_error_flags = pmbus_regulator_get_error_flags,
> >   	.get_voltage = pmbus_regulator_get_voltage,
> >   	.set_voltage = pmbus_regulator_set_voltage,
> > +	.list_voltage = pmbus_regulator_list_voltage,
> >   };
> >   EXPORT_SYMBOL_NS_GPL(pmbus_regulator_ops, PMBUS);
> >   
> 

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

* Re: [PATCH v2 3/3] hwmon: (pmbus/ltc2978) Set voltage resolution
  2022-06-10 14:27   ` Guenter Roeck
@ 2022-06-13 20:34     ` Marten Lindahl
  0 siblings, 0 replies; 8+ messages in thread
From: Marten Lindahl @ 2022-06-13 20:34 UTC (permalink / raw)
  To: Guenter Roeck; +Cc: Mårten Lindahl, Jean Delvare, linux-hwmon, kernel

On Fri, Jun 10, 2022 at 04:27:55PM +0200, Guenter Roeck wrote:
> On 6/10/22 04:47, Mårten Lindahl wrote:
> > The LTC2977 regulator does not set the regulator_desc .n_voltages value
> > which is needed in order to let the regulator core list the regulator
> > voltage range.
> > 
> > This patch defines a regulator_desc with a voltage range, and uses it
> > for defining voltage resolution for regulators LTC2972/LTC2974/LTC2975/
> > LTC2977/LTC2978/LTC2979/LTC2980/LTM2987 based on that they all have a 16
> > bit ADC with the same stepwise 122.07uV resolution. It also scales the
> > resolution to a 1mV resolution which is easier to handle.
> > 
> > Signed-off-by: Mårten Lindahl <marten.lindahl@axis.com>
> > ---
> >   drivers/hwmon/pmbus/ltc2978.c | 56 ++++++++++++++++++++++++++++++++---
> >   1 file changed, 52 insertions(+), 4 deletions(-)
> > 
> > diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c
> > index 531aa674a928..7d44e64c61c1 100644
> > --- a/drivers/hwmon/pmbus/ltc2978.c
> > +++ b/drivers/hwmon/pmbus/ltc2978.c
> > @@ -562,7 +562,36 @@ static const struct i2c_device_id ltc2978_id[] = {
> >   MODULE_DEVICE_TABLE(i2c, ltc2978_id);
> >   
> >   #if IS_ENABLED(CONFIG_SENSORS_LTC2978_REGULATOR)
> > +#define LTC2978_ADC_RES	0xFFFF
> > +#define LTC2978_N_ADC	122
> > +#define LTC2978_MAX_UV	(LTC2978_ADC_RES * LTC2978_N_ADC)
> > +#define LTC2978_UV_STEP	1000
> > +
> > +#define PMBUS_LTC2978_REGULATOR(_name, _id)               \
> > +	[_id] = {                                               \
> > +		.name = (_name # _id),                                \
> 
> This needs 'supply_name = "vin"'. See commit 54cc3dbfc10dc ("hwmon:
> (pmbus) Add regulator supply into macro").
> 
> > +		.id = (_id),                                          \
> > +		.of_match = of_match_ptr(_name # _id),                \
> > +		.regulators_node = of_match_ptr("regulators"),        \
> > +		.ops = &pmbus_regulator_ops,                          \
> > +		.type = REGULATOR_VOLTAGE,                            \
> > +		.owner = THIS_MODULE,                                 \
> > +		.n_voltages = (LTC2978_MAX_UV / LTC2978_UV_STEP) + 1, \
> > +		.uV_step = LTC2978_UV_STEP,                           \
> > +	}
> 
> Please introduce a new generic macro PMBUS_REGULATOR_STEP()
> with additional parameters n_voltages and uV_step and use it here.
> PMBUS_REGULATOR() can then be redefined as
> 
> #define PMBUS_REGULATOR(n, i)	PMBUS_REGULATOR_STEP(n, i, 0, 0)

Hi Guenter!

Yes, that's a good idea. I will do that, and then there wont be any risk
of missing attributes like this new 'supply_name' as it is only defined at
one place.

Kind regards
Mårten

> 
> Thanks,
> Guenter
> 
> > +
> >   static const struct regulator_desc ltc2978_reg_desc[] = {
> > +	PMBUS_LTC2978_REGULATOR("vout", 0),
> > +	PMBUS_LTC2978_REGULATOR("vout", 1),
> > +	PMBUS_LTC2978_REGULATOR("vout", 2),
> > +	PMBUS_LTC2978_REGULATOR("vout", 3),
> > +	PMBUS_LTC2978_REGULATOR("vout", 4),
> > +	PMBUS_LTC2978_REGULATOR("vout", 5),
> > +	PMBUS_LTC2978_REGULATOR("vout", 6),
> > +	PMBUS_LTC2978_REGULATOR("vout", 7),
> > +};
> > +
> > +static const struct regulator_desc ltc2978_reg_desc_default[] = {
> >   	PMBUS_REGULATOR("vout", 0),
> >   	PMBUS_REGULATOR("vout", 1),
> >   	PMBUS_REGULATOR("vout", 2),
> > @@ -839,10 +868,29 @@ static int ltc2978_probe(struct i2c_client *client)
> >   
> >   #if IS_ENABLED(CONFIG_SENSORS_LTC2978_REGULATOR)
> >   	info->num_regulators = info->pages;
> > -	info->reg_desc = ltc2978_reg_desc;
> > -	if (info->num_regulators > ARRAY_SIZE(ltc2978_reg_desc)) {
> > -		dev_err(&client->dev, "num_regulators too large!");
> > -		info->num_regulators = ARRAY_SIZE(ltc2978_reg_desc);
> > +	switch (data->id) {
> > +	case ltc2972:
> > +	case ltc2974:
> > +	case ltc2975:
> > +	case ltc2977:
> > +	case ltc2978:
> > +	case ltc2979:
> > +	case ltc2980:
> > +	case ltm2987:
> > +		info->reg_desc = ltc2978_reg_desc;
> > +		if (info->num_regulators > ARRAY_SIZE(ltc2978_reg_desc)) {
> > +			dev_warn(&client->dev, "num_regulators too large!");
> > +			info->num_regulators = ARRAY_SIZE(ltc2978_reg_desc);
> > +		}
> > +		break;
> > +	default:
> > +		info->reg_desc = ltc2978_reg_desc_default;
> > +		if (info->num_regulators > ARRAY_SIZE(ltc2978_reg_desc_default)) {
> > +			dev_warn(&client->dev, "num_regulators too large!");
> > +			info->num_regulators =
> > +			    ARRAY_SIZE(ltc2978_reg_desc_default);
> > +		}
> > +		break;
> >   	}
> >   #endif
> >   
> 

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

end of thread, other threads:[~2022-06-13 21:00 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-06-10 11:47 [PATCH v2 0/3] hwmon: (pmbus/ltc2978) Set voltage resolution Mårten Lindahl
2022-06-10 11:47 ` [PATCH v2 1/3] hwmon: (pmbus) Introduce and use cached vout margins Mårten Lindahl
2022-06-10 11:47 ` [PATCH v2 2/3] hwmon: (pmbus) Add list_voltage to pmbus ops Mårten Lindahl
2022-06-10 14:16   ` Guenter Roeck
2022-06-13 20:27     ` Marten Lindahl
2022-06-10 11:47 ` [PATCH v2 3/3] hwmon: (pmbus/ltc2978) Set voltage resolution Mårten Lindahl
2022-06-10 14:27   ` Guenter Roeck
2022-06-13 20:34     ` Marten Lindahl

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).