2024-01-21 01:44:26

by Aren

[permalink] [raw]
Subject: [PATCH 1/3] power: supply: axp20x_usb_power: add input current limit

Add properties for setting the maximum current that will be drawn from
the usb connection.

These changes don't apply to all axp20x chips, so we need to add new
power_desc and power supply property objects for axp813 specifically.
These are copied from the axp22x variants that were used before, with
extra fields added.

Also add a dev field to the axp20x_usb_power struct, so we can use
dev_dbg and dev_err in more places.

Signed-off-by: Aren Moynihan <[email protected]>
---

drivers/power/supply/axp20x_usb_power.c | 127 +++++++++++++++++++++++-
1 file changed, 125 insertions(+), 2 deletions(-)

diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c
index e23308ad4cc7..8c0c2c25565f 100644
--- a/drivers/power/supply/axp20x_usb_power.c
+++ b/drivers/power/supply/axp20x_usb_power.c
@@ -50,7 +50,10 @@ struct axp_data {
const char * const *irq_names;
unsigned int num_irq_names;
const int *curr_lim_table;
+ int input_curr_lim_table_size;
+ const int *input_curr_lim_table;
struct reg_field curr_lim_fld;
+ struct reg_field input_curr_lim_fld;
struct reg_field vbus_valid_bit;
struct reg_field vbus_mon_bit;
struct reg_field usb_bc_en_bit;
@@ -59,7 +62,9 @@ struct axp_data {
};

struct axp20x_usb_power {
+ struct device *dev;
struct regmap *regmap;
+ struct regmap_field *input_curr_lim_fld;
struct regmap_field *curr_lim_fld;
struct regmap_field *vbus_valid_bit;
struct regmap_field *vbus_mon_bit;
@@ -115,6 +120,15 @@ static void axp20x_usb_power_poll_vbus(struct work_struct *work)
if (val != power->old_status)
power_supply_changed(power->supply);

+ if (power->usb_bc_en_bit && (val & AXP20X_PWR_STATUS_VBUS_PRESENT) !=
+ (power->old_status & AXP20X_PWR_STATUS_VBUS_PRESENT)) {
+ dev_dbg(power->dev, "Cable status changed, re-enabling USB BC");
+ ret = regmap_field_write(power->usb_bc_en_bit, 1);
+ if (ret)
+ dev_err(power->dev, "failed to enable USB BC: errno %d",
+ ret);
+ }
+
power->old_status = val;
power->online = val & AXP20X_PWR_STATUS_VBUS_USED;

@@ -123,6 +137,66 @@ static void axp20x_usb_power_poll_vbus(struct work_struct *work)
mod_delayed_work(system_power_efficient_wq, &power->vbus_detect, DEBOUNCE_TIME);
}

+static int
+axp20x_usb_power_set_input_current_limit(struct axp20x_usb_power *power,
+ int limit)
+{
+ int ret;
+ unsigned int reg;
+
+ if (!power->axp_data->input_curr_lim_table)
+ return -EINVAL;
+
+ if (limit < power->axp_data->input_curr_lim_table[0])
+ return -EINVAL;
+
+ /*
+ * BC1.2 detection can cause a race condition if we try to set a current
+ * limit while it's in progress. When it finishes it will overwrite the
+ * current limit we just set.
+ */
+ if (power->usb_bc_en_bit) {
+ dev_dbg(power->dev,
+ "disabling BC1.2 detection because current limit was set");
+ ret = regmap_field_write(power->usb_bc_en_bit, 0);
+ if (ret)
+ return ret;
+ }
+
+ for (reg = power->axp_data->input_curr_lim_table_size - 1; reg > 0; reg--) {
+ if (limit >= power->axp_data->input_curr_lim_table[reg])
+ break;
+ }
+
+ dev_dbg(power->dev, "setting input current limit reg to %d (%d uA), requested %d uA",
+ reg, power->axp_data->input_curr_lim_table[reg], limit);
+
+ return regmap_field_write(power->input_curr_lim_fld, reg);
+}
+
+static int
+axp20x_usb_power_get_input_current_limit(struct axp20x_usb_power *power,
+ int *intval)
+{
+ int ret;
+ unsigned int v;
+
+ if (!power->axp_data->input_curr_lim_table)
+ return -EINVAL;
+
+ ret = regmap_field_read(power->input_curr_lim_fld, &v);
+ if (ret)
+ return ret;
+
+ if (v < power->axp_data->input_curr_lim_table_size)
+ *intval = power->axp_data->input_curr_lim_table[v];
+ else
+ *intval = power->axp_data->input_curr_lim_table[
+ power->axp_data->input_curr_lim_table_size - 1];
+
+ return 0;
+}
+
static int axp20x_usb_power_get_property(struct power_supply *psy,
enum power_supply_property psp, union power_supply_propval *val)
{
@@ -189,6 +263,9 @@ static int axp20x_usb_power_get_property(struct power_supply *psy,

val->intval = ret * 375; /* 1 step = 0.375 mA */
return 0;
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+ return axp20x_usb_power_get_input_current_limit(power,
+ &val->intval);
default:
break;
}
@@ -290,6 +367,10 @@ static int axp20x_usb_power_set_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_CURRENT_MAX:
return axp20x_usb_power_set_current_max(power, val->intval);

+ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+ return axp20x_usb_power_set_input_current_limit(power,
+ val->intval);
+
default:
return -EINVAL;
}
@@ -313,7 +394,8 @@ static int axp20x_usb_power_prop_writeable(struct power_supply *psy,
return power->vbus_disable_bit != NULL;

return psp == POWER_SUPPLY_PROP_VOLTAGE_MIN ||
- psp == POWER_SUPPLY_PROP_CURRENT_MAX;
+ psp == POWER_SUPPLY_PROP_CURRENT_MAX ||
+ psp == POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT;
}

static enum power_supply_property axp20x_usb_power_properties[] = {
@@ -334,6 +416,15 @@ static enum power_supply_property axp22x_usb_power_properties[] = {
POWER_SUPPLY_PROP_CURRENT_MAX,
};

+static enum power_supply_property axp813_usb_power_properties[] = {
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN,
+ POWER_SUPPLY_PROP_CURRENT_MAX,
+ POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
+};
+
static const struct power_supply_desc axp20x_usb_power_desc = {
.name = "axp20x-usb",
.type = POWER_SUPPLY_TYPE_USB,
@@ -354,6 +445,16 @@ static const struct power_supply_desc axp22x_usb_power_desc = {
.set_property = axp20x_usb_power_set_property,
};

+static const struct power_supply_desc axp813_usb_power_desc = {
+ .name = "axp20x-usb",
+ .type = POWER_SUPPLY_TYPE_USB,
+ .properties = axp813_usb_power_properties,
+ .num_properties = ARRAY_SIZE(axp813_usb_power_properties),
+ .property_is_writeable = axp20x_usb_power_prop_writeable,
+ .get_property = axp20x_usb_power_get_property,
+ .set_property = axp20x_usb_power_set_property,
+};
+
static const char * const axp20x_irq_names[] = {
"VBUS_PLUGIN",
"VBUS_REMOVAL",
@@ -394,6 +495,18 @@ static int axp813_usb_curr_lim_table[] = {
2500000,
};

+static int axp_813_usb_input_curr_lim_table[] = {
+ 100000,
+ 500000,
+ 900000,
+ 1500000,
+ 2000000,
+ 2500000,
+ 3000000,
+ 3500000,
+ 4000000,
+};
+
static const struct axp_data axp192_data = {
.power_desc = &axp20x_usb_power_desc,
.irq_names = axp20x_irq_names,
@@ -433,11 +546,14 @@ static const struct axp_data axp223_data = {
};

static const struct axp_data axp813_data = {
- .power_desc = &axp22x_usb_power_desc,
+ .power_desc = &axp813_usb_power_desc,
.irq_names = axp22x_irq_names,
.num_irq_names = ARRAY_SIZE(axp22x_irq_names),
.curr_lim_table = axp813_usb_curr_lim_table,
.curr_lim_fld = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 0, 1),
+ .input_curr_lim_table_size = ARRAY_SIZE(axp_813_usb_input_curr_lim_table),
+ .input_curr_lim_table = axp_813_usb_input_curr_lim_table,
+ .input_curr_lim_fld = REG_FIELD(AXP22X_CHRG_CTRL3, 4, 7),
.usb_bc_en_bit = REG_FIELD(AXP288_BC_GLOBAL, 0, 0),
.vbus_disable_bit = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 7, 7),
.vbus_needs_polling = true,
@@ -558,6 +674,7 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)

platform_set_drvdata(pdev, power);

+ power->dev = &pdev->dev;
power->axp_data = axp_data;
power->regmap = axp20x->regmap;
power->num_irqs = axp_data->num_irq_names;
@@ -567,6 +684,12 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
if (IS_ERR(power->curr_lim_fld))
return PTR_ERR(power->curr_lim_fld);

+ ret = axp20x_regmap_field_alloc_optional(&pdev->dev, power->regmap,
+ axp_data->input_curr_lim_fld,
+ &power->input_curr_lim_fld);
+ if (ret)
+ return ret;
+
ret = axp20x_regmap_field_alloc_optional(&pdev->dev, power->regmap,
axp_data->vbus_valid_bit,
&power->vbus_valid_bit);
--
2.43.0



2024-01-21 11:39:09

by Ondřej Jirman

[permalink] [raw]
Subject: Re: [PATCH 1/3] power: supply: axp20x_usb_power: add input current limit

Hello Aren,

On Sat, Jan 20, 2024 at 08:40:00PM -0500, Aren Moynihan wrote:
> Add properties for setting the maximum current that will be drawn from
> the usb connection.
>
> These changes don't apply to all axp20x chips, so we need to add new
> power_desc and power supply property objects for axp813 specifically.
> These are copied from the axp22x variants that were used before, with
> extra fields added.
>
> Also add a dev field to the axp20x_usb_power struct, so we can use
> dev_dbg and dev_err in more places.
>
> Signed-off-by: Aren Moynihan <[email protected]>
> ---
>
> drivers/power/supply/axp20x_usb_power.c | 127 +++++++++++++++++++++++-
> 1 file changed, 125 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c
> index e23308ad4cc7..8c0c2c25565f 100644
> --- a/drivers/power/supply/axp20x_usb_power.c
> +++ b/drivers/power/supply/axp20x_usb_power.c
> @@ -50,7 +50,10 @@ struct axp_data {
> const char * const *irq_names;
> unsigned int num_irq_names;
> const int *curr_lim_table;
> + int input_curr_lim_table_size;
> + const int *input_curr_lim_table;
> struct reg_field curr_lim_fld;
> + struct reg_field input_curr_lim_fld;
> struct reg_field vbus_valid_bit;
> struct reg_field vbus_mon_bit;
> struct reg_field usb_bc_en_bit;
> @@ -59,7 +62,9 @@ struct axp_data {
> };
>
> struct axp20x_usb_power {
> + struct device *dev;
> struct regmap *regmap;
> + struct regmap_field *input_curr_lim_fld;
> struct regmap_field *curr_lim_fld;
> struct regmap_field *vbus_valid_bit;
> struct regmap_field *vbus_mon_bit;
> @@ -115,6 +120,15 @@ static void axp20x_usb_power_poll_vbus(struct work_struct *work)
> if (val != power->old_status)
> power_supply_changed(power->supply);
>
> + if (power->usb_bc_en_bit && (val & AXP20X_PWR_STATUS_VBUS_PRESENT) !=
> + (power->old_status & AXP20X_PWR_STATUS_VBUS_PRESENT)) {
> + dev_dbg(power->dev, "Cable status changed, re-enabling USB BC");
> + ret = regmap_field_write(power->usb_bc_en_bit, 1);
> + if (ret)
> + dev_err(power->dev, "failed to enable USB BC: errno %d",
> + ret);
> + }
> +
> power->old_status = val;
> power->online = val & AXP20X_PWR_STATUS_VBUS_USED;
>
> @@ -123,6 +137,66 @@ static void axp20x_usb_power_poll_vbus(struct work_struct *work)
> mod_delayed_work(system_power_efficient_wq, &power->vbus_detect, DEBOUNCE_TIME);
> }
>
> +static int
> +axp20x_usb_power_set_input_current_limit(struct axp20x_usb_power *power,
> + int limit)
> +{
> + int ret;
> + unsigned int reg;
> +
> + if (!power->axp_data->input_curr_lim_table)
> + return -EINVAL;
> +
> + if (limit < power->axp_data->input_curr_lim_table[0])
> + return -EINVAL;

I think that you should just set the lowest possible limit. A caller (calling
driver or userspace or user) has no way to identify what is the lowest possible
limit on arbitrary PMIC and I kinda like having an option to
echo 0 > .../axp20x-usb/input_current_limit as a way to limit power consumption from
USB to a minimum without having to look up what actual minimum is in various
PMICs dataheets.

I looked through most of the uses of POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT in
the upstream drivers and both approaches are used. Erroring out when out of range
is less common.

Otherwise,

Reviewed-By: Ondrej Jirman <[email protected]>

Thank you,
Ondrej

2024-01-21 15:25:32

by Chen-Yu Tsai

[permalink] [raw]
Subject: Re: [PATCH 1/3] power: supply: axp20x_usb_power: add input current limit

Hi,

+CC Hans de Goede since the AXP288 is similar to the later AXPs in the USB
power section.

On Sun, Jan 21, 2024 at 9:54 AM Aren Moynihan <[email protected]> wrote:
>
> Add properties for setting the maximum current that will be drawn from
> the usb connection.
>
> These changes don't apply to all axp20x chips, so we need to add new
> power_desc and power supply property objects for axp813 specifically.
> These are copied from the axp22x variants that were used before, with
> extra fields added.
>
> Also add a dev field to the axp20x_usb_power struct, so we can use
> dev_dbg and dev_err in more places.

I think this patch highlights some errors in the driver.

1. We are likely misusing POWER_SUPPLY_PROP_CURRENT_MAX. Based on the
ABI docs, this is supposed to be a read only property. The correct
way would be to use POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT as you did.

2. For AXP288 and AXP803/AXP813/AXP818, we are setting the current limit
for when BC detection finishes in register 0x30, not the actual active
limit in register 0x35. This should be fixed.

3. BC detection race condition

Could you maybe respin the patches to address them separately instead?


Thanks
ChenYu



> Signed-off-by: Aren Moynihan <[email protected]>
> ---
>
> drivers/power/supply/axp20x_usb_power.c | 127 +++++++++++++++++++++++-
> 1 file changed, 125 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c
> index e23308ad4cc7..8c0c2c25565f 100644
> --- a/drivers/power/supply/axp20x_usb_power.c
> +++ b/drivers/power/supply/axp20x_usb_power.c
> @@ -50,7 +50,10 @@ struct axp_data {
> const char * const *irq_names;
> unsigned int num_irq_names;
> const int *curr_lim_table;
> + int input_curr_lim_table_size;
> + const int *input_curr_lim_table;
> struct reg_field curr_lim_fld;
> + struct reg_field input_curr_lim_fld;
> struct reg_field vbus_valid_bit;
> struct reg_field vbus_mon_bit;
> struct reg_field usb_bc_en_bit;
> @@ -59,7 +62,9 @@ struct axp_data {
> };
>
> struct axp20x_usb_power {
> + struct device *dev;
> struct regmap *regmap;
> + struct regmap_field *input_curr_lim_fld;
> struct regmap_field *curr_lim_fld;
> struct regmap_field *vbus_valid_bit;
> struct regmap_field *vbus_mon_bit;
> @@ -115,6 +120,15 @@ static void axp20x_usb_power_poll_vbus(struct work_struct *work)
> if (val != power->old_status)
> power_supply_changed(power->supply);
>
> + if (power->usb_bc_en_bit && (val & AXP20X_PWR_STATUS_VBUS_PRESENT) !=
> + (power->old_status & AXP20X_PWR_STATUS_VBUS_PRESENT)) {
> + dev_dbg(power->dev, "Cable status changed, re-enabling USB BC");
> + ret = regmap_field_write(power->usb_bc_en_bit, 1);
> + if (ret)
> + dev_err(power->dev, "failed to enable USB BC: errno %d",
> + ret);
> + }
> +
> power->old_status = val;
> power->online = val & AXP20X_PWR_STATUS_VBUS_USED;
>
> @@ -123,6 +137,66 @@ static void axp20x_usb_power_poll_vbus(struct work_struct *work)
> mod_delayed_work(system_power_efficient_wq, &power->vbus_detect, DEBOUNCE_TIME);
> }
>
> +static int
> +axp20x_usb_power_set_input_current_limit(struct axp20x_usb_power *power,
> + int limit)
> +{
> + int ret;
> + unsigned int reg;
> +
> + if (!power->axp_data->input_curr_lim_table)
> + return -EINVAL;
> +
> + if (limit < power->axp_data->input_curr_lim_table[0])
> + return -EINVAL;
> +
> + /*
> + * BC1.2 detection can cause a race condition if we try to set a current
> + * limit while it's in progress. When it finishes it will overwrite the
> + * current limit we just set.
> + */
> + if (power->usb_bc_en_bit) {
> + dev_dbg(power->dev,
> + "disabling BC1.2 detection because current limit was set");
> + ret = regmap_field_write(power->usb_bc_en_bit, 0);
> + if (ret)
> + return ret;
> + }
> +
> + for (reg = power->axp_data->input_curr_lim_table_size - 1; reg > 0; reg--) {
> + if (limit >= power->axp_data->input_curr_lim_table[reg])
> + break;
> + }
> +
> + dev_dbg(power->dev, "setting input current limit reg to %d (%d uA), requested %d uA",
> + reg, power->axp_data->input_curr_lim_table[reg], limit);
> +
> + return regmap_field_write(power->input_curr_lim_fld, reg);
> +}
> +
> +static int
> +axp20x_usb_power_get_input_current_limit(struct axp20x_usb_power *power,
> + int *intval)
> +{
> + int ret;
> + unsigned int v;
> +
> + if (!power->axp_data->input_curr_lim_table)
> + return -EINVAL;
> +
> + ret = regmap_field_read(power->input_curr_lim_fld, &v);
> + if (ret)
> + return ret;
> +
> + if (v < power->axp_data->input_curr_lim_table_size)
> + *intval = power->axp_data->input_curr_lim_table[v];
> + else
> + *intval = power->axp_data->input_curr_lim_table[
> + power->axp_data->input_curr_lim_table_size - 1];
> +
> + return 0;
> +}
> +
> static int axp20x_usb_power_get_property(struct power_supply *psy,
> enum power_supply_property psp, union power_supply_propval *val)
> {
> @@ -189,6 +263,9 @@ static int axp20x_usb_power_get_property(struct power_supply *psy,
>
> val->intval = ret * 375; /* 1 step = 0.375 mA */
> return 0;
> + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
> + return axp20x_usb_power_get_input_current_limit(power,
> + &val->intval);
> default:
> break;
> }
> @@ -290,6 +367,10 @@ static int axp20x_usb_power_set_property(struct power_supply *psy,
> case POWER_SUPPLY_PROP_CURRENT_MAX:
> return axp20x_usb_power_set_current_max(power, val->intval);
>
> + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
> + return axp20x_usb_power_set_input_current_limit(power,
> + val->intval);
> +
> default:
> return -EINVAL;
> }
> @@ -313,7 +394,8 @@ static int axp20x_usb_power_prop_writeable(struct power_supply *psy,
> return power->vbus_disable_bit != NULL;
>
> return psp == POWER_SUPPLY_PROP_VOLTAGE_MIN ||
> - psp == POWER_SUPPLY_PROP_CURRENT_MAX;
> + psp == POWER_SUPPLY_PROP_CURRENT_MAX ||
> + psp == POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT;
> }
>
> static enum power_supply_property axp20x_usb_power_properties[] = {
> @@ -334,6 +416,15 @@ static enum power_supply_property axp22x_usb_power_properties[] = {
> POWER_SUPPLY_PROP_CURRENT_MAX,
> };
>
> +static enum power_supply_property axp813_usb_power_properties[] = {
> + POWER_SUPPLY_PROP_HEALTH,
> + POWER_SUPPLY_PROP_PRESENT,
> + POWER_SUPPLY_PROP_ONLINE,
> + POWER_SUPPLY_PROP_VOLTAGE_MIN,
> + POWER_SUPPLY_PROP_CURRENT_MAX,
> + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
> +};
> +
> static const struct power_supply_desc axp20x_usb_power_desc = {
> .name = "axp20x-usb",
> .type = POWER_SUPPLY_TYPE_USB,
> @@ -354,6 +445,16 @@ static const struct power_supply_desc axp22x_usb_power_desc = {
> .set_property = axp20x_usb_power_set_property,
> };
>
> +static const struct power_supply_desc axp813_usb_power_desc = {
> + .name = "axp20x-usb",
> + .type = POWER_SUPPLY_TYPE_USB,
> + .properties = axp813_usb_power_properties,
> + .num_properties = ARRAY_SIZE(axp813_usb_power_properties),
> + .property_is_writeable = axp20x_usb_power_prop_writeable,
> + .get_property = axp20x_usb_power_get_property,
> + .set_property = axp20x_usb_power_set_property,
> +};
> +
> static const char * const axp20x_irq_names[] = {
> "VBUS_PLUGIN",
> "VBUS_REMOVAL",
> @@ -394,6 +495,18 @@ static int axp813_usb_curr_lim_table[] = {
> 2500000,
> };
>
> +static int axp_813_usb_input_curr_lim_table[] = {
> + 100000,
> + 500000,
> + 900000,
> + 1500000,
> + 2000000,
> + 2500000,
> + 3000000,
> + 3500000,
> + 4000000,
> +};
> +
> static const struct axp_data axp192_data = {
> .power_desc = &axp20x_usb_power_desc,
> .irq_names = axp20x_irq_names,
> @@ -433,11 +546,14 @@ static const struct axp_data axp223_data = {
> };
>
> static const struct axp_data axp813_data = {
> - .power_desc = &axp22x_usb_power_desc,
> + .power_desc = &axp813_usb_power_desc,
> .irq_names = axp22x_irq_names,
> .num_irq_names = ARRAY_SIZE(axp22x_irq_names),
> .curr_lim_table = axp813_usb_curr_lim_table,
> .curr_lim_fld = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 0, 1),
> + .input_curr_lim_table_size = ARRAY_SIZE(axp_813_usb_input_curr_lim_table),
> + .input_curr_lim_table = axp_813_usb_input_curr_lim_table,
> + .input_curr_lim_fld = REG_FIELD(AXP22X_CHRG_CTRL3, 4, 7),
> .usb_bc_en_bit = REG_FIELD(AXP288_BC_GLOBAL, 0, 0),
> .vbus_disable_bit = REG_FIELD(AXP20X_VBUS_IPSOUT_MGMT, 7, 7),
> .vbus_needs_polling = true,
> @@ -558,6 +674,7 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
>
> platform_set_drvdata(pdev, power);
>
> + power->dev = &pdev->dev;
> power->axp_data = axp_data;
> power->regmap = axp20x->regmap;
> power->num_irqs = axp_data->num_irq_names;
> @@ -567,6 +684,12 @@ static int axp20x_usb_power_probe(struct platform_device *pdev)
> if (IS_ERR(power->curr_lim_fld))
> return PTR_ERR(power->curr_lim_fld);
>
> + ret = axp20x_regmap_field_alloc_optional(&pdev->dev, power->regmap,
> + axp_data->input_curr_lim_fld,
> + &power->input_curr_lim_fld);
> + if (ret)
> + return ret;
> +
> ret = axp20x_regmap_field_alloc_optional(&pdev->dev, power->regmap,
> axp_data->vbus_valid_bit,
> &power->vbus_valid_bit);
> --
> 2.43.0
>

2024-01-23 01:53:01

by Aren

[permalink] [raw]
Subject: Re: Re: [PATCH 1/3] power: supply: axp20x_usb_power: add input current limit

On Sun, Jan 21, 2024 at 11:25:04PM +0800, Chen-Yu Tsai wrote:
> Hi,
>
> +CC Hans de Goede since the AXP288 is similar to the later AXPs in the USB
> power section.
>
> On Sun, Jan 21, 2024 at 9:54 AM Aren Moynihan <[email protected]> wrote:
> >
> > Add properties for setting the maximum current that will be drawn from
> > the usb connection.
> >
> > These changes don't apply to all axp20x chips, so we need to add new
> > power_desc and power supply property objects for axp813 specifically.
> > These are copied from the axp22x variants that were used before, with
> > extra fields added.
> >
> > Also add a dev field to the axp20x_usb_power struct, so we can use
> > dev_dbg and dev_err in more places.
>
> I think this patch highlights some errors in the driver.
>
> 1. We are likely misusing POWER_SUPPLY_PROP_CURRENT_MAX. Based on the
> ABI docs, this is supposed to be a read only property. The correct
> way would be to use POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT as you did.

I think so, yes. I was hesitant to touch that because I wasn't sure if
current_max was relied on anywhere. I'll do some grepping to confirm
that it'll be safe.

> 2. For AXP288 and AXP803/AXP813/AXP818, we are setting the current limit
> for when BC detection finishes in register 0x30, not the actual active
> limit in register 0x35. This should be fixed.
>
> 3. BC detection race condition
>
> Could you maybe respin the patches to address them separately instead?

Sure, that sounds like a better way to split it up.

- Aren

> Thanks
> ChenYu

2024-01-23 02:39:27

by Aren

[permalink] [raw]
Subject: Re: Re: [PATCH 1/3] power: supply: axp20x_usb_power: add input current limit

On Sun, Jan 21, 2024 at 12:38:54PM +0100, Ondřej Jirman wrote:
> Hello Aren,
>
> On Sat, Jan 20, 2024 at 08:40:00PM -0500, Aren Moynihan wrote:
> > Add properties for setting the maximum current that will be drawn from
> > the usb connection.
> >
> > These changes don't apply to all axp20x chips, so we need to add new
> > power_desc and power supply property objects for axp813 specifically.
> > These are copied from the axp22x variants that were used before, with
> > extra fields added.
> >
> > Also add a dev field to the axp20x_usb_power struct, so we can use
> > dev_dbg and dev_err in more places.
> >
> > Signed-off-by: Aren Moynihan <[email protected]>
> > ---
> >
> > drivers/power/supply/axp20x_usb_power.c | 127 +++++++++++++++++++++++-
> > 1 file changed, 125 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c
> > index e23308ad4cc7..8c0c2c25565f 100644
> > --- a/drivers/power/supply/axp20x_usb_power.c
> > +++ b/drivers/power/supply/axp20x_usb_power.c
> > @@ -50,7 +50,10 @@ struct axp_data {
> > const char * const *irq_names;
> > unsigned int num_irq_names;
> > const int *curr_lim_table;
> > + int input_curr_lim_table_size;
> > + const int *input_curr_lim_table;
> > struct reg_field curr_lim_fld;
> > + struct reg_field input_curr_lim_fld;
> > struct reg_field vbus_valid_bit;
> > struct reg_field vbus_mon_bit;
> > struct reg_field usb_bc_en_bit;
> > @@ -59,7 +62,9 @@ struct axp_data {
> > };
> >
> > struct axp20x_usb_power {
> > + struct device *dev;
> > struct regmap *regmap;
> > + struct regmap_field *input_curr_lim_fld;
> > struct regmap_field *curr_lim_fld;
> > struct regmap_field *vbus_valid_bit;
> > struct regmap_field *vbus_mon_bit;
> > @@ -115,6 +120,15 @@ static void axp20x_usb_power_poll_vbus(struct work_struct *work)
> > if (val != power->old_status)
> > power_supply_changed(power->supply);
> >
> > + if (power->usb_bc_en_bit && (val & AXP20X_PWR_STATUS_VBUS_PRESENT) !=
> > + (power->old_status & AXP20X_PWR_STATUS_VBUS_PRESENT)) {
> > + dev_dbg(power->dev, "Cable status changed, re-enabling USB BC");
> > + ret = regmap_field_write(power->usb_bc_en_bit, 1);
> > + if (ret)
> > + dev_err(power->dev, "failed to enable USB BC: errno %d",
> > + ret);
> > + }
> > +
> > power->old_status = val;
> > power->online = val & AXP20X_PWR_STATUS_VBUS_USED;
> >
> > @@ -123,6 +137,66 @@ static void axp20x_usb_power_poll_vbus(struct work_struct *work)
> > mod_delayed_work(system_power_efficient_wq, &power->vbus_detect, DEBOUNCE_TIME);
> > }
> >
> > +static int
> > +axp20x_usb_power_set_input_current_limit(struct axp20x_usb_power *power,
> > + int limit)
> > +{
> > + int ret;
> > + unsigned int reg;
> > +
> > + if (!power->axp_data->input_curr_lim_table)
> > + return -EINVAL;
> > +
> > + if (limit < power->axp_data->input_curr_lim_table[0])
> > + return -EINVAL;
>
> I think that you should just set the lowest possible limit. A caller (calling
> driver or userspace or user) has no way to identify what is the lowest possible
> limit on arbitrary PMIC and I kinda like having an option to
> echo 0 > .../axp20x-usb/input_current_limit as a way to limit power consumption from
> USB to a minimum without having to look up what actual minimum is in various
> PMICs dataheets.
>
> I looked through most of the uses of POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT in
> the upstream drivers and both approaches are used. Erroring out when out of range
> is less common.

That should be pretty easy, I love a good reason to delete code :)

Thanks
- Aren

> Otherwise,
>
> Reviewed-By: Ondrej Jirman <[email protected]>
>
> Thank you,
> Ondrej