[PATCH v4 2/3] power: supply: Add bd718(15/28/78) charger driver

Andreas Kemnade posted 3 patches 2 weeks ago
There is a newer version of this series
[PATCH v4 2/3] power: supply: Add bd718(15/28/78) charger driver
Posted by Andreas Kemnade 2 weeks ago
Add charger driver for ROHM BD718(15/28/78) PMIC charger block.
It is a stripped down version of the driver here:
https://lore.kernel.org/lkml/dbd97c1b0d715aa35a8b4d79741e433d97c562aa.1637061794.git.matti.vaittinen@fi.rohmeurope.com/

For the ease of review and to do a step-by-step approach remove all the
coloumb counter related stuff and do not sneak in BD71827 support. That
also avoids non-trivial rebasing of the above series.

Changes besides that:
Replace the custom property by a standard one and do not use megaohms
for the current sense resistor.

Signed-off-by: Andreas Kemnade <andreas@kemnade.info>
Reviewed-by: Matti Vaittinen <mazziesaccount@gmail.com>
---
 drivers/power/supply/Kconfig         |    9 +
 drivers/power/supply/Makefile        |    1 +
 drivers/power/supply/bd71828-power.c | 1051 ++++++++++++++++++++++++++++++++++
 3 files changed, 1061 insertions(+)

diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
index 79ddb006e2dad..16dddb2355d9b 100644
--- a/drivers/power/supply/Kconfig
+++ b/drivers/power/supply/Kconfig
@@ -974,6 +974,15 @@ config CHARGER_UCS1002
 	  Say Y to enable support for Microchip UCS1002 Programmable
 	  USB Port Power Controller with Charger Emulation.
 
+config CHARGER_BD71828
+	tristate "Power-supply driver for ROHM BD71828 and BD71815 PMIC"
+	depends on MFD_ROHM_BD71828
+	help
+	  Say Y here to enable support for charger and battery
+	  in ROHM BD71815, BD71817, ROHM BD71828 power management
+	  ICs. This driver gets various bits of information about battery
+	  and charger states.
+
 config CHARGER_BD99954
 	tristate "ROHM bd99954 charger driver"
 	depends on I2C
diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile
index f943c9150b326..c6520a11f021c 100644
--- a/drivers/power/supply/Makefile
+++ b/drivers/power/supply/Makefile
@@ -113,6 +113,7 @@ obj-$(CONFIG_CHARGER_SC2731)	+= sc2731_charger.o
 obj-$(CONFIG_FUEL_GAUGE_SC27XX)	+= sc27xx_fuel_gauge.o
 obj-$(CONFIG_FUEL_GAUGE_STC3117)       += stc3117_fuel_gauge.o
 obj-$(CONFIG_CHARGER_UCS1002)	+= ucs1002_power.o
+obj-$(CONFIG_CHARGER_BD71828)	+= bd71828-power.o
 obj-$(CONFIG_CHARGER_BD99954)	+= bd99954-charger.o
 obj-$(CONFIG_CHARGER_WILCO)	+= wilco-charger.o
 obj-$(CONFIG_RN5T618_POWER)	+= rn5t618_power.o
diff --git a/drivers/power/supply/bd71828-power.c b/drivers/power/supply/bd71828-power.c
new file mode 100644
index 0000000000000..fdd03159dc7bc
--- /dev/null
+++ b/drivers/power/supply/bd71828-power.c
@@ -0,0 +1,1051 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* ROHM BD71815, BD71828 and BD71878 Charger driver */
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/rohm-bd71815.h>
+#include <linux/mfd/rohm-bd71828.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+/* common defines */
+#define BD7182x_MASK_VBAT_U			0x1f
+#define BD7182x_MASK_VDCIN_U			0x0f
+#define BD7182x_MASK_IBAT_U			0x3f
+#define BD7182x_MASK_CURDIR_DISCHG		0x80
+#define BD7182x_MASK_CHG_STATE			0x7f
+#define BD7182x_MASK_BAT_TEMP			0x07
+#define BD7182x_MASK_DCIN_DET			BIT(0)
+#define BD7182x_MASK_CONF_PON			BIT(0)
+#define BD71815_MASK_CONF_XSTB			BIT(1)
+#define BD7182x_MASK_BAT_STAT			0x3f
+#define BD7182x_MASK_DCIN_STAT			0x07
+
+#define BD7182x_MASK_WDT_AUTO			0x40
+#define BD7182x_MASK_VBAT_ALM_LIMIT_U		0x01
+#define BD7182x_MASK_CHG_EN			0x01
+
+#define BD7182x_DCIN_COLLAPSE_DEFAULT		0x36
+
+#define MAX_CURRENT_DEFAULT			890000		/* uA */
+#define AC_NAME					"bd71828_ac"
+#define BAT_NAME				"bd71828_bat"
+
+#define BAT_OPEN	0x7
+
+/*
+ * VBAT Low voltage detection Threshold
+ * 0x00D4*16mV = 212*0.016 = 3.392v
+ */
+#define VBAT_LOW_TH			0x00D4
+
+struct pwr_regs {
+	u8 vbat_avg;
+	u8 ibat;
+	u8 ibat_avg;
+	u8 btemp_vth;
+	u8 chg_state;
+	u8 bat_temp;
+	u8 dcin_stat;
+	u8 dcin_collapse_limit;
+	u8 chg_set1;
+	u8 chg_en;
+	u8 vbat_alm_limit_u;
+	u8 conf;
+	u8 vdcin;
+};
+
+static const struct pwr_regs pwr_regs_bd71828 = {
+	.vbat_avg = BD71828_REG_VBAT_U,
+	.ibat = BD71828_REG_IBAT_U,
+	.ibat_avg = BD71828_REG_IBAT_AVG_U,
+	.btemp_vth = BD71828_REG_VM_BTMP_U,
+	.chg_state = BD71828_REG_CHG_STATE,
+	.bat_temp = BD71828_REG_BAT_TEMP,
+	.dcin_stat = BD71828_REG_DCIN_STAT,
+	.dcin_collapse_limit = BD71828_REG_DCIN_CLPS,
+	.chg_set1 = BD71828_REG_CHG_SET1,
+	.chg_en   = BD71828_REG_CHG_EN,
+	.vbat_alm_limit_u = BD71828_REG_ALM_VBAT_LIMIT_U,
+	.conf = BD71828_REG_CONF,
+	.vdcin = BD71828_REG_VDCIN_U,
+};
+
+static const struct pwr_regs pwr_regs_bd71815 = {
+	.vbat_avg = BD71815_REG_VM_SA_VBAT_U,
+	/* BD71815 does not have separate current and current avg */
+	.ibat = BD71815_REG_CC_CURCD_U,
+	.ibat_avg = BD71815_REG_CC_CURCD_U,
+
+	.btemp_vth = BD71815_REG_VM_BTMP,
+	.chg_state = BD71815_REG_CHG_STATE,
+	.bat_temp = BD71815_REG_BAT_TEMP,
+	.dcin_stat = BD71815_REG_DCIN_STAT,
+	.dcin_collapse_limit = BD71815_REG_DCIN_CLPS,
+	.chg_set1 = BD71815_REG_CHG_SET1,
+	.chg_en   = BD71815_REG_CHG_SET1,
+	.vbat_alm_limit_u = BD71815_REG_ALM_VBAT_TH_U,
+	.conf = BD71815_REG_CONF,
+
+	.vdcin = BD71815_REG_VM_DCIN_U,
+};
+
+struct bd71828_power {
+	struct regmap *regmap;
+	enum rohm_chip_type chip_type;
+	struct device *dev;
+	struct power_supply *ac;
+	struct power_supply *bat;
+
+	const struct pwr_regs *regs;
+	/* Reg val to uA */
+	int curr_factor;
+	int rsens;
+	int (*get_temp)(struct bd71828_power *pwr, int *temp);
+	int (*bat_inserted)(struct bd71828_power *pwr);
+};
+
+static int bd7182x_write16(struct bd71828_power *pwr, int reg, u16 val)
+{
+	__be16 tmp;
+
+	tmp = cpu_to_be16(val);
+
+	return regmap_bulk_write(pwr->regmap, reg, &tmp, sizeof(tmp));
+}
+
+static int bd7182x_read16_himask(struct bd71828_power *pwr, int reg, int himask,
+				 u16 *val)
+{
+	struct regmap *regmap = pwr->regmap;
+	int ret;
+	__be16 rvals;
+	u8 *tmp = (u8 *)&rvals;
+
+	ret = regmap_bulk_read(regmap, reg, &rvals, sizeof(*val));
+	if (!ret) {
+		*tmp &= himask;
+		*val = be16_to_cpu(rvals);
+	}
+
+	return ret;
+}
+
+static int bd71828_get_vbat(struct bd71828_power *pwr, int *vcell)
+{
+	u16 tmp_vcell;
+	int ret;
+
+	ret = bd7182x_read16_himask(pwr, pwr->regs->vbat_avg,
+				    BD7182x_MASK_VBAT_U, &tmp_vcell);
+	if (ret)
+		dev_err(pwr->dev, "Failed to read battery average voltage\n");
+	else
+		*vcell = ((int)tmp_vcell) * 1000;
+
+	return ret;
+}
+
+static int bd71828_get_current_ds_adc(struct bd71828_power *pwr, int *curr, int *curr_avg)
+{
+	__be16 tmp_curr;
+	char *tmp = (char *)&tmp_curr;
+	int dir = 1;
+	int regs[] = { pwr->regs->ibat, pwr->regs->ibat_avg };
+	int *vals[] = { curr, curr_avg };
+	int ret, i;
+
+	for (dir = 1, i = 0; i < ARRAY_SIZE(regs); i++) {
+		ret = regmap_bulk_read(pwr->regmap, regs[i], &tmp_curr,
+				       sizeof(tmp_curr));
+		if (ret)
+			break;
+
+		if (*tmp & BD7182x_MASK_CURDIR_DISCHG)
+			dir = -1;
+
+		*tmp &= BD7182x_MASK_IBAT_U;
+
+		*vals[i] = dir * ((int)be16_to_cpu(tmp_curr)) * pwr->curr_factor;
+	}
+
+	return ret;
+}
+
+/* Unit is tenths of degree C */
+static int bd71815_get_temp(struct bd71828_power *pwr, int *temp)
+{
+	struct regmap *regmap = pwr->regmap;
+	int ret;
+	int t;
+
+	ret = regmap_read(regmap, pwr->regs->btemp_vth, &t);
+	if (ret)
+		return ret;
+
+	t = 200 - t;
+
+	if (t > 200) {
+		dev_err(pwr->dev, "Failed to read battery temperature\n");
+		return -ENODATA;
+	}
+
+	return 0;
+}
+
+/* Unit is tenths of degree C */
+static int bd71828_get_temp(struct bd71828_power *pwr, int *temp)
+{
+	u16 t;
+	int ret;
+	int tmp = 200 * 10000;
+
+	ret = bd7182x_read16_himask(pwr, pwr->regs->btemp_vth,
+				    BD71828_MASK_VM_BTMP_U, &t);
+	if (ret)
+		return ret;
+
+	if (t > 3200) {
+		dev_err(pwr->dev,
+			"Failed to read battery temperature\n");
+		return -ENODATA;
+	}
+
+	tmp -= 625ULL * (unsigned int)t;
+	*temp = tmp / 1000;
+
+	return ret;
+}
+
+static int bd71828_charge_status(struct bd71828_power *pwr,
+				 int *s, int *h)
+{
+	unsigned int state;
+	int status, health;
+	int ret = 1;
+
+	ret = regmap_read(pwr->regmap, pwr->regs->chg_state, &state);
+	if (ret) {
+		dev_err(pwr->dev, "charger status reading failed (%d)\n", ret);
+		return ret;
+	}
+
+	state &= BD7182x_MASK_CHG_STATE;
+
+	dev_dbg(pwr->dev, "CHG_STATE %d\n", state);
+
+	switch (state) {
+	case 0x00:
+		status = POWER_SUPPLY_STATUS_DISCHARGING;
+		health = POWER_SUPPLY_HEALTH_GOOD;
+		break;
+	case 0x01:
+	case 0x02:
+	case 0x03:
+	case 0x0E:
+		status = POWER_SUPPLY_STATUS_CHARGING;
+		health = POWER_SUPPLY_HEALTH_GOOD;
+		break;
+	case 0x0F:
+		status = POWER_SUPPLY_STATUS_FULL;
+		health = POWER_SUPPLY_HEALTH_GOOD;
+		break;
+	case 0x10:
+	case 0x11:
+	case 0x12:
+	case 0x13:
+	case 0x14:
+	case 0x20:
+	case 0x21:
+	case 0x22:
+	case 0x23:
+	case 0x24:
+		status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+		health = POWER_SUPPLY_HEALTH_OVERHEAT;
+		break;
+	case 0x30:
+	case 0x31:
+	case 0x32:
+	case 0x40:
+		status = POWER_SUPPLY_STATUS_DISCHARGING;
+		health = POWER_SUPPLY_HEALTH_GOOD;
+		break;
+	case 0x7f:
+	default:
+		status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+		health = POWER_SUPPLY_HEALTH_DEAD;
+		break;
+	}
+
+	if (s)
+		*s = status;
+	if (h)
+		*h = health;
+
+	return ret;
+}
+
+static int get_chg_online(struct bd71828_power *pwr, int *chg_online)
+{
+	int r, ret;
+
+	ret = regmap_read(pwr->regmap, pwr->regs->dcin_stat, &r);
+	if (ret) {
+		dev_err(pwr->dev, "Failed to read DCIN status\n");
+		return ret;
+	}
+	*chg_online = ((r & BD7182x_MASK_DCIN_DET) != 0);
+
+	return 0;
+}
+
+static int get_bat_online(struct bd71828_power *pwr, int *bat_online)
+{
+	int r, ret;
+
+	ret = regmap_read(pwr->regmap, pwr->regs->bat_temp, &r);
+	if (ret) {
+		dev_err(pwr->dev, "Failed to read battery temperature\n");
+		return ret;
+	}
+	*bat_online = ((r & BD7182x_MASK_BAT_TEMP) != BAT_OPEN);
+
+	return 0;
+}
+
+static int bd71828_bat_inserted(struct bd71828_power *pwr)
+{
+	int ret, val;
+
+	ret = regmap_read(pwr->regmap, pwr->regs->conf, &val);
+	if (ret) {
+		dev_err(pwr->dev, "Failed to read CONF register\n");
+		return 0;
+	}
+	ret = val & BD7182x_MASK_CONF_PON;
+
+	if (ret)
+		regmap_update_bits(pwr->regmap, pwr->regs->conf,
+				   BD7182x_MASK_CONF_PON, 0);
+
+	return ret;
+}
+
+static int bd71815_bat_inserted(struct bd71828_power *pwr)
+{
+	int ret, val;
+
+	ret = regmap_read(pwr->regmap, pwr->regs->conf, &val);
+	if (ret) {
+		dev_err(pwr->dev, "Failed to read CONF register\n");
+		return ret;
+	}
+
+	ret = !(val & BD71815_MASK_CONF_XSTB);
+	if (ret)
+		regmap_write(pwr->regmap, pwr->regs->conf,  val |
+			     BD71815_MASK_CONF_XSTB);
+
+	return ret;
+}
+
+static int bd71828_init_hardware(struct bd71828_power *pwr)
+{
+	int ret;
+
+	/* TODO: Collapse limit should come from device-tree ? */
+	ret = regmap_write(pwr->regmap, pwr->regs->dcin_collapse_limit,
+			   BD7182x_DCIN_COLLAPSE_DEFAULT);
+	if (ret) {
+		dev_err(pwr->dev, "Failed to write DCIN collapse limit\n");
+		return ret;
+	}
+
+	ret = pwr->bat_inserted(pwr);
+	if (ret < 0)
+		return ret;
+
+	if (ret) {
+		/* WDT_FST auto set */
+		ret = regmap_update_bits(pwr->regmap, pwr->regs->chg_set1,
+					 BD7182x_MASK_WDT_AUTO,
+					 BD7182x_MASK_WDT_AUTO);
+		if (ret)
+			return ret;
+
+		ret = bd7182x_write16(pwr, pwr->regs->vbat_alm_limit_u,
+				      VBAT_LOW_TH);
+		if (ret)
+			return ret;
+
+		/*
+		 * On BD71815 "we mask the power-state" from relax detection.
+		 * I am unsure what the impact of the power-state would be if
+		 * we didn't - but this is what the vendor driver did - and
+		 * that driver has been used in few projects so I just assume
+		 * this is needed.
+		 */
+		if (pwr->chip_type == ROHM_CHIP_TYPE_BD71815) {
+			ret = regmap_set_bits(pwr->regmap,
+					      BD71815_REG_REX_CTRL_1,
+					      REX_PMU_STATE_MASK);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int bd71828_charger_get_property(struct power_supply *psy,
+					enum power_supply_property psp,
+					union power_supply_propval *val)
+{
+	struct bd71828_power *pwr = dev_get_drvdata(psy->dev.parent);
+	u32 vot;
+	u16 tmp;
+	int online;
+	int ret;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_ONLINE:
+		ret = get_chg_online(pwr, &online);
+		if (!ret)
+			val->intval = online;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		ret = bd7182x_read16_himask(pwr, pwr->regs->vdcin,
+					    BD7182x_MASK_VDCIN_U, &tmp);
+		if (ret)
+			return ret;
+
+		vot = tmp;
+		/* 5 milli volt steps */
+		val->intval = 5000 * vot;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int bd71828_battery_get_property(struct power_supply *psy,
+					enum power_supply_property psp,
+					union power_supply_propval *val)
+{
+	struct bd71828_power *pwr = dev_get_drvdata(psy->dev.parent);
+	int ret = 0;
+	int status, health, tmp, curr, curr_avg, chg_en;
+
+	if (psp == POWER_SUPPLY_PROP_STATUS ||
+	    psp == POWER_SUPPLY_PROP_HEALTH ||
+	    psp == POWER_SUPPLY_PROP_CHARGE_TYPE)
+		ret = bd71828_charge_status(pwr, &status, &health);
+	else if (psp == POWER_SUPPLY_PROP_CURRENT_AVG ||
+		 psp == POWER_SUPPLY_PROP_CURRENT_NOW)
+		ret = bd71828_get_current_ds_adc(pwr, &curr, &curr_avg);
+	if (ret)
+		return ret;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_STATUS:
+		val->intval = status;
+		break;
+	case POWER_SUPPLY_PROP_HEALTH:
+		val->intval = health;
+		break;
+	case POWER_SUPPLY_PROP_PRESENT:
+		ret = get_bat_online(pwr, &tmp);
+		if (!ret)
+			val->intval = tmp;
+		break;
+	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+		ret = bd71828_get_vbat(pwr, &tmp);
+		val->intval = tmp;
+		break;
+	case POWER_SUPPLY_PROP_TECHNOLOGY:
+		val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_AVG:
+		val->intval = curr_avg;
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_NOW:
+		val->intval = curr;
+		break;
+	case POWER_SUPPLY_PROP_CURRENT_MAX:
+		val->intval = MAX_CURRENT_DEFAULT;
+		break;
+	case POWER_SUPPLY_PROP_TEMP:
+		ret = pwr->get_temp(pwr, &val->intval);
+		break;
+	case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
+		ret = regmap_read(pwr->regmap, pwr->regs->chg_en, &chg_en);
+		if (ret)
+			return ret;
+
+		val->intval = (chg_en & BD7182x_MASK_CHG_EN) ?
+			POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO :
+			POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static int bd71828_battery_set_property(struct power_supply *psy,
+					enum power_supply_property psp,
+					const union power_supply_propval *val)
+{
+	struct bd71828_power *pwr = dev_get_drvdata(psy->dev.parent);
+	int ret = 0;
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
+		if (val->intval == POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO)
+			ret = regmap_update_bits(pwr->regmap, pwr->regs->chg_en,
+						 BD7182x_MASK_CHG_EN,
+						 BD7182x_MASK_CHG_EN);
+		else
+			ret = regmap_update_bits(pwr->regmap, pwr->regs->chg_en,
+						 BD7182x_MASK_CHG_EN,
+						 0);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+static int bd71828_battery_property_is_writeable(struct power_supply *psy,
+						 enum power_supply_property psp)
+{
+	switch (psp) {
+	case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
+		return true;
+	default:
+		return false;
+	}
+}
+
+/** @brief ac properties */
+static const enum power_supply_property bd71828_charger_props[] = {
+	POWER_SUPPLY_PROP_ONLINE,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+};
+
+static const enum power_supply_property bd71828_battery_props[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_HEALTH,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_HEALTH,
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_TECHNOLOGY,
+	POWER_SUPPLY_PROP_TEMP,
+	POWER_SUPPLY_PROP_CURRENT_AVG,
+	POWER_SUPPLY_PROP_CURRENT_NOW,
+	POWER_SUPPLY_PROP_CURRENT_MAX,
+	POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR,
+};
+
+/** @brief powers supplied by bd71828_ac */
+static char *bd71828_ac_supplied_to[] = {
+	BAT_NAME,
+};
+
+static const struct power_supply_desc bd71828_ac_desc = {
+	.name		= AC_NAME,
+	.type		= POWER_SUPPLY_TYPE_MAINS,
+	.properties	= bd71828_charger_props,
+	.num_properties	= ARRAY_SIZE(bd71828_charger_props),
+	.get_property	= bd71828_charger_get_property,
+};
+
+static const struct power_supply_desc bd71828_bat_desc = {
+	.name		= BAT_NAME,
+	.type		= POWER_SUPPLY_TYPE_BATTERY,
+	.charge_behaviours = BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO) |
+			     BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE),
+	.properties	= bd71828_battery_props,
+	.num_properties = ARRAY_SIZE(bd71828_battery_props),
+	.get_property	= bd71828_battery_get_property,
+	.set_property	= bd71828_battery_set_property,
+	.property_is_writeable   = bd71828_battery_property_is_writeable,
+};
+
+#define RSENS_CURR 10000000LLU
+
+#define BD_ISR_NAME(name) \
+bd7181x_##name##_isr
+
+#define BD_ISR_BAT(name, print, run_gauge)				\
+static irqreturn_t BD_ISR_NAME(name)(int irq, void *data)		\
+{									\
+	struct bd71828_power *pwr = (struct bd71828_power *)data;	\
+									\
+	dev_dbg(pwr->dev, "%s\n", print);				\
+	power_supply_changed(pwr->bat);				\
+									\
+	return IRQ_HANDLED;						\
+}
+
+#define BD_ISR_AC(name, print, run_gauge)				\
+static irqreturn_t BD_ISR_NAME(name)(int irq, void *data)		\
+{									\
+	struct bd71828_power *pwr = (struct bd71828_power *)data;	\
+									\
+	power_supply_changed(pwr->ac);					\
+	dev_dbg(pwr->dev, "%s\n", print);				\
+	power_supply_changed(pwr->bat);				\
+									\
+	return IRQ_HANDLED;						\
+}
+
+#define BD_ISR_DUMMY(name, print)					\
+static irqreturn_t BD_ISR_NAME(name)(int irq, void *data)		\
+{									\
+	struct bd71828_power *pwr = (struct bd71828_power *)data;	\
+									\
+	dev_dbg(pwr->dev, "%s\n", print);				\
+									\
+	return IRQ_HANDLED;						\
+}
+
+BD_ISR_BAT(chg_state_changed, "CHG state changed", true)
+/* DCIN voltage changes */
+BD_ISR_AC(dcin_removed, "DCIN removed", true)
+BD_ISR_AC(clps_out, "DCIN voltage back to normal", true)
+BD_ISR_AC(clps_in, "DCIN voltage collapsed", false)
+BD_ISR_AC(dcin_ovp_res, "DCIN voltage normal", true)
+BD_ISR_AC(dcin_ovp_det, "DCIN OVER VOLTAGE", true)
+
+BD_ISR_DUMMY(dcin_mon_det, "DCIN voltage below threshold")
+BD_ISR_DUMMY(dcin_mon_res, "DCIN voltage above threshold")
+
+BD_ISR_DUMMY(vsys_uv_res, "VSYS under-voltage cleared")
+BD_ISR_DUMMY(vsys_uv_det, "VSYS under-voltage")
+BD_ISR_DUMMY(vsys_low_res, "'VSYS low' cleared")
+BD_ISR_DUMMY(vsys_low_det, "VSYS low")
+BD_ISR_DUMMY(vsys_mon_res, "VSYS mon - resumed")
+BD_ISR_DUMMY(vsys_mon_det, "VSYS mon - detected")
+BD_ISR_BAT(chg_wdg_temp, "charger temperature watchdog triggered", true)
+BD_ISR_BAT(chg_wdg, "charging watchdog triggered", true)
+BD_ISR_BAT(bat_removed, "Battery removed", true)
+BD_ISR_BAT(bat_det, "Battery detected", true)
+/* TODO: Verify the meaning of these interrupts */
+BD_ISR_BAT(rechg_det, "Recharging", true)
+BD_ISR_BAT(rechg_res, "Recharge ending", true)
+BD_ISR_DUMMY(temp_transit, "Temperature transition")
+BD_ISR_BAT(therm_rmv, "bd71815-therm-rmv", false)
+BD_ISR_BAT(therm_det, "bd71815-therm-det", true)
+BD_ISR_BAT(bat_dead, "bd71815-bat-dead", false)
+BD_ISR_BAT(bat_short_res, "bd71815-bat-short-res", true)
+BD_ISR_BAT(bat_short, "bd71815-bat-short-det", false)
+BD_ISR_BAT(bat_low_res, "bd71815-bat-low-res", true)
+BD_ISR_BAT(bat_low, "bd71815-bat-low-det", true)
+BD_ISR_BAT(bat_ov_res, "bd71815-bat-over-res", true)
+/* What should we do here? */
+BD_ISR_BAT(bat_ov, "bd71815-bat-over-det", false)
+BD_ISR_BAT(bat_mon_res, "bd71815-bat-mon-res", true)
+BD_ISR_BAT(bat_mon, "bd71815-bat-mon-det", true)
+BD_ISR_BAT(bat_cc_mon, "bd71815-bat-cc-mon2", false)
+BD_ISR_BAT(bat_oc1_res, "bd71815-bat-oc1-res", true)
+BD_ISR_BAT(bat_oc1, "bd71815-bat-oc1-det", false)
+BD_ISR_BAT(bat_oc2_res, "bd71815-bat-oc2-res", true)
+BD_ISR_BAT(bat_oc2, "bd71815-bat-oc2-det", false)
+BD_ISR_BAT(bat_oc3_res, "bd71815-bat-oc3-res", true)
+BD_ISR_BAT(bat_oc3, "bd71815-bat-oc3-det", false)
+BD_ISR_BAT(temp_bat_low_res, "bd71815-temp-bat-low-res", true)
+BD_ISR_BAT(temp_bat_low, "bd71815-temp-bat-low-det", true)
+BD_ISR_BAT(temp_bat_hi_res, "bd71815-temp-bat-hi-res", true)
+BD_ISR_BAT(temp_bat_hi, "bd71815-temp-bat-hi-det", true)
+
+static irqreturn_t bd7182x_dcin_removed(int irq, void *data)
+{
+	struct bd71828_power *pwr = (struct bd71828_power *)data;
+
+	power_supply_changed(pwr->ac);
+	dev_dbg(pwr->dev, "DCIN removed\n");
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t bd718x7_chg_done(int irq, void *data)
+{
+	struct bd71828_power *pwr = (struct bd71828_power *)data;
+
+	power_supply_changed(pwr->bat);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t bd7182x_dcin_detected(int irq, void *data)
+{
+	struct bd71828_power *pwr = (struct bd71828_power *)data;
+
+	dev_dbg(pwr->dev, "DCIN inserted\n");
+	power_supply_changed(pwr->ac);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t bd71828_vbat_low_res(int irq, void *data)
+{
+	struct bd71828_power *pwr = (struct bd71828_power *)data;
+
+	dev_dbg(pwr->dev, "VBAT LOW Resumed\n");
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t bd71828_vbat_low_det(int irq, void *data)
+{
+	struct bd71828_power *pwr = (struct bd71828_power *)data;
+
+	dev_dbg(pwr->dev, "VBAT LOW Detected\n");
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t bd71828_temp_bat_hi_det(int irq, void *data)
+{
+	struct bd71828_power *pwr = (struct bd71828_power *)data;
+
+	dev_warn(pwr->dev, "Overtemp Detected\n");
+	power_supply_changed(pwr->bat);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t bd71828_temp_bat_hi_res(int irq, void *data)
+{
+	struct bd71828_power *pwr = (struct bd71828_power *)data;
+
+	dev_dbg(pwr->dev, "Overtemp Resumed\n");
+	power_supply_changed(pwr->bat);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t bd71828_temp_bat_low_det(int irq, void *data)
+{
+	struct bd71828_power *pwr = (struct bd71828_power *)data;
+
+	dev_dbg(pwr->dev, "Lowtemp Detected\n");
+	power_supply_changed(pwr->bat);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t bd71828_temp_bat_low_res(int irq, void *data)
+{
+	struct bd71828_power *pwr = (struct bd71828_power *)data;
+
+	dev_dbg(pwr->dev, "Lowtemp Resumed\n");
+	power_supply_changed(pwr->bat);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t bd71828_temp_vf_det(int irq, void *data)
+{
+	struct bd71828_power *pwr = (struct bd71828_power *)data;
+
+	dev_dbg(pwr->dev, "VF Detected\n");
+	power_supply_changed(pwr->bat);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t bd71828_temp_vf_res(int irq, void *data)
+{
+	struct bd71828_power *pwr = (struct bd71828_power *)data;
+
+	dev_dbg(pwr->dev, "VF Resumed\n");
+	power_supply_changed(pwr->bat);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t bd71828_temp_vf125_det(int irq, void *data)
+{
+	struct bd71828_power *pwr = (struct bd71828_power *)data;
+
+	dev_dbg(pwr->dev, "VF125 Detected\n");
+	power_supply_changed(pwr->bat);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t bd71828_temp_vf125_res(int irq, void *data)
+{
+	struct bd71828_power *pwr = (struct bd71828_power *)data;
+
+	dev_dbg(pwr->dev, "VF125 Resumed\n");
+	power_supply_changed(pwr->bat);
+
+	return IRQ_HANDLED;
+}
+
+struct bd7182x_irq_res {
+	const char *name;
+	irq_handler_t handler;
+};
+
+#define BDIRQ(na, hn) { .name = (na), .handler = (hn) }
+
+static int bd7182x_get_irqs(struct platform_device *pdev,
+			    struct bd71828_power *pwr)
+{
+	int i, irq, ret;
+	static const struct bd7182x_irq_res bd71815_irqs[] = {
+		BDIRQ("bd71815-dcin-rmv", BD_ISR_NAME(dcin_removed)),
+		BDIRQ("bd71815-dcin-clps-out", BD_ISR_NAME(clps_out)),
+		BDIRQ("bd71815-dcin-clps-in", BD_ISR_NAME(clps_in)),
+		BDIRQ("bd71815-dcin-ovp-res", BD_ISR_NAME(dcin_ovp_res)),
+		BDIRQ("bd71815-dcin-ovp-det", BD_ISR_NAME(dcin_ovp_det)),
+		BDIRQ("bd71815-dcin-mon-res", BD_ISR_NAME(dcin_mon_res)),
+		BDIRQ("bd71815-dcin-mon-det", BD_ISR_NAME(dcin_mon_det)),
+
+		BDIRQ("bd71815-vsys-uv-res", BD_ISR_NAME(vsys_uv_res)),
+		BDIRQ("bd71815-vsys-uv-det", BD_ISR_NAME(vsys_uv_det)),
+		BDIRQ("bd71815-vsys-low-res", BD_ISR_NAME(vsys_low_res)),
+		BDIRQ("bd71815-vsys-low-det",  BD_ISR_NAME(vsys_low_det)),
+		BDIRQ("bd71815-vsys-mon-res",  BD_ISR_NAME(vsys_mon_res)),
+		BDIRQ("bd71815-vsys-mon-det",  BD_ISR_NAME(vsys_mon_det)),
+		BDIRQ("bd71815-chg-wdg-temp", BD_ISR_NAME(chg_wdg_temp)),
+		BDIRQ("bd71815-chg-wdg",  BD_ISR_NAME(chg_wdg)),
+		BDIRQ("bd71815-rechg-det", BD_ISR_NAME(rechg_det)),
+		BDIRQ("bd71815-rechg-res", BD_ISR_NAME(rechg_res)),
+		BDIRQ("bd71815-ranged-temp-transit", BD_ISR_NAME(temp_transit)),
+		BDIRQ("bd71815-chg-state-change", BD_ISR_NAME(chg_state_changed)),
+		BDIRQ("bd71815-bat-temp-normal", bd71828_temp_bat_hi_res),
+		BDIRQ("bd71815-bat-temp-erange", bd71828_temp_bat_hi_det),
+		BDIRQ("bd71815-bat-rmv", BD_ISR_NAME(bat_removed)),
+		BDIRQ("bd71815-bat-det", BD_ISR_NAME(bat_det)),
+
+		/* Add ISRs for these */
+		BDIRQ("bd71815-therm-rmv", BD_ISR_NAME(therm_rmv)),
+		BDIRQ("bd71815-therm-det", BD_ISR_NAME(therm_det)),
+		BDIRQ("bd71815-bat-dead", BD_ISR_NAME(bat_dead)),
+		BDIRQ("bd71815-bat-short-res", BD_ISR_NAME(bat_short_res)),
+		BDIRQ("bd71815-bat-short-det", BD_ISR_NAME(bat_short)),
+		BDIRQ("bd71815-bat-low-res", BD_ISR_NAME(bat_low_res)),
+		BDIRQ("bd71815-bat-low-det", BD_ISR_NAME(bat_low)),
+		BDIRQ("bd71815-bat-over-res", BD_ISR_NAME(bat_ov_res)),
+		BDIRQ("bd71815-bat-over-det", BD_ISR_NAME(bat_ov)),
+		BDIRQ("bd71815-bat-mon-res", BD_ISR_NAME(bat_mon_res)),
+		BDIRQ("bd71815-bat-mon-det", BD_ISR_NAME(bat_mon)),
+		/* cc-mon 1 & 3 ? */
+		BDIRQ("bd71815-bat-cc-mon2", BD_ISR_NAME(bat_cc_mon)),
+		BDIRQ("bd71815-bat-oc1-res", BD_ISR_NAME(bat_oc1_res)),
+		BDIRQ("bd71815-bat-oc1-det", BD_ISR_NAME(bat_oc1)),
+		BDIRQ("bd71815-bat-oc2-res", BD_ISR_NAME(bat_oc2_res)),
+		BDIRQ("bd71815-bat-oc2-det", BD_ISR_NAME(bat_oc2)),
+		BDIRQ("bd71815-bat-oc3-res", BD_ISR_NAME(bat_oc3_res)),
+		BDIRQ("bd71815-bat-oc3-det", BD_ISR_NAME(bat_oc3)),
+		BDIRQ("bd71815-temp-bat-low-res", BD_ISR_NAME(temp_bat_low_res)),
+		BDIRQ("bd71815-temp-bat-low-det", BD_ISR_NAME(temp_bat_low)),
+		BDIRQ("bd71815-temp-bat-hi-res", BD_ISR_NAME(temp_bat_hi_res)),
+		BDIRQ("bd71815-temp-bat-hi-det", BD_ISR_NAME(temp_bat_hi)),
+		/*
+		 * TODO: add rest of the IRQs and re-check the handling.
+		 * Check the bd71815-bat-cc-mon1, bd71815-bat-cc-mon3,
+		 * bd71815-bat-low-res, bd71815-bat-low-det,
+		 * bd71815-bat-hi-res, bd71815-bat-hi-det.
+		 */
+	};
+	static const struct bd7182x_irq_res bd71828_irqs[] = {
+		BDIRQ("bd71828-chg-done", bd718x7_chg_done),
+		BDIRQ("bd71828-pwr-dcin-in", bd7182x_dcin_detected),
+		BDIRQ("bd71828-pwr-dcin-out", bd7182x_dcin_removed),
+		BDIRQ("bd71828-vbat-normal", bd71828_vbat_low_res),
+		BDIRQ("bd71828-vbat-low", bd71828_vbat_low_det),
+		BDIRQ("bd71828-btemp-hi", bd71828_temp_bat_hi_det),
+		BDIRQ("bd71828-btemp-cool", bd71828_temp_bat_hi_res),
+		BDIRQ("bd71828-btemp-lo", bd71828_temp_bat_low_det),
+		BDIRQ("bd71828-btemp-warm", bd71828_temp_bat_low_res),
+		BDIRQ("bd71828-temp-hi", bd71828_temp_vf_det),
+		BDIRQ("bd71828-temp-norm", bd71828_temp_vf_res),
+		BDIRQ("bd71828-temp-125-over", bd71828_temp_vf125_det),
+		BDIRQ("bd71828-temp-125-under", bd71828_temp_vf125_res),
+	};
+	int num_irqs;
+	const struct bd7182x_irq_res *irqs;
+
+	switch (pwr->chip_type) {
+	case ROHM_CHIP_TYPE_BD71828:
+		irqs = &bd71828_irqs[0];
+		num_irqs = ARRAY_SIZE(bd71828_irqs);
+		break;
+	case ROHM_CHIP_TYPE_BD71815:
+		irqs = &bd71815_irqs[0];
+		num_irqs = ARRAY_SIZE(bd71815_irqs);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	for (i = 0; i < num_irqs; i++) {
+		irq = platform_get_irq_byname(pdev, irqs[i].name);
+
+		ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+						irqs[i].handler, 0,
+						irqs[i].name, pwr);
+		if (ret)
+			break;
+	}
+
+	return ret;
+}
+
+#define RSENS_DEFAULT_30MOHM 30000 /* 30 mOhm in uOhms*/
+
+static int bd7182x_get_rsens(struct bd71828_power *pwr)
+{
+	u64 tmp = RSENS_CURR;
+	int rsens_ohm = RSENS_DEFAULT_30MOHM;
+	struct fwnode_handle *node = NULL;
+
+	if (pwr->dev->parent)
+		node = dev_fwnode(pwr->dev->parent);
+
+	if (node) {
+		int ret;
+		u32 rs;
+
+		ret = fwnode_property_read_u32(node,
+					       "rohm,charger-sense-resistor-micro-ohms",
+					       &rs);
+		if (ret) {
+			if (ret == -EINVAL) {
+				rs = RSENS_DEFAULT_30MOHM;
+			} else {
+				dev_err(pwr->dev, "Bad RSENS dt property\n");
+				return ret;
+			}
+		}
+		if (!rs) {
+			dev_err(pwr->dev, "Bad RSENS value\n");
+			return -EINVAL;
+		}
+
+		rsens_ohm = (int)rs;
+	}
+
+	/* Reg val to uA */
+	do_div(tmp, rsens_ohm);
+
+	pwr->curr_factor = tmp;
+	pwr->rsens = rsens_ohm;
+	dev_dbg(pwr->dev, "Setting rsens to %u micro ohm\n", pwr->rsens);
+	dev_dbg(pwr->dev, "Setting curr-factor to %u\n", pwr->curr_factor);
+
+	return 0;
+}
+
+static int bd71828_power_probe(struct platform_device *pdev)
+{
+	struct bd71828_power *pwr;
+	struct power_supply_config ac_cfg = {};
+	struct power_supply_config bat_cfg = {};
+	int ret;
+	struct regmap *regmap;
+
+	regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!regmap) {
+		dev_err(&pdev->dev, "No parent regmap\n");
+		return -EINVAL;
+	}
+
+	pwr = devm_kzalloc(&pdev->dev, sizeof(*pwr), GFP_KERNEL);
+	if (!pwr)
+		return -ENOMEM;
+
+	pwr->regmap = regmap;
+	pwr->dev = &pdev->dev;
+	pwr->chip_type = platform_get_device_id(pdev)->driver_data;
+
+	switch (pwr->chip_type) {
+	case ROHM_CHIP_TYPE_BD71828:
+		pwr->bat_inserted = bd71828_bat_inserted;
+		pwr->get_temp = bd71828_get_temp;
+		pwr->regs = &pwr_regs_bd71828;
+		break;
+	case ROHM_CHIP_TYPE_BD71815:
+		pwr->bat_inserted = bd71815_bat_inserted;
+		pwr->get_temp = bd71815_get_temp;
+		pwr->regs = &pwr_regs_bd71815;
+		break;
+	default:
+		dev_err(pwr->dev, "Unknown PMIC\n");
+		return -EINVAL;
+	}
+
+	ret = bd7182x_get_rsens(pwr);
+	if (ret)
+		return dev_err_probe(&pdev->dev, ret, "sense resistor missing\n");
+
+	dev_set_drvdata(&pdev->dev, pwr);
+	bd71828_init_hardware(pwr);
+
+	bat_cfg.drv_data	= pwr;
+	bat_cfg.fwnode		= dev_fwnode(&pdev->dev);
+
+	ac_cfg.supplied_to	= bd71828_ac_supplied_to;
+	ac_cfg.num_supplicants	= ARRAY_SIZE(bd71828_ac_supplied_to);
+	ac_cfg.drv_data		= pwr;
+
+	pwr->ac = devm_power_supply_register(&pdev->dev, &bd71828_ac_desc,
+					     &ac_cfg);
+	if (IS_ERR(pwr->ac))
+		return dev_err_probe(&pdev->dev, PTR_ERR(pwr->ac),
+				     "failed to register ac\n");
+
+	pwr->bat = devm_power_supply_register(&pdev->dev, &bd71828_bat_desc,
+					      &bat_cfg);
+	if (IS_ERR(pwr->bat))
+		return dev_err_probe(&pdev->dev, PTR_ERR(pwr->bat),
+				     "failed to register bat\n");
+
+	ret = bd7182x_get_irqs(pdev, pwr);
+	if (ret)
+		return dev_err_probe(&pdev->dev, ret, "failed to request IRQs");
+
+	/* Configure wakeup capable */
+	device_set_wakeup_capable(pwr->dev, 1);
+	device_set_wakeup_enable(pwr->dev, 1);
+
+	return 0;
+}
+
+static const struct platform_device_id bd71828_charger_id[] = {
+	{ "bd71815-power", ROHM_CHIP_TYPE_BD71815 },
+	{ "bd71828-power", ROHM_CHIP_TYPE_BD71828 },
+	{ },
+};
+MODULE_DEVICE_TABLE(platform, bd71828_charger_id);
+
+static struct platform_driver bd71828_power_driver = {
+	.driver = {
+		.name = "bd718xx-power",
+	},
+	.probe = bd71828_power_probe,
+	.id_table = bd71828_charger_id,
+};
+
+module_platform_driver(bd71828_power_driver);
+
+MODULE_AUTHOR("Cong Pham <cpham2403@gmail.com>");
+MODULE_DESCRIPTION("ROHM BD718(15/28/78) PMIC Battery Charger driver");
+MODULE_LICENSE("GPL");

-- 
2.47.3
Re: [PATCH v4 2/3] power: supply: Add bd718(15/28/78) charger driver
Posted by Sebastian Reichel 2 weeks ago
Hi,

On Wed, Sep 17, 2025 at 07:48:03PM +0200, Andreas Kemnade wrote:
> Add charger driver for ROHM BD718(15/28/78) PMIC charger block.
> It is a stripped down version of the driver here:
> https://lore.kernel.org/lkml/dbd97c1b0d715aa35a8b4d79741e433d97c562aa.1637061794.git.matti.vaittinen@fi.rohmeurope.com/
> 
> For the ease of review and to do a step-by-step approach remove all the
> coloumb counter related stuff and do not sneak in BD71827 support. That
> also avoids non-trivial rebasing of the above series.
> 
> Changes besides that:
> Replace the custom property by a standard one and do not use megaohms
> for the current sense resistor.
> 
> Signed-off-by: Andreas Kemnade <andreas@kemnade.info>
> Reviewed-by: Matti Vaittinen <mazziesaccount@gmail.com>
> ---
>  drivers/power/supply/Kconfig         |    9 +
>  drivers/power/supply/Makefile        |    1 +
>  drivers/power/supply/bd71828-power.c | 1051 ++++++++++++++++++++++++++++++++++
>  3 files changed, 1061 insertions(+)
> 
> diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
> index 79ddb006e2dad..16dddb2355d9b 100644
> --- a/drivers/power/supply/Kconfig
> +++ b/drivers/power/supply/Kconfig
> @@ -974,6 +974,15 @@ config CHARGER_UCS1002
>  	  Say Y to enable support for Microchip UCS1002 Programmable
>  	  USB Port Power Controller with Charger Emulation.
>  
> +config CHARGER_BD71828
> +	tristate "Power-supply driver for ROHM BD71828 and BD71815 PMIC"
> +	depends on MFD_ROHM_BD71828
> +	help
> +	  Say Y here to enable support for charger and battery
> +	  in ROHM BD71815, BD71817, ROHM BD71828 power management
> +	  ICs. This driver gets various bits of information about battery
> +	  and charger states.
> +
>  config CHARGER_BD99954
>  	tristate "ROHM bd99954 charger driver"
>  	depends on I2C
> diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile
> index f943c9150b326..c6520a11f021c 100644
> --- a/drivers/power/supply/Makefile
> +++ b/drivers/power/supply/Makefile
> @@ -113,6 +113,7 @@ obj-$(CONFIG_CHARGER_SC2731)	+= sc2731_charger.o
>  obj-$(CONFIG_FUEL_GAUGE_SC27XX)	+= sc27xx_fuel_gauge.o
>  obj-$(CONFIG_FUEL_GAUGE_STC3117)       += stc3117_fuel_gauge.o
>  obj-$(CONFIG_CHARGER_UCS1002)	+= ucs1002_power.o
> +obj-$(CONFIG_CHARGER_BD71828)	+= bd71828-power.o
>  obj-$(CONFIG_CHARGER_BD99954)	+= bd99954-charger.o
>  obj-$(CONFIG_CHARGER_WILCO)	+= wilco-charger.o
>  obj-$(CONFIG_RN5T618_POWER)	+= rn5t618_power.o
> diff --git a/drivers/power/supply/bd71828-power.c b/drivers/power/supply/bd71828-power.c
> new file mode 100644
> index 0000000000000..fdd03159dc7bc
> --- /dev/null
> +++ b/drivers/power/supply/bd71828-power.c
> @@ -0,0 +1,1051 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/* ROHM BD71815, BD71828 and BD71878 Charger driver */
> +
> +#include <linux/delay.h>
> +#include <linux/init.h>

Not needed.

> +#include <linux/interrupt.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/rohm-bd71815.h>
> +#include <linux/mfd/rohm-bd71828.h>
> +#include <linux/module.h>
> +#include <linux/of.h>

No of_ is being used in this, so please don't include of.h.
You need linux/mod_devicetable.h instead, which you currently
get via of.h.

> +#include <linux/platform_device.h>
> +#include <linux/power_supply.h>
> +#include <linux/slab.h>
> +#include <linux/uaccess.h>

I don't think uaccess.h is needed either.

Otherwise:

Acked-by: Sebastian Reichel <sebastian.reichel@collabora.com>

Greetings,

-- Sebastian

> +
> +/* common defines */
> +#define BD7182x_MASK_VBAT_U			0x1f
> +#define BD7182x_MASK_VDCIN_U			0x0f
> +#define BD7182x_MASK_IBAT_U			0x3f
> +#define BD7182x_MASK_CURDIR_DISCHG		0x80
> +#define BD7182x_MASK_CHG_STATE			0x7f
> +#define BD7182x_MASK_BAT_TEMP			0x07
> +#define BD7182x_MASK_DCIN_DET			BIT(0)
> +#define BD7182x_MASK_CONF_PON			BIT(0)
> +#define BD71815_MASK_CONF_XSTB			BIT(1)
> +#define BD7182x_MASK_BAT_STAT			0x3f
> +#define BD7182x_MASK_DCIN_STAT			0x07
> +
> +#define BD7182x_MASK_WDT_AUTO			0x40
> +#define BD7182x_MASK_VBAT_ALM_LIMIT_U		0x01
> +#define BD7182x_MASK_CHG_EN			0x01
> +
> +#define BD7182x_DCIN_COLLAPSE_DEFAULT		0x36
> +
> +#define MAX_CURRENT_DEFAULT			890000		/* uA */
> +#define AC_NAME					"bd71828_ac"
> +#define BAT_NAME				"bd71828_bat"
> +
> +#define BAT_OPEN	0x7
> +
> +/*
> + * VBAT Low voltage detection Threshold
> + * 0x00D4*16mV = 212*0.016 = 3.392v
> + */
> +#define VBAT_LOW_TH			0x00D4
> +
> +struct pwr_regs {
> +	u8 vbat_avg;
> +	u8 ibat;
> +	u8 ibat_avg;
> +	u8 btemp_vth;
> +	u8 chg_state;
> +	u8 bat_temp;
> +	u8 dcin_stat;
> +	u8 dcin_collapse_limit;
> +	u8 chg_set1;
> +	u8 chg_en;
> +	u8 vbat_alm_limit_u;
> +	u8 conf;
> +	u8 vdcin;
> +};
> +
> +static const struct pwr_regs pwr_regs_bd71828 = {
> +	.vbat_avg = BD71828_REG_VBAT_U,
> +	.ibat = BD71828_REG_IBAT_U,
> +	.ibat_avg = BD71828_REG_IBAT_AVG_U,
> +	.btemp_vth = BD71828_REG_VM_BTMP_U,
> +	.chg_state = BD71828_REG_CHG_STATE,
> +	.bat_temp = BD71828_REG_BAT_TEMP,
> +	.dcin_stat = BD71828_REG_DCIN_STAT,
> +	.dcin_collapse_limit = BD71828_REG_DCIN_CLPS,
> +	.chg_set1 = BD71828_REG_CHG_SET1,
> +	.chg_en   = BD71828_REG_CHG_EN,
> +	.vbat_alm_limit_u = BD71828_REG_ALM_VBAT_LIMIT_U,
> +	.conf = BD71828_REG_CONF,
> +	.vdcin = BD71828_REG_VDCIN_U,
> +};
> +
> +static const struct pwr_regs pwr_regs_bd71815 = {
> +	.vbat_avg = BD71815_REG_VM_SA_VBAT_U,
> +	/* BD71815 does not have separate current and current avg */
> +	.ibat = BD71815_REG_CC_CURCD_U,
> +	.ibat_avg = BD71815_REG_CC_CURCD_U,
> +
> +	.btemp_vth = BD71815_REG_VM_BTMP,
> +	.chg_state = BD71815_REG_CHG_STATE,
> +	.bat_temp = BD71815_REG_BAT_TEMP,
> +	.dcin_stat = BD71815_REG_DCIN_STAT,
> +	.dcin_collapse_limit = BD71815_REG_DCIN_CLPS,
> +	.chg_set1 = BD71815_REG_CHG_SET1,
> +	.chg_en   = BD71815_REG_CHG_SET1,
> +	.vbat_alm_limit_u = BD71815_REG_ALM_VBAT_TH_U,
> +	.conf = BD71815_REG_CONF,
> +
> +	.vdcin = BD71815_REG_VM_DCIN_U,
> +};
> +
> +struct bd71828_power {
> +	struct regmap *regmap;
> +	enum rohm_chip_type chip_type;
> +	struct device *dev;
> +	struct power_supply *ac;
> +	struct power_supply *bat;
> +
> +	const struct pwr_regs *regs;
> +	/* Reg val to uA */
> +	int curr_factor;
> +	int rsens;
> +	int (*get_temp)(struct bd71828_power *pwr, int *temp);
> +	int (*bat_inserted)(struct bd71828_power *pwr);
> +};
> +
> +static int bd7182x_write16(struct bd71828_power *pwr, int reg, u16 val)
> +{
> +	__be16 tmp;
> +
> +	tmp = cpu_to_be16(val);
> +
> +	return regmap_bulk_write(pwr->regmap, reg, &tmp, sizeof(tmp));
> +}
> +
> +static int bd7182x_read16_himask(struct bd71828_power *pwr, int reg, int himask,
> +				 u16 *val)
> +{
> +	struct regmap *regmap = pwr->regmap;
> +	int ret;
> +	__be16 rvals;
> +	u8 *tmp = (u8 *)&rvals;
> +
> +	ret = regmap_bulk_read(regmap, reg, &rvals, sizeof(*val));
> +	if (!ret) {
> +		*tmp &= himask;
> +		*val = be16_to_cpu(rvals);
> +	}
> +
> +	return ret;
> +}
> +
> +static int bd71828_get_vbat(struct bd71828_power *pwr, int *vcell)
> +{
> +	u16 tmp_vcell;
> +	int ret;
> +
> +	ret = bd7182x_read16_himask(pwr, pwr->regs->vbat_avg,
> +				    BD7182x_MASK_VBAT_U, &tmp_vcell);
> +	if (ret)
> +		dev_err(pwr->dev, "Failed to read battery average voltage\n");
> +	else
> +		*vcell = ((int)tmp_vcell) * 1000;
> +
> +	return ret;
> +}
> +
> +static int bd71828_get_current_ds_adc(struct bd71828_power *pwr, int *curr, int *curr_avg)
> +{
> +	__be16 tmp_curr;
> +	char *tmp = (char *)&tmp_curr;
> +	int dir = 1;
> +	int regs[] = { pwr->regs->ibat, pwr->regs->ibat_avg };
> +	int *vals[] = { curr, curr_avg };
> +	int ret, i;
> +
> +	for (dir = 1, i = 0; i < ARRAY_SIZE(regs); i++) {
> +		ret = regmap_bulk_read(pwr->regmap, regs[i], &tmp_curr,
> +				       sizeof(tmp_curr));
> +		if (ret)
> +			break;
> +
> +		if (*tmp & BD7182x_MASK_CURDIR_DISCHG)
> +			dir = -1;
> +
> +		*tmp &= BD7182x_MASK_IBAT_U;
> +
> +		*vals[i] = dir * ((int)be16_to_cpu(tmp_curr)) * pwr->curr_factor;
> +	}
> +
> +	return ret;
> +}
> +
> +/* Unit is tenths of degree C */
> +static int bd71815_get_temp(struct bd71828_power *pwr, int *temp)
> +{
> +	struct regmap *regmap = pwr->regmap;
> +	int ret;
> +	int t;
> +
> +	ret = regmap_read(regmap, pwr->regs->btemp_vth, &t);
> +	if (ret)
> +		return ret;
> +
> +	t = 200 - t;
> +
> +	if (t > 200) {
> +		dev_err(pwr->dev, "Failed to read battery temperature\n");
> +		return -ENODATA;
> +	}
> +
> +	return 0;
> +}
> +
> +/* Unit is tenths of degree C */
> +static int bd71828_get_temp(struct bd71828_power *pwr, int *temp)
> +{
> +	u16 t;
> +	int ret;
> +	int tmp = 200 * 10000;
> +
> +	ret = bd7182x_read16_himask(pwr, pwr->regs->btemp_vth,
> +				    BD71828_MASK_VM_BTMP_U, &t);
> +	if (ret)
> +		return ret;
> +
> +	if (t > 3200) {
> +		dev_err(pwr->dev,
> +			"Failed to read battery temperature\n");
> +		return -ENODATA;
> +	}
> +
> +	tmp -= 625ULL * (unsigned int)t;
> +	*temp = tmp / 1000;
> +
> +	return ret;
> +}
> +
> +static int bd71828_charge_status(struct bd71828_power *pwr,
> +				 int *s, int *h)
> +{
> +	unsigned int state;
> +	int status, health;
> +	int ret = 1;
> +
> +	ret = regmap_read(pwr->regmap, pwr->regs->chg_state, &state);
> +	if (ret) {
> +		dev_err(pwr->dev, "charger status reading failed (%d)\n", ret);
> +		return ret;
> +	}
> +
> +	state &= BD7182x_MASK_CHG_STATE;
> +
> +	dev_dbg(pwr->dev, "CHG_STATE %d\n", state);
> +
> +	switch (state) {
> +	case 0x00:
> +		status = POWER_SUPPLY_STATUS_DISCHARGING;
> +		health = POWER_SUPPLY_HEALTH_GOOD;
> +		break;
> +	case 0x01:
> +	case 0x02:
> +	case 0x03:
> +	case 0x0E:
> +		status = POWER_SUPPLY_STATUS_CHARGING;
> +		health = POWER_SUPPLY_HEALTH_GOOD;
> +		break;
> +	case 0x0F:
> +		status = POWER_SUPPLY_STATUS_FULL;
> +		health = POWER_SUPPLY_HEALTH_GOOD;
> +		break;
> +	case 0x10:
> +	case 0x11:
> +	case 0x12:
> +	case 0x13:
> +	case 0x14:
> +	case 0x20:
> +	case 0x21:
> +	case 0x22:
> +	case 0x23:
> +	case 0x24:
> +		status = POWER_SUPPLY_STATUS_NOT_CHARGING;
> +		health = POWER_SUPPLY_HEALTH_OVERHEAT;
> +		break;
> +	case 0x30:
> +	case 0x31:
> +	case 0x32:
> +	case 0x40:
> +		status = POWER_SUPPLY_STATUS_DISCHARGING;
> +		health = POWER_SUPPLY_HEALTH_GOOD;
> +		break;
> +	case 0x7f:
> +	default:
> +		status = POWER_SUPPLY_STATUS_NOT_CHARGING;
> +		health = POWER_SUPPLY_HEALTH_DEAD;
> +		break;
> +	}
> +
> +	if (s)
> +		*s = status;
> +	if (h)
> +		*h = health;
> +
> +	return ret;
> +}
> +
> +static int get_chg_online(struct bd71828_power *pwr, int *chg_online)
> +{
> +	int r, ret;
> +
> +	ret = regmap_read(pwr->regmap, pwr->regs->dcin_stat, &r);
> +	if (ret) {
> +		dev_err(pwr->dev, "Failed to read DCIN status\n");
> +		return ret;
> +	}
> +	*chg_online = ((r & BD7182x_MASK_DCIN_DET) != 0);
> +
> +	return 0;
> +}
> +
> +static int get_bat_online(struct bd71828_power *pwr, int *bat_online)
> +{
> +	int r, ret;
> +
> +	ret = regmap_read(pwr->regmap, pwr->regs->bat_temp, &r);
> +	if (ret) {
> +		dev_err(pwr->dev, "Failed to read battery temperature\n");
> +		return ret;
> +	}
> +	*bat_online = ((r & BD7182x_MASK_BAT_TEMP) != BAT_OPEN);
> +
> +	return 0;
> +}
> +
> +static int bd71828_bat_inserted(struct bd71828_power *pwr)
> +{
> +	int ret, val;
> +
> +	ret = regmap_read(pwr->regmap, pwr->regs->conf, &val);
> +	if (ret) {
> +		dev_err(pwr->dev, "Failed to read CONF register\n");
> +		return 0;
> +	}
> +	ret = val & BD7182x_MASK_CONF_PON;
> +
> +	if (ret)
> +		regmap_update_bits(pwr->regmap, pwr->regs->conf,
> +				   BD7182x_MASK_CONF_PON, 0);
> +
> +	return ret;
> +}
> +
> +static int bd71815_bat_inserted(struct bd71828_power *pwr)
> +{
> +	int ret, val;
> +
> +	ret = regmap_read(pwr->regmap, pwr->regs->conf, &val);
> +	if (ret) {
> +		dev_err(pwr->dev, "Failed to read CONF register\n");
> +		return ret;
> +	}
> +
> +	ret = !(val & BD71815_MASK_CONF_XSTB);
> +	if (ret)
> +		regmap_write(pwr->regmap, pwr->regs->conf,  val |
> +			     BD71815_MASK_CONF_XSTB);
> +
> +	return ret;
> +}
> +
> +static int bd71828_init_hardware(struct bd71828_power *pwr)
> +{
> +	int ret;
> +
> +	/* TODO: Collapse limit should come from device-tree ? */
> +	ret = regmap_write(pwr->regmap, pwr->regs->dcin_collapse_limit,
> +			   BD7182x_DCIN_COLLAPSE_DEFAULT);
> +	if (ret) {
> +		dev_err(pwr->dev, "Failed to write DCIN collapse limit\n");
> +		return ret;
> +	}
> +
> +	ret = pwr->bat_inserted(pwr);
> +	if (ret < 0)
> +		return ret;
> +
> +	if (ret) {
> +		/* WDT_FST auto set */
> +		ret = regmap_update_bits(pwr->regmap, pwr->regs->chg_set1,
> +					 BD7182x_MASK_WDT_AUTO,
> +					 BD7182x_MASK_WDT_AUTO);
> +		if (ret)
> +			return ret;
> +
> +		ret = bd7182x_write16(pwr, pwr->regs->vbat_alm_limit_u,
> +				      VBAT_LOW_TH);
> +		if (ret)
> +			return ret;
> +
> +		/*
> +		 * On BD71815 "we mask the power-state" from relax detection.
> +		 * I am unsure what the impact of the power-state would be if
> +		 * we didn't - but this is what the vendor driver did - and
> +		 * that driver has been used in few projects so I just assume
> +		 * this is needed.
> +		 */
> +		if (pwr->chip_type == ROHM_CHIP_TYPE_BD71815) {
> +			ret = regmap_set_bits(pwr->regmap,
> +					      BD71815_REG_REX_CTRL_1,
> +					      REX_PMU_STATE_MASK);
> +			if (ret)
> +				return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int bd71828_charger_get_property(struct power_supply *psy,
> +					enum power_supply_property psp,
> +					union power_supply_propval *val)
> +{
> +	struct bd71828_power *pwr = dev_get_drvdata(psy->dev.parent);
> +	u32 vot;
> +	u16 tmp;
> +	int online;
> +	int ret;
> +
> +	switch (psp) {
> +	case POWER_SUPPLY_PROP_ONLINE:
> +		ret = get_chg_online(pwr, &online);
> +		if (!ret)
> +			val->intval = online;
> +		break;
> +	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
> +		ret = bd7182x_read16_himask(pwr, pwr->regs->vdcin,
> +					    BD7182x_MASK_VDCIN_U, &tmp);
> +		if (ret)
> +			return ret;
> +
> +		vot = tmp;
> +		/* 5 milli volt steps */
> +		val->intval = 5000 * vot;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int bd71828_battery_get_property(struct power_supply *psy,
> +					enum power_supply_property psp,
> +					union power_supply_propval *val)
> +{
> +	struct bd71828_power *pwr = dev_get_drvdata(psy->dev.parent);
> +	int ret = 0;
> +	int status, health, tmp, curr, curr_avg, chg_en;
> +
> +	if (psp == POWER_SUPPLY_PROP_STATUS ||
> +	    psp == POWER_SUPPLY_PROP_HEALTH ||
> +	    psp == POWER_SUPPLY_PROP_CHARGE_TYPE)
> +		ret = bd71828_charge_status(pwr, &status, &health);
> +	else if (psp == POWER_SUPPLY_PROP_CURRENT_AVG ||
> +		 psp == POWER_SUPPLY_PROP_CURRENT_NOW)
> +		ret = bd71828_get_current_ds_adc(pwr, &curr, &curr_avg);
> +	if (ret)
> +		return ret;
> +
> +	switch (psp) {
> +	case POWER_SUPPLY_PROP_STATUS:
> +		val->intval = status;
> +		break;
> +	case POWER_SUPPLY_PROP_HEALTH:
> +		val->intval = health;
> +		break;
> +	case POWER_SUPPLY_PROP_PRESENT:
> +		ret = get_bat_online(pwr, &tmp);
> +		if (!ret)
> +			val->intval = tmp;
> +		break;
> +	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
> +		ret = bd71828_get_vbat(pwr, &tmp);
> +		val->intval = tmp;
> +		break;
> +	case POWER_SUPPLY_PROP_TECHNOLOGY:
> +		val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
> +		break;
> +	case POWER_SUPPLY_PROP_CURRENT_AVG:
> +		val->intval = curr_avg;
> +		break;
> +	case POWER_SUPPLY_PROP_CURRENT_NOW:
> +		val->intval = curr;
> +		break;
> +	case POWER_SUPPLY_PROP_CURRENT_MAX:
> +		val->intval = MAX_CURRENT_DEFAULT;
> +		break;
> +	case POWER_SUPPLY_PROP_TEMP:
> +		ret = pwr->get_temp(pwr, &val->intval);
> +		break;
> +	case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
> +		ret = regmap_read(pwr->regmap, pwr->regs->chg_en, &chg_en);
> +		if (ret)
> +			return ret;
> +
> +		val->intval = (chg_en & BD7182x_MASK_CHG_EN) ?
> +			POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO :
> +			POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE;
> +		break;
> +	default:
> +		ret = -EINVAL;
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +static int bd71828_battery_set_property(struct power_supply *psy,
> +					enum power_supply_property psp,
> +					const union power_supply_propval *val)
> +{
> +	struct bd71828_power *pwr = dev_get_drvdata(psy->dev.parent);
> +	int ret = 0;
> +
> +	switch (psp) {
> +	case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
> +		if (val->intval == POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO)
> +			ret = regmap_update_bits(pwr->regmap, pwr->regs->chg_en,
> +						 BD7182x_MASK_CHG_EN,
> +						 BD7182x_MASK_CHG_EN);
> +		else
> +			ret = regmap_update_bits(pwr->regmap, pwr->regs->chg_en,
> +						 BD7182x_MASK_CHG_EN,
> +						 0);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return ret;
> +}
> +
> +static int bd71828_battery_property_is_writeable(struct power_supply *psy,
> +						 enum power_supply_property psp)
> +{
> +	switch (psp) {
> +	case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +/** @brief ac properties */
> +static const enum power_supply_property bd71828_charger_props[] = {
> +	POWER_SUPPLY_PROP_ONLINE,
> +	POWER_SUPPLY_PROP_VOLTAGE_NOW,
> +};
> +
> +static const enum power_supply_property bd71828_battery_props[] = {
> +	POWER_SUPPLY_PROP_STATUS,
> +	POWER_SUPPLY_PROP_HEALTH,
> +	POWER_SUPPLY_PROP_VOLTAGE_NOW,
> +	POWER_SUPPLY_PROP_HEALTH,
> +	POWER_SUPPLY_PROP_PRESENT,
> +	POWER_SUPPLY_PROP_TECHNOLOGY,
> +	POWER_SUPPLY_PROP_TEMP,
> +	POWER_SUPPLY_PROP_CURRENT_AVG,
> +	POWER_SUPPLY_PROP_CURRENT_NOW,
> +	POWER_SUPPLY_PROP_CURRENT_MAX,
> +	POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR,
> +};
> +
> +/** @brief powers supplied by bd71828_ac */
> +static char *bd71828_ac_supplied_to[] = {
> +	BAT_NAME,
> +};
> +
> +static const struct power_supply_desc bd71828_ac_desc = {
> +	.name		= AC_NAME,
> +	.type		= POWER_SUPPLY_TYPE_MAINS,
> +	.properties	= bd71828_charger_props,
> +	.num_properties	= ARRAY_SIZE(bd71828_charger_props),
> +	.get_property	= bd71828_charger_get_property,
> +};
> +
> +static const struct power_supply_desc bd71828_bat_desc = {
> +	.name		= BAT_NAME,
> +	.type		= POWER_SUPPLY_TYPE_BATTERY,
> +	.charge_behaviours = BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO) |
> +			     BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE),
> +	.properties	= bd71828_battery_props,
> +	.num_properties = ARRAY_SIZE(bd71828_battery_props),
> +	.get_property	= bd71828_battery_get_property,
> +	.set_property	= bd71828_battery_set_property,
> +	.property_is_writeable   = bd71828_battery_property_is_writeable,
> +};
> +
> +#define RSENS_CURR 10000000LLU
> +
> +#define BD_ISR_NAME(name) \
> +bd7181x_##name##_isr
> +
> +#define BD_ISR_BAT(name, print, run_gauge)				\
> +static irqreturn_t BD_ISR_NAME(name)(int irq, void *data)		\
> +{									\
> +	struct bd71828_power *pwr = (struct bd71828_power *)data;	\
> +									\
> +	dev_dbg(pwr->dev, "%s\n", print);				\
> +	power_supply_changed(pwr->bat);				\
> +									\
> +	return IRQ_HANDLED;						\
> +}
> +
> +#define BD_ISR_AC(name, print, run_gauge)				\
> +static irqreturn_t BD_ISR_NAME(name)(int irq, void *data)		\
> +{									\
> +	struct bd71828_power *pwr = (struct bd71828_power *)data;	\
> +									\
> +	power_supply_changed(pwr->ac);					\
> +	dev_dbg(pwr->dev, "%s\n", print);				\
> +	power_supply_changed(pwr->bat);				\
> +									\
> +	return IRQ_HANDLED;						\
> +}
> +
> +#define BD_ISR_DUMMY(name, print)					\
> +static irqreturn_t BD_ISR_NAME(name)(int irq, void *data)		\
> +{									\
> +	struct bd71828_power *pwr = (struct bd71828_power *)data;	\
> +									\
> +	dev_dbg(pwr->dev, "%s\n", print);				\
> +									\
> +	return IRQ_HANDLED;						\
> +}
> +
> +BD_ISR_BAT(chg_state_changed, "CHG state changed", true)
> +/* DCIN voltage changes */
> +BD_ISR_AC(dcin_removed, "DCIN removed", true)
> +BD_ISR_AC(clps_out, "DCIN voltage back to normal", true)
> +BD_ISR_AC(clps_in, "DCIN voltage collapsed", false)
> +BD_ISR_AC(dcin_ovp_res, "DCIN voltage normal", true)
> +BD_ISR_AC(dcin_ovp_det, "DCIN OVER VOLTAGE", true)
> +
> +BD_ISR_DUMMY(dcin_mon_det, "DCIN voltage below threshold")
> +BD_ISR_DUMMY(dcin_mon_res, "DCIN voltage above threshold")
> +
> +BD_ISR_DUMMY(vsys_uv_res, "VSYS under-voltage cleared")
> +BD_ISR_DUMMY(vsys_uv_det, "VSYS under-voltage")
> +BD_ISR_DUMMY(vsys_low_res, "'VSYS low' cleared")
> +BD_ISR_DUMMY(vsys_low_det, "VSYS low")
> +BD_ISR_DUMMY(vsys_mon_res, "VSYS mon - resumed")
> +BD_ISR_DUMMY(vsys_mon_det, "VSYS mon - detected")
> +BD_ISR_BAT(chg_wdg_temp, "charger temperature watchdog triggered", true)
> +BD_ISR_BAT(chg_wdg, "charging watchdog triggered", true)
> +BD_ISR_BAT(bat_removed, "Battery removed", true)
> +BD_ISR_BAT(bat_det, "Battery detected", true)
> +/* TODO: Verify the meaning of these interrupts */
> +BD_ISR_BAT(rechg_det, "Recharging", true)
> +BD_ISR_BAT(rechg_res, "Recharge ending", true)
> +BD_ISR_DUMMY(temp_transit, "Temperature transition")
> +BD_ISR_BAT(therm_rmv, "bd71815-therm-rmv", false)
> +BD_ISR_BAT(therm_det, "bd71815-therm-det", true)
> +BD_ISR_BAT(bat_dead, "bd71815-bat-dead", false)
> +BD_ISR_BAT(bat_short_res, "bd71815-bat-short-res", true)
> +BD_ISR_BAT(bat_short, "bd71815-bat-short-det", false)
> +BD_ISR_BAT(bat_low_res, "bd71815-bat-low-res", true)
> +BD_ISR_BAT(bat_low, "bd71815-bat-low-det", true)
> +BD_ISR_BAT(bat_ov_res, "bd71815-bat-over-res", true)
> +/* What should we do here? */
> +BD_ISR_BAT(bat_ov, "bd71815-bat-over-det", false)
> +BD_ISR_BAT(bat_mon_res, "bd71815-bat-mon-res", true)
> +BD_ISR_BAT(bat_mon, "bd71815-bat-mon-det", true)
> +BD_ISR_BAT(bat_cc_mon, "bd71815-bat-cc-mon2", false)
> +BD_ISR_BAT(bat_oc1_res, "bd71815-bat-oc1-res", true)
> +BD_ISR_BAT(bat_oc1, "bd71815-bat-oc1-det", false)
> +BD_ISR_BAT(bat_oc2_res, "bd71815-bat-oc2-res", true)
> +BD_ISR_BAT(bat_oc2, "bd71815-bat-oc2-det", false)
> +BD_ISR_BAT(bat_oc3_res, "bd71815-bat-oc3-res", true)
> +BD_ISR_BAT(bat_oc3, "bd71815-bat-oc3-det", false)
> +BD_ISR_BAT(temp_bat_low_res, "bd71815-temp-bat-low-res", true)
> +BD_ISR_BAT(temp_bat_low, "bd71815-temp-bat-low-det", true)
> +BD_ISR_BAT(temp_bat_hi_res, "bd71815-temp-bat-hi-res", true)
> +BD_ISR_BAT(temp_bat_hi, "bd71815-temp-bat-hi-det", true)
> +
> +static irqreturn_t bd7182x_dcin_removed(int irq, void *data)
> +{
> +	struct bd71828_power *pwr = (struct bd71828_power *)data;
> +
> +	power_supply_changed(pwr->ac);
> +	dev_dbg(pwr->dev, "DCIN removed\n");
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t bd718x7_chg_done(int irq, void *data)
> +{
> +	struct bd71828_power *pwr = (struct bd71828_power *)data;
> +
> +	power_supply_changed(pwr->bat);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t bd7182x_dcin_detected(int irq, void *data)
> +{
> +	struct bd71828_power *pwr = (struct bd71828_power *)data;
> +
> +	dev_dbg(pwr->dev, "DCIN inserted\n");
> +	power_supply_changed(pwr->ac);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t bd71828_vbat_low_res(int irq, void *data)
> +{
> +	struct bd71828_power *pwr = (struct bd71828_power *)data;
> +
> +	dev_dbg(pwr->dev, "VBAT LOW Resumed\n");
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t bd71828_vbat_low_det(int irq, void *data)
> +{
> +	struct bd71828_power *pwr = (struct bd71828_power *)data;
> +
> +	dev_dbg(pwr->dev, "VBAT LOW Detected\n");
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t bd71828_temp_bat_hi_det(int irq, void *data)
> +{
> +	struct bd71828_power *pwr = (struct bd71828_power *)data;
> +
> +	dev_warn(pwr->dev, "Overtemp Detected\n");
> +	power_supply_changed(pwr->bat);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t bd71828_temp_bat_hi_res(int irq, void *data)
> +{
> +	struct bd71828_power *pwr = (struct bd71828_power *)data;
> +
> +	dev_dbg(pwr->dev, "Overtemp Resumed\n");
> +	power_supply_changed(pwr->bat);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t bd71828_temp_bat_low_det(int irq, void *data)
> +{
> +	struct bd71828_power *pwr = (struct bd71828_power *)data;
> +
> +	dev_dbg(pwr->dev, "Lowtemp Detected\n");
> +	power_supply_changed(pwr->bat);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t bd71828_temp_bat_low_res(int irq, void *data)
> +{
> +	struct bd71828_power *pwr = (struct bd71828_power *)data;
> +
> +	dev_dbg(pwr->dev, "Lowtemp Resumed\n");
> +	power_supply_changed(pwr->bat);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t bd71828_temp_vf_det(int irq, void *data)
> +{
> +	struct bd71828_power *pwr = (struct bd71828_power *)data;
> +
> +	dev_dbg(pwr->dev, "VF Detected\n");
> +	power_supply_changed(pwr->bat);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t bd71828_temp_vf_res(int irq, void *data)
> +{
> +	struct bd71828_power *pwr = (struct bd71828_power *)data;
> +
> +	dev_dbg(pwr->dev, "VF Resumed\n");
> +	power_supply_changed(pwr->bat);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t bd71828_temp_vf125_det(int irq, void *data)
> +{
> +	struct bd71828_power *pwr = (struct bd71828_power *)data;
> +
> +	dev_dbg(pwr->dev, "VF125 Detected\n");
> +	power_supply_changed(pwr->bat);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t bd71828_temp_vf125_res(int irq, void *data)
> +{
> +	struct bd71828_power *pwr = (struct bd71828_power *)data;
> +
> +	dev_dbg(pwr->dev, "VF125 Resumed\n");
> +	power_supply_changed(pwr->bat);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +struct bd7182x_irq_res {
> +	const char *name;
> +	irq_handler_t handler;
> +};
> +
> +#define BDIRQ(na, hn) { .name = (na), .handler = (hn) }
> +
> +static int bd7182x_get_irqs(struct platform_device *pdev,
> +			    struct bd71828_power *pwr)
> +{
> +	int i, irq, ret;
> +	static const struct bd7182x_irq_res bd71815_irqs[] = {
> +		BDIRQ("bd71815-dcin-rmv", BD_ISR_NAME(dcin_removed)),
> +		BDIRQ("bd71815-dcin-clps-out", BD_ISR_NAME(clps_out)),
> +		BDIRQ("bd71815-dcin-clps-in", BD_ISR_NAME(clps_in)),
> +		BDIRQ("bd71815-dcin-ovp-res", BD_ISR_NAME(dcin_ovp_res)),
> +		BDIRQ("bd71815-dcin-ovp-det", BD_ISR_NAME(dcin_ovp_det)),
> +		BDIRQ("bd71815-dcin-mon-res", BD_ISR_NAME(dcin_mon_res)),
> +		BDIRQ("bd71815-dcin-mon-det", BD_ISR_NAME(dcin_mon_det)),
> +
> +		BDIRQ("bd71815-vsys-uv-res", BD_ISR_NAME(vsys_uv_res)),
> +		BDIRQ("bd71815-vsys-uv-det", BD_ISR_NAME(vsys_uv_det)),
> +		BDIRQ("bd71815-vsys-low-res", BD_ISR_NAME(vsys_low_res)),
> +		BDIRQ("bd71815-vsys-low-det",  BD_ISR_NAME(vsys_low_det)),
> +		BDIRQ("bd71815-vsys-mon-res",  BD_ISR_NAME(vsys_mon_res)),
> +		BDIRQ("bd71815-vsys-mon-det",  BD_ISR_NAME(vsys_mon_det)),
> +		BDIRQ("bd71815-chg-wdg-temp", BD_ISR_NAME(chg_wdg_temp)),
> +		BDIRQ("bd71815-chg-wdg",  BD_ISR_NAME(chg_wdg)),
> +		BDIRQ("bd71815-rechg-det", BD_ISR_NAME(rechg_det)),
> +		BDIRQ("bd71815-rechg-res", BD_ISR_NAME(rechg_res)),
> +		BDIRQ("bd71815-ranged-temp-transit", BD_ISR_NAME(temp_transit)),
> +		BDIRQ("bd71815-chg-state-change", BD_ISR_NAME(chg_state_changed)),
> +		BDIRQ("bd71815-bat-temp-normal", bd71828_temp_bat_hi_res),
> +		BDIRQ("bd71815-bat-temp-erange", bd71828_temp_bat_hi_det),
> +		BDIRQ("bd71815-bat-rmv", BD_ISR_NAME(bat_removed)),
> +		BDIRQ("bd71815-bat-det", BD_ISR_NAME(bat_det)),
> +
> +		/* Add ISRs for these */
> +		BDIRQ("bd71815-therm-rmv", BD_ISR_NAME(therm_rmv)),
> +		BDIRQ("bd71815-therm-det", BD_ISR_NAME(therm_det)),
> +		BDIRQ("bd71815-bat-dead", BD_ISR_NAME(bat_dead)),
> +		BDIRQ("bd71815-bat-short-res", BD_ISR_NAME(bat_short_res)),
> +		BDIRQ("bd71815-bat-short-det", BD_ISR_NAME(bat_short)),
> +		BDIRQ("bd71815-bat-low-res", BD_ISR_NAME(bat_low_res)),
> +		BDIRQ("bd71815-bat-low-det", BD_ISR_NAME(bat_low)),
> +		BDIRQ("bd71815-bat-over-res", BD_ISR_NAME(bat_ov_res)),
> +		BDIRQ("bd71815-bat-over-det", BD_ISR_NAME(bat_ov)),
> +		BDIRQ("bd71815-bat-mon-res", BD_ISR_NAME(bat_mon_res)),
> +		BDIRQ("bd71815-bat-mon-det", BD_ISR_NAME(bat_mon)),
> +		/* cc-mon 1 & 3 ? */
> +		BDIRQ("bd71815-bat-cc-mon2", BD_ISR_NAME(bat_cc_mon)),
> +		BDIRQ("bd71815-bat-oc1-res", BD_ISR_NAME(bat_oc1_res)),
> +		BDIRQ("bd71815-bat-oc1-det", BD_ISR_NAME(bat_oc1)),
> +		BDIRQ("bd71815-bat-oc2-res", BD_ISR_NAME(bat_oc2_res)),
> +		BDIRQ("bd71815-bat-oc2-det", BD_ISR_NAME(bat_oc2)),
> +		BDIRQ("bd71815-bat-oc3-res", BD_ISR_NAME(bat_oc3_res)),
> +		BDIRQ("bd71815-bat-oc3-det", BD_ISR_NAME(bat_oc3)),
> +		BDIRQ("bd71815-temp-bat-low-res", BD_ISR_NAME(temp_bat_low_res)),
> +		BDIRQ("bd71815-temp-bat-low-det", BD_ISR_NAME(temp_bat_low)),
> +		BDIRQ("bd71815-temp-bat-hi-res", BD_ISR_NAME(temp_bat_hi_res)),
> +		BDIRQ("bd71815-temp-bat-hi-det", BD_ISR_NAME(temp_bat_hi)),
> +		/*
> +		 * TODO: add rest of the IRQs and re-check the handling.
> +		 * Check the bd71815-bat-cc-mon1, bd71815-bat-cc-mon3,
> +		 * bd71815-bat-low-res, bd71815-bat-low-det,
> +		 * bd71815-bat-hi-res, bd71815-bat-hi-det.
> +		 */
> +	};
> +	static const struct bd7182x_irq_res bd71828_irqs[] = {
> +		BDIRQ("bd71828-chg-done", bd718x7_chg_done),
> +		BDIRQ("bd71828-pwr-dcin-in", bd7182x_dcin_detected),
> +		BDIRQ("bd71828-pwr-dcin-out", bd7182x_dcin_removed),
> +		BDIRQ("bd71828-vbat-normal", bd71828_vbat_low_res),
> +		BDIRQ("bd71828-vbat-low", bd71828_vbat_low_det),
> +		BDIRQ("bd71828-btemp-hi", bd71828_temp_bat_hi_det),
> +		BDIRQ("bd71828-btemp-cool", bd71828_temp_bat_hi_res),
> +		BDIRQ("bd71828-btemp-lo", bd71828_temp_bat_low_det),
> +		BDIRQ("bd71828-btemp-warm", bd71828_temp_bat_low_res),
> +		BDIRQ("bd71828-temp-hi", bd71828_temp_vf_det),
> +		BDIRQ("bd71828-temp-norm", bd71828_temp_vf_res),
> +		BDIRQ("bd71828-temp-125-over", bd71828_temp_vf125_det),
> +		BDIRQ("bd71828-temp-125-under", bd71828_temp_vf125_res),
> +	};
> +	int num_irqs;
> +	const struct bd7182x_irq_res *irqs;
> +
> +	switch (pwr->chip_type) {
> +	case ROHM_CHIP_TYPE_BD71828:
> +		irqs = &bd71828_irqs[0];
> +		num_irqs = ARRAY_SIZE(bd71828_irqs);
> +		break;
> +	case ROHM_CHIP_TYPE_BD71815:
> +		irqs = &bd71815_irqs[0];
> +		num_irqs = ARRAY_SIZE(bd71815_irqs);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	for (i = 0; i < num_irqs; i++) {
> +		irq = platform_get_irq_byname(pdev, irqs[i].name);
> +
> +		ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
> +						irqs[i].handler, 0,
> +						irqs[i].name, pwr);
> +		if (ret)
> +			break;
> +	}
> +
> +	return ret;
> +}
> +
> +#define RSENS_DEFAULT_30MOHM 30000 /* 30 mOhm in uOhms*/
> +
> +static int bd7182x_get_rsens(struct bd71828_power *pwr)
> +{
> +	u64 tmp = RSENS_CURR;
> +	int rsens_ohm = RSENS_DEFAULT_30MOHM;
> +	struct fwnode_handle *node = NULL;
> +
> +	if (pwr->dev->parent)
> +		node = dev_fwnode(pwr->dev->parent);
> +
> +	if (node) {
> +		int ret;
> +		u32 rs;
> +
> +		ret = fwnode_property_read_u32(node,
> +					       "rohm,charger-sense-resistor-micro-ohms",
> +					       &rs);
> +		if (ret) {
> +			if (ret == -EINVAL) {
> +				rs = RSENS_DEFAULT_30MOHM;
> +			} else {
> +				dev_err(pwr->dev, "Bad RSENS dt property\n");
> +				return ret;
> +			}
> +		}
> +		if (!rs) {
> +			dev_err(pwr->dev, "Bad RSENS value\n");
> +			return -EINVAL;
> +		}
> +
> +		rsens_ohm = (int)rs;
> +	}
> +
> +	/* Reg val to uA */
> +	do_div(tmp, rsens_ohm);
> +
> +	pwr->curr_factor = tmp;
> +	pwr->rsens = rsens_ohm;
> +	dev_dbg(pwr->dev, "Setting rsens to %u micro ohm\n", pwr->rsens);
> +	dev_dbg(pwr->dev, "Setting curr-factor to %u\n", pwr->curr_factor);
> +
> +	return 0;
> +}
> +
> +static int bd71828_power_probe(struct platform_device *pdev)
> +{
> +	struct bd71828_power *pwr;
> +	struct power_supply_config ac_cfg = {};
> +	struct power_supply_config bat_cfg = {};
> +	int ret;
> +	struct regmap *regmap;
> +
> +	regmap = dev_get_regmap(pdev->dev.parent, NULL);
> +	if (!regmap) {
> +		dev_err(&pdev->dev, "No parent regmap\n");
> +		return -EINVAL;
> +	}
> +
> +	pwr = devm_kzalloc(&pdev->dev, sizeof(*pwr), GFP_KERNEL);
> +	if (!pwr)
> +		return -ENOMEM;
> +
> +	pwr->regmap = regmap;
> +	pwr->dev = &pdev->dev;
> +	pwr->chip_type = platform_get_device_id(pdev)->driver_data;
> +
> +	switch (pwr->chip_type) {
> +	case ROHM_CHIP_TYPE_BD71828:
> +		pwr->bat_inserted = bd71828_bat_inserted;
> +		pwr->get_temp = bd71828_get_temp;
> +		pwr->regs = &pwr_regs_bd71828;
> +		break;
> +	case ROHM_CHIP_TYPE_BD71815:
> +		pwr->bat_inserted = bd71815_bat_inserted;
> +		pwr->get_temp = bd71815_get_temp;
> +		pwr->regs = &pwr_regs_bd71815;
> +		break;
> +	default:
> +		dev_err(pwr->dev, "Unknown PMIC\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = bd7182x_get_rsens(pwr);
> +	if (ret)
> +		return dev_err_probe(&pdev->dev, ret, "sense resistor missing\n");
> +
> +	dev_set_drvdata(&pdev->dev, pwr);
> +	bd71828_init_hardware(pwr);
> +
> +	bat_cfg.drv_data	= pwr;
> +	bat_cfg.fwnode		= dev_fwnode(&pdev->dev);
> +
> +	ac_cfg.supplied_to	= bd71828_ac_supplied_to;
> +	ac_cfg.num_supplicants	= ARRAY_SIZE(bd71828_ac_supplied_to);
> +	ac_cfg.drv_data		= pwr;
> +
> +	pwr->ac = devm_power_supply_register(&pdev->dev, &bd71828_ac_desc,
> +					     &ac_cfg);
> +	if (IS_ERR(pwr->ac))
> +		return dev_err_probe(&pdev->dev, PTR_ERR(pwr->ac),
> +				     "failed to register ac\n");
> +
> +	pwr->bat = devm_power_supply_register(&pdev->dev, &bd71828_bat_desc,
> +					      &bat_cfg);
> +	if (IS_ERR(pwr->bat))
> +		return dev_err_probe(&pdev->dev, PTR_ERR(pwr->bat),
> +				     "failed to register bat\n");
> +
> +	ret = bd7182x_get_irqs(pdev, pwr);
> +	if (ret)
> +		return dev_err_probe(&pdev->dev, ret, "failed to request IRQs");
> +
> +	/* Configure wakeup capable */
> +	device_set_wakeup_capable(pwr->dev, 1);
> +	device_set_wakeup_enable(pwr->dev, 1);
> +
> +	return 0;
> +}
> +
> +static const struct platform_device_id bd71828_charger_id[] = {
> +	{ "bd71815-power", ROHM_CHIP_TYPE_BD71815 },
> +	{ "bd71828-power", ROHM_CHIP_TYPE_BD71828 },
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(platform, bd71828_charger_id);
> +
> +static struct platform_driver bd71828_power_driver = {
> +	.driver = {
> +		.name = "bd718xx-power",
> +	},
> +	.probe = bd71828_power_probe,
> +	.id_table = bd71828_charger_id,
> +};
> +
> +module_platform_driver(bd71828_power_driver);
> +
> +MODULE_AUTHOR("Cong Pham <cpham2403@gmail.com>");
> +MODULE_DESCRIPTION("ROHM BD718(15/28/78) PMIC Battery Charger driver");
> +MODULE_LICENSE("GPL");
> 
> -- 
> 2.47.3
> 
>