diff --git a/MAINTAINERS b/MAINTAINERS
index f41088418aae..061e2de72de5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9464,6 +9464,11 @@ F:	include/linux/mfd/ingenic-tcu.h
 F:	sound/soc/codecs/jz47*
 F:	sound/soc/jz4740/
 
+INJOINIC IP5xxx POWER BANK IC DRIVER
+M:	Samuel Holland <samuel@sholland.org>
+S:	Maintained
+F:	drivers/power/supply/ip5xxx_power.c
+
 INOTIFY
 M:	Jan Kara <jack@suse.cz>
 R:	Amir Goldstein <amir73il@gmail.com>
diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
index b366e2fd8e97..539f66018ec4 100644
--- a/drivers/power/supply/Kconfig
+++ b/drivers/power/supply/Kconfig
@@ -51,6 +51,14 @@ config GENERIC_ADC_BATTERY
 	  Say Y here to enable support for the generic battery driver
 	  which uses IIO framework to read adc.
 
+config IP5XXX_POWER
+	tristate "Injoinic IP5xxx power bank IC driver"
+	depends on I2C
+	select REGMAP_I2C
+	help
+	  Say Y to include support for Injoinic IP5xxx power bank ICs,
+	  which include a battery charger and a boost converter.
+
 config MAX8925_POWER
 	tristate "MAX8925 battery charger support"
 	depends on MFD_MAX8925
diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile
index 2c1b264b2046..7a70c740324e 100644
--- a/drivers/power/supply/Makefile
+++ b/drivers/power/supply/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_GENERIC_ADC_BATTERY)	+= generic-adc-battery.o
 obj-$(CONFIG_PDA_POWER)		+= pda_power.o
 obj-$(CONFIG_APM_POWER)		+= apm_power.o
 obj-$(CONFIG_AXP20X_POWER)	+= axp20x_usb_power.o
+obj-$(CONFIG_IP5XXX_POWER)	+= ip5xxx_power.o
 obj-$(CONFIG_MAX8925_POWER)	+= max8925_power.o
 obj-$(CONFIG_WM831X_BACKUP)	+= wm831x_backup.o
 obj-$(CONFIG_WM831X_POWER)	+= wm831x_power.o
diff --git a/drivers/power/supply/ip5xxx_power.c b/drivers/power/supply/ip5xxx_power.c
new file mode 100644
index 000000000000..00fae16f6297
--- /dev/null
+++ b/drivers/power/supply/ip5xxx_power.c
@@ -0,0 +1,563 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (C) 2021 Samuel Holland <samuel@sholland.org>
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/power_supply.h>
+#include <linux/regmap.h>
+
+#define IP5XXX_SYS_CTL0			0x01
+#define IP5XXX_SYS_CTL0_WLED_DET_EN		BIT(4)
+#define IP5XXX_SYS_CTL0_WLED_EN			BIT(3)
+#define IP5XXX_SYS_CTL0_BOOST_EN		BIT(2)
+#define IP5XXX_SYS_CTL0_CHARGER_EN		BIT(1)
+#define IP5XXX_SYS_CTL1			0x02
+#define IP5XXX_SYS_CTL1_LIGHT_SHDN_EN		BIT(1)
+#define IP5XXX_SYS_CTL1_LOAD_PWRUP_EN		BIT(0)
+#define IP5XXX_SYS_CTL2			0x0c
+#define IP5XXX_SYS_CTL2_LIGHT_SHDN_TH		GENMASK(7, 3)
+#define IP5XXX_SYS_CTL3			0x03
+#define IP5XXX_SYS_CTL3_LONG_PRESS_TIME_SEL	GENMASK(7, 6)
+#define IP5XXX_SYS_CTL3_BTN_SHDN_EN		BIT(5)
+#define IP5XXX_SYS_CTL4			0x04
+#define IP5XXX_SYS_CTL4_SHDN_TIME_SEL		GENMASK(7, 6)
+#define IP5XXX_SYS_CTL4_VIN_PULLOUT_BOOST_EN	BIT(5)
+#define IP5XXX_SYS_CTL5			0x07
+#define IP5XXX_SYS_CTL5_NTC_DIS			BIT(6)
+#define IP5XXX_SYS_CTL5_WLED_MODE_SEL		BIT(1)
+#define IP5XXX_SYS_CTL5_BTN_SHDN_SEL		BIT(0)
+#define IP5XXX_CHG_CTL1			0x22
+#define IP5XXX_CHG_CTL1_BOOST_UVP_SEL		GENMASK(3, 2)
+#define IP5XXX_CHG_CTL2			0x24
+#define IP5XXX_CHG_CTL2_BAT_TYPE_SEL		GENMASK(6, 5)
+#define IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_2V	(0x0 << 5)
+#define IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_3V	(0x1 << 5)
+#define IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_35V	(0x2 << 5)
+#define IP5XXX_CHG_CTL2_CONST_VOLT_SEL		GENMASK(2, 1)
+#define IP5XXX_CHG_CTL4			0x26
+#define IP5XXX_CHG_CTL4_BAT_TYPE_SEL_EN		BIT(6)
+#define IP5XXX_CHG_CTL4A		0x25
+#define IP5XXX_CHG_CTL4A_CONST_CUR_SEL		GENMASK(4, 0)
+#define IP5XXX_MFP_CTL0			0x51
+#define IP5XXX_MFP_CTL1			0x52
+#define IP5XXX_GPIO_CTL2		0x53
+#define IP5XXX_GPIO_CTL2A		0x54
+#define IP5XXX_GPIO_CTL3		0x55
+#define IP5XXX_READ0			0x71
+#define IP5XXX_READ0_CHG_STAT			GENMASK(7, 5)
+#define IP5XXX_READ0_CHG_STAT_IDLE		(0x0 << 5)
+#define IP5XXX_READ0_CHG_STAT_TRICKLE		(0x1 << 5)
+#define IP5XXX_READ0_CHG_STAT_CONST_VOLT	(0x2 << 5)
+#define IP5XXX_READ0_CHG_STAT_CONST_CUR		(0x3 << 5)
+#define IP5XXX_READ0_CHG_STAT_CONST_VOLT_STOP	(0x4 << 5)
+#define IP5XXX_READ0_CHG_STAT_FULL		(0x5 << 5)
+#define IP5XXX_READ0_CHG_STAT_TIMEOUT		(0x6 << 5)
+#define IP5XXX_READ0_CHG_OP			BIT(4)
+#define IP5XXX_READ0_CHG_END			BIT(3)
+#define IP5XXX_READ0_CONST_VOLT_TIMEOUT		BIT(2)
+#define IP5XXX_READ0_CHG_TIMEOUT		BIT(1)
+#define IP5XXX_READ0_TRICKLE_TIMEOUT		BIT(0)
+#define IP5XXX_READ0_TIMEOUT			GENMASK(2, 0)
+#define IP5XXX_READ1			0x72
+#define IP5XXX_READ1_WLED_PRESENT		BIT(7)
+#define IP5XXX_READ1_LIGHT_LOAD			BIT(6)
+#define IP5XXX_READ1_VIN_OVERVOLT		BIT(5)
+#define IP5XXX_READ2			0x77
+#define IP5XXX_READ2_BTN_PRESS			BIT(3)
+#define IP5XXX_READ2_BTN_LONG_PRESS		BIT(1)
+#define IP5XXX_READ2_BTN_SHORT_PRESS		BIT(0)
+#define IP5XXX_BATVADC_DAT0		0xa2
+#define IP5XXX_BATVADC_DAT1		0xa3
+#define IP5XXX_BATIADC_DAT0		0xa4
+#define IP5XXX_BATIADC_DAT1		0xa5
+#define IP5XXX_BATOCV_DAT0		0xa8
+#define IP5XXX_BATOCV_DAT1		0xa9
+
+struct ip5xxx {
+	struct regmap *regmap;
+	bool initialized;
+};
+
+/*
+ * The IP5xxx charger only responds on I2C when it is "awake". The charger is
+ * generally only awake when VIN is powered or when its boost converter is
+ * enabled. Going into shutdown resets all register values. To handle this:
+ *  1) When any bus error occurs, assume the charger has gone into shutdown.
+ *  2) Attempt the initialization sequence on each subsequent register access
+ *     until it succeeds.
+ */
+static int ip5xxx_read(struct ip5xxx *ip5xxx, unsigned int reg,
+		       unsigned int *val)
+{
+	int ret;
+
+	ret = regmap_read(ip5xxx->regmap, reg, val);
+	if (ret)
+		ip5xxx->initialized = false;
+
+	return ret;
+}
+
+static int ip5xxx_update_bits(struct ip5xxx *ip5xxx, unsigned int reg,
+			      unsigned int mask, unsigned int val)
+{
+	int ret;
+
+	ret = regmap_update_bits(ip5xxx->regmap, reg, mask, val);
+	if (ret)
+		ip5xxx->initialized = false;
+
+	return ret;
+}
+
+static int ip5xxx_initialize(struct power_supply *psy)
+{
+	struct ip5xxx *ip5xxx = power_supply_get_drvdata(psy);
+	int ret;
+
+	if (ip5xxx->initialized)
+		return 0;
+
+	/*
+	 * Disable shutdown under light load.
+	 * Enable power on when under load.
+	 */
+	ret = ip5xxx_update_bits(ip5xxx, IP5XXX_SYS_CTL1,
+				 IP5XXX_SYS_CTL1_LIGHT_SHDN_EN |
+				 IP5XXX_SYS_CTL1_LOAD_PWRUP_EN,
+				 IP5XXX_SYS_CTL1_LOAD_PWRUP_EN);
+	if (ret)
+		return ret;
+
+	/*
+	 * Enable shutdown after a long button press (as configured below).
+	 */
+	ret = ip5xxx_update_bits(ip5xxx, IP5XXX_SYS_CTL3,
+				 IP5XXX_SYS_CTL3_BTN_SHDN_EN,
+				 IP5XXX_SYS_CTL3_BTN_SHDN_EN);
+	if (ret)
+		return ret;
+
+	/*
+	 * Power on automatically when VIN is removed.
+	 */
+	ret = ip5xxx_update_bits(ip5xxx, IP5XXX_SYS_CTL4,
+				 IP5XXX_SYS_CTL4_VIN_PULLOUT_BOOST_EN,
+				 IP5XXX_SYS_CTL4_VIN_PULLOUT_BOOST_EN);
+	if (ret)
+		return ret;
+
+	/*
+	 * Enable the NTC.
+	 * Configure the button for two presses => LED, long press => shutdown.
+	 */
+	ret = ip5xxx_update_bits(ip5xxx, IP5XXX_SYS_CTL5,
+				 IP5XXX_SYS_CTL5_NTC_DIS |
+				 IP5XXX_SYS_CTL5_WLED_MODE_SEL |
+				 IP5XXX_SYS_CTL5_BTN_SHDN_SEL,
+				 IP5XXX_SYS_CTL5_WLED_MODE_SEL |
+				 IP5XXX_SYS_CTL5_BTN_SHDN_SEL);
+	if (ret)
+		return ret;
+
+	ip5xxx->initialized = true;
+	dev_dbg(psy->dev.parent, "Initialized after power on\n");
+
+	return 0;
+}
+
+static const enum power_supply_property ip5xxx_charger_properties[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_CHARGE_TYPE,
+	POWER_SUPPLY_PROP_HEALTH,
+	POWER_SUPPLY_PROP_ONLINE,
+	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_VOLTAGE_OCV,
+	POWER_SUPPLY_PROP_CURRENT_NOW,
+	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
+	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
+	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
+	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
+};
+
+static int ip5xxx_charger_get_status(struct ip5xxx *ip5xxx, int *val)
+{
+	unsigned int rval;
+	int ret;
+
+	ret = ip5xxx_read(ip5xxx, IP5XXX_READ0, &rval);
+	if (ret)
+		return ret;
+
+	switch (rval & IP5XXX_READ0_CHG_STAT) {
+	case IP5XXX_READ0_CHG_STAT_IDLE:
+		*val = POWER_SUPPLY_STATUS_DISCHARGING;
+		break;
+	case IP5XXX_READ0_CHG_STAT_TRICKLE:
+	case IP5XXX_READ0_CHG_STAT_CONST_CUR:
+	case IP5XXX_READ0_CHG_STAT_CONST_VOLT:
+		*val = POWER_SUPPLY_STATUS_CHARGING;
+		break;
+	case IP5XXX_READ0_CHG_STAT_CONST_VOLT_STOP:
+	case IP5XXX_READ0_CHG_STAT_FULL:
+		*val = POWER_SUPPLY_STATUS_FULL;
+		break;
+	case IP5XXX_READ0_CHG_STAT_TIMEOUT:
+		*val = POWER_SUPPLY_STATUS_NOT_CHARGING;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int ip5xxx_charger_get_charge_type(struct ip5xxx *ip5xxx, int *val)
+{
+	unsigned int rval;
+	int ret;
+
+	ret = ip5xxx_read(ip5xxx, IP5XXX_READ0, &rval);
+	if (ret)
+		return ret;
+
+	switch (rval & IP5XXX_READ0_CHG_STAT) {
+	case IP5XXX_READ0_CHG_STAT_IDLE:
+	case IP5XXX_READ0_CHG_STAT_CONST_VOLT_STOP:
+	case IP5XXX_READ0_CHG_STAT_FULL:
+	case IP5XXX_READ0_CHG_STAT_TIMEOUT:
+		*val = POWER_SUPPLY_CHARGE_TYPE_NONE;
+		break;
+	case IP5XXX_READ0_CHG_STAT_TRICKLE:
+		*val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+		break;
+	case IP5XXX_READ0_CHG_STAT_CONST_CUR:
+	case IP5XXX_READ0_CHG_STAT_CONST_VOLT:
+		*val = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int ip5xxx_charger_get_health(struct ip5xxx *ip5xxx, int *val)
+{
+	unsigned int rval;
+	int ret;
+
+	ret = ip5xxx_read(ip5xxx, IP5XXX_READ0, &rval);
+	if (ret)
+		return ret;
+
+	if (rval & IP5XXX_READ0_TIMEOUT)
+		*val = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
+	else
+		*val = POWER_SUPPLY_HEALTH_GOOD;
+
+	return 0;
+}
+
+static int ip5xxx_charger_get_voltage_max(struct ip5xxx *ip5xxx, int *val)
+{
+	unsigned int rval;
+	int ret;
+
+	ret = ip5xxx_read(ip5xxx, IP5XXX_CHG_CTL2, &rval);
+	if (ret)
+		return ret;
+
+	/*
+	 * It is not clear what this will return if
+	 * IP5XXX_CHG_CTL4_BAT_TYPE_SEL_EN is not set...
+	 */
+	switch (rval & IP5XXX_CHG_CTL2_BAT_TYPE_SEL) {
+	case IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_2V:
+		*val = 4200000;
+		break;
+	case IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_3V:
+		*val = 4300000;
+		break;
+	case IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_35V:
+		*val = 4350000;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int ip5xxx_charger_read_adc(struct ip5xxx *ip5xxx,
+				   u8 lo_reg, u8 hi_reg, int *val)
+{
+	unsigned int hi, lo;
+	int ret;
+
+	ret = ip5xxx_read(ip5xxx, lo_reg, &lo);
+	if (ret)
+		return ret;
+
+	ret = ip5xxx_read(ip5xxx, hi_reg, &hi);
+	if (ret)
+		return ret;
+
+	*val = sign_extend32(hi << 8 | lo, 13);
+
+	return 0;
+}
+
+static int ip5xxx_charger_get_property(struct power_supply *psy,
+				       enum power_supply_property psp,
+				       union power_supply_propval *val)
+{
+	struct ip5xxx *ip5xxx = power_supply_get_drvdata(psy);
+	int raw, ret, vmax;
+	unsigned int rval;
+
+	ret = ip5xxx_initialize(psy);
+	if (ret)
+		return ret;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_STATUS:
+		return ip5xxx_charger_get_status(ip5xxx, &val->intval);
+
+	case POWER_SUPPLY_PROP_CHARGE_TYPE:
+		return ip5xxx_charger_get_charge_type(ip5xxx, &val->intval);
+
+	case POWER_SUPPLY_PROP_HEALTH:
+		return ip5xxx_charger_get_health(ip5xxx, &val->intval);
+
+	case POWER_SUPPLY_PROP_ONLINE:
+		ret = ip5xxx_read(ip5xxx, IP5XXX_SYS_CTL0, &rval);
+		if (ret)
+			return ret;
+
+		val->intval = !!(rval & IP5XXX_SYS_CTL0_BOOST_EN);
+		return 0;
+
+	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+		return ip5xxx_charger_get_voltage_max(ip5xxx, &val->intval);
+
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		ret = ip5xxx_charger_read_adc(ip5xxx, IP5XXX_BATVADC_DAT0,
+					      IP5XXX_BATVADC_DAT1, &raw);
+
+		val->intval = 2600000 + DIV_ROUND_CLOSEST(raw * 26855, 100);
+		return 0;
+
+	case POWER_SUPPLY_PROP_VOLTAGE_OCV:
+		ret = ip5xxx_charger_read_adc(ip5xxx, IP5XXX_BATOCV_DAT0,
+					      IP5XXX_BATOCV_DAT1, &raw);
+
+		val->intval = 2600000 + DIV_ROUND_CLOSEST(raw * 26855, 100);
+		return 0;
+
+	case POWER_SUPPLY_PROP_CURRENT_NOW:
+		ret = ip5xxx_charger_read_adc(ip5xxx, IP5XXX_BATIADC_DAT0,
+					      IP5XXX_BATIADC_DAT1, &raw);
+
+		val->intval = DIV_ROUND_CLOSEST(raw * 745985, 1000);
+		return 0;
+
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+		ret = ip5xxx_read(ip5xxx, IP5XXX_CHG_CTL4A, &rval);
+		if (ret)
+			return ret;
+
+		rval &= IP5XXX_CHG_CTL4A_CONST_CUR_SEL;
+		val->intval = 100000 * rval;
+		return 0;
+
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
+		val->intval = 100000 * 0x1f;
+		return 0;
+
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+		ret = ip5xxx_charger_get_voltage_max(ip5xxx, &vmax);
+		if (ret)
+			return ret;
+
+		ret = ip5xxx_read(ip5xxx, IP5XXX_CHG_CTL2, &rval);
+		if (ret)
+			return ret;
+
+		rval &= IP5XXX_CHG_CTL2_CONST_VOLT_SEL;
+		val->intval = vmax + 14000 * (rval >> 1);
+		return 0;
+
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
+		ret = ip5xxx_charger_get_voltage_max(ip5xxx, &vmax);
+		if (ret)
+			return ret;
+
+		val->intval = vmax + 14000 * 3;
+		return 0;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static int ip5xxx_charger_set_voltage_max(struct ip5xxx *ip5xxx, int val)
+{
+	unsigned int rval;
+	int ret;
+
+	switch (val) {
+	case 4200000:
+		rval = IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_2V;
+		break;
+	case 4300000:
+		rval = IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_3V;
+		break;
+	case 4350000:
+		rval = IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_35V;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = ip5xxx_update_bits(ip5xxx, IP5XXX_CHG_CTL2,
+				 IP5XXX_CHG_CTL2_BAT_TYPE_SEL, rval);
+	if (ret)
+		return ret;
+
+	ret = ip5xxx_update_bits(ip5xxx, IP5XXX_CHG_CTL4,
+				 IP5XXX_CHG_CTL4_BAT_TYPE_SEL_EN,
+				 IP5XXX_CHG_CTL4_BAT_TYPE_SEL_EN);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int ip5xxx_charger_set_property(struct power_supply *psy,
+				       enum power_supply_property psp,
+				       const union power_supply_propval *val)
+{
+	struct ip5xxx *ip5xxx = power_supply_get_drvdata(psy);
+	unsigned int rval;
+	int ret, vmax;
+
+	ret = ip5xxx_initialize(psy);
+	if (ret)
+		return ret;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_STATUS:
+		switch (val->intval) {
+		case POWER_SUPPLY_STATUS_CHARGING:
+			rval = IP5XXX_SYS_CTL0_CHARGER_EN;
+			break;
+		case POWER_SUPPLY_STATUS_DISCHARGING:
+		case POWER_SUPPLY_STATUS_NOT_CHARGING:
+			rval = 0;
+			break;
+		default:
+			return -EINVAL;
+		}
+		return ip5xxx_update_bits(ip5xxx, IP5XXX_SYS_CTL0,
+					  IP5XXX_SYS_CTL0_CHARGER_EN, rval);
+
+	case POWER_SUPPLY_PROP_ONLINE:
+		rval = val->intval ? IP5XXX_SYS_CTL0_BOOST_EN : 0;
+		return ip5xxx_update_bits(ip5xxx, IP5XXX_SYS_CTL0,
+					  IP5XXX_SYS_CTL0_BOOST_EN, rval);
+
+	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+		return ip5xxx_charger_set_voltage_max(ip5xxx, val->intval);
+
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
+		rval = val->intval / 100000;
+		return ip5xxx_update_bits(ip5xxx, IP5XXX_CHG_CTL4A,
+					  IP5XXX_CHG_CTL4A_CONST_CUR_SEL, rval);
+
+	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
+		ret = ip5xxx_charger_get_voltage_max(ip5xxx, &vmax);
+		if (ret)
+			return ret;
+
+		rval = ((val->intval - vmax) / 14000) << 1;
+		return ip5xxx_update_bits(ip5xxx, IP5XXX_CHG_CTL2,
+					  IP5XXX_CHG_CTL2_CONST_VOLT_SEL, rval);
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static int ip5xxx_charger_property_is_writeable(struct power_supply *psy,
+						enum power_supply_property psp)
+{
+	return psp == POWER_SUPPLY_PROP_STATUS ||
+	       psp == POWER_SUPPLY_PROP_ONLINE ||
+	       psp == POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN ||
+	       psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT ||
+	       psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE;
+}
+
+static const struct power_supply_desc ip5xxx_charger_desc = {
+	.name			= "ip5xxx-charger",
+	.type			= POWER_SUPPLY_TYPE_BATTERY,
+	.properties		= ip5xxx_charger_properties,
+	.num_properties		= ARRAY_SIZE(ip5xxx_charger_properties),
+	.get_property		= ip5xxx_charger_get_property,
+	.set_property		= ip5xxx_charger_set_property,
+	.property_is_writeable	= ip5xxx_charger_property_is_writeable,
+};
+
+static const struct regmap_config ip5xxx_regmap_config = {
+	.reg_bits		= 8,
+	.val_bits		= 8,
+	.max_register		= IP5XXX_BATOCV_DAT1,
+};
+
+static int ip5xxx_power_probe(struct i2c_client *client)
+{
+	struct power_supply_config psy_cfg = {};
+	struct device *dev = &client->dev;
+	struct power_supply *psy;
+	struct ip5xxx *ip5xxx;
+
+	ip5xxx = devm_kzalloc(dev, sizeof(*ip5xxx), GFP_KERNEL);
+
+	ip5xxx->regmap = devm_regmap_init_i2c(client, &ip5xxx_regmap_config);
+	if (IS_ERR(ip5xxx->regmap))
+		return PTR_ERR(ip5xxx->regmap);
+
+	psy_cfg.of_node = dev->of_node;
+	psy_cfg.drv_data = ip5xxx;
+
+	psy = devm_power_supply_register(dev, &ip5xxx_charger_desc, &psy_cfg);
+	if (IS_ERR(psy))
+		return PTR_ERR(psy);
+
+	return 0;
+}
+
+static const struct of_device_id ip5xxx_power_of_match[] = {
+	{ .compatible = "injoinic,ip5108" },
+	{ .compatible = "injoinic,ip5109" },
+	{ .compatible = "injoinic,ip5207" },
+	{ .compatible = "injoinic,ip5209" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, ip5xxx_power_of_match);
+
+static struct i2c_driver ip5xxx_power_driver = {
+	.probe_new	= ip5xxx_power_probe,
+	.driver		= {
+		.name		= "ip5xxx-power",
+		.of_match_table	= ip5xxx_power_of_match,
+	}
+};
+module_i2c_driver(ip5xxx_power_driver);
+
+MODULE_AUTHOR("Samuel Holland <samuel@sholland.org>");
+MODULE_DESCRIPTION("Injoinic IP5xxx power bank IC driver");
+MODULE_LICENSE("GPL");
