[PATCH 5/7] power: supply: axp20x_usb_power: Add support for the AXP2101
From: Olivier Louvignes
Date: Fri Oct 23 2026 - 06:05:47 EST
Report whether VBUS is present (bit 5 of the first status register) and
the input current limit (100 mA to 2 A, in IIN_LIM), and update on the
VBUS plug-in and removal interrupts. Nothing is written: the AXP2101's
VBUS input needs no set-up for that.
Tested on a HiBy R1: unplugging and replugging the cable gives one
VBUS_REMOVAL and one VBUS_PLUGIN interrupt, and "online" goes 1, 0, 1.
Signed-off-by: Olivier Louvignes <olivier@xxxxxxxxx>
---
drivers/power/supply/axp20x_usb_power.c | 86 +++++++++++++++++++++++++
1 file changed, 86 insertions(+)
diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c
index e951b2b64b25..594f0492b18a 100644
--- a/drivers/power/supply/axp20x_usb_power.c
+++ b/drivers/power/supply/axp20x_usb_power.c
@@ -31,6 +31,7 @@
#define AXP20X_PWR_STATUS_VBUS_USED BIT(4)
#define AXP717_PWR_STATUS_VBUS_GOOD BIT(5)
+#define AXP2101_PWR_STATUS_VBUS_GOOD BIT(5)
#define AXP20X_USB_STATUS_VBUS_VALID BIT(2)
@@ -172,6 +173,24 @@ static void axp717_usb_power_poll_vbus(struct work_struct *work)
power->old_status = val;
}
+static void axp2101_usb_power_poll_vbus(struct work_struct *work)
+{
+ struct axp20x_usb_power *power =
+ container_of(work, struct axp20x_usb_power, vbus_detect.work);
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(power->regmap, AXP2101_PMU_STATUS_1, &val);
+ if (ret)
+ return;
+
+ val &= AXP2101_PWR_STATUS_VBUS_GOOD;
+ if (val != power->old_status)
+ power_supply_changed(power->supply);
+
+ power->old_status = val;
+}
+
static int axp20x_get_usb_type(struct axp20x_usb_power *power,
union power_supply_propval *val)
{
@@ -315,6 +334,42 @@ static int axp20x_usb_power_get_property(struct power_supply *psy,
return 0;
}
+/* Input current limits of AXP2101_IIN_LIM; 6 and 7 are reserved. */
+static const int axp2101_usb_curr_lim_table[] = {
+ 100000, 500000, 900000, 1000000, 1500000, 2000000,
+};
+
+static int axp2101_usb_power_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct axp20x_usb_power *power = power_supply_get_drvdata(psy);
+ unsigned int v;
+ int ret;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
+ ret = regmap_field_read(power->curr_lim_fld, &v);
+ if (ret)
+ return ret;
+ if (v >= ARRAY_SIZE(axp2101_usb_curr_lim_table))
+ return -EINVAL;
+ val->intval = axp2101_usb_curr_lim_table[v];
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ case POWER_SUPPLY_PROP_PRESENT:
+ ret = regmap_read(power->regmap, AXP2101_PMU_STATUS_1, &v);
+ if (ret)
+ return ret;
+ val->intval = !!(v & AXP2101_PWR_STATUS_VBUS_GOOD);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int axp717_usb_power_get_property(struct power_supply *psy,
enum power_supply_property psp, union power_supply_propval *val)
{
@@ -643,6 +698,12 @@ static enum power_supply_property axp22x_usb_power_properties[] = {
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
};
+static enum power_supply_property axp2101_usb_power_properties[] = {
+ POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_PRESENT,
+};
+
static enum power_supply_property axp717_usb_power_properties[] = {
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
@@ -682,6 +743,14 @@ static const struct power_supply_desc axp22x_usb_power_desc = {
.set_property = axp20x_usb_power_set_property,
};
+static const struct power_supply_desc axp2101_usb_power_desc = {
+ .name = "axp20x-usb",
+ .type = POWER_SUPPLY_TYPE_USB,
+ .properties = axp2101_usb_power_properties,
+ .num_properties = ARRAY_SIZE(axp2101_usb_power_properties),
+ .get_property = axp2101_usb_power_get_property,
+};
+
static const struct power_supply_desc axp717_usb_power_desc = {
.name = "axp20x-usb",
.type = POWER_SUPPLY_TYPE_USB,
@@ -722,6 +791,11 @@ static const char * const axp22x_irq_names[] = {
"VBUS_REMOVAL",
};
+static const char * const axp2101_irq_names[] = {
+ "VBUS_PLUGIN",
+ "VBUS_REMOVAL",
+};
+
static const char * const axp717_irq_names[] = {
"VBUS_PLUGIN",
"VBUS_REMOVAL",
@@ -815,6 +889,15 @@ static const struct axp_data axp223_data = {
.axp20x_cfg_adc_reg = axp20x_configure_adc_registers,
};
+static const struct axp_data axp2101_data = {
+ .power_desc = &axp2101_usb_power_desc,
+ .irq_names = axp2101_irq_names,
+ .num_irq_names = ARRAY_SIZE(axp2101_irq_names),
+ .curr_lim_fld = REG_FIELD(AXP2101_IIN_LIM, 0, 2),
+ .vbus_needs_polling = false,
+ .axp20x_read_vbus = &axp2101_usb_power_poll_vbus,
+};
+
static const struct axp_data axp717_data = {
.power_desc = &axp717_usb_power_desc,
.irq_names = axp717_irq_names,
@@ -1056,6 +1139,9 @@ static const struct of_device_id axp20x_usb_power_match[] = {
}, {
.compatible = "x-powers,axp717-usb-power-supply",
.data = &axp717_data,
+ }, {
+ .compatible = "x-powers,axp2101-usb-power-supply",
+ .data = &axp2101_data,
}, {
.compatible = "x-powers,axp813-usb-power-supply",
.data = &axp813_data,
--
2.39.5