Add support for bq27521 battery monitor

Message ID 20171109210615.GA27638@amd
State Not Applicable
Headers show
Series
  • Add support for bq27521 battery monitor
Related show

Commit Message

Pavel Machek Nov. 9, 2017, 9:06 p.m.
This adds basic support for BQ27521 battery monitor, used in Nokia N9
and N950. In particular, battery voltage is important to be able to
tell when the battery is almost empty. Emptying battery on N950 is
pretty painful, as flasher needs to be used to recover phone in such case.

Signed-off-by: Pavel Machek <pavel@ucw.cz>

Comments

Pali Rohár Nov. 9, 2017, 9:29 p.m. | #1
On Thursday 09 November 2017 22:06:15 Pavel Machek wrote:
> diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
> index ed44439..ee2851a 100644
> --- a/drivers/power/supply/bq27xxx_battery.c
> +++ b/drivers/power/supply/bq27xxx_battery.c
> @@ -381,6 +381,30 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
>  		[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
>  		BQ27XXX_DM_REG_ROWS,
>  	},
> +	[BQ27521] = {				/* FIXME */
> +		[BQ27XXX_REG_CTRL] = 0x02,
> +		[BQ27XXX_REG_TEMP] = 0x0a,
> +		[BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR,
> +		[BQ27XXX_REG_VOLT] = 0x0c,
> +		[BQ27XXX_REG_AI] = 0x0e,
> +		[BQ27XXX_REG_FLAGS] = 0x08,
> +		[BQ27XXX_REG_TTE] = INVALID_REG_ADDR,
> +		[BQ27XXX_REG_TTF] = INVALID_REG_ADDR,
> +		[BQ27XXX_REG_TTES] = INVALID_REG_ADDR,
> +		[BQ27XXX_REG_TTECP] = INVALID_REG_ADDR,
> +		[BQ27XXX_REG_NAC] = INVALID_REG_ADDR,
> +		[BQ27XXX_REG_FCC] = INVALID_REG_ADDR,
> +		[BQ27XXX_REG_CYCT] = INVALID_REG_ADDR,
> +		[BQ27XXX_REG_AE] = INVALID_REG_ADDR,
> +		[BQ27XXX_REG_SOC] = INVALID_REG_ADDR,
> +		[BQ27XXX_REG_DCAP] = INVALID_REG_ADDR,
> +		[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
> +		[BQ27XXX_DM_CTRL] = INVALID_REG_ADDR,
> +		[BQ27XXX_DM_CLASS] = INVALID_REG_ADDR,
> +		[BQ27XXX_DM_BLOCK] = INVALID_REG_ADDR,
> +		[BQ27XXX_DM_DATA] = INVALID_REG_ADDR,
> +		[BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR,
> +	},
>  	[BQ27530] = {
>  		[BQ27XXX_REG_CTRL] = 0x00,
>  		[BQ27XXX_REG_TEMP] = 0x06,

Hi! IIRC those registers are valid only for sn27521 chip in revision 14
(or new). For older revision is used different register map. And
detection of chip revision is somehow possible via some registers.
Pavel Machek Nov. 9, 2017, 9:45 p.m. | #2
On Thu 2017-11-09 22:29:45, Pali Rohár wrote:
> On Thursday 09 November 2017 22:06:15 Pavel Machek wrote:
> > diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
> > index ed44439..ee2851a 100644
> > --- a/drivers/power/supply/bq27xxx_battery.c
> > +++ b/drivers/power/supply/bq27xxx_battery.c
> > @@ -381,6 +381,30 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
> >  		[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
> >  		BQ27XXX_DM_REG_ROWS,
> >  	},
> > +	[BQ27521] = {				/* FIXME */
> > +		[BQ27XXX_REG_CTRL] = 0x02,
> > +		[BQ27XXX_REG_TEMP] = 0x0a,
> > +		[BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR,
> > +		[BQ27XXX_REG_VOLT] = 0x0c,
> > +		[BQ27XXX_REG_AI] = 0x0e,
> > +		[BQ27XXX_REG_FLAGS] = 0x08,
> > +		[BQ27XXX_REG_TTE] = INVALID_REG_ADDR,
> > +		[BQ27XXX_REG_TTF] = INVALID_REG_ADDR,
> > +		[BQ27XXX_REG_TTES] = INVALID_REG_ADDR,
> > +		[BQ27XXX_REG_TTECP] = INVALID_REG_ADDR,
> > +		[BQ27XXX_REG_NAC] = INVALID_REG_ADDR,
> > +		[BQ27XXX_REG_FCC] = INVALID_REG_ADDR,
> > +		[BQ27XXX_REG_CYCT] = INVALID_REG_ADDR,
> > +		[BQ27XXX_REG_AE] = INVALID_REG_ADDR,
> > +		[BQ27XXX_REG_SOC] = INVALID_REG_ADDR,
> > +		[BQ27XXX_REG_DCAP] = INVALID_REG_ADDR,
> > +		[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
> > +		[BQ27XXX_DM_CTRL] = INVALID_REG_ADDR,
> > +		[BQ27XXX_DM_CLASS] = INVALID_REG_ADDR,
> > +		[BQ27XXX_DM_BLOCK] = INVALID_REG_ADDR,
> > +		[BQ27XXX_DM_DATA] = INVALID_REG_ADDR,
> > +		[BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR,
> > +	},
> >  	[BQ27530] = {
> >  		[BQ27XXX_REG_CTRL] = 0x00,
> >  		[BQ27XXX_REG_TEMP] = 0x06,
> 
> Hi! IIRC those registers are valid only for sn27521 chip in revision 14
> (or new). For older revision is used different register map. And
> detection of chip revision is somehow possible via some registers.

Yes, I know.. I even had this, but as I can't test it, and early N950
samples should be very very rare, I'd prefer the patch as is.

@@ -1905,6 +1940,23 @@ int bq27xxx_battery_setup(struct bq27xxx_device_info *di)
 
 	dev_info(di->dev, "support ver. %s enabled\n", DRIVER_VERSION);
 
+	if (di->chip == BQ27521) {
+		int val;
+		val = di->bus.read(di, 0x32, false);
+		if (val == 0x2100) {
+			printk("rev. 13 chip detected; add support\n");
+			/* https://elinux.org/N950 has details */
+			return PTR_ERR(ENODEV);
+		}
+
+		val = di->bus.read(di, 0x34, false);
+		if ((val & 0xff00) != 0x2100) {
+			printk("rev. 14 chip not detected?!\n");
+			return PTR_ERR(EINVAL);
+		}
+		printk("verified chip rev. 14\n");
+	}
+
 	bq27xxx_battery_settings(di);
 	bq27xxx_battery_update(di);
Pali Rohár Nov. 9, 2017, 9:52 p.m. | #3
On Thursday 09 November 2017 22:45:40 Pavel Machek wrote:
> On Thu 2017-11-09 22:29:45, Pali Rohár wrote:
> > On Thursday 09 November 2017 22:06:15 Pavel Machek wrote:
> > > diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
> > > index ed44439..ee2851a 100644
> > > --- a/drivers/power/supply/bq27xxx_battery.c
> > > +++ b/drivers/power/supply/bq27xxx_battery.c
> > > @@ -381,6 +381,30 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
> > >  		[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
> > >  		BQ27XXX_DM_REG_ROWS,
> > >  	},
> > > +	[BQ27521] = {				/* FIXME */
> > > +		[BQ27XXX_REG_CTRL] = 0x02,
> > > +		[BQ27XXX_REG_TEMP] = 0x0a,
> > > +		[BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR,
> > > +		[BQ27XXX_REG_VOLT] = 0x0c,
> > > +		[BQ27XXX_REG_AI] = 0x0e,
> > > +		[BQ27XXX_REG_FLAGS] = 0x08,
> > > +		[BQ27XXX_REG_TTE] = INVALID_REG_ADDR,
> > > +		[BQ27XXX_REG_TTF] = INVALID_REG_ADDR,
> > > +		[BQ27XXX_REG_TTES] = INVALID_REG_ADDR,
> > > +		[BQ27XXX_REG_TTECP] = INVALID_REG_ADDR,
> > > +		[BQ27XXX_REG_NAC] = INVALID_REG_ADDR,
> > > +		[BQ27XXX_REG_FCC] = INVALID_REG_ADDR,
> > > +		[BQ27XXX_REG_CYCT] = INVALID_REG_ADDR,
> > > +		[BQ27XXX_REG_AE] = INVALID_REG_ADDR,
> > > +		[BQ27XXX_REG_SOC] = INVALID_REG_ADDR,
> > > +		[BQ27XXX_REG_DCAP] = INVALID_REG_ADDR,
> > > +		[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
> > > +		[BQ27XXX_DM_CTRL] = INVALID_REG_ADDR,
> > > +		[BQ27XXX_DM_CLASS] = INVALID_REG_ADDR,
> > > +		[BQ27XXX_DM_BLOCK] = INVALID_REG_ADDR,
> > > +		[BQ27XXX_DM_DATA] = INVALID_REG_ADDR,
> > > +		[BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR,
> > > +	},
> > >  	[BQ27530] = {
> > >  		[BQ27XXX_REG_CTRL] = 0x00,
> > >  		[BQ27XXX_REG_TEMP] = 0x06,
> > 
> > Hi! IIRC those registers are valid only for sn27521 chip in revision 14
> > (or new). For older revision is used different register map. And
> > detection of chip revision is somehow possible via some registers.
> 
> Yes, I know.. I even had this, but as I can't test it, and early N950
> samples should be very very rare, I'd prefer the patch as is.
> 
> @@ -1905,6 +1940,23 @@ int bq27xxx_battery_setup(struct bq27xxx_device_info *di)
>  
>  	dev_info(di->dev, "support ver. %s enabled\n", DRIVER_VERSION);
>  
> +	if (di->chip == BQ27521) {
> +		int val;
> +		val = di->bus.read(di, 0x32, false);
> +		if (val == 0x2100) {
> +			printk("rev. 13 chip detected; add support\n");
> +			/* https://elinux.org/N950 has details */
> +			return PTR_ERR(ENODEV);
> +		}
> +
> +		val = di->bus.read(di, 0x34, false);
> +		if ((val & 0xff00) != 0x2100) {
> +			printk("rev. 14 chip not detected?!\n");
> +			return PTR_ERR(EINVAL);
> +		}
> +		printk("verified chip rev. 14\n");
> +	}
> +
>  	bq27xxx_battery_settings(di);
>  	bq27xxx_battery_update(di);

This change could be useful, specially if somebody got device with
sn27521 chip which is not in revision 14 (no idea if in world are those
chips used) -- at least would get message that current driver does not
support it.
Rob Herring Nov. 15, 2017, 7:20 p.m. | #4
On Thu, Nov 09, 2017 at 10:06:15PM +0100, Pavel Machek wrote:
> 
> This adds basic support for BQ27521 battery monitor, used in Nokia N9
> and N950. In particular, battery voltage is important to be able to
> tell when the battery is almost empty. Emptying battery on N950 is
> pretty painful, as flasher needs to be used to recover phone in such case.
> 
> Signed-off-by: Pavel Machek <pavel@ucw.cz>

For the binding,

Acked-by: Rob Herring <robh@kernel.org>

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Patch

diff --git a/Documentation/devicetree/bindings/power/supply/bq27xxx.txt b/Documentation/devicetree/bindings/power/supply/bq27xxx.txt
index 6858e1a..615c1cb 100644
--- a/Documentation/devicetree/bindings/power/supply/bq27xxx.txt
+++ b/Documentation/devicetree/bindings/power/supply/bq27xxx.txt
@@ -15,6 +15,7 @@  Required properties:
  * "ti,bq27520g2" - BQ27520-g2
  * "ti,bq27520g3" - BQ27520-g3
  * "ti,bq27520g4" - BQ27520-g4
+ * "ti,bq27521" - BQ27521
  * "ti,bq27530" - BQ27530
  * "ti,bq27531" - BQ27531
  * "ti,bq27541" - BQ27541
diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
index ed44439..ee2851a 100644
--- a/drivers/power/supply/bq27xxx_battery.c
+++ b/drivers/power/supply/bq27xxx_battery.c
@@ -381,6 +381,30 @@  static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
 		[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
 		BQ27XXX_DM_REG_ROWS,
 	},
+	[BQ27521] = {				/* FIXME */
+		[BQ27XXX_REG_CTRL] = 0x02,
+		[BQ27XXX_REG_TEMP] = 0x0a,
+		[BQ27XXX_REG_INT_TEMP] = INVALID_REG_ADDR,
+		[BQ27XXX_REG_VOLT] = 0x0c,
+		[BQ27XXX_REG_AI] = 0x0e,
+		[BQ27XXX_REG_FLAGS] = 0x08,
+		[BQ27XXX_REG_TTE] = INVALID_REG_ADDR,
+		[BQ27XXX_REG_TTF] = INVALID_REG_ADDR,
+		[BQ27XXX_REG_TTES] = INVALID_REG_ADDR,
+		[BQ27XXX_REG_TTECP] = INVALID_REG_ADDR,
+		[BQ27XXX_REG_NAC] = INVALID_REG_ADDR,
+		[BQ27XXX_REG_FCC] = INVALID_REG_ADDR,
+		[BQ27XXX_REG_CYCT] = INVALID_REG_ADDR,
+		[BQ27XXX_REG_AE] = INVALID_REG_ADDR,
+		[BQ27XXX_REG_SOC] = INVALID_REG_ADDR,
+		[BQ27XXX_REG_DCAP] = INVALID_REG_ADDR,
+		[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
+		[BQ27XXX_DM_CTRL] = INVALID_REG_ADDR,
+		[BQ27XXX_DM_CLASS] = INVALID_REG_ADDR,
+		[BQ27XXX_DM_BLOCK] = INVALID_REG_ADDR,
+		[BQ27XXX_DM_DATA] = INVALID_REG_ADDR,
+		[BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR,
+	},
 	[BQ27530] = {
 		[BQ27XXX_REG_CTRL] = 0x00,
 		[BQ27XXX_REG_TEMP] = 0x06,
@@ -700,6 +724,15 @@  static enum power_supply_property bq27520g4_battery_props[] = {
 	POWER_SUPPLY_PROP_MANUFACTURER,
 };
 
+static enum power_supply_property bq27521_battery_props[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_CURRENT_NOW,
+	POWER_SUPPLY_PROP_TEMP,
+	POWER_SUPPLY_PROP_TECHNOLOGY,
+};
+
 static enum power_supply_property bq27530_battery_props[] = {
 	POWER_SUPPLY_PROP_STATUS,
 	POWER_SUPPLY_PROP_PRESENT,
@@ -792,6 +825,7 @@  static struct {
 	BQ27XXX_PROP(BQ27520G2, bq27520g2_battery_props),
 	BQ27XXX_PROP(BQ27520G3, bq27520g3_battery_props),
 	BQ27XXX_PROP(BQ27520G4, bq27520g4_battery_props),
+	BQ27XXX_PROP(BQ27521, bq27521_battery_props),
 	BQ27XXX_PROP(BQ27530, bq27530_battery_props),
 	BQ27XXX_PROP(BQ27541, bq27541_battery_props),
 	BQ27XXX_PROP(BQ27545, bq27545_battery_props),
@@ -1529,6 +1563,7 @@  static bool bq27xxx_battery_overtemp(struct bq27xxx_device_info *di, u16 flags)
 	case BQ27520G2:
 	case BQ27520G3:
 	case BQ27520G4:
+	case BQ27521:
 	case BQ27541:
 	case BQ27545:
 		return flags & (BQ27XXX_FLAG_OTC | BQ27XXX_FLAG_OTD);
diff --git a/drivers/power/supply/bq27xxx_battery_i2c.c b/drivers/power/supply/bq27xxx_battery_i2c.c
index a597221..fd9b989 100644
--- a/drivers/power/supply/bq27xxx_battery_i2c.c
+++ b/drivers/power/supply/bq27xxx_battery_i2c.c
@@ -239,6 +239,7 @@  static const struct i2c_device_id bq27xxx_i2c_id_table[] = {
 	{ "bq27520g2", BQ27520G2 },
 	{ "bq27520g3", BQ27520G3 },
 	{ "bq27520g4", BQ27520G4 },
+	{ "bq27521", BQ27521 },
 	{ "bq27530", BQ27530 },
 	{ "bq27531", BQ27530 },
 	{ "bq27541", BQ27541 },
@@ -269,6 +270,7 @@  static const struct of_device_id bq27xxx_battery_i2c_of_match_table[] = {
 	{ .compatible = "ti,bq27520g2" },
 	{ .compatible = "ti,bq27520g3" },
 	{ .compatible = "ti,bq27520g4" },
+	{ .compatible = "ti,bq27521" },
 	{ .compatible = "ti,bq27530" },
 	{ .compatible = "ti,bq27531" },
 	{ .compatible = "ti,bq27541" },
diff --git a/include/linux/power/bq27xxx_battery.h b/include/linux/power/bq27xxx_battery.h
index 11e1168..1e80931 100644
--- a/include/linux/power/bq27xxx_battery.h
+++ b/include/linux/power/bq27xxx_battery.h
@@ -14,6 +14,7 @@  enum bq27xxx_chip {
 	BQ27520G2, /* bq27520G2 */
 	BQ27520G3, /* bq27520G3 */
 	BQ27520G4, /* bq27520G4 */
+	BQ27521, /* bq27521 */	
 	BQ27530, /* bq27530, bq27531 */
 	BQ27541, /* bq27541, bq27542, bq27546, bq27742 */
 	BQ27545, /* bq27545 */