From: Aren Moynihan <aren@peacevolution.org>
To: linux-pm@vger.kernel.org, linux-kernel@vger.kernel.org
Cc: "Aidan MacDonald" <aidanmacdonald.0x0@gmail.com>,
"Ondřej Jirman" <megi@xff.cz>,
"Aren Moynihan" <aren@peacevolution.org>,
"Chen-Yu Tsai" <wens@csie.org>,
"Sebastian Reichel" <sre@kernel.org>
Subject: [PATCH 1/3] power: supply: axp20x_usb_power: add input current limit
Date: Sat, 20 Jan 2024 20:40:00 -0500 [thread overview]
Message-ID: <20240121014057.1042466-3-aren@peacevolution.org> (raw)
In-Reply-To: <20240121014057.1042466-1-aren@peacevolution.org>
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 <aren@peacevolution.org>
---
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
next prev parent reply other threads:[~2024-01-21 1:44 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-01-21 1:39 [PATCH 0/3] power: supply: axp20x_usb_power: prepare for external power delivery negotiation Aren Moynihan
2024-01-21 1:40 ` Aren Moynihan [this message]
2024-01-21 11:38 ` [PATCH 1/3] power: supply: axp20x_usb_power: add input current limit Ondřej Jirman
2024-01-23 1:50 ` Aren
2024-01-21 15:25 ` Chen-Yu Tsai
2024-01-23 0:52 ` Aren
2024-01-21 1:40 ` [PATCH 2/3] power: supply: axp20x_usb_power: enable usb_type reporting Aren Moynihan
2024-01-21 11:55 ` Ondřej Jirman
2024-01-23 2:41 ` Aren
2024-01-21 1:40 ` [PATCH 3/3] power: supply: axp20x_usb_power: set input current limit in probe Aren Moynihan
2024-01-21 11:08 ` Ondřej Jirman
2024-01-23 1:40 ` Aren
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20240121014057.1042466-3-aren@peacevolution.org \
--to=aren@peacevolution.org \
--cc=aidanmacdonald.0x0@gmail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pm@vger.kernel.org \
--cc=megi@xff.cz \
--cc=sre@kernel.org \
--cc=wens@csie.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®