lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:	Wed, 5 Aug 2015 11:05:39 -0500
From:	"Andrew F. Davis" <afd@...com>
To:	Pali Rohár <pali.rohar@...il.com>,
	Sebastian Reichel <sre@...nel.org>,
	Dmitry Eremin-Solenikov <dbaryshkov@...il.com>,
	David Woodhouse <dwmw2@...radead.org>,
	Dan Murphy <dmurphy@...com>
CC:	<linux-pm@...r.kernel.org>, <linux-kernel@...r.kernel.org>,
	"Andrew F. Davis" <afd@...com>
Subject: [PATCH v3 5/6] power: bq27xxx_battery: Add support for additional bq27xxx family devices

Add support for additional devices and register equivalent family devices
including the bq27010, bq27210, bq27500, bq27510, bq27520, bq27530,
bq27531, bq27541, bq27542, bq27546, bq27545, bq27441, bq27421, and the
bq27641.

To facilitate this process the register mapings have been moved to tables
and other small cleanups have been made.

Signed-off-by: Andrew F. Davis <afd@...com>
---
 drivers/power/bq27xxx_battery.c       | 627 +++++++++++++++++++++++-----------
 include/linux/power/bq27xxx_battery.h |  10 +-
 2 files changed, 428 insertions(+), 209 deletions(-)

diff --git a/drivers/power/bq27xxx_battery.c b/drivers/power/bq27xxx_battery.c
index 1ff88ad..3098609 100644
--- a/drivers/power/bq27xxx_battery.c
+++ b/drivers/power/bq27xxx_battery.c
@@ -17,11 +17,24 @@
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  *
  * Datasheets:
- * http://focus.ti.com/docs/prod/folders/print/bq27000.html
- * http://focus.ti.com/docs/prod/folders/print/bq27500.html
+ * http://www.ti.com/product/bq27000
+ * http://www.ti.com/product/bq27200
+ * http://www.ti.com/product/bq27010
+ * http://www.ti.com/product/bq27210
+ * http://www.ti.com/product/bq27500
+ * http://www.ti.com/product/bq27510-g3
+ * http://www.ti.com/product/bq27520-g4
+ * http://www.ti.com/product/bq27530-g1
+ * http://www.ti.com/product/bq27531-g1
+ * http://www.ti.com/product/bq27541-g1
+ * http://www.ti.com/product/bq27542-g1
+ * http://www.ti.com/product/bq27546-g1
+ * http://www.ti.com/product/bq27742-g1
+ * http://www.ti.com/product/bq27545-g1
+ * http://www.ti.com/product/bq27421-g1
  * http://www.ti.com/product/bq27425-g1
- * http://www.ti.com/product/BQ27742-G1
- * http://www.ti.com/product/BQ27510-G3
+ * http://www.ti.com/product/bq27411-g1
+ * http://www.ti.com/product/bq27621-g1
  */
 
 #include <linux/device.h>
@@ -43,54 +56,57 @@
 
 #define BQ27XXX_MANUFACTURER	"Texas Instruments"
 
-#define BQ27x00_REG_TEMP	0x06
-#define BQ27x00_REG_VOLT	0x08
-#define BQ27x00_REG_AI		0x14
-#define BQ27x00_REG_FLAGS	0x0A
-#define BQ27x00_REG_TTE		0x16
-#define BQ27x00_REG_TTF		0x18
-#define BQ27x00_REG_TTECP	0x26
-#define BQ27x00_REG_NAC		0x0C /* Nominal available capacity */
-#define BQ27x00_REG_LMD		0x12 /* Last measured discharge */
-#define BQ27x00_REG_CYCT	0x2A /* Cycle count total */
-#define BQ27x00_REG_AE		0x22 /* Available energy */
-#define BQ27x00_POWER_AVG	0x24
-
-#define BQ27000_REG_RSOC	0x0B /* Relative State-of-Charge */
-#define BQ27000_REG_ILMD	0x76 /* Initial last measured discharge */
+/* BQ27XXX Flags */
+#define BQ27XXX_FLAG_DSC	BIT(0)
+#define BQ27XXX_FLAG_SOCF	BIT(1) /* State-of-Charge threshold final */
+#define BQ27XXX_FLAG_SOC1	BIT(2) /* State-of-Charge threshold 1 */
+#define BQ27XXX_FLAG_FC		BIT(9)
+#define BQ27XXX_FLAG_OTD	BIT(14)
+#define BQ27XXX_FLAG_OTC	BIT(15)
+
+/* BQ27000 has different layout for Flags register */
 #define BQ27000_FLAG_EDVF	BIT(0) /* Final End-of-Discharge-Voltage flag */
 #define BQ27000_FLAG_EDV1	BIT(1) /* First End-of-Discharge-Voltage flag */
 #define BQ27000_FLAG_CI		BIT(4) /* Capacity Inaccurate flag */
 #define BQ27000_FLAG_FC		BIT(5)
 #define BQ27000_FLAG_CHGS	BIT(7) /* Charge state flag */
 
-#define BQ27500_REG_SOC		0x2C
-#define BQ27500_REG_DCAP	0x3C /* Design capacity */
-#define BQ27500_FLAG_DSC	BIT(0)
-#define BQ27500_FLAG_SOCF	BIT(1) /* State-of-Charge threshold final */
-#define BQ27500_FLAG_SOC1	BIT(2) /* State-of-Charge threshold 1 */
-#define BQ27500_FLAG_FC		BIT(9)
-#define BQ27500_FLAG_OTC	BIT(15)
-
-#define BQ27742_POWER_AVG	0x76
-
-#define BQ27510_REG_SOC		0x20
-#define BQ27510_REG_DCAP	0x2E /* Design capacity */
-#define BQ27510_REG_CYCT	0x1E /* Cycle count total */
-
-/* bq27425 register addresses are same as bq27x00 addresses minus 4 */
-#define BQ27425_REG_OFFSET	0x04
-#define BQ27425_REG_SOC		(0x1C + BQ27425_REG_OFFSET)
-#define BQ27425_REG_DCAP	(0x3C + BQ27425_REG_OFFSET)
-
-#define BQ27XXX_RS			20 /* Resistor sense */
-#define BQ27XXX_POWER_CONSTANT		(256 * 29200 / 1000)
+#define BQ27XXX_RS			(20) /* Resistor sense mOhm */
+#define BQ27XXX_POWER_CONSTANT		(29200) /* 29.2 µV^2 * 1000 */
+#define BQ27XXX_CURRENT_CONSTANT	(3570) /* 3.57 µV * 1000 */
 
 struct bq27xxx_device_info;
 struct bq27xxx_access_methods {
 	int (*read)(struct bq27xxx_device_info *di, u8 reg, bool single);
 };
 
+#define INVALID_REG_ADDR	0xff
+
+/*
+ * bq27xxx_reg_index - Register names
+ *
+ * These are indexes into a device's register mapping array.
+ */
+enum bq27xxx_reg_index {
+	BQ27XXX_REG_CTRL = 0,	/* Control */
+	BQ27XXX_REG_TEMP,	/* Temperature */
+	BQ27XXX_REG_INT_TEMP,	/* Internal Temperature */
+	BQ27XXX_REG_VOLT,	/* Voltage */
+	BQ27XXX_REG_AI,		/* Average Current */
+	BQ27XXX_REG_FLAGS,	/* Flags */
+	BQ27XXX_REG_TTE,	/* Time-to-Empty */
+	BQ27XXX_REG_TTF,	/* Time-to-Full */
+	BQ27XXX_REG_TTES,	/* Time-to-Empty Standby */
+	BQ27XXX_REG_TTECP,	/* Time-to-Empty at Constant Power */
+	BQ27XXX_REG_NAC,	/* Nominal Available Capacity */
+	BQ27XXX_REG_FCC,	/* Full Charge Capacity */
+	BQ27XXX_REG_CYCT,	/* Cycle Count */
+	BQ27XXX_REG_AE,		/* Available Energy */
+	BQ27XXX_REG_SOC,	/* State-of-Charge */
+	BQ27XXX_REG_DCAP,	/* Design Capacity */
+	BQ27XXX_REG_AP,		/* Average Power */
+};
+
 struct bq27xxx_reg_cache {
 	int temperature;
 	int time_to_empty;
@@ -121,9 +137,152 @@ struct bq27xxx_device_info {
 	struct bq27xxx_access_methods bus;
 
 	struct mutex lock;
+
+	u8 *regs;
+};
+
+/* Register mappings */
+static u8 bq27000_regs[] = {
+	0x00,	/* CONTROL	*/
+	0x06,	/* TEMP		*/
+	INVALID_REG_ADDR,	/* INT TEMP - NA*/
+	0x08,	/* VOLT		*/
+	0x14,	/* AVG CURR	*/
+	0x0a,	/* FLAGS	*/
+	0x16,	/* TTE		*/
+	0x18,	/* TTF		*/
+	0x1c,	/* TTES		*/
+	0x26,	/* TTECP	*/
+	0x0c,	/* NAC		*/
+	0x12,	/* LMD(FCC)	*/
+	0x2a,	/* CYCT		*/
+	0x22,	/* AE		*/
+	0x0b,	/* SOC(RSOC)	*/
+	0x76,	/* DCAP(ILMD)	*/
+	0x24,	/* AP		*/
+};
+
+static u8 bq27010_regs[] = {
+	0x00,	/* CONTROL	*/
+	0x06,	/* TEMP		*/
+	INVALID_REG_ADDR,	/* INT TEMP - NA*/
+	0x08,	/* VOLT		*/
+	0x14,	/* AVG CURR	*/
+	0x0a,	/* FLAGS	*/
+	0x16,	/* TTE		*/
+	0x18,	/* TTF		*/
+	0x1c,	/* TTES		*/
+	0x26,	/* TTECP	*/
+	0x0c,	/* NAC		*/
+	0x12,	/* LMD(FCC)	*/
+	0x2a,	/* CYCT		*/
+	INVALID_REG_ADDR,	/* AE - NA	*/
+	0x0b,	/* SOC(RSOC)	*/
+	0x76,	/* DCAP(ILMD)	*/
+	INVALID_REG_ADDR,	/* AP - NA	*/
+};
+
+static u8 bq27500_regs[] = {
+	0x00,	/* CONTROL	*/
+	0x06,	/* TEMP		*/
+	0x28,	/* INT TEMP	*/
+	0x08,	/* VOLT		*/
+	0x14,	/* AVG CURR	*/
+	0x0a,	/* FLAGS	*/
+	0x16,	/* TTE		*/
+	INVALID_REG_ADDR,	/* TTF - NA	*/
+	0x1a,	/* TTES		*/
+	INVALID_REG_ADDR,	/* TTECP - NA	*/
+	0x0c,	/* NAC		*/
+	0x12,	/* LMD(FCC)	*/
+	0x1e,	/* CYCT		*/
+	INVALID_REG_ADDR,	/* AE - NA	*/
+	0x20,	/* SOC(RSOC)	*/
+	0x2e,	/* DCAP(ILMD)	*/
+	INVALID_REG_ADDR,	/* AP - NA	*/
 };
 
-static enum power_supply_property bq27x00_battery_props[] = {
+static u8 bq27530_regs[] = {
+	0x00,	/* CONTROL	*/
+	0x06,	/* TEMP		*/
+	0x32,	/* INT TEMP	*/
+	0x08,	/* VOLT		*/
+	0x14,	/* AVG CURR	*/
+	0x0a,	/* FLAGS	*/
+	0x16,	/* TTE		*/
+	INVALID_REG_ADDR,	/* TTF - NA	*/
+	INVALID_REG_ADDR,	/* TTES - NA	*/
+	INVALID_REG_ADDR,	/* TTECP - NA	*/
+	0x0c,	/* NAC		*/
+	0x12,	/* LMD(FCC)	*/
+	0x2a,	/* CYCT		*/
+	INVALID_REG_ADDR,	/* AE - NA	*/
+	0x2c,	/* SOC(RSOC)	*/
+	INVALID_REG_ADDR,	/* DCAP - NA	*/
+	0x24,	/* AP		*/
+};
+
+static u8 bq27541_regs[] = {
+	0x00,	/* CONTROL	*/
+	0x06,	/* TEMP		*/
+	0x28,	/* INT TEMP	*/
+	0x08,	/* VOLT		*/
+	0x14,	/* AVG CURR	*/
+	0x0a,	/* FLAGS	*/
+	0x16,	/* TTE		*/
+	INVALID_REG_ADDR,	/* TTF - NA	*/
+	INVALID_REG_ADDR,	/* TTES - NA	*/
+	INVALID_REG_ADDR,	/* TTECP - NA	*/
+	0x0c,	/* NAC		*/
+	0x12,	/* LMD(FCC)	*/
+	0x2a,	/* CYCT		*/
+	INVALID_REG_ADDR,	/* AE - NA	*/
+	0x2c,	/* SOC(RSOC)	*/
+	0x3c,	/* DCAP		*/
+	0x76,	/* AP		*/
+};
+
+static u8 bq27545_regs[] = {
+	0x00,	/* CONTROL	*/
+	0x06,	/* TEMP		*/
+	0x28,	/* INT TEMP	*/
+	0x08,	/* VOLT		*/
+	0x14,	/* AVG CURR	*/
+	0x0a,	/* FLAGS	*/
+	0x16,	/* TTE		*/
+	INVALID_REG_ADDR,	/* TTF - NA	*/
+	INVALID_REG_ADDR,	/* TTES - NA	*/
+	INVALID_REG_ADDR,	/* TTECP - NA	*/
+	0x0c,	/* NAC		*/
+	0x12,	/* LMD(FCC)	*/
+	0x2a,	/* CYCT		*/
+	INVALID_REG_ADDR,	/* AE - NA	*/
+	0x2c,	/* SOC(RSOC)	*/
+	INVALID_REG_ADDR,	/* DCAP - NA */
+	0x24,	/* AP		*/
+};
+
+static u8 bq27421_regs[] = {
+	0x00,	/* CONTROL	*/
+	0x02,	/* TEMP		*/
+	0x1e,	/* INT TEMP	*/
+	0x04,	/* VOLT		*/
+	0x10,	/* AVG CURR	*/
+	0x06,	/* FLAGS	*/
+	INVALID_REG_ADDR,	/* TTE - NA	*/
+	INVALID_REG_ADDR,	/* TTF - NA	*/
+	INVALID_REG_ADDR,	/* TTES - NA	*/
+	INVALID_REG_ADDR,	/* TTECP - NA	*/
+	0x08,	/* NAC		*/
+	0x0e,	/* FCC		*/
+	INVALID_REG_ADDR,	/* CYCT - NA	*/
+	INVALID_REG_ADDR,	/* AE - NA	*/
+	0x1c,	/* SOC		*/
+	0x3c,	/* DCAP		*/
+	0x18,	/* AP		*/
+};
+
+static enum power_supply_property bq27000_battery_props[] = {
 	POWER_SUPPLY_PROP_STATUS,
 	POWER_SUPPLY_PROP_PRESENT,
 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
@@ -145,7 +304,7 @@ static enum power_supply_property bq27x00_battery_props[] = {
 	POWER_SUPPLY_PROP_MANUFACTURER,
 };
 
-static enum power_supply_property bq27425_battery_props[] = {
+static enum power_supply_property bq27010_battery_props[] = {
 	POWER_SUPPLY_PROP_STATUS,
 	POWER_SUPPLY_PROP_PRESENT,
 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
@@ -153,14 +312,19 @@ static enum power_supply_property bq27425_battery_props[] = {
 	POWER_SUPPLY_PROP_CAPACITY,
 	POWER_SUPPLY_PROP_CAPACITY_LEVEL,
 	POWER_SUPPLY_PROP_TEMP,
+	POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
+	POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
+	POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
 	POWER_SUPPLY_PROP_TECHNOLOGY,
 	POWER_SUPPLY_PROP_CHARGE_FULL,
 	POWER_SUPPLY_PROP_CHARGE_NOW,
 	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+	POWER_SUPPLY_PROP_CYCLE_COUNT,
+	POWER_SUPPLY_PROP_HEALTH,
 	POWER_SUPPLY_PROP_MANUFACTURER,
 };
 
-static enum power_supply_property bq27742_battery_props[] = {
+static enum power_supply_property bq27500_battery_props[] = {
 	POWER_SUPPLY_PROP_STATUS,
 	POWER_SUPPLY_PROP_PRESENT,
 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
@@ -174,12 +338,29 @@ static enum power_supply_property bq27742_battery_props[] = {
 	POWER_SUPPLY_PROP_CHARGE_NOW,
 	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
 	POWER_SUPPLY_PROP_CYCLE_COUNT,
+	POWER_SUPPLY_PROP_HEALTH,
+	POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static enum power_supply_property bq27530_battery_props[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_CURRENT_NOW,
+	POWER_SUPPLY_PROP_CAPACITY,
+	POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+	POWER_SUPPLY_PROP_TEMP,
+	POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
+	POWER_SUPPLY_PROP_TECHNOLOGY,
+	POWER_SUPPLY_PROP_CHARGE_FULL,
+	POWER_SUPPLY_PROP_CHARGE_NOW,
 	POWER_SUPPLY_PROP_POWER_AVG,
 	POWER_SUPPLY_PROP_HEALTH,
+	POWER_SUPPLY_PROP_CYCLE_COUNT,
 	POWER_SUPPLY_PROP_MANUFACTURER,
 };
 
-static enum power_supply_property bq27510_battery_props[] = {
+static enum power_supply_property bq27541_battery_props[] = {
 	POWER_SUPPLY_PROP_STATUS,
 	POWER_SUPPLY_PROP_PRESENT,
 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
@@ -198,6 +379,39 @@ static enum power_supply_property bq27510_battery_props[] = {
 	POWER_SUPPLY_PROP_MANUFACTURER,
 };
 
+static enum power_supply_property bq27545_battery_props[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_CURRENT_NOW,
+	POWER_SUPPLY_PROP_CAPACITY,
+	POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+	POWER_SUPPLY_PROP_TEMP,
+	POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
+	POWER_SUPPLY_PROP_TECHNOLOGY,
+	POWER_SUPPLY_PROP_CHARGE_FULL,
+	POWER_SUPPLY_PROP_CHARGE_NOW,
+	POWER_SUPPLY_PROP_HEALTH,
+	POWER_SUPPLY_PROP_CYCLE_COUNT,
+	POWER_SUPPLY_PROP_POWER_AVG,
+	POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static enum power_supply_property bq27421_battery_props[] = {
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_CURRENT_NOW,
+	POWER_SUPPLY_PROP_CAPACITY,
+	POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+	POWER_SUPPLY_PROP_TEMP,
+	POWER_SUPPLY_PROP_TECHNOLOGY,
+	POWER_SUPPLY_PROP_CHARGE_FULL,
+	POWER_SUPPLY_PROP_CHARGE_NOW,
+	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+	POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
 static unsigned int poll_interval = 360;
 module_param(poll_interval, uint, 0644);
 MODULE_PARM_DESC(poll_interval,
@@ -207,25 +421,14 @@ MODULE_PARM_DESC(poll_interval,
  * Common code for BQ27xxx devices
  */
 
-static inline int bq27xxx_read(struct bq27xxx_device_info *di, u8 reg,
+static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index,
 			       bool single)
 {
-	if (di->chip == BQ27425)
-		return di->bus.read(di, reg - BQ27425_REG_OFFSET, single);
-	return di->bus.read(di, reg, single);
-}
+	/* Reports EINVAL for invalid/missing registers */
+	if (!di || di->regs[reg_index] == INVALID_REG_ADDR)
+		return -EINVAL;
 
-/*
- * Higher versions of the chip like BQ27425 and BQ27500
- * differ from BQ27000 and BQ27200 in calculation of certain
- * parameters. Hence we need to check for the chip type.
- */
-static bool bq27xxx_is_chip_version_higher(struct bq27xxx_device_info *di)
-{
-	if (di->chip == BQ27425 || di->chip == BQ27500 || di->chip == BQ27742
-	    || di->chip == BQ27510)
-		return true;
-	return false;
+	return di->bus.read(di, di->regs[reg_index], single);
 }
 
 /*
@@ -236,14 +439,7 @@ static int bq27xxx_battery_read_soc(struct bq27xxx_device_info *di)
 {
 	int soc;
 
-	if (di->chip == BQ27500 || di->chip == BQ27742)
-		soc = bq27xxx_read(di, BQ27500_REG_SOC, false);
-	else if (di->chip == BQ27510)
-		soc = bq27xxx_read(di, BQ27510_REG_SOC, false);
-	else if (di->chip == BQ27425)
-		soc = bq27xxx_read(di, BQ27425_REG_SOC, false);
-	else /* for the bq27000 we read the "relative" SoC register */
-		soc = bq27xxx_read(di, BQ27000_REG_RSOC, true);
+	soc = bq27xxx_read(di, BQ27XXX_REG_SOC, false);
 
 	if (soc < 0)
 		dev_dbg(di->dev, "error reading State-of-Charge\n");
@@ -266,10 +462,10 @@ static int bq27xxx_battery_read_charge(struct bq27xxx_device_info *di, u8 reg)
 		return charge;
 	}
 
-	if (bq27xxx_is_chip_version_higher(di))
-		charge *= 1000;
+	if (di->chip == BQ27000 || di->chip == BQ27010)
+		charge *= BQ27XXX_CURRENT_CONSTANT / BQ27XXX_RS;
 	else
-		charge = charge * 3570 / BQ27XXX_RS;
+		charge *= 1000;
 
 	return charge;
 }
@@ -281,57 +477,46 @@ static int bq27xxx_battery_read_charge(struct bq27xxx_device_info *di, u8 reg)
 static inline int bq27xxx_battery_read_nac(struct bq27xxx_device_info *di)
 {
 	int flags;
-	bool is_bq27500 = di->chip == BQ27500;
-	bool is_bq27742 = di->chip == BQ27742;
-	bool is_higher = bq27xxx_is_chip_version_higher(di);
-	bool flags_1b = !(is_bq27500 || is_bq27742);
 
-	flags = bq27xxx_read(di, BQ27x00_REG_FLAGS, flags_1b);
-	if (flags >= 0 && !is_higher && (flags & BQ27000_FLAG_CI))
-		return -ENODATA;
+	if (di->chip == BQ27000 || di->chip == BQ27010) {
+		flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, true);
+		if (flags >= 0 && (flags & BQ27000_FLAG_CI))
+			return -ENODATA;
+	}
 
-	return bq27xxx_battery_read_charge(di, BQ27x00_REG_NAC);
+	return bq27xxx_battery_read_charge(di, BQ27XXX_REG_NAC);
 }
 
 /*
- * Return the battery Last measured discharge in µAh
+ * Return the battery Full Charge Capacity in µAh
  * Or < 0 if something fails.
  */
-static inline int bq27xxx_battery_read_lmd(struct bq27xxx_device_info *di)
+static inline int bq27xxx_battery_read_fcc(struct bq27xxx_device_info *di)
 {
-	return bq27xxx_battery_read_charge(di, BQ27x00_REG_LMD);
+	return bq27xxx_battery_read_charge(di, BQ27XXX_REG_FCC);
 }
 
 /*
- * Return the battery Initial last measured discharge in µAh
+ * Return the Design Capacity in µAh
  * Or < 0 if something fails.
  */
-static int bq27xxx_battery_read_ilmd(struct bq27xxx_device_info *di)
+static int bq27xxx_battery_read_dcap(struct bq27xxx_device_info *di)
 {
-	int ilmd;
+	int dcap;
 
-	if (bq27xxx_is_chip_version_higher(di)) {
-		if (di->chip == BQ27425)
-			ilmd = bq27xxx_read(di, BQ27425_REG_DCAP, false);
-		else if (di->chip == BQ27510)
-			ilmd = bq27xxx_read(di, BQ27510_REG_DCAP, false);
-		else
-			ilmd = bq27xxx_read(di, BQ27500_REG_DCAP, false);
-	} else {
-		ilmd = bq27xxx_read(di, BQ27000_REG_ILMD, true);
-	}
+	dcap = bq27xxx_read(di, BQ27XXX_REG_DCAP, false);
 
-	if (ilmd < 0) {
+	if (dcap < 0) {
 		dev_dbg(di->dev, "error reading initial last measured discharge\n");
-		return ilmd;
+		return dcap;
 	}
 
-	if (bq27xxx_is_chip_version_higher(di))
-		ilmd *= 1000;
+	if (di->chip == BQ27000 || di->chip == BQ27010)
+		dcap *= BQ27XXX_CURRENT_CONSTANT / BQ27XXX_RS;
 	else
-		ilmd = ilmd * 256 * 3570 / BQ27XXX_RS;
+		dcap *= 1000;
 
-	return ilmd;
+	return dcap;
 }
 
 /*
@@ -342,16 +527,16 @@ static int bq27xxx_battery_read_energy(struct bq27xxx_device_info *di)
 {
 	int ae;
 
-	ae = bq27xxx_read(di, BQ27x00_REG_AE, false);
+	ae = bq27xxx_read(di, BQ27XXX_REG_AE, false);
 	if (ae < 0) {
 		dev_dbg(di->dev, "error reading available energy\n");
 		return ae;
 	}
 
-	if (di->chip == BQ27500)
-		ae *= 1000;
+	if (di->chip == BQ27000 || di->chip == BQ27010)
+		ae *= BQ27XXX_POWER_CONSTANT / BQ27XXX_RS;
 	else
-		ae = ae * 29200 / BQ27XXX_RS;
+		ae *= 1000;
 
 	return ae;
 }
@@ -364,13 +549,13 @@ static int bq27xxx_battery_read_temperature(struct bq27xxx_device_info *di)
 {
 	int temp;
 
-	temp = bq27xxx_read(di, BQ27x00_REG_TEMP, false);
+	temp = bq27xxx_read(di, BQ27XXX_REG_TEMP, false);
 	if (temp < 0) {
 		dev_err(di->dev, "error reading temperature\n");
 		return temp;
 	}
 
-	if (!bq27xxx_is_chip_version_higher(di))
+	if (di->chip == BQ27000 || di->chip == BQ27010)
 		temp = 5 * temp / 2;
 
 	return temp;
@@ -384,10 +569,7 @@ static int bq27xxx_battery_read_cyct(struct bq27xxx_device_info *di)
 {
 	int cyct;
 
-	if (di->chip == BQ27510)
-		cyct = bq27xxx_read(di, BQ27510_REG_CYCT, false);
-	else
-		cyct = bq27xxx_read(di, BQ27x00_REG_CYCT, false);
+	cyct = bq27xxx_read(di, BQ27XXX_REG_CYCT, false);
 	if (cyct < 0)
 		dev_err(di->dev, "error reading cycle count total\n");
 
@@ -419,21 +601,32 @@ static int bq27xxx_battery_read_time(struct bq27xxx_device_info *di, u8 reg)
  * Read an average power register.
  * Return < 0 if something fails.
  */
-static int bq27xxx_battery_read_pwr_avg(struct bq27xxx_device_info *di, u8 reg)
+static int bq27xxx_battery_read_pwr_avg(struct bq27xxx_device_info *di)
 {
 	int tval;
 
-	tval = bq27xxx_read(di, reg, false);
+	tval = bq27xxx_read(di, BQ27XXX_REG_AP, false);
 	if (tval < 0) {
-		dev_err(di->dev, "error reading power avg rgister  %02x: %d\n",
-			reg, tval);
+		dev_err(di->dev, "error reading average power register  %02x: %d\n",
+			BQ27XXX_REG_AP, tval);
 		return tval;
 	}
 
-	if (di->chip == BQ27500)
+	if (di->chip == BQ27000 || di->chip == BQ27010)
+		return (tval * BQ27XXX_POWER_CONSTANT) / BQ27XXX_RS;
+	else
 		return tval;
+}
+
+/*
+ * Returns true if a battery over temperature condition is detected
+ */
+static int bq27xxx_battery_overtemp(struct bq27xxx_device_info *di, u16 flags)
+{
+	if (di->chip == BQ27500 || di->chip == BQ27541)
+		return flags & (BQ27XXX_FLAG_OTC | BQ27XXX_FLAG_OTD);
 	else
-		return (tval * BQ27XXX_POWER_CONSTANT) / BQ27XXX_RS;
+		return flags & BQ27XXX_FLAG_OTC;
 }
 
 /*
@@ -442,53 +635,43 @@ static int bq27xxx_battery_read_pwr_avg(struct bq27xxx_device_info *di, u8 reg)
  */
 static int bq27xxx_battery_read_health(struct bq27xxx_device_info *di)
 {
-	int tval;
+	u16 tval;
 
-	tval = bq27xxx_read(di, BQ27x00_REG_FLAGS, false);
+	tval = bq27xxx_read(di, BQ27XXX_REG_FLAGS, false);
 	if (tval < 0) {
 		dev_err(di->dev, "error reading flag register:%d\n", tval);
 		return tval;
 	}
 
-	if (di->chip == BQ27500) {
-		if (tval & BQ27500_FLAG_SOCF)
+	if (di->chip == BQ27000 || di->chip == BQ27010) {
+		if (tval & BQ27000_FLAG_EDV1)
 			tval = POWER_SUPPLY_HEALTH_DEAD;
-		else if (tval & BQ27500_FLAG_OTC)
-			tval = POWER_SUPPLY_HEALTH_OVERHEAT;
 		else
 			tval = POWER_SUPPLY_HEALTH_GOOD;
-		return tval;
-	} else if (di->chip == BQ27510) {
-		if (tval & BQ27500_FLAG_OTC)
-			return POWER_SUPPLY_HEALTH_OVERHEAT;
-		return POWER_SUPPLY_HEALTH_GOOD;
 	} else {
-		if (tval & BQ27000_FLAG_EDV1)
+		if (tval & BQ27XXX_FLAG_SOCF)
 			tval = POWER_SUPPLY_HEALTH_DEAD;
+		else if (bq27xxx_battery_overtemp(di, tval))
+			tval = POWER_SUPPLY_HEALTH_OVERHEAT;
 		else
 			tval = POWER_SUPPLY_HEALTH_GOOD;
-		return tval;
 	}
 
-	return -1;
+	return tval;
 }
 
 static void bq27xxx_battery_update(struct bq27xxx_device_info *di)
 {
 	struct bq27xxx_reg_cache cache = {0, };
-	bool is_bq27500 = di->chip == BQ27500;
-	bool is_bq27510 = di->chip == BQ27510;
-	bool is_bq27425 = di->chip == BQ27425;
-	bool is_bq27742 = di->chip == BQ27742;
-	bool flags_1b = !(is_bq27500 || is_bq27742);
+	bool has_ci_flag = di->chip == BQ27000 || di->chip == BQ27010;
+	bool has_singe_flag = di->chip == BQ27000 || di->chip == BQ27010;
 
-	cache.flags = bq27xxx_read(di, BQ27x00_REG_FLAGS, flags_1b);
+	cache.flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, has_singe_flag);
 	if ((cache.flags & 0xff) == 0xff)
-		/* read error */
-		cache.flags = -1;
+		cache.flags = -1; /* read error */
 	if (cache.flags >= 0) {
-		if (!is_bq27500 && !is_bq27425 && !is_bq27742 && !is_bq27510
-				&& (cache.flags & BQ27000_FLAG_CI)) {
+		cache.temperature = bq27xxx_battery_read_temperature(di);
+		if (has_ci_flag && (cache.flags & BQ27000_FLAG_CI)) {
 			dev_info(di->dev, "battery is not calibrated! ignoring capacity values\n");
 			cache.capacity = -ENODATA;
 			cache.energy = -ENODATA;
@@ -498,41 +681,26 @@ static void bq27xxx_battery_update(struct bq27xxx_device_info *di)
 			cache.charge_full = -ENODATA;
 			cache.health = -ENODATA;
 		} else {
+			if (di->regs[BQ27XXX_REG_TTE] != INVALID_REG_ADDR)
+				cache.time_to_empty = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTE);
+			if (di->regs[BQ27XXX_REG_TTECP] != INVALID_REG_ADDR)
+				cache.time_to_empty_avg = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTECP);
+			if (di->regs[BQ27XXX_REG_TTF] != INVALID_REG_ADDR)
+				cache.time_to_full = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTF);
+			cache.charge_full = bq27xxx_battery_read_fcc(di);
 			cache.capacity = bq27xxx_battery_read_soc(di);
-			if (is_bq27742 || is_bq27510)
-				cache.time_to_empty =
-					bq27xxx_battery_read_time(di,
-							BQ27x00_REG_TTE);
-			else if (!is_bq27425) {
+			if (di->regs[BQ27XXX_REG_AE] != INVALID_REG_ADDR)
 				cache.energy = bq27xxx_battery_read_energy(di);
-				cache.time_to_empty =
-					bq27xxx_battery_read_time(di,
-							BQ27x00_REG_TTE);
-				cache.time_to_empty_avg =
-					bq27xxx_battery_read_time(di,
-							BQ27x00_REG_TTECP);
-				cache.time_to_full =
-					bq27xxx_battery_read_time(di,
-							BQ27x00_REG_TTF);
-			}
-			cache.charge_full = bq27xxx_battery_read_lmd(di);
 			cache.health = bq27xxx_battery_read_health(di);
 		}
-		cache.temperature = bq27xxx_battery_read_temperature(di);
-		if (!is_bq27425)
+		if (di->regs[BQ27XXX_REG_CYCT] != INVALID_REG_ADDR)
 			cache.cycle_count = bq27xxx_battery_read_cyct(di);
-		if (is_bq27742)
-			cache.power_avg =
-				bq27xxx_battery_read_pwr_avg(di,
-						BQ27742_POWER_AVG);
-		else
-			cache.power_avg =
-				bq27xxx_battery_read_pwr_avg(di,
-						BQ27x00_POWER_AVG);
+		if (di->regs[BQ27XXX_REG_AP] != INVALID_REG_ADDR)
+			cache.power_avg = bq27xxx_battery_read_pwr_avg(di);
 
 		/* We only have to read charge design full once */
 		if (di->charge_design_full <= 0)
-			di->charge_design_full = bq27xxx_battery_read_ilmd(di);
+			di->charge_design_full = bq27xxx_battery_read_dcap(di);
 	}
 
 	if (di->cache.capacity != cache.capacity)
@@ -547,7 +715,8 @@ static void bq27xxx_battery_update(struct bq27xxx_device_info *di)
 static void bq27xxx_battery_poll(struct work_struct *work)
 {
 	struct bq27xxx_device_info *di =
-		container_of(work, struct bq27xxx_device_info, work.work);
+			container_of(work, struct bq27xxx_device_info,
+				     work.work);
 
 	bq27xxx_battery_update(di);
 
@@ -569,23 +738,23 @@ static int bq27xxx_battery_current(struct bq27xxx_device_info *di,
 	int curr;
 	int flags;
 
-	curr = bq27xxx_read(di, BQ27x00_REG_AI, false);
+	curr = bq27xxx_read(di, BQ27XXX_REG_AI, false);
 	if (curr < 0) {
 		dev_err(di->dev, "error reading current\n");
 		return curr;
 	}
 
-	if (bq27xxx_is_chip_version_higher(di)) {
-		/* bq27500 returns signed value */
-		val->intval = (int)((s16)curr) * 1000;
-	} else {
-		flags = bq27xxx_read(di, BQ27x00_REG_FLAGS, false);
+	if (di->chip == BQ27000 || di->chip == BQ27010) {
+		flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, false);
 		if (flags & BQ27000_FLAG_CHGS) {
 			dev_dbg(di->dev, "negative current!\n");
 			curr = -curr;
 		}
 
-		val->intval = curr * 3570 / BQ27XXX_RS;
+		val->intval = curr * BQ27XXX_CURRENT_CONSTANT / BQ27XXX_RS;
+	} else {
+		/* Other gauges return signed value */
+		val->intval = (int)((s16)curr) * 1000;
 	}
 
 	return 0;
@@ -596,14 +765,7 @@ static int bq27xxx_battery_status(struct bq27xxx_device_info *di,
 {
 	int status;
 
-	if (bq27xxx_is_chip_version_higher(di)) {
-		if (di->cache.flags & BQ27500_FLAG_FC)
-			status = POWER_SUPPLY_STATUS_FULL;
-		else if (di->cache.flags & BQ27500_FLAG_DSC)
-			status = POWER_SUPPLY_STATUS_DISCHARGING;
-		else
-			status = POWER_SUPPLY_STATUS_CHARGING;
-	} else {
+	if (di->chip == BQ27000 || di->chip == BQ27010) {
 		if (di->cache.flags & BQ27000_FLAG_FC)
 			status = POWER_SUPPLY_STATUS_FULL;
 		else if (di->cache.flags & BQ27000_FLAG_CHGS)
@@ -612,6 +774,13 @@ static int bq27xxx_battery_status(struct bq27xxx_device_info *di,
 			status = POWER_SUPPLY_STATUS_NOT_CHARGING;
 		else
 			status = POWER_SUPPLY_STATUS_DISCHARGING;
+	} else {
+		if (di->cache.flags & BQ27XXX_FLAG_FC)
+			status = POWER_SUPPLY_STATUS_FULL;
+		else if (di->cache.flags & BQ27XXX_FLAG_DSC)
+			status = POWER_SUPPLY_STATUS_DISCHARGING;
+		else
+			status = POWER_SUPPLY_STATUS_CHARGING;
 	}
 
 	val->intval = status;
@@ -624,21 +793,21 @@ static int bq27xxx_battery_capacity_level(struct bq27xxx_device_info *di,
 {
 	int level;
 
-	if (bq27xxx_is_chip_version_higher(di)) {
-		if (di->cache.flags & BQ27500_FLAG_FC)
+	if (di->chip == BQ27000 || di->chip == BQ27010) {
+		if (di->cache.flags & BQ27000_FLAG_FC)
 			level = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
-		else if (di->cache.flags & BQ27500_FLAG_SOC1)
+		else if (di->cache.flags & BQ27000_FLAG_EDV1)
 			level = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
-		else if (di->cache.flags & BQ27500_FLAG_SOCF)
+		else if (di->cache.flags & BQ27000_FLAG_EDVF)
 			level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
 		else
 			level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
 	} else {
-		if (di->cache.flags & BQ27000_FLAG_FC)
+		if (di->cache.flags & BQ27XXX_FLAG_FC)
 			level = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
-		else if (di->cache.flags & BQ27000_FLAG_EDV1)
+		else if (di->cache.flags & BQ27XXX_FLAG_SOC1)
 			level = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
-		else if (di->cache.flags & BQ27000_FLAG_EDVF)
+		else if (di->cache.flags & BQ27XXX_FLAG_SOCF)
 			level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
 		else
 			level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
@@ -658,7 +827,7 @@ static int bq27xxx_battery_voltage(struct bq27xxx_device_info *di,
 {
 	int volt;
 
-	volt = bq27xxx_read(di, BQ27x00_REG_VOLT, false);
+	volt = bq27xxx_read(di, BQ27XXX_REG_VOLT, false);
 	if (volt < 0) {
 		dev_err(di->dev, "error reading voltage\n");
 		return volt;
@@ -719,7 +888,7 @@ static int bq27xxx_battery_get_property(struct power_supply *psy,
 	case POWER_SUPPLY_PROP_TEMP:
 		ret = bq27xxx_simple_value(di->cache.temperature, val);
 		if (ret == 0)
-			val->intval -= 2731;
+			val->intval -= 2731; /* convert decidegree k to c */
 		break;
 	case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
 		ret = bq27xxx_simple_value(di->cache.time_to_empty, val);
@@ -785,18 +954,27 @@ static int bq27xxx_powersupply_init(struct bq27xxx_device_info *di,
 
 	psy_desc->name = name;
 	psy_desc->type = POWER_SUPPLY_TYPE_BATTERY;
-	if (di->chip == BQ27425) {
-		psy_desc->properties = bq27425_battery_props;
-		psy_desc->num_properties = ARRAY_SIZE(bq27425_battery_props);
-	} else if (di->chip == BQ27742) {
-		psy_desc->properties = bq27742_battery_props;
-		psy_desc->num_properties = ARRAY_SIZE(bq27742_battery_props);
-	} else if (di->chip == BQ27510) {
-		psy_desc->properties = bq27510_battery_props;
-		psy_desc->num_properties = ARRAY_SIZE(bq27510_battery_props);
+	if (di->chip == BQ27000) {
+		psy_desc->properties = bq27000_battery_props;
+		psy_desc->num_properties = ARRAY_SIZE(bq27000_battery_props);
+	} else if (di->chip == BQ27010) {
+		psy_desc->properties = bq27010_battery_props;
+		psy_desc->num_properties = ARRAY_SIZE(bq27010_battery_props);
+	} else if (di->chip == BQ27500) {
+		psy_desc->properties = bq27500_battery_props;
+		psy_desc->num_properties = ARRAY_SIZE(bq27500_battery_props);
+	} else if (di->chip == BQ27530) {
+		psy_desc->properties = bq27530_battery_props;
+		psy_desc->num_properties = ARRAY_SIZE(bq27530_battery_props);
+	} else if (di->chip == BQ27541) {
+		psy_desc->properties = bq27541_battery_props;
+		psy_desc->num_properties = ARRAY_SIZE(bq27541_battery_props);
+	} else if (di->chip == BQ27545) {
+		psy_desc->properties = bq27545_battery_props;
+		psy_desc->num_properties = ARRAY_SIZE(bq27545_battery_props);
 	} else {
-		psy_desc->properties = bq27x00_battery_props;
-		psy_desc->num_properties = ARRAY_SIZE(bq27x00_battery_props);
+		psy_desc->properties = bq27421_battery_props;
+		psy_desc->num_properties = ARRAY_SIZE(bq27421_battery_props);
 	}
 	psy_desc->get_property = bq27xxx_battery_get_property;
 	psy_desc->external_power_changed = bq27xxx_external_power_changed;
@@ -911,10 +1089,32 @@ static int bq27xxx_battery_i2c_probe(struct i2c_client *client,
 	di->chip = id->driver_data;
 	di->bus.read = &bq27xxx_battery_i2c_read;
 
+	if (di->chip == BQ27000) {
+		di->regs = bq27000_regs;
+	} else if (di->chip == BQ27010) {
+		di->regs = bq27010_regs;
+	} else if (di->chip == BQ27500) {
+		di->regs = bq27500_regs;
+	} else if (di->chip == BQ27530) {
+		di->regs = bq27530_regs;
+	} else if (di->chip == BQ27541) {
+		di->regs = bq27541_regs;
+	} else if (di->chip == BQ27545) {
+		di->regs = bq27545_regs;
+	} else if (di->chip == BQ27421) {
+		di->regs = bq27421_regs;
+	} else {
+		dev_err(&client->dev, "Unexpected gas gauge: %d\n", di->chip);
+		di->regs = bq27000_regs;
+	}
+
 	retval = bq27xxx_powersupply_init(di, name);
 	if (retval)
 		goto batt_failed;
 
+	/* Schedule a polling after about 1 min */
+	schedule_delayed_work(&di->work, 60 * HZ);
+
 	i2c_set_clientdata(client, di);
 
 	return 0;
@@ -941,11 +1141,22 @@ static int bq27xxx_battery_i2c_remove(struct i2c_client *client)
 }
 
 static const struct i2c_device_id bq27xxx_id[] = {
-	{ "bq27200", BQ27000 },	/* bq27200 is same as bq27000, but with i2c */
+	{ "bq27200", BQ27000 },
+	{ "bq27210", BQ27010 },
 	{ "bq27500", BQ27500 },
-	{ "bq27425", BQ27425 },
-	{ "bq27742", BQ27742 },
-	{ "bq27510", BQ27510 },
+	{ "bq27510", BQ27500 },
+	{ "bq27520", BQ27500 },
+	{ "bq27530", BQ27530 },
+	{ "bq27531", BQ27530 },
+	{ "bq27541", BQ27541 },
+	{ "bq27542", BQ27541 },
+	{ "bq27546", BQ27541 },
+	{ "bq27742", BQ27541 },
+	{ "bq27545", BQ27545 },
+	{ "bq27421", BQ27421 },
+	{ "bq27425", BQ27421 },
+	{ "bq27441", BQ27421 },
+	{ "bq27621", BQ27421 },
 	{},
 };
 MODULE_DEVICE_TABLE(i2c, bq27xxx_id);
diff --git a/include/linux/power/bq27xxx_battery.h b/include/linux/power/bq27xxx_battery.h
index a4efb10..45f6a7b 100644
--- a/include/linux/power/bq27xxx_battery.h
+++ b/include/linux/power/bq27xxx_battery.h
@@ -12,7 +12,15 @@
  *	register to be read. The return value should either be the content of
  *	the passed register or an error value.
  */
-enum bq27xxx_chip { BQ27000 = 1, BQ27500, BQ27425, BQ27742, BQ27510 };
+enum bq27xxx_chip {
+	BQ27000 = 1, /* bq27000, bq27200 */
+	BQ27010, /* bq27010, bq27210 */
+	BQ27500, /* bq27500, bq27510, bq27520 */
+	BQ27530, /* bq27530, bq27531 */
+	BQ27541, /* bq27541, bq27542, bq27546, bq27742 */
+	BQ27545, /* bq27545 */
+	BQ27421, /* bq27421, bq27425, bq27441, bq27621 */
+};
 
 struct bq27xxx_platform_data {
 	const char *name;
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ