All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v7 0/9] devicetree battery support and client bq27xxx_battery
@ 2017-02-21 21:29 Liam Breck
       [not found] ` <20170221213008.30044-1-liam-RYWXG+zxWwBdeoIcmNTgJF6hYfS7NtTn@public.gmane.org>
                   ` (8 more replies)
  0 siblings, 9 replies; 34+ messages in thread
From: Liam Breck @ 2017-02-21 21:29 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 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 (5):
  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
  power: bq27xxx_battery: Add print_dm() to log chip memory

Matt Ranostay (4):
  devicetree: property-units: Add uWh and uAh units
  power: bq27xxx_battery: Define access methods to write chip registers
  power: bq27xxx_battery: Add BQ27425 chip id
  power: bq27xxx_battery_i2c: Add I2C bulk read/write functions

 .../devicetree/bindings/power/supply/battery.txt   |  42 ++
 .../devicetree/bindings/power/supply/bq27xxx.txt   |  11 +-
 .../devicetree/bindings/property-units.txt         |   2 +
 drivers/power/supply/bq27xxx_battery.c             | 473 ++++++++++++++++++++-
 drivers/power/supply/bq27xxx_battery_i2c.c         |  84 +++-
 drivers/power/supply/power_supply_core.c           |  40 ++
 include/linux/power/bq27xxx_battery.h              |   6 +-
 include/linux/power_supply.h                       |  18 +
 8 files changed, 669 insertions(+), 7 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/power/supply/battery.txt

-- 
2.9.3

^ permalink raw reply	[flat|nested] 34+ messages in thread

* [PATCH v7 1/9] devicetree: power: Add battery.txt
       [not found] ` <20170221213008.30044-1-liam-RYWXG+zxWwBdeoIcmNTgJF6hYfS7NtTn@public.gmane.org>
@ 2017-02-21 21:30   ` Liam Breck
       [not found]     ` <20170221213008.30044-2-liam-RYWXG+zxWwBdeoIcmNTgJF6hYfS7NtTn@public.gmane.org>
  2017-02-21 21:30   ` [PATCH v7 3/9] devicetree: power: bq27xxx: Add monitored-battery documentation Liam Breck
  1 sibling, 1 reply; 34+ messages in thread
From: Liam Breck @ 2017-02-21 21:30 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   | 42 ++++++++++++++++++++++
 1 file changed, 42 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..3d916bd
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/supply/battery.txt
@@ -0,0 +1,42 @@
+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
+
+Future 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] 34+ messages in thread

* [PATCH v7 2/9] devicetree: property-units: Add uWh and uAh units
       [not found] ` <20170221213008.30044-1-liam-RYWXG+zxWwBdeoIcmNTgJF6hYfS7NtTn@public.gmane.org>
@ 2017-02-21 21:30   ` Liam Breck
  2017-02-21 21:30   ` [PATCH v7 3/9] devicetree: power: bq27xxx: Add monitored-battery documentation Liam Breck
  1 sibling, 0 replies; 34+ messages in thread
From: Liam Breck @ 2017-02-21 21:30 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] 34+ messages in thread

* [PATCH v7 2/9] devicetree: property-units: Add uWh and uAh units
@ 2017-02-21 21:30   ` Liam Breck
  0 siblings, 0 replies; 34+ messages in thread
From: Liam Breck @ 2017-02-21 21:30 UTC (permalink / raw)
  To: Sebastian Reichel
  Cc: Andrew F. Davis, linux-pm-u79uwXL29TY76Z2rM5mHXA, Matt Ranostay,
	Rob Herring, Mark Rutland, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Liam Breck

From: Matt Ranostay <matt-sk+viVC6FLCDq+mSdOJa79kegs52MxvZ@public.gmane.org>

Add entries for microwatt-hours and microamp-hours.

Cc: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Cc: Mark Rutland <mark.rutland-5wv7dgnIgG8@public.gmane.org>
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: linux-kernel-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: Sebastian Reichel <sre-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.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

--
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] 34+ messages in thread

* [PATCH v7 3/9] devicetree: power: bq27xxx: Add monitored-battery documentation
       [not found] ` <20170221213008.30044-1-liam-RYWXG+zxWwBdeoIcmNTgJF6hYfS7NtTn@public.gmane.org>
  2017-02-21 21:30   ` [PATCH v7 1/9] devicetree: power: Add battery.txt Liam Breck
@ 2017-02-21 21:30   ` Liam Breck
  1 sibling, 0 replies; 34+ messages in thread
From: Liam Breck @ 2017-02-21 21:30 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>
---
 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] 34+ messages in thread

* [PATCH v7 4/9] power: power_supply: Add power_supply_battery_info and API
  2017-02-21 21:29 [PATCH v7 0/9] devicetree battery support and client bq27xxx_battery Liam Breck
       [not found] ` <20170221213008.30044-1-liam-RYWXG+zxWwBdeoIcmNTgJF6hYfS7NtTn@public.gmane.org>
  2017-02-21 21:30   ` Liam Breck
@ 2017-02-21 21:30 ` Liam Breck
  2017-02-21 21:30 ` [PATCH v7 5/9] power: bq27xxx_battery: Define access methods to write chip registers Liam Breck
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 34+ messages in thread
From: Liam Breck @ 2017-02-21 21:30 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] 34+ messages in thread

* [PATCH v7 5/9] power: bq27xxx_battery: Define access methods to write chip registers
  2017-02-21 21:29 [PATCH v7 0/9] devicetree battery support and client bq27xxx_battery Liam Breck
                   ` (2 preceding siblings ...)
  2017-02-21 21:30 ` [PATCH v7 4/9] power: power_supply: Add power_supply_battery_info and API Liam Breck
@ 2017-02-21 21:30 ` Liam Breck
  2017-02-21 21:30 ` [PATCH v7 6/9] power: bq27xxx_battery: Add BQ27425 chip id Liam Breck
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 34+ messages in thread
From: Liam Breck @ 2017-02-21 21:30 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] 34+ messages in thread

* [PATCH v7 6/9] power: bq27xxx_battery: Add BQ27425 chip id
  2017-02-21 21:29 [PATCH v7 0/9] devicetree battery support and client bq27xxx_battery Liam Breck
                   ` (3 preceding siblings ...)
  2017-02-21 21:30 ` [PATCH v7 5/9] power: bq27xxx_battery: Define access methods to write chip registers Liam Breck
@ 2017-02-21 21:30 ` Liam Breck
  2017-02-22 20:15   ` Andrew F. Davis
  2017-02-21 21:30 ` [PATCH v7 7/9] power: bq27xxx_battery: Add power_supply_battery_info support Liam Breck
                   ` (3 subsequent siblings)
  8 siblings, 1 reply; 34+ messages in thread
From: Liam Breck @ 2017-02-21 21:30 UTC (permalink / raw)
  To: Sebastian Reichel; +Cc: Andrew F. Davis, linux-pm, Matt Ranostay, Liam Breck

From: Matt Ranostay <matt@ranostay.consulting>

This patch does not alter the function of the driver.
This chip ID is referenced in 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 |  2 +-
 include/linux/power/bq27xxx_battery.h      |  3 ++-
 3 files changed, 25 insertions(+), 3 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..d7da535 100644
--- a/drivers/power/supply/bq27xxx_battery_i2c.c
+++ b/drivers/power/supply/bq27xxx_battery_i2c.c
@@ -159,7 +159,7 @@ static const struct i2c_device_id bq27xxx_i2c_id_table[] = {
 	{ "bq27742", BQ27541 },
 	{ "bq27545", BQ27545 },
 	{ "bq27421", BQ27421 },
-	{ "bq27425", BQ27421 },
+	{ "bq27425", BQ27425 },
 	{ "bq27441", BQ27421 },
 	{ "bq27621", BQ27421 },
 	{},
diff --git a/include/linux/power/bq27xxx_battery.h b/include/linux/power/bq27xxx_battery.h
index 92df553..4925478 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, bq27441, bq27621 */
+	BQ27425, /* bq27425 */
 };
 
 /**
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH v7 7/9] power: bq27xxx_battery: Add power_supply_battery_info support
  2017-02-21 21:29 [PATCH v7 0/9] devicetree battery support and client bq27xxx_battery Liam Breck
                   ` (4 preceding siblings ...)
  2017-02-21 21:30 ` [PATCH v7 6/9] power: bq27xxx_battery: Add BQ27425 chip id Liam Breck
@ 2017-02-21 21:30 ` Liam Breck
  2017-02-22 20:35   ` Andrew F. Davis
  2017-02-21 21:30 ` [PATCH v7 8/9] power: bq27xxx_battery: Add print_dm() to log chip memory Liam Breck
                   ` (2 subsequent siblings)
  8 siblings, 1 reply; 34+ messages in thread
From: Liam Breck @ 2017-02-21 21:30 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 | 399 ++++++++++++++++++++++++++++++++-
 1 file changed, 397 insertions(+), 2 deletions(-)

diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
index 7475a5f..8085d4a 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"
 
@@ -452,6 +452,99 @@ static struct {
 static DEFINE_MUTEX(bq27xxx_list_lock);
 static LIST_HEAD(bq27xxx_battery_devices);
 
+/* writable registers */
+#define BQ27XXX_CONTROL			0x00
+#define BQ27XXX_DATA_CLASS		0x3E
+#define BQ27XXX_DATA_BLOCK		0x3F
+#define BQ27XXX_BLOCK_DATA		0x40
+#define BQ27XXX_BLOCK_DATA_CHECKSUM	0x60
+#define BQ27XXX_BLOCK_DATA_CONTROL	0x61
+
+/* control register params */
+#define BQ27XXX_SEALED			0x20
+#define BQ27XXX_SET_CFGUPDATE		0x13
+#define BQ27XXX_SOFT_RESET		0x42
+
+#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[32];
+	bool full, updt;
+};
+
+#define BQ27XXX_DM_BUF_PTR(b, r) \
+	( (u16*) &(b).a[ (r)->offset % sizeof (b).a ] )
+
+#define BQ27XXX_DM_BUF(di, i) { \
+	.class = bq27xxx_dm_regs[(di)->chip][i].subclass_id, \
+	.block = bq27xxx_dm_regs[(di)->chip][i].offset / sizeof ((struct bq27xxx_dm_buf*)0)->a, \
+}
+
+static inline bool bq27xxx_dm_buf_set(struct bq27xxx_dm_buf *buf,
+				      struct bq27xxx_dm_reg *reg) {
+	if (buf->class == reg->subclass_id
+	 && buf->block == reg->offset / sizeof buf->a
+	 && buf->full)
+		return false;
+	buf->class = reg->subclass_id;
+	buf->block = reg->offset / sizeof buf->a;
+	buf->full = buf->updt = false;
+	return true;
+}
+
+enum bq27xxx_dm_reg_id {
+	BQ27XXX_DM_DESIGN_CAPACITY = 0,
+	BQ27XXX_DM_DESIGN_ENERGY,
+	BQ27XXX_DM_TERMINATE_VOLTAGE,
+	BQ27XXX_DM_END,
+};
+
+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 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 bq27421_dm_regs[] = { /* not tested */
+	[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 },
+};
+
+static struct bq27xxx_dm_reg bq27621_dm_regs[] = { /* not tested */
+	[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[] = {
+	[BQ27421] = bq27421_dm_regs, /* and BQ27441 */
+	[BQ27425] = bq27425_dm_regs,
+/*	[BQ27621] = */ bq27621_dm_regs,
+};
+
+static u32 bq27xxx_unseal_keys[] = {
+	[BQ27421] = 0x80008000, /* and BQ27441 */
+	[BQ27425] = 0x04143672,
+/*	[BQ27621] = */ 0x80008000,
+};
+
+
 static int poll_interval_param_set(const char *val, const struct kernel_param *kp)
 {
 	struct bq27xxx_device_info *di;
@@ -496,6 +589,297 @@ 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)
+{
+	u32 key = bq27xxx_unseal_keys[di->chip];
+	int ret;
+
+	if (state) {
+		ret = di->bus.write(di, BQ27XXX_CONTROL, BQ27XXX_SEALED, false);
+		if (ret < 0)
+			goto out;
+	} else {
+		ret = di->bus.write(di, BQ27XXX_CONTROL, (u16)(key >> 16), false);
+		if (ret < 0)
+			goto out;
+
+		ret = di->bus.write(di, BQ27XXX_CONTROL, (u16)key, false);
+		if (ret < 0)
+			goto out;
+	}
+	return 0;
+
+out:
+	dev_err(di->dev, "bus error in %s: %d\n", __func__, ret);
+	return ret;
+}
+
+static u8 bq27xxx_battery_checksum(struct bq27xxx_dm_buf *buf)
+{
+	u16 sum = 0;
+	int i;
+
+	for (i = 0; i < sizeof buf->a; 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, BQ27XXX_DATA_CLASS, buf->class, true);
+	if (ret < 0)
+		goto out;
+
+	ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, buf->block, true);
+	if (ret < 0)
+		goto out;
+
+	BQ27XXX_MSLEEP(1);
+
+	ret = di->bus.read_bulk(di, BQ27XXX_BLOCK_DATA, buf->a, sizeof buf->a);
+	if (ret < 0)
+		goto out;
+
+	ret = di->bus.read(di, BQ27XXX_BLOCK_DATA_CHECKSUM, true);
+	if (ret < 0)
+		goto out;
+
+	if ((u8)ret != bq27xxx_battery_checksum(buf)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	buf->full = true;
+	buf->updt = false;
+	return 0;
+
+out:
+	dev_err(di->dev, "bus error in %s: %d\n", __func__, ret);
+	return ret;
+}
+
+static void bq27xxx_battery_print_config(struct bq27xxx_device_info *di)
+{
+	struct bq27xxx_dm_reg *reg = bq27xxx_dm_regs[di->chip];
+	struct bq27xxx_dm_buf buf = { .class = 0xFF };
+	int i;
+
+	for (i = 0; i < BQ27XXX_DM_END; i++, reg++) {
+		const char* str = bq27xxx_dm_reg_name[i];
+
+		if (bq27xxx_dm_buf_set(&buf, reg))
+			if (bq27xxx_battery_read_dm_block(di, &buf) < 0)
+				continue;
+
+		if (reg->bytes == 2)
+			dev_info(di->dev, "config register %s is %d\n", str,
+				 be16_to_cpup(BQ27XXX_DM_BUF_PTR(buf, reg)));
+		else
+			dev_warn(di->dev, "unsupported config register %s\n", str);
+	}
+}
+
+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];
+	u16 *prev;
+
+	if (reg->bytes != 2)
+		return;
+
+	prev = BQ27XXX_DM_BUF_PTR(*buf, reg);
+	if (be16_to_cpup(prev) == val)
+		return;
+	*prev = cpu_to_be16(val);
+
+	dev_info(di->dev, "update chip %d, class %u, block %u, offset %u, value %u\n",
+		 di->chip, buf->class, buf->block, reg->offset, val);
+
+	buf->updt = true;
+	return;
+}
+
+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 (cfgup) {
+		ret = di->bus.write(di, BQ27XXX_CONTROL, BQ27XXX_SET_CFGUPDATE, false);
+		if (ret < 0)
+			goto out1;
+	}
+
+	ret = di->bus.write(di, BQ27XXX_BLOCK_DATA_CONTROL, 0, true);
+	if (ret < 0)
+		goto out2;
+
+	ret = di->bus.write(di, BQ27XXX_DATA_CLASS, buf->class, true);
+	if (ret < 0)
+		goto out2;
+
+	ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, buf->block, true);
+	if (ret < 0)
+		goto out2;
+
+	BQ27XXX_MSLEEP(1);
+
+	ret = di->bus.write_bulk(di, BQ27XXX_BLOCK_DATA, buf->a, sizeof buf->a);
+	if (ret < 0)
+		goto out2;
+
+	ret = di->bus.write(di, BQ27XXX_BLOCK_DATA_CHECKSUM,
+			    bq27xxx_battery_checksum(buf), true);
+	if (ret < 0)
+		goto out2;
+
+	/* THE FOLLOWING CODE IS TOXIC. DO NOT USE!
+	 * If the 350ms delay is insufficient, NVM corruption results
+	 * on the '425 chip, which could damage the chip.
+	 * It was suggested in this TI tool:
+	 *   http://git.ti.com/bms-linux/bqtool/blobs/master/gauge.c#line328
+	 *
+	 * BQ27XXX_MSLEEP(350);
+	 * ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, buf->block, true);
+	 * BQ27XXX_MSLEEP(1);
+	 * ret = di->bus.read(di, BQ27XXX_BLOCK_DATA_CHECKSUM, true);
+	 * if ((u8)ret != bq27xxx_battery_checksum(buf))
+	 * 	ret = -EINVAL;
+	 */
+
+	if (cfgup) {
+		BQ27XXX_MSLEEP(1);
+		ret = di->bus.write(di, BQ27XXX_CONTROL, BQ27XXX_SOFT_RESET, false);
+		if (ret < 0)
+			goto out1;
+	}
+
+	buf->updt = false;
+	return 0;
+
+out2:
+	if (cfgup)
+		di->bus.write(di, BQ27XXX_CONTROL, BQ27XXX_SOFT_RESET, false);
+out1:
+	dev_err(di->dev, "bus error in %s: %d\n", __func__, 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_ENERGY);
+	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);
+		if (bd.full) {
+			/* 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.full && bd.class == bt.class && bd.block == bt.block;
+		if (!same)
+			bq27xxx_battery_read_dm_block(di, &bt);
+		if (same ? bd.full : bt.full)
+			bq27xxx_battery_update_dm_block(di, same ? &bd : &bt,
+					BQ27XXX_DM_TERMINATE_VOLTAGE,
+					info->voltage_min_design_uv / 1000);
+	}
+
+	if (bd.updt)
+		bq27xxx_battery_write_dm_block(di, &bd);
+	if (bt.updt)
+		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_print_config(di);
+	bq27xxx_battery_set_seal_state(di, true);
+}
+
 /*
  * Return the battery State-of-Charge
  * Or < 0 if something fails.
@@ -1006,6 +1390,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 +1430,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 +1458,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] 34+ messages in thread

* [PATCH v7 8/9] power: bq27xxx_battery: Add print_dm() to log chip memory
  2017-02-21 21:29 [PATCH v7 0/9] devicetree battery support and client bq27xxx_battery Liam Breck
                   ` (5 preceding siblings ...)
  2017-02-21 21:30 ` [PATCH v7 7/9] power: bq27xxx_battery: Add power_supply_battery_info support Liam Breck
@ 2017-02-21 21:30 ` Liam Breck
  2017-02-22 20:39   ` Andrew F. Davis
  2017-02-21 21:30 ` [PATCH v7 9/9] power: bq27xxx_battery_i2c: Add I2C bulk read/write functions Liam Breck
  2017-03-02 22:52 ` [PATCH v7 0/9] devicetree battery support and client bq27xxx_battery Liam Breck
  8 siblings, 1 reply; 34+ messages in thread
From: Liam Breck @ 2017-02-21 21:30 UTC (permalink / raw)
  To: Sebastian Reichel; +Cc: Andrew F. Davis, linux-pm, Matt Ranostay, Liam Breck

From: Liam Breck <kernel@networkimprov.net>

Enable listing of chip RAM and/or NVM at startup, for debugging use.
Intially supports BQ27425 State NVM, subclass 82.
Support for other chips may be added.

Signed-off-by: Liam Breck <kernel@networkimprov.net>
---
 drivers/power/supply/bq27xxx_battery.c | 51 ++++++++++++++++++++++++++++++++++
 1 file changed, 51 insertions(+)

diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
index 8085d4a..bfb3aa9 100644
--- a/drivers/power/supply/bq27xxx_battery.c
+++ b/drivers/power/supply/bq27xxx_battery.c
@@ -664,6 +664,55 @@ static int bq27xxx_battery_read_dm_block(struct bq27xxx_device_info *di,
 	return ret;
 }
 
+/* Translate f4 floating point values to/from hexadecimal:
+ *   perl -e 'printf("%08x\n", unpack("I", pack("f", 3.93e-4   )))' # 39ce0b91
+ *   perl -e 'printf("%f\n"  , unpack("f", pack("I", 0x39ce0b91)))' # 0.000393
+ */
+
+#define BQ27XXX_REG(f,o,v) dev_info(di->dev, "offset %d, " f "\n", o, v)
+
+static void bq27xxx_battery_print_dm(struct bq27xxx_device_info *di) {
+	enum { h1, h2, i2, u1, f4 };
+
+	struct dm_reg { int offset, type; }
+		/* each array row describes one data block */
+		c425_s82[][10] = {
+			{ { 2,h1},{ 3,i2},{ 5,h2},{12,i2},{14,i2},{18,i2},{22,i2},{29,u1},{30,i2},{99,0} },
+			{ {32,i2},{34,i2},{36,i2},{38,u1},{39,u1},{40,f4},{99,0} },
+		};
+		/* add more subclass maps here */
+
+	struct dm_class { int id, len; struct dm_reg (*reg)[10]; }
+		c425[] = {
+			{ .id = 82, .len = 2, .reg = c425_s82 },
+		};
+		/* add more chip maps here */
+
+	struct dm_class *chip = c425; /* set to your chip */
+
+	struct bq27xxx_dm_buf buf = { };
+	int c, b, r;
+	for (c=0; c < 1; ++c) {
+		buf.class = chip[c].id;
+		dev_info(di->dev, "subclass %d registers...\n", chip[c].id);
+		for (b=0; b < chip[c].len; ++b) {
+			buf.block = b;
+			bq27xxx_battery_read_dm_block(di, &buf);
+			for (r=0; chip[c].reg[b][r].offset != 99; ++r) {
+				int o = chip[c].reg[b][r].offset;
+				u8* p = &buf.a[o % sizeof buf.a];
+				switch (chip[c].reg[b][r].type) {
+				case h1: BQ27XXX_REG("%02x", o,      *p); break;
+				case h2: BQ27XXX_REG("%04x", o,      be16_to_cpup((u16*)p)); break;
+				case i2: BQ27XXX_REG("%d",   o, (s16)be16_to_cpup((u16*)p)); break;
+				case u1: BQ27XXX_REG("%u",   o,      *p); break;
+				case f4: BQ27XXX_REG("%08x", o,      be32_to_cpup((u32*)p)); break;
+				}
+			}
+		}
+	}
+}
+
 static void bq27xxx_battery_print_config(struct bq27xxx_device_info *di)
 {
 	struct bq27xxx_dm_reg *reg = bq27xxx_dm_regs[di->chip];
@@ -683,6 +732,8 @@ static void bq27xxx_battery_print_config(struct bq27xxx_device_info *di)
 		else
 			dev_warn(di->dev, "unsupported config register %s\n", str);
 	}
+	/* bq27xxx_battery_print_dm(di); uncomment for debugging */
+	(void)bq27xxx_battery_print_dm; /* prevent compiler warning */
 }
 
 static void bq27xxx_battery_update_dm_block(struct bq27xxx_device_info *di,
-- 
2.9.3

^ permalink raw reply related	[flat|nested] 34+ messages in thread

* [PATCH v7 9/9] power: bq27xxx_battery_i2c: Add I2C bulk read/write functions
  2017-02-21 21:29 [PATCH v7 0/9] devicetree battery support and client bq27xxx_battery Liam Breck
                   ` (6 preceding siblings ...)
  2017-02-21 21:30 ` [PATCH v7 8/9] power: bq27xxx_battery: Add print_dm() to log chip memory Liam Breck
@ 2017-02-21 21:30 ` Liam Breck
  2017-03-02 22:52 ` [PATCH v7 0/9] devicetree battery support and client bq27xxx_battery Liam Breck
  8 siblings, 0 replies; 34+ messages in thread
From: Liam Breck @ 2017-02-21 21:30 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 d7da535..0414573 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] 34+ messages in thread

* Re: [PATCH v7 1/9] devicetree: power: Add battery.txt
       [not found]     ` <20170221213008.30044-2-liam-RYWXG+zxWwBdeoIcmNTgJF6hYfS7NtTn@public.gmane.org>
@ 2017-02-22 20:05       ` Andrew F. Davis
       [not found]         ` <bd747560-8780-3294-86dd-f7f7f4de1736-l0cyMroinI0@public.gmane.org>
  0 siblings, 1 reply; 34+ messages in thread
From: Andrew F. Davis @ 2017-02-22 20:05 UTC (permalink / raw)
  To: Liam Breck, Sebastian Reichel
  Cc: linux-pm-u79uwXL29TY76Z2rM5mHXA, Matt Ranostay, Rob Herring,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Liam Breck

On 02/21/2017 03:30 PM, Liam Breck wrote:
> 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   | 42 ++++++++++++++++++++++
>  1 file changed, 42 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..3d916bd
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/power/supply/battery.txt
> @@ -0,0 +1,42 @@
> +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
> +
> +Future 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>;
> +	};
> +

Still not sure what a non-fixed-battery would be. I guess it would be a
battery with an integrated fuel-gauge pre-programmed with this design
info. So then the charger node would want to point to a fuel-gauge node,
and the fuel-gauge can optionally point to a fixed-battery if that info
is not pre-programmable.

The issues then is for chargers with an integrated fuel-gauge, or
systems without a fuel-gauge, that may get a bit messy, so I have no
issues with this current binding.

> +	charger: charger@11 {
> +		....
> +		monitored-battery = <&bat>;
> +		...
> +	};
> +
> +	fuel_gauge: fuel-gauge@22 {
> +		....
> +		monitored-battery = <&bat>;
> +		...
> +	};
> 
--
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	[flat|nested] 34+ messages in thread

* Re: [PATCH v7 6/9] power: bq27xxx_battery: Add BQ27425 chip id
  2017-02-21 21:30 ` [PATCH v7 6/9] power: bq27xxx_battery: Add BQ27425 chip id Liam Breck
@ 2017-02-22 20:15   ` Andrew F. Davis
  0 siblings, 0 replies; 34+ messages in thread
From: Andrew F. Davis @ 2017-02-22 20:15 UTC (permalink / raw)
  To: Liam Breck, Sebastian Reichel; +Cc: linux-pm, Matt Ranostay, Liam Breck

On 02/21/2017 03:30 PM, Liam Breck wrote:
> From: Matt Ranostay <matt@ranostay.consulting>
> 
> This patch does not alter the function of the driver.
> This chip ID is referenced in the devicetree code.
> 
> Signed-off-by: Matt Ranostay <matt@ranostay.consulting>
> Signed-off-by: Liam Breck <kernel@networkimprov.net>

Looks inline with the datasheet,

Acked-by: Andrew F. Davis <afd@ti.com>

> ---
>  drivers/power/supply/bq27xxx_battery.c     | 23 ++++++++++++++++++++++-
>  drivers/power/supply/bq27xxx_battery_i2c.c |  2 +-
>  include/linux/power/bq27xxx_battery.h      |  3 ++-
>  3 files changed, 25 insertions(+), 3 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..d7da535 100644
> --- a/drivers/power/supply/bq27xxx_battery_i2c.c
> +++ b/drivers/power/supply/bq27xxx_battery_i2c.c
> @@ -159,7 +159,7 @@ static const struct i2c_device_id bq27xxx_i2c_id_table[] = {
>  	{ "bq27742", BQ27541 },
>  	{ "bq27545", BQ27545 },
>  	{ "bq27421", BQ27421 },
> -	{ "bq27425", BQ27421 },
> +	{ "bq27425", BQ27425 },
>  	{ "bq27441", BQ27421 },
>  	{ "bq27621", BQ27421 },
>  	{},
> diff --git a/include/linux/power/bq27xxx_battery.h b/include/linux/power/bq27xxx_battery.h
> index 92df553..4925478 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, bq27441, bq27621 */
> +	BQ27425, /* bq27425 */
>  };
>  
>  /**
> 

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v7 7/9] power: bq27xxx_battery: Add power_supply_battery_info support
  2017-02-21 21:30 ` [PATCH v7 7/9] power: bq27xxx_battery: Add power_supply_battery_info support Liam Breck
@ 2017-02-22 20:35   ` Andrew F. Davis
  2017-02-22 21:29     ` Liam Breck
  2017-02-23 21:15     ` Liam Breck
  0 siblings, 2 replies; 34+ messages in thread
From: Andrew F. Davis @ 2017-02-22 20:35 UTC (permalink / raw)
  To: Liam Breck, Sebastian Reichel; +Cc: linux-pm, Matt Ranostay, Liam Breck

On 02/21/2017 03:30 PM, Liam Breck wrote:
> 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 | 399 ++++++++++++++++++++++++++++++++-
>  1 file changed, 397 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
> index 7475a5f..8085d4a 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"
>  
> @@ -452,6 +452,99 @@ static struct {
>  static DEFINE_MUTEX(bq27xxx_list_lock);
>  static LIST_HEAD(bq27xxx_battery_devices);
>  
> +/* writable registers */
> +#define BQ27XXX_CONTROL			0x00
> +#define BQ27XXX_DATA_CLASS		0x3E
> +#define BQ27XXX_DATA_BLOCK		0x3F
> +#define BQ27XXX_BLOCK_DATA		0x40
> +#define BQ27XXX_BLOCK_DATA_CHECKSUM	0x60
> +#define BQ27XXX_BLOCK_DATA_CONTROL	0x61
> +

These all belong in the bq27xxx_regs array, you can add new register
types to the bq27xxx_reg_index enum.

> +/* control register params */
> +#define BQ27XXX_SEALED			0x20
> +#define BQ27XXX_SET_CFGUPDATE		0x13
> +#define BQ27XXX_SOFT_RESET		0x42
> +
> +#define BQ27XXX_MSLEEP(i) usleep_range((i)*1000, (i)*1000+500)

why?

> +
> +struct bq27xxx_dm_reg {
> +	u8 subclass_id;
> +	u8 offset;
> +	u8 bytes;
> +	u16 min, max;
> +};
> +
> +struct bq27xxx_dm_buf {
> +	u8 class;
> +	u8 block;
> +	u8 a[32];
> +	bool full, updt;
> +};
> +
> +#define BQ27XXX_DM_BUF_PTR(b, r) \
> +	( (u16*) &(b).a[ (r)->offset % sizeof (b).a ] )
> +

This could compete in the IOCCC, there has to be a more readable way to
do this..

> +#define BQ27XXX_DM_BUF(di, i) { \
> +	.class = bq27xxx_dm_regs[(di)->chip][i].subclass_id, \
> +	.block = bq27xxx_dm_regs[(di)->chip][i].offset / sizeof ((struct bq27xxx_dm_buf*)0)->a, \
> +}
> +
> +static inline bool bq27xxx_dm_buf_set(struct bq27xxx_dm_buf *buf,
> +				      struct bq27xxx_dm_reg *reg) {
> +	if (buf->class == reg->subclass_id
> +	 && buf->block == reg->offset / sizeof buf->a
> +	 && buf->full)

Some alignment issues here.

> +		return false;
> +	buf->class = reg->subclass_id;
> +	buf->block = reg->offset / sizeof buf->a;
> +	buf->full = buf->updt = false;

newline before return

> +	return true;
> +}
> +
> +enum bq27xxx_dm_reg_id {
> +	BQ27XXX_DM_DESIGN_CAPACITY = 0,
> +	BQ27XXX_DM_DESIGN_ENERGY,
> +	BQ27XXX_DM_TERMINATE_VOLTAGE,
> +	BQ27XXX_DM_END,
> +};
> +
> +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 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 bq27421_dm_regs[] = { /* not tested */
> +	[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 },
> +};
> +
> +static struct bq27xxx_dm_reg bq27621_dm_regs[] = { /* not tested */
> +	[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[] = {
> +	[BQ27421] = bq27421_dm_regs, /* and BQ27441 */
> +	[BQ27425] = bq27425_dm_regs,
> +/*	[BQ27621] = */ bq27621_dm_regs,
> +};
> +
> +static u32 bq27xxx_unseal_keys[] = {
> +	[BQ27421] = 0x80008000, /* and BQ27441 */
> +	[BQ27425] = 0x04143672,
> +/*	[BQ27621] = */ 0x80008000,

Why is this commented out.

> +};
> +
> +
>  static int poll_interval_param_set(const char *val, const struct kernel_param *kp)
>  {
>  	struct bq27xxx_device_info *di;
> @@ -496,6 +589,297 @@ 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)
> +{
> +	u32 key = bq27xxx_unseal_keys[di->chip];
> +	int ret;
> +
> +	if (state) {
> +		ret = di->bus.write(di, BQ27XXX_CONTROL, BQ27XXX_SEALED, false);
> +		if (ret < 0)
> +			goto out;
> +	} else {
> +		ret = di->bus.write(di, BQ27XXX_CONTROL, (u16)(key >> 16), false);
> +		if (ret < 0)
> +			goto out;
> +
> +		ret = di->bus.write(di, BQ27XXX_CONTROL, (u16)key, false);
> +		if (ret < 0)
> +			goto out;
> +	}
> +	return 0;
> +
> +out:
> +	dev_err(di->dev, "bus error in %s: %d\n", __func__, ret);

This is not a debug statement, we don't need the function name, just say
where it broke.

> +	return ret;
> +}
> +
> +static u8 bq27xxx_battery_checksum(struct bq27xxx_dm_buf *buf)
> +{
> +	u16 sum = 0;
> +	int i;
> +
> +	for (i = 0; i < sizeof buf->a; i++)

Linus would like a word with you:
https://lkml.org/lkml/2012/7/11/103

> +		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, BQ27XXX_DATA_CLASS, buf->class, true);
> +	if (ret < 0)
> +		goto out;
> +
> +	ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, buf->block, true);
> +	if (ret < 0)
> +		goto out;
> +
> +	BQ27XXX_MSLEEP(1);
> +
> +	ret = di->bus.read_bulk(di, BQ27XXX_BLOCK_DATA, buf->a, sizeof buf->a);
> +	if (ret < 0)
> +		goto out;
> +
> +	ret = di->bus.read(di, BQ27XXX_BLOCK_DATA_CHECKSUM, true);
> +	if (ret < 0)
> +		goto out;
> +
> +	if ((u8)ret != bq27xxx_battery_checksum(buf)) {
> +		ret = -EINVAL;
> +		goto out;
> +	}
> +
> +	buf->full = true;
> +	buf->updt = false;
> +	return 0;
> +
> +out:
> +	dev_err(di->dev, "bus error in %s: %d\n", __func__, ret);
> +	return ret;
> +}
> +
> +static void bq27xxx_battery_print_config(struct bq27xxx_device_info *di)
> +{
> +	struct bq27xxx_dm_reg *reg = bq27xxx_dm_regs[di->chip];
> +	struct bq27xxx_dm_buf buf = { .class = 0xFF };
> +	int i;
> +
> +	for (i = 0; i < BQ27XXX_DM_END; i++, reg++) {
> +		const char* str = bq27xxx_dm_reg_name[i];
> +
> +		if (bq27xxx_dm_buf_set(&buf, reg))
> +			if (bq27xxx_battery_read_dm_block(di, &buf) < 0)
> +				continue;
> +
> +		if (reg->bytes == 2)
> +			dev_info(di->dev, "config register %s is %d\n", str,
> +				 be16_to_cpup(BQ27XXX_DM_BUF_PTR(buf, reg)));
> +		else
> +			dev_warn(di->dev, "unsupported config register %s\n", str);
> +	}
> +}
> +
> +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];
> +	u16 *prev;
> +
> +	if (reg->bytes != 2)
> +		return;
> +
> +	prev = BQ27XXX_DM_BUF_PTR(*buf, reg);
> +	if (be16_to_cpup(prev) == val)
> +		return;
> +	*prev = cpu_to_be16(val);
> +
> +	dev_info(di->dev, "update chip %d, class %u, block %u, offset %u, value %u\n",
> +		 di->chip, buf->class, buf->block, reg->offset, val);
> +

This should probably be dev_debug, no normal user cares about all this
detail.

> +	buf->updt = true;
> +	return;
> +}
> +
> +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 (cfgup) {
> +		ret = di->bus.write(di, BQ27XXX_CONTROL, BQ27XXX_SET_CFGUPDATE, false);
> +		if (ret < 0)
> +			goto out1;
> +	}
> +
> +	ret = di->bus.write(di, BQ27XXX_BLOCK_DATA_CONTROL, 0, true);
> +	if (ret < 0)
> +		goto out2;
> +
> +	ret = di->bus.write(di, BQ27XXX_DATA_CLASS, buf->class, true);
> +	if (ret < 0)
> +		goto out2;
> +
> +	ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, buf->block, true);
> +	if (ret < 0)
> +		goto out2;
> +
> +	BQ27XXX_MSLEEP(1);
> +
> +	ret = di->bus.write_bulk(di, BQ27XXX_BLOCK_DATA, buf->a, sizeof buf->a);
> +	if (ret < 0)
> +		goto out2;
> +
> +	ret = di->bus.write(di, BQ27XXX_BLOCK_DATA_CHECKSUM,
> +			    bq27xxx_battery_checksum(buf), true);
> +	if (ret < 0)
> +		goto out2;
> +
> +	/* THE FOLLOWING CODE IS TOXIC. DO NOT USE!

Then why include it, what is this code supposed to do, and what do we
lose without it? If it is really needed we can find a way to guarantee
the delay length.

> +	 * If the 350ms delay is insufficient, NVM corruption results
> +	 * on the '425 chip, which could damage the chip.
> +	 * It was suggested in this TI tool:
> +	 *   http://git.ti.com/bms-linux/bqtool/blobs/master/gauge.c#line328
> +	 *
> +	 * BQ27XXX_MSLEEP(350);
> +	 * ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, buf->block, true);
> +	 * BQ27XXX_MSLEEP(1);
> +	 * ret = di->bus.read(di, BQ27XXX_BLOCK_DATA_CHECKSUM, true);
> +	 * if ((u8)ret != bq27xxx_battery_checksum(buf))
> +	 * 	ret = -EINVAL;
> +	 */
> +
> +	if (cfgup) {
> +		BQ27XXX_MSLEEP(1);
> +		ret = di->bus.write(di, BQ27XXX_CONTROL, BQ27XXX_SOFT_RESET, false);
> +		if (ret < 0)
> +			goto out1;
> +	}
> +
> +	buf->updt = false;
> +	return 0;
> +
> +out2:
> +	if (cfgup)
> +		di->bus.write(di, BQ27XXX_CONTROL, BQ27XXX_SOFT_RESET, false);
> +out1:
> +	dev_err(di->dev, "bus error in %s: %d\n", __func__, 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_ENERGY);
> +	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);
> +		if (bd.full) {
> +			/* 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.full && bd.class == bt.class && bd.block == bt.block;
> +		if (!same)
> +			bq27xxx_battery_read_dm_block(di, &bt);
> +		if (same ? bd.full : bt.full)
> +			bq27xxx_battery_update_dm_block(di, same ? &bd : &bt,
> +					BQ27XXX_DM_TERMINATE_VOLTAGE,
> +					info->voltage_min_design_uv / 1000);
> +	}
> +
> +	if (bd.updt)
> +		bq27xxx_battery_write_dm_block(di, &bd);
> +	if (bt.updt)
> +		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_print_config(di);
> +	bq27xxx_battery_set_seal_state(di, true);
> +}
> +
>  /*
>   * Return the battery State-of-Charge
>   * Or < 0 if something fails.
> @@ -1006,6 +1390,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 +1430,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 +1458,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);
> 

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v7 8/9] power: bq27xxx_battery: Add print_dm() to log chip memory
  2017-02-21 21:30 ` [PATCH v7 8/9] power: bq27xxx_battery: Add print_dm() to log chip memory Liam Breck
@ 2017-02-22 20:39   ` Andrew F. Davis
  2017-02-22 21:36     ` Liam Breck
  0 siblings, 1 reply; 34+ messages in thread
From: Andrew F. Davis @ 2017-02-22 20:39 UTC (permalink / raw)
  To: Liam Breck, Sebastian Reichel; +Cc: linux-pm, Matt Ranostay, Liam Breck

On 02/21/2017 03:30 PM, Liam Breck wrote:
> From: Liam Breck <kernel@networkimprov.net>
> 

Do you prefer kernel@ or liam@, you should probably pick one and use it
uniformly.

This patch is nice for debugging, but not upstreamable, it is full of
checkpatch and formatting issues, it should probably just be dropped
from this series.

> Enable listing of chip RAM and/or NVM at startup, for debugging use.
> Intially supports BQ27425 State NVM, subclass 82.
> Support for other chips may be added.
> 
> Signed-off-by: Liam Breck <kernel@networkimprov.net>
> ---
>  drivers/power/supply/bq27xxx_battery.c | 51 ++++++++++++++++++++++++++++++++++
>  1 file changed, 51 insertions(+)
> 
> diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
> index 8085d4a..bfb3aa9 100644
> --- a/drivers/power/supply/bq27xxx_battery.c
> +++ b/drivers/power/supply/bq27xxx_battery.c
> @@ -664,6 +664,55 @@ static int bq27xxx_battery_read_dm_block(struct bq27xxx_device_info *di,
>  	return ret;
>  }
>  
> +/* Translate f4 floating point values to/from hexadecimal:
> + *   perl -e 'printf("%08x\n", unpack("I", pack("f", 3.93e-4   )))' # 39ce0b91
> + *   perl -e 'printf("%f\n"  , unpack("f", pack("I", 0x39ce0b91)))' # 0.000393
> + */
> +
> +#define BQ27XXX_REG(f,o,v) dev_info(di->dev, "offset %d, " f "\n", o, v)
> +
> +static void bq27xxx_battery_print_dm(struct bq27xxx_device_info *di) {
> +	enum { h1, h2, i2, u1, f4 };
> +
> +	struct dm_reg { int offset, type; }
> +		/* each array row describes one data block */
> +		c425_s82[][10] = {
> +			{ { 2,h1},{ 3,i2},{ 5,h2},{12,i2},{14,i2},{18,i2},{22,i2},{29,u1},{30,i2},{99,0} },
> +			{ {32,i2},{34,i2},{36,i2},{38,u1},{39,u1},{40,f4},{99,0} },
> +		};
> +		/* add more subclass maps here */
> +
> +	struct dm_class { int id, len; struct dm_reg (*reg)[10]; }
> +		c425[] = {
> +			{ .id = 82, .len = 2, .reg = c425_s82 },
> +		};
> +		/* add more chip maps here */
> +
> +	struct dm_class *chip = c425; /* set to your chip */
> +
> +	struct bq27xxx_dm_buf buf = { };
> +	int c, b, r;
> +	for (c=0; c < 1; ++c) {
> +		buf.class = chip[c].id;
> +		dev_info(di->dev, "subclass %d registers...\n", chip[c].id);
> +		for (b=0; b < chip[c].len; ++b) {
> +			buf.block = b;
> +			bq27xxx_battery_read_dm_block(di, &buf);
> +			for (r=0; chip[c].reg[b][r].offset != 99; ++r) {
> +				int o = chip[c].reg[b][r].offset;
> +				u8* p = &buf.a[o % sizeof buf.a];
> +				switch (chip[c].reg[b][r].type) {
> +				case h1: BQ27XXX_REG("%02x", o,      *p); break;
> +				case h2: BQ27XXX_REG("%04x", o,      be16_to_cpup((u16*)p)); break;
> +				case i2: BQ27XXX_REG("%d",   o, (s16)be16_to_cpup((u16*)p)); break;
> +				case u1: BQ27XXX_REG("%u",   o,      *p); break;
> +				case f4: BQ27XXX_REG("%08x", o,      be32_to_cpup((u32*)p)); break;
> +				}
> +			}
> +		}
> +	}
> +}
> +
>  static void bq27xxx_battery_print_config(struct bq27xxx_device_info *di)
>  {
>  	struct bq27xxx_dm_reg *reg = bq27xxx_dm_regs[di->chip];
> @@ -683,6 +732,8 @@ static void bq27xxx_battery_print_config(struct bq27xxx_device_info *di)
>  		else
>  			dev_warn(di->dev, "unsupported config register %s\n", str);
>  	}
> +	/* bq27xxx_battery_print_dm(di); uncomment for debugging */
> +	(void)bq27xxx_battery_print_dm; /* prevent compiler warning */
>  }
>  
>  static void bq27xxx_battery_update_dm_block(struct bq27xxx_device_info *di,
> 

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v7 7/9] power: bq27xxx_battery: Add power_supply_battery_info support
  2017-02-22 20:35   ` Andrew F. Davis
@ 2017-02-22 21:29     ` Liam Breck
  2017-02-23  0:30       ` Andrew F. Davis
  2017-02-23 21:15     ` Liam Breck
  1 sibling, 1 reply; 34+ messages in thread
From: Liam Breck @ 2017-02-22 21:29 UTC (permalink / raw)
  To: Andrew F. Davis; +Cc: Sebastian Reichel, linux-pm, Matt Ranostay

On Wed, Feb 22, 2017 at 12:35 PM, Andrew F. Davis <afd@ti.com> wrote:
> On 02/21/2017 03:30 PM, Liam Breck wrote:
>> 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 | 399 ++++++++++++++++++++++++++++++++-
>>  1 file changed, 397 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
>> index 7475a5f..8085d4a 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"
>>
>> @@ -452,6 +452,99 @@ static struct {
>>  static DEFINE_MUTEX(bq27xxx_list_lock);
>>  static LIST_HEAD(bq27xxx_battery_devices);
>>
>> +/* writable registers */
>> +#define BQ27XXX_CONTROL                      0x00
>> +#define BQ27XXX_DATA_CLASS           0x3E
>> +#define BQ27XXX_DATA_BLOCK           0x3F
>> +#define BQ27XXX_BLOCK_DATA           0x40
>> +#define BQ27XXX_BLOCK_DATA_CHECKSUM  0x60
>> +#define BQ27XXX_BLOCK_DATA_CONTROL   0x61
>> +
>
> These all belong in the bq27xxx_regs array, you can add new register
> types to the bq27xxx_reg_index enum.

They're the same on all chips. Let's dup this code 9+ times when we have cause.

Or make a default array of reg numbers and memcopy it onto each chip
array, then alter as req'd per chip. But that's outside our scope.

>> +/* control register params */
>> +#define BQ27XXX_SEALED                       0x20
>> +#define BQ27XXX_SET_CFGUPDATE                0x13
>> +#define BQ27XXX_SOFT_RESET           0x42
>> +
>> +#define BQ27XXX_MSLEEP(i) usleep_range((i)*1000, (i)*1000+500)
>
> why?

I think this might vary on diff chips. Also I have a pending change
which uses MSLEEP(20)

>> +
>> +struct bq27xxx_dm_reg {
>> +     u8 subclass_id;
>> +     u8 offset;
>> +     u8 bytes;
>> +     u16 min, max;
>> +};
>> +
>> +struct bq27xxx_dm_buf {
>> +     u8 class;
>> +     u8 block;
>> +     u8 a[32];
>> +     bool full, updt;
>> +};
>> +
>> +#define BQ27XXX_DM_BUF_PTR(b, r) \
>> +     ( (u16*) &(b).a[ (r)->offset % sizeof (b).a ] )
>> +
>
> This could compete in the IOCCC, there has to be a more readable way to
> do this..

Hence the macro. It didn't seem to merit a function, but I could write
an inline fn.


>> +#define BQ27XXX_DM_BUF(di, i) { \
>> +     .class = bq27xxx_dm_regs[(di)->chip][i].subclass_id, \
>> +     .block = bq27xxx_dm_regs[(di)->chip][i].offset / sizeof ((struct bq27xxx_dm_buf*)0)->a, \
>> +}
>> +
>> +static inline bool bq27xxx_dm_buf_set(struct bq27xxx_dm_buf *buf,
>> +                                   struct bq27xxx_dm_reg *reg) {
>> +     if (buf->class == reg->subclass_id
>> +      && buf->block == reg->offset / sizeof buf->a
>> +      && buf->full)
>
> Some alignment issues here.
>
>> +             return false;
>> +     buf->class = reg->subclass_id;
>> +     buf->block = reg->offset / sizeof buf->a;
>> +     buf->full = buf->updt = false;
>
> newline before return
>
>> +     return true;
>> +}
>> +
>> +enum bq27xxx_dm_reg_id {
>> +     BQ27XXX_DM_DESIGN_CAPACITY = 0,
>> +     BQ27XXX_DM_DESIGN_ENERGY,
>> +     BQ27XXX_DM_TERMINATE_VOLTAGE,
>> +     BQ27XXX_DM_END,
>> +};
>> +
>> +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 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 bq27421_dm_regs[] = { /* not tested */
>> +     [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 },
>> +};
>> +
>> +static struct bq27xxx_dm_reg bq27621_dm_regs[] = { /* not tested */
>> +     [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[] = {
>> +     [BQ27421] = bq27421_dm_regs, /* and BQ27441 */
>> +     [BQ27425] = bq27425_dm_regs,
>> +/*   [BQ27621] = */ bq27621_dm_regs,
>> +};
>> +
>> +static u32 bq27xxx_unseal_keys[] = {
>> +     [BQ27421] = 0x80008000, /* and BQ27441 */
>> +     [BQ27425] = 0x04143672,
>> +/*   [BQ27621] = */ 0x80008000,
>
> Why is this commented out.

The 621 id isn't defined yet. I didn't want to enable something I can't test.

>> +};
>> +
>> +
>>  static int poll_interval_param_set(const char *val, const struct kernel_param *kp)
>>  {
>>       struct bq27xxx_device_info *di;
>> @@ -496,6 +589,297 @@ 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)
>> +{
>> +     u32 key = bq27xxx_unseal_keys[di->chip];
>> +     int ret;
>> +
>> +     if (state) {
>> +             ret = di->bus.write(di, BQ27XXX_CONTROL, BQ27XXX_SEALED, false);
>> +             if (ret < 0)
>> +                     goto out;
>> +     } else {
>> +             ret = di->bus.write(di, BQ27XXX_CONTROL, (u16)(key >> 16), false);
>> +             if (ret < 0)
>> +                     goto out;
>> +
>> +             ret = di->bus.write(di, BQ27XXX_CONTROL, (u16)key, false);
>> +             if (ret < 0)
>> +                     goto out;
>> +     }
>> +     return 0;
>> +
>> +out:
>> +     dev_err(di->dev, "bus error in %s: %d\n", __func__, ret);
>
> This is not a debug statement, we don't need the function name, just say
> where it broke.

What's "where" if not fn name?

>> +     return ret;
>> +}
>> +
>> +static u8 bq27xxx_battery_checksum(struct bq27xxx_dm_buf *buf)
>> +{
>> +     u16 sum = 0;
>> +     int i;
>> +
>> +     for (i = 0; i < sizeof buf->a; i++)
>
> Linus would like a word with you:
> https://lkml.org/lkml/2012/7/11/103
>
>> +             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, BQ27XXX_DATA_CLASS, buf->class, true);
>> +     if (ret < 0)
>> +             goto out;
>> +
>> +     ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, buf->block, true);
>> +     if (ret < 0)
>> +             goto out;
>> +
>> +     BQ27XXX_MSLEEP(1);
>> +
>> +     ret = di->bus.read_bulk(di, BQ27XXX_BLOCK_DATA, buf->a, sizeof buf->a);
>> +     if (ret < 0)
>> +             goto out;
>> +
>> +     ret = di->bus.read(di, BQ27XXX_BLOCK_DATA_CHECKSUM, true);
>> +     if (ret < 0)
>> +             goto out;
>> +
>> +     if ((u8)ret != bq27xxx_battery_checksum(buf)) {
>> +             ret = -EINVAL;
>> +             goto out;
>> +     }
>> +
>> +     buf->full = true;
>> +     buf->updt = false;
>> +     return 0;
>> +
>> +out:
>> +     dev_err(di->dev, "bus error in %s: %d\n", __func__, ret);
>> +     return ret;
>> +}
>> +
>> +static void bq27xxx_battery_print_config(struct bq27xxx_device_info *di)
>> +{
>> +     struct bq27xxx_dm_reg *reg = bq27xxx_dm_regs[di->chip];
>> +     struct bq27xxx_dm_buf buf = { .class = 0xFF };
>> +     int i;
>> +
>> +     for (i = 0; i < BQ27XXX_DM_END; i++, reg++) {
>> +             const char* str = bq27xxx_dm_reg_name[i];
>> +
>> +             if (bq27xxx_dm_buf_set(&buf, reg))
>> +                     if (bq27xxx_battery_read_dm_block(di, &buf) < 0)
>> +                             continue;
>> +
>> +             if (reg->bytes == 2)
>> +                     dev_info(di->dev, "config register %s is %d\n", str,
>> +                              be16_to_cpup(BQ27XXX_DM_BUF_PTR(buf, reg)));
>> +             else
>> +                     dev_warn(di->dev, "unsupported config register %s\n", str);
>> +     }
>> +}
>> +
>> +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];
>> +     u16 *prev;
>> +
>> +     if (reg->bytes != 2)
>> +             return;
>> +
>> +     prev = BQ27XXX_DM_BUF_PTR(*buf, reg);
>> +     if (be16_to_cpup(prev) == val)
>> +             return;
>> +     *prev = cpu_to_be16(val);
>> +
>> +     dev_info(di->dev, "update chip %d, class %u, block %u, offset %u, value %u\n",
>> +              di->chip, buf->class, buf->block, reg->offset, val);
>> +
>
> This should probably be dev_debug, no normal user cares about all this
> detail.

It's only on change to NVM, which a hw developer (like me) wants to
know about. I plan to remove the print_config routine so this is not
redundant.

>> +     buf->updt = true;
>> +     return;
>> +}
>> +
>> +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 (cfgup) {
>> +             ret = di->bus.write(di, BQ27XXX_CONTROL, BQ27XXX_SET_CFGUPDATE, false);
>> +             if (ret < 0)
>> +                     goto out1;
>> +     }
>> +
>> +     ret = di->bus.write(di, BQ27XXX_BLOCK_DATA_CONTROL, 0, true);
>> +     if (ret < 0)
>> +             goto out2;
>> +
>> +     ret = di->bus.write(di, BQ27XXX_DATA_CLASS, buf->class, true);
>> +     if (ret < 0)
>> +             goto out2;
>> +
>> +     ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, buf->block, true);
>> +     if (ret < 0)
>> +             goto out2;
>> +
>> +     BQ27XXX_MSLEEP(1);
>> +
>> +     ret = di->bus.write_bulk(di, BQ27XXX_BLOCK_DATA, buf->a, sizeof buf->a);
>> +     if (ret < 0)
>> +             goto out2;
>> +
>> +     ret = di->bus.write(di, BQ27XXX_BLOCK_DATA_CHECKSUM,
>> +                         bq27xxx_battery_checksum(buf), true);
>> +     if (ret < 0)
>> +             goto out2;
>> +
>> +     /* THE FOLLOWING CODE IS TOXIC. DO NOT USE!
>
> Then why include it, what is this code supposed to do, and what do we
> lose without it? If it is really needed we can find a way to guarantee
> the delay length.

This triggers a chip bug, and future devs need to know to avoid it. I
was tempted by bqtool into using it for the extra error check, and now
my gauge isn't working, tho I have only reset one NVM block back to
factory defaults. Bqtool is easy to find, if poorly written.

>> +      * If the 350ms delay is insufficient, NVM corruption results
>> +      * on the '425 chip, which could damage the chip.
>> +      * It was suggested in this TI tool:
>> +      *   http://git.ti.com/bms-linux/bqtool/blobs/master/gauge.c#line328
>> +      *
>> +      * BQ27XXX_MSLEEP(350);
>> +      * ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, buf->block, true);
>> +      * BQ27XXX_MSLEEP(1);
>> +      * ret = di->bus.read(di, BQ27XXX_BLOCK_DATA_CHECKSUM, true);
>> +      * if ((u8)ret != bq27xxx_battery_checksum(buf))
>> +      *      ret = -EINVAL;
>> +      */
>> +
>> +     if (cfgup) {
>> +             BQ27XXX_MSLEEP(1);
>> +             ret = di->bus.write(di, BQ27XXX_CONTROL, BQ27XXX_SOFT_RESET, false);
>> +             if (ret < 0)
>> +                     goto out1;
>> +     }
>> +
>> +     buf->updt = false;
>> +     return 0;
>> +
>> +out2:
>> +     if (cfgup)
>> +             di->bus.write(di, BQ27XXX_CONTROL, BQ27XXX_SOFT_RESET, false);
>> +out1:
>> +     dev_err(di->dev, "bus error in %s: %d\n", __func__, 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_ENERGY);
>> +     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);
>> +             if (bd.full) {
>> +                     /* 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.full && bd.class == bt.class && bd.block == bt.block;
>> +             if (!same)
>> +                     bq27xxx_battery_read_dm_block(di, &bt);
>> +             if (same ? bd.full : bt.full)
>> +                     bq27xxx_battery_update_dm_block(di, same ? &bd : &bt,
>> +                                     BQ27XXX_DM_TERMINATE_VOLTAGE,
>> +                                     info->voltage_min_design_uv / 1000);
>> +     }
>> +
>> +     if (bd.updt)
>> +             bq27xxx_battery_write_dm_block(di, &bd);
>> +     if (bt.updt)
>> +             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_print_config(di);
>> +     bq27xxx_battery_set_seal_state(di, true);
>> +}
>> +
>>  /*
>>   * Return the battery State-of-Charge
>>   * Or < 0 if something fails.
>> @@ -1006,6 +1390,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 +1430,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 +1458,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);
>>

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v7 8/9] power: bq27xxx_battery: Add print_dm() to log chip memory
  2017-02-22 20:39   ` Andrew F. Davis
@ 2017-02-22 21:36     ` Liam Breck
  2017-02-23  0:34       ` Andrew F. Davis
  0 siblings, 1 reply; 34+ messages in thread
From: Liam Breck @ 2017-02-22 21:36 UTC (permalink / raw)
  To: Andrew F. Davis; +Cc: Sebastian Reichel, linux-pm, Matt Ranostay, Liam Breck

On Wed, Feb 22, 2017 at 12:39 PM, Andrew F. Davis <afd@ti.com> wrote:
> On 02/21/2017 03:30 PM, Liam Breck wrote:
>> From: Liam Breck <kernel@networkimprov.net>
>>
>
> Do you prefer kernel@ or liam@, you should probably pick one and use it
> uniformly.
>
> This patch is nice for debugging, but not upstreamable, it is full of
> checkpatch and formatting issues, it should probably just be dropped
> from this series.

Your hw developer customers will want this.

It's up to you whether every line of code in bq27xxx must fit the
format guidelines. I can adjust the format. Or leave it in commented
out?

>> Enable listing of chip RAM and/or NVM at startup, for debugging use.
>> Intially supports BQ27425 State NVM, subclass 82.
>> Support for other chips may be added.
>>
>> Signed-off-by: Liam Breck <kernel@networkimprov.net>
>> ---
>>  drivers/power/supply/bq27xxx_battery.c | 51 ++++++++++++++++++++++++++++++++++
>>  1 file changed, 51 insertions(+)
>>
>> diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
>> index 8085d4a..bfb3aa9 100644
>> --- a/drivers/power/supply/bq27xxx_battery.c
>> +++ b/drivers/power/supply/bq27xxx_battery.c
>> @@ -664,6 +664,55 @@ static int bq27xxx_battery_read_dm_block(struct bq27xxx_device_info *di,
>>       return ret;
>>  }
>>
>> +/* Translate f4 floating point values to/from hexadecimal:
>> + *   perl -e 'printf("%08x\n", unpack("I", pack("f", 3.93e-4   )))' # 39ce0b91
>> + *   perl -e 'printf("%f\n"  , unpack("f", pack("I", 0x39ce0b91)))' # 0.000393
>> + */
>> +
>> +#define BQ27XXX_REG(f,o,v) dev_info(di->dev, "offset %d, " f "\n", o, v)
>> +
>> +static void bq27xxx_battery_print_dm(struct bq27xxx_device_info *di) {
>> +     enum { h1, h2, i2, u1, f4 };
>> +
>> +     struct dm_reg { int offset, type; }
>> +             /* each array row describes one data block */
>> +             c425_s82[][10] = {
>> +                     { { 2,h1},{ 3,i2},{ 5,h2},{12,i2},{14,i2},{18,i2},{22,i2},{29,u1},{30,i2},{99,0} },
>> +                     { {32,i2},{34,i2},{36,i2},{38,u1},{39,u1},{40,f4},{99,0} },
>> +             };
>> +             /* add more subclass maps here */
>> +
>> +     struct dm_class { int id, len; struct dm_reg (*reg)[10]; }
>> +             c425[] = {
>> +                     { .id = 82, .len = 2, .reg = c425_s82 },
>> +             };
>> +             /* add more chip maps here */
>> +
>> +     struct dm_class *chip = c425; /* set to your chip */
>> +
>> +     struct bq27xxx_dm_buf buf = { };
>> +     int c, b, r;
>> +     for (c=0; c < 1; ++c) {
>> +             buf.class = chip[c].id;
>> +             dev_info(di->dev, "subclass %d registers...\n", chip[c].id);
>> +             for (b=0; b < chip[c].len; ++b) {
>> +                     buf.block = b;
>> +                     bq27xxx_battery_read_dm_block(di, &buf);
>> +                     for (r=0; chip[c].reg[b][r].offset != 99; ++r) {
>> +                             int o = chip[c].reg[b][r].offset;
>> +                             u8* p = &buf.a[o % sizeof buf.a];
>> +                             switch (chip[c].reg[b][r].type) {
>> +                             case h1: BQ27XXX_REG("%02x", o,      *p); break;
>> +                             case h2: BQ27XXX_REG("%04x", o,      be16_to_cpup((u16*)p)); break;
>> +                             case i2: BQ27XXX_REG("%d",   o, (s16)be16_to_cpup((u16*)p)); break;
>> +                             case u1: BQ27XXX_REG("%u",   o,      *p); break;
>> +                             case f4: BQ27XXX_REG("%08x", o,      be32_to_cpup((u32*)p)); break;
>> +                             }
>> +                     }
>> +             }
>> +     }
>> +}
>> +
>>  static void bq27xxx_battery_print_config(struct bq27xxx_device_info *di)
>>  {
>>       struct bq27xxx_dm_reg *reg = bq27xxx_dm_regs[di->chip];
>> @@ -683,6 +732,8 @@ static void bq27xxx_battery_print_config(struct bq27xxx_device_info *di)
>>               else
>>                       dev_warn(di->dev, "unsupported config register %s\n", str);
>>       }
>> +     /* bq27xxx_battery_print_dm(di); uncomment for debugging */
>> +     (void)bq27xxx_battery_print_dm; /* prevent compiler warning */
>>  }
>>
>>  static void bq27xxx_battery_update_dm_block(struct bq27xxx_device_info *di,
>>

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v7 1/9] devicetree: power: Add battery.txt
       [not found]         ` <bd747560-8780-3294-86dd-f7f7f4de1736-l0cyMroinI0@public.gmane.org>
@ 2017-02-22 21:39           ` Liam Breck
  0 siblings, 0 replies; 34+ messages in thread
From: Liam Breck @ 2017-02-22 21:39 UTC (permalink / raw)
  To: Andrew F. Davis
  Cc: Sebastian Reichel, linux-pm-u79uwXL29TY76Z2rM5mHXA,
	Matt Ranostay, Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA,
	Liam Breck

On Wed, Feb 22, 2017 at 12:05 PM, Andrew F. Davis <afd-l0cyMroinI0@public.gmane.org> wrote:
> On 02/21/2017 03:30 PM, Liam Breck wrote:
>> 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   | 42 ++++++++++++++++++++++
>>  1 file changed, 42 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..3d916bd
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/power/supply/battery.txt
>> @@ -0,0 +1,42 @@
>> +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
>> +
>> +Future 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>;
>> +     };
>> +
>
> Still not sure what a non-fixed-battery would be. I guess it would be a
> battery with an integrated fuel-gauge pre-programmed with this design
> info. So then the charger node would want to point to a fuel-gauge node,
> and the fuel-gauge can optionally point to a fixed-battery if that info
> is not pre-programmable.

Sebastian wondered about a chain from charger thru gauge to battery,
but there are battery characteristics the gauge won't know about, e.g.
pre-charge and post-charge current, which is usually derived from
battery capacity.

> The issues then is for chargers with an integrated fuel-gauge, or
> systems without a fuel-gauge, that may get a bit messy, so I have no
> issues with this current binding.
>
>> +     charger: charger@11 {
>> +             ....
>> +             monitored-battery = <&bat>;
>> +             ...
>> +     };
>> +
>> +     fuel_gauge: fuel-gauge@22 {
>> +             ....
>> +             monitored-battery = <&bat>;
>> +             ...
>> +     };
>>
--
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	[flat|nested] 34+ messages in thread

* Re: [PATCH v7 7/9] power: bq27xxx_battery: Add power_supply_battery_info support
  2017-02-22 21:29     ` Liam Breck
@ 2017-02-23  0:30       ` Andrew F. Davis
  2017-02-23  0:54         ` Liam Breck
  0 siblings, 1 reply; 34+ messages in thread
From: Andrew F. Davis @ 2017-02-23  0:30 UTC (permalink / raw)
  To: Liam Breck; +Cc: Sebastian Reichel, linux-pm, Matt Ranostay

On 02/22/2017 03:29 PM, Liam Breck wrote:
> On Wed, Feb 22, 2017 at 12:35 PM, Andrew F. Davis <afd@ti.com> wrote:
>> On 02/21/2017 03:30 PM, Liam Breck wrote:
>>> 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 | 399 ++++++++++++++++++++++++++++++++-
>>>  1 file changed, 397 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
>>> index 7475a5f..8085d4a 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"
>>>
>>> @@ -452,6 +452,99 @@ static struct {
>>>  static DEFINE_MUTEX(bq27xxx_list_lock);
>>>  static LIST_HEAD(bq27xxx_battery_devices);
>>>
>>> +/* writable registers */
>>> +#define BQ27XXX_CONTROL                      0x00
>>> +#define BQ27XXX_DATA_CLASS           0x3E
>>> +#define BQ27XXX_DATA_BLOCK           0x3F
>>> +#define BQ27XXX_BLOCK_DATA           0x40
>>> +#define BQ27XXX_BLOCK_DATA_CHECKSUM  0x60
>>> +#define BQ27XXX_BLOCK_DATA_CONTROL   0x61
>>> +
>>
>> These all belong in the bq27xxx_regs array, you can add new register
>> types to the bq27xxx_reg_index enum.
> 
> They're the same on all chips. Let's dup this code 9+ times when we have cause.
> 

They are not the same on all chips, some chips don't have these
registers at all. Plus, BQ27XXX_CONTROL is the same for all chips, and
we already have it in all the chips bq27xxx_regs.

> Or make a default array of reg numbers and memcopy it onto each chip
> array, then alter as req'd per chip. But that's outside our scope.
> 
>>> +/* control register params */
>>> +#define BQ27XXX_SEALED                       0x20
>>> +#define BQ27XXX_SET_CFGUPDATE                0x13
>>> +#define BQ27XXX_SOFT_RESET           0x42
>>> +
>>> +#define BQ27XXX_MSLEEP(i) usleep_range((i)*1000, (i)*1000+500)
>>
>> why?
> 
> I think this might vary on diff chips. Also I have a pending change
> which uses MSLEEP(20)
> 

If they are different we should delay for the largest needed time, a
range means some may not get enough delay, we would always be targeting
the lowest common denominator chip anyway.

>>> +
>>> +struct bq27xxx_dm_reg {
>>> +     u8 subclass_id;
>>> +     u8 offset;
>>> +     u8 bytes;
>>> +     u16 min, max;
>>> +};
>>> +
>>> +struct bq27xxx_dm_buf {
>>> +     u8 class;
>>> +     u8 block;
>>> +     u8 a[32];
>>> +     bool full, updt;
>>> +};
>>> +
>>> +#define BQ27XXX_DM_BUF_PTR(b, r) \
>>> +     ( (u16*) &(b).a[ (r)->offset % sizeof (b).a ] )
>>> +
>>
>> This could compete in the IOCCC, there has to be a more readable way to
>> do this..
> 
> Hence the macro. It didn't seem to merit a function, but I could write
> an inline fn.
> 

That would be nice. With C, the whole kernel could be one long line, but
just because you can doesn't mean you should, lets keep this readable
for all of our sake :)

> 
>>> +#define BQ27XXX_DM_BUF(di, i) { \
>>> +     .class = bq27xxx_dm_regs[(di)->chip][i].subclass_id, \
>>> +     .block = bq27xxx_dm_regs[(di)->chip][i].offset / sizeof ((struct bq27xxx_dm_buf*)0)->a, \
>>> +}
>>> +
>>> +static inline bool bq27xxx_dm_buf_set(struct bq27xxx_dm_buf *buf,
>>> +                                   struct bq27xxx_dm_reg *reg) {
>>> +     if (buf->class == reg->subclass_id
>>> +      && buf->block == reg->offset / sizeof buf->a
>>> +      && buf->full)
>>
>> Some alignment issues here.
>>
>>> +             return false;
>>> +     buf->class = reg->subclass_id;
>>> +     buf->block = reg->offset / sizeof buf->a;
>>> +     buf->full = buf->updt = false;
>>
>> newline before return
>>
>>> +     return true;
>>> +}
>>> +
>>> +enum bq27xxx_dm_reg_id {
>>> +     BQ27XXX_DM_DESIGN_CAPACITY = 0,
>>> +     BQ27XXX_DM_DESIGN_ENERGY,
>>> +     BQ27XXX_DM_TERMINATE_VOLTAGE,
>>> +     BQ27XXX_DM_END,
>>> +};
>>> +
>>> +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 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 bq27421_dm_regs[] = { /* not tested */
>>> +     [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 },
>>> +};
>>> +
>>> +static struct bq27xxx_dm_reg bq27621_dm_regs[] = { /* not tested */
>>> +     [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[] = {
>>> +     [BQ27421] = bq27421_dm_regs, /* and BQ27441 */
>>> +     [BQ27425] = bq27425_dm_regs,
>>> +/*   [BQ27621] = */ bq27621_dm_regs,
>>> +};
>>> +
>>> +static u32 bq27xxx_unseal_keys[] = {
>>> +     [BQ27421] = 0x80008000, /* and BQ27441 */
>>> +     [BQ27425] = 0x04143672,
>>> +/*   [BQ27621] = */ 0x80008000,
>>
>> Why is this commented out.
> 
> The 621 id isn't defined yet. I didn't want to enable something I can't test.
> 

Yes, but you are only commenting out the index, it still is added to the
list. You should wait to add that line when the rest of BQ27621 support
is added.

>>> +};
>>> +
>>> +
>>>  static int poll_interval_param_set(const char *val, const struct kernel_param *kp)
>>>  {
>>>       struct bq27xxx_device_info *di;
>>> @@ -496,6 +589,297 @@ 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)
>>> +{
>>> +     u32 key = bq27xxx_unseal_keys[di->chip];
>>> +     int ret;
>>> +
>>> +     if (state) {
>>> +             ret = di->bus.write(di, BQ27XXX_CONTROL, BQ27XXX_SEALED, false);
>>> +             if (ret < 0)
>>> +                     goto out;
>>> +     } else {
>>> +             ret = di->bus.write(di, BQ27XXX_CONTROL, (u16)(key >> 16), false);
>>> +             if (ret < 0)
>>> +                     goto out;
>>> +
>>> +             ret = di->bus.write(di, BQ27XXX_CONTROL, (u16)key, false);
>>> +             if (ret < 0)
>>> +                     goto out;
>>> +     }
>>> +     return 0;
>>> +
>>> +out:
>>> +     dev_err(di->dev, "bus error in %s: %d\n", __func__, ret);
>>
>> This is not a debug statement, we don't need the function name, just say
>> where it broke.
> 
> What's "where" if not fn name?
> 

For example:

"Bus error while setting seal state"

The actually function name is unimportant, and the user doesn't care.

>>> +     return ret;
>>> +}
>>> +
>>> +static u8 bq27xxx_battery_checksum(struct bq27xxx_dm_buf *buf)
>>> +{
>>> +     u16 sum = 0;
>>> +     int i;
>>> +
>>> +     for (i = 0; i < sizeof buf->a; i++)
>>
>> Linus would like a word with you:
>> https://lkml.org/lkml/2012/7/11/103
>>
>>> +             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, BQ27XXX_DATA_CLASS, buf->class, true);
>>> +     if (ret < 0)
>>> +             goto out;
>>> +
>>> +     ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, buf->block, true);
>>> +     if (ret < 0)
>>> +             goto out;
>>> +
>>> +     BQ27XXX_MSLEEP(1);
>>> +
>>> +     ret = di->bus.read_bulk(di, BQ27XXX_BLOCK_DATA, buf->a, sizeof buf->a);
>>> +     if (ret < 0)
>>> +             goto out;
>>> +
>>> +     ret = di->bus.read(di, BQ27XXX_BLOCK_DATA_CHECKSUM, true);
>>> +     if (ret < 0)
>>> +             goto out;
>>> +
>>> +     if ((u8)ret != bq27xxx_battery_checksum(buf)) {
>>> +             ret = -EINVAL;
>>> +             goto out;
>>> +     }
>>> +
>>> +     buf->full = true;
>>> +     buf->updt = false;
>>> +     return 0;
>>> +
>>> +out:
>>> +     dev_err(di->dev, "bus error in %s: %d\n", __func__, ret);
>>> +     return ret;
>>> +}
>>> +
>>> +static void bq27xxx_battery_print_config(struct bq27xxx_device_info *di)
>>> +{
>>> +     struct bq27xxx_dm_reg *reg = bq27xxx_dm_regs[di->chip];
>>> +     struct bq27xxx_dm_buf buf = { .class = 0xFF };
>>> +     int i;
>>> +
>>> +     for (i = 0; i < BQ27XXX_DM_END; i++, reg++) {
>>> +             const char* str = bq27xxx_dm_reg_name[i];
>>> +
>>> +             if (bq27xxx_dm_buf_set(&buf, reg))
>>> +                     if (bq27xxx_battery_read_dm_block(di, &buf) < 0)
>>> +                             continue;
>>> +
>>> +             if (reg->bytes == 2)
>>> +                     dev_info(di->dev, "config register %s is %d\n", str,
>>> +                              be16_to_cpup(BQ27XXX_DM_BUF_PTR(buf, reg)));
>>> +             else
>>> +                     dev_warn(di->dev, "unsupported config register %s\n", str);
>>> +     }
>>> +}
>>> +
>>> +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];
>>> +     u16 *prev;
>>> +
>>> +     if (reg->bytes != 2)
>>> +             return;
>>> +
>>> +     prev = BQ27XXX_DM_BUF_PTR(*buf, reg);
>>> +     if (be16_to_cpup(prev) == val)
>>> +             return;
>>> +     *prev = cpu_to_be16(val);
>>> +
>>> +     dev_info(di->dev, "update chip %d, class %u, block %u, offset %u, value %u\n",
>>> +              di->chip, buf->class, buf->block, reg->offset, val);
>>> +
>>
>> This should probably be dev_debug, no normal user cares about all this
>> detail.
> 
> It's only on change to NVM, which a hw developer (like me) wants to
> know about. I plan to remove the print_config routine so this is not
> redundant.
> 

HW developers should then have debug enabled for drivers they care
about. For everyone else this is just kernel log clutter.

>>> +     buf->updt = true;
>>> +     return;
>>> +}
>>> +
>>> +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 (cfgup) {
>>> +             ret = di->bus.write(di, BQ27XXX_CONTROL, BQ27XXX_SET_CFGUPDATE, false);
>>> +             if (ret < 0)
>>> +                     goto out1;
>>> +     }
>>> +
>>> +     ret = di->bus.write(di, BQ27XXX_BLOCK_DATA_CONTROL, 0, true);
>>> +     if (ret < 0)
>>> +             goto out2;
>>> +
>>> +     ret = di->bus.write(di, BQ27XXX_DATA_CLASS, buf->class, true);
>>> +     if (ret < 0)
>>> +             goto out2;
>>> +
>>> +     ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, buf->block, true);
>>> +     if (ret < 0)
>>> +             goto out2;
>>> +
>>> +     BQ27XXX_MSLEEP(1);
>>> +
>>> +     ret = di->bus.write_bulk(di, BQ27XXX_BLOCK_DATA, buf->a, sizeof buf->a);
>>> +     if (ret < 0)
>>> +             goto out2;
>>> +
>>> +     ret = di->bus.write(di, BQ27XXX_BLOCK_DATA_CHECKSUM,
>>> +                         bq27xxx_battery_checksum(buf), true);
>>> +     if (ret < 0)
>>> +             goto out2;
>>> +
>>> +     /* THE FOLLOWING CODE IS TOXIC. DO NOT USE!
>>
>> Then why include it, what is this code supposed to do, and what do we
>> lose without it? If it is really needed we can find a way to guarantee
>> the delay length.
> 
> This triggers a chip bug, and future devs need to know to avoid it. I
> was tempted by bqtool into using it for the extra error check, and now
> my gauge isn't working, tho I have only reset one NVM block back to
> factory defaults. Bqtool is easy to find, if poorly written.
> 

Just the warning would be enough then, we don't gain anything by writing
out the exact code to break your device in the comment also.

>>> +      * If the 350ms delay is insufficient, NVM corruption results
>>> +      * on the '425 chip, which could damage the chip.
>>> +      * It was suggested in this TI tool:
>>> +      *   http://git.ti.com/bms-linux/bqtool/blobs/master/gauge.c#line328
>>> +      *
>>> +      * BQ27XXX_MSLEEP(350);
>>> +      * ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, buf->block, true);
>>> +      * BQ27XXX_MSLEEP(1);
>>> +      * ret = di->bus.read(di, BQ27XXX_BLOCK_DATA_CHECKSUM, true);
>>> +      * if ((u8)ret != bq27xxx_battery_checksum(buf))
>>> +      *      ret = -EINVAL;
>>> +      */
>>> +
>>> +     if (cfgup) {
>>> +             BQ27XXX_MSLEEP(1);
>>> +             ret = di->bus.write(di, BQ27XXX_CONTROL, BQ27XXX_SOFT_RESET, false);
>>> +             if (ret < 0)
>>> +                     goto out1;
>>> +     }
>>> +
>>> +     buf->updt = false;
>>> +     return 0;
>>> +
>>> +out2:
>>> +     if (cfgup)
>>> +             di->bus.write(di, BQ27XXX_CONTROL, BQ27XXX_SOFT_RESET, false);
>>> +out1:
>>> +     dev_err(di->dev, "bus error in %s: %d\n", __func__, 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_ENERGY);
>>> +     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);
>>> +             if (bd.full) {
>>> +                     /* 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.full && bd.class == bt.class && bd.block == bt.block;
>>> +             if (!same)
>>> +                     bq27xxx_battery_read_dm_block(di, &bt);
>>> +             if (same ? bd.full : bt.full)
>>> +                     bq27xxx_battery_update_dm_block(di, same ? &bd : &bt,
>>> +                                     BQ27XXX_DM_TERMINATE_VOLTAGE,
>>> +                                     info->voltage_min_design_uv / 1000);
>>> +     }
>>> +
>>> +     if (bd.updt)
>>> +             bq27xxx_battery_write_dm_block(di, &bd);
>>> +     if (bt.updt)
>>> +             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_print_config(di);
>>> +     bq27xxx_battery_set_seal_state(di, true);
>>> +}
>>> +
>>>  /*
>>>   * Return the battery State-of-Charge
>>>   * Or < 0 if something fails.
>>> @@ -1006,6 +1390,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 +1430,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 +1458,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);
>>>

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v7 8/9] power: bq27xxx_battery: Add print_dm() to log chip memory
  2017-02-22 21:36     ` Liam Breck
@ 2017-02-23  0:34       ` Andrew F. Davis
  0 siblings, 0 replies; 34+ messages in thread
From: Andrew F. Davis @ 2017-02-23  0:34 UTC (permalink / raw)
  To: Liam Breck; +Cc: Sebastian Reichel, linux-pm, Matt Ranostay, Liam Breck

On 02/22/2017 03:36 PM, Liam Breck wrote:
> On Wed, Feb 22, 2017 at 12:39 PM, Andrew F. Davis <afd@ti.com> wrote:
>> On 02/21/2017 03:30 PM, Liam Breck wrote:
>>> From: Liam Breck <kernel@networkimprov.net>
>>>
>>
>> Do you prefer kernel@ or liam@, you should probably pick one and use it
>> uniformly.
>>
>> This patch is nice for debugging, but not upstreamable, it is full of
>> checkpatch and formatting issues, it should probably just be dropped
>> from this series.
> 
> Your hw developer customers will want this.
> 

The kernel is not for hw developers, we have our evil vendor trees for
this stuff. That said, I have no problem with this here, but all the
print outs should at least dev_debug.

> It's up to you whether every line of code in bq27xxx must fit the
> format guidelines. I can adjust the format. Or leave it in commented
> out?
> 

It's not really up to me, Sebastian has the real final call, I would
just cleanup the format.

>>> Enable listing of chip RAM and/or NVM at startup, for debugging use.
>>> Intially supports BQ27425 State NVM, subclass 82.
>>> Support for other chips may be added.
>>>
>>> Signed-off-by: Liam Breck <kernel@networkimprov.net>
>>> ---
>>>  drivers/power/supply/bq27xxx_battery.c | 51 ++++++++++++++++++++++++++++++++++
>>>  1 file changed, 51 insertions(+)
>>>
>>> diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
>>> index 8085d4a..bfb3aa9 100644
>>> --- a/drivers/power/supply/bq27xxx_battery.c
>>> +++ b/drivers/power/supply/bq27xxx_battery.c
>>> @@ -664,6 +664,55 @@ static int bq27xxx_battery_read_dm_block(struct bq27xxx_device_info *di,
>>>       return ret;
>>>  }
>>>
>>> +/* Translate f4 floating point values to/from hexadecimal:
>>> + *   perl -e 'printf("%08x\n", unpack("I", pack("f", 3.93e-4   )))' # 39ce0b91
>>> + *   perl -e 'printf("%f\n"  , unpack("f", pack("I", 0x39ce0b91)))' # 0.000393
>>> + */
>>> +
>>> +#define BQ27XXX_REG(f,o,v) dev_info(di->dev, "offset %d, " f "\n", o, v)
>>> +
>>> +static void bq27xxx_battery_print_dm(struct bq27xxx_device_info *di) {
>>> +     enum { h1, h2, i2, u1, f4 };
>>> +
>>> +     struct dm_reg { int offset, type; }
>>> +             /* each array row describes one data block */
>>> +             c425_s82[][10] = {
>>> +                     { { 2,h1},{ 3,i2},{ 5,h2},{12,i2},{14,i2},{18,i2},{22,i2},{29,u1},{30,i2},{99,0} },
>>> +                     { {32,i2},{34,i2},{36,i2},{38,u1},{39,u1},{40,f4},{99,0} },
>>> +             };
>>> +             /* add more subclass maps here */
>>> +
>>> +     struct dm_class { int id, len; struct dm_reg (*reg)[10]; }
>>> +             c425[] = {
>>> +                     { .id = 82, .len = 2, .reg = c425_s82 },
>>> +             };
>>> +             /* add more chip maps here */
>>> +
>>> +     struct dm_class *chip = c425; /* set to your chip */
>>> +
>>> +     struct bq27xxx_dm_buf buf = { };
>>> +     int c, b, r;
>>> +     for (c=0; c < 1; ++c) {
>>> +             buf.class = chip[c].id;
>>> +             dev_info(di->dev, "subclass %d registers...\n", chip[c].id);
>>> +             for (b=0; b < chip[c].len; ++b) {
>>> +                     buf.block = b;
>>> +                     bq27xxx_battery_read_dm_block(di, &buf);
>>> +                     for (r=0; chip[c].reg[b][r].offset != 99; ++r) {
>>> +                             int o = chip[c].reg[b][r].offset;
>>> +                             u8* p = &buf.a[o % sizeof buf.a];
>>> +                             switch (chip[c].reg[b][r].type) {
>>> +                             case h1: BQ27XXX_REG("%02x", o,      *p); break;
>>> +                             case h2: BQ27XXX_REG("%04x", o,      be16_to_cpup((u16*)p)); break;
>>> +                             case i2: BQ27XXX_REG("%d",   o, (s16)be16_to_cpup((u16*)p)); break;
>>> +                             case u1: BQ27XXX_REG("%u",   o,      *p); break;
>>> +                             case f4: BQ27XXX_REG("%08x", o,      be32_to_cpup((u32*)p)); break;
>>> +                             }
>>> +                     }
>>> +             }
>>> +     }
>>> +}
>>> +
>>>  static void bq27xxx_battery_print_config(struct bq27xxx_device_info *di)
>>>  {
>>>       struct bq27xxx_dm_reg *reg = bq27xxx_dm_regs[di->chip];
>>> @@ -683,6 +732,8 @@ static void bq27xxx_battery_print_config(struct bq27xxx_device_info *di)
>>>               else
>>>                       dev_warn(di->dev, "unsupported config register %s\n", str);
>>>       }
>>> +     /* bq27xxx_battery_print_dm(di); uncomment for debugging */
>>> +     (void)bq27xxx_battery_print_dm; /* prevent compiler warning */
>>>  }
>>>
>>>  static void bq27xxx_battery_update_dm_block(struct bq27xxx_device_info *di,
>>>

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v7 7/9] power: bq27xxx_battery: Add power_supply_battery_info support
  2017-02-23  0:30       ` Andrew F. Davis
@ 2017-02-23  0:54         ` Liam Breck
  2017-02-23 15:17           ` Andrew F. Davis
  0 siblings, 1 reply; 34+ messages in thread
From: Liam Breck @ 2017-02-23  0:54 UTC (permalink / raw)
  To: Andrew F. Davis; +Cc: Sebastian Reichel, linux-pm, Matt Ranostay

On Wed, Feb 22, 2017 at 4:30 PM, Andrew F. Davis <afd@ti.com> wrote:
> On 02/22/2017 03:29 PM, Liam Breck wrote:
>> On Wed, Feb 22, 2017 at 12:35 PM, Andrew F. Davis <afd@ti.com> wrote:
>>> On 02/21/2017 03:30 PM, Liam Breck wrote:
>>>> 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 | 399 ++++++++++++++++++++++++++++++++-
>>>>  1 file changed, 397 insertions(+), 2 deletions(-)
>>>>
>>>> diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
>>>> index 7475a5f..8085d4a 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"
>>>>
>>>> @@ -452,6 +452,99 @@ static struct {
>>>>  static DEFINE_MUTEX(bq27xxx_list_lock);
>>>>  static LIST_HEAD(bq27xxx_battery_devices);
>>>>
>>>> +/* writable registers */
>>>> +#define BQ27XXX_CONTROL                      0x00
>>>> +#define BQ27XXX_DATA_CLASS           0x3E
>>>> +#define BQ27XXX_DATA_BLOCK           0x3F
>>>> +#define BQ27XXX_BLOCK_DATA           0x40
>>>> +#define BQ27XXX_BLOCK_DATA_CHECKSUM  0x60
>>>> +#define BQ27XXX_BLOCK_DATA_CONTROL   0x61
>>>> +
>>>
>>> These all belong in the bq27xxx_regs array, you can add new register
>>> types to the bq27xxx_reg_index enum.
>>
>> They're the same on all chips. Let's dup this code 9+ times when we have cause.
>>
>
> They are not the same on all chips, some chips don't have these
> registers at all. Plus, BQ27XXX_CONTROL is the same for all chips, and
> we already have it in all the chips bq27xxx_regs.

Datasheets? Do those chips even support this feature set? Bqtool
doesn't appear to support any other memory update scheme.

>> Or make a default array of reg numbers and memcopy it onto each chip
>> array, then alter as req'd per chip. But that's outside our scope.
>>
>>>> +/* control register params */
>>>> +#define BQ27XXX_SEALED                       0x20
>>>> +#define BQ27XXX_SET_CFGUPDATE                0x13
>>>> +#define BQ27XXX_SOFT_RESET           0x42
>>>> +
>>>> +#define BQ27XXX_MSLEEP(i) usleep_range((i)*1000, (i)*1000+500)
>>>
>>> why?
>>
>> I think this might vary on diff chips. Also I have a pending change
>> which uses MSLEEP(20)
>>
>
> If they are different we should delay for the largest needed time, a
> range means some may not get enough delay, we would always be targeting
> the lowest common denominator chip anyway.

That will be a task for someone else who has hw to test :-)

>>>> +
>>>> +struct bq27xxx_dm_reg {
>>>> +     u8 subclass_id;
>>>> +     u8 offset;
>>>> +     u8 bytes;
>>>> +     u16 min, max;
>>>> +};
>>>> +
>>>> +struct bq27xxx_dm_buf {
>>>> +     u8 class;
>>>> +     u8 block;
>>>> +     u8 a[32];
>>>> +     bool full, updt;
>>>> +};
>>>> +
>>>> +#define BQ27XXX_DM_BUF_PTR(b, r) \
>>>> +     ( (u16*) &(b).a[ (r)->offset % sizeof (b).a ] )
>>>> +
>>>
>>> This could compete in the IOCCC, there has to be a more readable way to
>>> do this..
>>
>> Hence the macro. It didn't seem to merit a function, but I could write
>> an inline fn.
>>
>
> That would be nice. With C, the whole kernel could be one long line, but
> just because you can doesn't mean you should, lets keep this readable
> for all of our sake :)
>
>>
>>>> +#define BQ27XXX_DM_BUF(di, i) { \
>>>> +     .class = bq27xxx_dm_regs[(di)->chip][i].subclass_id, \
>>>> +     .block = bq27xxx_dm_regs[(di)->chip][i].offset / sizeof ((struct bq27xxx_dm_buf*)0)->a, \
>>>> +}
>>>> +
>>>> +static inline bool bq27xxx_dm_buf_set(struct bq27xxx_dm_buf *buf,
>>>> +                                   struct bq27xxx_dm_reg *reg) {
>>>> +     if (buf->class == reg->subclass_id
>>>> +      && buf->block == reg->offset / sizeof buf->a
>>>> +      && buf->full)
>>>
>>> Some alignment issues here.
>>>
>>>> +             return false;
>>>> +     buf->class = reg->subclass_id;
>>>> +     buf->block = reg->offset / sizeof buf->a;
>>>> +     buf->full = buf->updt = false;
>>>
>>> newline before return
>>>
>>>> +     return true;
>>>> +}
>>>> +
>>>> +enum bq27xxx_dm_reg_id {
>>>> +     BQ27XXX_DM_DESIGN_CAPACITY = 0,
>>>> +     BQ27XXX_DM_DESIGN_ENERGY,
>>>> +     BQ27XXX_DM_TERMINATE_VOLTAGE,
>>>> +     BQ27XXX_DM_END,
>>>> +};
>>>> +
>>>> +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 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 bq27421_dm_regs[] = { /* not tested */
>>>> +     [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 },
>>>> +};
>>>> +
>>>> +static struct bq27xxx_dm_reg bq27621_dm_regs[] = { /* not tested */
>>>> +     [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[] = {
>>>> +     [BQ27421] = bq27421_dm_regs, /* and BQ27441 */
>>>> +     [BQ27425] = bq27425_dm_regs,
>>>> +/*   [BQ27621] = */ bq27621_dm_regs,
>>>> +};
>>>> +
>>>> +static u32 bq27xxx_unseal_keys[] = {
>>>> +     [BQ27421] = 0x80008000, /* and BQ27441 */
>>>> +     [BQ27425] = 0x04143672,
>>>> +/*   [BQ27621] = */ 0x80008000,
>>>
>>> Why is this commented out.
>>
>> The 621 id isn't defined yet. I didn't want to enable something I can't test.
>>
>
> Yes, but you are only commenting out the index, it still is added to the
> list. You should wait to add that line when the rest of BQ27621 support
> is added.
>
>>>> +};
>>>> +
>>>> +
>>>>  static int poll_interval_param_set(const char *val, const struct kernel_param *kp)
>>>>  {
>>>>       struct bq27xxx_device_info *di;
>>>> @@ -496,6 +589,297 @@ 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)
>>>> +{
>>>> +     u32 key = bq27xxx_unseal_keys[di->chip];
>>>> +     int ret;
>>>> +
>>>> +     if (state) {
>>>> +             ret = di->bus.write(di, BQ27XXX_CONTROL, BQ27XXX_SEALED, false);
>>>> +             if (ret < 0)
>>>> +                     goto out;
>>>> +     } else {
>>>> +             ret = di->bus.write(di, BQ27XXX_CONTROL, (u16)(key >> 16), false);
>>>> +             if (ret < 0)
>>>> +                     goto out;
>>>> +
>>>> +             ret = di->bus.write(di, BQ27XXX_CONTROL, (u16)key, false);
>>>> +             if (ret < 0)
>>>> +                     goto out;
>>>> +     }
>>>> +     return 0;
>>>> +
>>>> +out:
>>>> +     dev_err(di->dev, "bus error in %s: %d\n", __func__, ret);
>>>
>>> This is not a debug statement, we don't need the function name, just say
>>> where it broke.
>>
>> What's "where" if not fn name?
>>
>
> For example:
>
> "Bus error while setting seal state"
>
> The actually function name is unimportant, and the user doesn't care.
>
>>>> +     return ret;
>>>> +}
>>>> +
>>>> +static u8 bq27xxx_battery_checksum(struct bq27xxx_dm_buf *buf)
>>>> +{
>>>> +     u16 sum = 0;
>>>> +     int i;
>>>> +
>>>> +     for (i = 0; i < sizeof buf->a; i++)
>>>
>>> Linus would like a word with you:
>>> https://lkml.org/lkml/2012/7/11/103
>>>
>>>> +             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, BQ27XXX_DATA_CLASS, buf->class, true);
>>>> +     if (ret < 0)
>>>> +             goto out;
>>>> +
>>>> +     ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, buf->block, true);
>>>> +     if (ret < 0)
>>>> +             goto out;
>>>> +
>>>> +     BQ27XXX_MSLEEP(1);
>>>> +
>>>> +     ret = di->bus.read_bulk(di, BQ27XXX_BLOCK_DATA, buf->a, sizeof buf->a);
>>>> +     if (ret < 0)
>>>> +             goto out;
>>>> +
>>>> +     ret = di->bus.read(di, BQ27XXX_BLOCK_DATA_CHECKSUM, true);
>>>> +     if (ret < 0)
>>>> +             goto out;
>>>> +
>>>> +     if ((u8)ret != bq27xxx_battery_checksum(buf)) {
>>>> +             ret = -EINVAL;
>>>> +             goto out;
>>>> +     }
>>>> +
>>>> +     buf->full = true;
>>>> +     buf->updt = false;
>>>> +     return 0;
>>>> +
>>>> +out:
>>>> +     dev_err(di->dev, "bus error in %s: %d\n", __func__, ret);
>>>> +     return ret;
>>>> +}
>>>> +
>>>> +static void bq27xxx_battery_print_config(struct bq27xxx_device_info *di)
>>>> +{
>>>> +     struct bq27xxx_dm_reg *reg = bq27xxx_dm_regs[di->chip];
>>>> +     struct bq27xxx_dm_buf buf = { .class = 0xFF };
>>>> +     int i;
>>>> +
>>>> +     for (i = 0; i < BQ27XXX_DM_END; i++, reg++) {
>>>> +             const char* str = bq27xxx_dm_reg_name[i];
>>>> +
>>>> +             if (bq27xxx_dm_buf_set(&buf, reg))
>>>> +                     if (bq27xxx_battery_read_dm_block(di, &buf) < 0)
>>>> +                             continue;
>>>> +
>>>> +             if (reg->bytes == 2)
>>>> +                     dev_info(di->dev, "config register %s is %d\n", str,
>>>> +                              be16_to_cpup(BQ27XXX_DM_BUF_PTR(buf, reg)));
>>>> +             else
>>>> +                     dev_warn(di->dev, "unsupported config register %s\n", str);
>>>> +     }
>>>> +}
>>>> +
>>>> +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];
>>>> +     u16 *prev;
>>>> +
>>>> +     if (reg->bytes != 2)
>>>> +             return;
>>>> +
>>>> +     prev = BQ27XXX_DM_BUF_PTR(*buf, reg);
>>>> +     if (be16_to_cpup(prev) == val)
>>>> +             return;
>>>> +     *prev = cpu_to_be16(val);
>>>> +
>>>> +     dev_info(di->dev, "update chip %d, class %u, block %u, offset %u, value %u\n",
>>>> +              di->chip, buf->class, buf->block, reg->offset, val);
>>>> +
>>>
>>> This should probably be dev_debug, no normal user cares about all this
>>> detail.
>>
>> It's only on change to NVM, which a hw developer (like me) wants to
>> know about. I plan to remove the print_config routine so this is not
>> redundant.
>>
>
> HW developers should then have debug enabled for drivers they care
> about. For everyone else this is just kernel log clutter.
>
>>>> +     buf->updt = true;
>>>> +     return;
>>>> +}
>>>> +
>>>> +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 (cfgup) {
>>>> +             ret = di->bus.write(di, BQ27XXX_CONTROL, BQ27XXX_SET_CFGUPDATE, false);
>>>> +             if (ret < 0)
>>>> +                     goto out1;
>>>> +     }
>>>> +
>>>> +     ret = di->bus.write(di, BQ27XXX_BLOCK_DATA_CONTROL, 0, true);
>>>> +     if (ret < 0)
>>>> +             goto out2;
>>>> +
>>>> +     ret = di->bus.write(di, BQ27XXX_DATA_CLASS, buf->class, true);
>>>> +     if (ret < 0)
>>>> +             goto out2;
>>>> +
>>>> +     ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, buf->block, true);
>>>> +     if (ret < 0)
>>>> +             goto out2;
>>>> +
>>>> +     BQ27XXX_MSLEEP(1);
>>>> +
>>>> +     ret = di->bus.write_bulk(di, BQ27XXX_BLOCK_DATA, buf->a, sizeof buf->a);
>>>> +     if (ret < 0)
>>>> +             goto out2;
>>>> +
>>>> +     ret = di->bus.write(di, BQ27XXX_BLOCK_DATA_CHECKSUM,
>>>> +                         bq27xxx_battery_checksum(buf), true);
>>>> +     if (ret < 0)
>>>> +             goto out2;
>>>> +
>>>> +     /* THE FOLLOWING CODE IS TOXIC. DO NOT USE!
>>>
>>> Then why include it, what is this code supposed to do, and what do we
>>> lose without it? If it is really needed we can find a way to guarantee
>>> the delay length.
>>
>> This triggers a chip bug, and future devs need to know to avoid it. I
>> was tempted by bqtool into using it for the extra error check, and now
>> my gauge isn't working, tho I have only reset one NVM block back to
>> factory defaults. Bqtool is easy to find, if poorly written.
>>
>
> Just the warning would be enough then, we don't gain anything by writing
> out the exact code to break your device in the comment also.
>
>>>> +      * If the 350ms delay is insufficient, NVM corruption results
>>>> +      * on the '425 chip, which could damage the chip.
>>>> +      * It was suggested in this TI tool:
>>>> +      *   http://git.ti.com/bms-linux/bqtool/blobs/master/gauge.c#line328
>>>> +      *
>>>> +      * BQ27XXX_MSLEEP(350);
>>>> +      * ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, buf->block, true);
>>>> +      * BQ27XXX_MSLEEP(1);
>>>> +      * ret = di->bus.read(di, BQ27XXX_BLOCK_DATA_CHECKSUM, true);
>>>> +      * if ((u8)ret != bq27xxx_battery_checksum(buf))
>>>> +      *      ret = -EINVAL;
>>>> +      */
>>>> +
>>>> +     if (cfgup) {
>>>> +             BQ27XXX_MSLEEP(1);
>>>> +             ret = di->bus.write(di, BQ27XXX_CONTROL, BQ27XXX_SOFT_RESET, false);
>>>> +             if (ret < 0)
>>>> +                     goto out1;
>>>> +     }
>>>> +
>>>> +     buf->updt = false;
>>>> +     return 0;
>>>> +
>>>> +out2:
>>>> +     if (cfgup)
>>>> +             di->bus.write(di, BQ27XXX_CONTROL, BQ27XXX_SOFT_RESET, false);
>>>> +out1:
>>>> +     dev_err(di->dev, "bus error in %s: %d\n", __func__, 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_ENERGY);
>>>> +     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);
>>>> +             if (bd.full) {
>>>> +                     /* 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.full && bd.class == bt.class && bd.block == bt.block;
>>>> +             if (!same)
>>>> +                     bq27xxx_battery_read_dm_block(di, &bt);
>>>> +             if (same ? bd.full : bt.full)
>>>> +                     bq27xxx_battery_update_dm_block(di, same ? &bd : &bt,
>>>> +                                     BQ27XXX_DM_TERMINATE_VOLTAGE,
>>>> +                                     info->voltage_min_design_uv / 1000);
>>>> +     }
>>>> +
>>>> +     if (bd.updt)
>>>> +             bq27xxx_battery_write_dm_block(di, &bd);
>>>> +     if (bt.updt)
>>>> +             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_print_config(di);
>>>> +     bq27xxx_battery_set_seal_state(di, true);
>>>> +}
>>>> +
>>>>  /*
>>>>   * Return the battery State-of-Charge
>>>>   * Or < 0 if something fails.
>>>> @@ -1006,6 +1390,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 +1430,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 +1458,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);
>>>>

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v7 7/9] power: bq27xxx_battery: Add power_supply_battery_info support
  2017-02-23  0:54         ` Liam Breck
@ 2017-02-23 15:17           ` Andrew F. Davis
  2017-02-23 15:49             ` Liam Breck
  0 siblings, 1 reply; 34+ messages in thread
From: Andrew F. Davis @ 2017-02-23 15:17 UTC (permalink / raw)
  To: Liam Breck; +Cc: Sebastian Reichel, linux-pm, Matt Ranostay

On 02/22/2017 06:54 PM, Liam Breck wrote:
> On Wed, Feb 22, 2017 at 4:30 PM, Andrew F. Davis <afd@ti.com> wrote:
>> On 02/22/2017 03:29 PM, Liam Breck wrote:
>>> On Wed, Feb 22, 2017 at 12:35 PM, Andrew F. Davis <afd@ti.com> wrote:
>>>> On 02/21/2017 03:30 PM, Liam Breck wrote:
>>>>> 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 | 399 ++++++++++++++++++++++++++++++++-
>>>>>  1 file changed, 397 insertions(+), 2 deletions(-)
>>>>>
>>>>> diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
>>>>> index 7475a5f..8085d4a 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"
>>>>>
>>>>> @@ -452,6 +452,99 @@ static struct {
>>>>>  static DEFINE_MUTEX(bq27xxx_list_lock);
>>>>>  static LIST_HEAD(bq27xxx_battery_devices);
>>>>>
>>>>> +/* writable registers */
>>>>> +#define BQ27XXX_CONTROL                      0x00
>>>>> +#define BQ27XXX_DATA_CLASS           0x3E
>>>>> +#define BQ27XXX_DATA_BLOCK           0x3F
>>>>> +#define BQ27XXX_BLOCK_DATA           0x40
>>>>> +#define BQ27XXX_BLOCK_DATA_CHECKSUM  0x60
>>>>> +#define BQ27XXX_BLOCK_DATA_CONTROL   0x61
>>>>> +
>>>>
>>>> These all belong in the bq27xxx_regs array, you can add new register
>>>> types to the bq27xxx_reg_index enum.
>>>
>>> They're the same on all chips. Let's dup this code 9+ times when we have cause.
>>>
>>
>> They are not the same on all chips, some chips don't have these
>> registers at all. Plus, BQ27XXX_CONTROL is the same for all chips, and
>> we already have it in all the chips bq27xxx_regs.
> 
> Datasheets? Do those chips even support this feature set? Bqtool
> doesn't appear to support any other memory update scheme.
> 

Many chips don't, which is why we don't want these registers being
global defines, you can have the chips without these registers as
INVALID_REG_ADDR

>>> Or make a default array of reg numbers and memcopy it onto each chip
>>> array, then alter as req'd per chip. But that's outside our scope.
>>>
>>>>> +/* control register params */
>>>>> +#define BQ27XXX_SEALED                       0x20
>>>>> +#define BQ27XXX_SET_CFGUPDATE                0x13
>>>>> +#define BQ27XXX_SOFT_RESET           0x42
>>>>> +
>>>>> +#define BQ27XXX_MSLEEP(i) usleep_range((i)*1000, (i)*1000+500)
>>>>
>>>> why?
>>>
>>> I think this might vary on diff chips. Also I have a pending change
>>> which uses MSLEEP(20)
>>>
>>
>> If they are different we should delay for the largest needed time, a
>> range means some may not get enough delay, we would always be targeting
>> the lowest common denominator chip anyway.
> 
> That will be a task for someone else who has hw to test :-)
> 

Right, so pick a delay that works on the device you are adding, as other
devices are added they can be tested and new values can be chosen.

>>>>> +
>>>>> +struct bq27xxx_dm_reg {
>>>>> +     u8 subclass_id;
>>>>> +     u8 offset;
>>>>> +     u8 bytes;
>>>>> +     u16 min, max;
>>>>> +};
>>>>> +
>>>>> +struct bq27xxx_dm_buf {
>>>>> +     u8 class;
>>>>> +     u8 block;
>>>>> +     u8 a[32];
>>>>> +     bool full, updt;
>>>>> +};
>>>>> +
>>>>> +#define BQ27XXX_DM_BUF_PTR(b, r) \
>>>>> +     ( (u16*) &(b).a[ (r)->offset % sizeof (b).a ] )
>>>>> +
>>>>
>>>> This could compete in the IOCCC, there has to be a more readable way to
>>>> do this..
>>>
>>> Hence the macro. It didn't seem to merit a function, but I could write
>>> an inline fn.
>>>
>>
>> That would be nice. With C, the whole kernel could be one long line, but
>> just because you can doesn't mean you should, lets keep this readable
>> for all of our sake :)
>>
>>>
>>>>> +#define BQ27XXX_DM_BUF(di, i) { \
>>>>> +     .class = bq27xxx_dm_regs[(di)->chip][i].subclass_id, \
>>>>> +     .block = bq27xxx_dm_regs[(di)->chip][i].offset / sizeof ((struct bq27xxx_dm_buf*)0)->a, \
>>>>> +}
>>>>> +
>>>>> +static inline bool bq27xxx_dm_buf_set(struct bq27xxx_dm_buf *buf,
>>>>> +                                   struct bq27xxx_dm_reg *reg) {
>>>>> +     if (buf->class == reg->subclass_id
>>>>> +      && buf->block == reg->offset / sizeof buf->a
>>>>> +      && buf->full)
>>>>
>>>> Some alignment issues here.
>>>>
>>>>> +             return false;
>>>>> +     buf->class = reg->subclass_id;
>>>>> +     buf->block = reg->offset / sizeof buf->a;
>>>>> +     buf->full = buf->updt = false;
>>>>
>>>> newline before return
>>>>
>>>>> +     return true;
>>>>> +}
>>>>> +
>>>>> +enum bq27xxx_dm_reg_id {
>>>>> +     BQ27XXX_DM_DESIGN_CAPACITY = 0,
>>>>> +     BQ27XXX_DM_DESIGN_ENERGY,
>>>>> +     BQ27XXX_DM_TERMINATE_VOLTAGE,
>>>>> +     BQ27XXX_DM_END,
>>>>> +};
>>>>> +
>>>>> +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 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 bq27421_dm_regs[] = { /* not tested */
>>>>> +     [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 },
>>>>> +};
>>>>> +
>>>>> +static struct bq27xxx_dm_reg bq27621_dm_regs[] = { /* not tested */
>>>>> +     [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[] = {
>>>>> +     [BQ27421] = bq27421_dm_regs, /* and BQ27441 */
>>>>> +     [BQ27425] = bq27425_dm_regs,
>>>>> +/*   [BQ27621] = */ bq27621_dm_regs,
>>>>> +};
>>>>> +
>>>>> +static u32 bq27xxx_unseal_keys[] = {
>>>>> +     [BQ27421] = 0x80008000, /* and BQ27441 */
>>>>> +     [BQ27425] = 0x04143672,
>>>>> +/*   [BQ27621] = */ 0x80008000,
>>>>
>>>> Why is this commented out.
>>>
>>> The 621 id isn't defined yet. I didn't want to enable something I can't test.
>>>
>>
>> Yes, but you are only commenting out the index, it still is added to the
>> list. You should wait to add that line when the rest of BQ27621 support
>> is added.
>>
>>>>> +};
>>>>> +
>>>>> +
>>>>>  static int poll_interval_param_set(const char *val, const struct kernel_param *kp)
>>>>>  {
>>>>>       struct bq27xxx_device_info *di;
>>>>> @@ -496,6 +589,297 @@ 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)
>>>>> +{
>>>>> +     u32 key = bq27xxx_unseal_keys[di->chip];
>>>>> +     int ret;
>>>>> +
>>>>> +     if (state) {
>>>>> +             ret = di->bus.write(di, BQ27XXX_CONTROL, BQ27XXX_SEALED, false);
>>>>> +             if (ret < 0)
>>>>> +                     goto out;
>>>>> +     } else {
>>>>> +             ret = di->bus.write(di, BQ27XXX_CONTROL, (u16)(key >> 16), false);
>>>>> +             if (ret < 0)
>>>>> +                     goto out;
>>>>> +
>>>>> +             ret = di->bus.write(di, BQ27XXX_CONTROL, (u16)key, false);
>>>>> +             if (ret < 0)
>>>>> +                     goto out;
>>>>> +     }
>>>>> +     return 0;
>>>>> +
>>>>> +out:
>>>>> +     dev_err(di->dev, "bus error in %s: %d\n", __func__, ret);
>>>>
>>>> This is not a debug statement, we don't need the function name, just say
>>>> where it broke.
>>>
>>> What's "where" if not fn name?
>>>
>>
>> For example:
>>
>> "Bus error while setting seal state"
>>
>> The actually function name is unimportant, and the user doesn't care.
>>
>>>>> +     return ret;
>>>>> +}
>>>>> +
>>>>> +static u8 bq27xxx_battery_checksum(struct bq27xxx_dm_buf *buf)
>>>>> +{
>>>>> +     u16 sum = 0;
>>>>> +     int i;
>>>>> +
>>>>> +     for (i = 0; i < sizeof buf->a; i++)
>>>>
>>>> Linus would like a word with you:
>>>> https://lkml.org/lkml/2012/7/11/103
>>>>
>>>>> +             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, BQ27XXX_DATA_CLASS, buf->class, true);
>>>>> +     if (ret < 0)
>>>>> +             goto out;
>>>>> +
>>>>> +     ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, buf->block, true);
>>>>> +     if (ret < 0)
>>>>> +             goto out;
>>>>> +
>>>>> +     BQ27XXX_MSLEEP(1);
>>>>> +
>>>>> +     ret = di->bus.read_bulk(di, BQ27XXX_BLOCK_DATA, buf->a, sizeof buf->a);
>>>>> +     if (ret < 0)
>>>>> +             goto out;
>>>>> +
>>>>> +     ret = di->bus.read(di, BQ27XXX_BLOCK_DATA_CHECKSUM, true);
>>>>> +     if (ret < 0)
>>>>> +             goto out;
>>>>> +
>>>>> +     if ((u8)ret != bq27xxx_battery_checksum(buf)) {
>>>>> +             ret = -EINVAL;
>>>>> +             goto out;
>>>>> +     }
>>>>> +
>>>>> +     buf->full = true;
>>>>> +     buf->updt = false;
>>>>> +     return 0;
>>>>> +
>>>>> +out:
>>>>> +     dev_err(di->dev, "bus error in %s: %d\n", __func__, ret);
>>>>> +     return ret;
>>>>> +}
>>>>> +
>>>>> +static void bq27xxx_battery_print_config(struct bq27xxx_device_info *di)
>>>>> +{
>>>>> +     struct bq27xxx_dm_reg *reg = bq27xxx_dm_regs[di->chip];
>>>>> +     struct bq27xxx_dm_buf buf = { .class = 0xFF };
>>>>> +     int i;
>>>>> +
>>>>> +     for (i = 0; i < BQ27XXX_DM_END; i++, reg++) {
>>>>> +             const char* str = bq27xxx_dm_reg_name[i];
>>>>> +
>>>>> +             if (bq27xxx_dm_buf_set(&buf, reg))
>>>>> +                     if (bq27xxx_battery_read_dm_block(di, &buf) < 0)
>>>>> +                             continue;
>>>>> +
>>>>> +             if (reg->bytes == 2)
>>>>> +                     dev_info(di->dev, "config register %s is %d\n", str,
>>>>> +                              be16_to_cpup(BQ27XXX_DM_BUF_PTR(buf, reg)));
>>>>> +             else
>>>>> +                     dev_warn(di->dev, "unsupported config register %s\n", str);
>>>>> +     }
>>>>> +}
>>>>> +
>>>>> +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];
>>>>> +     u16 *prev;
>>>>> +
>>>>> +     if (reg->bytes != 2)
>>>>> +             return;
>>>>> +
>>>>> +     prev = BQ27XXX_DM_BUF_PTR(*buf, reg);
>>>>> +     if (be16_to_cpup(prev) == val)
>>>>> +             return;
>>>>> +     *prev = cpu_to_be16(val);
>>>>> +
>>>>> +     dev_info(di->dev, "update chip %d, class %u, block %u, offset %u, value %u\n",
>>>>> +              di->chip, buf->class, buf->block, reg->offset, val);
>>>>> +
>>>>
>>>> This should probably be dev_debug, no normal user cares about all this
>>>> detail.
>>>
>>> It's only on change to NVM, which a hw developer (like me) wants to
>>> know about. I plan to remove the print_config routine so this is not
>>> redundant.
>>>
>>
>> HW developers should then have debug enabled for drivers they care
>> about. For everyone else this is just kernel log clutter.
>>
>>>>> +     buf->updt = true;
>>>>> +     return;
>>>>> +}
>>>>> +
>>>>> +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 (cfgup) {
>>>>> +             ret = di->bus.write(di, BQ27XXX_CONTROL, BQ27XXX_SET_CFGUPDATE, false);
>>>>> +             if (ret < 0)
>>>>> +                     goto out1;
>>>>> +     }
>>>>> +
>>>>> +     ret = di->bus.write(di, BQ27XXX_BLOCK_DATA_CONTROL, 0, true);
>>>>> +     if (ret < 0)
>>>>> +             goto out2;
>>>>> +
>>>>> +     ret = di->bus.write(di, BQ27XXX_DATA_CLASS, buf->class, true);
>>>>> +     if (ret < 0)
>>>>> +             goto out2;
>>>>> +
>>>>> +     ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, buf->block, true);
>>>>> +     if (ret < 0)
>>>>> +             goto out2;
>>>>> +
>>>>> +     BQ27XXX_MSLEEP(1);
>>>>> +
>>>>> +     ret = di->bus.write_bulk(di, BQ27XXX_BLOCK_DATA, buf->a, sizeof buf->a);
>>>>> +     if (ret < 0)
>>>>> +             goto out2;
>>>>> +
>>>>> +     ret = di->bus.write(di, BQ27XXX_BLOCK_DATA_CHECKSUM,
>>>>> +                         bq27xxx_battery_checksum(buf), true);
>>>>> +     if (ret < 0)
>>>>> +             goto out2;
>>>>> +
>>>>> +     /* THE FOLLOWING CODE IS TOXIC. DO NOT USE!
>>>>
>>>> Then why include it, what is this code supposed to do, and what do we
>>>> lose without it? If it is really needed we can find a way to guarantee
>>>> the delay length.
>>>
>>> This triggers a chip bug, and future devs need to know to avoid it. I
>>> was tempted by bqtool into using it for the extra error check, and now
>>> my gauge isn't working, tho I have only reset one NVM block back to
>>> factory defaults. Bqtool is easy to find, if poorly written.
>>>
>>
>> Just the warning would be enough then, we don't gain anything by writing
>> out the exact code to break your device in the comment also.
>>
>>>>> +      * If the 350ms delay is insufficient, NVM corruption results
>>>>> +      * on the '425 chip, which could damage the chip.
>>>>> +      * It was suggested in this TI tool:
>>>>> +      *   http://git.ti.com/bms-linux/bqtool/blobs/master/gauge.c#line328
>>>>> +      *
>>>>> +      * BQ27XXX_MSLEEP(350);
>>>>> +      * ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, buf->block, true);
>>>>> +      * BQ27XXX_MSLEEP(1);
>>>>> +      * ret = di->bus.read(di, BQ27XXX_BLOCK_DATA_CHECKSUM, true);
>>>>> +      * if ((u8)ret != bq27xxx_battery_checksum(buf))
>>>>> +      *      ret = -EINVAL;
>>>>> +      */
>>>>> +
>>>>> +     if (cfgup) {
>>>>> +             BQ27XXX_MSLEEP(1);
>>>>> +             ret = di->bus.write(di, BQ27XXX_CONTROL, BQ27XXX_SOFT_RESET, false);
>>>>> +             if (ret < 0)
>>>>> +                     goto out1;
>>>>> +     }
>>>>> +
>>>>> +     buf->updt = false;
>>>>> +     return 0;
>>>>> +
>>>>> +out2:
>>>>> +     if (cfgup)
>>>>> +             di->bus.write(di, BQ27XXX_CONTROL, BQ27XXX_SOFT_RESET, false);
>>>>> +out1:
>>>>> +     dev_err(di->dev, "bus error in %s: %d\n", __func__, 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_ENERGY);
>>>>> +     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);
>>>>> +             if (bd.full) {
>>>>> +                     /* 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.full && bd.class == bt.class && bd.block == bt.block;
>>>>> +             if (!same)
>>>>> +                     bq27xxx_battery_read_dm_block(di, &bt);
>>>>> +             if (same ? bd.full : bt.full)
>>>>> +                     bq27xxx_battery_update_dm_block(di, same ? &bd : &bt,
>>>>> +                                     BQ27XXX_DM_TERMINATE_VOLTAGE,
>>>>> +                                     info->voltage_min_design_uv / 1000);
>>>>> +     }
>>>>> +
>>>>> +     if (bd.updt)
>>>>> +             bq27xxx_battery_write_dm_block(di, &bd);
>>>>> +     if (bt.updt)
>>>>> +             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_print_config(di);
>>>>> +     bq27xxx_battery_set_seal_state(di, true);
>>>>> +}
>>>>> +
>>>>>  /*
>>>>>   * Return the battery State-of-Charge
>>>>>   * Or < 0 if something fails.
>>>>> @@ -1006,6 +1390,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 +1430,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 +1458,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);
>>>>>

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v7 7/9] power: bq27xxx_battery: Add power_supply_battery_info support
  2017-02-23 15:17           ` Andrew F. Davis
@ 2017-02-23 15:49             ` Liam Breck
  2017-02-23 16:08               ` Andrew F. Davis
  0 siblings, 1 reply; 34+ messages in thread
From: Liam Breck @ 2017-02-23 15:49 UTC (permalink / raw)
  To: Andrew F. Davis; +Cc: Sebastian Reichel, linux-pm, Matt Ranostay

On Thu, Feb 23, 2017 at 7:17 AM, Andrew F. Davis <afd@ti.com> wrote:
> On 02/22/2017 06:54 PM, Liam Breck wrote:
>> On Wed, Feb 22, 2017 at 4:30 PM, Andrew F. Davis <afd@ti.com> wrote:
>>> On 02/22/2017 03:29 PM, Liam Breck wrote:
>>>> On Wed, Feb 22, 2017 at 12:35 PM, Andrew F. Davis <afd@ti.com> wrote:
>>>>> On 02/21/2017 03:30 PM, Liam Breck wrote:
>>>>>> 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 | 399 ++++++++++++++++++++++++++++++++-
>>>>>>  1 file changed, 397 insertions(+), 2 deletions(-)
>>>>>>
>>>>>> diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
>>>>>> index 7475a5f..8085d4a 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"
>>>>>>
>>>>>> @@ -452,6 +452,99 @@ static struct {
>>>>>>  static DEFINE_MUTEX(bq27xxx_list_lock);
>>>>>>  static LIST_HEAD(bq27xxx_battery_devices);
>>>>>>
>>>>>> +/* writable registers */
>>>>>> +#define BQ27XXX_CONTROL                      0x00
>>>>>> +#define BQ27XXX_DATA_CLASS           0x3E
>>>>>> +#define BQ27XXX_DATA_BLOCK           0x3F
>>>>>> +#define BQ27XXX_BLOCK_DATA           0x40
>>>>>> +#define BQ27XXX_BLOCK_DATA_CHECKSUM  0x60
>>>>>> +#define BQ27XXX_BLOCK_DATA_CONTROL   0x61
>>>>>> +
>>>>>
>>>>> These all belong in the bq27xxx_regs array, you can add new register
>>>>> types to the bq27xxx_reg_index enum.
>>>>
>>>> They're the same on all chips. Let's dup this code 9+ times when we have cause.
>>>>
>>>
>>> They are not the same on all chips, some chips don't have these
>>> registers at all. Plus, BQ27XXX_CONTROL is the same for all chips, and
>>> we already have it in all the chips bq27xxx_regs.
>>
>> Datasheets? Do those chips even support this feature set? Bqtool
>> doesn't appear to support any other memory update scheme.
>>
>
> Many chips don't, which is why we don't want these registers being
> global defines, you can have the chips without these registers as
> INVALID_REG_ADDR

Chips that don't support memory update won't have a bq27xxx_dm_regs
list; we look for that at the start of this code path and do nothing
if it's null for di->chip. All chips that do support memory update use
the same command registers, so there's no reason to dup those.

Also you can delete REG_CTRL as it's not used by the existing code  :-)

>>>> Or make a default array of reg numbers and memcopy it onto each chip
>>>> array, then alter as req'd per chip. But that's outside our scope.
>>>>
>>>>>> +/* control register params */
>>>>>> +#define BQ27XXX_SEALED                       0x20
>>>>>> +#define BQ27XXX_SET_CFGUPDATE                0x13
>>>>>> +#define BQ27XXX_SOFT_RESET           0x42
>>>>>> +
>>>>>> +#define BQ27XXX_MSLEEP(i) usleep_range((i)*1000, (i)*1000+500)
>>>>>
>>>>> why?
>>>>
>>>> I think this might vary on diff chips. Also I have a pending change
>>>> which uses MSLEEP(20)
>>>>
>>>
>>> If they are different we should delay for the largest needed time, a
>>> range means some may not get enough delay, we would always be targeting
>>> the lowest common denominator chip anyway.
>>
>> That will be a task for someone else who has hw to test :-)
>>
>
> Right, so pick a delay that works on the device you are adding, as other
> devices are added they can be tested and new values can be chosen.
>
>>>>>> +
>>>>>> +struct bq27xxx_dm_reg {
>>>>>> +     u8 subclass_id;
>>>>>> +     u8 offset;
>>>>>> +     u8 bytes;
>>>>>> +     u16 min, max;
>>>>>> +};
>>>>>> +
>>>>>> +struct bq27xxx_dm_buf {
>>>>>> +     u8 class;
>>>>>> +     u8 block;
>>>>>> +     u8 a[32];
>>>>>> +     bool full, updt;
>>>>>> +};
>>>>>> +
>>>>>> +#define BQ27XXX_DM_BUF_PTR(b, r) \
>>>>>> +     ( (u16*) &(b).a[ (r)->offset % sizeof (b).a ] )
>>>>>> +
>>>>>
>>>>> This could compete in the IOCCC, there has to be a more readable way to
>>>>> do this..
>>>>
>>>> Hence the macro. It didn't seem to merit a function, but I could write
>>>> an inline fn.
>>>>
>>>
>>> That would be nice. With C, the whole kernel could be one long line, but
>>> just because you can doesn't mean you should, lets keep this readable
>>> for all of our sake :)
>>>
>>>>
>>>>>> +#define BQ27XXX_DM_BUF(di, i) { \
>>>>>> +     .class = bq27xxx_dm_regs[(di)->chip][i].subclass_id, \
>>>>>> +     .block = bq27xxx_dm_regs[(di)->chip][i].offset / sizeof ((struct bq27xxx_dm_buf*)0)->a, \
>>>>>> +}
>>>>>> +
>>>>>> +static inline bool bq27xxx_dm_buf_set(struct bq27xxx_dm_buf *buf,
>>>>>> +                                   struct bq27xxx_dm_reg *reg) {
>>>>>> +     if (buf->class == reg->subclass_id
>>>>>> +      && buf->block == reg->offset / sizeof buf->a
>>>>>> +      && buf->full)
>>>>>
>>>>> Some alignment issues here.
>>>>>
>>>>>> +             return false;
>>>>>> +     buf->class = reg->subclass_id;
>>>>>> +     buf->block = reg->offset / sizeof buf->a;
>>>>>> +     buf->full = buf->updt = false;
>>>>>
>>>>> newline before return
>>>>>
>>>>>> +     return true;
>>>>>> +}
>>>>>> +
>>>>>> +enum bq27xxx_dm_reg_id {
>>>>>> +     BQ27XXX_DM_DESIGN_CAPACITY = 0,
>>>>>> +     BQ27XXX_DM_DESIGN_ENERGY,
>>>>>> +     BQ27XXX_DM_TERMINATE_VOLTAGE,
>>>>>> +     BQ27XXX_DM_END,
>>>>>> +};
>>>>>> +
>>>>>> +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 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 bq27421_dm_regs[] = { /* not tested */
>>>>>> +     [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 },
>>>>>> +};
>>>>>> +
>>>>>> +static struct bq27xxx_dm_reg bq27621_dm_regs[] = { /* not tested */
>>>>>> +     [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[] = {
>>>>>> +     [BQ27421] = bq27421_dm_regs, /* and BQ27441 */
>>>>>> +     [BQ27425] = bq27425_dm_regs,
>>>>>> +/*   [BQ27621] = */ bq27621_dm_regs,
>>>>>> +};
>>>>>> +
>>>>>> +static u32 bq27xxx_unseal_keys[] = {
>>>>>> +     [BQ27421] = 0x80008000, /* and BQ27441 */
>>>>>> +     [BQ27425] = 0x04143672,
>>>>>> +/*   [BQ27621] = */ 0x80008000,
>>>>>
>>>>> Why is this commented out.
>>>>
>>>> The 621 id isn't defined yet. I didn't want to enable something I can't test.
>>>>
>>>
>>> Yes, but you are only commenting out the index, it still is added to the
>>> list. You should wait to add that line when the rest of BQ27621 support
>>> is added.
>>>
>>>>>> +};
>>>>>> +
>>>>>> +
>>>>>>  static int poll_interval_param_set(const char *val, const struct kernel_param *kp)
>>>>>>  {
>>>>>>       struct bq27xxx_device_info *di;
>>>>>> @@ -496,6 +589,297 @@ 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)
>>>>>> +{
>>>>>> +     u32 key = bq27xxx_unseal_keys[di->chip];
>>>>>> +     int ret;
>>>>>> +
>>>>>> +     if (state) {
>>>>>> +             ret = di->bus.write(di, BQ27XXX_CONTROL, BQ27XXX_SEALED, false);
>>>>>> +             if (ret < 0)
>>>>>> +                     goto out;
>>>>>> +     } else {
>>>>>> +             ret = di->bus.write(di, BQ27XXX_CONTROL, (u16)(key >> 16), false);
>>>>>> +             if (ret < 0)
>>>>>> +                     goto out;
>>>>>> +
>>>>>> +             ret = di->bus.write(di, BQ27XXX_CONTROL, (u16)key, false);
>>>>>> +             if (ret < 0)
>>>>>> +                     goto out;
>>>>>> +     }
>>>>>> +     return 0;
>>>>>> +
>>>>>> +out:
>>>>>> +     dev_err(di->dev, "bus error in %s: %d\n", __func__, ret);
>>>>>
>>>>> This is not a debug statement, we don't need the function name, just say
>>>>> where it broke.
>>>>
>>>> What's "where" if not fn name?
>>>>
>>>
>>> For example:
>>>
>>> "Bus error while setting seal state"
>>>
>>> The actually function name is unimportant, and the user doesn't care.
>>>
>>>>>> +     return ret;
>>>>>> +}
>>>>>> +
>>>>>> +static u8 bq27xxx_battery_checksum(struct bq27xxx_dm_buf *buf)
>>>>>> +{
>>>>>> +     u16 sum = 0;
>>>>>> +     int i;
>>>>>> +
>>>>>> +     for (i = 0; i < sizeof buf->a; i++)
>>>>>
>>>>> Linus would like a word with you:
>>>>> https://lkml.org/lkml/2012/7/11/103
>>>>>
>>>>>> +             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, BQ27XXX_DATA_CLASS, buf->class, true);
>>>>>> +     if (ret < 0)
>>>>>> +             goto out;
>>>>>> +
>>>>>> +     ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, buf->block, true);
>>>>>> +     if (ret < 0)
>>>>>> +             goto out;
>>>>>> +
>>>>>> +     BQ27XXX_MSLEEP(1);
>>>>>> +
>>>>>> +     ret = di->bus.read_bulk(di, BQ27XXX_BLOCK_DATA, buf->a, sizeof buf->a);
>>>>>> +     if (ret < 0)
>>>>>> +             goto out;
>>>>>> +
>>>>>> +     ret = di->bus.read(di, BQ27XXX_BLOCK_DATA_CHECKSUM, true);
>>>>>> +     if (ret < 0)
>>>>>> +             goto out;
>>>>>> +
>>>>>> +     if ((u8)ret != bq27xxx_battery_checksum(buf)) {
>>>>>> +             ret = -EINVAL;
>>>>>> +             goto out;
>>>>>> +     }
>>>>>> +
>>>>>> +     buf->full = true;
>>>>>> +     buf->updt = false;
>>>>>> +     return 0;
>>>>>> +
>>>>>> +out:
>>>>>> +     dev_err(di->dev, "bus error in %s: %d\n", __func__, ret);
>>>>>> +     return ret;
>>>>>> +}
>>>>>> +
>>>>>> +static void bq27xxx_battery_print_config(struct bq27xxx_device_info *di)
>>>>>> +{
>>>>>> +     struct bq27xxx_dm_reg *reg = bq27xxx_dm_regs[di->chip];
>>>>>> +     struct bq27xxx_dm_buf buf = { .class = 0xFF };
>>>>>> +     int i;
>>>>>> +
>>>>>> +     for (i = 0; i < BQ27XXX_DM_END; i++, reg++) {
>>>>>> +             const char* str = bq27xxx_dm_reg_name[i];
>>>>>> +
>>>>>> +             if (bq27xxx_dm_buf_set(&buf, reg))
>>>>>> +                     if (bq27xxx_battery_read_dm_block(di, &buf) < 0)
>>>>>> +                             continue;
>>>>>> +
>>>>>> +             if (reg->bytes == 2)
>>>>>> +                     dev_info(di->dev, "config register %s is %d\n", str,
>>>>>> +                              be16_to_cpup(BQ27XXX_DM_BUF_PTR(buf, reg)));
>>>>>> +             else
>>>>>> +                     dev_warn(di->dev, "unsupported config register %s\n", str);
>>>>>> +     }
>>>>>> +}
>>>>>> +
>>>>>> +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];
>>>>>> +     u16 *prev;
>>>>>> +
>>>>>> +     if (reg->bytes != 2)
>>>>>> +             return;
>>>>>> +
>>>>>> +     prev = BQ27XXX_DM_BUF_PTR(*buf, reg);
>>>>>> +     if (be16_to_cpup(prev) == val)
>>>>>> +             return;
>>>>>> +     *prev = cpu_to_be16(val);
>>>>>> +
>>>>>> +     dev_info(di->dev, "update chip %d, class %u, block %u, offset %u, value %u\n",
>>>>>> +              di->chip, buf->class, buf->block, reg->offset, val);
>>>>>> +
>>>>>
>>>>> This should probably be dev_debug, no normal user cares about all this
>>>>> detail.
>>>>
>>>> It's only on change to NVM, which a hw developer (like me) wants to
>>>> know about. I plan to remove the print_config routine so this is not
>>>> redundant.
>>>>
>>>
>>> HW developers should then have debug enabled for drivers they care
>>> about. For everyone else this is just kernel log clutter.
>>>
>>>>>> +     buf->updt = true;
>>>>>> +     return;
>>>>>> +}
>>>>>> +
>>>>>> +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 (cfgup) {
>>>>>> +             ret = di->bus.write(di, BQ27XXX_CONTROL, BQ27XXX_SET_CFGUPDATE, false);
>>>>>> +             if (ret < 0)
>>>>>> +                     goto out1;
>>>>>> +     }
>>>>>> +
>>>>>> +     ret = di->bus.write(di, BQ27XXX_BLOCK_DATA_CONTROL, 0, true);
>>>>>> +     if (ret < 0)
>>>>>> +             goto out2;
>>>>>> +
>>>>>> +     ret = di->bus.write(di, BQ27XXX_DATA_CLASS, buf->class, true);
>>>>>> +     if (ret < 0)
>>>>>> +             goto out2;
>>>>>> +
>>>>>> +     ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, buf->block, true);
>>>>>> +     if (ret < 0)
>>>>>> +             goto out2;
>>>>>> +
>>>>>> +     BQ27XXX_MSLEEP(1);
>>>>>> +
>>>>>> +     ret = di->bus.write_bulk(di, BQ27XXX_BLOCK_DATA, buf->a, sizeof buf->a);
>>>>>> +     if (ret < 0)
>>>>>> +             goto out2;
>>>>>> +
>>>>>> +     ret = di->bus.write(di, BQ27XXX_BLOCK_DATA_CHECKSUM,
>>>>>> +                         bq27xxx_battery_checksum(buf), true);
>>>>>> +     if (ret < 0)
>>>>>> +             goto out2;
>>>>>> +
>>>>>> +     /* THE FOLLOWING CODE IS TOXIC. DO NOT USE!
>>>>>
>>>>> Then why include it, what is this code supposed to do, and what do we
>>>>> lose without it? If it is really needed we can find a way to guarantee
>>>>> the delay length.
>>>>
>>>> This triggers a chip bug, and future devs need to know to avoid it. I
>>>> was tempted by bqtool into using it for the extra error check, and now
>>>> my gauge isn't working, tho I have only reset one NVM block back to
>>>> factory defaults. Bqtool is easy to find, if poorly written.
>>>>
>>>
>>> Just the warning would be enough then, we don't gain anything by writing
>>> out the exact code to break your device in the comment also.
>>>
>>>>>> +      * If the 350ms delay is insufficient, NVM corruption results
>>>>>> +      * on the '425 chip, which could damage the chip.
>>>>>> +      * It was suggested in this TI tool:
>>>>>> +      *   http://git.ti.com/bms-linux/bqtool/blobs/master/gauge.c#line328
>>>>>> +      *
>>>>>> +      * BQ27XXX_MSLEEP(350);
>>>>>> +      * ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, buf->block, true);
>>>>>> +      * BQ27XXX_MSLEEP(1);
>>>>>> +      * ret = di->bus.read(di, BQ27XXX_BLOCK_DATA_CHECKSUM, true);
>>>>>> +      * if ((u8)ret != bq27xxx_battery_checksum(buf))
>>>>>> +      *      ret = -EINVAL;
>>>>>> +      */
>>>>>> +
>>>>>> +     if (cfgup) {
>>>>>> +             BQ27XXX_MSLEEP(1);
>>>>>> +             ret = di->bus.write(di, BQ27XXX_CONTROL, BQ27XXX_SOFT_RESET, false);
>>>>>> +             if (ret < 0)
>>>>>> +                     goto out1;
>>>>>> +     }
>>>>>> +
>>>>>> +     buf->updt = false;
>>>>>> +     return 0;
>>>>>> +
>>>>>> +out2:
>>>>>> +     if (cfgup)
>>>>>> +             di->bus.write(di, BQ27XXX_CONTROL, BQ27XXX_SOFT_RESET, false);
>>>>>> +out1:
>>>>>> +     dev_err(di->dev, "bus error in %s: %d\n", __func__, 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_ENERGY);
>>>>>> +     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);
>>>>>> +             if (bd.full) {
>>>>>> +                     /* 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.full && bd.class == bt.class && bd.block == bt.block;
>>>>>> +             if (!same)
>>>>>> +                     bq27xxx_battery_read_dm_block(di, &bt);
>>>>>> +             if (same ? bd.full : bt.full)
>>>>>> +                     bq27xxx_battery_update_dm_block(di, same ? &bd : &bt,
>>>>>> +                                     BQ27XXX_DM_TERMINATE_VOLTAGE,
>>>>>> +                                     info->voltage_min_design_uv / 1000);
>>>>>> +     }
>>>>>> +
>>>>>> +     if (bd.updt)
>>>>>> +             bq27xxx_battery_write_dm_block(di, &bd);
>>>>>> +     if (bt.updt)
>>>>>> +             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_print_config(di);
>>>>>> +     bq27xxx_battery_set_seal_state(di, true);
>>>>>> +}
>>>>>> +
>>>>>>  /*
>>>>>>   * Return the battery State-of-Charge
>>>>>>   * Or < 0 if something fails.
>>>>>> @@ -1006,6 +1390,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 +1430,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 +1458,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);
>>>>>>

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v7 7/9] power: bq27xxx_battery: Add power_supply_battery_info support
  2017-02-23 15:49             ` Liam Breck
@ 2017-02-23 16:08               ` Andrew F. Davis
  2017-02-23 16:38                 ` Liam Breck
  0 siblings, 1 reply; 34+ messages in thread
From: Andrew F. Davis @ 2017-02-23 16:08 UTC (permalink / raw)
  To: Liam Breck; +Cc: Sebastian Reichel, linux-pm, Matt Ranostay

On 02/23/2017 09:49 AM, Liam Breck wrote:
> On Thu, Feb 23, 2017 at 7:17 AM, Andrew F. Davis <afd@ti.com> wrote:
>> On 02/22/2017 06:54 PM, Liam Breck wrote:
>>> On Wed, Feb 22, 2017 at 4:30 PM, Andrew F. Davis <afd@ti.com> wrote:
>>>> On 02/22/2017 03:29 PM, Liam Breck wrote:
>>>>> On Wed, Feb 22, 2017 at 12:35 PM, Andrew F. Davis <afd@ti.com> wrote:
>>>>>> On 02/21/2017 03:30 PM, Liam Breck wrote:
>>>>>>> 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 | 399 ++++++++++++++++++++++++++++++++-
>>>>>>>  1 file changed, 397 insertions(+), 2 deletions(-)
>>>>>>>
>>>>>>> diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
>>>>>>> index 7475a5f..8085d4a 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"
>>>>>>>
>>>>>>> @@ -452,6 +452,99 @@ static struct {
>>>>>>>  static DEFINE_MUTEX(bq27xxx_list_lock);
>>>>>>>  static LIST_HEAD(bq27xxx_battery_devices);
>>>>>>>
>>>>>>> +/* writable registers */
>>>>>>> +#define BQ27XXX_CONTROL                      0x00
>>>>>>> +#define BQ27XXX_DATA_CLASS           0x3E
>>>>>>> +#define BQ27XXX_DATA_BLOCK           0x3F
>>>>>>> +#define BQ27XXX_BLOCK_DATA           0x40
>>>>>>> +#define BQ27XXX_BLOCK_DATA_CHECKSUM  0x60
>>>>>>> +#define BQ27XXX_BLOCK_DATA_CONTROL   0x61
>>>>>>> +
>>>>>>
>>>>>> These all belong in the bq27xxx_regs array, you can add new register
>>>>>> types to the bq27xxx_reg_index enum.
>>>>>
>>>>> They're the same on all chips. Let's dup this code 9+ times when we have cause.
>>>>>
>>>>
>>>> They are not the same on all chips, some chips don't have these
>>>> registers at all. Plus, BQ27XXX_CONTROL is the same for all chips, and
>>>> we already have it in all the chips bq27xxx_regs.
>>>
>>> Datasheets? Do those chips even support this feature set? Bqtool
>>> doesn't appear to support any other memory update scheme.
>>>
>>
>> Many chips don't, which is why we don't want these registers being
>> global defines, you can have the chips without these registers as
>> INVALID_REG_ADDR
> 
> Chips that don't support memory update won't have a bq27xxx_dm_regs
> list; we look for that at the start of this code path and do nothing
> if it's null for di->chip. All chips that do support memory update use
> the same command registers, so there's no reason to dup those.
> 

Someday they might use a different value, we don't lose anything by
putting these device registers in the table with the other device
registers. I would rather duplicate some lines if it makes the code more
uniform and easier to follow, again this is not a contest, we write code
for ourselves, let the compiler organize/optimize things.

> Also you can delete REG_CTRL as it's not used by the existing code  :-)
> 

True, but as before, I just like having the complete register set in a
nice simple table, as opposed to having to add and change things all
over if/when we may need that register.

Being a static table, I wonder if a sufficiently smart compiler could
see that it is not used and optimize the value out for us...

>>>>> Or make a default array of reg numbers and memcopy it onto each chip
>>>>> array, then alter as req'd per chip. But that's outside our scope.
>>>>>
>>>>>>> +/* control register params */
>>>>>>> +#define BQ27XXX_SEALED                       0x20
>>>>>>> +#define BQ27XXX_SET_CFGUPDATE                0x13
>>>>>>> +#define BQ27XXX_SOFT_RESET           0x42
>>>>>>> +
>>>>>>> +#define BQ27XXX_MSLEEP(i) usleep_range((i)*1000, (i)*1000+500)
>>>>>>

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v7 7/9] power: bq27xxx_battery: Add power_supply_battery_info support
  2017-02-23 16:08               ` Andrew F. Davis
@ 2017-02-23 16:38                 ` Liam Breck
  2017-02-23 16:53                   ` Andrew F. Davis
  0 siblings, 1 reply; 34+ messages in thread
From: Liam Breck @ 2017-02-23 16:38 UTC (permalink / raw)
  To: Andrew F. Davis; +Cc: Sebastian Reichel, linux-pm, Matt Ranostay

On Thu, Feb 23, 2017 at 8:08 AM, Andrew F. Davis <afd@ti.com> wrote:
> On 02/23/2017 09:49 AM, Liam Breck wrote:
>> On Thu, Feb 23, 2017 at 7:17 AM, Andrew F. Davis <afd@ti.com> wrote:
>>> On 02/22/2017 06:54 PM, Liam Breck wrote:
>>>> On Wed, Feb 22, 2017 at 4:30 PM, Andrew F. Davis <afd@ti.com> wrote:
>>>>> On 02/22/2017 03:29 PM, Liam Breck wrote:
>>>>>> On Wed, Feb 22, 2017 at 12:35 PM, Andrew F. Davis <afd@ti.com> wrote:
>>>>>>> On 02/21/2017 03:30 PM, Liam Breck wrote:
>>>>>>>> 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 | 399 ++++++++++++++++++++++++++++++++-
>>>>>>>>  1 file changed, 397 insertions(+), 2 deletions(-)
>>>>>>>>
>>>>>>>> diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
>>>>>>>> index 7475a5f..8085d4a 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"
>>>>>>>>
>>>>>>>> @@ -452,6 +452,99 @@ static struct {
>>>>>>>>  static DEFINE_MUTEX(bq27xxx_list_lock);
>>>>>>>>  static LIST_HEAD(bq27xxx_battery_devices);
>>>>>>>>
>>>>>>>> +/* writable registers */
>>>>>>>> +#define BQ27XXX_CONTROL                      0x00
>>>>>>>> +#define BQ27XXX_DATA_CLASS           0x3E
>>>>>>>> +#define BQ27XXX_DATA_BLOCK           0x3F
>>>>>>>> +#define BQ27XXX_BLOCK_DATA           0x40
>>>>>>>> +#define BQ27XXX_BLOCK_DATA_CHECKSUM  0x60
>>>>>>>> +#define BQ27XXX_BLOCK_DATA_CONTROL   0x61
>>>>>>>> +
>>>>>>>
>>>>>>> These all belong in the bq27xxx_regs array, you can add new register
>>>>>>> types to the bq27xxx_reg_index enum.
>>>>>>
>>>>>> They're the same on all chips. Let's dup this code 9+ times when we have cause.
>>>>>>
>>>>>
>>>>> They are not the same on all chips, some chips don't have these
>>>>> registers at all. Plus, BQ27XXX_CONTROL is the same for all chips, and
>>>>> we already have it in all the chips bq27xxx_regs.
>>>>
>>>> Datasheets? Do those chips even support this feature set? Bqtool
>>>> doesn't appear to support any other memory update scheme.
>>>>
>>>
>>> Many chips don't, which is why we don't want these registers being
>>> global defines, you can have the chips without these registers as
>>> INVALID_REG_ADDR
>>
>> Chips that don't support memory update won't have a bq27xxx_dm_regs
>> list; we look for that at the start of this code path and do nothing
>> if it's null for di->chip. All chips that do support memory update use
>> the same command registers, so there's no reason to dup those.
>>
>
> Someday they might use a different value, we don't lose anything by
> putting these device registers in the table with the other device
> registers. I would rather duplicate some lines if it makes the code more
> uniform and easier to follow, again this is not a contest, we write code
> for ourselves, let the compiler organize/optimize things.

I prefer the principle that every line you write must have a purpose
in the current codebase, vs a future one. But if you are adamant, I
capitulate.

>> Also you can delete REG_CTRL as it's not used by the existing code  :-)
>>
>
> True, but as before, I just like having the complete register set in a
> nice simple table, as opposed to having to add and change things all
> over if/when we may need that register.
>
> Being a static table, I wonder if a sufficiently smart compiler could
> see that it is not used and optimize the value out for us...

No :-p All the compiler can do for unused data is issue a warning. And
it can't do so for array elements.

You really should define those tables in a function which memcpy's the
right one onto di.regs.

>>>>>> Or make a default array of reg numbers and memcopy it onto each chip
>>>>>> array, then alter as req'd per chip. But that's outside our scope.
>>>>>>
>>>>>>>> +/* control register params */
>>>>>>>> +#define BQ27XXX_SEALED                       0x20
>>>>>>>> +#define BQ27XXX_SET_CFGUPDATE                0x13
>>>>>>>> +#define BQ27XXX_SOFT_RESET           0x42
>>>>>>>> +
>>>>>>>> +#define BQ27XXX_MSLEEP(i) usleep_range((i)*1000, (i)*1000+500)
>>>>>>>

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v7 7/9] power: bq27xxx_battery: Add power_supply_battery_info support
  2017-02-23 16:38                 ` Liam Breck
@ 2017-02-23 16:53                   ` Andrew F. Davis
  2017-02-23 17:36                     ` Liam Breck
  0 siblings, 1 reply; 34+ messages in thread
From: Andrew F. Davis @ 2017-02-23 16:53 UTC (permalink / raw)
  To: Liam Breck; +Cc: Sebastian Reichel, linux-pm, Matt Ranostay

On 02/23/2017 10:38 AM, Liam Breck wrote:
> On Thu, Feb 23, 2017 at 8:08 AM, Andrew F. Davis <afd@ti.com> wrote:
>> On 02/23/2017 09:49 AM, Liam Breck wrote:
>>> On Thu, Feb 23, 2017 at 7:17 AM, Andrew F. Davis <afd@ti.com> wrote:
>>>> On 02/22/2017 06:54 PM, Liam Breck wrote:
>>>>> On Wed, Feb 22, 2017 at 4:30 PM, Andrew F. Davis <afd@ti.com> wrote:
>>>>>> On 02/22/2017 03:29 PM, Liam Breck wrote:
>>>>>>> On Wed, Feb 22, 2017 at 12:35 PM, Andrew F. Davis <afd@ti.com> wrote:
>>>>>>>> On 02/21/2017 03:30 PM, Liam Breck wrote:
>>>>>>>>> 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 | 399 ++++++++++++++++++++++++++++++++-
>>>>>>>>>  1 file changed, 397 insertions(+), 2 deletions(-)
>>>>>>>>>
>>>>>>>>> diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
>>>>>>>>> index 7475a5f..8085d4a 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"
>>>>>>>>>
>>>>>>>>> @@ -452,6 +452,99 @@ static struct {
>>>>>>>>>  static DEFINE_MUTEX(bq27xxx_list_lock);
>>>>>>>>>  static LIST_HEAD(bq27xxx_battery_devices);
>>>>>>>>>
>>>>>>>>> +/* writable registers */
>>>>>>>>> +#define BQ27XXX_CONTROL                      0x00
>>>>>>>>> +#define BQ27XXX_DATA_CLASS           0x3E
>>>>>>>>> +#define BQ27XXX_DATA_BLOCK           0x3F
>>>>>>>>> +#define BQ27XXX_BLOCK_DATA           0x40
>>>>>>>>> +#define BQ27XXX_BLOCK_DATA_CHECKSUM  0x60
>>>>>>>>> +#define BQ27XXX_BLOCK_DATA_CONTROL   0x61
>>>>>>>>> +
>>>>>>>>
>>>>>>>> These all belong in the bq27xxx_regs array, you can add new register
>>>>>>>> types to the bq27xxx_reg_index enum.
>>>>>>>
>>>>>>> They're the same on all chips. Let's dup this code 9+ times when we have cause.
>>>>>>>
>>>>>>
>>>>>> They are not the same on all chips, some chips don't have these
>>>>>> registers at all. Plus, BQ27XXX_CONTROL is the same for all chips, and
>>>>>> we already have it in all the chips bq27xxx_regs.
>>>>>
>>>>> Datasheets? Do those chips even support this feature set? Bqtool
>>>>> doesn't appear to support any other memory update scheme.
>>>>>
>>>>
>>>> Many chips don't, which is why we don't want these registers being
>>>> global defines, you can have the chips without these registers as
>>>> INVALID_REG_ADDR
>>>
>>> Chips that don't support memory update won't have a bq27xxx_dm_regs
>>> list; we look for that at the start of this code path and do nothing
>>> if it's null for di->chip. All chips that do support memory update use
>>> the same command registers, so there's no reason to dup those.
>>>
>>
>> Someday they might use a different value, we don't lose anything by
>> putting these device registers in the table with the other device
>> registers. I would rather duplicate some lines if it makes the code more
>> uniform and easier to follow, again this is not a contest, we write code
>> for ourselves, let the compiler organize/optimize things.
> 
> I prefer the principle that every line you write must have a purpose
> in the current codebase, vs a future one. But if you are adamant, I
> capitulate.
> 

Normally I would agree, but for listing registers on a device we would
have a register map that looks like swiss cheese.

>>> Also you can delete REG_CTRL as it's not used by the existing code  :-)
>>>
>>
>> True, but as before, I just like having the complete register set in a
>> nice simple table, as opposed to having to add and change things all
>> over if/when we may need that register.
>>
>> Being a static table, I wonder if a sufficiently smart compiler could
>> see that it is not used and optimize the value out for us...
> 
> No :-p All the compiler can do for unused data is issue a warning. And
> it can't do so for array elements.
> 

I said "sufficiently smart compiler", it could find that no code within
the tables context accesses it outside of the set indexes, it could then
even remove the tables altogether and replace accesses with immediate
values. Not practical, but not impossible, I think.. :)

> You really should define those tables in a function which memcpy's the
> right one onto di.regs.
> 

What would that do for us? We still need to store the tables, it just
removes one level of pointers.

>>>>>>> Or make a default array of reg numbers and memcopy it onto each chip
>>>>>>> array, then alter as req'd per chip. But that's outside our scope.
>>>>>>>
>>>>>>>>> +/* control register params */
>>>>>>>>> +#define BQ27XXX_SEALED                       0x20
>>>>>>>>> +#define BQ27XXX_SET_CFGUPDATE                0x13
>>>>>>>>> +#define BQ27XXX_SOFT_RESET           0x42
>>>>>>>>> +
>>>>>>>>> +#define BQ27XXX_MSLEEP(i) usleep_range((i)*1000, (i)*1000+500)
>>>>>>>>

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v7 7/9] power: bq27xxx_battery: Add power_supply_battery_info support
  2017-02-23 16:53                   ` Andrew F. Davis
@ 2017-02-23 17:36                     ` Liam Breck
  2017-02-23 18:02                       ` Andrew F. Davis
  0 siblings, 1 reply; 34+ messages in thread
From: Liam Breck @ 2017-02-23 17:36 UTC (permalink / raw)
  To: Andrew F. Davis; +Cc: Sebastian Reichel, linux-pm, Matt Ranostay

On Thu, Feb 23, 2017 at 8:53 AM, Andrew F. Davis <afd@ti.com> wrote:
> On 02/23/2017 10:38 AM, Liam Breck wrote:
>> On Thu, Feb 23, 2017 at 8:08 AM, Andrew F. Davis <afd@ti.com> wrote:
>>> On 02/23/2017 09:49 AM, Liam Breck wrote:
>>>> On Thu, Feb 23, 2017 at 7:17 AM, Andrew F. Davis <afd@ti.com> wrote:
>>>>> On 02/22/2017 06:54 PM, Liam Breck wrote:
>>>>>> On Wed, Feb 22, 2017 at 4:30 PM, Andrew F. Davis <afd@ti.com> wrote:
>>>>>>> On 02/22/2017 03:29 PM, Liam Breck wrote:
>>>>>>>> On Wed, Feb 22, 2017 at 12:35 PM, Andrew F. Davis <afd@ti.com> wrote:
>>>>>>>>> On 02/21/2017 03:30 PM, Liam Breck wrote:
>>>>>>>>>> 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 | 399 ++++++++++++++++++++++++++++++++-
>>>>>>>>>>  1 file changed, 397 insertions(+), 2 deletions(-)
>>>>>>>>>>
>>>>>>>>>> diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
>>>>>>>>>> index 7475a5f..8085d4a 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"
>>>>>>>>>>
>>>>>>>>>> @@ -452,6 +452,99 @@ static struct {
>>>>>>>>>>  static DEFINE_MUTEX(bq27xxx_list_lock);
>>>>>>>>>>  static LIST_HEAD(bq27xxx_battery_devices);
>>>>>>>>>>
>>>>>>>>>> +/* writable registers */
>>>>>>>>>> +#define BQ27XXX_CONTROL                      0x00
>>>>>>>>>> +#define BQ27XXX_DATA_CLASS           0x3E
>>>>>>>>>> +#define BQ27XXX_DATA_BLOCK           0x3F
>>>>>>>>>> +#define BQ27XXX_BLOCK_DATA           0x40
>>>>>>>>>> +#define BQ27XXX_BLOCK_DATA_CHECKSUM  0x60
>>>>>>>>>> +#define BQ27XXX_BLOCK_DATA_CONTROL   0x61
>>>>>>>>>> +
>>>>>>>>>
>>>>>>>>> These all belong in the bq27xxx_regs array, you can add new register
>>>>>>>>> types to the bq27xxx_reg_index enum.
>>>>>>>>
>>>>>>>> They're the same on all chips. Let's dup this code 9+ times when we have cause.
>>>>>>>>
>>>>>>>
>>>>>>> They are not the same on all chips, some chips don't have these
>>>>>>> registers at all. Plus, BQ27XXX_CONTROL is the same for all chips, and
>>>>>>> we already have it in all the chips bq27xxx_regs.
>>>>>>
>>>>>> Datasheets? Do those chips even support this feature set? Bqtool
>>>>>> doesn't appear to support any other memory update scheme.
>>>>>>
>>>>>
>>>>> Many chips don't, which is why we don't want these registers being
>>>>> global defines, you can have the chips without these registers as
>>>>> INVALID_REG_ADDR
>>>>
>>>> Chips that don't support memory update won't have a bq27xxx_dm_regs
>>>> list; we look for that at the start of this code path and do nothing
>>>> if it's null for di->chip. All chips that do support memory update use
>>>> the same command registers, so there's no reason to dup those.
>>>>
>>>
>>> Someday they might use a different value, we don't lose anything by
>>> putting these device registers in the table with the other device
>>> registers. I would rather duplicate some lines if it makes the code more
>>> uniform and easier to follow, again this is not a contest, we write code
>>> for ourselves, let the compiler organize/optimize things.
>>
>> I prefer the principle that every line you write must have a purpose
>> in the current codebase, vs a future one. But if you are adamant, I
>> capitulate.
>>
>
> Normally I would agree, but for listing registers on a device we would
> have a register map that looks like swiss cheese.

Was that adamant? Keep the readable registers in tables; make the
writable ones universal since they're all the same.

>>>> Also you can delete REG_CTRL as it's not used by the existing code  :-)
>>>>
>>>
>>> True, but as before, I just like having the complete register set in a
>>> nice simple table, as opposed to having to add and change things all
>>> over if/when we may need that register.
>>>
>>> Being a static table, I wonder if a sufficiently smart compiler could
>>> see that it is not used and optimize the value out for us...
>>
>> No :-p All the compiler can do for unused data is issue a warning. And
>> it can't do so for array elements.
>>
>
> I said "sufficiently smart compiler", it could find that no code within
> the tables context accesses it outside of the set indexes, it could then
> even remove the tables altogether and replace accesses with immediate
> values. Not practical, but not impossible, I think.. :)

I'm not so interested in hypothetical compilers and chips. There are
reasons why compilers don't optimize away data.

>> You really should define those tables in a function which memcpy's the
>> right one onto di.regs.
>
> What would that do for us? We still need to store the tables, it just
> removes one level of pointers.

It would store 1 table for the life of the kernel, vs 9+

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v7 7/9] power: bq27xxx_battery: Add power_supply_battery_info support
  2017-02-23 17:36                     ` Liam Breck
@ 2017-02-23 18:02                       ` Andrew F. Davis
  2017-02-23 21:28                         ` Liam Breck
  0 siblings, 1 reply; 34+ messages in thread
From: Andrew F. Davis @ 2017-02-23 18:02 UTC (permalink / raw)
  To: Liam Breck; +Cc: Sebastian Reichel, linux-pm, Matt Ranostay

On 02/23/2017 11:36 AM, Liam Breck wrote:
> On Thu, Feb 23, 2017 at 8:53 AM, Andrew F. Davis <afd@ti.com> wrote:
>> On 02/23/2017 10:38 AM, Liam Breck wrote:
>>> On Thu, Feb 23, 2017 at 8:08 AM, Andrew F. Davis <afd@ti.com> wrote:
>>>> On 02/23/2017 09:49 AM, Liam Breck wrote:
>>>>> On Thu, Feb 23, 2017 at 7:17 AM, Andrew F. Davis <afd@ti.com> wrote:
>>>>>> On 02/22/2017 06:54 PM, Liam Breck wrote:
>>>>>>> On Wed, Feb 22, 2017 at 4:30 PM, Andrew F. Davis <afd@ti.com> wrote:
>>>>>>>> On 02/22/2017 03:29 PM, Liam Breck wrote:
>>>>>>>>> On Wed, Feb 22, 2017 at 12:35 PM, Andrew F. Davis <afd@ti.com> wrote:
>>>>>>>>>> On 02/21/2017 03:30 PM, Liam Breck wrote:
>>>>>>>>>>> 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 | 399 ++++++++++++++++++++++++++++++++-
>>>>>>>>>>>  1 file changed, 397 insertions(+), 2 deletions(-)
>>>>>>>>>>>
>>>>>>>>>>> diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
>>>>>>>>>>> index 7475a5f..8085d4a 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"
>>>>>>>>>>>
>>>>>>>>>>> @@ -452,6 +452,99 @@ static struct {
>>>>>>>>>>>  static DEFINE_MUTEX(bq27xxx_list_lock);
>>>>>>>>>>>  static LIST_HEAD(bq27xxx_battery_devices);
>>>>>>>>>>>
>>>>>>>>>>> +/* writable registers */
>>>>>>>>>>> +#define BQ27XXX_CONTROL                      0x00
>>>>>>>>>>> +#define BQ27XXX_DATA_CLASS           0x3E
>>>>>>>>>>> +#define BQ27XXX_DATA_BLOCK           0x3F
>>>>>>>>>>> +#define BQ27XXX_BLOCK_DATA           0x40
>>>>>>>>>>> +#define BQ27XXX_BLOCK_DATA_CHECKSUM  0x60
>>>>>>>>>>> +#define BQ27XXX_BLOCK_DATA_CONTROL   0x61
>>>>>>>>>>> +
>>>>>>>>>>
>>>>>>>>>> These all belong in the bq27xxx_regs array, you can add new register
>>>>>>>>>> types to the bq27xxx_reg_index enum.
>>>>>>>>>
>>>>>>>>> They're the same on all chips. Let's dup this code 9+ times when we have cause.
>>>>>>>>>
>>>>>>>>
>>>>>>>> They are not the same on all chips, some chips don't have these
>>>>>>>> registers at all. Plus, BQ27XXX_CONTROL is the same for all chips, and
>>>>>>>> we already have it in all the chips bq27xxx_regs.
>>>>>>>
>>>>>>> Datasheets? Do those chips even support this feature set? Bqtool
>>>>>>> doesn't appear to support any other memory update scheme.
>>>>>>>
>>>>>>
>>>>>> Many chips don't, which is why we don't want these registers being
>>>>>> global defines, you can have the chips without these registers as
>>>>>> INVALID_REG_ADDR
>>>>>
>>>>> Chips that don't support memory update won't have a bq27xxx_dm_regs
>>>>> list; we look for that at the start of this code path and do nothing
>>>>> if it's null for di->chip. All chips that do support memory update use
>>>>> the same command registers, so there's no reason to dup those.
>>>>>
>>>>
>>>> Someday they might use a different value, we don't lose anything by
>>>> putting these device registers in the table with the other device
>>>> registers. I would rather duplicate some lines if it makes the code more
>>>> uniform and easier to follow, again this is not a contest, we write code
>>>> for ourselves, let the compiler organize/optimize things.
>>>
>>> I prefer the principle that every line you write must have a purpose
>>> in the current codebase, vs a future one. But if you are adamant, I
>>> capitulate.
>>>
>>
>> Normally I would agree, but for listing registers on a device we would
>> have a register map that looks like swiss cheese.
> 
> Was that adamant? Keep the readable registers in tables; make the
> writable ones universal since they're all the same.
> 
>>>>> Also you can delete REG_CTRL as it's not used by the existing code  :-)
>>>>>
>>>>
>>>> True, but as before, I just like having the complete register set in a
>>>> nice simple table, as opposed to having to add and change things all
>>>> over if/when we may need that register.
>>>>
>>>> Being a static table, I wonder if a sufficiently smart compiler could
>>>> see that it is not used and optimize the value out for us...
>>>
>>> No :-p All the compiler can do for unused data is issue a warning. And
>>> it can't do so for array elements.
>>>
>>
>> I said "sufficiently smart compiler", it could find that no code within
>> the tables context accesses it outside of the set indexes, it could then
>> even remove the tables altogether and replace accesses with immediate
>> values. Not practical, but not impossible, I think.. :)
> 
> I'm not so interested in hypothetical compilers and chips. There are
> reasons why compilers don't optimize away data.
> 
>>> You really should define those tables in a function which memcpy's the
>>> right one onto di.regs.
>>
>> What would that do for us? We still need to store the tables, it just
>> removes one level of pointers.
> 
> It would store 1 table for the life of the kernel, vs 9+
> 

Helping the compiler optimize away a couple bytes is much less important
than readability and maintainability of the code base, IMHO.

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v7 7/9] power: bq27xxx_battery: Add power_supply_battery_info support
  2017-02-22 20:35   ` Andrew F. Davis
  2017-02-22 21:29     ` Liam Breck
@ 2017-02-23 21:15     ` Liam Breck
  2017-02-24 14:47       ` Andrew F. Davis
  1 sibling, 1 reply; 34+ messages in thread
From: Liam Breck @ 2017-02-23 21:15 UTC (permalink / raw)
  To: Andrew F. Davis; +Cc: Sebastian Reichel, linux-pm, Matt Ranostay

Did you have a chance to test the patchset against hardware?

If you don't have a board with a 425 chip, I can send you the dm_regs
tables for whatever parts you have to hand...


On Wed, Feb 22, 2017 at 12:35 PM, Andrew F. Davis <afd@ti.com> wrote:
> On 02/21/2017 03:30 PM, Liam Breck wrote:
>> 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 | 399 ++++++++++++++++++++++++++++++++-
>>  1 file changed, 397 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
>> index 7475a5f..8085d4a 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"
>>
>> @@ -452,6 +452,99 @@ static struct {
>>  static DEFINE_MUTEX(bq27xxx_list_lock);
>>  static LIST_HEAD(bq27xxx_battery_devices);
>>
>> +/* writable registers */
>> +#define BQ27XXX_CONTROL                      0x00
>> +#define BQ27XXX_DATA_CLASS           0x3E
>> +#define BQ27XXX_DATA_BLOCK           0x3F
>> +#define BQ27XXX_BLOCK_DATA           0x40
>> +#define BQ27XXX_BLOCK_DATA_CHECKSUM  0x60
>> +#define BQ27XXX_BLOCK_DATA_CONTROL   0x61
>> +
>
> These all belong in the bq27xxx_regs array, you can add new register
> types to the bq27xxx_reg_index enum.
>
>> +/* control register params */
>> +#define BQ27XXX_SEALED                       0x20
>> +#define BQ27XXX_SET_CFGUPDATE                0x13
>> +#define BQ27XXX_SOFT_RESET           0x42
>> +
>> +#define BQ27XXX_MSLEEP(i) usleep_range((i)*1000, (i)*1000+500)
>
> why?
>
>> +
>> +struct bq27xxx_dm_reg {
>> +     u8 subclass_id;
>> +     u8 offset;
>> +     u8 bytes;
>> +     u16 min, max;
>> +};
>> +
>> +struct bq27xxx_dm_buf {
>> +     u8 class;
>> +     u8 block;
>> +     u8 a[32];
>> +     bool full, updt;
>> +};
>> +
>> +#define BQ27XXX_DM_BUF_PTR(b, r) \
>> +     ( (u16*) &(b).a[ (r)->offset % sizeof (b).a ] )
>> +
>
> This could compete in the IOCCC, there has to be a more readable way to
> do this..
>
>> +#define BQ27XXX_DM_BUF(di, i) { \
>> +     .class = bq27xxx_dm_regs[(di)->chip][i].subclass_id, \
>> +     .block = bq27xxx_dm_regs[(di)->chip][i].offset / sizeof ((struct bq27xxx_dm_buf*)0)->a, \
>> +}
>> +
>> +static inline bool bq27xxx_dm_buf_set(struct bq27xxx_dm_buf *buf,
>> +                                   struct bq27xxx_dm_reg *reg) {
>> +     if (buf->class == reg->subclass_id
>> +      && buf->block == reg->offset / sizeof buf->a
>> +      && buf->full)
>
> Some alignment issues here.
>
>> +             return false;
>> +     buf->class = reg->subclass_id;
>> +     buf->block = reg->offset / sizeof buf->a;
>> +     buf->full = buf->updt = false;
>
> newline before return
>
>> +     return true;
>> +}
>> +
>> +enum bq27xxx_dm_reg_id {
>> +     BQ27XXX_DM_DESIGN_CAPACITY = 0,
>> +     BQ27XXX_DM_DESIGN_ENERGY,
>> +     BQ27XXX_DM_TERMINATE_VOLTAGE,
>> +     BQ27XXX_DM_END,
>> +};
>> +
>> +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 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 bq27421_dm_regs[] = { /* not tested */
>> +     [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 },
>> +};
>> +
>> +static struct bq27xxx_dm_reg bq27621_dm_regs[] = { /* not tested */
>> +     [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[] = {
>> +     [BQ27421] = bq27421_dm_regs, /* and BQ27441 */
>> +     [BQ27425] = bq27425_dm_regs,
>> +/*   [BQ27621] = */ bq27621_dm_regs,
>> +};
>> +
>> +static u32 bq27xxx_unseal_keys[] = {
>> +     [BQ27421] = 0x80008000, /* and BQ27441 */
>> +     [BQ27425] = 0x04143672,
>> +/*   [BQ27621] = */ 0x80008000,
>
> Why is this commented out.
>
>> +};
>> +
>> +
>>  static int poll_interval_param_set(const char *val, const struct kernel_param *kp)
>>  {
>>       struct bq27xxx_device_info *di;
>> @@ -496,6 +589,297 @@ 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)
>> +{
>> +     u32 key = bq27xxx_unseal_keys[di->chip];
>> +     int ret;
>> +
>> +     if (state) {
>> +             ret = di->bus.write(di, BQ27XXX_CONTROL, BQ27XXX_SEALED, false);
>> +             if (ret < 0)
>> +                     goto out;
>> +     } else {
>> +             ret = di->bus.write(di, BQ27XXX_CONTROL, (u16)(key >> 16), false);
>> +             if (ret < 0)
>> +                     goto out;
>> +
>> +             ret = di->bus.write(di, BQ27XXX_CONTROL, (u16)key, false);
>> +             if (ret < 0)
>> +                     goto out;
>> +     }
>> +     return 0;
>> +
>> +out:
>> +     dev_err(di->dev, "bus error in %s: %d\n", __func__, ret);
>
> This is not a debug statement, we don't need the function name, just say
> where it broke.
>
>> +     return ret;
>> +}
>> +
>> +static u8 bq27xxx_battery_checksum(struct bq27xxx_dm_buf *buf)
>> +{
>> +     u16 sum = 0;
>> +     int i;
>> +
>> +     for (i = 0; i < sizeof buf->a; i++)
>
> Linus would like a word with you:
> https://lkml.org/lkml/2012/7/11/103
>
>> +             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, BQ27XXX_DATA_CLASS, buf->class, true);
>> +     if (ret < 0)
>> +             goto out;
>> +
>> +     ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, buf->block, true);
>> +     if (ret < 0)
>> +             goto out;
>> +
>> +     BQ27XXX_MSLEEP(1);
>> +
>> +     ret = di->bus.read_bulk(di, BQ27XXX_BLOCK_DATA, buf->a, sizeof buf->a);
>> +     if (ret < 0)
>> +             goto out;
>> +
>> +     ret = di->bus.read(di, BQ27XXX_BLOCK_DATA_CHECKSUM, true);
>> +     if (ret < 0)
>> +             goto out;
>> +
>> +     if ((u8)ret != bq27xxx_battery_checksum(buf)) {
>> +             ret = -EINVAL;
>> +             goto out;
>> +     }
>> +
>> +     buf->full = true;
>> +     buf->updt = false;
>> +     return 0;
>> +
>> +out:
>> +     dev_err(di->dev, "bus error in %s: %d\n", __func__, ret);
>> +     return ret;
>> +}
>> +
>> +static void bq27xxx_battery_print_config(struct bq27xxx_device_info *di)
>> +{
>> +     struct bq27xxx_dm_reg *reg = bq27xxx_dm_regs[di->chip];
>> +     struct bq27xxx_dm_buf buf = { .class = 0xFF };
>> +     int i;
>> +
>> +     for (i = 0; i < BQ27XXX_DM_END; i++, reg++) {
>> +             const char* str = bq27xxx_dm_reg_name[i];
>> +
>> +             if (bq27xxx_dm_buf_set(&buf, reg))
>> +                     if (bq27xxx_battery_read_dm_block(di, &buf) < 0)
>> +                             continue;
>> +
>> +             if (reg->bytes == 2)
>> +                     dev_info(di->dev, "config register %s is %d\n", str,
>> +                              be16_to_cpup(BQ27XXX_DM_BUF_PTR(buf, reg)));
>> +             else
>> +                     dev_warn(di->dev, "unsupported config register %s\n", str);
>> +     }
>> +}
>> +
>> +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];
>> +     u16 *prev;
>> +
>> +     if (reg->bytes != 2)
>> +             return;
>> +
>> +     prev = BQ27XXX_DM_BUF_PTR(*buf, reg);
>> +     if (be16_to_cpup(prev) == val)
>> +             return;
>> +     *prev = cpu_to_be16(val);
>> +
>> +     dev_info(di->dev, "update chip %d, class %u, block %u, offset %u, value %u\n",
>> +              di->chip, buf->class, buf->block, reg->offset, val);
>> +
>
> This should probably be dev_debug, no normal user cares about all this
> detail.
>
>> +     buf->updt = true;
>> +     return;
>> +}
>> +
>> +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 (cfgup) {
>> +             ret = di->bus.write(di, BQ27XXX_CONTROL, BQ27XXX_SET_CFGUPDATE, false);
>> +             if (ret < 0)
>> +                     goto out1;
>> +     }
>> +
>> +     ret = di->bus.write(di, BQ27XXX_BLOCK_DATA_CONTROL, 0, true);
>> +     if (ret < 0)
>> +             goto out2;
>> +
>> +     ret = di->bus.write(di, BQ27XXX_DATA_CLASS, buf->class, true);
>> +     if (ret < 0)
>> +             goto out2;
>> +
>> +     ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, buf->block, true);
>> +     if (ret < 0)
>> +             goto out2;
>> +
>> +     BQ27XXX_MSLEEP(1);
>> +
>> +     ret = di->bus.write_bulk(di, BQ27XXX_BLOCK_DATA, buf->a, sizeof buf->a);
>> +     if (ret < 0)
>> +             goto out2;
>> +
>> +     ret = di->bus.write(di, BQ27XXX_BLOCK_DATA_CHECKSUM,
>> +                         bq27xxx_battery_checksum(buf), true);
>> +     if (ret < 0)
>> +             goto out2;
>> +
>> +     /* THE FOLLOWING CODE IS TOXIC. DO NOT USE!
>
> Then why include it, what is this code supposed to do, and what do we
> lose without it? If it is really needed we can find a way to guarantee
> the delay length.
>
>> +      * If the 350ms delay is insufficient, NVM corruption results
>> +      * on the '425 chip, which could damage the chip.
>> +      * It was suggested in this TI tool:
>> +      *   http://git.ti.com/bms-linux/bqtool/blobs/master/gauge.c#line328
>> +      *
>> +      * BQ27XXX_MSLEEP(350);
>> +      * ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, buf->block, true);
>> +      * BQ27XXX_MSLEEP(1);
>> +      * ret = di->bus.read(di, BQ27XXX_BLOCK_DATA_CHECKSUM, true);
>> +      * if ((u8)ret != bq27xxx_battery_checksum(buf))
>> +      *      ret = -EINVAL;
>> +      */
>> +
>> +     if (cfgup) {
>> +             BQ27XXX_MSLEEP(1);
>> +             ret = di->bus.write(di, BQ27XXX_CONTROL, BQ27XXX_SOFT_RESET, false);
>> +             if (ret < 0)
>> +                     goto out1;
>> +     }
>> +
>> +     buf->updt = false;
>> +     return 0;
>> +
>> +out2:
>> +     if (cfgup)
>> +             di->bus.write(di, BQ27XXX_CONTROL, BQ27XXX_SOFT_RESET, false);
>> +out1:
>> +     dev_err(di->dev, "bus error in %s: %d\n", __func__, 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_ENERGY);
>> +     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);
>> +             if (bd.full) {
>> +                     /* 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.full && bd.class == bt.class && bd.block == bt.block;
>> +             if (!same)
>> +                     bq27xxx_battery_read_dm_block(di, &bt);
>> +             if (same ? bd.full : bt.full)
>> +                     bq27xxx_battery_update_dm_block(di, same ? &bd : &bt,
>> +                                     BQ27XXX_DM_TERMINATE_VOLTAGE,
>> +                                     info->voltage_min_design_uv / 1000);
>> +     }
>> +
>> +     if (bd.updt)
>> +             bq27xxx_battery_write_dm_block(di, &bd);
>> +     if (bt.updt)
>> +             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_print_config(di);
>> +     bq27xxx_battery_set_seal_state(di, true);
>> +}
>> +
>>  /*
>>   * Return the battery State-of-Charge
>>   * Or < 0 if something fails.
>> @@ -1006,6 +1390,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 +1430,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 +1458,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);
>>

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v7 7/9] power: bq27xxx_battery: Add power_supply_battery_info support
  2017-02-23 18:02                       ` Andrew F. Davis
@ 2017-02-23 21:28                         ` Liam Breck
  2017-02-24 14:51                           ` Andrew F. Davis
  0 siblings, 1 reply; 34+ messages in thread
From: Liam Breck @ 2017-02-23 21:28 UTC (permalink / raw)
  To: Andrew F. Davis; +Cc: Sebastian Reichel, linux-pm, Matt Ranostay

On Thu, Feb 23, 2017 at 10:02 AM, Andrew F. Davis <afd@ti.com> wrote:
> On 02/23/2017 11:36 AM, Liam Breck wrote:
>> On Thu, Feb 23, 2017 at 8:53 AM, Andrew F. Davis <afd@ti.com> wrote:
>>> On 02/23/2017 10:38 AM, Liam Breck wrote:
>>>> On Thu, Feb 23, 2017 at 8:08 AM, Andrew F. Davis <afd@ti.com> wrote:
>>>>> On 02/23/2017 09:49 AM, Liam Breck wrote:
>>>>>> On Thu, Feb 23, 2017 at 7:17 AM, Andrew F. Davis <afd@ti.com> wrote:
>>>>>>> On 02/22/2017 06:54 PM, Liam Breck wrote:
>>>>>>>> On Wed, Feb 22, 2017 at 4:30 PM, Andrew F. Davis <afd@ti.com> wrote:
>>>>>>>>> On 02/22/2017 03:29 PM, Liam Breck wrote:
>>>>>>>>>> On Wed, Feb 22, 2017 at 12:35 PM, Andrew F. Davis <afd@ti.com> wrote:
>>>>>>>>>>> On 02/21/2017 03:30 PM, Liam Breck wrote:
>>>>>>>>>>>> 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 | 399 ++++++++++++++++++++++++++++++++-
>>>>>>>>>>>>  1 file changed, 397 insertions(+), 2 deletions(-)
>>>>>>>>>>>>
>>>>>>>>>>>> diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
>>>>>>>>>>>> index 7475a5f..8085d4a 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"
>>>>>>>>>>>>
>>>>>>>>>>>> @@ -452,6 +452,99 @@ static struct {
>>>>>>>>>>>>  static DEFINE_MUTEX(bq27xxx_list_lock);
>>>>>>>>>>>>  static LIST_HEAD(bq27xxx_battery_devices);
>>>>>>>>>>>>
>>>>>>>>>>>> +/* writable registers */
>>>>>>>>>>>> +#define BQ27XXX_CONTROL                      0x00
>>>>>>>>>>>> +#define BQ27XXX_DATA_CLASS           0x3E
>>>>>>>>>>>> +#define BQ27XXX_DATA_BLOCK           0x3F
>>>>>>>>>>>> +#define BQ27XXX_BLOCK_DATA           0x40
>>>>>>>>>>>> +#define BQ27XXX_BLOCK_DATA_CHECKSUM  0x60
>>>>>>>>>>>> +#define BQ27XXX_BLOCK_DATA_CONTROL   0x61
>>>>>>>>>>>> +
>>>>>>>>>>>
>>>>>>>>>>> These all belong in the bq27xxx_regs array, you can add new register
>>>>>>>>>>> types to the bq27xxx_reg_index enum.
>>>>>>>>>>
>>>>>>>>>> They're the same on all chips. Let's dup this code 9+ times when we have cause.
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> They are not the same on all chips, some chips don't have these
>>>>>>>>> registers at all. Plus, BQ27XXX_CONTROL is the same for all chips, and
>>>>>>>>> we already have it in all the chips bq27xxx_regs.
>>>>>>>>
>>>>>>>> Datasheets? Do those chips even support this feature set? Bqtool
>>>>>>>> doesn't appear to support any other memory update scheme.
>>>>>>>>
>>>>>>>
>>>>>>> Many chips don't, which is why we don't want these registers being
>>>>>>> global defines, you can have the chips without these registers as
>>>>>>> INVALID_REG_ADDR
>>>>>>
>>>>>> Chips that don't support memory update won't have a bq27xxx_dm_regs
>>>>>> list; we look for that at the start of this code path and do nothing
>>>>>> if it's null for di->chip. All chips that do support memory update use
>>>>>> the same command registers, so there's no reason to dup those.
>>>>>>
>>>>>
>>>>> Someday they might use a different value, we don't lose anything by
>>>>> putting these device registers in the table with the other device
>>>>> registers. I would rather duplicate some lines if it makes the code more
>>>>> uniform and easier to follow, again this is not a contest, we write code
>>>>> for ourselves, let the compiler organize/optimize things.
>>>>
>>>> I prefer the principle that every line you write must have a purpose
>>>> in the current codebase, vs a future one. But if you are adamant, I
>>>> capitulate.
>>>>
>>>
>>> Normally I would agree, but for listing registers on a device we would
>>> have a register map that looks like swiss cheese.
>>
>> Was that adamant? Keep the readable registers in tables; make the
>> writable ones universal since they're all the same.
>>
>>>>>> Also you can delete REG_CTRL as it's not used by the existing code  :-)
>>>>>>
>>>>>
>>>>> True, but as before, I just like having the complete register set in a
>>>>> nice simple table, as opposed to having to add and change things all
>>>>> over if/when we may need that register.
>>>>>
>>>>> Being a static table, I wonder if a sufficiently smart compiler could
>>>>> see that it is not used and optimize the value out for us...
>>>>
>>>> No :-p All the compiler can do for unused data is issue a warning. And
>>>> it can't do so for array elements.
>>>>
>>>
>>> I said "sufficiently smart compiler", it could find that no code within
>>> the tables context accesses it outside of the set indexes, it could then
>>> even remove the tables altogether and replace accesses with immediate
>>> values. Not practical, but not impossible, I think.. :)
>>
>> I'm not so interested in hypothetical compilers and chips. There are
>> reasons why compilers don't optimize away data.
>>
>>>> You really should define those tables in a function which memcpy's the
>>>> right one onto di.regs.
>>>
>>> What would that do for us? We still need to store the tables, it just
>>> removes one level of pointers.
>>
>> It would store 1 table for the life of the kernel, vs 9+
>>
>
> Helping the compiler optimize away a couple bytes is much less important
> than readability and maintainability of the code base, IMHO.

It's 176 bytes (8x22) after adding the new registers in this patch.
And more again when more chip IDs are added.

Not a burden for most environments...

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v7 7/9] power: bq27xxx_battery: Add power_supply_battery_info support
  2017-02-23 21:15     ` Liam Breck
@ 2017-02-24 14:47       ` Andrew F. Davis
  0 siblings, 0 replies; 34+ messages in thread
From: Andrew F. Davis @ 2017-02-24 14:47 UTC (permalink / raw)
  To: Liam Breck; +Cc: Sebastian Reichel, linux-pm, Matt Ranostay

I can get ahold of any BQ27xxx part, I'm holding off on testing until
the comments start to get sorted out, that way I don't have to test
again after all these changes.

On 02/23/2017 03:15 PM, Liam Breck wrote:
> Did you have a chance to test the patchset against hardware?
> 
> If you don't have a board with a 425 chip, I can send you the dm_regs
> tables for whatever parts you have to hand...
> 
> 
> On Wed, Feb 22, 2017 at 12:35 PM, Andrew F. Davis <afd@ti.com> wrote:
>> On 02/21/2017 03:30 PM, Liam Breck wrote:
>>> 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 | 399 ++++++++++++++++++++++++++++++++-
>>>  1 file changed, 397 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
>>> index 7475a5f..8085d4a 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"
>>>
>>> @@ -452,6 +452,99 @@ static struct {
>>>  static DEFINE_MUTEX(bq27xxx_list_lock);
>>>  static LIST_HEAD(bq27xxx_battery_devices);
>>>
>>> +/* writable registers */
>>> +#define BQ27XXX_CONTROL                      0x00
>>> +#define BQ27XXX_DATA_CLASS           0x3E
>>> +#define BQ27XXX_DATA_BLOCK           0x3F
>>> +#define BQ27XXX_BLOCK_DATA           0x40
>>> +#define BQ27XXX_BLOCK_DATA_CHECKSUM  0x60
>>> +#define BQ27XXX_BLOCK_DATA_CONTROL   0x61
>>> +
>>
>> These all belong in the bq27xxx_regs array, you can add new register
>> types to the bq27xxx_reg_index enum.
>>
>>> +/* control register params */
>>> +#define BQ27XXX_SEALED                       0x20
>>> +#define BQ27XXX_SET_CFGUPDATE                0x13
>>> +#define BQ27XXX_SOFT_RESET           0x42
>>> +
>>> +#define BQ27XXX_MSLEEP(i) usleep_range((i)*1000, (i)*1000+500)
>>
>> why?
>>
>>> +
>>> +struct bq27xxx_dm_reg {
>>> +     u8 subclass_id;
>>> +     u8 offset;
>>> +     u8 bytes;
>>> +     u16 min, max;
>>> +};
>>> +
>>> +struct bq27xxx_dm_buf {
>>> +     u8 class;
>>> +     u8 block;
>>> +     u8 a[32];
>>> +     bool full, updt;
>>> +};
>>> +
>>> +#define BQ27XXX_DM_BUF_PTR(b, r) \
>>> +     ( (u16*) &(b).a[ (r)->offset % sizeof (b).a ] )
>>> +
>>
>> This could compete in the IOCCC, there has to be a more readable way to
>> do this..
>>
>>> +#define BQ27XXX_DM_BUF(di, i) { \
>>> +     .class = bq27xxx_dm_regs[(di)->chip][i].subclass_id, \
>>> +     .block = bq27xxx_dm_regs[(di)->chip][i].offset / sizeof ((struct bq27xxx_dm_buf*)0)->a, \
>>> +}
>>> +
>>> +static inline bool bq27xxx_dm_buf_set(struct bq27xxx_dm_buf *buf,
>>> +                                   struct bq27xxx_dm_reg *reg) {
>>> +     if (buf->class == reg->subclass_id
>>> +      && buf->block == reg->offset / sizeof buf->a
>>> +      && buf->full)
>>
>> Some alignment issues here.
>>
>>> +             return false;
>>> +     buf->class = reg->subclass_id;
>>> +     buf->block = reg->offset / sizeof buf->a;
>>> +     buf->full = buf->updt = false;
>>
>> newline before return
>>
>>> +     return true;
>>> +}
>>> +
>>> +enum bq27xxx_dm_reg_id {
>>> +     BQ27XXX_DM_DESIGN_CAPACITY = 0,
>>> +     BQ27XXX_DM_DESIGN_ENERGY,
>>> +     BQ27XXX_DM_TERMINATE_VOLTAGE,
>>> +     BQ27XXX_DM_END,
>>> +};
>>> +
>>> +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 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 bq27421_dm_regs[] = { /* not tested */
>>> +     [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 },
>>> +};
>>> +
>>> +static struct bq27xxx_dm_reg bq27621_dm_regs[] = { /* not tested */
>>> +     [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[] = {
>>> +     [BQ27421] = bq27421_dm_regs, /* and BQ27441 */
>>> +     [BQ27425] = bq27425_dm_regs,
>>> +/*   [BQ27621] = */ bq27621_dm_regs,
>>> +};
>>> +
>>> +static u32 bq27xxx_unseal_keys[] = {
>>> +     [BQ27421] = 0x80008000, /* and BQ27441 */
>>> +     [BQ27425] = 0x04143672,
>>> +/*   [BQ27621] = */ 0x80008000,
>>
>> Why is this commented out.
>>
>>> +};
>>> +
>>> +
>>>  static int poll_interval_param_set(const char *val, const struct kernel_param *kp)
>>>  {
>>>       struct bq27xxx_device_info *di;
>>> @@ -496,6 +589,297 @@ 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)
>>> +{
>>> +     u32 key = bq27xxx_unseal_keys[di->chip];
>>> +     int ret;
>>> +
>>> +     if (state) {
>>> +             ret = di->bus.write(di, BQ27XXX_CONTROL, BQ27XXX_SEALED, false);
>>> +             if (ret < 0)
>>> +                     goto out;
>>> +     } else {
>>> +             ret = di->bus.write(di, BQ27XXX_CONTROL, (u16)(key >> 16), false);
>>> +             if (ret < 0)
>>> +                     goto out;
>>> +
>>> +             ret = di->bus.write(di, BQ27XXX_CONTROL, (u16)key, false);
>>> +             if (ret < 0)
>>> +                     goto out;
>>> +     }
>>> +     return 0;
>>> +
>>> +out:
>>> +     dev_err(di->dev, "bus error in %s: %d\n", __func__, ret);
>>
>> This is not a debug statement, we don't need the function name, just say
>> where it broke.
>>
>>> +     return ret;
>>> +}
>>> +
>>> +static u8 bq27xxx_battery_checksum(struct bq27xxx_dm_buf *buf)
>>> +{
>>> +     u16 sum = 0;
>>> +     int i;
>>> +
>>> +     for (i = 0; i < sizeof buf->a; i++)
>>
>> Linus would like a word with you:
>> https://lkml.org/lkml/2012/7/11/103
>>
>>> +             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, BQ27XXX_DATA_CLASS, buf->class, true);
>>> +     if (ret < 0)
>>> +             goto out;
>>> +
>>> +     ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, buf->block, true);
>>> +     if (ret < 0)
>>> +             goto out;
>>> +
>>> +     BQ27XXX_MSLEEP(1);
>>> +
>>> +     ret = di->bus.read_bulk(di, BQ27XXX_BLOCK_DATA, buf->a, sizeof buf->a);
>>> +     if (ret < 0)
>>> +             goto out;
>>> +
>>> +     ret = di->bus.read(di, BQ27XXX_BLOCK_DATA_CHECKSUM, true);
>>> +     if (ret < 0)
>>> +             goto out;
>>> +
>>> +     if ((u8)ret != bq27xxx_battery_checksum(buf)) {
>>> +             ret = -EINVAL;
>>> +             goto out;
>>> +     }
>>> +
>>> +     buf->full = true;
>>> +     buf->updt = false;
>>> +     return 0;
>>> +
>>> +out:
>>> +     dev_err(di->dev, "bus error in %s: %d\n", __func__, ret);
>>> +     return ret;
>>> +}
>>> +
>>> +static void bq27xxx_battery_print_config(struct bq27xxx_device_info *di)
>>> +{
>>> +     struct bq27xxx_dm_reg *reg = bq27xxx_dm_regs[di->chip];
>>> +     struct bq27xxx_dm_buf buf = { .class = 0xFF };
>>> +     int i;
>>> +
>>> +     for (i = 0; i < BQ27XXX_DM_END; i++, reg++) {
>>> +             const char* str = bq27xxx_dm_reg_name[i];
>>> +
>>> +             if (bq27xxx_dm_buf_set(&buf, reg))
>>> +                     if (bq27xxx_battery_read_dm_block(di, &buf) < 0)
>>> +                             continue;
>>> +
>>> +             if (reg->bytes == 2)
>>> +                     dev_info(di->dev, "config register %s is %d\n", str,
>>> +                              be16_to_cpup(BQ27XXX_DM_BUF_PTR(buf, reg)));
>>> +             else
>>> +                     dev_warn(di->dev, "unsupported config register %s\n", str);
>>> +     }
>>> +}
>>> +
>>> +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];
>>> +     u16 *prev;
>>> +
>>> +     if (reg->bytes != 2)
>>> +             return;
>>> +
>>> +     prev = BQ27XXX_DM_BUF_PTR(*buf, reg);
>>> +     if (be16_to_cpup(prev) == val)
>>> +             return;
>>> +     *prev = cpu_to_be16(val);
>>> +
>>> +     dev_info(di->dev, "update chip %d, class %u, block %u, offset %u, value %u\n",
>>> +              di->chip, buf->class, buf->block, reg->offset, val);
>>> +
>>
>> This should probably be dev_debug, no normal user cares about all this
>> detail.
>>
>>> +     buf->updt = true;
>>> +     return;
>>> +}
>>> +
>>> +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 (cfgup) {
>>> +             ret = di->bus.write(di, BQ27XXX_CONTROL, BQ27XXX_SET_CFGUPDATE, false);
>>> +             if (ret < 0)
>>> +                     goto out1;
>>> +     }
>>> +
>>> +     ret = di->bus.write(di, BQ27XXX_BLOCK_DATA_CONTROL, 0, true);
>>> +     if (ret < 0)
>>> +             goto out2;
>>> +
>>> +     ret = di->bus.write(di, BQ27XXX_DATA_CLASS, buf->class, true);
>>> +     if (ret < 0)
>>> +             goto out2;
>>> +
>>> +     ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, buf->block, true);
>>> +     if (ret < 0)
>>> +             goto out2;
>>> +
>>> +     BQ27XXX_MSLEEP(1);
>>> +
>>> +     ret = di->bus.write_bulk(di, BQ27XXX_BLOCK_DATA, buf->a, sizeof buf->a);
>>> +     if (ret < 0)
>>> +             goto out2;
>>> +
>>> +     ret = di->bus.write(di, BQ27XXX_BLOCK_DATA_CHECKSUM,
>>> +                         bq27xxx_battery_checksum(buf), true);
>>> +     if (ret < 0)
>>> +             goto out2;
>>> +
>>> +     /* THE FOLLOWING CODE IS TOXIC. DO NOT USE!
>>
>> Then why include it, what is this code supposed to do, and what do we
>> lose without it? If it is really needed we can find a way to guarantee
>> the delay length.
>>
>>> +      * If the 350ms delay is insufficient, NVM corruption results
>>> +      * on the '425 chip, which could damage the chip.
>>> +      * It was suggested in this TI tool:
>>> +      *   http://git.ti.com/bms-linux/bqtool/blobs/master/gauge.c#line328
>>> +      *
>>> +      * BQ27XXX_MSLEEP(350);
>>> +      * ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, buf->block, true);
>>> +      * BQ27XXX_MSLEEP(1);
>>> +      * ret = di->bus.read(di, BQ27XXX_BLOCK_DATA_CHECKSUM, true);
>>> +      * if ((u8)ret != bq27xxx_battery_checksum(buf))
>>> +      *      ret = -EINVAL;
>>> +      */
>>> +
>>> +     if (cfgup) {
>>> +             BQ27XXX_MSLEEP(1);
>>> +             ret = di->bus.write(di, BQ27XXX_CONTROL, BQ27XXX_SOFT_RESET, false);
>>> +             if (ret < 0)
>>> +                     goto out1;
>>> +     }
>>> +
>>> +     buf->updt = false;
>>> +     return 0;
>>> +
>>> +out2:
>>> +     if (cfgup)
>>> +             di->bus.write(di, BQ27XXX_CONTROL, BQ27XXX_SOFT_RESET, false);
>>> +out1:
>>> +     dev_err(di->dev, "bus error in %s: %d\n", __func__, 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_ENERGY);
>>> +     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);
>>> +             if (bd.full) {
>>> +                     /* 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.full && bd.class == bt.class && bd.block == bt.block;
>>> +             if (!same)
>>> +                     bq27xxx_battery_read_dm_block(di, &bt);
>>> +             if (same ? bd.full : bt.full)
>>> +                     bq27xxx_battery_update_dm_block(di, same ? &bd : &bt,
>>> +                                     BQ27XXX_DM_TERMINATE_VOLTAGE,
>>> +                                     info->voltage_min_design_uv / 1000);
>>> +     }
>>> +
>>> +     if (bd.updt)
>>> +             bq27xxx_battery_write_dm_block(di, &bd);
>>> +     if (bt.updt)
>>> +             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_print_config(di);
>>> +     bq27xxx_battery_set_seal_state(di, true);
>>> +}
>>> +
>>>  /*
>>>   * Return the battery State-of-Charge
>>>   * Or < 0 if something fails.
>>> @@ -1006,6 +1390,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 +1430,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 +1458,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);
>>>

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v7 7/9] power: bq27xxx_battery: Add power_supply_battery_info support
  2017-02-23 21:28                         ` Liam Breck
@ 2017-02-24 14:51                           ` Andrew F. Davis
  0 siblings, 0 replies; 34+ messages in thread
From: Andrew F. Davis @ 2017-02-24 14:51 UTC (permalink / raw)
  To: Liam Breck; +Cc: Sebastian Reichel, linux-pm, Matt Ranostay

On 02/23/2017 03:28 PM, Liam Breck wrote:
> On Thu, Feb 23, 2017 at 10:02 AM, Andrew F. Davis <afd@ti.com> wrote:
>> On 02/23/2017 11:36 AM, Liam Breck wrote:
>>> On Thu, Feb 23, 2017 at 8:53 AM, Andrew F. Davis <afd@ti.com> wrote:
>>>> On 02/23/2017 10:38 AM, Liam Breck wrote:
>>>>> On Thu, Feb 23, 2017 at 8:08 AM, Andrew F. Davis <afd@ti.com> wrote:
>>>>>> On 02/23/2017 09:49 AM, Liam Breck wrote:
>>>>>>> On Thu, Feb 23, 2017 at 7:17 AM, Andrew F. Davis <afd@ti.com> wrote:
>>>>>>>> On 02/22/2017 06:54 PM, Liam Breck wrote:
>>>>>>>>> On Wed, Feb 22, 2017 at 4:30 PM, Andrew F. Davis <afd@ti.com> wrote:
>>>>>>>>>> On 02/22/2017 03:29 PM, Liam Breck wrote:
>>>>>>>>>>> On Wed, Feb 22, 2017 at 12:35 PM, Andrew F. Davis <afd@ti.com> wrote:
>>>>>>>>>>>> On 02/21/2017 03:30 PM, Liam Breck wrote:
>>>>>>>>>>>>> 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 | 399 ++++++++++++++++++++++++++++++++-
>>>>>>>>>>>>>  1 file changed, 397 insertions(+), 2 deletions(-)
>>>>>>>>>>>>>
>>>>>>>>>>>>> diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
>>>>>>>>>>>>> index 7475a5f..8085d4a 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"
>>>>>>>>>>>>>
>>>>>>>>>>>>> @@ -452,6 +452,99 @@ static struct {
>>>>>>>>>>>>>  static DEFINE_MUTEX(bq27xxx_list_lock);
>>>>>>>>>>>>>  static LIST_HEAD(bq27xxx_battery_devices);
>>>>>>>>>>>>>
>>>>>>>>>>>>> +/* writable registers */
>>>>>>>>>>>>> +#define BQ27XXX_CONTROL                      0x00
>>>>>>>>>>>>> +#define BQ27XXX_DATA_CLASS           0x3E
>>>>>>>>>>>>> +#define BQ27XXX_DATA_BLOCK           0x3F
>>>>>>>>>>>>> +#define BQ27XXX_BLOCK_DATA           0x40
>>>>>>>>>>>>> +#define BQ27XXX_BLOCK_DATA_CHECKSUM  0x60
>>>>>>>>>>>>> +#define BQ27XXX_BLOCK_DATA_CONTROL   0x61
>>>>>>>>>>>>> +
>>>>>>>>>>>>
>>>>>>>>>>>> These all belong in the bq27xxx_regs array, you can add new register
>>>>>>>>>>>> types to the bq27xxx_reg_index enum.
>>>>>>>>>>>
>>>>>>>>>>> They're the same on all chips. Let's dup this code 9+ times when we have cause.
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> They are not the same on all chips, some chips don't have these
>>>>>>>>>> registers at all. Plus, BQ27XXX_CONTROL is the same for all chips, and
>>>>>>>>>> we already have it in all the chips bq27xxx_regs.
>>>>>>>>>
>>>>>>>>> Datasheets? Do those chips even support this feature set? Bqtool
>>>>>>>>> doesn't appear to support any other memory update scheme.
>>>>>>>>>
>>>>>>>>
>>>>>>>> Many chips don't, which is why we don't want these registers being
>>>>>>>> global defines, you can have the chips without these registers as
>>>>>>>> INVALID_REG_ADDR
>>>>>>>
>>>>>>> Chips that don't support memory update won't have a bq27xxx_dm_regs
>>>>>>> list; we look for that at the start of this code path and do nothing
>>>>>>> if it's null for di->chip. All chips that do support memory update use
>>>>>>> the same command registers, so there's no reason to dup those.
>>>>>>>
>>>>>>
>>>>>> Someday they might use a different value, we don't lose anything by
>>>>>> putting these device registers in the table with the other device
>>>>>> registers. I would rather duplicate some lines if it makes the code more
>>>>>> uniform and easier to follow, again this is not a contest, we write code
>>>>>> for ourselves, let the compiler organize/optimize things.
>>>>>
>>>>> I prefer the principle that every line you write must have a purpose
>>>>> in the current codebase, vs a future one. But if you are adamant, I
>>>>> capitulate.
>>>>>
>>>>
>>>> Normally I would agree, but for listing registers on a device we would
>>>> have a register map that looks like swiss cheese.
>>>
>>> Was that adamant? Keep the readable registers in tables; make the
>>> writable ones universal since they're all the same.
>>>
>>>>>>> Also you can delete REG_CTRL as it's not used by the existing code  :-)
>>>>>>>
>>>>>>
>>>>>> True, but as before, I just like having the complete register set in a
>>>>>> nice simple table, as opposed to having to add and change things all
>>>>>> over if/when we may need that register.
>>>>>>
>>>>>> Being a static table, I wonder if a sufficiently smart compiler could
>>>>>> see that it is not used and optimize the value out for us...
>>>>>
>>>>> No :-p All the compiler can do for unused data is issue a warning. And
>>>>> it can't do so for array elements.
>>>>>
>>>>
>>>> I said "sufficiently smart compiler", it could find that no code within
>>>> the tables context accesses it outside of the set indexes, it could then
>>>> even remove the tables altogether and replace accesses with immediate
>>>> values. Not practical, but not impossible, I think.. :)
>>>
>>> I'm not so interested in hypothetical compilers and chips. There are
>>> reasons why compilers don't optimize away data.
>>>
>>>>> You really should define those tables in a function which memcpy's the
>>>>> right one onto di.regs.
>>>>
>>>> What would that do for us? We still need to store the tables, it just
>>>> removes one level of pointers.
>>>
>>> It would store 1 table for the life of the kernel, vs 9+
>>>
>>
>> Helping the compiler optimize away a couple bytes is much less important
>> than readability and maintainability of the code base, IMHO.
> 
> It's 176 bytes (8x22) after adding the new registers in this patch.
> And more again when more chip IDs are added.
> 
> Not a burden for most environments...
> 

If it is really a huge annoyance for you, moving these registers into a
separate bq27xxx_dm_regs style array may also be acceptable, I'm just
not a fan of #define some registers when we have other registers in
tables. Should be all one way or all the other, how you organize the
tables isn't too big of a concern as long as there is some logic to it.

Andrew

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v7 0/9] devicetree battery support and client bq27xxx_battery
  2017-02-21 21:29 [PATCH v7 0/9] devicetree battery support and client bq27xxx_battery Liam Breck
                   ` (7 preceding siblings ...)
  2017-02-21 21:30 ` [PATCH v7 9/9] power: bq27xxx_battery_i2c: Add I2C bulk read/write functions Liam Breck
@ 2017-03-02 22:52 ` Liam Breck
  2017-03-02 23:04   ` Andrew F. Davis
  8 siblings, 1 reply; 34+ messages in thread
From: Liam Breck @ 2017-03-02 22:52 UTC (permalink / raw)
  To: Sebastian Reichel; +Cc: Andrew F. Davis, linux-pm, Matt Ranostay

Hi Andrew,

I have a new rev ready,with support for bq27500 & 545 & 421. How soon
could you test it? If this week, I'll post ASAP...


On Tue, Feb 21, 2017 at 1:29 PM, Liam Breck <liam@networkimprov.net> wrote:
> 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 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 (5):
>   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
>   power: bq27xxx_battery: Add print_dm() to log chip memory
>
> Matt Ranostay (4):
>   devicetree: property-units: Add uWh and uAh units
>   power: bq27xxx_battery: Define access methods to write chip registers
>   power: bq27xxx_battery: Add BQ27425 chip id
>   power: bq27xxx_battery_i2c: Add I2C bulk read/write functions
>
>  .../devicetree/bindings/power/supply/battery.txt   |  42 ++
>  .../devicetree/bindings/power/supply/bq27xxx.txt   |  11 +-
>  .../devicetree/bindings/property-units.txt         |   2 +
>  drivers/power/supply/bq27xxx_battery.c             | 473 ++++++++++++++++++++-
>  drivers/power/supply/bq27xxx_battery_i2c.c         |  84 +++-
>  drivers/power/supply/power_supply_core.c           |  40 ++
>  include/linux/power/bq27xxx_battery.h              |   6 +-
>  include/linux/power_supply.h                       |  18 +
>  8 files changed, 669 insertions(+), 7 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/power/supply/battery.txt
>
> --
> 2.9.3
>

^ permalink raw reply	[flat|nested] 34+ messages in thread

* Re: [PATCH v7 0/9] devicetree battery support and client bq27xxx_battery
  2017-03-02 22:52 ` [PATCH v7 0/9] devicetree battery support and client bq27xxx_battery Liam Breck
@ 2017-03-02 23:04   ` Andrew F. Davis
  0 siblings, 0 replies; 34+ messages in thread
From: Andrew F. Davis @ 2017-03-02 23:04 UTC (permalink / raw)
  To: Liam Breck, Sebastian Reichel; +Cc: linux-pm, Matt Ranostay

On 03/02/2017 04:52 PM, Liam Breck wrote:
> Hi Andrew,
> 
> I have a new rev ready,with support for bq27500 & 545 & 421. How soon
> could you test it? If this week, I'll post ASAP...
> 

Probably no time this week, so take your time. Although it never hurts
to post.

Andrew

> 
> On Tue, Feb 21, 2017 at 1:29 PM, Liam Breck <liam@networkimprov.net> wrote:
>> 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 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 (5):
>>   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
>>   power: bq27xxx_battery: Add print_dm() to log chip memory
>>
>> Matt Ranostay (4):
>>   devicetree: property-units: Add uWh and uAh units
>>   power: bq27xxx_battery: Define access methods to write chip registers
>>   power: bq27xxx_battery: Add BQ27425 chip id
>>   power: bq27xxx_battery_i2c: Add I2C bulk read/write functions
>>
>>  .../devicetree/bindings/power/supply/battery.txt   |  42 ++
>>  .../devicetree/bindings/power/supply/bq27xxx.txt   |  11 +-
>>  .../devicetree/bindings/property-units.txt         |   2 +
>>  drivers/power/supply/bq27xxx_battery.c             | 473 ++++++++++++++++++++-
>>  drivers/power/supply/bq27xxx_battery_i2c.c         |  84 +++-
>>  drivers/power/supply/power_supply_core.c           |  40 ++
>>  include/linux/power/bq27xxx_battery.h              |   6 +-
>>  include/linux/power_supply.h                       |  18 +
>>  8 files changed, 669 insertions(+), 7 deletions(-)
>>  create mode 100644 Documentation/devicetree/bindings/power/supply/battery.txt
>>
>> --
>> 2.9.3
>>

^ permalink raw reply	[flat|nested] 34+ messages in thread

end of thread, other threads:[~2017-03-03 18:23 UTC | newest]

Thread overview: 34+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-02-21 21:29 [PATCH v7 0/9] devicetree battery support and client bq27xxx_battery Liam Breck
     [not found] ` <20170221213008.30044-1-liam-RYWXG+zxWwBdeoIcmNTgJF6hYfS7NtTn@public.gmane.org>
2017-02-21 21:30   ` [PATCH v7 1/9] devicetree: power: Add battery.txt Liam Breck
     [not found]     ` <20170221213008.30044-2-liam-RYWXG+zxWwBdeoIcmNTgJF6hYfS7NtTn@public.gmane.org>
2017-02-22 20:05       ` Andrew F. Davis
     [not found]         ` <bd747560-8780-3294-86dd-f7f7f4de1736-l0cyMroinI0@public.gmane.org>
2017-02-22 21:39           ` Liam Breck
2017-02-21 21:30   ` [PATCH v7 3/9] devicetree: power: bq27xxx: Add monitored-battery documentation Liam Breck
2017-02-21 21:30 ` [PATCH v7 2/9] devicetree: property-units: Add uWh and uAh units Liam Breck
2017-02-21 21:30   ` Liam Breck
2017-02-21 21:30 ` [PATCH v7 4/9] power: power_supply: Add power_supply_battery_info and API Liam Breck
2017-02-21 21:30 ` [PATCH v7 5/9] power: bq27xxx_battery: Define access methods to write chip registers Liam Breck
2017-02-21 21:30 ` [PATCH v7 6/9] power: bq27xxx_battery: Add BQ27425 chip id Liam Breck
2017-02-22 20:15   ` Andrew F. Davis
2017-02-21 21:30 ` [PATCH v7 7/9] power: bq27xxx_battery: Add power_supply_battery_info support Liam Breck
2017-02-22 20:35   ` Andrew F. Davis
2017-02-22 21:29     ` Liam Breck
2017-02-23  0:30       ` Andrew F. Davis
2017-02-23  0:54         ` Liam Breck
2017-02-23 15:17           ` Andrew F. Davis
2017-02-23 15:49             ` Liam Breck
2017-02-23 16:08               ` Andrew F. Davis
2017-02-23 16:38                 ` Liam Breck
2017-02-23 16:53                   ` Andrew F. Davis
2017-02-23 17:36                     ` Liam Breck
2017-02-23 18:02                       ` Andrew F. Davis
2017-02-23 21:28                         ` Liam Breck
2017-02-24 14:51                           ` Andrew F. Davis
2017-02-23 21:15     ` Liam Breck
2017-02-24 14:47       ` Andrew F. Davis
2017-02-21 21:30 ` [PATCH v7 8/9] power: bq27xxx_battery: Add print_dm() to log chip memory Liam Breck
2017-02-22 20:39   ` Andrew F. Davis
2017-02-22 21:36     ` Liam Breck
2017-02-23  0:34       ` Andrew F. Davis
2017-02-21 21:30 ` [PATCH v7 9/9] power: bq27xxx_battery_i2c: Add I2C bulk read/write functions Liam Breck
2017-03-02 22:52 ` [PATCH v7 0/9] devicetree battery support and client bq27xxx_battery Liam Breck
2017-03-02 23:04   ` Andrew F. Davis

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.