[Letux-kernel] [PATCH 6/6] drivers: power: supply: bq2429x: store chip status and fault in struct rather than shadow register

Nick Elsmore nicholaselsmore at gmail.com
Thu Aug 13 03:22:05 CEST 2020


Signed-off-by: Nick Elsmore <nicholaselsmore at gmail.com>

---
 drivers/power/supply/bq2429x_charger.c | 342 ++++++++++++++-----------
 1 file changed, 193 insertions(+), 149 deletions(-)

diff --git a/drivers/power/supply/bq2429x_charger.c
b/drivers/power/supply/bq2429x_charger.c
index 0fa69eb152e4..37edab909de5 100644
--- a/drivers/power/supply/bq2429x_charger.c
+++ b/drivers/power/supply/bq2429x_charger.c
@@ -55,18 +55,6 @@
 #define CHRG_PRE_CHARGE		1
 #define CHRG_FAST_CHARGE	2
 #define CHRG_CHRGE_DONE		3
-#define CHRG_OFF		4
-#define CHRG_MASK		3
-
-#define DPM_STAT	0x08
-#define PG_STAT		0x04
-
-/* REG09 fault status register value */
-#define CHRG_FAULT_OFF	4
-#define CHRG_FAULT_MASK	0x3
-#define NTC_FAULT_OFF	0
-// FIXME: MP2624 has 3 bits
-#define NTC_FAULT_MASK	0x3

 /* REG0a vendor status register value */
 #define CHIP_BQ24296		0x20
@@ -184,6 +172,19 @@ static const struct regmap_config bq2429x_regmap_config = {
 	.volatile_table = &bq2429x_volatile_regs
 };

+struct bq2429x_state {
+	u8 vbus_stat;
+	u8 chrg_stat;
+	u8 dpm_stat;
+	u8 pg_stat;
+	u8 therm_stat;
+	u8 vsys_stat;
+	u8 wd_fault;
+	u8 otg_fault;
+	u8 chrg_fault;
+	u8 bat_fault;
+	u8 ntc_fault;
+};

 struct bq2429x_device_info {
 	struct device *dev;
@@ -212,12 +213,8 @@ struct bq2429x_device_info {
 	/* output connected to psel of bq24296 */
 	struct gpio_desc *psel_pin;

-	/* status register values from last read */
-	u8 r8;
-	u8 r9;
-	/* second last read for change detection */
-	u8 prev_r8;
-	u8 prev_r9;
+	struct bq2429x_state state;
+
 	/* is power adapter plugged in */
 	bool adapter_plugged;

@@ -755,90 +752,74 @@ static int
bq2429x_set_otg_current_limit_uA(struct bq2429x_device_info *di,
 }

 /* initialize the chip */
-
-static int bq2429x_get_vendor_id(struct bq2429x_device_info *di)
-{
-	return bq2429x_field_read(di, F_PN_REV);
-}
-
-static int bq2429x_init_registers(struct bq2429x_device_info *di)
+static int bq2429x_get_chip_state(struct bq2429x_device_info *di,
+				  struct bq2429x_state *state)
 {
-	int max_uV;
-	int bits;
+	int i;
 	int ret;

-	ret = power_supply_get_battery_info(di->usb, &di->bat_info);
-	if (ret < 0) {
-		dev_err(di->dev, "unable to get battery info: %d\n", ret);
-		return ret;
-	}
-
-	if (di->bat_info.energy_full_design_uwh         == -EINVAL ||
-	    di->bat_info.charge_full_design_uah         == -EINVAL ||
-	    di->bat_info.voltage_min_design_uv          == -EINVAL ||
-	    di->bat_info.voltage_max_design_uv          == -EINVAL ||
-	    di->bat_info.constant_charge_current_max_ua == -EINVAL ||
-	    di->bat_info.constant_charge_voltage_max_uv == -EINVAL)
-	{
-		dev_err(di->dev, "battery info is incomplete\n");
-		return ret;
-	}
-
-	bq2429x_set_precharge_current_uA(di,
-			(di->bat_info.precharge_current_ua == -EINVAL) ?
-			128000 : di->bat_info.precharge_current_ua);
-	bq2429x_set_charge_term_current_uA(di,
-			(di->bat_info.charge_term_current_ua == -EINVAL) ?
-			128000 : di->bat_info.charge_term_current_ua);
-
-	/*-	 * VSYS may be up to 150 mV above fully charged battery
voltage-	 * if operating from VBUS.-	 * So to effectively limit VSYS
we may have to lower the max. battery-	 * voltage. The offset can be
reduced to 100 mV for the mps,mp2624.-	 */
-
-	if (di->id->driver_data == CHIP_MP2624)
-// FIXME: can be configured to 50/100mV by additional bit in REG01: VSYS_MAX
-		max_uV = di->max_VSYS_uV - 100000;
-	else
-		max_uV = di->max_VSYS_uV - 150000;
-
-	max_uV = min_t(int, max_uV, di->bat_info.voltage_max_design_uv);
-
-// MP2624 has slightly different scale and offset
-	bits = bq2429x_find_idx(max_uV, TBL_VREG);
-	if (bits < 0)
-		return bits;
-
-	dev_dbg(di->dev, "%s(): translated vbatt_max=%u and VSYS_max=%u to
VREG=%u (%02x)\n",
-		__func__,
-		di->bat_info.voltage_max_design_uv, di->max_VSYS_uV, max_uV,
-		bits);
+	struct {
+		enum bq2429x_fields id;
+		u8 *data;
+	} state_fields[] = {
+		{F_VBUS_STAT,		&state->vbus_stat},
+		{F_CHRG_STAT,		&state->chrg_stat},
+		{F_DPM_STAT,		&state->dpm_stat},
+		{F_PG_STAT,		&state->pg_stat},
+		{F_THERM_STAT,		&state->therm_stat},
+		{F_VSYS_STAT,		&state->vsys_stat},
+		{F_WATCHDOG_FAULT,	&state->wd_fault},
+		{F_OTG_FAULT,		&state->otg_fault}
+		{F_CHRG_FAULT,		&state->chrg_fault},
+		{F_BAT_FAULT,		&state->bat_fault},
+		{F_NTC_FAULT,		&state->ntc_fault},
+	};

-	/* revisit: bq2429x_set_charge_current_uA(di, ?); */
+	for (i=0; i < ARRAY_SIZE(state_fields); i++) {
+		ret = bq2429x_field_read(di, state_fields[i].id);
+		if (ret < 0)
+			return ret;

-	ret = bq2429x_field_write(di, F_VREG, bits);
-	if (ret < 0) {
-		dev_err(di->dev, "%s(): Failed to set max. battery voltage\n",
-				__func__);
-		return ret;
+		*state_fields[i].data = ret;
 	}

 	return 0;
 }

+static bool bq2429x_state_changed(struct bq2429x_device_info *di,
+				  struct bq2429x_state *new_state)
+{
+	struct bq2429x_state old_state = di->state;
+
+	return (old_state.vbus_stat != new_state->vbus_stat	||
+		old_state.chrg_stat != new_state->chrg_stat	||
+		old_state.dpm_stat != new_state->dpm_stat	||
+		old_state.pg_stat != new_state->pg_stat		||
+		old_state.therm_stat != new_state->therm_stat	||
+		old_state.vsys_stat != new_state->vsys_stat	||
+		old_state.wd_fault != new_state->wd_fault	||
+		old_state.otg_fault != new_state->otg_fault	||
+		old_state.chrg_fault != new_state->chrg_fault	||
+		old_state.bat_fault != new_state->bat_fault	||
+		old_state.ntc_fault != new_state->ntc_fault);
+}
+
+static int bq2429x_get_vendor_id(struct bq2429x_device_info *di)
+{
+	return bq2429x_field_read(di, F_PN_REV);
+}
+
 /* handle USB detection and charging based on status registers */

 static inline bool bq2429x_battery_present(struct bq2429x_device_info *di)
 { /* assume if there is an NTC fault there is no battery  */
 // MP2624 has 3 NTC bits
-	return ((di->r9 >> NTC_FAULT_OFF) & NTC_FAULT_MASK) == 0;
+	return di->state.ntc_fault == 0;
 }

 static inline bool bq2429x_input_present(struct bq2429x_device_info *di)
 { /* VBUS is available */
-	return (di->r8 & PG_STAT) != 0;
+	return di->state.chrg_fault != 0;
 }

 static int bq2429x_battery_temperature_mC(struct bq2429x_device_info *di)
@@ -848,10 +829,9 @@ static int bq2429x_battery_temperature_mC(struct
bq2429x_device_info *di)
 	 * revisit: during boost mode deduce values from BHOT and BCOLD
 	 * settings
 	 */
-
-	if (di->r9 & 0x02)
+	if (di->state.ntc_fault & 0x02)
 		return -10000;	/* too cold (-10C) */
-	else if (di->r9 & 0x01)
+	else if (di->state.ntc_fault & 0x01)
 		return 60000;	/* too hot (60C) */
 	return 22500;	/* ok (22.5C) */
 }
@@ -896,85 +876,66 @@ static void bq2429x_input_available(struct
bq2429x_device_info *di, bool state)

 static int bq2429x_usb_detect(struct bq2429x_device_info *di)
 {
+	struct bq2429x_state state;
+	char string[200];
 	int ret;

-	/* lock if interrupt and polling occur at the same time */
 	mutex_lock(&di->var_lock);

-	dev_dbg(di->dev, "%s, line=%d\n", __func__, __LINE__);
-
-	ret = bq2429x_field_read(di, F_SYS_STAT_REG);
+	ret = bq2429x_get_chip_state(di, &state);
 	if (ret < 0) {
 		mutex_unlock(&di->var_lock);
-
-		dev_err(&di->client->dev, "%s: err %d\n", __func__, ret);
-
 		return ret;
 	}
-	di->r8 = ret;

-	ret = bq2429x_field_read(di, F_NEW_FAULT_REG);
-	if (ret < 0) {
+	if (!bq2429x_state_changed(di, &state)) {
 		mutex_unlock(&di->var_lock);
-
-		dev_err(&di->client->dev, "%s: err %d\n", __func__, ret);
-
-		return ret;
+		return -EAGAIN;
 	}
-	di->r9 = ret;

 	/* report changes to last state */
-	if (di->r8 != di->prev_r8 || di->r9 != di->prev_r9)
-		{
-		char string[200];
-		sprintf(string, "r8=%02x", di->r8);
-		switch ((di->r8 >> 6) & 3) {
+	sprintf(string, "state changed: state->[");
+	switch (state.vbus_stat) {
 		case 1: strcat(string, " HOST"); break;
 		case 2: strcat(string, " ADAP"); break;
 		case 3: strcat(string, " OTG"); break;
-		};
-		switch ((di->r8 >> 4) & 3) {
+	};
+	switch (state.chrg_stat) {
 		case 1: strcat(string, " PRECHG"); break;
 		case 2: strcat(string, " FCHG"); break;
 		case 3: strcat(string, " CHGTERM"); break;
-		};
-		if ((di->r8 >> 3) & 1)
-			strcat(string, " INDPM");
-		if ((di->r8 >> 2) & 1)
-			strcat(string, " PWRGOOD");
-		if ((di->r8 >> 1) & 1)
-			strcat(string, " THERMREG");
-		if ((di->r8 >> 0) & 1)
-			strcat(string, " VSYSMIN");
-		sprintf(string+strlen(string), " r9=%02x", di->r9);
-		if ((di->r9 >> 7) & 1)
-			strcat(string, " WDOG");
-		if ((di->r9 >> 6) & 1)
-			strcat(string, " OTGFAULT");
-		switch ((di->r9 >> 4) & 3) {
+	};
+	if (state.dpm_stat)
+		strcat(string, " INDPM");
+	if (state.pg_stat)
+		strcat(string, " PWRGOOD");
+	if (state.therm_stat)
+		strcat(string, " THERMREG");
+	if (state.vsys_stat)
+		strcat(string, " VSYSMIN");
+	sprintf(string+strlen(string), "] fault->[");
+	if (state.wd_fault)
+		strcat(string, " WDOG");
+	if (state.otg_fault)
+		strcat(string, " OTGFAULT");
+	switch (state.chrg_fault) {
 		case 1: strcat(string, " UNPLUG"); break;
 		case 2: strcat(string, " THERMAL"); break;
 		case 3: strcat(string, " CHGTIME"); break;
-		};
-		if ((di->r9 >> 3) & 1)
-			strcat(string, " BATFAULT");
-		if ((di->r9 >> 2) & 1)
-			strcat(string, " RESERVED");
-		if ((di->r9 >> 1) & 1)
-			strcat(string, " COLD");
-		if ((di->r9 >> 0) & 1)
-			strcat(string, " HOT");
-		dev_notice(di->dev, "%s: %s\n", __func__, string);
-		di->prev_r8 = di->r8, di->prev_r9 = di->r9;
-		}
+	};
+	if (state.bat_fault)
+		strcat(string, " BATFAULT");
+	if (state.ntc_fault & 2)
+		strcat(string, " COLD");
+	if (state.ntc_fault & 1)
+		strcat(string, " HOT");
+	strcat(string, "]");
+	dev_notice(di->dev, "%s: %s\n", __func__, string);

-	if (((di->r8 >> 4) & 3) == 3) {
-		/* charging  terminated */
-		/* power_supply_changed(di->usb); */
-	}
+	di->state = state;

 	/* handle (momentarily) disconnect of VBUS */
-	if ((di->r9 >> CHRG_FAULT_OFF) & CHRG_FAULT_MASK)
+	if (state.chrg_fault)
 		bq2429x_input_available(di, false);

 	/* since we are polling slowly, VBUS may already be back again */
@@ -1008,8 +969,6 @@ static void bq2729x_irq_work_func(struct work_struct *wp)
 	dev_dbg(di->dev, "%s: di = %px\n", __func__, di);

 	bq2429x_usb_detect(di);
-
-	dev_dbg(di->dev, "%s: r8=%02x r9=%02x\n", __func__, di->r8, di->r9);
 }

 static irqreturn_t bq2729x_chg_irq_func(int irq, void *dev_id)
@@ -1260,6 +1219,88 @@ static DEVICE_ATTR(max_current, 0644,
bq2429x_input_current_limit_uA_show,

 static DEVICE_ATTR(otg, 0644, bq2429x_otg_show, bq2429x_otg_store);

+static int bq2429x_init_registers(struct bq2429x_device_info *di)
+{
+	int max_uV;
+	int bits;
+	int ret;
+
+	ret = power_supply_get_battery_info(di->usb, &di->bat_info);
+	if (ret < 0) {
+		dev_err(di->dev, "unable to get battery info: %d\n", ret);
+		return ret;
+	}
+
+	if (di->bat_info.energy_full_design_uwh         == -EINVAL ||
+	    di->bat_info.charge_full_design_uah         == -EINVAL ||
+	    di->bat_info.voltage_min_design_uv          == -EINVAL ||
+	    di->bat_info.voltage_max_design_uv          == -EINVAL ||
+	    di->bat_info.constant_charge_current_max_ua == -EINVAL ||
+	    di->bat_info.constant_charge_voltage_max_uv == -EINVAL)
+	{
+		dev_err(di->dev, "battery info is incomplete\n");
+		return ret;
+	}
+
+	/* disable watchdog */
+	ret = bq2429x_field_write(di, F_WD_RESET, 1);
+	if (ret < 0)
+		return ret;
+
+	ret = bq2429x_field_write(di, F_WATCHDOG, 0);
+	if (ret < 0)
+		return ret;
+
+	bq2429x_set_precharge_current_uA(di,
+			(di->bat_info.precharge_current_ua == -EINVAL) ?
+			128000 : di->bat_info.precharge_current_ua);
+	bq2429x_set_charge_term_current_uA(di,
+			(di->bat_info.charge_term_current_ua == -EINVAL) ?
+			128000 : di->bat_info.charge_term_current_ua);
+
+	/*+	 * VSYS may be up to 150 mV above fully charged battery
voltage+	 * if operating from VBUS.+	 * So to effectively limit VSYS
we may have to lower the max. battery+	 * voltage. The offset can be
reduced to 100 mV for the mps,mp2624.+	 */
+
+	if (di->id->driver_data == CHIP_MP2624)
+// FIXME: can be configured to 50/100mV by additional bit in REG01: VSYS_MAX
+		max_uV = di->max_VSYS_uV - 100000;
+	else
+		max_uV = di->max_VSYS_uV - 150000;
+
+	max_uV = min_t(int, max_uV, di->bat_info.voltage_max_design_uv);
+
+// MP2624 has slightly different scale and offset
+	bits = bq2429x_find_idx(max_uV, TBL_VREG);
+	if (bits < 0)
+		return bits;
+
+	dev_dbg(di->dev, "%s(): translated vbatt_max=%u and VSYS_max=%u to
VREG=%u (%02x)\n",
+		__func__,
+		di->bat_info.voltage_max_design_uv, di->max_VSYS_uV, max_uV,
+		bits);
+
+	/* revisit: bq2429x_set_charge_current_uA(di, ?); */
+
+	ret = bq2429x_field_write(di, F_VREG, bits);
+	if (ret < 0) {
+		dev_err(di->dev, "%s(): Failed to set max. battery voltage\n",
+				__func__);
+		return ret;
+	}
+
+	ret = bq2429x_get_chip_state(di, &di->state);
+	if (ret < 0) {
+		dev_err(di->dev, "failed to get chip state\n");
+		return ret;
+	}
+
+	return 0;
+}
+
 /* power_supply interface */

 static int bq2429x_get_property(struct power_supply *psy,
@@ -1267,13 +1308,18 @@ static int bq2429x_get_property(struct
power_supply *psy,
 				union power_supply_propval *val)
 {
 	struct bq2429x_device_info *di = power_supply_get_drvdata(psy);
+	struct bq2429x_state state;
 	int ret;

 	dev_dbg(di->dev, "%s,line=%d prop=%d\n", __func__, __LINE__, psp);

+	mutex_lock(&di->var_lock);
+	state = di->state;
+	mutex_unlock(&di->var_lock);
+
 	switch (psp) {
 	case POWER_SUPPLY_PROP_STATUS:
-		switch ((di->r8 >> CHRG_OFF) & CHRG_MASK) {
+		switch (state.chrg_stat) {
 		case CHRG_NO_CHARGING:
 			val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
 			break;
@@ -1290,7 +1336,7 @@ static int bq2429x_get_property(struct power_supply *psy,
 		break;

 	case POWER_SUPPLY_PROP_CHARGE_TYPE:
-		switch ((di->r8 >> CHRG_OFF) & CHRG_MASK) {
+		switch (state.chrg_stat) {
 		case CHRG_NO_CHARGING:
 			val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
 			break;
@@ -1307,7 +1353,7 @@ static int bq2429x_get_property(struct power_supply *psy,
 		break;

 	case POWER_SUPPLY_PROP_HEALTH:
-		switch ((di->r9 >> CHRG_FAULT_OFF) & CHRG_FAULT_MASK) {
+		switch (state.chrg_fault) {
 		case 0:
 			val->intval = POWER_SUPPLY_HEALTH_GOOD;
 			break;
@@ -1325,7 +1371,7 @@ static int bq2429x_get_property(struct power_supply *psy,

 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
 		if(bq2429x_input_present(di)) {
-			if ((di->r8 & DPM_STAT) != 0)
+			if (state.vbus_stat != 0)
 				val->intval = bq2429x_get_vindpm_uV(di);
 			else
 				/* power good: assume VBUS 5V */
@@ -1342,7 +1388,7 @@ static int bq2429x_get_property(struct power_supply *psy,
 		break;

 	case POWER_SUPPLY_PROP_CURRENT_NOW:
-		switch ((di->r8 >> CHRG_OFF) & CHRG_MASK) {
+		switch (state.chrg_stat) {
 		case CHRG_NO_CHARGING:
 		case CHRG_CHRGE_DONE:
 			/* assume no charging current */
@@ -1638,8 +1684,6 @@ static int bq2429x_charger_probe(struct
i2c_client *client,
 	di->client = client;
 	di->id = id;
 	i2c_set_clientdata(client, di);
-	di->prev_r8 = 0xff;
-	di->prev_r9 = 0xff;

 	di->rmap = devm_regmap_init_i2c(client, &bq2429x_regmap_config);
 	if (IS_ERR(di->rmap)) {
-- 
2.25.1
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.goldelico.com/pipermail/letux-kernel/attachments/20200812/a8d34845/attachment-0001.htm>


More information about the Letux-kernel mailing list