|
@@ -5,6 +5,7 @@
|
|
* Copyright (C) 2008 Eurotech S.p.A. <info@eurotech.it>
|
|
* Copyright (C) 2008 Eurotech S.p.A. <info@eurotech.it>
|
|
* Copyright (C) 2010-2011 Lars-Peter Clausen <lars@metafoo.de>
|
|
* Copyright (C) 2010-2011 Lars-Peter Clausen <lars@metafoo.de>
|
|
* Copyright (C) 2011 Pali Rohár <pali.rohar@gmail.com>
|
|
* Copyright (C) 2011 Pali Rohár <pali.rohar@gmail.com>
|
|
|
|
+ * Copyright (C) 2017 Liam Breck <kernel@networkimprov.net>
|
|
*
|
|
*
|
|
* Based on a previous work by Copyright (C) 2008 Texas Instruments, Inc.
|
|
* Based on a previous work by Copyright (C) 2008 Texas Instruments, Inc.
|
|
*
|
|
*
|
|
@@ -65,6 +66,7 @@
|
|
#define BQ27XXX_FLAG_DSC BIT(0)
|
|
#define BQ27XXX_FLAG_DSC BIT(0)
|
|
#define BQ27XXX_FLAG_SOCF BIT(1) /* State-of-Charge threshold final */
|
|
#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_SOC1 BIT(2) /* State-of-Charge threshold 1 */
|
|
|
|
+#define BQ27XXX_FLAG_CFGUP BIT(4)
|
|
#define BQ27XXX_FLAG_FC BIT(9)
|
|
#define BQ27XXX_FLAG_FC BIT(9)
|
|
#define BQ27XXX_FLAG_OTD BIT(14)
|
|
#define BQ27XXX_FLAG_OTD BIT(14)
|
|
#define BQ27XXX_FLAG_OTC BIT(15)
|
|
#define BQ27XXX_FLAG_OTC BIT(15)
|
|
@@ -78,6 +80,12 @@
|
|
#define BQ27000_FLAG_FC BIT(5)
|
|
#define BQ27000_FLAG_FC BIT(5)
|
|
#define BQ27000_FLAG_CHGS BIT(7) /* Charge state flag */
|
|
#define BQ27000_FLAG_CHGS BIT(7) /* Charge state flag */
|
|
|
|
|
|
|
|
+/* control register params */
|
|
|
|
+#define BQ27XXX_SEALED 0x20
|
|
|
|
+#define BQ27XXX_SET_CFGUPDATE 0x13
|
|
|
|
+#define BQ27XXX_SOFT_RESET 0x42
|
|
|
|
+#define BQ27XXX_RESET 0x41
|
|
|
|
+
|
|
#define BQ27XXX_RS (20) /* Resistor sense mOhm */
|
|
#define BQ27XXX_RS (20) /* Resistor sense mOhm */
|
|
#define BQ27XXX_POWER_CONSTANT (29200) /* 29.2 µV^2 * 1000 */
|
|
#define BQ27XXX_POWER_CONSTANT (29200) /* 29.2 µV^2 * 1000 */
|
|
#define BQ27XXX_CURRENT_CONSTANT (3570) /* 3.57 µV * 1000 */
|
|
#define BQ27XXX_CURRENT_CONSTANT (3570) /* 3.57 µV * 1000 */
|
|
@@ -108,9 +116,21 @@ enum bq27xxx_reg_index {
|
|
BQ27XXX_REG_SOC, /* State-of-Charge */
|
|
BQ27XXX_REG_SOC, /* State-of-Charge */
|
|
BQ27XXX_REG_DCAP, /* Design Capacity */
|
|
BQ27XXX_REG_DCAP, /* Design Capacity */
|
|
BQ27XXX_REG_AP, /* Average Power */
|
|
BQ27XXX_REG_AP, /* Average Power */
|
|
|
|
+ BQ27XXX_DM_CTRL, /* Block Data Control */
|
|
|
|
+ BQ27XXX_DM_CLASS, /* Data Class */
|
|
|
|
+ BQ27XXX_DM_BLOCK, /* Data Block */
|
|
|
|
+ BQ27XXX_DM_DATA, /* Block Data */
|
|
|
|
+ BQ27XXX_DM_CKSUM, /* Block Data Checksum */
|
|
BQ27XXX_REG_MAX, /* sentinel */
|
|
BQ27XXX_REG_MAX, /* sentinel */
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+#define BQ27XXX_DM_REG_ROWS \
|
|
|
|
+ [BQ27XXX_DM_CTRL] = 0x61, \
|
|
|
|
+ [BQ27XXX_DM_CLASS] = 0x3e, \
|
|
|
|
+ [BQ27XXX_DM_BLOCK] = 0x3f, \
|
|
|
|
+ [BQ27XXX_DM_DATA] = 0x40, \
|
|
|
|
+ [BQ27XXX_DM_CKSUM] = 0x60
|
|
|
|
+
|
|
/* Register mappings */
|
|
/* Register mappings */
|
|
static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
|
|
static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
|
|
[BQ27000] = {
|
|
[BQ27000] = {
|
|
@@ -131,6 +151,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
|
|
[BQ27XXX_REG_SOC] = 0x0b,
|
|
[BQ27XXX_REG_SOC] = 0x0b,
|
|
[BQ27XXX_REG_DCAP] = 0x76,
|
|
[BQ27XXX_REG_DCAP] = 0x76,
|
|
[BQ27XXX_REG_AP] = 0x24,
|
|
[BQ27XXX_REG_AP] = 0x24,
|
|
|
|
+ [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,
|
|
},
|
|
},
|
|
[BQ27010] = {
|
|
[BQ27010] = {
|
|
[BQ27XXX_REG_CTRL] = 0x00,
|
|
[BQ27XXX_REG_CTRL] = 0x00,
|
|
@@ -150,6 +175,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
|
|
[BQ27XXX_REG_SOC] = 0x0b,
|
|
[BQ27XXX_REG_SOC] = 0x0b,
|
|
[BQ27XXX_REG_DCAP] = 0x76,
|
|
[BQ27XXX_REG_DCAP] = 0x76,
|
|
[BQ27XXX_REG_AP] = 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,
|
|
},
|
|
},
|
|
[BQ2750X] = {
|
|
[BQ2750X] = {
|
|
[BQ27XXX_REG_CTRL] = 0x00,
|
|
[BQ27XXX_REG_CTRL] = 0x00,
|
|
@@ -169,6 +199,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
|
|
[BQ27XXX_REG_SOC] = 0x2c,
|
|
[BQ27XXX_REG_SOC] = 0x2c,
|
|
[BQ27XXX_REG_DCAP] = 0x3c,
|
|
[BQ27XXX_REG_DCAP] = 0x3c,
|
|
[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
|
|
[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
|
|
|
|
+ BQ27XXX_DM_REG_ROWS,
|
|
},
|
|
},
|
|
[BQ2751X] = {
|
|
[BQ2751X] = {
|
|
[BQ27XXX_REG_CTRL] = 0x00,
|
|
[BQ27XXX_REG_CTRL] = 0x00,
|
|
@@ -188,6 +219,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
|
|
[BQ27XXX_REG_SOC] = 0x20,
|
|
[BQ27XXX_REG_SOC] = 0x20,
|
|
[BQ27XXX_REG_DCAP] = 0x2e,
|
|
[BQ27XXX_REG_DCAP] = 0x2e,
|
|
[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
|
|
[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
|
|
|
|
+ BQ27XXX_DM_REG_ROWS,
|
|
},
|
|
},
|
|
[BQ27500] = {
|
|
[BQ27500] = {
|
|
[BQ27XXX_REG_CTRL] = 0x00,
|
|
[BQ27XXX_REG_CTRL] = 0x00,
|
|
@@ -207,6 +239,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
|
|
[BQ27XXX_REG_SOC] = 0x2c,
|
|
[BQ27XXX_REG_SOC] = 0x2c,
|
|
[BQ27XXX_REG_DCAP] = 0x3c,
|
|
[BQ27XXX_REG_DCAP] = 0x3c,
|
|
[BQ27XXX_REG_AP] = 0x24,
|
|
[BQ27XXX_REG_AP] = 0x24,
|
|
|
|
+ BQ27XXX_DM_REG_ROWS,
|
|
},
|
|
},
|
|
[BQ27510G1] = {
|
|
[BQ27510G1] = {
|
|
[BQ27XXX_REG_CTRL] = 0x00,
|
|
[BQ27XXX_REG_CTRL] = 0x00,
|
|
@@ -226,6 +259,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
|
|
[BQ27XXX_REG_SOC] = 0x2c,
|
|
[BQ27XXX_REG_SOC] = 0x2c,
|
|
[BQ27XXX_REG_DCAP] = 0x3c,
|
|
[BQ27XXX_REG_DCAP] = 0x3c,
|
|
[BQ27XXX_REG_AP] = 0x24,
|
|
[BQ27XXX_REG_AP] = 0x24,
|
|
|
|
+ BQ27XXX_DM_REG_ROWS,
|
|
},
|
|
},
|
|
[BQ27510G2] = {
|
|
[BQ27510G2] = {
|
|
[BQ27XXX_REG_CTRL] = 0x00,
|
|
[BQ27XXX_REG_CTRL] = 0x00,
|
|
@@ -245,6 +279,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
|
|
[BQ27XXX_REG_SOC] = 0x2c,
|
|
[BQ27XXX_REG_SOC] = 0x2c,
|
|
[BQ27XXX_REG_DCAP] = 0x3c,
|
|
[BQ27XXX_REG_DCAP] = 0x3c,
|
|
[BQ27XXX_REG_AP] = 0x24,
|
|
[BQ27XXX_REG_AP] = 0x24,
|
|
|
|
+ BQ27XXX_DM_REG_ROWS,
|
|
},
|
|
},
|
|
[BQ27510G3] = {
|
|
[BQ27510G3] = {
|
|
[BQ27XXX_REG_CTRL] = 0x00,
|
|
[BQ27XXX_REG_CTRL] = 0x00,
|
|
@@ -264,6 +299,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
|
|
[BQ27XXX_REG_SOC] = 0x20,
|
|
[BQ27XXX_REG_SOC] = 0x20,
|
|
[BQ27XXX_REG_DCAP] = 0x2e,
|
|
[BQ27XXX_REG_DCAP] = 0x2e,
|
|
[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
|
|
[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
|
|
|
|
+ BQ27XXX_DM_REG_ROWS,
|
|
},
|
|
},
|
|
[BQ27520G1] = {
|
|
[BQ27520G1] = {
|
|
[BQ27XXX_REG_CTRL] = 0x00,
|
|
[BQ27XXX_REG_CTRL] = 0x00,
|
|
@@ -283,6 +319,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
|
|
[BQ27XXX_REG_SOC] = 0x2c,
|
|
[BQ27XXX_REG_SOC] = 0x2c,
|
|
[BQ27XXX_REG_DCAP] = 0x3c,
|
|
[BQ27XXX_REG_DCAP] = 0x3c,
|
|
[BQ27XXX_REG_AP] = 0x24,
|
|
[BQ27XXX_REG_AP] = 0x24,
|
|
|
|
+ BQ27XXX_DM_REG_ROWS,
|
|
},
|
|
},
|
|
[BQ27520G2] = {
|
|
[BQ27520G2] = {
|
|
[BQ27XXX_REG_CTRL] = 0x00,
|
|
[BQ27XXX_REG_CTRL] = 0x00,
|
|
@@ -302,6 +339,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
|
|
[BQ27XXX_REG_SOC] = 0x2c,
|
|
[BQ27XXX_REG_SOC] = 0x2c,
|
|
[BQ27XXX_REG_DCAP] = 0x3c,
|
|
[BQ27XXX_REG_DCAP] = 0x3c,
|
|
[BQ27XXX_REG_AP] = 0x24,
|
|
[BQ27XXX_REG_AP] = 0x24,
|
|
|
|
+ BQ27XXX_DM_REG_ROWS,
|
|
},
|
|
},
|
|
[BQ27520G3] = {
|
|
[BQ27520G3] = {
|
|
[BQ27XXX_REG_CTRL] = 0x00,
|
|
[BQ27XXX_REG_CTRL] = 0x00,
|
|
@@ -321,6 +359,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
|
|
[BQ27XXX_REG_SOC] = 0x2c,
|
|
[BQ27XXX_REG_SOC] = 0x2c,
|
|
[BQ27XXX_REG_DCAP] = 0x3c,
|
|
[BQ27XXX_REG_DCAP] = 0x3c,
|
|
[BQ27XXX_REG_AP] = 0x24,
|
|
[BQ27XXX_REG_AP] = 0x24,
|
|
|
|
+ BQ27XXX_DM_REG_ROWS,
|
|
},
|
|
},
|
|
[BQ27520G4] = {
|
|
[BQ27520G4] = {
|
|
[BQ27XXX_REG_CTRL] = 0x00,
|
|
[BQ27XXX_REG_CTRL] = 0x00,
|
|
@@ -340,6 +379,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
|
|
[BQ27XXX_REG_SOC] = 0x20,
|
|
[BQ27XXX_REG_SOC] = 0x20,
|
|
[BQ27XXX_REG_DCAP] = INVALID_REG_ADDR,
|
|
[BQ27XXX_REG_DCAP] = INVALID_REG_ADDR,
|
|
[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
|
|
[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
|
|
|
|
+ BQ27XXX_DM_REG_ROWS,
|
|
},
|
|
},
|
|
[BQ27530] = {
|
|
[BQ27530] = {
|
|
[BQ27XXX_REG_CTRL] = 0x00,
|
|
[BQ27XXX_REG_CTRL] = 0x00,
|
|
@@ -359,6 +399,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
|
|
[BQ27XXX_REG_SOC] = 0x2c,
|
|
[BQ27XXX_REG_SOC] = 0x2c,
|
|
[BQ27XXX_REG_DCAP] = INVALID_REG_ADDR,
|
|
[BQ27XXX_REG_DCAP] = INVALID_REG_ADDR,
|
|
[BQ27XXX_REG_AP] = 0x24,
|
|
[BQ27XXX_REG_AP] = 0x24,
|
|
|
|
+ BQ27XXX_DM_REG_ROWS,
|
|
},
|
|
},
|
|
[BQ27541] = {
|
|
[BQ27541] = {
|
|
[BQ27XXX_REG_CTRL] = 0x00,
|
|
[BQ27XXX_REG_CTRL] = 0x00,
|
|
@@ -378,6 +419,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
|
|
[BQ27XXX_REG_SOC] = 0x2c,
|
|
[BQ27XXX_REG_SOC] = 0x2c,
|
|
[BQ27XXX_REG_DCAP] = 0x3c,
|
|
[BQ27XXX_REG_DCAP] = 0x3c,
|
|
[BQ27XXX_REG_AP] = 0x24,
|
|
[BQ27XXX_REG_AP] = 0x24,
|
|
|
|
+ BQ27XXX_DM_REG_ROWS,
|
|
},
|
|
},
|
|
[BQ27545] = {
|
|
[BQ27545] = {
|
|
[BQ27XXX_REG_CTRL] = 0x00,
|
|
[BQ27XXX_REG_CTRL] = 0x00,
|
|
@@ -397,6 +439,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
|
|
[BQ27XXX_REG_SOC] = 0x2c,
|
|
[BQ27XXX_REG_SOC] = 0x2c,
|
|
[BQ27XXX_REG_DCAP] = INVALID_REG_ADDR,
|
|
[BQ27XXX_REG_DCAP] = INVALID_REG_ADDR,
|
|
[BQ27XXX_REG_AP] = 0x24,
|
|
[BQ27XXX_REG_AP] = 0x24,
|
|
|
|
+ BQ27XXX_DM_REG_ROWS,
|
|
},
|
|
},
|
|
[BQ27421] = {
|
|
[BQ27421] = {
|
|
[BQ27XXX_REG_CTRL] = 0x00,
|
|
[BQ27XXX_REG_CTRL] = 0x00,
|
|
@@ -416,6 +459,7 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
|
|
[BQ27XXX_REG_SOC] = 0x1c,
|
|
[BQ27XXX_REG_SOC] = 0x1c,
|
|
[BQ27XXX_REG_DCAP] = 0x3c,
|
|
[BQ27XXX_REG_DCAP] = 0x3c,
|
|
[BQ27XXX_REG_AP] = 0x18,
|
|
[BQ27XXX_REG_AP] = 0x18,
|
|
|
|
+ BQ27XXX_DM_REG_ROWS,
|
|
},
|
|
},
|
|
};
|
|
};
|
|
|
|
|
|
@@ -757,6 +801,73 @@ static struct {
|
|
static DEFINE_MUTEX(bq27xxx_list_lock);
|
|
static DEFINE_MUTEX(bq27xxx_list_lock);
|
|
static LIST_HEAD(bq27xxx_battery_devices);
|
|
static LIST_HEAD(bq27xxx_battery_devices);
|
|
|
|
|
|
|
|
+#define BQ27XXX_MSLEEP(i) usleep_range((i)*1000, (i)*1000+500)
|
|
|
|
+
|
|
|
|
+#define BQ27XXX_DM_SZ 32
|
|
|
|
+
|
|
|
|
+struct bq27xxx_dm_reg {
|
|
|
|
+ u8 subclass_id;
|
|
|
|
+ u8 offset;
|
|
|
|
+ u8 bytes;
|
|
|
|
+ u16 min, max;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * struct bq27xxx_dm_buf - chip data memory buffer
|
|
|
|
+ * @class: data memory subclass_id
|
|
|
|
+ * @block: data memory block number
|
|
|
|
+ * @data: data from/for the block
|
|
|
|
+ * @has_data: true if data has been filled by read
|
|
|
|
+ * @dirty: true if data has changed since last read/write
|
|
|
|
+ *
|
|
|
|
+ * Encapsulates info required to manage chip data memory blocks.
|
|
|
|
+ */
|
|
|
|
+struct bq27xxx_dm_buf {
|
|
|
|
+ u8 class;
|
|
|
|
+ u8 block;
|
|
|
|
+ u8 data[BQ27XXX_DM_SZ];
|
|
|
|
+ bool has_data, dirty;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+#define BQ27XXX_DM_BUF(di, i) { \
|
|
|
|
+ .class = (di)->dm_regs[i].subclass_id, \
|
|
|
|
+ .block = (di)->dm_regs[i].offset / BQ27XXX_DM_SZ, \
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static inline u16 *bq27xxx_dm_reg_ptr(struct bq27xxx_dm_buf *buf,
|
|
|
|
+ struct bq27xxx_dm_reg *reg)
|
|
|
|
+{
|
|
|
|
+ if (buf->class == reg->subclass_id &&
|
|
|
|
+ buf->block == reg->offset / BQ27XXX_DM_SZ)
|
|
|
|
+ return (u16 *) (buf->data + reg->offset % BQ27XXX_DM_SZ);
|
|
|
|
+
|
|
|
|
+ return NULL;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+enum bq27xxx_dm_reg_id {
|
|
|
|
+ BQ27XXX_DM_DESIGN_CAPACITY = 0,
|
|
|
|
+ BQ27XXX_DM_DESIGN_ENERGY,
|
|
|
|
+ BQ27XXX_DM_TERMINATE_VOLTAGE,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static const char * const bq27xxx_dm_reg_name[] = {
|
|
|
|
+ [BQ27XXX_DM_DESIGN_CAPACITY] = "design-capacity",
|
|
|
|
+ [BQ27XXX_DM_DESIGN_ENERGY] = "design-energy",
|
|
|
|
+ [BQ27XXX_DM_TERMINATE_VOLTAGE] = "terminate-voltage",
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+static bool bq27xxx_dt_to_nvm = true;
|
|
|
|
+module_param_named(dt_monitored_battery_updates_nvm, bq27xxx_dt_to_nvm, bool, 0444);
|
|
|
|
+MODULE_PARM_DESC(dt_monitored_battery_updates_nvm,
|
|
|
|
+ "Devicetree monitored-battery config updates data memory on NVM/flash chips.\n"
|
|
|
|
+ "Users must set this =0 when installing a different type of battery!\n"
|
|
|
|
+ "Default is =1."
|
|
|
|
+#ifndef CONFIG_BATTERY_BQ27XXX_DT_UPDATES_NVM
|
|
|
|
+ "\nSetting this affects future kernel updates, not the current configuration."
|
|
|
|
+#endif
|
|
|
|
+);
|
|
|
|
+
|
|
static int poll_interval_param_set(const char *val, const struct kernel_param *kp)
|
|
static int poll_interval_param_set(const char *val, const struct kernel_param *kp)
|
|
{
|
|
{
|
|
struct bq27xxx_device_info *di;
|
|
struct bq27xxx_device_info *di;
|
|
@@ -794,11 +905,419 @@ MODULE_PARM_DESC(poll_interval,
|
|
static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index,
|
|
static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index,
|
|
bool single)
|
|
bool single)
|
|
{
|
|
{
|
|
- /* Reports EINVAL for invalid/missing registers */
|
|
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ if (!di || di->regs[reg_index] == INVALID_REG_ADDR)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ ret = di->bus.read(di, di->regs[reg_index], single);
|
|
|
|
+ if (ret < 0)
|
|
|
|
+ dev_dbg(di->dev, "failed to read register 0x%02x (index %d)\n",
|
|
|
|
+ di->regs[reg_index], reg_index);
|
|
|
|
+
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static inline int bq27xxx_write(struct bq27xxx_device_info *di, int reg_index,
|
|
|
|
+ u16 value, bool single)
|
|
|
|
+{
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ if (!di || di->regs[reg_index] == INVALID_REG_ADDR)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ if (!di->bus.write)
|
|
|
|
+ return -EPERM;
|
|
|
|
+
|
|
|
|
+ ret = di->bus.write(di, di->regs[reg_index], value, single);
|
|
|
|
+ if (ret < 0)
|
|
|
|
+ dev_dbg(di->dev, "failed to write register 0x%02x (index %d)\n",
|
|
|
|
+ di->regs[reg_index], reg_index);
|
|
|
|
+
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static inline int bq27xxx_read_block(struct bq27xxx_device_info *di, int reg_index,
|
|
|
|
+ u8 *data, int len)
|
|
|
|
+{
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ if (!di || di->regs[reg_index] == INVALID_REG_ADDR)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ if (!di->bus.read_bulk)
|
|
|
|
+ return -EPERM;
|
|
|
|
+
|
|
|
|
+ ret = di->bus.read_bulk(di, di->regs[reg_index], data, len);
|
|
|
|
+ if (ret < 0)
|
|
|
|
+ dev_dbg(di->dev, "failed to read_bulk register 0x%02x (index %d)\n",
|
|
|
|
+ di->regs[reg_index], reg_index);
|
|
|
|
+
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static inline int bq27xxx_write_block(struct bq27xxx_device_info *di, int reg_index,
|
|
|
|
+ u8 *data, int len)
|
|
|
|
+{
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
if (!di || di->regs[reg_index] == INVALID_REG_ADDR)
|
|
if (!di || di->regs[reg_index] == INVALID_REG_ADDR)
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
|
|
|
|
- return di->bus.read(di, di->regs[reg_index], single);
|
|
|
|
|
|
+ if (!di->bus.write_bulk)
|
|
|
|
+ return -EPERM;
|
|
|
|
+
|
|
|
|
+ ret = di->bus.write_bulk(di, di->regs[reg_index], data, len);
|
|
|
|
+ if (ret < 0)
|
|
|
|
+ dev_dbg(di->dev, "failed to write_bulk register 0x%02x (index %d)\n",
|
|
|
|
+ di->regs[reg_index], reg_index);
|
|
|
|
+
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int bq27xxx_battery_seal(struct bq27xxx_device_info *di)
|
|
|
|
+{
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ ret = bq27xxx_write(di, BQ27XXX_REG_CTRL, BQ27XXX_SEALED, false);
|
|
|
|
+ if (ret < 0) {
|
|
|
|
+ dev_err(di->dev, "bus error on seal: %d\n", ret);
|
|
|
|
+ return ret;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int bq27xxx_battery_unseal(struct bq27xxx_device_info *di)
|
|
|
|
+{
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ if (di->unseal_key == 0) {
|
|
|
|
+ dev_err(di->dev, "unseal failed due to missing key\n");
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ret = bq27xxx_write(di, BQ27XXX_REG_CTRL, (u16)(di->unseal_key >> 16), false);
|
|
|
|
+ if (ret < 0)
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ ret = bq27xxx_write(di, BQ27XXX_REG_CTRL, (u16)di->unseal_key, false);
|
|
|
|
+ if (ret < 0)
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+out:
|
|
|
|
+ dev_err(di->dev, "bus error on unseal: %d\n", ret);
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static u8 bq27xxx_battery_checksum_dm_block(struct bq27xxx_dm_buf *buf)
|
|
|
|
+{
|
|
|
|
+ u16 sum = 0;
|
|
|
|
+ int i;
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < BQ27XXX_DM_SZ; i++)
|
|
|
|
+ sum += buf->data[i];
|
|
|
|
+ sum &= 0xff;
|
|
|
|
+
|
|
|
|
+ return 0xff - sum;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int bq27xxx_battery_read_dm_block(struct bq27xxx_device_info *di,
|
|
|
|
+ struct bq27xxx_dm_buf *buf)
|
|
|
|
+{
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ buf->has_data = false;
|
|
|
|
+
|
|
|
|
+ ret = bq27xxx_write(di, BQ27XXX_DM_CLASS, buf->class, true);
|
|
|
|
+ if (ret < 0)
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ ret = bq27xxx_write(di, BQ27XXX_DM_BLOCK, buf->block, true);
|
|
|
|
+ if (ret < 0)
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ BQ27XXX_MSLEEP(1);
|
|
|
|
+
|
|
|
|
+ ret = bq27xxx_read_block(di, BQ27XXX_DM_DATA, buf->data, BQ27XXX_DM_SZ);
|
|
|
|
+ if (ret < 0)
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ ret = bq27xxx_read(di, BQ27XXX_DM_CKSUM, true);
|
|
|
|
+ if (ret < 0)
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ if ((u8)ret != bq27xxx_battery_checksum_dm_block(buf)) {
|
|
|
|
+ ret = -EINVAL;
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ buf->has_data = true;
|
|
|
|
+ buf->dirty = false;
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+out:
|
|
|
|
+ dev_err(di->dev, "bus error reading chip memory: %d\n", ret);
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void bq27xxx_battery_update_dm_block(struct bq27xxx_device_info *di,
|
|
|
|
+ struct bq27xxx_dm_buf *buf,
|
|
|
|
+ enum bq27xxx_dm_reg_id reg_id,
|
|
|
|
+ unsigned int val)
|
|
|
|
+{
|
|
|
|
+ struct bq27xxx_dm_reg *reg = &di->dm_regs[reg_id];
|
|
|
|
+ const char *str = bq27xxx_dm_reg_name[reg_id];
|
|
|
|
+ u16 *prev = bq27xxx_dm_reg_ptr(buf, reg);
|
|
|
|
+
|
|
|
|
+ if (prev == NULL) {
|
|
|
|
+ dev_warn(di->dev, "buffer does not match %s dm spec\n", str);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (reg->bytes != 2) {
|
|
|
|
+ dev_warn(di->dev, "%s dm spec has unsupported byte size\n", str);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!buf->has_data)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ if (be16_to_cpup(prev) == val) {
|
|
|
|
+ dev_info(di->dev, "%s has %u\n", str, val);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+#ifdef CONFIG_BATTERY_BQ27XXX_DT_UPDATES_NVM
|
|
|
|
+ if (!di->ram_chip && !bq27xxx_dt_to_nvm) {
|
|
|
|
+#else
|
|
|
|
+ if (!di->ram_chip) {
|
|
|
|
+#endif
|
|
|
|
+ /* devicetree and NVM differ; defer to NVM */
|
|
|
|
+ dev_warn(di->dev, "%s has %u; update to %u disallowed "
|
|
|
|
+#ifdef CONFIG_BATTERY_BQ27XXX_DT_UPDATES_NVM
|
|
|
|
+ "by dt_monitored_battery_updates_nvm=0"
|
|
|
|
+#else
|
|
|
|
+ "for flash/NVM data memory"
|
|
|
|
+#endif
|
|
|
|
+ "\n", str, be16_to_cpup(prev), val);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ dev_info(di->dev, "update %s to %u\n", str, val);
|
|
|
|
+
|
|
|
|
+ *prev = cpu_to_be16(val);
|
|
|
|
+ buf->dirty = true;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int bq27xxx_battery_cfgupdate_priv(struct bq27xxx_device_info *di, bool active)
|
|
|
|
+{
|
|
|
|
+ const int limit = 100;
|
|
|
|
+ u16 cmd = active ? BQ27XXX_SET_CFGUPDATE : BQ27XXX_SOFT_RESET;
|
|
|
|
+ int ret, try = limit;
|
|
|
|
+
|
|
|
|
+ ret = bq27xxx_write(di, BQ27XXX_REG_CTRL, cmd, false);
|
|
|
|
+ if (ret < 0)
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
|
|
+ do {
|
|
|
|
+ BQ27XXX_MSLEEP(25);
|
|
|
|
+ ret = bq27xxx_read(di, BQ27XXX_REG_FLAGS, false);
|
|
|
|
+ if (ret < 0)
|
|
|
|
+ return ret;
|
|
|
|
+ } while (!!(ret & BQ27XXX_FLAG_CFGUP) != active && --try);
|
|
|
|
+
|
|
|
|
+ if (!try) {
|
|
|
|
+ dev_err(di->dev, "timed out waiting for cfgupdate flag %d\n", active);
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (limit - try > 3)
|
|
|
|
+ dev_warn(di->dev, "cfgupdate %d, retries %d\n", active, limit - try);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static inline int bq27xxx_battery_set_cfgupdate(struct bq27xxx_device_info *di)
|
|
|
|
+{
|
|
|
|
+ int ret = bq27xxx_battery_cfgupdate_priv(di, true);
|
|
|
|
+ if (ret < 0 && ret != -EINVAL)
|
|
|
|
+ dev_err(di->dev, "bus error on set_cfgupdate: %d\n", ret);
|
|
|
|
+
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static inline int bq27xxx_battery_soft_reset(struct bq27xxx_device_info *di)
|
|
|
|
+{
|
|
|
|
+ int ret = bq27xxx_battery_cfgupdate_priv(di, false);
|
|
|
|
+ if (ret < 0 && ret != -EINVAL)
|
|
|
|
+ dev_err(di->dev, "bus error on soft_reset: %d\n", ret);
|
|
|
|
+
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int bq27xxx_battery_write_dm_block(struct bq27xxx_device_info *di,
|
|
|
|
+ struct bq27xxx_dm_buf *buf)
|
|
|
|
+{
|
|
|
|
+ bool cfgup = di->chip == BQ27421; /* assume related chips need cfgupdate */
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ if (!buf->dirty)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ if (cfgup) {
|
|
|
|
+ ret = bq27xxx_battery_set_cfgupdate(di);
|
|
|
|
+ if (ret < 0)
|
|
|
|
+ return ret;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ret = bq27xxx_write(di, BQ27XXX_DM_CTRL, 0, true);
|
|
|
|
+ if (ret < 0)
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ ret = bq27xxx_write(di, BQ27XXX_DM_CLASS, buf->class, true);
|
|
|
|
+ if (ret < 0)
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ ret = bq27xxx_write(di, BQ27XXX_DM_BLOCK, buf->block, true);
|
|
|
|
+ if (ret < 0)
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ BQ27XXX_MSLEEP(1);
|
|
|
|
+
|
|
|
|
+ ret = bq27xxx_write_block(di, BQ27XXX_DM_DATA, buf->data, BQ27XXX_DM_SZ);
|
|
|
|
+ if (ret < 0)
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ ret = bq27xxx_write(di, BQ27XXX_DM_CKSUM,
|
|
|
|
+ bq27xxx_battery_checksum_dm_block(buf), true);
|
|
|
|
+ if (ret < 0)
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ /* DO NOT read BQ27XXX_DM_CKSUM here to verify it! That may cause NVM
|
|
|
|
+ * corruption on the '425 chip (and perhaps others), which can damage
|
|
|
|
+ * the chip.
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+ if (cfgup) {
|
|
|
|
+ BQ27XXX_MSLEEP(1);
|
|
|
|
+ ret = bq27xxx_battery_soft_reset(di);
|
|
|
|
+ if (ret < 0)
|
|
|
|
+ return ret;
|
|
|
|
+ } else {
|
|
|
|
+ BQ27XXX_MSLEEP(100); /* flash DM updates in <100ms */
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ buf->dirty = false;
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+out:
|
|
|
|
+ if (cfgup)
|
|
|
|
+ bq27xxx_battery_soft_reset(di);
|
|
|
|
+
|
|
|
|
+ dev_err(di->dev, "bus error writing chip memory: %d\n", ret);
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void bq27xxx_battery_set_config(struct bq27xxx_device_info *di,
|
|
|
|
+ struct power_supply_battery_info *info)
|
|
|
|
+{
|
|
|
|
+ struct bq27xxx_dm_buf bd = BQ27XXX_DM_BUF(di, BQ27XXX_DM_DESIGN_CAPACITY);
|
|
|
|
+ struct bq27xxx_dm_buf bt = BQ27XXX_DM_BUF(di, BQ27XXX_DM_TERMINATE_VOLTAGE);
|
|
|
|
+ bool updated;
|
|
|
|
+
|
|
|
|
+ if (bq27xxx_battery_unseal(di) < 0)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ if (info->charge_full_design_uah != -EINVAL &&
|
|
|
|
+ info->energy_full_design_uwh != -EINVAL) {
|
|
|
|
+ bq27xxx_battery_read_dm_block(di, &bd);
|
|
|
|
+ /* assume design energy & capacity are in same block */
|
|
|
|
+ bq27xxx_battery_update_dm_block(di, &bd,
|
|
|
|
+ BQ27XXX_DM_DESIGN_CAPACITY,
|
|
|
|
+ info->charge_full_design_uah / 1000);
|
|
|
|
+ bq27xxx_battery_update_dm_block(di, &bd,
|
|
|
|
+ BQ27XXX_DM_DESIGN_ENERGY,
|
|
|
|
+ info->energy_full_design_uwh / 1000);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (info->voltage_min_design_uv != -EINVAL) {
|
|
|
|
+ bool same = bd.class == bt.class && bd.block == bt.block;
|
|
|
|
+ if (!same)
|
|
|
|
+ bq27xxx_battery_read_dm_block(di, &bt);
|
|
|
|
+ bq27xxx_battery_update_dm_block(di, same ? &bd : &bt,
|
|
|
|
+ BQ27XXX_DM_TERMINATE_VOLTAGE,
|
|
|
|
+ info->voltage_min_design_uv / 1000);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ updated = bd.dirty || bt.dirty;
|
|
|
|
+
|
|
|
|
+ bq27xxx_battery_write_dm_block(di, &bd);
|
|
|
|
+ bq27xxx_battery_write_dm_block(di, &bt);
|
|
|
|
+
|
|
|
|
+ bq27xxx_battery_seal(di);
|
|
|
|
+
|
|
|
|
+ if (updated && di->chip != BQ27421) { /* not a cfgupdate chip, so reset */
|
|
|
|
+ bq27xxx_write(di, BQ27XXX_REG_CTRL, BQ27XXX_RESET, false);
|
|
|
|
+ BQ27XXX_MSLEEP(300); /* reset time is not documented */
|
|
|
|
+ }
|
|
|
|
+ /* assume bq27xxx_battery_update() is called hereafter */
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void bq27xxx_battery_settings(struct bq27xxx_device_info *di)
|
|
|
|
+{
|
|
|
|
+ struct power_supply_battery_info info = {};
|
|
|
|
+ unsigned int min, max;
|
|
|
|
+
|
|
|
|
+ if (power_supply_get_battery_info(di->bat, &info) < 0)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ if (!di->dm_regs) {
|
|
|
|
+ dev_warn(di->dev, "data memory update not supported for chip\n");
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (info.energy_full_design_uwh != info.charge_full_design_uah) {
|
|
|
|
+ if (info.energy_full_design_uwh == -EINVAL)
|
|
|
|
+ dev_warn(di->dev, "missing battery:energy-full-design-microwatt-hours\n");
|
|
|
|
+ else if (info.charge_full_design_uah == -EINVAL)
|
|
|
|
+ dev_warn(di->dev, "missing battery:charge-full-design-microamp-hours\n");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* assume min == 0 */
|
|
|
|
+ max = di->dm_regs[BQ27XXX_DM_DESIGN_ENERGY].max;
|
|
|
|
+ if (info.energy_full_design_uwh > max * 1000) {
|
|
|
|
+ dev_err(di->dev, "invalid battery:energy-full-design-microwatt-hours %d\n",
|
|
|
|
+ info.energy_full_design_uwh);
|
|
|
|
+ info.energy_full_design_uwh = -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* assume min == 0 */
|
|
|
|
+ max = di->dm_regs[BQ27XXX_DM_DESIGN_CAPACITY].max;
|
|
|
|
+ if (info.charge_full_design_uah > max * 1000) {
|
|
|
|
+ dev_err(di->dev, "invalid battery:charge-full-design-microamp-hours %d\n",
|
|
|
|
+ info.charge_full_design_uah);
|
|
|
|
+ info.charge_full_design_uah = -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ min = di->dm_regs[BQ27XXX_DM_TERMINATE_VOLTAGE].min;
|
|
|
|
+ max = di->dm_regs[BQ27XXX_DM_TERMINATE_VOLTAGE].max;
|
|
|
|
+ if ((info.voltage_min_design_uv < min * 1000 ||
|
|
|
|
+ info.voltage_min_design_uv > max * 1000) &&
|
|
|
|
+ info.voltage_min_design_uv != -EINVAL) {
|
|
|
|
+ dev_err(di->dev, "invalid battery:voltage-min-design-microvolt %d\n",
|
|
|
|
+ info.voltage_min_design_uv);
|
|
|
|
+ info.voltage_min_design_uv = -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if ((info.energy_full_design_uwh != -EINVAL &&
|
|
|
|
+ info.charge_full_design_uah != -EINVAL) ||
|
|
|
|
+ info.voltage_min_design_uv != -EINVAL)
|
|
|
|
+ bq27xxx_battery_set_config(di, &info);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -1318,6 +1837,13 @@ static int bq27xxx_battery_get_property(struct power_supply *psy,
|
|
case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
|
|
case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
|
|
ret = bq27xxx_simple_value(di->charge_design_full, val);
|
|
ret = bq27xxx_simple_value(di->charge_design_full, val);
|
|
break;
|
|
break;
|
|
|
|
+ /*
|
|
|
|
+ * TODO: Implement these to make registers set from
|
|
|
|
+ * power_supply_battery_info visible in sysfs.
|
|
|
|
+ */
|
|
|
|
+ case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
|
|
|
|
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
|
|
|
|
+ return -EINVAL;
|
|
case POWER_SUPPLY_PROP_CYCLE_COUNT:
|
|
case POWER_SUPPLY_PROP_CYCLE_COUNT:
|
|
ret = bq27xxx_simple_value(di->cache.cycle_count, val);
|
|
ret = bq27xxx_simple_value(di->cache.cycle_count, val);
|
|
break;
|
|
break;
|
|
@@ -1351,7 +1877,10 @@ static void bq27xxx_external_power_changed(struct power_supply *psy)
|
|
int bq27xxx_battery_setup(struct bq27xxx_device_info *di)
|
|
int bq27xxx_battery_setup(struct bq27xxx_device_info *di)
|
|
{
|
|
{
|
|
struct power_supply_desc *psy_desc;
|
|
struct power_supply_desc *psy_desc;
|
|
- struct power_supply_config psy_cfg = { .drv_data = di, };
|
|
|
|
|
|
+ struct power_supply_config psy_cfg = {
|
|
|
|
+ .of_node = di->dev->of_node,
|
|
|
|
+ .drv_data = di,
|
|
|
|
+ };
|
|
|
|
|
|
INIT_DELAYED_WORK(&di->work, bq27xxx_battery_poll);
|
|
INIT_DELAYED_WORK(&di->work, bq27xxx_battery_poll);
|
|
mutex_init(&di->lock);
|
|
mutex_init(&di->lock);
|
|
@@ -1376,6 +1905,7 @@ int bq27xxx_battery_setup(struct bq27xxx_device_info *di)
|
|
|
|
|
|
dev_info(di->dev, "support ver. %s enabled\n", DRIVER_VERSION);
|
|
dev_info(di->dev, "support ver. %s enabled\n", DRIVER_VERSION);
|
|
|
|
|
|
|
|
+ bq27xxx_battery_settings(di);
|
|
bq27xxx_battery_update(di);
|
|
bq27xxx_battery_update(di);
|
|
|
|
|
|
mutex_lock(&bq27xxx_list_lock);
|
|
mutex_lock(&bq27xxx_list_lock);
|