* [PATCH v9 0/8] devicetree battery support and client bq27xxx_battery
@ 2017-03-06 7:25 Liam Breck
2017-03-06 7:25 ` [PATCH v9 2/8] devicetree: property-units: Add uWh and uAh units Liam Breck
` (6 more replies)
0 siblings, 7 replies; 9+ messages in thread
From: Liam Breck @ 2017-03-06 7:25 UTC (permalink / raw)
To: Sebastian Reichel; +Cc: Andrew F. Davis, linux-pm, Matt Ranostay
Overview:
* new devicetree battery node specifies static battery data
* fuel gauge and charger nodes shall use monitored-battery=<&battery_node>
* new power_supply_get_battery_info() reads battery data from devicetree
* new struct power_supply_battery_info provides battery data to drivers
* drivers surface battery data in sysfs via related power_supply_prop_* fields
* bq27xxx driver calls the above and writes battery data to RAM/NVM for params
essential to correct operation: energy-full-design-microwatt-hours,
charge-full-design-microamp-hours, voltage-min-design-microvolt
Changes in v9:
bq27xxx_battery:
* fix set_cfgupdate()
* support bq27500, 545, 421; defer others
* bq27441, 621 use enum BQ27425 instead of BQ27421
* drop print_dm_blocks() patch
* minor polishing
Documentation/devicetree/.../battery.txt:
* describe rationale for enum power_supply_property names
Changes in v8:
bq27xxx_battery:
* wait on flag after set_cfgupdate & soft_reset
* drop print_config(), report status in update_dm_block()
* clarify error messages
* cleanup from Andrew's feedback; minor polishing
Changes in v7:
bq27xxx_battery:
* support chips where terminate_voltage & design_* live in separate blocks
* draft support for 421, 441, 621 chips
* new patch to log chip memory fields
* report bus I/O errors; return error code in bq27xxx_battery_i2c
* verify checksum in read_dm_block()
* use set_cfgupdate only if chip provides it, soft_reset on I/O error
* block_data_control=0 only in write_dm_block()
* note toxic code from TI bqtool in write_dm_block()
* lots of functionally neutral polishing
Documentation/devicetree/.../battery.txt:
* mention power_supply_get_battery_info()
Changes in v6:
* Documentation/devictree/... fixes
* bq27xxx_battery: clarify names
* bq27xxx_battery: verify that selected registers are supported
* bq27xxx_battery: allocate NVM buffer on stack
* bq27xxx_battery_i2c: fix return code of bulk_read
Changes in v5:
* incorporate feedback into Documentation/devicetree/.../battery.txt
* use power_supply_prop_* names in devicetree and power_supply_battery_info
* default fields to -EINVAL in power_supply_battery_info
* power_supply_get_battery_info() always looks for "monitored-battery"
* power_supply_get_battery_info() emits a warning if !psy->of_node
* squash patches for power_supply_battery_info
* bq27xxx_battery: check power_supply_battery_info values
* bq27xxx_battery: note missing power_supply_prop_* features
* bq27xxx_battery: new patch for access methods
Changes in v4:
* add "fixed-battery" compatible field to be be more consistant with devicetree
Changes in v3:
* split i2c changes into respective patches
* add documentation for battery information for fuel gauge
* rebased documentation patches on change on the list
* abstracted the battery configuration for the state machine
to an generic struct and platform data access function
Changes in v2:
* add documentation for uWh and uAh property units
* change devicetree entries to match new property units
Liam (4):
devicetree: power: Add battery.txt
devicetree: power: bq27xxx: Add monitored-battery documentation
power: power_supply: Add power_supply_battery_info and API
power: bq27xxx_battery: Add power_supply_battery_info support
Matt Ranostay (4):
devicetree: property-units: Add uWh and uAh units
power: bq27xxx_battery: Define access methods to write chip registers
power: bq27xxx_battery: Move BQ27421 chip id to its own group
power: bq27xxx_battery_i2c: Add I2C bulk read/write functions
.../devicetree/bindings/power/supply/battery.txt | 45 ++
.../devicetree/bindings/power/supply/bq27xxx.txt | 11 +-
.../devicetree/bindings/property-units.txt | 2 +
drivers/power/supply/bq27xxx_battery.c | 506 ++++++++++++++++++++-
drivers/power/supply/bq27xxx_battery_i2c.c | 88 +++-
drivers/power/supply/power_supply_core.c | 40 ++
include/linux/power/bq27xxx_battery.h | 6 +-
include/linux/power_supply.h | 18 +
8 files changed, 707 insertions(+), 9 deletions(-)
create mode 100644 Documentation/devicetree/bindings/power/supply/battery.txt
--
2.9.3
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v9 1/8] devicetree: power: Add battery.txt
[not found] ` <20170306072539.980-1-liam-RYWXG+zxWwBdeoIcmNTgJF6hYfS7NtTn@public.gmane.org>
@ 2017-03-06 7:25 ` Liam Breck
2017-03-06 7:25 ` [PATCH v9 3/8] devicetree: power: bq27xxx: Add monitored-battery documentation Liam Breck
1 sibling, 0 replies; 9+ messages in thread
From: Liam Breck @ 2017-03-06 7:25 UTC (permalink / raw)
To: Sebastian Reichel
Cc: Andrew F. Davis, linux-pm-u79uwXL29TY76Z2rM5mHXA, Matt Ranostay,
Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA, Liam Breck
From: Liam Breck <kernel-RYWXG+zxWwBdeoIcmNTgJF6hYfS7NtTn@public.gmane.org>
Documentation of static battery characteristics that can be defined
for batteries which cannot self-identify. This information is required
by fuel-gauge and charger chips for proper handling of the battery.
Cc: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Signed-off-by: Matt Ranostay <matt-sk+viVC6FLCDq+mSdOJa79kegs52MxvZ@public.gmane.org>
Signed-off-by: Liam Breck <kernel-RYWXG+zxWwBdeoIcmNTgJF6hYfS7NtTn@public.gmane.org>
---
.../devicetree/bindings/power/supply/battery.txt | 45 ++++++++++++++++++++++
1 file changed, 45 insertions(+)
create mode 100644 Documentation/devicetree/bindings/power/supply/battery.txt
diff --git a/Documentation/devicetree/bindings/power/supply/battery.txt b/Documentation/devicetree/bindings/power/supply/battery.txt
new file mode 100644
index 0000000..0278617
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/supply/battery.txt
@@ -0,0 +1,45 @@
+Battery Characteristics
+
+Required Properties:
+ - compatible: Must be "fixed-battery"
+
+Optional Properties:
+ - voltage-min-design-microvolt: drained battery voltage
+ - energy-full-design-microwatt-hours: battery design energy
+ - charge-full-design-microamp-hours: battery design capacity
+
+Because drivers surface properties in sysfs using names derived
+from enum power_supply_property, e.g.
+/sys/class/power_supply/<device>/charge_full_design, our
+battery properties must be named for the corresponding elements in
+enum power_supply_property, defined in include/linux/power_supply.h.
+
+Batteries must be referenced by chargers and/or fuel-gauges
+using a phandle. The phandle's property should be named
+"monitored-battery".
+
+Driver code should call power_supply_get_battery_info() to obtain
+battery properties via monitored-battery. For details see:
+ drivers/power/supply/power_supply_core.c
+ drivers/power/supply/bq27xxx_battery.c
+
+Example:
+
+ bat: battery {
+ compatible = "fixed-battery";
+ voltage-min-design-microvolt = <3200000>;
+ energy-full-design-microwatt-hours = <5290000>;
+ charge-full-design-microamp-hours = <1430000>;
+ };
+
+ charger: charger@11 {
+ ....
+ monitored-battery = <&bat>;
+ ...
+ };
+
+ fuel_gauge: fuel-gauge@22 {
+ ....
+ monitored-battery = <&bat>;
+ ...
+ };
--
2.9.3
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v9 2/8] devicetree: property-units: Add uWh and uAh units
2017-03-06 7:25 [PATCH v9 0/8] devicetree battery support and client bq27xxx_battery Liam Breck
@ 2017-03-06 7:25 ` Liam Breck
[not found] ` <20170306072539.980-1-liam-RYWXG+zxWwBdeoIcmNTgJF6hYfS7NtTn@public.gmane.org>
` (5 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Liam Breck @ 2017-03-06 7:25 UTC (permalink / raw)
To: Sebastian Reichel
Cc: Andrew F. Davis, linux-pm, Matt Ranostay, Rob Herring,
Mark Rutland, devicetree, linux-kernel, Liam Breck
From: Matt Ranostay <matt@ranostay.consulting>
Add entries for microwatt-hours and microamp-hours.
Cc: Rob Herring <robh@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Signed-off-by: Matt Ranostay <matt@ranostay.consulting>
Signed-off-by: Liam Breck <kernel@networkimprov.net>
Acked-by: Sebastian Reichel <sre@kernel.org>
Acked-by: Rob Herring <robh@kernel.org>
---
Documentation/devicetree/bindings/property-units.txt | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Documentation/devicetree/bindings/property-units.txt b/Documentation/devicetree/bindings/property-units.txt
index 12278d7..0849618 100644
--- a/Documentation/devicetree/bindings/property-units.txt
+++ b/Documentation/devicetree/bindings/property-units.txt
@@ -25,8 +25,10 @@ Distance
Electricity
----------------------------------------
-microamp : micro amps
+-microamp-hours : micro amp-hours
-ohms : Ohms
-micro-ohms : micro Ohms
+-microwatt-hours: micro Watt-hours
-microvolt : micro volts
Temperature
--
2.9.3
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v9 3/8] devicetree: power: bq27xxx: Add monitored-battery documentation
[not found] ` <20170306072539.980-1-liam-RYWXG+zxWwBdeoIcmNTgJF6hYfS7NtTn@public.gmane.org>
2017-03-06 7:25 ` [PATCH v9 1/8] devicetree: power: Add battery.txt Liam Breck
@ 2017-03-06 7:25 ` Liam Breck
1 sibling, 0 replies; 9+ messages in thread
From: Liam Breck @ 2017-03-06 7:25 UTC (permalink / raw)
To: Sebastian Reichel
Cc: Andrew F. Davis, linux-pm-u79uwXL29TY76Z2rM5mHXA, Matt Ranostay,
Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA, Liam Breck
From: Liam Breck <kernel-RYWXG+zxWwBdeoIcmNTgJF6hYfS7NtTn@public.gmane.org>
Document monitored-battery = <&battery_node>
Cc: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Signed-off-by: Matt Ranostay <matt-sk+viVC6FLCDq+mSdOJa79kegs52MxvZ@public.gmane.org>
Signed-off-by: Liam Breck <kernel-RYWXG+zxWwBdeoIcmNTgJF6hYfS7NtTn@public.gmane.org>
Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
---
Documentation/devicetree/bindings/power/supply/bq27xxx.txt | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/power/supply/bq27xxx.txt b/Documentation/devicetree/bindings/power/supply/bq27xxx.txt
index b0c95ef..cf83371 100644
--- a/Documentation/devicetree/bindings/power/supply/bq27xxx.txt
+++ b/Documentation/devicetree/bindings/power/supply/bq27xxx.txt
@@ -28,9 +28,18 @@ Required properties:
* "ti,bq27621" - BQ27621
- reg: integer, i2c address of the device.
+Optional properties:
+- monitored-battery: phandle of battery information devicetree node
+
+ See Documentation/devicetree/bindings/power/supply/battery.txt
+ If either of the referenced battery's *-full-design-*-hours properties are set,
+ then both must be.
+
Example:
-bq27510g3 {
+bq27510g3 : fuel-gauge@55 {
compatible = "ti,bq27510g3";
reg = <0x55>;
+
+ monitored-battery = <&bat>;
};
--
2.9.3
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v9 4/8] power: power_supply: Add power_supply_battery_info and API
2017-03-06 7:25 [PATCH v9 0/8] devicetree battery support and client bq27xxx_battery Liam Breck
2017-03-06 7:25 ` [PATCH v9 2/8] devicetree: property-units: Add uWh and uAh units Liam Breck
[not found] ` <20170306072539.980-1-liam-RYWXG+zxWwBdeoIcmNTgJF6hYfS7NtTn@public.gmane.org>
@ 2017-03-06 7:25 ` Liam Breck
2017-03-06 7:25 ` [PATCH v9 5/8] power: bq27xxx_battery: Define access methods to write chip registers Liam Breck
` (3 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Liam Breck @ 2017-03-06 7:25 UTC (permalink / raw)
To: Sebastian Reichel; +Cc: Andrew F. Davis, linux-pm, Matt Ranostay, Liam Breck
From: Liam Breck <kernel@networkimprov.net>
power_supply_get_battery_info() reads battery data from devicetree.
struct power_supply_battery_info provides battery data to drivers.
Drivers may surface battery data in sysfs via corresponding
POWER_SUPPLY_PROP_* fields.
Signed-off-by: Matt Ranostay <matt@ranostay.consulting>
Signed-off-by: Liam Breck <kernel@networkimprov.net>
---
drivers/power/supply/power_supply_core.c | 40 ++++++++++++++++++++++++++++++++
include/linux/power_supply.h | 18 ++++++++++++++
2 files changed, 58 insertions(+)
diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c
index a74d8ca..c121931 100644
--- a/drivers/power/supply/power_supply_core.c
+++ b/drivers/power/supply/power_supply_core.c
@@ -17,6 +17,7 @@
#include <linux/device.h>
#include <linux/notifier.h>
#include <linux/err.h>
+#include <linux/of.h>
#include <linux/power_supply.h>
#include <linux/thermal.h>
#include "power_supply.h"
@@ -487,6 +488,45 @@ struct power_supply *devm_power_supply_get_by_phandle(struct device *dev,
EXPORT_SYMBOL_GPL(devm_power_supply_get_by_phandle);
#endif /* CONFIG_OF */
+int power_supply_get_battery_info(struct power_supply *psy,
+ struct power_supply_battery_info *info)
+{
+ struct device_node *battery_np;
+ const char *value;
+ int err;
+
+ info->energy_full_design_uwh = -EINVAL;
+ info->charge_full_design_uah = -EINVAL;
+ info->voltage_min_design_uv = -EINVAL;
+
+ if (!psy->of_node) {
+ dev_warn(&psy->dev, "%s currently only supports devicetree\n",
+ __func__);
+ return -ENXIO;
+ }
+
+ battery_np = of_parse_phandle(psy->of_node, "monitored-battery", 0);
+ if (!battery_np)
+ return -ENODEV;
+
+ err = of_property_read_string(battery_np, "compatible", &value);
+ if (err)
+ return err;
+
+ if (strcmp("fixed-battery", value))
+ return -ENODEV;
+
+ of_property_read_u32(battery_np, "energy-full-design-microwatt-hours",
+ &info->energy_full_design_uwh);
+ of_property_read_u32(battery_np, "charge-full-design-microamp-hours",
+ &info->charge_full_design_uah);
+ of_property_read_u32(battery_np, "voltage-min-design-microvolt",
+ &info->voltage_min_design_uv);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(power_supply_get_battery_info);
+
int power_supply_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index 3965503..e84f1d3 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -288,6 +288,21 @@ struct power_supply_info {
int use_for_apm;
};
+/*
+ * This is the recommended struct to manage static battery parameters,
+ * populated by power_supply_get_battery_info(). Most platform drivers should
+ * use these for consistency.
+ * Its field names must correspond to elements in enum power_supply_property.
+ * The default field value is -EINVAL.
+ * Power supply class itself doesn't use this.
+ */
+
+struct power_supply_battery_info {
+ int energy_full_design_uwh; /* microWatt-hours */
+ int charge_full_design_uah; /* microAmp-hours */
+ int voltage_min_design_uv; /* microVolts */
+};
+
extern struct atomic_notifier_head power_supply_notifier;
extern int power_supply_reg_notifier(struct notifier_block *nb);
extern void power_supply_unreg_notifier(struct notifier_block *nb);
@@ -306,6 +321,9 @@ static inline struct power_supply *
devm_power_supply_get_by_phandle(struct device *dev, const char *property)
{ return NULL; }
#endif /* CONFIG_OF */
+
+extern int power_supply_get_battery_info(struct power_supply *psy,
+ struct power_supply_battery_info *info);
extern void power_supply_changed(struct power_supply *psy);
extern int power_supply_am_i_supplied(struct power_supply *psy);
extern int power_supply_set_battery_charged(struct power_supply *psy);
--
2.9.3
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v9 5/8] power: bq27xxx_battery: Define access methods to write chip registers
2017-03-06 7:25 [PATCH v9 0/8] devicetree battery support and client bq27xxx_battery Liam Breck
` (2 preceding siblings ...)
2017-03-06 7:25 ` [PATCH v9 4/8] power: power_supply: Add power_supply_battery_info and API Liam Breck
@ 2017-03-06 7:25 ` Liam Breck
2017-03-06 7:25 ` [PATCH v9 6/8] power: bq27xxx_battery: Move BQ27421 chip id to its own group Liam Breck
` (2 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Liam Breck @ 2017-03-06 7:25 UTC (permalink / raw)
To: Sebastian Reichel; +Cc: Andrew F. Davis, linux-pm, Matt Ranostay, Liam Breck
From: Matt Ranostay <matt@ranostay.consulting>
write(), read_bulk(), write_bulk() support setting chip registers,
e.g. with data obtained from power_supply_battery_info.
Signed-off-by: Matt Ranostay <matt@ranostay.consulting>
Signed-off-by: Liam Breck <kernel@networkimprov.net>
---
include/linux/power/bq27xxx_battery.h | 3 +++
1 file changed, 3 insertions(+)
diff --git a/include/linux/power/bq27xxx_battery.h b/include/linux/power/bq27xxx_battery.h
index bed9557..92df553 100644
--- a/include/linux/power/bq27xxx_battery.h
+++ b/include/linux/power/bq27xxx_battery.h
@@ -32,6 +32,9 @@ struct bq27xxx_platform_data {
struct bq27xxx_device_info;
struct bq27xxx_access_methods {
int (*read)(struct bq27xxx_device_info *di, u8 reg, bool single);
+ int (*write)(struct bq27xxx_device_info *di, u8 reg, int value, bool single);
+ int (*read_bulk)(struct bq27xxx_device_info *di, u8 reg, u8 *data, int len);
+ int (*write_bulk)(struct bq27xxx_device_info *di, u8 reg, u8 *data, int len);
};
struct bq27xxx_reg_cache {
--
2.9.3
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v9 6/8] power: bq27xxx_battery: Move BQ27421 chip id to its own group
2017-03-06 7:25 [PATCH v9 0/8] devicetree battery support and client bq27xxx_battery Liam Breck
` (3 preceding siblings ...)
2017-03-06 7:25 ` [PATCH v9 5/8] power: bq27xxx_battery: Define access methods to write chip registers Liam Breck
@ 2017-03-06 7:25 ` Liam Breck
2017-03-06 7:25 ` [PATCH v9 7/8] power: bq27xxx_battery: Add power_supply_battery_info support Liam Breck
2017-03-06 7:25 ` [PATCH v9 8/8] power: bq27xxx_battery_i2c: Add I2C bulk read/write functions Liam Breck
6 siblings, 0 replies; 9+ messages in thread
From: Liam Breck @ 2017-03-06 7:25 UTC (permalink / raw)
To: Sebastian Reichel; +Cc: Andrew F. Davis, linux-pm, Matt Ranostay, Liam Breck
From: Matt Ranostay <matt@ranostay.consulting>
Add BQ27425 id to cover 425, 441, 621 chips.
This allows support for 421 by the devicetree code.
Signed-off-by: Matt Ranostay <matt@ranostay.consulting>
Signed-off-by: Liam Breck <kernel@networkimprov.net>
---
drivers/power/supply/bq27xxx_battery.c | 23 ++++++++++++++++++++++-
drivers/power/supply/bq27xxx_battery_i2c.c | 6 +++---
include/linux/power/bq27xxx_battery.h | 3 ++-
3 files changed, 27 insertions(+), 5 deletions(-)
diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
index 7272d1e..7475a5f 100644
--- a/drivers/power/supply/bq27xxx_battery.c
+++ b/drivers/power/supply/bq27xxx_battery.c
@@ -259,6 +259,25 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
[BQ27XXX_REG_DCAP] = 0x3c,
[BQ27XXX_REG_AP] = 0x18,
},
+ [BQ27425] = {
+ [BQ27XXX_REG_CTRL] = 0x00,
+ [BQ27XXX_REG_TEMP] = 0x02,
+ [BQ27XXX_REG_INT_TEMP] = 0x1e,
+ [BQ27XXX_REG_VOLT] = 0x04,
+ [BQ27XXX_REG_AI] = 0x10,
+ [BQ27XXX_REG_FLAGS] = 0x06,
+ [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] = 0x08,
+ [BQ27XXX_REG_FCC] = 0x0e,
+ [BQ27XXX_REG_CYCT] = INVALID_REG_ADDR,
+ [BQ27XXX_REG_AE] = INVALID_REG_ADDR,
+ [BQ27XXX_REG_SOC] = 0x1c,
+ [BQ27XXX_REG_DCAP] = 0x3c,
+ [BQ27XXX_REG_AP] = 0x18,
+ },
};
static enum power_supply_property bq27000_battery_props[] = {
@@ -427,6 +446,7 @@ static struct {
BQ27XXX_PROP(BQ27541, bq27541_battery_props),
BQ27XXX_PROP(BQ27545, bq27545_battery_props),
BQ27XXX_PROP(BQ27421, bq27421_battery_props),
+ BQ27XXX_PROP(BQ27425, bq27421_battery_props),
};
static DEFINE_MUTEX(bq27xxx_list_lock);
@@ -682,6 +702,7 @@ static bool bq27xxx_battery_overtemp(struct bq27xxx_device_info *di, u16 flags)
return flags & (BQ27XXX_FLAG_OTC | BQ27XXX_FLAG_OTD);
case BQ27530:
case BQ27421:
+ case BQ27425:
return flags & BQ27XXX_FLAG_OT;
default:
return false;
@@ -693,7 +714,7 @@ static bool bq27xxx_battery_overtemp(struct bq27xxx_device_info *di, u16 flags)
*/
static bool bq27xxx_battery_undertemp(struct bq27xxx_device_info *di, u16 flags)
{
- if (di->chip == BQ27530 || di->chip == BQ27421)
+ if (di->chip == BQ27530 || di->chip == BQ27421 || di->chip == BQ27425)
return flags & BQ27XXX_FLAG_UT;
return false;
diff --git a/drivers/power/supply/bq27xxx_battery_i2c.c b/drivers/power/supply/bq27xxx_battery_i2c.c
index 5c5c3a6..7781ca9 100644
--- a/drivers/power/supply/bq27xxx_battery_i2c.c
+++ b/drivers/power/supply/bq27xxx_battery_i2c.c
@@ -159,9 +159,9 @@ static const struct i2c_device_id bq27xxx_i2c_id_table[] = {
{ "bq27742", BQ27541 },
{ "bq27545", BQ27545 },
{ "bq27421", BQ27421 },
- { "bq27425", BQ27421 },
- { "bq27441", BQ27421 },
- { "bq27621", BQ27421 },
+ { "bq27425", BQ27425 },
+ { "bq27441", BQ27425 },
+ { "bq27621", BQ27425 },
{},
};
MODULE_DEVICE_TABLE(i2c, bq27xxx_i2c_id_table);
diff --git a/include/linux/power/bq27xxx_battery.h b/include/linux/power/bq27xxx_battery.h
index 92df553..44f469a 100644
--- a/include/linux/power/bq27xxx_battery.h
+++ b/include/linux/power/bq27xxx_battery.h
@@ -9,7 +9,8 @@ enum bq27xxx_chip {
BQ27530, /* bq27530, bq27531 */
BQ27541, /* bq27541, bq27542, bq27546, bq27742 */
BQ27545, /* bq27545 */
- BQ27421, /* bq27421, bq27425, bq27441, bq27621 */
+ BQ27421, /* bq27421 */
+ BQ27425, /* bq27425, bq27441, bq27621 */
};
/**
--
2.9.3
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v9 7/8] power: bq27xxx_battery: Add power_supply_battery_info support
2017-03-06 7:25 [PATCH v9 0/8] devicetree battery support and client bq27xxx_battery Liam Breck
` (4 preceding siblings ...)
2017-03-06 7:25 ` [PATCH v9 6/8] power: bq27xxx_battery: Move BQ27421 chip id to its own group Liam Breck
@ 2017-03-06 7:25 ` Liam Breck
2017-03-06 7:25 ` [PATCH v9 8/8] power: bq27xxx_battery_i2c: Add I2C bulk read/write functions Liam Breck
6 siblings, 0 replies; 9+ messages in thread
From: Liam Breck @ 2017-03-06 7:25 UTC (permalink / raw)
To: Sebastian Reichel; +Cc: Andrew F. Davis, linux-pm, Matt Ranostay, Liam Breck
From: Liam Breck <kernel@networkimprov.net>
Previously there was no way to configure chip registers in the event that the
defaults didn't match the battery in question.
BQ27xxx driver now calls power_supply_get_battery_info, checks the inputs,
and writes battery data to chip RAM or non-volatile memory.
Signed-off-by: Matt Ranostay <matt@ranostay.consulting>
Signed-off-by: Liam Breck <kernel@networkimprov.net>
---
drivers/power/supply/bq27xxx_battery.c | 483 ++++++++++++++++++++++++++++++++-
1 file changed, 481 insertions(+), 2 deletions(-)
diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
index 7475a5f..ed976a1 100644
--- a/drivers/power/supply/bq27xxx_battery.c
+++ b/drivers/power/supply/bq27xxx_battery.c
@@ -51,7 +51,7 @@
#include <linux/power/bq27xxx_battery.h>
-#define DRIVER_VERSION "1.2.0"
+#define DRIVER_VERSION "1.3.0"
#define BQ27XXX_MANUFACTURER "Texas Instruments"
@@ -59,6 +59,7 @@
#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_CFGUP BIT(4)
#define BQ27XXX_FLAG_FC BIT(9)
#define BQ27XXX_FLAG_OTD BIT(14)
#define BQ27XXX_FLAG_OTC BIT(15)
@@ -72,6 +73,11 @@
#define BQ27000_FLAG_FC BIT(5)
#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_RS (20) /* Resistor sense mOhm */
#define BQ27XXX_POWER_CONSTANT (29200) /* 29.2 µV^2 * 1000 */
#define BQ27XXX_CURRENT_CONSTANT (3570) /* 3.57 µV * 1000 */
@@ -102,6 +108,11 @@ enum bq27xxx_reg_index {
BQ27XXX_REG_SOC, /* State-of-Charge */
BQ27XXX_REG_DCAP, /* Design Capacity */
BQ27XXX_REG_AP, /* Average Power */
+ BQ27XXX_DM_CTRL, /* BlockDataControl() */
+ BQ27XXX_DM_CLASS, /* DataClass() */
+ BQ27XXX_DM_BLOCK, /* DataBlock() */
+ BQ27XXX_DM_DATA, /* BlockData() */
+ BQ27XXX_DM_CKSUM, /* BlockDataChecksum() */
BQ27XXX_REG_MAX, /* sentinel */
};
@@ -125,6 +136,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
[BQ27XXX_REG_SOC] = 0x0b,
[BQ27XXX_REG_DCAP] = 0x76,
[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] = {
[BQ27XXX_REG_CTRL] = 0x00,
@@ -144,6 +160,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
[BQ27XXX_REG_SOC] = 0x0b,
[BQ27XXX_REG_DCAP] = 0x76,
[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,
},
[BQ27500] = {
[BQ27XXX_REG_CTRL] = 0x00,
@@ -163,6 +184,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
[BQ27XXX_REG_SOC] = 0x2c,
[BQ27XXX_REG_DCAP] = 0x3c,
[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
+ [BQ27XXX_DM_CTRL] = 0x61,
+ [BQ27XXX_DM_CLASS] = 0x3e,
+ [BQ27XXX_DM_BLOCK] = 0x3f,
+ [BQ27XXX_DM_DATA] = 0x40,
+ [BQ27XXX_DM_CKSUM] = 0x60,
},
[BQ27510] = {
[BQ27XXX_REG_CTRL] = 0x00,
@@ -182,6 +208,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
[BQ27XXX_REG_SOC] = 0x20,
[BQ27XXX_REG_DCAP] = 0x2e,
[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
+ [BQ27XXX_DM_CTRL] = 0x61,
+ [BQ27XXX_DM_CLASS] = 0x3e,
+ [BQ27XXX_DM_BLOCK] = 0x3f,
+ [BQ27XXX_DM_DATA] = 0x40,
+ [BQ27XXX_DM_CKSUM] = 0x60,
},
[BQ27530] = {
[BQ27XXX_REG_CTRL] = 0x00,
@@ -201,6 +232,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
[BQ27XXX_REG_SOC] = 0x2c,
[BQ27XXX_REG_DCAP] = INVALID_REG_ADDR,
[BQ27XXX_REG_AP] = 0x24,
+ [BQ27XXX_DM_CTRL] = 0x61,
+ [BQ27XXX_DM_CLASS] = 0x3e,
+ [BQ27XXX_DM_BLOCK] = 0x3f,
+ [BQ27XXX_DM_DATA] = 0x40,
+ [BQ27XXX_DM_CKSUM] = 0x60,
},
[BQ27541] = {
[BQ27XXX_REG_CTRL] = 0x00,
@@ -220,6 +256,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
[BQ27XXX_REG_SOC] = 0x2c,
[BQ27XXX_REG_DCAP] = 0x3c,
[BQ27XXX_REG_AP] = 0x24,
+ [BQ27XXX_DM_CTRL] = 0x61,
+ [BQ27XXX_DM_CLASS] = 0x3e,
+ [BQ27XXX_DM_BLOCK] = 0x3f,
+ [BQ27XXX_DM_DATA] = 0x40,
+ [BQ27XXX_DM_CKSUM] = 0x60,
},
[BQ27545] = {
[BQ27XXX_REG_CTRL] = 0x00,
@@ -239,6 +280,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
[BQ27XXX_REG_SOC] = 0x2c,
[BQ27XXX_REG_DCAP] = INVALID_REG_ADDR,
[BQ27XXX_REG_AP] = 0x24,
+ [BQ27XXX_DM_CTRL] = 0x61,
+ [BQ27XXX_DM_CLASS] = 0x3e,
+ [BQ27XXX_DM_BLOCK] = 0x3f,
+ [BQ27XXX_DM_DATA] = 0x40,
+ [BQ27XXX_DM_CKSUM] = 0x60,
},
[BQ27421] = {
[BQ27XXX_REG_CTRL] = 0x00,
@@ -258,6 +304,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
[BQ27XXX_REG_SOC] = 0x1c,
[BQ27XXX_REG_DCAP] = 0x3c,
[BQ27XXX_REG_AP] = 0x18,
+ [BQ27XXX_DM_CTRL] = 0x61,
+ [BQ27XXX_DM_CLASS] = 0x3e,
+ [BQ27XXX_DM_BLOCK] = 0x3f,
+ [BQ27XXX_DM_DATA] = 0x40,
+ [BQ27XXX_DM_CKSUM] = 0x60,
},
[BQ27425] = {
[BQ27XXX_REG_CTRL] = 0x00,
@@ -277,6 +328,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
[BQ27XXX_REG_SOC] = 0x1c,
[BQ27XXX_REG_DCAP] = 0x3c,
[BQ27XXX_REG_AP] = 0x18,
+ [BQ27XXX_DM_CTRL] = 0x61,
+ [BQ27XXX_DM_CLASS] = 0x3e,
+ [BQ27XXX_DM_BLOCK] = 0x3f,
+ [BQ27XXX_DM_DATA] = 0x40,
+ [BQ27XXX_DM_CKSUM] = 0x60,
},
};
@@ -452,6 +508,106 @@ static struct {
static DEFINE_MUTEX(bq27xxx_list_lock);
static LIST_HEAD(bq27xxx_battery_devices);
+#define BQ27XXX_DM_SZ 32
+
+#define BQ27XXX_MSLEEP(i) usleep_range((i)*1000, (i)*1000+500)
+
+struct bq27xxx_dm_reg {
+ u8 subclass_id;
+ u8 offset;
+ u8 bytes;
+ u16 min, max;
+};
+
+struct bq27xxx_dm_buf {
+ u8 class;
+ u8 block;
+ u8 a[BQ27XXX_DM_SZ];
+ bool full, updt;
+};
+
+#define BQ27XXX_DM_BUF(di, i) { \
+ .class = bq27xxx_dm_regs[(di)->chip][i].subclass_id, \
+ .block = bq27xxx_dm_regs[(di)->chip][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->a + 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* bq27xxx_dm_reg_name[] = {
+ [BQ27XXX_DM_DESIGN_CAPACITY] = "design-capacity",
+ [BQ27XXX_DM_DESIGN_ENERGY] = "design-energy",
+ [BQ27XXX_DM_TERMINATE_VOLTAGE] = "terminate-voltage",
+};
+
+static struct bq27xxx_dm_reg bq27500_dm_regs[] = {
+ [BQ27XXX_DM_DESIGN_CAPACITY] = { 48, 10, 2, 0, 65535 },
+ [BQ27XXX_DM_DESIGN_ENERGY] = { }, /* missing on chip */
+ [BQ27XXX_DM_TERMINATE_VOLTAGE] = { 80, 48, 2, 1000, 32767 },
+};
+
+static struct bq27xxx_dm_reg bq27545_dm_regs[] = {
+ [BQ27XXX_DM_DESIGN_CAPACITY] = { 48, 23, 2, 0, 32767 },
+ [BQ27XXX_DM_DESIGN_ENERGY] = { 48, 25, 2, 0, 32767 },
+ [BQ27XXX_DM_TERMINATE_VOLTAGE] = { 80, 67, 2, 2800, 3700 },
+};
+
+static struct bq27xxx_dm_reg bq27421_dm_regs[] = {
+ [BQ27XXX_DM_DESIGN_CAPACITY] = { 82, 10, 2, 0, 8000 },
+ [BQ27XXX_DM_DESIGN_ENERGY] = { 82, 12, 2, 0, 32767 },
+ [BQ27XXX_DM_TERMINATE_VOLTAGE] = { 82, 16, 2, 2500, 3700 },
+};
+
+/* requires di->chip/group distinction
+ *static struct bq27xxx_dm_reg bq27425_dm_regs[] = {
+ * [BQ27XXX_DM_DESIGN_CAPACITY] = { 82, 12, 2, 0, 32767 },
+ * [BQ27XXX_DM_DESIGN_ENERGY] = { 82, 14, 2, 0, 32767 },
+ * [BQ27XXX_DM_TERMINATE_VOLTAGE] = { 82, 18, 2, 2800, 3700 },
+ *};
+ *
+ *static struct bq27xxx_dm_reg bq27621_dm_regs[] = {
+ * [BQ27XXX_DM_DESIGN_CAPACITY] = { 82, 3, 2, 0, 8000 },
+ * [BQ27XXX_DM_DESIGN_ENERGY] = { 82, 5, 2, 0, 32767 },
+ * [BQ27XXX_DM_TERMINATE_VOLTAGE] = { 82, 9, 2, 2500, 3700 },
+ *};
+ */
+
+static struct bq27xxx_dm_reg *bq27xxx_dm_regs[] = {
+ [BQ27500] = bq27500_dm_regs,
+ [BQ27545] = bq27545_dm_regs,
+ [BQ27421] = bq27421_dm_regs,
+/* requires di->chip/group distinction
+ * [BQ27425] = bq27425_dm_regs,
+ * [BQ27441] = bq27421_dm_regs,
+ * [BQ27621] = bq27621_dm_regs,
+ */
+};
+
+static u32 bq27xxx_unseal_keys[] = {
+ [BQ27500] = 0x04143672,
+ [BQ27545] = 0x04143672,
+ [BQ27421] = 0x80008000,
+/* requires di->chip/group distinction
+ * [BQ27425] = 0x04143672,
+ * [BQ27441] = 0x80008000,
+ * [BQ27621] = 0x80008000,
+ */
+};
+
+
static int poll_interval_param_set(const char *val, const struct kernel_param *kp)
{
struct bq27xxx_device_info *di;
@@ -496,6 +652,318 @@ static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index,
return di->bus.read(di, di->regs[reg_index], single);
}
+static int bq27xxx_battery_set_seal_state(struct bq27xxx_device_info *di,
+ bool state)
+{
+ int ret;
+
+ if (state) {
+ ret = di->bus.write(di, di->regs[BQ27XXX_REG_CTRL], BQ27XXX_SEALED, false);
+ if (ret < 0)
+ goto out;
+ } else {
+ u32 key = bq27xxx_unseal_keys[di->chip];
+
+ ret = di->bus.write(di, di->regs[BQ27XXX_REG_CTRL], (u16)(key >> 16), false);
+ if (ret < 0)
+ goto out;
+
+ ret = di->bus.write(di, di->regs[BQ27XXX_REG_CTRL], (u16)key, false);
+ if (ret < 0)
+ goto out;
+ }
+ return 0;
+
+out:
+ dev_err(di->dev, "bus error on %s: %d\n", state ? "seal" : "unseal", 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->a[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;
+
+ ret = di->bus.write(di, di->regs[BQ27XXX_DM_CLASS], buf->class, true);
+ if (ret < 0)
+ goto out;
+
+ ret = di->bus.write(di, di->regs[BQ27XXX_DM_BLOCK], buf->block, true);
+ if (ret < 0)
+ goto out;
+
+ BQ27XXX_MSLEEP(1);
+
+ ret = di->bus.read_bulk(di, di->regs[BQ27XXX_DM_DATA], buf->a, BQ27XXX_DM_SZ);
+ if (ret < 0)
+ goto out;
+
+ ret = di->bus.read(di, di->regs[BQ27XXX_DM_CKSUM], true);
+ if (ret < 0)
+ goto out;
+
+ if ((u8)ret != bq27xxx_battery_checksum_dm_block(buf)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ buf->full = true;
+ buf->updt = 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 = &bq27xxx_dm_regs[di->chip][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->full)
+ return;
+
+ if (be16_to_cpup(prev) == val) {
+ dev_info(di->dev, "%s has %u\n", str, val);
+ return;
+ }
+
+ dev_info(di->dev, "update %s to %u\n", str, val);
+
+ *prev = cpu_to_be16(val);
+ buf->updt = true;
+}
+
+static int bq27xxx_battery_set_cfgupdate(struct bq27xxx_device_info *di,
+ bool state)
+{
+ int ret, try=100;
+
+ ret = di->bus.write(di, di->regs[BQ27XXX_REG_CTRL],
+ state ? BQ27XXX_SET_CFGUPDATE : BQ27XXX_SOFT_RESET,
+ false);
+ if (ret < 0)
+ goto out;
+
+ do {
+ BQ27XXX_MSLEEP(25);
+ ret = di->bus.read(di, di->regs[BQ27XXX_REG_FLAGS], false);
+ if (ret < 0)
+ goto out;
+ } while (!(ret & BQ27XXX_FLAG_CFGUP) == state && --try);
+
+ if (100-try) /* remove after other cfgupdate parts are tested */
+ dev_info(di->dev, "cfgupdate %d, retries %d\n", state, 100-try);
+
+ if (try)
+ return 0;
+
+ dev_err(di->dev, "timed out waiting for cfgupdate flag %d\n", state);
+ return -EINVAL;
+
+out:
+ dev_err(di->dev, "bus error on %s: %d\n", state ? "set_cfgupdate" : "soft_reset", ret);
+ return ret;
+}
+
+static int bq27xxx_battery_write_dm_block(struct bq27xxx_device_info *di,
+ struct bq27xxx_dm_buf *buf)
+{
+ bool cfgup = di->chip == BQ27425 || di->chip == BQ27421; /* || BQ27441 || BQ27621 */
+ int ret;
+
+ if (!buf->updt)
+ return 0;
+
+ if (cfgup) {
+ ret = bq27xxx_battery_set_cfgupdate(di, true);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = di->bus.write(di, di->regs[BQ27XXX_DM_CTRL], 0, true);
+ if (ret < 0)
+ goto out;
+
+ ret = di->bus.write(di, di->regs[BQ27XXX_DM_CLASS], buf->class, true);
+ if (ret < 0)
+ goto out;
+
+ ret = di->bus.write(di, di->regs[BQ27XXX_DM_BLOCK], buf->block, true);
+ if (ret < 0)
+ goto out;
+
+ BQ27XXX_MSLEEP(1);
+
+ ret = di->bus.write_bulk(di, di->regs[BQ27XXX_DM_DATA], buf->a, BQ27XXX_DM_SZ);
+ if (ret < 0)
+ goto out;
+
+ ret = di->bus.write(di, di->regs[BQ27XXX_DM_CKSUM],
+ bq27xxx_battery_checksum_dm_block(buf), true);
+ if (ret < 0)
+ goto out;
+
+ /* THE FOLLOWING SEQUENCE IS TOXIC. DO NOT USE!
+ * If the 'time' delay is insufficient, NVM corruption results on
+ * the '425 chip (and perhaps others), which could damage the chip.
+ * It was suggested in this TI tool:
+ * http://git.ti.com/bms-linux/bqtool/blobs/master/gauge.c#line328
+ *
+ * 1. MSLEEP(time) after above write(BQ27XXX_DM_CKSUM, ...)
+ * 2. write(BQ27XXX_DM_BLOCK, buf->block)
+ * 3. sum = read(BQ27XXX_DM_CKSUM)
+ * 4. if (sum != bq27xxx_battery_checksum_dm_block(buf))
+ * report error
+ */
+
+ if (cfgup) {
+ BQ27XXX_MSLEEP(1);
+ ret = bq27xxx_battery_set_cfgupdate(di, false);
+ if (ret < 0)
+ return ret;
+ } else {
+ /* flash DM updates in <100ms, but reset time isn't documented */
+ BQ27XXX_MSLEEP(400);
+ }
+
+ buf->updt = false;
+ return 0;
+
+out:
+ if (cfgup)
+ bq27xxx_battery_set_cfgupdate(di, false);
+
+ 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);
+
+ 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);
+ }
+
+ bq27xxx_battery_write_dm_block(di, &bd);
+ bq27xxx_battery_write_dm_block(di, &bt);
+}
+
+void bq27xxx_battery_settings(struct bq27xxx_device_info *di)
+{
+ struct power_supply_battery_info info = {};
+ unsigned int min, max;
+
+ /* functions don't exist for writing data so abort */
+ if (!di->bus.write || !di->bus.write_bulk)
+ return;
+
+ /* no settings to be set for this chipset so abort */
+ if (!bq27xxx_dm_regs[di->chip])
+ return;
+
+ if (bq27xxx_battery_set_seal_state(di, false) < 0)
+ return;
+
+ if (power_supply_get_battery_info(di->bat, &info) < 0)
+ goto out;
+
+ 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 = bq27xxx_dm_regs[di->chip][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 = bq27xxx_dm_regs[di->chip][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 = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_TERMINATE_VOLTAGE].min;
+ max = bq27xxx_dm_regs[di->chip][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)
+ goto out;
+
+ bq27xxx_battery_set_config(di, &info);
+
+out:
+ bq27xxx_battery_set_seal_state(di, true);
+}
+
/*
* Return the battery State-of-Charge
* Or < 0 if something fails.
@@ -1006,6 +1474,13 @@ static int bq27xxx_battery_get_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
ret = bq27xxx_simple_value(di->charge_design_full, val);
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:
ret = bq27xxx_simple_value(di->cache.cycle_count, val);
break;
@@ -1039,7 +1514,10 @@ static void bq27xxx_external_power_changed(struct power_supply *psy)
int bq27xxx_battery_setup(struct bq27xxx_device_info *di)
{
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);
mutex_init(&di->lock);
@@ -1064,6 +1542,7 @@ int bq27xxx_battery_setup(struct bq27xxx_device_info *di)
dev_info(di->dev, "support ver. %s enabled\n", DRIVER_VERSION);
+ bq27xxx_battery_settings(di);
bq27xxx_battery_update(di);
mutex_lock(&bq27xxx_list_lock);
--
2.9.3
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v9 8/8] power: bq27xxx_battery_i2c: Add I2C bulk read/write functions
2017-03-06 7:25 [PATCH v9 0/8] devicetree battery support and client bq27xxx_battery Liam Breck
` (5 preceding siblings ...)
2017-03-06 7:25 ` [PATCH v9 7/8] power: bq27xxx_battery: Add power_supply_battery_info support Liam Breck
@ 2017-03-06 7:25 ` Liam Breck
6 siblings, 0 replies; 9+ messages in thread
From: Liam Breck @ 2017-03-06 7:25 UTC (permalink / raw)
To: Sebastian Reichel; +Cc: Andrew F. Davis, linux-pm, Matt Ranostay, Liam Breck
From: Matt Ranostay <matt@ranostay.consulting>
write(), read_bulk(), write_bulk() are required by bq27xxx_battery
devicetree code.
Signed-off-by: Matt Ranostay <matt@ranostay.consulting>
Signed-off-by: Liam Breck <kernel@networkimprov.net>
---
drivers/power/supply/bq27xxx_battery_i2c.c | 82 +++++++++++++++++++++++++++++-
1 file changed, 81 insertions(+), 1 deletion(-)
diff --git a/drivers/power/supply/bq27xxx_battery_i2c.c b/drivers/power/supply/bq27xxx_battery_i2c.c
index 7781ca9..4039b9d 100644
--- a/drivers/power/supply/bq27xxx_battery_i2c.c
+++ b/drivers/power/supply/bq27xxx_battery_i2c.c
@@ -38,7 +38,7 @@ static int bq27xxx_battery_i2c_read(struct bq27xxx_device_info *di, u8 reg,
{
struct i2c_client *client = to_i2c_client(di->dev);
struct i2c_msg msg[2];
- unsigned char data[2];
+ u8 data[2];
int ret;
if (!client->adapter)
@@ -68,6 +68,82 @@ static int bq27xxx_battery_i2c_read(struct bq27xxx_device_info *di, u8 reg,
return ret;
}
+static int bq27xxx_battery_i2c_write(struct bq27xxx_device_info *di, u8 reg,
+ int value, bool single)
+{
+ struct i2c_client *client = to_i2c_client(di->dev);
+ struct i2c_msg msg;
+ u8 data[4];
+ int ret;
+
+ if (!client->adapter)
+ return -ENODEV;
+
+ data[0] = reg;
+ if (single) {
+ data[1] = (u8) value;
+ msg.len = 2;
+ } else {
+ put_unaligned_le16(value, &data[1]);
+ msg.len = 3;
+ }
+
+ msg.buf = data;
+ msg.addr = client->addr;
+ msg.flags = 0;
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret < 0)
+ return ret;
+ if (ret != 1)
+ return -EINVAL;
+ return 0;
+}
+
+static int bq27xxx_battery_i2c_bulk_read(struct bq27xxx_device_info *di, u8 reg,
+ u8 *data, int len)
+{
+ struct i2c_client *client = to_i2c_client(di->dev);
+ int ret;
+
+ if (!client->adapter)
+ return -ENODEV;
+
+ ret = i2c_smbus_read_i2c_block_data(client, reg, len, data);
+ if (ret < 0)
+ return ret;
+ if (ret != len)
+ return -EINVAL;
+ return 0;
+}
+
+static int bq27xxx_battery_i2c_bulk_write(struct bq27xxx_device_info *di,
+ u8 reg, u8 *data, int len)
+{
+ struct i2c_client *client = to_i2c_client(di->dev);
+ struct i2c_msg msg;
+ u8 buf[33];
+ int ret;
+
+ if (!client->adapter)
+ return -ENODEV;
+
+ buf[0] = reg;
+ memcpy(&buf[1], data, len);
+
+ msg.buf = buf;
+ msg.addr = client->addr;
+ msg.flags = 0;
+ msg.len = len + 1;
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret < 0)
+ return ret;
+ if (ret != 1)
+ return -EINVAL;
+ return 0;
+}
+
static int bq27xxx_battery_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -95,7 +171,11 @@ static int bq27xxx_battery_i2c_probe(struct i2c_client *client,
di->dev = &client->dev;
di->chip = id->driver_data;
di->name = name;
+
di->bus.read = bq27xxx_battery_i2c_read;
+ di->bus.write = bq27xxx_battery_i2c_write;
+ di->bus.read_bulk = bq27xxx_battery_i2c_bulk_read;
+ di->bus.write_bulk = bq27xxx_battery_i2c_bulk_write;
ret = bq27xxx_battery_setup(di);
if (ret)
--
2.9.3
^ permalink raw reply related [flat|nested] 9+ messages in thread
end of thread, other threads:[~2017-03-06 7:34 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2017-03-06 7:25 [PATCH v9 0/8] devicetree battery support and client bq27xxx_battery Liam Breck
2017-03-06 7:25 ` [PATCH v9 2/8] devicetree: property-units: Add uWh and uAh units Liam Breck
[not found] ` <20170306072539.980-1-liam-RYWXG+zxWwBdeoIcmNTgJF6hYfS7NtTn@public.gmane.org>
2017-03-06 7:25 ` [PATCH v9 1/8] devicetree: power: Add battery.txt Liam Breck
2017-03-06 7:25 ` [PATCH v9 3/8] devicetree: power: bq27xxx: Add monitored-battery documentation Liam Breck
2017-03-06 7:25 ` [PATCH v9 4/8] power: power_supply: Add power_supply_battery_info and API Liam Breck
2017-03-06 7:25 ` [PATCH v9 5/8] power: bq27xxx_battery: Define access methods to write chip registers Liam Breck
2017-03-06 7:25 ` [PATCH v9 6/8] power: bq27xxx_battery: Move BQ27421 chip id to its own group Liam Breck
2017-03-06 7:25 ` [PATCH v9 7/8] power: bq27xxx_battery: Add power_supply_battery_info support Liam Breck
2017-03-06 7:25 ` [PATCH v9 8/8] power: bq27xxx_battery_i2c: Add I2C bulk read/write functions Liam Breck
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).