Skip to contentSkip to conversation

devicetree battery support and client bq27xxx_battery

PATCH Superseded Liam Breck 5 participants 37 messages Feb 27, 2017 – Mar 18, 2017 7 messages not in the archive

Subsystems (4)

v8 of 14 Superseded Latest is v14 →

lore
Share & tools
lore

Liam Breck

author
#0 lore raw

v8 cover devicetree battery support and client bq27xxx_battery

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 v8:
bq27xxx_battery:

*   wait on flag after set_cfgupdate & soft_reset
*   drop print_config(), report status in update_dm_block()
*   clarify error messages
*   cleanup from Andrew's feedback; minor polishing

Changes in v7:
bq27xxx_battery:

*   support chips where terminate_voltage & design_* live in separate blocks
*   draft support for 421, 441, 621 chips
*   new patch to log chip memory fields
*   report bus I/O errors; return error code in bq27xxx_battery_i2c
*   verify checksum in read_dm_block()
*   use set_cfgupdate only if chip provides it, soft_reset on I/O error
*   block_data_control=0 only in write_dm_block()
*   note toxic code from TI bqtool in write_dm_block()
*   lots of functionally neutral polishing

Documentation/devicetree/.../battery.txt:
* mention power_supply_get_battery_info()

Changes in v6:
* Documentation/devictree/... fixes
* bq27xxx_battery: clarify names
* bq27xxx_battery: verify that selected registers are supported
* bq27xxx_battery: allocate NVM buffer on stack
* bq27xxx_battery_i2c: fix return code of bulk_read

Changes in v5:
* incorporate feedback into Documentation/devicetree/.../battery.txt
* use power_supply_prop_* names in devicetree and power_supply_battery_info
* default fields to -EINVAL in power_supply_battery_info
* power_supply_get_battery_info() always looks for "monitored-battery"
* power_supply_get_battery_info() emits a warning if !psy->of_node
* squash patches for power_supply_battery_info
* bq27xxx_battery: check power_supply_battery_info values
* bq27xxx_battery: note missing power_supply_prop_* features
* bq27xxx_battery: new patch for access methods

Changes in v4:
* add "fixed-battery" compatible field to be be more consistant with devicetree

Changes in v3:
* split i2c changes into respective patches
* add documentation for battery information for fuel gauge
* rebased documentation patches on change on the list
* abstracted the battery configuration for the state machine to an generic struct and platform data access function

Changes in v2:
* add documentation for uWh and uAh property units
* change devicetree entries to match new property units

Liam (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_blocks() 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             | 551 ++++++++++++++++++++-
 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, 747 insertions(+), 7 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/power/supply/battery.txt
Signature · 1 line
2.9.3

A message that is not in the archive (sent off-list or not yet received): 20170227071117.18934-1-liam-RYWXG+zxWwBdeoIcmNTgJF6hYfS7NtTn@public.gmane.org

#2 lore raw

PATCH v8 1/9 devicetree: power: Add battery.txt

From: Liam Breck <[email protected]>

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.

---
 .../devicetree/bindings/power/supply/battery.txt   | 42 ++++++++++++++++++++++
 1 file changed, 42 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/power/supply/battery.txt
Documentation/devicetree/bindings/power/supply/battery.txt new file +42 -0
new file mode 100644
index 0000000..3d916bd
@@ -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>;
+		...
+	};
Signature · 6 lines
2.9.3

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

A message that is not in the archive (sent off-list or not yet received): 20170227071117.18934-2-liam-RYWXG+zxWwBdeoIcmNTgJF6hYfS7NtTn@public.gmane.org

#4 lore raw

Re: [PATCH v8 1/9] devicetree: power: Add battery.txt

Quoted 21 lines · Liam Breck

From: Liam Breck <[email protected]>

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.

---
 .../devicetree/bindings/power/supply/battery.txt   | 42 ++++++++++++++++++++++
 1 file changed, 42 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/power/supply/battery.txt
Documentation/devicetree/bindings/power/supply/battery.txt new file +0 -0
new file mode 100644
index 0000000..3d916bd

Documentation/devicetree/bindings/power/supply/battery.txt line 4

Quoted 4 lines
Documentation/devicetree/bindings/power/supply/battery.txt new file +4 -0
@@ -0,0 +1,42 @@
+Battery Characteristics
+
+Required Properties:
+ - compatible: Must be "fixed-battery"

Still not liking this name, but I don't have a better suggestion. Please describe here what is and isn't a "fixed battery".

Documentation/devicetree/bindings/power/supply/battery.txt line 12

Quoted 8 lines
Documentation/devicetree/bindings/power/supply/battery.txt new file +8 -0
+
+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.

Um, no. That's exactly not how DT bindings should be done.

Quoted 33 lines
Documentation/devicetree/bindings/power/supply/battery.txt new file +30 -0
+
+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>;
+		...
+	};
Signature · 1 line
2.9.3

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

#5 lore raw

Re: [PATCH v8 1/9] devicetree: power: Add battery.txt

Hi Rob,

Quoted 16 lines · Rob Herring
Quoted 15 lines · Liam Breck

From: Liam Breck <[email protected]>

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.

---
 .../devicetree/bindings/power/supply/battery.txt   | 42 ++++++++++++++++++++++
 1 file changed, 42 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/power/supply/battery.txt
Quoted 13 lines

Documentation/devicetree/bindings/power/supply/battery.txt line 4

Quoted 10 lines
Documentation/devicetree/bindings/power/supply/battery.txt new file +4 -0
new file mode 100644
index 0000000..3d916bd
@@ -0,0 +1,42 @@
+Battery Characteristics
+
+Required Properties:
+ - compatible: Must be "fixed-battery"

Still not liking this name, but I don't have a better suggestion. Please describe here what is and isn't a "fixed battery".

Sebastian...?

Quoted 10 lines

Documentation/devicetree/bindings/power/supply/battery.txt line 12

Quoted 8 lines
Documentation/devicetree/bindings/power/supply/battery.txt new file +8 -0
+
+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.

Um, no. That's exactly not how DT bindings should be done.

Power supply components surface stats in sysfs using the names in enum power_supply_property. For example, /sys/class/power_supply/.../voltage_min_design. Matching input (DT) and output (sysfs) names is sensible. The above comment is already attached to struct power_supply_battery_info, which is the initial destination for the above DT properties.

Shall I mention sysfs in the above comment?

Quoted 25 lines
Quoted 25 lines
Documentation/devicetree/bindings/power/supply/battery.txt new file +25 -0
+
+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>;
+             ...
+     };
+
Quoted 8 lines
Quoted 8 lines
Documentation/devicetree/bindings/power/supply/battery.txt new file +5 -1
+     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 [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html

maintainer
#6 lore raw

Re: [PATCH v8 1/9] devicetree: power: Add battery.txt

Quoted 46 lines · Liam Breck

Hi Rob,

Quoted 16 lines · Rob Herring
Quoted 15 lines · Liam Breck

From: Liam Breck <[email protected]>

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.

---
 .../devicetree/bindings/power/supply/battery.txt   | 42 ++++++++++++++++++++++
 1 file changed, 42 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/power/supply/battery.txt
Quoted 13 lines

Documentation/devicetree/bindings/power/supply/battery.txt line 4

Quoted 10 lines
Documentation/devicetree/bindings/power/supply/battery.txt new file +4 -0
new file mode 100644
index 0000000..3d916bd
@@ -0,0 +1,42 @@
+Battery Characteristics
+
+Required Properties:
+ - compatible: Must be "fixed-battery"

Still not liking this name, but I don't have a better suggestion. Please describe here what is and isn't a "fixed battery".

Sebastian...?

Quoted 10 lines

Documentation/devicetree/bindings/power/supply/battery.txt line 12

Quoted 8 lines
Documentation/devicetree/bindings/power/supply/battery.txt new file +8 -0
+
+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.

Um, no. That's exactly not how DT bindings should be done.

Quoted 8 lines

Power supply components surface stats in sysfs using the names in enum power_supply_property. For example, /sys/class/power_supply/.../voltage_min_design. Matching input (DT) and output (sysfs) names is sensible. The above comment is already attached to struct power_supply_battery_info, which is the initial destination for the above DT properties.

Shall I mention sysfs in the above comment?

Absolutely not. Bindings should not reference Linux.

While sometimes things can align, there is no guarantee that they will. DT is h/w description. sysfs is user configuration.

Rob

A message that is not in the archive (sent off-list or not yet received): CAL_Jsq+8Y=GtyjVZA-suZLH+ySDshaoU9qh96BF+PeUYK3FZxg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org

#8 lore raw

Re: [PATCH v8 1/9] devicetree: power: Add battery.txt

Quoted 54 lines · Rob Herring
Quoted 53 lines · Liam Breck

Hi Rob,

Quoted 16 lines · Rob Herring
Quoted 15 lines · Liam Breck

From: Liam Breck <[email protected]>

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.

---
 .../devicetree/bindings/power/supply/battery.txt   | 42 ++++++++++++++++++++++
 1 file changed, 42 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/power/supply/battery.txt
Quoted 13 lines

Documentation/devicetree/bindings/power/supply/battery.txt line 4

Quoted 10 lines
Documentation/devicetree/bindings/power/supply/battery.txt new file +4 -0
new file mode 100644
index 0000000..3d916bd
@@ -0,0 +1,42 @@
+Battery Characteristics
+
+Required Properties:
+ - compatible: Must be "fixed-battery"

Still not liking this name, but I don't have a better suggestion. Please describe here what is and isn't a "fixed battery".

Sebastian...?

Quoted 10 lines

Documentation/devicetree/bindings/power/supply/battery.txt line 12

Quoted 8 lines
Documentation/devicetree/bindings/power/supply/battery.txt new file +8 -0
+
+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.

Um, no. That's exactly not how DT bindings should be done.

Power supply components surface stats in sysfs using the names in enum power_supply_property. For example, /sys/class/power_supply/.../voltage_min_design. Matching input (DT) and output (sysfs) names is sensible. The above comment is already attached to struct power_supply_battery_info, which is the initial destination for the above DT properties.

Quoted 6 lines
Quoted 1 line

Shall I mention sysfs in the above comment?

Absolutely not. Bindings should not reference Linux.

While sometimes things can align, there is no guarantee that they will. DT is h/w description. sysfs is user configuration.

sysfs for power_supply also provides fixed hw characteristics.

Sebastian proposed DT:battery specifically to be consumed by power_supply_core. Allowing names in DT:battery and power_supply_property to diverge would cause confusion and wasted time, for no particular benefit. As there is no rationale to reconsider the names of these fields for DT:battery, let's write that into the docs.
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html

maintainer
#9 lore raw

Re: [PATCH v8 1/9] devicetree: power: Add battery.txt

Quoted 62 lines · Liam Breck
Quoted 54 lines · Rob Herring
Quoted 53 lines · Liam Breck

Hi Rob,

Quoted 16 lines · Rob Herring
Quoted 15 lines · Liam Breck

From: Liam Breck <[email protected]>

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.

---
 .../devicetree/bindings/power/supply/battery.txt   | 42 ++++++++++++++++++++++
 1 file changed, 42 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/power/supply/battery.txt
Quoted 13 lines

Documentation/devicetree/bindings/power/supply/battery.txt line 4

Quoted 10 lines
Documentation/devicetree/bindings/power/supply/battery.txt new file +4 -0
new file mode 100644
index 0000000..3d916bd
@@ -0,0 +1,42 @@
+Battery Characteristics
+
+Required Properties:
+ - compatible: Must be "fixed-battery"

Still not liking this name, but I don't have a better suggestion. Please describe here what is and isn't a "fixed battery".

Sebastian...?

Quoted 10 lines

Documentation/devicetree/bindings/power/supply/battery.txt line 12

Quoted 8 lines
Documentation/devicetree/bindings/power/supply/battery.txt new file +8 -0
+
+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.

Um, no. That's exactly not how DT bindings should be done.

Power supply components surface stats in sysfs using the names in enum power_supply_property. For example, /sys/class/power_supply/.../voltage_min_design. Matching input (DT) and output (sysfs) names is sensible. The above comment is already attached to struct power_supply_battery_info, which is the initial destination for the above DT properties.

Quoted 6 lines
Quoted 1 line

Shall I mention sysfs in the above comment?

Absolutely not. Bindings should not reference Linux.

While sometimes things can align, there is no guarantee that they will. DT is h/w description. sysfs is user configuration.

Quoted 8 lines

sysfs for power_supply also provides fixed hw characteristics.

Sebastian proposed DT:battery specifically to be consumed by power_supply_core. Allowing names in DT:battery and power_supply_property to diverge would cause confusion and wasted time, for no particular benefit. As there is no rationale to reconsider the names of these fields for DT:battery, let's write that into the docs.

Write it into the Linux docs then. The DT docs need to stand on their own for the standalone DT tree[1] that other projects import.

Rob

[1] https://git.kernel.org/pub/scm/linux/kernel/git/devicetree/devicetree-rebasing.git/

A message that is not in the archive (sent off-list or not yet received): CAL_JsqJOFUSRY_KPzwNWrx4FF1_F6XNK8OfkqhqgBpxH9WrZeQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org

#11 lore raw

Re: [PATCH v8 1/9] devicetree: power: Add battery.txt

Quoted 72 lines · Rob Herring
Quoted 62 lines · Liam Breck
Quoted 54 lines · Rob Herring
Quoted 53 lines · Liam Breck

Hi Rob,

Quoted 16 lines · Rob Herring
Quoted 15 lines · Liam Breck

From: Liam Breck <[email protected]>

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.

---
 .../devicetree/bindings/power/supply/battery.txt   | 42 ++++++++++++++++++++++
 1 file changed, 42 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/power/supply/battery.txt
Quoted 13 lines

Documentation/devicetree/bindings/power/supply/battery.txt line 4

Quoted 10 lines
Documentation/devicetree/bindings/power/supply/battery.txt new file +4 -0
new file mode 100644
index 0000000..3d916bd
@@ -0,0 +1,42 @@
+Battery Characteristics
+
+Required Properties:
+ - compatible: Must be "fixed-battery"

Still not liking this name, but I don't have a better suggestion. Please describe here what is and isn't a "fixed battery".

Sebastian...?

Quoted 10 lines

Documentation/devicetree/bindings/power/supply/battery.txt line 12

Quoted 8 lines
Documentation/devicetree/bindings/power/supply/battery.txt new file +8 -0
+
+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.

Um, no. That's exactly not how DT bindings should be done.

Power supply components surface stats in sysfs using the names in enum power_supply_property. For example, /sys/class/power_supply/.../voltage_min_design. Matching input (DT) and output (sysfs) names is sensible. The above comment is already attached to struct power_supply_battery_info, which is the initial destination for the above DT properties.

Quoted 6 lines
Quoted 1 line

Shall I mention sysfs in the above comment?

Absolutely not. Bindings should not reference Linux.

While sometimes things can align, there is no guarantee that they will. DT is h/w description. sysfs is user configuration.

Quoted 8 lines

sysfs for power_supply also provides fixed hw characteristics.

Sebastian proposed DT:battery specifically to be consumed by power_supply_core. Allowing names in DT:battery and power_supply_property to diverge would cause confusion and wasted time, for no particular benefit. As there is no rationale to reconsider the names of these fields for DT:battery, let's write that into the docs.

Quoted 2 lines

Write it into the Linux docs then. The DT docs need to stand on their own for the standalone DT tree[1] that other projects import.

We will document it on the Linux side. But referencing a Linux header file as the origin of property names in DT;battery does not create a Linux dependency for standalone DT. It merely clarifies the naming scheme.

Also a huge number of DT bindings are Linux-specific, as they are implemented by some kernel driver. Do you plan to rip those out of devicetree-rebasing?

Quoted 1 line

[1] https://git.kernel.org/pub/scm/linux/kernel/git/devicetree/devicetree-rebasing.git/

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

#12 lore raw

Re: [PATCH v8 1/9] devicetree: power: Add battery.txt

Quoted 76 lines · Liam Breck
Quoted 72 lines · Rob Herring
Quoted 62 lines · Liam Breck
Quoted 54 lines · Rob Herring
Quoted 53 lines · Liam Breck

Hi Rob,

Quoted 16 lines · Rob Herring
Quoted 15 lines · Liam Breck

From: Liam Breck <[email protected]>

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.

---
 .../devicetree/bindings/power/supply/battery.txt   | 42 ++++++++++++++++++++++
 1 file changed, 42 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/power/supply/battery.txt
Quoted 13 lines

Documentation/devicetree/bindings/power/supply/battery.txt line 4

Quoted 10 lines
Documentation/devicetree/bindings/power/supply/battery.txt new file +4 -0
new file mode 100644
index 0000000..3d916bd
@@ -0,0 +1,42 @@
+Battery Characteristics
+
+Required Properties:
+ - compatible: Must be "fixed-battery"

Still not liking this name, but I don't have a better suggestion. Please describe here what is and isn't a "fixed battery".

Sebastian...?

Quoted 10 lines

Documentation/devicetree/bindings/power/supply/battery.txt line 12

Quoted 8 lines
Documentation/devicetree/bindings/power/supply/battery.txt new file +8 -0
+
+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.

Um, no. That's exactly not how DT bindings should be done.

Power supply components surface stats in sysfs using the names in enum power_supply_property. For example, /sys/class/power_supply/.../voltage_min_design. Matching input (DT) and output (sysfs) names is sensible. The above comment is already attached to struct power_supply_battery_info, which is the initial destination for the above DT properties.

Quoted 6 lines
Quoted 1 line

Shall I mention sysfs in the above comment?

Absolutely not. Bindings should not reference Linux.

While sometimes things can align, there is no guarantee that they will. DT is h/w description. sysfs is user configuration.

Quoted 8 lines

sysfs for power_supply also provides fixed hw characteristics.

Sebastian proposed DT:battery specifically to be consumed by power_supply_core. Allowing names in DT:battery and power_supply_property to diverge would cause confusion and wasted time, for no particular benefit. As there is no rationale to reconsider the names of these fields for DT:battery, let's write that into the docs.

Quoted 2 lines

Write it into the Linux docs then. The DT docs need to stand on their own for the standalone DT tree[1] that other projects import.

Quoted 9 lines

We will document it on the Linux side. But referencing a Linux header file as the origin of property names in DT;battery does not create a Linux dependency for standalone DT. It merely clarifies the naming scheme.

Also a huge number of DT bindings are Linux-specific, as they are implemented by some kernel driver. Do you plan to rip those out of devicetree-rebasing?

Being implemented by a Linux kernel driver does not mean they can *only* every be implemented by a Linux kernel driver, U-Boot for instance takes drivers from Linux and so the DTs work mostly unchanged.

Also the biggest thing to remember is that doing something wrong before, doesn't justify continuing to do it wrong.

Quoted 1 line
Quoted 1 line

[1] https://git.kernel.org/pub/scm/linux/kernel/git/devicetree/devicetree-rebasing.git/

A message that is not in the archive (sent off-list or not yet received): bb0928ce-6d29-3d09-2c5b-f4a084fe06e9-l0cyMroinI0@public.gmane.org

#14 lore raw

Re: [PATCH v8 1/9] devicetree: power: Add battery.txt

Quoted 82 lines · Andrew F. Davis
Quoted 81 lines · Liam Breck
Quoted 72 lines · Rob Herring
Quoted 62 lines · Liam Breck
Quoted 54 lines · Rob Herring
Quoted 53 lines · Liam Breck

Hi Rob,

Quoted 16 lines · Rob Herring
Quoted 15 lines · Liam Breck

From: Liam Breck <[email protected]>

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.

---
 .../devicetree/bindings/power/supply/battery.txt   | 42 ++++++++++++++++++++++
 1 file changed, 42 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/power/supply/battery.txt
Quoted 13 lines

Documentation/devicetree/bindings/power/supply/battery.txt line 4

Quoted 10 lines
Documentation/devicetree/bindings/power/supply/battery.txt new file +4 -0
new file mode 100644
index 0000000..3d916bd
@@ -0,0 +1,42 @@
+Battery Characteristics
+
+Required Properties:
+ - compatible: Must be "fixed-battery"

Still not liking this name, but I don't have a better suggestion. Please describe here what is and isn't a "fixed battery".

Sebastian...?

Quoted 10 lines

Documentation/devicetree/bindings/power/supply/battery.txt line 12

Quoted 8 lines
Documentation/devicetree/bindings/power/supply/battery.txt new file +8 -0
+
+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.

Um, no. That's exactly not how DT bindings should be done.

Power supply components surface stats in sysfs using the names in enum power_supply_property. For example, /sys/class/power_supply/.../voltage_min_design. Matching input (DT) and output (sysfs) names is sensible. The above comment is already attached to struct power_supply_battery_info, which is the initial destination for the above DT properties.

Quoted 6 lines
Quoted 1 line

Shall I mention sysfs in the above comment?

Absolutely not. Bindings should not reference Linux.

While sometimes things can align, there is no guarantee that they will. DT is h/w description. sysfs is user configuration.

Quoted 8 lines

sysfs for power_supply also provides fixed hw characteristics.

Sebastian proposed DT:battery specifically to be consumed by power_supply_core. Allowing names in DT:battery and power_supply_property to diverge would cause confusion and wasted time, for no particular benefit. As there is no rationale to reconsider the names of these fields for DT:battery, let's write that into the docs.

Quoted 2 lines

Write it into the Linux docs then. The DT docs need to stand on their own for the standalone DT tree[1] that other projects import.

We will document it on the Linux side. But referencing a Linux header file as the origin of property names in DT;battery does not create a Linux dependency for standalone DT. It merely clarifies the naming scheme.

Quoted 8 lines
Quoted 4 lines

Also a huge number of DT bindings are Linux-specific, as they are implemented by some kernel driver. Do you plan to rip those out of devicetree-rebasing?

Being implemented by a Linux kernel driver does not mean they can *only* every be implemented by a Linux kernel driver, U-Boot for instance takes drivers from Linux and so the DTs work mostly unchanged.

True, but in many cases Linux drivers have implemented a DT property set badly. The standalone DT user might prefer to renovate vs replicate a design flaw. Either the kernel-derived DT bindings in the standalone tree mandate their flaws, or that tree accumulates overlapping bindings.

Quoted 2 lines

Also the biggest thing to remember is that doing something wrong before, doesn't justify continuing to do it wrong.

It certainly is justified if most ppl insist on continuing to do it wrong :-) --
To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html

A message that is not in the archive (sent off-list or not yet received): CAKvHMgQujE4uYNt0vPkt6qUOtVqdvzXs0zXTkDVRq9YLNvunBQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org

#16 lore raw

Re: [PATCH v8 1/9] devicetree: power: Add battery.txt

Quoted 73 lines · Liam Breck
Quoted 72 lines · Rob Herring
Quoted 70 lines · Liam Breck
Quoted 54 lines · Rob Herring
Quoted 53 lines · Liam Breck

Hi Rob,

Quoted 16 lines · Rob Herring
Quoted 15 lines · Liam Breck

From: Liam Breck <[email protected]>

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.

---
 .../devicetree/bindings/power/supply/battery.txt   | 42 ++++++++++++++++++++++
 1 file changed, 42 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/power/supply/battery.txt
Quoted 13 lines

Documentation/devicetree/bindings/power/supply/battery.txt line 4

Quoted 10 lines
Documentation/devicetree/bindings/power/supply/battery.txt new file +4 -0
new file mode 100644
index 0000000..3d916bd
@@ -0,0 +1,42 @@
+Battery Characteristics
+
+Required Properties:
+ - compatible: Must be "fixed-battery"

Still not liking this name, but I don't have a better suggestion. Please describe here what is and isn't a "fixed battery".

Sebastian...?

Quoted 10 lines

Documentation/devicetree/bindings/power/supply/battery.txt line 12

Quoted 8 lines
Documentation/devicetree/bindings/power/supply/battery.txt new file +8 -0
+
+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.

Um, no. That's exactly not how DT bindings should be done.

Power supply components surface stats in sysfs using the names in enum power_supply_property. For example, /sys/class/power_supply/.../voltage_min_design. Matching input (DT) and output (sysfs) names is sensible. The above comment is already attached to struct power_supply_battery_info, which is the initial destination for the above DT properties.

Quoted 6 lines
Quoted 1 line

Shall I mention sysfs in the above comment?

Absolutely not. Bindings should not reference Linux.

While sometimes things can align, there is no guarantee that they will. DT is h/w description. sysfs is user configuration.

sysfs for power_supply also provides fixed hw characteristics.

Sebastian proposed DT:battery specifically to be consumed by power_supply_core. Allowing names in DT:battery and power_supply_property to diverge would cause confusion and wasted time, for no particular benefit. As there is no rationale to reconsider the names of these fields for DT:battery, let's write that into the docs.

Quoted 7 lines
Quoted 2 lines

Write it into the Linux docs then. The DT docs need to stand on their own for the standalone DT tree[1] that other projects import.

We will document it on the Linux side. But referencing a Linux header file as the origin of property names in DT;battery does not create a Linux dependency for standalone DT. It merely clarifies the naming scheme.

Perhaps, but it doesn't help the perception that bindings located in the kernel tree are "kernel bindings". The policy is bindings should stand on their own.

Quoted 3 lines

Also a huge number of DT bindings are Linux-specific, as they are implemented by some kernel driver. Do you plan to rip those out of devicetree-rebasing?

Care to highlight some? I'm aware of a few cases, but hardly a "huge number". I hear lots of complaints of bindings/dts's being Linux specific, but no specific examples nor attempts to fix those cases. I'd happily take patches to at least mark bad or linux specific bindings.

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

#17 lore raw

PATCH v8 3/9 devicetree: power: bq27xxx: Add monitored-battery documentation

In reply to a message not in the archive

From: Liam Breck <[email protected]>

Document monitored-battery = <&battery_node>

---
 Documentation/devicetree/bindings/power/supply/bq27xxx.txt | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)
Documentation/devicetree/bindings/power/supply/bq27xxx.txt +10 -1
index b0c95ef..cf83371 100644
@@ -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>;
 };
Signature · 6 lines
2.9.3

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

A message that is not in the archive (sent off-list or not yet received): 20170227071117.18934-4-liam-RYWXG+zxWwBdeoIcmNTgJF6hYfS7NtTn@public.gmane.org

Acked-by
#19 lore raw

Re: [PATCH v8 3/9] devicetree: power: bq27xxx: Add monitored-battery documentation

Quoted 11 lines · Liam Breck

From: Liam Breck <[email protected]>

Document monitored-battery = <&battery_node>

---
 Documentation/devicetree/bindings/power/supply/bq27xxx.txt | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

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

author
#20 lore raw

PATCH v8 2/9 devicetree: property-units: Add uWh and uAh units

In reply to Liam Breck

From: Matt Ranostay <[email protected]>

Add entries for microwatt-hours and microamp-hours.

---
 Documentation/devicetree/bindings/property-units.txt | 2 ++
 1 file changed, 2 insertions(+)
Documentation/devicetree/bindings/property-units.txt +2 -0
index 12278d7..0849618 100644
@@ -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
Signature · 1 line
2.9.3
author
#21 lore raw

PATCH v8 4/9 power: power_supply: Add power_supply_battery_info and API

In reply to Liam Breck

From: Liam Breck <[email protected]>

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.

---
 drivers/power/supply/power_supply_core.c | 40 ++++++++++++++++++++++++++++++++
 include/linux/power_supply.h             | 18 ++++++++++++++
 2 files changed, 58 insertions(+)
drivers/power/supply/power_supply_core.c +40 -0
index a74d8ca..c121931 100644
@@ -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)
include/linux/power_supply.h +18 -0
index 3965503..e84f1d3 100644
@@ -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);
Signature · 1 line
2.9.3
author
#22 lore raw

PATCH v8 5/9 power: bq27xxx_battery: Define access methods to write chip registers

In reply to Liam Breck

From: Matt Ranostay <[email protected]>

write(), read_bulk(), write_bulk() support setting chip registers, e.g. with data obtained from power_supply_battery_info.

---
 include/linux/power/bq27xxx_battery.h | 3 +++
 1 file changed, 3 insertions(+)
include/linux/power/bq27xxx_battery.h +3 -0
index bed9557..92df553 100644
@@ -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 {
Signature · 1 line
2.9.3
author
#23 lore raw

PATCH v8 6/9 power: bq27xxx_battery: Add BQ27425 chip id

In reply to Liam Breck

From: Matt Ranostay <[email protected]>

This patch does not alter the function of the driver. This chip ID is referenced in the devicetree code.

---
 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(-)
drivers/power/supply/bq27xxx_battery.c +22 -1
index 7272d1e..7475a5f 100644
@@ -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;
drivers/power/supply/bq27xxx_battery_i2c.c +1 -1
index 5c5c3a6..d7da535 100644
@@ -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 },
 	{},
include/linux/power/bq27xxx_battery.h +2 -1
index 92df553..4925478 100644
@@ -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 */
 };
 
 /**
Signature · 1 line
2.9.3
Acked-by on v8 6/9
#24 lore raw

Re: [PATCH v8 6/9] power: bq27xxx_battery: Add BQ27425 chip id

Quoted 7 lines · Liam Breck

From: Matt Ranostay <[email protected]>

This patch does not alter the function of the driver. This chip ID is referenced in the devicetree code.

I think I already acked this one..

Quoted 89 lines
---
 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(-)
drivers/power/supply/bq27xxx_battery.c +22 -1
index 7272d1e..7475a5f 100644
@@ -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;
drivers/power/supply/bq27xxx_battery_i2c.c +1 -1
index 5c5c3a6..d7da535 100644
@@ -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 },
 	{},
include/linux/power/bq27xxx_battery.h +2 -1
index 92df553..4925478 100644
@@ -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 */
 };
 
 /**
author
#25 lore raw

PATCH v8 7/9 power: bq27xxx_battery: Add power_supply_battery_info support

In reply to Liam Breck

From: Liam Breck <[email protected]>

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.

---
 drivers/power/supply/bq27xxx_battery.c | 458 ++++++++++++++++++++++++++++++++-
 1 file changed, 456 insertions(+), 2 deletions(-)
drivers/power/supply/bq27xxx_battery.c +456 -2
index 7475a5f..41d4ce7 100644
@@ -51,7 +51,7 @@
 
 #include <linux/power/bq27xxx_battery.h>
 
-#define DRIVER_VERSION		"1.2.0"
+#define DRIVER_VERSION		"1.3.0"
 
 #define BQ27XXX_MANUFACTURER	"Texas Instruments"
 
@@ -59,6 +59,7 @@
 #define BQ27XXX_FLAG_DSC	BIT(0)
 #define BQ27XXX_FLAG_SOCF	BIT(1) /* State-of-Charge threshold final */
 #define BQ27XXX_FLAG_SOC1	BIT(2) /* State-of-Charge threshold 1 */
+#define BQ27XXX_FLAG_CFGUP	BIT(5)
 #define BQ27XXX_FLAG_FC		BIT(9)
 #define BQ27XXX_FLAG_OTD	BIT(14)
 #define BQ27XXX_FLAG_OTC	BIT(15)
@@ -72,6 +73,11 @@
 #define BQ27000_FLAG_FC		BIT(5)
 #define BQ27000_FLAG_CHGS	BIT(7) /* Charge state flag */
 
+/* control register params */
+#define BQ27XXX_SEALED			0x20
+#define BQ27XXX_SET_CFGUPDATE		0x13
+#define BQ27XXX_SOFT_RESET		0x42
+
 #define BQ27XXX_RS			(20) /* Resistor sense mOhm */
 #define BQ27XXX_POWER_CONSTANT		(29200) /* 29.2 µV^2 * 1000 */
 #define BQ27XXX_CURRENT_CONSTANT	(3570) /* 3.57 µV * 1000 */
@@ -102,6 +108,11 @@ enum bq27xxx_reg_index {
 	BQ27XXX_REG_SOC,	/* State-of-Charge */
 	BQ27XXX_REG_DCAP,	/* Design Capacity */
 	BQ27XXX_REG_AP,		/* Average Power */
+	BQ27XXX_DM_CTRL,	/* BlockDataControl() */
+	BQ27XXX_DM_CLASS,	/* DataClass() */
+	BQ27XXX_DM_BLOCK,	/* DataBlock() */
+	BQ27XXX_DM_DATA,	/* BlockData() */
+	BQ27XXX_DM_CKSUM,	/* BlockDataChecksum() */
 	BQ27XXX_REG_MAX,	/* sentinel */
 };
 
@@ -125,6 +136,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
 		[BQ27XXX_REG_SOC] = 0x0b,
 		[BQ27XXX_REG_DCAP] = 0x76,
 		[BQ27XXX_REG_AP] = 0x24,
+		[BQ27XXX_DM_CTRL] = INVALID_REG_ADDR,
+		[BQ27XXX_DM_CLASS] = INVALID_REG_ADDR,
+		[BQ27XXX_DM_BLOCK] = INVALID_REG_ADDR,
+		[BQ27XXX_DM_DATA] = INVALID_REG_ADDR,
+		[BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR,
 	},
 	[BQ27010] = {
 		[BQ27XXX_REG_CTRL] = 0x00,
@@ -144,6 +160,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
 		[BQ27XXX_REG_SOC] = 0x0b,
 		[BQ27XXX_REG_DCAP] = 0x76,
 		[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
+		[BQ27XXX_DM_CTRL] = INVALID_REG_ADDR,
+		[BQ27XXX_DM_CLASS] = INVALID_REG_ADDR,
+		[BQ27XXX_DM_BLOCK] = INVALID_REG_ADDR,
+		[BQ27XXX_DM_DATA] = INVALID_REG_ADDR,
+		[BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR,
 	},
 	[BQ27500] = {
 		[BQ27XXX_REG_CTRL] = 0x00,
@@ -163,6 +184,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
 		[BQ27XXX_REG_SOC] = 0x2c,
 		[BQ27XXX_REG_DCAP] = 0x3c,
 		[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
+		[BQ27XXX_DM_CTRL] = 0x61,
+		[BQ27XXX_DM_CLASS] = 0x3e,
+		[BQ27XXX_DM_BLOCK] = 0x3f,
+		[BQ27XXX_DM_DATA] = 0x40,
+		[BQ27XXX_DM_CKSUM] = 0x60,
 	},
 	[BQ27510] = {
 		[BQ27XXX_REG_CTRL] = 0x00,
@@ -182,6 +208,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
 		[BQ27XXX_REG_SOC] = 0x20,
 		[BQ27XXX_REG_DCAP] = 0x2e,
 		[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
+		[BQ27XXX_DM_CTRL] = 0x61,
+		[BQ27XXX_DM_CLASS] = 0x3e,
+		[BQ27XXX_DM_BLOCK] = 0x3f,
+		[BQ27XXX_DM_DATA] = 0x40,
+		[BQ27XXX_DM_CKSUM] = 0x60,
 	},
 	[BQ27530] = {
 		[BQ27XXX_REG_CTRL] = 0x00,
@@ -201,6 +232,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
 		[BQ27XXX_REG_SOC] = 0x2c,
 		[BQ27XXX_REG_DCAP] = INVALID_REG_ADDR,
 		[BQ27XXX_REG_AP] = 0x24,
+		[BQ27XXX_DM_CTRL] = 0x61,
+		[BQ27XXX_DM_CLASS] = 0x3e,
+		[BQ27XXX_DM_BLOCK] = 0x3f,
+		[BQ27XXX_DM_DATA] = 0x40,
+		[BQ27XXX_DM_CKSUM] = 0x60,
 	},
 	[BQ27541] = {
 		[BQ27XXX_REG_CTRL] = 0x00,
@@ -220,6 +256,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
 		[BQ27XXX_REG_SOC] = 0x2c,
 		[BQ27XXX_REG_DCAP] = 0x3c,
 		[BQ27XXX_REG_AP] = 0x24,
+		[BQ27XXX_DM_CTRL] = 0x61,
+		[BQ27XXX_DM_CLASS] = 0x3e,
+		[BQ27XXX_DM_BLOCK] = 0x3f,
+		[BQ27XXX_DM_DATA] = 0x40,
+		[BQ27XXX_DM_CKSUM] = 0x60,
 	},
 	[BQ27545] = {
 		[BQ27XXX_REG_CTRL] = 0x00,
@@ -239,6 +280,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
 		[BQ27XXX_REG_SOC] = 0x2c,
 		[BQ27XXX_REG_DCAP] = INVALID_REG_ADDR,
 		[BQ27XXX_REG_AP] = 0x24,
+		[BQ27XXX_DM_CTRL] = 0x61,
+		[BQ27XXX_DM_CLASS] = 0x3e,
+		[BQ27XXX_DM_BLOCK] = 0x3f,
+		[BQ27XXX_DM_DATA] = 0x40,
+		[BQ27XXX_DM_CKSUM] = 0x60,
 	},
 	[BQ27421] = {
 		[BQ27XXX_REG_CTRL] = 0x00,
@@ -258,6 +304,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
 		[BQ27XXX_REG_SOC] = 0x1c,
 		[BQ27XXX_REG_DCAP] = 0x3c,
 		[BQ27XXX_REG_AP] = 0x18,
+		[BQ27XXX_DM_CTRL] = 0x61,
+		[BQ27XXX_DM_CLASS] = 0x3e,
+		[BQ27XXX_DM_BLOCK] = 0x3f,
+		[BQ27XXX_DM_DATA] = 0x40,
+		[BQ27XXX_DM_CKSUM] = 0x60,
 	},
 	[BQ27425] = {
 		[BQ27XXX_REG_CTRL] = 0x00,
@@ -277,6 +328,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
 		[BQ27XXX_REG_SOC] = 0x1c,
 		[BQ27XXX_REG_DCAP] = 0x3c,
 		[BQ27XXX_REG_AP] = 0x18,
+		[BQ27XXX_DM_CTRL] = 0x61,
+		[BQ27XXX_DM_CLASS] = 0x3e,
+		[BQ27XXX_DM_BLOCK] = 0x3f,
+		[BQ27XXX_DM_DATA] = 0x40,
+		[BQ27XXX_DM_CKSUM] = 0x60,
 	},
 };
 
@@ -452,6 +508,81 @@ static struct {
 static DEFINE_MUTEX(bq27xxx_list_lock);
 static LIST_HEAD(bq27xxx_battery_devices);
 
+#define BQ27XXX_DM_SZ			32
+
+#define BQ27XXX_MSLEEP(i) usleep_range((i)*1000, (i)*1000+500)
+
+struct bq27xxx_dm_reg {
+	u8 subclass_id;
+	u8 offset;
+	u8 bytes;
+	u16 min, max;
+};
+
+struct bq27xxx_dm_buf {
+	u8 class;
+	u8 block;
+	u8 a[BQ27XXX_DM_SZ];
+	bool full, updt;
+};
+
+#define BQ27XXX_DM_BUF(di, i) { \
+	.class = bq27xxx_dm_regs[(di)->chip][i].subclass_id, \
+	.block = bq27xxx_dm_regs[(di)->chip][i].offset / BQ27XXX_DM_SZ, \
+}
+
+static inline u16* bq27xxx_dm_buf_ptr(struct bq27xxx_dm_buf *buf,
+				      struct bq27xxx_dm_reg *reg) {
+	if (buf->class == reg->subclass_id
+	 && buf->block == reg->offset / BQ27XXX_DM_SZ)
+		return (u16*) (buf->a + reg->offset % BQ27XXX_DM_SZ);
+
+	return NULL;
+}
+
+enum bq27xxx_dm_reg_id {
+	BQ27XXX_DM_DESIGN_CAPACITY = 0,
+	BQ27XXX_DM_DESIGN_ENERGY,
+	BQ27XXX_DM_TERMINATE_VOLTAGE,
+};
+
+static const char* bq27xxx_dm_reg_name[] = {
+	[BQ27XXX_DM_DESIGN_CAPACITY] = "design-capacity",
+	[BQ27XXX_DM_DESIGN_ENERGY] = "design-energy",
+	[BQ27XXX_DM_TERMINATE_VOLTAGE] = "terminate-voltage",
+};
+
+static struct bq27xxx_dm_reg 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 +627,318 @@ static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index,
 	return di->bus.read(di, di->regs[reg_index], single);
 }
 
+static int bq27xxx_battery_set_seal_state(struct bq27xxx_device_info *di,
+					  bool state)
+{
+	int ret;
+
+	if (state) {
+		ret = di->bus.write(di, di->regs[BQ27XXX_REG_CTRL], BQ27XXX_SEALED, false);
+		if (ret < 0)
+			goto out;
+	} else {
+		u32 key = bq27xxx_unseal_keys[di->chip];
+
+		ret = di->bus.write(di, di->regs[BQ27XXX_REG_CTRL], (u16)(key >> 16), false);
+		if (ret < 0)
+			goto out;
+
+		ret = di->bus.write(di, di->regs[BQ27XXX_REG_CTRL], (u16)key, false);
+		if (ret < 0)
+			goto out;
+	}
+	return 0;
+
+out:
+	dev_err(di->dev, "bus error on %s: %d\n", state ? "seal" : "unseal", ret);
+	return ret;
+}
+
+static u8 bq27xxx_battery_checksum(struct bq27xxx_dm_buf *buf)
+{
+	u16 sum = 0;
+	int i;
+
+	for (i = 0; i < BQ27XXX_DM_SZ; i++)
+		sum += buf->a[i];
+	sum &= 0xff;
+
+	return 0xff - sum;
+}
+
+static int bq27xxx_battery_read_dm_block(struct bq27xxx_device_info *di,
+					 struct bq27xxx_dm_buf *buf)
+{
+	int ret;
+
+	ret = di->bus.write(di, di->regs[BQ27XXX_DM_CLASS], buf->class, true);
+	if (ret < 0)
+		goto out;
+
+	ret = di->bus.write(di, di->regs[BQ27XXX_DM_BLOCK], buf->block, true);
+	if (ret < 0)
+		goto out;
+
+	BQ27XXX_MSLEEP(1);
+
+	ret = di->bus.read_bulk(di, di->regs[BQ27XXX_DM_DATA], buf->a, BQ27XXX_DM_SZ);
+	if (ret < 0)
+		goto out;
+
+	ret = di->bus.read(di, di->regs[BQ27XXX_DM_CKSUM], true);
+	if (ret < 0)
+		goto out;
+
+	if ((u8)ret != bq27xxx_battery_checksum(buf)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	buf->full = true;
+	buf->updt = false;
+	return 0;
+
+out:
+	dev_err(di->dev, "bus error reading chip memory: %d\n", ret);
+	return ret;
+}
+
+static void bq27xxx_battery_update_dm_block(struct bq27xxx_device_info *di,
+					    struct bq27xxx_dm_buf *buf,
+					    enum bq27xxx_dm_reg_id reg_id,
+					    unsigned int val)
+{
+	struct bq27xxx_dm_reg *reg = &bq27xxx_dm_regs[di->chip][reg_id];
+	const char* str = bq27xxx_dm_reg_name[reg_id];
+	u16 *prev = bq27xxx_dm_buf_ptr(buf, reg);
+
+	if (prev == NULL) {
+		dev_warn(di->dev, "buffer does not match %s dm spec\n", str);
+		return;
+	}
+
+	if (reg->bytes != 2) {
+		dev_warn(di->dev, "%s dm spec has unsupported byte size\n", str);
+		return;
+	}
+
+	if (!buf->full)
+		return;
+
+	if (be16_to_cpup(prev) == val) {
+		dev_info(di->dev, "%s has %u\n", str, val);
+		return;
+	}
+
+	dev_info(di->dev, "update %s to %u\n", str, val);
+
+	*prev = cpu_to_be16(val);
+	buf->updt = true;
+}
+
+static int bq27xxx_battery_set_cfgupdate(struct bq27xxx_device_info *di,
+					 bool state)
+{
+	int ret, try=100;
+
+	ret = di->bus.write(di, di->regs[BQ27XXX_REG_CTRL],
+			    state ? BQ27XXX_SET_CFGUPDATE : BQ27XXX_SOFT_RESET,
+			    false);
+	if (ret < 0)
+		goto out;
+
+	if (di->chip == BQ27425) /* chip fails to set/clear flag */
+		return 0;
+
+	do {
+		BQ27XXX_MSLEEP(5);
+		ret = di->bus.read(di, di->regs[BQ27XXX_REG_FLAGS], false);
+		if (ret < 0)
+			goto out;
+	} while (!(ret & BQ27XXX_FLAG_CFGUP) == state && --try);
+
+	if (100-try) /* remove after other cfgupdate parts are tested */
+		dev_info(di->dev, "cfgupdate %d, retries %d\n", state, 100-try);
+
+	if (try)
+		return 0;
+
+	dev_err(di->dev, "timed out waiting for cfgupdate flag %d\n", state);
+	return -EINVAL;
+
+out:
+	dev_err(di->dev, "bus error on %s: %d\n", state ? "set_cfgupdate" : "soft_reset", ret);
+	return ret;
+}
+
+static int bq27xxx_battery_write_dm_block(struct bq27xxx_device_info *di,
+					  struct bq27xxx_dm_buf *buf)
+{
+	bool cfgup = di->chip == BQ27425 || di->chip == BQ27421; /* || BQ27441 || BQ27621 */
+	int ret;
+
+	if (!buf->updt)
+		return 0;
+
+	if (cfgup) {
+		ret = bq27xxx_battery_set_cfgupdate(di, true);
+		if (ret < 0)
+			return ret;
+	}
+
+	ret = di->bus.write(di, di->regs[BQ27XXX_DM_CTRL], 0, true);
+	if (ret < 0)
+		goto out;
+
+	ret = di->bus.write(di, di->regs[BQ27XXX_DM_CLASS], buf->class, true);
+	if (ret < 0)
+		goto out;
+
+	ret = di->bus.write(di, di->regs[BQ27XXX_DM_BLOCK], buf->block, true);
+	if (ret < 0)
+		goto out;
+
+	BQ27XXX_MSLEEP(1);
+
+	ret = di->bus.write_bulk(di, di->regs[BQ27XXX_DM_DATA], buf->a, BQ27XXX_DM_SZ);
+	if (ret < 0)
+		goto out;
+
+	ret = di->bus.write(di, di->regs[BQ27XXX_DM_CKSUM],
+			    bq27xxx_battery_checksum(buf), true);
+	if (ret < 0)
+		goto out;
+
+	/* THE FOLLOWING SEQUENCE IS TOXIC. DO NOT USE!
+	 * If the 'time' delay is insufficient, NVM corruption results on
+	 * the '425 chip (and perhaps others), which could damage the chip.
+	 * It was suggested in this TI tool:
+	 *   http://git.ti.com/bms-linux/bqtool/blobs/master/gauge.c#line328
+	 *
+	 * 1. sleep 'time' after above write(BQ27XXX_DM_CKSUM, ...)
+	 * 2. write(BQ27XXX_DM_BLOCK, buf->block)
+	 * 3. sum = read(BQ27XXX_DM_CKSUM)
+	 * 4. if (sum != bq27xxx_battery_checksum(buf))
+	 *      error
+	 */
+
+	if (cfgup) {
+		BQ27XXX_MSLEEP(1);
+		ret = bq27xxx_battery_set_cfgupdate(di, false);
+		if (ret < 0)
+			return ret;
+	}
+
+	buf->updt = false;
+	return 0;
+
+out:
+	if (cfgup)
+		bq27xxx_battery_set_cfgupdate(di, false);
+
+	dev_err(di->dev, "bus error writing chip memory: %d\n", ret);
+	return ret;
+}
+
+static void bq27xxx_battery_set_config(struct bq27xxx_device_info *di,
+				       struct power_supply_battery_info *info)
+{
+	struct bq27xxx_dm_buf bd = BQ27XXX_DM_BUF(di, BQ27XXX_DM_DESIGN_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);
+		/* assume design energy & capacity are in same block */
+		bq27xxx_battery_update_dm_block(di, &bd,
+					BQ27XXX_DM_DESIGN_CAPACITY,
+					info->charge_full_design_uah / 1000);
+		bq27xxx_battery_update_dm_block(di, &bd,
+					BQ27XXX_DM_DESIGN_ENERGY,
+					info->energy_full_design_uwh / 1000);
+	}
+
+	if (info->voltage_min_design_uv != -EINVAL) {
+		bool same = bd.class == bt.class && bd.block == bt.block;
+		if (!same)
+			bq27xxx_battery_read_dm_block(di, &bt);
+		bq27xxx_battery_update_dm_block(di, same ? &bd : &bt,
+					BQ27XXX_DM_TERMINATE_VOLTAGE,
+					info->voltage_min_design_uv / 1000);
+	}
+
+	bq27xxx_battery_write_dm_block(di, &bd);
+	bq27xxx_battery_write_dm_block(di, &bt);
+}
+
+void bq27xxx_battery_settings(struct bq27xxx_device_info *di)
+{
+	struct power_supply_battery_info info = {};
+	unsigned int min, max;
+
+	/* functions don't exist for writing data so abort */
+	if (!di->bus.write || !di->bus.write_bulk)
+		return;
+
+	/* no settings to be set for this chipset so abort */
+	if (!bq27xxx_dm_regs[di->chip])
+		return;
+
+	if (bq27xxx_battery_set_seal_state(di, false) < 0)
+		return;
+
+	if (power_supply_get_battery_info(di->bat, &info) < 0)
+		goto out;
+
+	if (info.energy_full_design_uwh != info.charge_full_design_uah) {
+		if (info.energy_full_design_uwh == -EINVAL)
+			dev_warn(di->dev,
+				"missing battery:energy-full-design-microwatt-hours\n");
+		else if (info.charge_full_design_uah == -EINVAL)
+			dev_warn(di->dev,
+				"missing battery:charge-full-design-microamp-hours\n");
+	}
+
+	/* assume min == 0 */
+	max = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_DESIGN_ENERGY].max;
+	if (info.energy_full_design_uwh > max * 1000) {
+		dev_err(di->dev,
+		       "invalid battery:energy-full-design-microwatt-hours %d\n",
+			info.energy_full_design_uwh);
+		info.energy_full_design_uwh = -EINVAL;
+	}
+
+	/* assume min == 0 */
+	max = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_DESIGN_CAPACITY].max;
+	if (info.charge_full_design_uah > max * 1000) {
+		dev_err(di->dev,
+		       "invalid battery:charge-full-design-microamp-hours %d\n",
+			info.charge_full_design_uah);
+		info.charge_full_design_uah = -EINVAL;
+	}
+
+	min = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_TERMINATE_VOLTAGE].min;
+	max = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_TERMINATE_VOLTAGE].max;
+	if ((info.voltage_min_design_uv < min * 1000
+	  || info.voltage_min_design_uv > max * 1000)
+	  && info.voltage_min_design_uv != -EINVAL) {
+		dev_err(di->dev,
+		       "invalid battery:voltage-min-design-microvolt %d\n",
+			info.voltage_min_design_uv);
+		info.voltage_min_design_uv = -EINVAL;
+	}
+
+	if ((info.energy_full_design_uwh == -EINVAL
+	  || info.charge_full_design_uah == -EINVAL)
+	  && info.voltage_min_design_uv  == -EINVAL)
+		goto out;
+
+	bq27xxx_battery_set_config(di, &info);
+
+out:
+	bq27xxx_battery_set_seal_state(di, true);
+}
+
 /*
  * Return the battery State-of-Charge
  * Or < 0 if something fails.
@@ -1006,6 +1449,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 +1489,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 +1517,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);
Signature · 1 line
2.9.3
#26 lore raw

Re: [PATCH v8 7/9] power: bq27xxx_battery: Add power_supply_battery_info support

Quoted 48 lines · Liam Breck

From: Liam Breck <[email protected]>

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.

---
 drivers/power/supply/bq27xxx_battery.c | 458 ++++++++++++++++++++++++++++++++-
 1 file changed, 456 insertions(+), 2 deletions(-)
drivers/power/supply/bq27xxx_battery.c +7 -1
index 7475a5f..41d4ce7 100644
@@ -51,7 +51,7 @@
 
 #include <linux/power/bq27xxx_battery.h>
 
-#define DRIVER_VERSION		"1.2.0"
+#define DRIVER_VERSION		"1.3.0"
 
 #define BQ27XXX_MANUFACTURER	"Texas Instruments"
 
@@ -59,6 +59,7 @@
 #define BQ27XXX_FLAG_DSC	BIT(0)
 #define BQ27XXX_FLAG_SOCF	BIT(1) /* State-of-Charge threshold final */
 #define BQ27XXX_FLAG_SOC1	BIT(2) /* State-of-Charge threshold 1 */
+#define BQ27XXX_FLAG_CFGUP	BIT(5)
 #define BQ27XXX_FLAG_FC		BIT(9)
 #define BQ27XXX_FLAG_OTD	BIT(14)
 #define BQ27XXX_FLAG_OTC	BIT(15)
@@ -72,6 +73,11 @@
 #define BQ27000_FLAG_FC		BIT(5)
 #define BQ27000_FLAG_CHGS	BIT(7) /* Charge state flag */
 
+/* control register params */
+#define BQ27XXX_SEALED			0x20
+#define BQ27XXX_SET_CFGUPDATE		0x13
+#define BQ27XXX_SOFT_RESET		0x42
+
 #define BQ27XXX_RS			(20) /* Resistor sense mOhm */
 #define BQ27XXX_POWER_CONSTANT		(29200) /* 29.2 µV^2 * 1000 */
 #define BQ27XXX_CURRENT_CONSTANT	(3570) /* 3.57 µV * 1000 */

drivers/power/supply/bq27xxx_battery.c enum bq27xxx_reg_index { · line 111

Quoted 4 lines
drivers/power/supply/bq27xxx_battery.c +1 -0
@@ -102,6 +108,11 @@ enum bq27xxx_reg_index {
 	BQ27XXX_REG_SOC,	/* State-of-Charge */
 	BQ27XXX_REG_DCAP,	/* Design Capacity */
 	BQ27XXX_REG_AP,		/* Average Power */
+	BQ27XXX_DM_CTRL,	/* BlockDataControl() */

/* Block Data Control */

drivers/power/supply/bq27xxx_battery.c enum bq27xxx_reg_index { · line 112

Quoted 1 line
drivers/power/supply/bq27xxx_battery.c +1 -0
+	BQ27XXX_DM_CLASS,	/* DataClass() */

/* Data Class */

etc..

Quoted 103 lines
drivers/power/supply/bq27xxx_battery.c +43 -0
+	BQ27XXX_DM_BLOCK,	/* DataBlock() */
+	BQ27XXX_DM_DATA,	/* BlockData() */
+	BQ27XXX_DM_CKSUM,	/* BlockDataChecksum() */
 	BQ27XXX_REG_MAX,	/* sentinel */
 };
 
@@ -125,6 +136,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
 		[BQ27XXX_REG_SOC] = 0x0b,
 		[BQ27XXX_REG_DCAP] = 0x76,
 		[BQ27XXX_REG_AP] = 0x24,
+		[BQ27XXX_DM_CTRL] = INVALID_REG_ADDR,
+		[BQ27XXX_DM_CLASS] = INVALID_REG_ADDR,
+		[BQ27XXX_DM_BLOCK] = INVALID_REG_ADDR,
+		[BQ27XXX_DM_DATA] = INVALID_REG_ADDR,
+		[BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR,
 	},
 	[BQ27010] = {
 		[BQ27XXX_REG_CTRL] = 0x00,
@@ -144,6 +160,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
 		[BQ27XXX_REG_SOC] = 0x0b,
 		[BQ27XXX_REG_DCAP] = 0x76,
 		[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
+		[BQ27XXX_DM_CTRL] = INVALID_REG_ADDR,
+		[BQ27XXX_DM_CLASS] = INVALID_REG_ADDR,
+		[BQ27XXX_DM_BLOCK] = INVALID_REG_ADDR,
+		[BQ27XXX_DM_DATA] = INVALID_REG_ADDR,
+		[BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR,
 	},
 	[BQ27500] = {
 		[BQ27XXX_REG_CTRL] = 0x00,
@@ -163,6 +184,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
 		[BQ27XXX_REG_SOC] = 0x2c,
 		[BQ27XXX_REG_DCAP] = 0x3c,
 		[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
+		[BQ27XXX_DM_CTRL] = 0x61,
+		[BQ27XXX_DM_CLASS] = 0x3e,
+		[BQ27XXX_DM_BLOCK] = 0x3f,
+		[BQ27XXX_DM_DATA] = 0x40,
+		[BQ27XXX_DM_CKSUM] = 0x60,
 	},
 	[BQ27510] = {
 		[BQ27XXX_REG_CTRL] = 0x00,
@@ -182,6 +208,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
 		[BQ27XXX_REG_SOC] = 0x20,
 		[BQ27XXX_REG_DCAP] = 0x2e,
 		[BQ27XXX_REG_AP] = INVALID_REG_ADDR,
+		[BQ27XXX_DM_CTRL] = 0x61,
+		[BQ27XXX_DM_CLASS] = 0x3e,
+		[BQ27XXX_DM_BLOCK] = 0x3f,
+		[BQ27XXX_DM_DATA] = 0x40,
+		[BQ27XXX_DM_CKSUM] = 0x60,
 	},
 	[BQ27530] = {
 		[BQ27XXX_REG_CTRL] = 0x00,
@@ -201,6 +232,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
 		[BQ27XXX_REG_SOC] = 0x2c,
 		[BQ27XXX_REG_DCAP] = INVALID_REG_ADDR,
 		[BQ27XXX_REG_AP] = 0x24,
+		[BQ27XXX_DM_CTRL] = 0x61,
+		[BQ27XXX_DM_CLASS] = 0x3e,
+		[BQ27XXX_DM_BLOCK] = 0x3f,
+		[BQ27XXX_DM_DATA] = 0x40,
+		[BQ27XXX_DM_CKSUM] = 0x60,
 	},
 	[BQ27541] = {
 		[BQ27XXX_REG_CTRL] = 0x00,
@@ -220,6 +256,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
 		[BQ27XXX_REG_SOC] = 0x2c,
 		[BQ27XXX_REG_DCAP] = 0x3c,
 		[BQ27XXX_REG_AP] = 0x24,
+		[BQ27XXX_DM_CTRL] = 0x61,
+		[BQ27XXX_DM_CLASS] = 0x3e,
+		[BQ27XXX_DM_BLOCK] = 0x3f,
+		[BQ27XXX_DM_DATA] = 0x40,
+		[BQ27XXX_DM_CKSUM] = 0x60,
 	},
 	[BQ27545] = {
 		[BQ27XXX_REG_CTRL] = 0x00,
@@ -239,6 +280,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
 		[BQ27XXX_REG_SOC] = 0x2c,
 		[BQ27XXX_REG_DCAP] = INVALID_REG_ADDR,
 		[BQ27XXX_REG_AP] = 0x24,
+		[BQ27XXX_DM_CTRL] = 0x61,
+		[BQ27XXX_DM_CLASS] = 0x3e,
+		[BQ27XXX_DM_BLOCK] = 0x3f,
+		[BQ27XXX_DM_DATA] = 0x40,
+		[BQ27XXX_DM_CKSUM] = 0x60,
 	},
 	[BQ27421] = {
 		[BQ27XXX_REG_CTRL] = 0x00,
@@ -258,6 +304,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
 		[BQ27XXX_REG_SOC] = 0x1c,
 		[BQ27XXX_REG_DCAP] = 0x3c,
 		[BQ27XXX_REG_AP] = 0x18,
+		[BQ27XXX_DM_CTRL] = 0x61,
+		[BQ27XXX_DM_CLASS] = 0x3e,
+		[BQ27XXX_DM_BLOCK] = 0x3f,
+		[BQ27XXX_DM_DATA] = 0x40,
+		[BQ27XXX_DM_CKSUM] = 0x60,
 	},
 	[BQ27425] = {
 		[BQ27XXX_REG_CTRL] = 0x00,

drivers/power/supply/bq27xxx_battery.c static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = { · line 335

Quoted 8 lines
drivers/power/supply/bq27xxx_battery.c +5 -0
@@ -277,6 +328,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
 		[BQ27XXX_REG_SOC] = 0x1c,
 		[BQ27XXX_REG_DCAP] = 0x3c,
 		[BQ27XXX_REG_AP] = 0x18,
+		[BQ27XXX_DM_CTRL] = 0x61,
+		[BQ27XXX_DM_CLASS] = 0x3e,
+		[BQ27XXX_DM_BLOCK] = 0x3f,
+		[BQ27XXX_DM_DATA] = 0x40,
+		[BQ27XXX_DM_CKSUM] = 0x60,

That wasn't so painful was it :)

Quoted 58 lines
drivers/power/supply/bq27xxx_battery.c +51 -0
 	},
 };
 
@@ -452,6 +508,81 @@ static struct {
 static DEFINE_MUTEX(bq27xxx_list_lock);
 static LIST_HEAD(bq27xxx_battery_devices);
 
+#define BQ27XXX_DM_SZ			32
+
+#define BQ27XXX_MSLEEP(i) usleep_range((i)*1000, (i)*1000+500)
+
+struct bq27xxx_dm_reg {
+	u8 subclass_id;
+	u8 offset;
+	u8 bytes;
+	u16 min, max;
+};
+
+struct bq27xxx_dm_buf {
+	u8 class;
+	u8 block;
+	u8 a[BQ27XXX_DM_SZ];
+	bool full, updt;
+};
+
+#define BQ27XXX_DM_BUF(di, i) { \
+	.class = bq27xxx_dm_regs[(di)->chip][i].subclass_id, \
+	.block = bq27xxx_dm_regs[(di)->chip][i].offset / BQ27XXX_DM_SZ, \
+}
+
+static inline u16* bq27xxx_dm_buf_ptr(struct bq27xxx_dm_buf *buf,
+				      struct bq27xxx_dm_reg *reg) {
+	if (buf->class == reg->subclass_id
+	 && buf->block == reg->offset / BQ27XXX_DM_SZ)
+		return (u16*) (buf->a + reg->offset % BQ27XXX_DM_SZ);
+
+	return NULL;
+}
+
+enum bq27xxx_dm_reg_id {
+	BQ27XXX_DM_DESIGN_CAPACITY = 0,
+	BQ27XXX_DM_DESIGN_ENERGY,
+	BQ27XXX_DM_TERMINATE_VOLTAGE,
+};
+
+static const char* bq27xxx_dm_reg_name[] = {
+	[BQ27XXX_DM_DESIGN_CAPACITY] = "design-capacity",
+	[BQ27XXX_DM_DESIGN_ENERGY] = "design-energy",
+	[BQ27XXX_DM_TERMINATE_VOLTAGE] = "terminate-voltage",
+};
+
+static struct bq27xxx_dm_reg 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 */

drivers/power/supply/bq27xxx_battery.c static struct { · line 571

Quoted 10 lines
drivers/power/supply/bq27xxx_battery.c +10 -0
+	[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 },
+//};

I don't think this comment style is allowed.

drivers/power/supply/bq27xxx_battery.c static struct { · line 577

Quoted 6 lines
drivers/power/supply/bq27xxx_battery.c +6 -0
+
+static struct bq27xxx_dm_reg *bq27xxx_dm_regs[] = {
+	[BQ27421] = bq27421_dm_regs, /* and BQ27441 */
+	[BQ27425] = bq27425_dm_regs,
+//	[BQ27621] = bq27621_dm_regs,
+};

I know it is not tested, but lets not comment it out, I'll do a round of testing for this part when this series is ready.

Quoted 365 lines
drivers/power/supply/bq27xxx_battery.c +332 -1
+
+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 +627,318 @@ static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index,
 	return di->bus.read(di, di->regs[reg_index], single);
 }
 
+static int bq27xxx_battery_set_seal_state(struct bq27xxx_device_info *di,
+					  bool state)
+{
+	int ret;
+
+	if (state) {
+		ret = di->bus.write(di, di->regs[BQ27XXX_REG_CTRL], BQ27XXX_SEALED, false);
+		if (ret < 0)
+			goto out;
+	} else {
+		u32 key = bq27xxx_unseal_keys[di->chip];
+
+		ret = di->bus.write(di, di->regs[BQ27XXX_REG_CTRL], (u16)(key >> 16), false);
+		if (ret < 0)
+			goto out;
+
+		ret = di->bus.write(di, di->regs[BQ27XXX_REG_CTRL], (u16)key, false);
+		if (ret < 0)
+			goto out;
+	}
+	return 0;
+
+out:
+	dev_err(di->dev, "bus error on %s: %d\n", state ? "seal" : "unseal", ret);
+	return ret;
+}
+
+static u8 bq27xxx_battery_checksum(struct bq27xxx_dm_buf *buf)
+{
+	u16 sum = 0;
+	int i;
+
+	for (i = 0; i < BQ27XXX_DM_SZ; i++)
+		sum += buf->a[i];
+	sum &= 0xff;
+
+	return 0xff - sum;
+}
+
+static int bq27xxx_battery_read_dm_block(struct bq27xxx_device_info *di,
+					 struct bq27xxx_dm_buf *buf)
+{
+	int ret;
+
+	ret = di->bus.write(di, di->regs[BQ27XXX_DM_CLASS], buf->class, true);
+	if (ret < 0)
+		goto out;
+
+	ret = di->bus.write(di, di->regs[BQ27XXX_DM_BLOCK], buf->block, true);
+	if (ret < 0)
+		goto out;
+
+	BQ27XXX_MSLEEP(1);
+
+	ret = di->bus.read_bulk(di, di->regs[BQ27XXX_DM_DATA], buf->a, BQ27XXX_DM_SZ);
+	if (ret < 0)
+		goto out;
+
+	ret = di->bus.read(di, di->regs[BQ27XXX_DM_CKSUM], true);
+	if (ret < 0)
+		goto out;
+
+	if ((u8)ret != bq27xxx_battery_checksum(buf)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	buf->full = true;
+	buf->updt = false;
+	return 0;
+
+out:
+	dev_err(di->dev, "bus error reading chip memory: %d\n", ret);
+	return ret;
+}
+
+static void bq27xxx_battery_update_dm_block(struct bq27xxx_device_info *di,
+					    struct bq27xxx_dm_buf *buf,
+					    enum bq27xxx_dm_reg_id reg_id,
+					    unsigned int val)
+{
+	struct bq27xxx_dm_reg *reg = &bq27xxx_dm_regs[di->chip][reg_id];
+	const char* str = bq27xxx_dm_reg_name[reg_id];
+	u16 *prev = bq27xxx_dm_buf_ptr(buf, reg);
+
+	if (prev == NULL) {
+		dev_warn(di->dev, "buffer does not match %s dm spec\n", str);
+		return;
+	}
+
+	if (reg->bytes != 2) {
+		dev_warn(di->dev, "%s dm spec has unsupported byte size\n", str);
+		return;
+	}
+
+	if (!buf->full)
+		return;
+
+	if (be16_to_cpup(prev) == val) {
+		dev_info(di->dev, "%s has %u\n", str, val);
+		return;
+	}
+
+	dev_info(di->dev, "update %s to %u\n", str, val);
+
+	*prev = cpu_to_be16(val);
+	buf->updt = true;
+}
+
+static int bq27xxx_battery_set_cfgupdate(struct bq27xxx_device_info *di,
+					 bool state)
+{
+	int ret, try=100;
+
+	ret = di->bus.write(di, di->regs[BQ27XXX_REG_CTRL],
+			    state ? BQ27XXX_SET_CFGUPDATE : BQ27XXX_SOFT_RESET,
+			    false);
+	if (ret < 0)
+		goto out;
+
+	if (di->chip == BQ27425) /* chip fails to set/clear flag */
+		return 0;
+
+	do {
+		BQ27XXX_MSLEEP(5);
+		ret = di->bus.read(di, di->regs[BQ27XXX_REG_FLAGS], false);
+		if (ret < 0)
+			goto out;
+	} while (!(ret & BQ27XXX_FLAG_CFGUP) == state && --try);
+
+	if (100-try) /* remove after other cfgupdate parts are tested */
+		dev_info(di->dev, "cfgupdate %d, retries %d\n", state, 100-try);
+
+	if (try)
+		return 0;
+
+	dev_err(di->dev, "timed out waiting for cfgupdate flag %d\n", state);
+	return -EINVAL;
+
+out:
+	dev_err(di->dev, "bus error on %s: %d\n", state ? "set_cfgupdate" : "soft_reset", ret);
+	return ret;
+}
+
+static int bq27xxx_battery_write_dm_block(struct bq27xxx_device_info *di,
+					  struct bq27xxx_dm_buf *buf)
+{
+	bool cfgup = di->chip == BQ27425 || di->chip == BQ27421; /* || BQ27441 || BQ27621 */
+	int ret;
+
+	if (!buf->updt)
+		return 0;
+
+	if (cfgup) {
+		ret = bq27xxx_battery_set_cfgupdate(di, true);
+		if (ret < 0)
+			return ret;
+	}
+
+	ret = di->bus.write(di, di->regs[BQ27XXX_DM_CTRL], 0, true);
+	if (ret < 0)
+		goto out;
+
+	ret = di->bus.write(di, di->regs[BQ27XXX_DM_CLASS], buf->class, true);
+	if (ret < 0)
+		goto out;
+
+	ret = di->bus.write(di, di->regs[BQ27XXX_DM_BLOCK], buf->block, true);
+	if (ret < 0)
+		goto out;
+
+	BQ27XXX_MSLEEP(1);
+
+	ret = di->bus.write_bulk(di, di->regs[BQ27XXX_DM_DATA], buf->a, BQ27XXX_DM_SZ);
+	if (ret < 0)
+		goto out;
+
+	ret = di->bus.write(di, di->regs[BQ27XXX_DM_CKSUM],
+			    bq27xxx_battery_checksum(buf), true);
+	if (ret < 0)
+		goto out;
+
+	/* THE FOLLOWING SEQUENCE IS TOXIC. DO NOT USE!
+	 * If the 'time' delay is insufficient, NVM corruption results on
+	 * the '425 chip (and perhaps others), which could damage the chip.
+	 * It was suggested in this TI tool:
+	 *   http://git.ti.com/bms-linux/bqtool/blobs/master/gauge.c#line328
+	 *
+	 * 1. sleep 'time' after above write(BQ27XXX_DM_CKSUM, ...)
+	 * 2. write(BQ27XXX_DM_BLOCK, buf->block)
+	 * 3. sum = read(BQ27XXX_DM_CKSUM)
+	 * 4. if (sum != bq27xxx_battery_checksum(buf))
+	 *      error
+	 */
+
+	if (cfgup) {
+		BQ27XXX_MSLEEP(1);
+		ret = bq27xxx_battery_set_cfgupdate(di, false);
+		if (ret < 0)
+			return ret;
+	}
+
+	buf->updt = false;
+	return 0;
+
+out:
+	if (cfgup)
+		bq27xxx_battery_set_cfgupdate(di, false);
+
+	dev_err(di->dev, "bus error writing chip memory: %d\n", ret);
+	return ret;
+}
+
+static void bq27xxx_battery_set_config(struct bq27xxx_device_info *di,
+				       struct power_supply_battery_info *info)
+{
+	struct bq27xxx_dm_buf bd = BQ27XXX_DM_BUF(di, BQ27XXX_DM_DESIGN_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);
+		/* assume design energy & capacity are in same block */
+		bq27xxx_battery_update_dm_block(di, &bd,
+					BQ27XXX_DM_DESIGN_CAPACITY,
+					info->charge_full_design_uah / 1000);
+		bq27xxx_battery_update_dm_block(di, &bd,
+					BQ27XXX_DM_DESIGN_ENERGY,
+					info->energy_full_design_uwh / 1000);
+	}
+
+	if (info->voltage_min_design_uv != -EINVAL) {
+		bool same = bd.class == bt.class && bd.block == bt.block;
+		if (!same)
+			bq27xxx_battery_read_dm_block(di, &bt);
+		bq27xxx_battery_update_dm_block(di, same ? &bd : &bt,
+					BQ27XXX_DM_TERMINATE_VOLTAGE,
+					info->voltage_min_design_uv / 1000);
+	}
+
+	bq27xxx_battery_write_dm_block(di, &bd);
+	bq27xxx_battery_write_dm_block(di, &bt);
+}
+
+void bq27xxx_battery_settings(struct bq27xxx_device_info *di)
+{
+	struct power_supply_battery_info info = {};
+	unsigned int min, max;
+
+	/* functions don't exist for writing data so abort */
+	if (!di->bus.write || !di->bus.write_bulk)
+		return;
+
+	/* no settings to be set for this chipset so abort */
+	if (!bq27xxx_dm_regs[di->chip])
+		return;
+
+	if (bq27xxx_battery_set_seal_state(di, false) < 0)
+		return;
+
+	if (power_supply_get_battery_info(di->bat, &info) < 0)
+		goto out;
+
+	if (info.energy_full_design_uwh != info.charge_full_design_uah) {
+		if (info.energy_full_design_uwh == -EINVAL)
+			dev_warn(di->dev,
+				"missing battery:energy-full-design-microwatt-hours\n");
+		else if (info.charge_full_design_uah == -EINVAL)
+			dev_warn(di->dev,
+				"missing battery:charge-full-design-microamp-hours\n");
+	}
+
+	/* assume min == 0 */
+	max = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_DESIGN_ENERGY].max;
+	if (info.energy_full_design_uwh > max * 1000) {
+		dev_err(di->dev,
+		       "invalid battery:energy-full-design-microwatt-hours %d\n",
+			info.energy_full_design_uwh);
+		info.energy_full_design_uwh = -EINVAL;
+	}
+
+	/* assume min == 0 */
+	max = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_DESIGN_CAPACITY].max;
+	if (info.charge_full_design_uah > max * 1000) {
+		dev_err(di->dev,
+		       "invalid battery:charge-full-design-microamp-hours %d\n",
+			info.charge_full_design_uah);
+		info.charge_full_design_uah = -EINVAL;
+	}
+
+	min = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_TERMINATE_VOLTAGE].min;
+	max = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_TERMINATE_VOLTAGE].max;
+	if ((info.voltage_min_design_uv < min * 1000
+	  || info.voltage_min_design_uv > max * 1000)
+	  && info.voltage_min_design_uv != -EINVAL) {
+		dev_err(di->dev,
+		       "invalid battery:voltage-min-design-microvolt %d\n",
+			info.voltage_min_design_uv);
+		info.voltage_min_design_uv = -EINVAL;
+	}
+
+	if ((info.energy_full_design_uwh == -EINVAL
+	  || info.charge_full_design_uah == -EINVAL)
+	  && info.voltage_min_design_uv  == -EINVAL)
+		goto out;
+
+	bq27xxx_battery_set_config(di, &info);
+
+out:
+	bq27xxx_battery_set_seal_state(di, true);
+}
+
 /*
  * Return the battery State-of-Charge
  * Or < 0 if something fails.
@@ -1006,6 +1449,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 +1489,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 +1517,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);
author
#27 lore raw

Re: [PATCH v8 7/9] power: bq27xxx_battery: Add power_supply_battery_info support

Quoted 56 lines · Andrew F. Davis
Quoted 48 lines · Liam Breck

From: Liam Breck <[email protected]>

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.

---
 drivers/power/supply/bq27xxx_battery.c | 458 ++++++++++++++++++++++++++++++++-
 1 file changed, 456 insertions(+), 2 deletions(-)
drivers/power/supply/bq27xxx_battery.c +7 -1
index 7475a5f..41d4ce7 100644
@@ -51,7 +51,7 @@
 
 #include <linux/power/bq27xxx_battery.h>
 
-#define DRIVER_VERSION               "1.2.0"
+#define DRIVER_VERSION               "1.3.0"
 
 #define BQ27XXX_MANUFACTURER "Texas Instruments"
 
@@ -59,6 +59,7 @@
 #define BQ27XXX_FLAG_DSC     BIT(0)
 #define BQ27XXX_FLAG_SOCF    BIT(1) /* State-of-Charge threshold final */
 #define BQ27XXX_FLAG_SOC1    BIT(2) /* State-of-Charge threshold 1 */
+#define BQ27XXX_FLAG_CFGUP   BIT(5)
 #define BQ27XXX_FLAG_FC              BIT(9)
 #define BQ27XXX_FLAG_OTD     BIT(14)
 #define BQ27XXX_FLAG_OTC     BIT(15)
@@ -72,6 +73,11 @@
 #define BQ27000_FLAG_FC              BIT(5)
 #define BQ27000_FLAG_CHGS    BIT(7) /* Charge state flag */
 
+/* control register params */
+#define BQ27XXX_SEALED                       0x20
+#define BQ27XXX_SET_CFGUPDATE                0x13
+#define BQ27XXX_SOFT_RESET           0x42
+
 #define BQ27XXX_RS                   (20) /* Resistor sense mOhm */
 #define BQ27XXX_POWER_CONSTANT               (29200) /* 29.2 µV^2 * 1000 */
 #define BQ27XXX_CURRENT_CONSTANT     (3570) /* 3.57 µV * 1000 */

drivers/power/supply/bq27xxx_battery.c enum bq27xxx_reg_index { · line 111

Quoted 4 lines
drivers/power/supply/bq27xxx_battery.c +1 -0
@@ -102,6 +108,11 @@ enum bq27xxx_reg_index {
      BQ27XXX_REG_SOC,        /* State-of-Charge */
      BQ27XXX_REG_DCAP,       /* Design Capacity */
      BQ27XXX_REG_AP,         /* Average Power */
+     BQ27XXX_DM_CTRL,        /* BlockDataControl() */

/* Block Data Control */

Quoted 3 lines

drivers/power/supply/bq27xxx_battery.c enum bq27xxx_reg_index { · line 112

Quoted 1 line
drivers/power/supply/bq27xxx_battery.c +1 -0
+     BQ27XXX_DM_CLASS,       /* DataClass() */

/* Data Class */

I used the terms from the docs, so a search will find meaningful results.

Quoted 105 lines
Quoted 105 lines
drivers/power/supply/bq27xxx_battery.c +43 -0
+     BQ27XXX_DM_BLOCK,       /* DataBlock() */
+     BQ27XXX_DM_DATA,        /* BlockData() */
+     BQ27XXX_DM_CKSUM,       /* BlockDataChecksum() */
      BQ27XXX_REG_MAX,        /* sentinel */
 };
 
@@ -125,6 +136,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x0b,
              [BQ27XXX_REG_DCAP] = 0x76,
              [BQ27XXX_REG_AP] = 0x24,
+             [BQ27XXX_DM_CTRL] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CLASS] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_BLOCK] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_DATA] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR,
      },
      [BQ27010] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -144,6 +160,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x0b,
              [BQ27XXX_REG_DCAP] = 0x76,
              [BQ27XXX_REG_AP] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CTRL] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CLASS] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_BLOCK] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_DATA] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR,
      },
      [BQ27500] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -163,6 +184,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x2c,
              [BQ27XXX_REG_DCAP] = 0x3c,
              [BQ27XXX_REG_AP] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27510] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -182,6 +208,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x20,
              [BQ27XXX_REG_DCAP] = 0x2e,
              [BQ27XXX_REG_AP] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27530] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -201,6 +232,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x2c,
              [BQ27XXX_REG_DCAP] = INVALID_REG_ADDR,
              [BQ27XXX_REG_AP] = 0x24,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27541] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -220,6 +256,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x2c,
              [BQ27XXX_REG_DCAP] = 0x3c,
              [BQ27XXX_REG_AP] = 0x24,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27545] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -239,6 +280,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x2c,
              [BQ27XXX_REG_DCAP] = INVALID_REG_ADDR,
              [BQ27XXX_REG_AP] = 0x24,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27421] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -258,6 +304,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x1c,
              [BQ27XXX_REG_DCAP] = 0x3c,
              [BQ27XXX_REG_AP] = 0x18,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27425] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -277,6 +328,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x1c,
              [BQ27XXX_REG_DCAP] = 0x3c,
Quoted 8 lines

drivers/power/supply/bq27xxx_battery.c static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = { · line 311

Quoted 6 lines
drivers/power/supply/bq27xxx_battery.c +5 -0
              [BQ27XXX_REG_AP] = 0x18,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,

That wasn't so painful was it :)

It added 11% to the patch :-/

Quoted 62 lines
Quoted 62 lines
drivers/power/supply/bq27xxx_battery.c +55 -0
      },
 };
 
@@ -452,6 +508,81 @@ static struct {
 static DEFINE_MUTEX(bq27xxx_list_lock);
 static LIST_HEAD(bq27xxx_battery_devices);
 
+#define BQ27XXX_DM_SZ                        32
+
+#define BQ27XXX_MSLEEP(i) usleep_range((i)*1000, (i)*1000+500)
+
+struct bq27xxx_dm_reg {
+     u8 subclass_id;
+     u8 offset;
+     u8 bytes;
+     u16 min, max;
+};
+
+struct bq27xxx_dm_buf {
+     u8 class;
+     u8 block;
+     u8 a[BQ27XXX_DM_SZ];
+     bool full, updt;
+};
+
+#define BQ27XXX_DM_BUF(di, i) { \
+     .class = bq27xxx_dm_regs[(di)->chip][i].subclass_id, \
+     .block = bq27xxx_dm_regs[(di)->chip][i].offset / BQ27XXX_DM_SZ, \
+}
+
+static inline u16* bq27xxx_dm_buf_ptr(struct bq27xxx_dm_buf *buf,
+                                   struct bq27xxx_dm_reg *reg) {
+     if (buf->class == reg->subclass_id
+      && buf->block == reg->offset / BQ27XXX_DM_SZ)
+             return (u16*) (buf->a + reg->offset % BQ27XXX_DM_SZ);
+
+     return NULL;
+}
+
+enum bq27xxx_dm_reg_id {
+     BQ27XXX_DM_DESIGN_CAPACITY = 0,
+     BQ27XXX_DM_DESIGN_ENERGY,
+     BQ27XXX_DM_TERMINATE_VOLTAGE,
+};
+
+static const char* bq27xxx_dm_reg_name[] = {
+     [BQ27XXX_DM_DESIGN_CAPACITY] = "design-capacity",
+     [BQ27XXX_DM_DESIGN_ENERGY] = "design-energy",
+     [BQ27XXX_DM_TERMINATE_VOLTAGE] = "terminate-voltage",
+};
+
+static struct bq27xxx_dm_reg 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 },
+};
Quoted 8 lines

drivers/power/supply/bq27xxx_battery.c static struct { · line 571

Quoted 6 lines
drivers/power/supply/bq27xxx_battery.c +6 -0
+
+//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 },
+//};

I don't think this comment style is allowed.

This is a to-do item.

Quoted 9 lines

drivers/power/supply/bq27xxx_battery.c static struct { · line 577

Quoted 6 lines
drivers/power/supply/bq27xxx_battery.c +6 -0
+
+static struct bq27xxx_dm_reg *bq27xxx_dm_regs[] = {
+     [BQ27421] = bq27421_dm_regs, /* and BQ27441 */
+     [BQ27425] = bq27425_dm_regs,
+//   [BQ27621] = bq27621_dm_regs,
+};

I know it is not tested, but lets not comment it out, I'll do a round of testing for this part when this series is ready.

ID for 621 is not yet defined. We need an efficient way to add IDs. Perhaps via chip-id and group-id. Most current ID refs would be to group-id. I'd like to defer that, so we should only add inputs for the single-chip groups in this patchset: 500, 545, 425.

We must NOT set one-time program (OTP) memory, mentioned in 421 & 441 docs. It's not clear whether documented data-memory ops change OTP. Can you ask? The 425 has rewritable NVM. The 621 docs don't say. All the above have cfgupdate mode, which the rest of the family lacks.

#28 lore raw

Re: [PATCH v8 7/9] power: bq27xxx_battery: Add power_supply_battery_info support

Quoted 267 lines · Liam Breck
Quoted 56 lines · Andrew F. Davis
Quoted 48 lines · Liam Breck

From: Liam Breck <[email protected]>

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.

---
 drivers/power/supply/bq27xxx_battery.c | 458 ++++++++++++++++++++++++++++++++-
 1 file changed, 456 insertions(+), 2 deletions(-)
drivers/power/supply/bq27xxx_battery.c +7 -1
index 7475a5f..41d4ce7 100644
@@ -51,7 +51,7 @@
 
 #include <linux/power/bq27xxx_battery.h>
 
-#define DRIVER_VERSION               "1.2.0"
+#define DRIVER_VERSION               "1.3.0"
 
 #define BQ27XXX_MANUFACTURER "Texas Instruments"
 
@@ -59,6 +59,7 @@
 #define BQ27XXX_FLAG_DSC     BIT(0)
 #define BQ27XXX_FLAG_SOCF    BIT(1) /* State-of-Charge threshold final */
 #define BQ27XXX_FLAG_SOC1    BIT(2) /* State-of-Charge threshold 1 */
+#define BQ27XXX_FLAG_CFGUP   BIT(5)
 #define BQ27XXX_FLAG_FC              BIT(9)
 #define BQ27XXX_FLAG_OTD     BIT(14)
 #define BQ27XXX_FLAG_OTC     BIT(15)
@@ -72,6 +73,11 @@
 #define BQ27000_FLAG_FC              BIT(5)
 #define BQ27000_FLAG_CHGS    BIT(7) /* Charge state flag */
 
+/* control register params */
+#define BQ27XXX_SEALED                       0x20
+#define BQ27XXX_SET_CFGUPDATE                0x13
+#define BQ27XXX_SOFT_RESET           0x42
+
 #define BQ27XXX_RS                   (20) /* Resistor sense mOhm */
 #define BQ27XXX_POWER_CONSTANT               (29200) /* 29.2 µV^2 * 1000 */
 #define BQ27XXX_CURRENT_CONSTANT     (3570) /* 3.57 µV * 1000 */

drivers/power/supply/bq27xxx_battery.c enum bq27xxx_reg_index { · line 111

Quoted 4 lines
drivers/power/supply/bq27xxx_battery.c +1 -0
@@ -102,6 +108,11 @@ enum bq27xxx_reg_index {
      BQ27XXX_REG_SOC,        /* State-of-Charge */
      BQ27XXX_REG_DCAP,       /* Design Capacity */
      BQ27XXX_REG_AP,         /* Average Power */
+     BQ27XXX_DM_CTRL,        /* BlockDataControl() */

/* Block Data Control */

Quoted 3 lines

drivers/power/supply/bq27xxx_battery.c enum bq27xxx_reg_index { · line 112

Quoted 1 line
drivers/power/supply/bq27xxx_battery.c +1 -0
+     BQ27XXX_DM_CLASS,       /* DataClass() */

/* Data Class */

I used the terms from the docs, so a search will find meaningful results.

Quoted 105 lines
Quoted 105 lines
drivers/power/supply/bq27xxx_battery.c +43 -0
+     BQ27XXX_DM_BLOCK,       /* DataBlock() */
+     BQ27XXX_DM_DATA,        /* BlockData() */
+     BQ27XXX_DM_CKSUM,       /* BlockDataChecksum() */
      BQ27XXX_REG_MAX,        /* sentinel */
 };
 
@@ -125,6 +136,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x0b,
              [BQ27XXX_REG_DCAP] = 0x76,
              [BQ27XXX_REG_AP] = 0x24,
+             [BQ27XXX_DM_CTRL] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CLASS] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_BLOCK] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_DATA] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR,
      },
      [BQ27010] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -144,6 +160,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x0b,
              [BQ27XXX_REG_DCAP] = 0x76,
              [BQ27XXX_REG_AP] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CTRL] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CLASS] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_BLOCK] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_DATA] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR,
      },
      [BQ27500] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -163,6 +184,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x2c,
              [BQ27XXX_REG_DCAP] = 0x3c,
              [BQ27XXX_REG_AP] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27510] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -182,6 +208,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x20,
              [BQ27XXX_REG_DCAP] = 0x2e,
              [BQ27XXX_REG_AP] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27530] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -201,6 +232,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x2c,
              [BQ27XXX_REG_DCAP] = INVALID_REG_ADDR,
              [BQ27XXX_REG_AP] = 0x24,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27541] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -220,6 +256,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x2c,
              [BQ27XXX_REG_DCAP] = 0x3c,
              [BQ27XXX_REG_AP] = 0x24,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27545] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -239,6 +280,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x2c,
              [BQ27XXX_REG_DCAP] = INVALID_REG_ADDR,
              [BQ27XXX_REG_AP] = 0x24,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27421] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -258,6 +304,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x1c,
              [BQ27XXX_REG_DCAP] = 0x3c,
              [BQ27XXX_REG_AP] = 0x18,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27425] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -277,6 +328,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x1c,
              [BQ27XXX_REG_DCAP] = 0x3c,
Quoted 8 lines

drivers/power/supply/bq27xxx_battery.c static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = { · line 311

Quoted 6 lines
drivers/power/supply/bq27xxx_battery.c +5 -0
              [BQ27XXX_REG_AP] = 0x18,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,

That wasn't so painful was it :)

It added 11% to the patch :-/

Quoted 62 lines
Quoted 62 lines
drivers/power/supply/bq27xxx_battery.c +55 -0
      },
 };
 
@@ -452,6 +508,81 @@ static struct {
 static DEFINE_MUTEX(bq27xxx_list_lock);
 static LIST_HEAD(bq27xxx_battery_devices);
 
+#define BQ27XXX_DM_SZ                        32
+
+#define BQ27XXX_MSLEEP(i) usleep_range((i)*1000, (i)*1000+500)
+
+struct bq27xxx_dm_reg {
+     u8 subclass_id;
+     u8 offset;
+     u8 bytes;
+     u16 min, max;
+};
+
+struct bq27xxx_dm_buf {
+     u8 class;
+     u8 block;
+     u8 a[BQ27XXX_DM_SZ];
+     bool full, updt;
+};
+
+#define BQ27XXX_DM_BUF(di, i) { \
+     .class = bq27xxx_dm_regs[(di)->chip][i].subclass_id, \
+     .block = bq27xxx_dm_regs[(di)->chip][i].offset / BQ27XXX_DM_SZ, \
+}
+
+static inline u16* bq27xxx_dm_buf_ptr(struct bq27xxx_dm_buf *buf,
+                                   struct bq27xxx_dm_reg *reg) {
+     if (buf->class == reg->subclass_id
+      && buf->block == reg->offset / BQ27XXX_DM_SZ)
+             return (u16*) (buf->a + reg->offset % BQ27XXX_DM_SZ);
+
+     return NULL;
+}
+
+enum bq27xxx_dm_reg_id {
+     BQ27XXX_DM_DESIGN_CAPACITY = 0,
+     BQ27XXX_DM_DESIGN_ENERGY,
+     BQ27XXX_DM_TERMINATE_VOLTAGE,
+};
+
+static const char* bq27xxx_dm_reg_name[] = {
+     [BQ27XXX_DM_DESIGN_CAPACITY] = "design-capacity",
+     [BQ27XXX_DM_DESIGN_ENERGY] = "design-energy",
+     [BQ27XXX_DM_TERMINATE_VOLTAGE] = "terminate-voltage",
+};
+
+static struct bq27xxx_dm_reg 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 },
+};
Quoted 8 lines

drivers/power/supply/bq27xxx_battery.c static struct { · line 571

Quoted 6 lines
drivers/power/supply/bq27xxx_battery.c +6 -0
+
+//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 },
+//};

I don't think this comment style is allowed.

This is a to-do item.

Quoted 9 lines

drivers/power/supply/bq27xxx_battery.c static struct { · line 577

Quoted 6 lines
drivers/power/supply/bq27xxx_battery.c +6 -0
+
+static struct bq27xxx_dm_reg *bq27xxx_dm_regs[] = {
+     [BQ27421] = bq27421_dm_regs, /* and BQ27441 */
+     [BQ27425] = bq27425_dm_regs,
+//   [BQ27621] = bq27621_dm_regs,
+};

I know it is not tested, but lets not comment it out, I'll do a round of testing for this part when this series is ready.

ID for 621 is not yet defined. We need an efficient way to add IDs. Perhaps via chip-id and group-id. Most current ID refs would be to group-id. I'd like to defer that, so we should only add inputs for the single-chip groups in this patchset: 500, 545, 425.

Quoted 5 lines

We must NOT set one-time program (OTP) memory, mentioned in 421 & 441 docs. It's not clear whether documented data-memory ops change OTP. Can you ask? The 425 has rewritable NVM. The 621 docs don't say. All the above have cfgupdate mode, which the rest of the family lacks.

This would be a good question for e2e[0].

[0] http://e2e.ti.com/support/power_management/battery_management/f/180

author
#29 lore raw

Re: [PATCH v8 7/9] power: bq27xxx_battery: Add power_supply_battery_info support

Quoted 274 lines · Andrew F. Davis
Quoted 267 lines · Liam Breck
Quoted 56 lines · Andrew F. Davis
Quoted 48 lines · Liam Breck

From: Liam Breck <[email protected]>

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.

---
 drivers/power/supply/bq27xxx_battery.c | 458 ++++++++++++++++++++++++++++++++-
 1 file changed, 456 insertions(+), 2 deletions(-)
drivers/power/supply/bq27xxx_battery.c +7 -1
index 7475a5f..41d4ce7 100644
@@ -51,7 +51,7 @@
 
 #include <linux/power/bq27xxx_battery.h>
 
-#define DRIVER_VERSION               "1.2.0"
+#define DRIVER_VERSION               "1.3.0"
 
 #define BQ27XXX_MANUFACTURER "Texas Instruments"
 
@@ -59,6 +59,7 @@
 #define BQ27XXX_FLAG_DSC     BIT(0)
 #define BQ27XXX_FLAG_SOCF    BIT(1) /* State-of-Charge threshold final */
 #define BQ27XXX_FLAG_SOC1    BIT(2) /* State-of-Charge threshold 1 */
+#define BQ27XXX_FLAG_CFGUP   BIT(5)
 #define BQ27XXX_FLAG_FC              BIT(9)
 #define BQ27XXX_FLAG_OTD     BIT(14)
 #define BQ27XXX_FLAG_OTC     BIT(15)
@@ -72,6 +73,11 @@
 #define BQ27000_FLAG_FC              BIT(5)
 #define BQ27000_FLAG_CHGS    BIT(7) /* Charge state flag */
 
+/* control register params */
+#define BQ27XXX_SEALED                       0x20
+#define BQ27XXX_SET_CFGUPDATE                0x13
+#define BQ27XXX_SOFT_RESET           0x42
+
 #define BQ27XXX_RS                   (20) /* Resistor sense mOhm */
 #define BQ27XXX_POWER_CONSTANT               (29200) /* 29.2 µV^2 * 1000 */
 #define BQ27XXX_CURRENT_CONSTANT     (3570) /* 3.57 µV * 1000 */

drivers/power/supply/bq27xxx_battery.c enum bq27xxx_reg_index { · line 111

Quoted 4 lines
drivers/power/supply/bq27xxx_battery.c +1 -0
@@ -102,6 +108,11 @@ enum bq27xxx_reg_index {
      BQ27XXX_REG_SOC,        /* State-of-Charge */
      BQ27XXX_REG_DCAP,       /* Design Capacity */
      BQ27XXX_REG_AP,         /* Average Power */
+     BQ27XXX_DM_CTRL,        /* BlockDataControl() */

/* Block Data Control */

Quoted 3 lines

drivers/power/supply/bq27xxx_battery.c enum bq27xxx_reg_index { · line 112

Quoted 1 line
drivers/power/supply/bq27xxx_battery.c +1 -0
+     BQ27XXX_DM_CLASS,       /* DataClass() */

/* Data Class */

I used the terms from the docs, so a search will find meaningful results.

Quoted 105 lines
Quoted 105 lines
drivers/power/supply/bq27xxx_battery.c +43 -0
+     BQ27XXX_DM_BLOCK,       /* DataBlock() */
+     BQ27XXX_DM_DATA,        /* BlockData() */
+     BQ27XXX_DM_CKSUM,       /* BlockDataChecksum() */
      BQ27XXX_REG_MAX,        /* sentinel */
 };
 
@@ -125,6 +136,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x0b,
              [BQ27XXX_REG_DCAP] = 0x76,
              [BQ27XXX_REG_AP] = 0x24,
+             [BQ27XXX_DM_CTRL] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CLASS] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_BLOCK] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_DATA] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR,
      },
      [BQ27010] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -144,6 +160,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x0b,
              [BQ27XXX_REG_DCAP] = 0x76,
              [BQ27XXX_REG_AP] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CTRL] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CLASS] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_BLOCK] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_DATA] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR,
      },
      [BQ27500] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -163,6 +184,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x2c,
              [BQ27XXX_REG_DCAP] = 0x3c,
              [BQ27XXX_REG_AP] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27510] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -182,6 +208,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x20,
              [BQ27XXX_REG_DCAP] = 0x2e,
              [BQ27XXX_REG_AP] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27530] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -201,6 +232,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x2c,
              [BQ27XXX_REG_DCAP] = INVALID_REG_ADDR,
              [BQ27XXX_REG_AP] = 0x24,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27541] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -220,6 +256,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x2c,
              [BQ27XXX_REG_DCAP] = 0x3c,
              [BQ27XXX_REG_AP] = 0x24,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27545] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -239,6 +280,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x2c,
              [BQ27XXX_REG_DCAP] = INVALID_REG_ADDR,
              [BQ27XXX_REG_AP] = 0x24,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27421] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -258,6 +304,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x1c,
              [BQ27XXX_REG_DCAP] = 0x3c,
              [BQ27XXX_REG_AP] = 0x18,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27425] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -277,6 +328,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x1c,
              [BQ27XXX_REG_DCAP] = 0x3c,
Quoted 8 lines

drivers/power/supply/bq27xxx_battery.c static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = { · line 311

Quoted 6 lines
drivers/power/supply/bq27xxx_battery.c +5 -0
              [BQ27XXX_REG_AP] = 0x18,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,

That wasn't so painful was it :)

It added 11% to the patch :-/

Quoted 62 lines
Quoted 62 lines
drivers/power/supply/bq27xxx_battery.c +55 -0
      },
 };
 
@@ -452,6 +508,81 @@ static struct {
 static DEFINE_MUTEX(bq27xxx_list_lock);
 static LIST_HEAD(bq27xxx_battery_devices);
 
+#define BQ27XXX_DM_SZ                        32
+
+#define BQ27XXX_MSLEEP(i) usleep_range((i)*1000, (i)*1000+500)
+
+struct bq27xxx_dm_reg {
+     u8 subclass_id;
+     u8 offset;
+     u8 bytes;
+     u16 min, max;
+};
+
+struct bq27xxx_dm_buf {
+     u8 class;
+     u8 block;
+     u8 a[BQ27XXX_DM_SZ];
+     bool full, updt;
+};
+
+#define BQ27XXX_DM_BUF(di, i) { \
+     .class = bq27xxx_dm_regs[(di)->chip][i].subclass_id, \
+     .block = bq27xxx_dm_regs[(di)->chip][i].offset / BQ27XXX_DM_SZ, \
+}
+
+static inline u16* bq27xxx_dm_buf_ptr(struct bq27xxx_dm_buf *buf,
+                                   struct bq27xxx_dm_reg *reg) {
+     if (buf->class == reg->subclass_id
+      && buf->block == reg->offset / BQ27XXX_DM_SZ)
+             return (u16*) (buf->a + reg->offset % BQ27XXX_DM_SZ);
+
+     return NULL;
+}
+
+enum bq27xxx_dm_reg_id {
+     BQ27XXX_DM_DESIGN_CAPACITY = 0,
+     BQ27XXX_DM_DESIGN_ENERGY,
+     BQ27XXX_DM_TERMINATE_VOLTAGE,
+};
+
+static const char* bq27xxx_dm_reg_name[] = {
+     [BQ27XXX_DM_DESIGN_CAPACITY] = "design-capacity",
+     [BQ27XXX_DM_DESIGN_ENERGY] = "design-energy",
+     [BQ27XXX_DM_TERMINATE_VOLTAGE] = "terminate-voltage",
+};
+
+static struct bq27xxx_dm_reg 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 },
+};
Quoted 8 lines

drivers/power/supply/bq27xxx_battery.c static struct { · line 571

Quoted 6 lines
drivers/power/supply/bq27xxx_battery.c +6 -0
+
+//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 },
+//};

I don't think this comment style is allowed.

This is a to-do item.

Quoted 9 lines

drivers/power/supply/bq27xxx_battery.c static struct { · line 577

Quoted 6 lines
drivers/power/supply/bq27xxx_battery.c +6 -0
+
+static struct bq27xxx_dm_reg *bq27xxx_dm_regs[] = {
+     [BQ27421] = bq27421_dm_regs, /* and BQ27441 */
+     [BQ27425] = bq27425_dm_regs,
+//   [BQ27621] = bq27621_dm_regs,
+};

I know it is not tested, but lets not comment it out, I'll do a round of testing for this part when this series is ready.

ID for 621 is not yet defined. We need an efficient way to add IDs. Perhaps via chip-id and group-id. Most current ID refs would be to group-id. I'd like to defer that, so we should only add inputs for the single-chip groups in this patchset: 500, 545, 425.

Quoted 5 lines

We must NOT set one-time program (OTP) memory, mentioned in 421 & 441 docs. It's not clear whether documented data-memory ops change OTP. Can you ask? The 425 has rewritable NVM. The 621 docs don't say. All the above have cfgupdate mode, which the rest of the family lacks.

Quoted 3 lines

This would be a good question for e2e[0].

[0] http://e2e.ti.com/support/power_management/battery_management/f/180

Can I ask you to handle discussions with TI folks?

Thanks :-)

#30 lore raw

Re: [PATCH v8 7/9] power: bq27xxx_battery: Add power_supply_battery_info support

Quoted 275 lines · Liam Breck
Quoted 274 lines · Andrew F. Davis
Quoted 272 lines · Liam Breck
Quoted 56 lines · Andrew F. Davis
Quoted 48 lines · Liam Breck

From: Liam Breck <[email protected]>

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.

---
 drivers/power/supply/bq27xxx_battery.c | 458 ++++++++++++++++++++++++++++++++-
 1 file changed, 456 insertions(+), 2 deletions(-)
drivers/power/supply/bq27xxx_battery.c +7 -1
index 7475a5f..41d4ce7 100644
@@ -51,7 +51,7 @@
 
 #include <linux/power/bq27xxx_battery.h>
 
-#define DRIVER_VERSION               "1.2.0"
+#define DRIVER_VERSION               "1.3.0"
 
 #define BQ27XXX_MANUFACTURER "Texas Instruments"
 
@@ -59,6 +59,7 @@
 #define BQ27XXX_FLAG_DSC     BIT(0)
 #define BQ27XXX_FLAG_SOCF    BIT(1) /* State-of-Charge threshold final */
 #define BQ27XXX_FLAG_SOC1    BIT(2) /* State-of-Charge threshold 1 */
+#define BQ27XXX_FLAG_CFGUP   BIT(5)
 #define BQ27XXX_FLAG_FC              BIT(9)
 #define BQ27XXX_FLAG_OTD     BIT(14)
 #define BQ27XXX_FLAG_OTC     BIT(15)
@@ -72,6 +73,11 @@
 #define BQ27000_FLAG_FC              BIT(5)
 #define BQ27000_FLAG_CHGS    BIT(7) /* Charge state flag */
 
+/* control register params */
+#define BQ27XXX_SEALED                       0x20
+#define BQ27XXX_SET_CFGUPDATE                0x13
+#define BQ27XXX_SOFT_RESET           0x42
+
 #define BQ27XXX_RS                   (20) /* Resistor sense mOhm */
 #define BQ27XXX_POWER_CONSTANT               (29200) /* 29.2 µV^2 * 1000 */
 #define BQ27XXX_CURRENT_CONSTANT     (3570) /* 3.57 µV * 1000 */

drivers/power/supply/bq27xxx_battery.c enum bq27xxx_reg_index { · line 111

Quoted 4 lines
drivers/power/supply/bq27xxx_battery.c +1 -0
@@ -102,6 +108,11 @@ enum bq27xxx_reg_index {
      BQ27XXX_REG_SOC,        /* State-of-Charge */
      BQ27XXX_REG_DCAP,       /* Design Capacity */
      BQ27XXX_REG_AP,         /* Average Power */
+     BQ27XXX_DM_CTRL,        /* BlockDataControl() */

/* Block Data Control */

Quoted 3 lines

drivers/power/supply/bq27xxx_battery.c enum bq27xxx_reg_index { · line 112

Quoted 1 line
drivers/power/supply/bq27xxx_battery.c +1 -0
+     BQ27XXX_DM_CLASS,       /* DataClass() */

/* Data Class */

I used the terms from the docs, so a search will find meaningful results.

Quoted 105 lines
Quoted 105 lines
drivers/power/supply/bq27xxx_battery.c +43 -0
+     BQ27XXX_DM_BLOCK,       /* DataBlock() */
+     BQ27XXX_DM_DATA,        /* BlockData() */
+     BQ27XXX_DM_CKSUM,       /* BlockDataChecksum() */
      BQ27XXX_REG_MAX,        /* sentinel */
 };
 
@@ -125,6 +136,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x0b,
              [BQ27XXX_REG_DCAP] = 0x76,
              [BQ27XXX_REG_AP] = 0x24,
+             [BQ27XXX_DM_CTRL] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CLASS] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_BLOCK] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_DATA] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR,
      },
      [BQ27010] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -144,6 +160,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x0b,
              [BQ27XXX_REG_DCAP] = 0x76,
              [BQ27XXX_REG_AP] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CTRL] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CLASS] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_BLOCK] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_DATA] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR,
      },
      [BQ27500] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -163,6 +184,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x2c,
              [BQ27XXX_REG_DCAP] = 0x3c,
              [BQ27XXX_REG_AP] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27510] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -182,6 +208,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x20,
              [BQ27XXX_REG_DCAP] = 0x2e,
              [BQ27XXX_REG_AP] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27530] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -201,6 +232,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x2c,
              [BQ27XXX_REG_DCAP] = INVALID_REG_ADDR,
              [BQ27XXX_REG_AP] = 0x24,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27541] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -220,6 +256,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x2c,
              [BQ27XXX_REG_DCAP] = 0x3c,
              [BQ27XXX_REG_AP] = 0x24,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27545] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -239,6 +280,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x2c,
              [BQ27XXX_REG_DCAP] = INVALID_REG_ADDR,
              [BQ27XXX_REG_AP] = 0x24,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27421] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -258,6 +304,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x1c,
              [BQ27XXX_REG_DCAP] = 0x3c,
              [BQ27XXX_REG_AP] = 0x18,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27425] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -277,6 +328,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x1c,
              [BQ27XXX_REG_DCAP] = 0x3c,
Quoted 8 lines

drivers/power/supply/bq27xxx_battery.c static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = { · line 311

Quoted 6 lines
drivers/power/supply/bq27xxx_battery.c +5 -0
              [BQ27XXX_REG_AP] = 0x18,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,

That wasn't so painful was it :)

It added 11% to the patch :-/

Quoted 62 lines
Quoted 62 lines
drivers/power/supply/bq27xxx_battery.c +55 -0
      },
 };
 
@@ -452,6 +508,81 @@ static struct {
 static DEFINE_MUTEX(bq27xxx_list_lock);
 static LIST_HEAD(bq27xxx_battery_devices);
 
+#define BQ27XXX_DM_SZ                        32
+
+#define BQ27XXX_MSLEEP(i) usleep_range((i)*1000, (i)*1000+500)
+
+struct bq27xxx_dm_reg {
+     u8 subclass_id;
+     u8 offset;
+     u8 bytes;
+     u16 min, max;
+};
+
+struct bq27xxx_dm_buf {
+     u8 class;
+     u8 block;
+     u8 a[BQ27XXX_DM_SZ];
+     bool full, updt;
+};
+
+#define BQ27XXX_DM_BUF(di, i) { \
+     .class = bq27xxx_dm_regs[(di)->chip][i].subclass_id, \
+     .block = bq27xxx_dm_regs[(di)->chip][i].offset / BQ27XXX_DM_SZ, \
+}
+
+static inline u16* bq27xxx_dm_buf_ptr(struct bq27xxx_dm_buf *buf,
+                                   struct bq27xxx_dm_reg *reg) {
+     if (buf->class == reg->subclass_id
+      && buf->block == reg->offset / BQ27XXX_DM_SZ)
+             return (u16*) (buf->a + reg->offset % BQ27XXX_DM_SZ);
+
+     return NULL;
+}
+
+enum bq27xxx_dm_reg_id {
+     BQ27XXX_DM_DESIGN_CAPACITY = 0,
+     BQ27XXX_DM_DESIGN_ENERGY,
+     BQ27XXX_DM_TERMINATE_VOLTAGE,
+};
+
+static const char* bq27xxx_dm_reg_name[] = {
+     [BQ27XXX_DM_DESIGN_CAPACITY] = "design-capacity",
+     [BQ27XXX_DM_DESIGN_ENERGY] = "design-energy",
+     [BQ27XXX_DM_TERMINATE_VOLTAGE] = "terminate-voltage",
+};
+
+static struct bq27xxx_dm_reg 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 },
+};
Quoted 8 lines

drivers/power/supply/bq27xxx_battery.c static struct { · line 571

Quoted 6 lines
drivers/power/supply/bq27xxx_battery.c +6 -0
+
+//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 },
+//};

I don't think this comment style is allowed.

This is a to-do item.

Quoted 9 lines

drivers/power/supply/bq27xxx_battery.c static struct { · line 577

Quoted 6 lines
drivers/power/supply/bq27xxx_battery.c +6 -0
+
+static struct bq27xxx_dm_reg *bq27xxx_dm_regs[] = {
+     [BQ27421] = bq27421_dm_regs, /* and BQ27441 */
+     [BQ27425] = bq27425_dm_regs,
+//   [BQ27621] = bq27621_dm_regs,
+};

I know it is not tested, but lets not comment it out, I'll do a round of testing for this part when this series is ready.

ID for 621 is not yet defined. We need an efficient way to add IDs. Perhaps via chip-id and group-id. Most current ID refs would be to group-id. I'd like to defer that, so we should only add inputs for the single-chip groups in this patchset: 500, 545, 425.

We must NOT set one-time program (OTP) memory, mentioned in 421 & 441 docs. It's not clear whether documented data-memory ops change OTP. Can you ask? The 425 has rewritable NVM. The 621 docs don't say. All the above have cfgupdate mode, which the rest of the family lacks.

Quoted 6 lines
Quoted 3 lines

This would be a good question for e2e[0].

[0] http://e2e.ti.com/support/power_management/battery_management/f/180

Can I ask you to handle discussions with TI folks?

You can ask, doesn't mean I will :) but really I don't have any special means of getting a hold of the gauge team that you don't have (except maybe walking over to their cubes and bugging them).

We try to ask even internal inter-team questions on e2e, you can see questions asked by TI employees to other employees on that site, the idea is that if we have a problem, someday customers may have the same problem, so it's best not no keep these answers buried in some internal email chain. So the most I would do is post the same question to them on e2e, you can avoid me being a middle-man by asking them directly.

Thanks,
Andrew

Quoted 2 lines

Thanks :-)

author
#31 lore raw

Re: [PATCH v8 7/9] power: bq27xxx_battery: Add power_supply_battery_info support

Quoted 287 lines · Andrew F. Davis
Quoted 275 lines · Liam Breck
Quoted 274 lines · Andrew F. Davis
Quoted 272 lines · Liam Breck
Quoted 56 lines · Andrew F. Davis
Quoted 48 lines · Liam Breck

From: Liam Breck <[email protected]>

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.

---
 drivers/power/supply/bq27xxx_battery.c | 458 ++++++++++++++++++++++++++++++++-
 1 file changed, 456 insertions(+), 2 deletions(-)
drivers/power/supply/bq27xxx_battery.c +7 -1
index 7475a5f..41d4ce7 100644
@@ -51,7 +51,7 @@
 
 #include <linux/power/bq27xxx_battery.h>
 
-#define DRIVER_VERSION               "1.2.0"
+#define DRIVER_VERSION               "1.3.0"
 
 #define BQ27XXX_MANUFACTURER "Texas Instruments"
 
@@ -59,6 +59,7 @@
 #define BQ27XXX_FLAG_DSC     BIT(0)
 #define BQ27XXX_FLAG_SOCF    BIT(1) /* State-of-Charge threshold final */
 #define BQ27XXX_FLAG_SOC1    BIT(2) /* State-of-Charge threshold 1 */
+#define BQ27XXX_FLAG_CFGUP   BIT(5)
 #define BQ27XXX_FLAG_FC              BIT(9)
 #define BQ27XXX_FLAG_OTD     BIT(14)
 #define BQ27XXX_FLAG_OTC     BIT(15)
@@ -72,6 +73,11 @@
 #define BQ27000_FLAG_FC              BIT(5)
 #define BQ27000_FLAG_CHGS    BIT(7) /* Charge state flag */
 
+/* control register params */
+#define BQ27XXX_SEALED                       0x20
+#define BQ27XXX_SET_CFGUPDATE                0x13
+#define BQ27XXX_SOFT_RESET           0x42
+
 #define BQ27XXX_RS                   (20) /* Resistor sense mOhm */
 #define BQ27XXX_POWER_CONSTANT               (29200) /* 29.2 µV^2 * 1000 */
 #define BQ27XXX_CURRENT_CONSTANT     (3570) /* 3.57 µV * 1000 */

drivers/power/supply/bq27xxx_battery.c enum bq27xxx_reg_index { · line 111

Quoted 4 lines
drivers/power/supply/bq27xxx_battery.c +1 -0
@@ -102,6 +108,11 @@ enum bq27xxx_reg_index {
      BQ27XXX_REG_SOC,        /* State-of-Charge */
      BQ27XXX_REG_DCAP,       /* Design Capacity */
      BQ27XXX_REG_AP,         /* Average Power */
+     BQ27XXX_DM_CTRL,        /* BlockDataControl() */

/* Block Data Control */

Quoted 3 lines

drivers/power/supply/bq27xxx_battery.c enum bq27xxx_reg_index { · line 112

Quoted 1 line
drivers/power/supply/bq27xxx_battery.c +1 -0
+     BQ27XXX_DM_CLASS,       /* DataClass() */

/* Data Class */

I used the terms from the docs, so a search will find meaningful results.

Quoted 105 lines
Quoted 105 lines
drivers/power/supply/bq27xxx_battery.c +43 -0
+     BQ27XXX_DM_BLOCK,       /* DataBlock() */
+     BQ27XXX_DM_DATA,        /* BlockData() */
+     BQ27XXX_DM_CKSUM,       /* BlockDataChecksum() */
      BQ27XXX_REG_MAX,        /* sentinel */
 };
 
@@ -125,6 +136,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x0b,
              [BQ27XXX_REG_DCAP] = 0x76,
              [BQ27XXX_REG_AP] = 0x24,
+             [BQ27XXX_DM_CTRL] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CLASS] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_BLOCK] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_DATA] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR,
      },
      [BQ27010] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -144,6 +160,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x0b,
              [BQ27XXX_REG_DCAP] = 0x76,
              [BQ27XXX_REG_AP] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CTRL] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CLASS] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_BLOCK] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_DATA] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR,
      },
      [BQ27500] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -163,6 +184,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x2c,
              [BQ27XXX_REG_DCAP] = 0x3c,
              [BQ27XXX_REG_AP] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27510] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -182,6 +208,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x20,
              [BQ27XXX_REG_DCAP] = 0x2e,
              [BQ27XXX_REG_AP] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27530] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -201,6 +232,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x2c,
              [BQ27XXX_REG_DCAP] = INVALID_REG_ADDR,
              [BQ27XXX_REG_AP] = 0x24,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27541] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -220,6 +256,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x2c,
              [BQ27XXX_REG_DCAP] = 0x3c,
              [BQ27XXX_REG_AP] = 0x24,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27545] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -239,6 +280,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x2c,
              [BQ27XXX_REG_DCAP] = INVALID_REG_ADDR,
              [BQ27XXX_REG_AP] = 0x24,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27421] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -258,6 +304,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x1c,
              [BQ27XXX_REG_DCAP] = 0x3c,
              [BQ27XXX_REG_AP] = 0x18,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27425] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -277,6 +328,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x1c,
              [BQ27XXX_REG_DCAP] = 0x3c,
Quoted 8 lines

drivers/power/supply/bq27xxx_battery.c static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = { · line 311

Quoted 6 lines
drivers/power/supply/bq27xxx_battery.c +5 -0
              [BQ27XXX_REG_AP] = 0x18,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,

That wasn't so painful was it :)

It added 11% to the patch :-/

Quoted 62 lines
Quoted 62 lines
drivers/power/supply/bq27xxx_battery.c +55 -0
      },
 };
 
@@ -452,6 +508,81 @@ static struct {
 static DEFINE_MUTEX(bq27xxx_list_lock);
 static LIST_HEAD(bq27xxx_battery_devices);
 
+#define BQ27XXX_DM_SZ                        32
+
+#define BQ27XXX_MSLEEP(i) usleep_range((i)*1000, (i)*1000+500)
+
+struct bq27xxx_dm_reg {
+     u8 subclass_id;
+     u8 offset;
+     u8 bytes;
+     u16 min, max;
+};
+
+struct bq27xxx_dm_buf {
+     u8 class;
+     u8 block;
+     u8 a[BQ27XXX_DM_SZ];
+     bool full, updt;
+};
+
+#define BQ27XXX_DM_BUF(di, i) { \
+     .class = bq27xxx_dm_regs[(di)->chip][i].subclass_id, \
+     .block = bq27xxx_dm_regs[(di)->chip][i].offset / BQ27XXX_DM_SZ, \
+}
+
+static inline u16* bq27xxx_dm_buf_ptr(struct bq27xxx_dm_buf *buf,
+                                   struct bq27xxx_dm_reg *reg) {
+     if (buf->class == reg->subclass_id
+      && buf->block == reg->offset / BQ27XXX_DM_SZ)
+             return (u16*) (buf->a + reg->offset % BQ27XXX_DM_SZ);
+
+     return NULL;
+}
+
+enum bq27xxx_dm_reg_id {
+     BQ27XXX_DM_DESIGN_CAPACITY = 0,
+     BQ27XXX_DM_DESIGN_ENERGY,
+     BQ27XXX_DM_TERMINATE_VOLTAGE,
+};
+
+static const char* bq27xxx_dm_reg_name[] = {
+     [BQ27XXX_DM_DESIGN_CAPACITY] = "design-capacity",
+     [BQ27XXX_DM_DESIGN_ENERGY] = "design-energy",
+     [BQ27XXX_DM_TERMINATE_VOLTAGE] = "terminate-voltage",
+};
+
+static struct bq27xxx_dm_reg 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 },
+};
Quoted 8 lines

drivers/power/supply/bq27xxx_battery.c static struct { · line 571

Quoted 6 lines
drivers/power/supply/bq27xxx_battery.c +6 -0
+
+//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 },
+//};

I don't think this comment style is allowed.

This is a to-do item.

Quoted 9 lines

drivers/power/supply/bq27xxx_battery.c static struct { · line 577

Quoted 6 lines
drivers/power/supply/bq27xxx_battery.c +6 -0
+
+static struct bq27xxx_dm_reg *bq27xxx_dm_regs[] = {
+     [BQ27421] = bq27421_dm_regs, /* and BQ27441 */
+     [BQ27425] = bq27425_dm_regs,
+//   [BQ27621] = bq27621_dm_regs,
+};

I know it is not tested, but lets not comment it out, I'll do a round of testing for this part when this series is ready.

ID for 621 is not yet defined. We need an efficient way to add IDs. Perhaps via chip-id and group-id. Most current ID refs would be to group-id. I'd like to defer that, so we should only add inputs for the single-chip groups in this patchset: 500, 545, 425.

We must NOT set one-time program (OTP) memory, mentioned in 421 & 441 docs. It's not clear whether documented data-memory ops change OTP. Can you ask? The 425 has rewritable NVM. The 621 docs don't say. All the above have cfgupdate mode, which the rest of the family lacks.

Quoted 6 lines
Quoted 3 lines

This would be a good question for e2e[0].

[0] http://e2e.ti.com/support/power_management/battery_management/f/180

Can I ask you to handle discussions with TI folks?

You can ask, doesn't mean I will :) but really I don't have any special means of getting a hold of the gauge team that you don't have (except maybe walking over to their cubes and bugging them).

Quoted 6 lines

We try to ask even internal inter-team questions on e2e, you can see questions asked by TI employees to other employees on that site, the idea is that if we have a problem, someday customers may have the same problem, so it's best not no keep these answers buried in some internal email chain. So the most I would do is post the same question to them on e2e, you can avoid me being a middle-man by asking them directly.

I understand; I'm just trying to minimize my todo list for this patchset. We can put off the OTP issue, as this patchset won't support those parts, since that needs the chip-id/group-id fix I mentioned.

author
#32 lore raw

Re: [PATCH v8 7/9] power: bq27xxx_battery: Add power_supply_battery_info support

Quoted 295 lines · Liam Breck
Quoted 287 lines · Andrew F. Davis
Quoted 275 lines · Liam Breck
Quoted 274 lines · Andrew F. Davis
Quoted 272 lines · Liam Breck
Quoted 56 lines · Andrew F. Davis
Quoted 48 lines · Liam Breck

From: Liam Breck <[email protected]>

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.

---
 drivers/power/supply/bq27xxx_battery.c | 458 ++++++++++++++++++++++++++++++++-
 1 file changed, 456 insertions(+), 2 deletions(-)
drivers/power/supply/bq27xxx_battery.c +7 -1
index 7475a5f..41d4ce7 100644
@@ -51,7 +51,7 @@
 
 #include <linux/power/bq27xxx_battery.h>
 
-#define DRIVER_VERSION               "1.2.0"
+#define DRIVER_VERSION               "1.3.0"
 
 #define BQ27XXX_MANUFACTURER "Texas Instruments"
 
@@ -59,6 +59,7 @@
 #define BQ27XXX_FLAG_DSC     BIT(0)
 #define BQ27XXX_FLAG_SOCF    BIT(1) /* State-of-Charge threshold final */
 #define BQ27XXX_FLAG_SOC1    BIT(2) /* State-of-Charge threshold 1 */
+#define BQ27XXX_FLAG_CFGUP   BIT(5)
 #define BQ27XXX_FLAG_FC              BIT(9)
 #define BQ27XXX_FLAG_OTD     BIT(14)
 #define BQ27XXX_FLAG_OTC     BIT(15)
@@ -72,6 +73,11 @@
 #define BQ27000_FLAG_FC              BIT(5)
 #define BQ27000_FLAG_CHGS    BIT(7) /* Charge state flag */
 
+/* control register params */
+#define BQ27XXX_SEALED                       0x20
+#define BQ27XXX_SET_CFGUPDATE                0x13
+#define BQ27XXX_SOFT_RESET           0x42
+
 #define BQ27XXX_RS                   (20) /* Resistor sense mOhm */
 #define BQ27XXX_POWER_CONSTANT               (29200) /* 29.2 µV^2 * 1000 */
 #define BQ27XXX_CURRENT_CONSTANT     (3570) /* 3.57 µV * 1000 */

drivers/power/supply/bq27xxx_battery.c enum bq27xxx_reg_index { · line 111

Quoted 4 lines
drivers/power/supply/bq27xxx_battery.c +1 -0
@@ -102,6 +108,11 @@ enum bq27xxx_reg_index {
      BQ27XXX_REG_SOC,        /* State-of-Charge */
      BQ27XXX_REG_DCAP,       /* Design Capacity */
      BQ27XXX_REG_AP,         /* Average Power */
+     BQ27XXX_DM_CTRL,        /* BlockDataControl() */

/* Block Data Control */

Quoted 3 lines

drivers/power/supply/bq27xxx_battery.c enum bq27xxx_reg_index { · line 112

Quoted 1 line
drivers/power/supply/bq27xxx_battery.c +1 -0
+     BQ27XXX_DM_CLASS,       /* DataClass() */

/* Data Class */

I used the terms from the docs, so a search will find meaningful results.

Quoted 105 lines
Quoted 105 lines
drivers/power/supply/bq27xxx_battery.c +43 -0
+     BQ27XXX_DM_BLOCK,       /* DataBlock() */
+     BQ27XXX_DM_DATA,        /* BlockData() */
+     BQ27XXX_DM_CKSUM,       /* BlockDataChecksum() */
      BQ27XXX_REG_MAX,        /* sentinel */
 };
 
@@ -125,6 +136,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x0b,
              [BQ27XXX_REG_DCAP] = 0x76,
              [BQ27XXX_REG_AP] = 0x24,
+             [BQ27XXX_DM_CTRL] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CLASS] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_BLOCK] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_DATA] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR,
      },
      [BQ27010] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -144,6 +160,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x0b,
              [BQ27XXX_REG_DCAP] = 0x76,
              [BQ27XXX_REG_AP] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CTRL] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CLASS] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_BLOCK] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_DATA] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR,
      },
      [BQ27500] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -163,6 +184,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x2c,
              [BQ27XXX_REG_DCAP] = 0x3c,
              [BQ27XXX_REG_AP] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27510] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -182,6 +208,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x20,
              [BQ27XXX_REG_DCAP] = 0x2e,
              [BQ27XXX_REG_AP] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27530] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -201,6 +232,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x2c,
              [BQ27XXX_REG_DCAP] = INVALID_REG_ADDR,
              [BQ27XXX_REG_AP] = 0x24,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27541] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -220,6 +256,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x2c,
              [BQ27XXX_REG_DCAP] = 0x3c,
              [BQ27XXX_REG_AP] = 0x24,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27545] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -239,6 +280,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x2c,
              [BQ27XXX_REG_DCAP] = INVALID_REG_ADDR,
              [BQ27XXX_REG_AP] = 0x24,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27421] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -258,6 +304,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x1c,
              [BQ27XXX_REG_DCAP] = 0x3c,
              [BQ27XXX_REG_AP] = 0x18,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27425] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -277,6 +328,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x1c,
              [BQ27XXX_REG_DCAP] = 0x3c,
Quoted 8 lines

drivers/power/supply/bq27xxx_battery.c static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = { · line 335

Quoted 6 lines
drivers/power/supply/bq27xxx_battery.c +5 -0
              [BQ27XXX_REG_AP] = 0x18,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,

That wasn't so painful was it :)

It added 11% to the patch :-/

Quoted 62 lines
Quoted 62 lines
drivers/power/supply/bq27xxx_battery.c +55 -0
      },
 };
 
@@ -452,6 +508,81 @@ static struct {
 static DEFINE_MUTEX(bq27xxx_list_lock);
 static LIST_HEAD(bq27xxx_battery_devices);
 
+#define BQ27XXX_DM_SZ                        32
+
+#define BQ27XXX_MSLEEP(i) usleep_range((i)*1000, (i)*1000+500)
+
+struct bq27xxx_dm_reg {
+     u8 subclass_id;
+     u8 offset;
+     u8 bytes;
+     u16 min, max;
+};
+
+struct bq27xxx_dm_buf {
+     u8 class;
+     u8 block;
+     u8 a[BQ27XXX_DM_SZ];
+     bool full, updt;
+};
+
+#define BQ27XXX_DM_BUF(di, i) { \
+     .class = bq27xxx_dm_regs[(di)->chip][i].subclass_id, \
+     .block = bq27xxx_dm_regs[(di)->chip][i].offset / BQ27XXX_DM_SZ, \
+}
+
+static inline u16* bq27xxx_dm_buf_ptr(struct bq27xxx_dm_buf *buf,
+                                   struct bq27xxx_dm_reg *reg) {
+     if (buf->class == reg->subclass_id
+      && buf->block == reg->offset / BQ27XXX_DM_SZ)
+             return (u16*) (buf->a + reg->offset % BQ27XXX_DM_SZ);
+
+     return NULL;
+}
+
+enum bq27xxx_dm_reg_id {
+     BQ27XXX_DM_DESIGN_CAPACITY = 0,
+     BQ27XXX_DM_DESIGN_ENERGY,
+     BQ27XXX_DM_TERMINATE_VOLTAGE,
+};
+
+static const char* bq27xxx_dm_reg_name[] = {
+     [BQ27XXX_DM_DESIGN_CAPACITY] = "design-capacity",
+     [BQ27XXX_DM_DESIGN_ENERGY] = "design-energy",
+     [BQ27XXX_DM_TERMINATE_VOLTAGE] = "terminate-voltage",
+};
+
+static struct bq27xxx_dm_reg 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 },
+};
Quoted 8 lines

drivers/power/supply/bq27xxx_battery.c static struct { · line 571

Quoted 6 lines
drivers/power/supply/bq27xxx_battery.c +6 -0
+
+//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 },
+//};

I don't think this comment style is allowed.

This is a to-do item.

Quoted 9 lines

drivers/power/supply/bq27xxx_battery.c static struct { · line 577

Quoted 6 lines
drivers/power/supply/bq27xxx_battery.c +6 -0
+
+static struct bq27xxx_dm_reg *bq27xxx_dm_regs[] = {
+     [BQ27421] = bq27421_dm_regs, /* and BQ27441 */
+     [BQ27425] = bq27425_dm_regs,
+//   [BQ27621] = bq27621_dm_regs,
+};

I know it is not tested, but lets not comment it out, I'll do a round of testing for this part when this series is ready.

ID for 621 is not yet defined. We need an efficient way to add IDs. Perhaps via chip-id and group-id. Most current ID refs would be to group-id. I'd like to defer that, so we should only add inputs for the single-chip groups in this patchset: 500, 545, 425.

We must NOT set one-time program (OTP) memory, mentioned in 421 & 441 docs. It's not clear whether documented data-memory ops change OTP. Can you ask? The 425 has rewritable NVM. The 621 docs don't say. All the above have cfgupdate mode, which the rest of the family lacks.

Quoted 6 lines
Quoted 3 lines

This would be a good question for e2e[0].

[0] http://e2e.ti.com/support/power_management/battery_management/f/180

Can I ask you to handle discussions with TI folks?

You can ask, doesn't mean I will :) but really I don't have any special means of getting a hold of the gauge team that you don't have (except maybe walking over to their cubes and bugging them).

Quoted 6 lines

We try to ask even internal inter-team questions on e2e, you can see questions asked by TI employees to other employees on that site, the idea is that if we have a problem, someday customers may have the same problem, so it's best not no keep these answers buried in some internal email chain. So the most I would do is post the same question to them on e2e, you can avoid me being a middle-man by asking them directly.

Quoted 3 lines

I understand; I'm just trying to minimize my todo list for this patchset. We can put off the OTP issue, as this patchset won't support those parts, since that needs the chip-id/group-id fix I mentioned.

Here for use now or later is the Q I would ask:

The following parts provide set_cfgupdate & soft_reset commands:
BQ27... 421, 425, 441, 621

Docs for 421 & 441 mention OTP memory
Docs for 621 do not mention OTP
Docs for 425 mention rewritable NVM

What is in OTP on 421 & 441?
Is anything in OTP on 621?
Is anything in OTP on 425?

Will documented data-memory ops, e.g. BlockData(), change OTP? Is OTP only changed in config-update mode?

Context: we are adding DM update to the BQ27xxx linux driver, and must avoid setting OTP memory.

author
#33 lore raw

Re: [PATCH v8 7/9] power: bq27xxx_battery: Add power_supply_battery_info support

Quoted 313 lines · Liam Breck
Quoted 295 lines · Liam Breck
Quoted 287 lines · Andrew F. Davis
Quoted 275 lines · Liam Breck
Quoted 274 lines · Andrew F. Davis
Quoted 272 lines · Liam Breck
Quoted 56 lines · Andrew F. Davis
Quoted 48 lines · Liam Breck

From: Liam Breck <[email protected]>

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.

---
 drivers/power/supply/bq27xxx_battery.c | 458 ++++++++++++++++++++++++++++++++-
 1 file changed, 456 insertions(+), 2 deletions(-)
drivers/power/supply/bq27xxx_battery.c +7 -1
index 7475a5f..41d4ce7 100644
@@ -51,7 +51,7 @@
 
 #include <linux/power/bq27xxx_battery.h>
 
-#define DRIVER_VERSION               "1.2.0"
+#define DRIVER_VERSION               "1.3.0"
 
 #define BQ27XXX_MANUFACTURER "Texas Instruments"
 
@@ -59,6 +59,7 @@
 #define BQ27XXX_FLAG_DSC     BIT(0)
 #define BQ27XXX_FLAG_SOCF    BIT(1) /* State-of-Charge threshold final */
 #define BQ27XXX_FLAG_SOC1    BIT(2) /* State-of-Charge threshold 1 */
+#define BQ27XXX_FLAG_CFGUP   BIT(5)
 #define BQ27XXX_FLAG_FC              BIT(9)
 #define BQ27XXX_FLAG_OTD     BIT(14)
 #define BQ27XXX_FLAG_OTC     BIT(15)
@@ -72,6 +73,11 @@
 #define BQ27000_FLAG_FC              BIT(5)
 #define BQ27000_FLAG_CHGS    BIT(7) /* Charge state flag */
 
+/* control register params */
+#define BQ27XXX_SEALED                       0x20
+#define BQ27XXX_SET_CFGUPDATE                0x13
+#define BQ27XXX_SOFT_RESET           0x42
+
 #define BQ27XXX_RS                   (20) /* Resistor sense mOhm */
 #define BQ27XXX_POWER_CONSTANT               (29200) /* 29.2 µV^2 * 1000 */
 #define BQ27XXX_CURRENT_CONSTANT     (3570) /* 3.57 µV * 1000 */

drivers/power/supply/bq27xxx_battery.c enum bq27xxx_reg_index { · line 111

Quoted 4 lines
drivers/power/supply/bq27xxx_battery.c +1 -0
@@ -102,6 +108,11 @@ enum bq27xxx_reg_index {
      BQ27XXX_REG_SOC,        /* State-of-Charge */
      BQ27XXX_REG_DCAP,       /* Design Capacity */
      BQ27XXX_REG_AP,         /* Average Power */
+     BQ27XXX_DM_CTRL,        /* BlockDataControl() */

/* Block Data Control */

Quoted 3 lines

drivers/power/supply/bq27xxx_battery.c enum bq27xxx_reg_index { · line 112

Quoted 1 line
drivers/power/supply/bq27xxx_battery.c +1 -0
+     BQ27XXX_DM_CLASS,       /* DataClass() */

/* Data Class */

I used the terms from the docs, so a search will find meaningful results.

Quoted 105 lines
Quoted 105 lines
drivers/power/supply/bq27xxx_battery.c +43 -0
+     BQ27XXX_DM_BLOCK,       /* DataBlock() */
+     BQ27XXX_DM_DATA,        /* BlockData() */
+     BQ27XXX_DM_CKSUM,       /* BlockDataChecksum() */
      BQ27XXX_REG_MAX,        /* sentinel */
 };
 
@@ -125,6 +136,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x0b,
              [BQ27XXX_REG_DCAP] = 0x76,
              [BQ27XXX_REG_AP] = 0x24,
+             [BQ27XXX_DM_CTRL] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CLASS] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_BLOCK] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_DATA] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR,
      },
      [BQ27010] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -144,6 +160,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x0b,
              [BQ27XXX_REG_DCAP] = 0x76,
              [BQ27XXX_REG_AP] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CTRL] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CLASS] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_BLOCK] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_DATA] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CKSUM] = INVALID_REG_ADDR,
      },
      [BQ27500] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -163,6 +184,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x2c,
              [BQ27XXX_REG_DCAP] = 0x3c,
              [BQ27XXX_REG_AP] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27510] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -182,6 +208,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x20,
              [BQ27XXX_REG_DCAP] = 0x2e,
              [BQ27XXX_REG_AP] = INVALID_REG_ADDR,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27530] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -201,6 +232,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x2c,
              [BQ27XXX_REG_DCAP] = INVALID_REG_ADDR,
              [BQ27XXX_REG_AP] = 0x24,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27541] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -220,6 +256,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x2c,
              [BQ27XXX_REG_DCAP] = 0x3c,
              [BQ27XXX_REG_AP] = 0x24,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27545] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -239,6 +280,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x2c,
              [BQ27XXX_REG_DCAP] = INVALID_REG_ADDR,
              [BQ27XXX_REG_AP] = 0x24,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27421] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -258,6 +304,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x1c,
              [BQ27XXX_REG_DCAP] = 0x3c,
              [BQ27XXX_REG_AP] = 0x18,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,
      },
      [BQ27425] = {
              [BQ27XXX_REG_CTRL] = 0x00,
@@ -277,6 +328,11 @@ static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = {
              [BQ27XXX_REG_SOC] = 0x1c,
              [BQ27XXX_REG_DCAP] = 0x3c,
Quoted 8 lines

drivers/power/supply/bq27xxx_battery.c static u8 bq27xxx_regs[][BQ27XXX_REG_MAX] = { · line 335

Quoted 6 lines
drivers/power/supply/bq27xxx_battery.c +5 -0
              [BQ27XXX_REG_AP] = 0x18,
+             [BQ27XXX_DM_CTRL] = 0x61,
+             [BQ27XXX_DM_CLASS] = 0x3e,
+             [BQ27XXX_DM_BLOCK] = 0x3f,
+             [BQ27XXX_DM_DATA] = 0x40,
+             [BQ27XXX_DM_CKSUM] = 0x60,

That wasn't so painful was it :)

It added 11% to the patch :-/

Quoted 62 lines
Quoted 62 lines
drivers/power/supply/bq27xxx_battery.c +55 -0
      },
 };
 
@@ -452,6 +508,81 @@ static struct {
 static DEFINE_MUTEX(bq27xxx_list_lock);
 static LIST_HEAD(bq27xxx_battery_devices);
 
+#define BQ27XXX_DM_SZ                        32
+
+#define BQ27XXX_MSLEEP(i) usleep_range((i)*1000, (i)*1000+500)
+
+struct bq27xxx_dm_reg {
+     u8 subclass_id;
+     u8 offset;
+     u8 bytes;
+     u16 min, max;
+};
+
+struct bq27xxx_dm_buf {
+     u8 class;
+     u8 block;
+     u8 a[BQ27XXX_DM_SZ];
+     bool full, updt;
+};
+
+#define BQ27XXX_DM_BUF(di, i) { \
+     .class = bq27xxx_dm_regs[(di)->chip][i].subclass_id, \
+     .block = bq27xxx_dm_regs[(di)->chip][i].offset / BQ27XXX_DM_SZ, \
+}
+
+static inline u16* bq27xxx_dm_buf_ptr(struct bq27xxx_dm_buf *buf,
+                                   struct bq27xxx_dm_reg *reg) {
+     if (buf->class == reg->subclass_id
+      && buf->block == reg->offset / BQ27XXX_DM_SZ)
+             return (u16*) (buf->a + reg->offset % BQ27XXX_DM_SZ);
+
+     return NULL;
+}
+
+enum bq27xxx_dm_reg_id {
+     BQ27XXX_DM_DESIGN_CAPACITY = 0,
+     BQ27XXX_DM_DESIGN_ENERGY,
+     BQ27XXX_DM_TERMINATE_VOLTAGE,
+};
+
+static const char* bq27xxx_dm_reg_name[] = {
+     [BQ27XXX_DM_DESIGN_CAPACITY] = "design-capacity",
+     [BQ27XXX_DM_DESIGN_ENERGY] = "design-energy",
+     [BQ27XXX_DM_TERMINATE_VOLTAGE] = "terminate-voltage",
+};
+
+static struct bq27xxx_dm_reg 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 },
+};
Quoted 8 lines

drivers/power/supply/bq27xxx_battery.c static struct { · line 571

Quoted 6 lines
drivers/power/supply/bq27xxx_battery.c +6 -0
+
+//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 },
+//};

I don't think this comment style is allowed.

This is a to-do item.

Quoted 9 lines

drivers/power/supply/bq27xxx_battery.c static struct { · line 577

Quoted 6 lines
drivers/power/supply/bq27xxx_battery.c +6 -0
+
+static struct bq27xxx_dm_reg *bq27xxx_dm_regs[] = {
+     [BQ27421] = bq27421_dm_regs, /* and BQ27441 */
+     [BQ27425] = bq27425_dm_regs,
+//   [BQ27621] = bq27621_dm_regs,
+};

I know it is not tested, but lets not comment it out, I'll do a round of testing for this part when this series is ready.

ID for 621 is not yet defined. We need an efficient way to add IDs. Perhaps via chip-id and group-id. Most current ID refs would be to group-id. I'd like to defer that, so we should only add inputs for the single-chip groups in this patchset: 500, 545, 425.

We must NOT set one-time program (OTP) memory, mentioned in 421 & 441 docs. It's not clear whether documented data-memory ops change OTP. Can you ask? The 425 has rewritable NVM. The 621 docs don't say. All the above have cfgupdate mode, which the rest of the family lacks.

Quoted 6 lines
Quoted 3 lines

This would be a good question for e2e[0].

[0] http://e2e.ti.com/support/power_management/battery_management/f/180

Can I ask you to handle discussions with TI folks?

You can ask, doesn't mean I will :) but really I don't have any special means of getting a hold of the gauge team that you don't have (except maybe walking over to their cubes and bugging them).

Quoted 6 lines

We try to ask even internal inter-team questions on e2e, you can see questions asked by TI employees to other employees on that site, the idea is that if we have a problem, someday customers may have the same problem, so it's best not no keep these answers buried in some internal email chain. So the most I would do is post the same question to them on e2e, you can avoid me being a middle-man by asking them directly.

Quoted 3 lines

I understand; I'm just trying to minimize my todo list for this patchset. We can put off the OTP issue, as this patchset won't support those parts, since that needs the chip-id/group-id fix I mentioned.

Here for use now or later is the Q I would ask:

The following parts provide set_cfgupdate & soft_reset commands:
BQ27... 421, 425, 441, 621

Docs for 421 & 441 mention OTP memory
Docs for 621 do not mention OTP
Docs for 425 mention rewritable NVM

What is in OTP on 421 & 441?
Is anything in OTP on 621?
Is anything in OTP on 425?

Quoted 5 lines

Will documented data-memory ops, e.g. BlockData(), change OTP? Is OTP only changed in config-update mode?

Context: we are adding DM update to the BQ27xxx linux driver, and must avoid setting OTP memory.

The above Q was asked and answered here:
https://e2e.ti.com/support/power_management/battery_management/f/180/t/577798

On that basis, I'll use set_cfgupdate for all the mentioned parts.

author
#34 lore raw

Re: [PATCH v8 7/9] power: bq27xxx_battery: Add power_supply_battery_info support

In reply to Liam Breck

Hi Andrew,

Quoted 86 lines · Liam Breck

From: Liam Breck <[email protected]>

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.

---
 drivers/power/supply/bq27xxx_battery.c | 458 ++++++++++++++++++++++++++++++++-
 1 file changed, 456 insertions(+), 2 deletions(-)
drivers/power/supply/bq27xxx_battery.c +63 -0
index 7475a5f..41d4ce7 100644
@@ -452,6 +508,81 @@ static struct {
 static DEFINE_MUTEX(bq27xxx_list_lock);
 static LIST_HEAD(bq27xxx_battery_devices);
 
+#define BQ27XXX_DM_SZ                  32
+
+#define BQ27XXX_MSLEEP(i) usleep_range((i)*1000, (i)*1000+500)
+
+struct bq27xxx_dm_reg {
+       u8 subclass_id;
+       u8 offset;
+       u8 bytes;
+       u16 min, max;
+};
+
+struct bq27xxx_dm_buf {
+       u8 class;
+       u8 block;
+       u8 a[BQ27XXX_DM_SZ];
+       bool full, updt;
+};
+
+#define BQ27XXX_DM_BUF(di, i) { \
+       .class = bq27xxx_dm_regs[(di)->chip][i].subclass_id, \
+       .block = bq27xxx_dm_regs[(di)->chip][i].offset / BQ27XXX_DM_SZ, \
+}
+
+static inline u16* bq27xxx_dm_buf_ptr(struct bq27xxx_dm_buf *buf,
+                                     struct bq27xxx_dm_reg *reg) {
+       if (buf->class == reg->subclass_id
+        && buf->block == reg->offset / BQ27XXX_DM_SZ)
+               return (u16*) (buf->a + reg->offset % BQ27XXX_DM_SZ);
+
+       return NULL;
+}
+
+enum bq27xxx_dm_reg_id {
+       BQ27XXX_DM_DESIGN_CAPACITY = 0,
+       BQ27XXX_DM_DESIGN_ENERGY,
+       BQ27XXX_DM_TERMINATE_VOLTAGE,
+};
+
+static const char* bq27xxx_dm_reg_name[] = {
+       [BQ27XXX_DM_DESIGN_CAPACITY] = "design-capacity",
+       [BQ27XXX_DM_DESIGN_ENERGY] = "design-energy",
+       [BQ27XXX_DM_TERMINATE_VOLTAGE] = "terminate-voltage",
+};
+
+static struct bq27xxx_dm_reg 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[] = {

drivers/power/supply/bq27xxx_battery.c static struct { · line 583

Quoted 10 lines
drivers/power/supply/bq27xxx_battery.c +10 -0
+       [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,
+};

Maybe instead of defining dm_regs_* tables for many chips in this file, we should land them in a new header, to let the kernel builder define and/or pick one for his platform by setting a config option, e.g. config_bq27xxx_dm_type=421

We could also pull in the dm update code if such an option is defined, tho it isn't that much code, ~230 lines plus whitespace.

Thoughts?

#35 lore raw

Re: [PATCH v8 7/9] power: bq27xxx_battery: Add power_supply_battery_info support

Quoted 96 lines · Liam Breck

Hi Andrew,

Quoted 93 lines · Liam Breck

From: Liam Breck <[email protected]>

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.

---
 drivers/power/supply/bq27xxx_battery.c | 458 ++++++++++++++++++++++++++++++++-
 1 file changed, 456 insertions(+), 2 deletions(-)
drivers/power/supply/bq27xxx_battery.c +70 -0
index 7475a5f..41d4ce7 100644
@@ -452,6 +508,81 @@ static struct {
 static DEFINE_MUTEX(bq27xxx_list_lock);
 static LIST_HEAD(bq27xxx_battery_devices);
 
+#define BQ27XXX_DM_SZ                  32
+
+#define BQ27XXX_MSLEEP(i) usleep_range((i)*1000, (i)*1000+500)
+
+struct bq27xxx_dm_reg {
+       u8 subclass_id;
+       u8 offset;
+       u8 bytes;
+       u16 min, max;
+};
+
+struct bq27xxx_dm_buf {
+       u8 class;
+       u8 block;
+       u8 a[BQ27XXX_DM_SZ];
+       bool full, updt;
+};
+
+#define BQ27XXX_DM_BUF(di, i) { \
+       .class = bq27xxx_dm_regs[(di)->chip][i].subclass_id, \
+       .block = bq27xxx_dm_regs[(di)->chip][i].offset / BQ27XXX_DM_SZ, \
+}
+
+static inline u16* bq27xxx_dm_buf_ptr(struct bq27xxx_dm_buf *buf,
+                                     struct bq27xxx_dm_reg *reg) {
+       if (buf->class == reg->subclass_id
+        && buf->block == reg->offset / BQ27XXX_DM_SZ)
+               return (u16*) (buf->a + reg->offset % BQ27XXX_DM_SZ);
+
+       return NULL;
+}
+
+enum bq27xxx_dm_reg_id {
+       BQ27XXX_DM_DESIGN_CAPACITY = 0,
+       BQ27XXX_DM_DESIGN_ENERGY,
+       BQ27XXX_DM_TERMINATE_VOLTAGE,
+};
+
+static const char* bq27xxx_dm_reg_name[] = {
+       [BQ27XXX_DM_DESIGN_CAPACITY] = "design-capacity",
+       [BQ27XXX_DM_DESIGN_ENERGY] = "design-energy",
+       [BQ27XXX_DM_TERMINATE_VOLTAGE] = "terminate-voltage",
+};
+
+static struct bq27xxx_dm_reg 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 */
Quoted 9 lines

drivers/power/supply/bq27xxx_battery.c static struct { · line 583

Quoted 3 lines
drivers/power/supply/bq27xxx_battery.c +3 -0
+       [BQ27425] = 0x04143672,
+//     [BQ27621] = 0x80008000,
+};

Maybe instead of defining dm_regs_* tables for many chips in this file, we should land them in a new header, to let the kernel builder define and/or pick one for his platform by setting a config option, e.g. config_bq27xxx_dm_type=421

Size isn't really an issue, we build drivers for parts we may not have, you should be able to add a part to a system and only change the DT. So supporting all these chips is okay.

Quoted 3 lines

We could also pull in the dm update code if such an option is defined, tho it isn't that much code, ~230 lines plus whitespace.

We can have a config option to enable support for DM update, if it can be factored out in such a way. Otherwise if someone uses it, just include it for everyone, if they need it it's there, if they don't then no harm done.

Quoted 2 lines

Thoughts?

author
#36 lore raw

Re: [PATCH v8 7/9] power: bq27xxx_battery: Add power_supply_battery_info support

Quoted 107 lines · Andrew F. Davis
Quoted 96 lines · Liam Breck

Hi Andrew,

Quoted 93 lines · Liam Breck

From: Liam Breck <[email protected]>

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.

---
 drivers/power/supply/bq27xxx_battery.c | 458 ++++++++++++++++++++++++++++++++-
 1 file changed, 456 insertions(+), 2 deletions(-)
drivers/power/supply/bq27xxx_battery.c +70 -0
index 7475a5f..41d4ce7 100644
@@ -452,6 +508,81 @@ static struct {
 static DEFINE_MUTEX(bq27xxx_list_lock);
 static LIST_HEAD(bq27xxx_battery_devices);
 
+#define BQ27XXX_DM_SZ                  32
+
+#define BQ27XXX_MSLEEP(i) usleep_range((i)*1000, (i)*1000+500)
+
+struct bq27xxx_dm_reg {
+       u8 subclass_id;
+       u8 offset;
+       u8 bytes;
+       u16 min, max;
+};
+
+struct bq27xxx_dm_buf {
+       u8 class;
+       u8 block;
+       u8 a[BQ27XXX_DM_SZ];
+       bool full, updt;
+};
+
+#define BQ27XXX_DM_BUF(di, i) { \
+       .class = bq27xxx_dm_regs[(di)->chip][i].subclass_id, \
+       .block = bq27xxx_dm_regs[(di)->chip][i].offset / BQ27XXX_DM_SZ, \
+}
+
+static inline u16* bq27xxx_dm_buf_ptr(struct bq27xxx_dm_buf *buf,
+                                     struct bq27xxx_dm_reg *reg) {
+       if (buf->class == reg->subclass_id
+        && buf->block == reg->offset / BQ27XXX_DM_SZ)
+               return (u16*) (buf->a + reg->offset % BQ27XXX_DM_SZ);
+
+       return NULL;
+}
+
+enum bq27xxx_dm_reg_id {
+       BQ27XXX_DM_DESIGN_CAPACITY = 0,
+       BQ27XXX_DM_DESIGN_ENERGY,
+       BQ27XXX_DM_TERMINATE_VOLTAGE,
+};
+
+static const char* bq27xxx_dm_reg_name[] = {
+       [BQ27XXX_DM_DESIGN_CAPACITY] = "design-capacity",
+       [BQ27XXX_DM_DESIGN_ENERGY] = "design-energy",
+       [BQ27XXX_DM_TERMINATE_VOLTAGE] = "terminate-voltage",
+};
+
+static struct bq27xxx_dm_reg 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 */
Quoted 9 lines

drivers/power/supply/bq27xxx_battery.c static struct { · line 583

Quoted 3 lines
drivers/power/supply/bq27xxx_battery.c +3 -0
+       [BQ27425] = 0x04143672,
+//     [BQ27621] = 0x80008000,
+};

Maybe instead of defining dm_regs_* tables for many chips in this file, we should land them in a new header, to let the kernel builder define and/or pick one for his platform by setting a config option, e.g. config_bq27xxx_dm_type=421

Quoted 3 lines

Size isn't really an issue, we build drivers for parts we may not have, you should be able to add a part to a system and only change the DT. So supporting all these chips is okay.

OK but suppose kernel builders define a dm_regs{} when it's missing. Doing that in bq27xxx_battery.c isn't ideal.

Quoted 11 lines
Quoted 3 lines

We could also pull in the dm update code if such an option is defined, tho it isn't that much code, ~230 lines plus whitespace.

We can have a config option to enable support for DM update, if it can be factored out in such a way. Otherwise if someone uses it, just include it for everyone, if they need it it's there, if they don't then no harm done.

Quoted 2 lines

Thoughts?

#37 lore raw

Re: [PATCH v8 7/9] power: bq27xxx_battery: Add power_supply_battery_info support

Quoted 108 lines · Liam Breck
Quoted 107 lines · Andrew F. Davis
Quoted 105 lines · Liam Breck

Hi Andrew,

Quoted 86 lines · Liam Breck

From: Liam Breck <[email protected]>

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.

---
 drivers/power/supply/bq27xxx_battery.c | 458 ++++++++++++++++++++++++++++++++-
 1 file changed, 456 insertions(+), 2 deletions(-)
drivers/power/supply/bq27xxx_battery.c +63 -0
index 7475a5f..41d4ce7 100644
@@ -452,6 +508,81 @@ static struct {
 static DEFINE_MUTEX(bq27xxx_list_lock);
 static LIST_HEAD(bq27xxx_battery_devices);
 
+#define BQ27XXX_DM_SZ                  32
+
+#define BQ27XXX_MSLEEP(i) usleep_range((i)*1000, (i)*1000+500)
+
+struct bq27xxx_dm_reg {
+       u8 subclass_id;
+       u8 offset;
+       u8 bytes;
+       u16 min, max;
+};
+
+struct bq27xxx_dm_buf {
+       u8 class;
+       u8 block;
+       u8 a[BQ27XXX_DM_SZ];
+       bool full, updt;
+};
+
+#define BQ27XXX_DM_BUF(di, i) { \
+       .class = bq27xxx_dm_regs[(di)->chip][i].subclass_id, \
+       .block = bq27xxx_dm_regs[(di)->chip][i].offset / BQ27XXX_DM_SZ, \
+}
+
+static inline u16* bq27xxx_dm_buf_ptr(struct bq27xxx_dm_buf *buf,
+                                     struct bq27xxx_dm_reg *reg) {
+       if (buf->class == reg->subclass_id
+        && buf->block == reg->offset / BQ27XXX_DM_SZ)
+               return (u16*) (buf->a + reg->offset % BQ27XXX_DM_SZ);
+
+       return NULL;
+}
+
+enum bq27xxx_dm_reg_id {
+       BQ27XXX_DM_DESIGN_CAPACITY = 0,
+       BQ27XXX_DM_DESIGN_ENERGY,
+       BQ27XXX_DM_TERMINATE_VOLTAGE,
+};
+
+static const char* bq27xxx_dm_reg_name[] = {
+       [BQ27XXX_DM_DESIGN_CAPACITY] = "design-capacity",
+       [BQ27XXX_DM_DESIGN_ENERGY] = "design-energy",
+       [BQ27XXX_DM_TERMINATE_VOLTAGE] = "terminate-voltage",
+};
+
+static struct bq27xxx_dm_reg 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[] = {

drivers/power/supply/bq27xxx_battery.c static struct { · line 583

Quoted 10 lines
drivers/power/supply/bq27xxx_battery.c +10 -0
+       [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,
+};

Maybe instead of defining dm_regs_* tables for many chips in this file, we should land them in a new header, to let the kernel builder define and/or pick one for his platform by setting a config option, e.g. config_bq27xxx_dm_type=421

Quoted 7 lines
Quoted 3 lines

Size isn't really an issue, we build drivers for parts we may not have, you should be able to add a part to a system and only change the DT. So supporting all these chips is okay.

OK but suppose kernel builders define a dm_regs{} when it's missing. Doing that in bq27xxx_battery.c isn't ideal.

Why would it be missing? All this stuff should be left defined always, if it is unused on a particular board that really cant be avoided.

Quoted 11 lines
Quoted 11 lines
Quoted 3 lines

We could also pull in the dm update code if such an option is defined, tho it isn't that much code, ~230 lines plus whitespace.

We can have a config option to enable support for DM update, if it can be factored out in such a way. Otherwise if someone uses it, just include it for everyone, if they need it it's there, if they don't then no harm done.

Quoted 2 lines

Thoughts?

author
#38 lore raw

Re: [PATCH v8 7/9] power: bq27xxx_battery: Add power_supply_battery_info support

Quoted 113 lines · Andrew F. Davis
Quoted 112 lines · Liam Breck
Quoted 110 lines · Andrew F. Davis
Quoted 96 lines · Liam Breck

Hi Andrew,

Quoted 93 lines · Liam Breck

From: Liam Breck <[email protected]>

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.

---
 drivers/power/supply/bq27xxx_battery.c | 458 ++++++++++++++++++++++++++++++++-
 1 file changed, 456 insertions(+), 2 deletions(-)
drivers/power/supply/bq27xxx_battery.c +70 -0
index 7475a5f..41d4ce7 100644
@@ -452,6 +508,81 @@ static struct {
 static DEFINE_MUTEX(bq27xxx_list_lock);
 static LIST_HEAD(bq27xxx_battery_devices);
 
+#define BQ27XXX_DM_SZ                  32
+
+#define BQ27XXX_MSLEEP(i) usleep_range((i)*1000, (i)*1000+500)
+
+struct bq27xxx_dm_reg {
+       u8 subclass_id;
+       u8 offset;
+       u8 bytes;
+       u16 min, max;
+};
+
+struct bq27xxx_dm_buf {
+       u8 class;
+       u8 block;
+       u8 a[BQ27XXX_DM_SZ];
+       bool full, updt;
+};
+
+#define BQ27XXX_DM_BUF(di, i) { \
+       .class = bq27xxx_dm_regs[(di)->chip][i].subclass_id, \
+       .block = bq27xxx_dm_regs[(di)->chip][i].offset / BQ27XXX_DM_SZ, \
+}
+
+static inline u16* bq27xxx_dm_buf_ptr(struct bq27xxx_dm_buf *buf,
+                                     struct bq27xxx_dm_reg *reg) {
+       if (buf->class == reg->subclass_id
+        && buf->block == reg->offset / BQ27XXX_DM_SZ)
+               return (u16*) (buf->a + reg->offset % BQ27XXX_DM_SZ);
+
+       return NULL;
+}
+
+enum bq27xxx_dm_reg_id {
+       BQ27XXX_DM_DESIGN_CAPACITY = 0,
+       BQ27XXX_DM_DESIGN_ENERGY,
+       BQ27XXX_DM_TERMINATE_VOLTAGE,
+};
+
+static const char* bq27xxx_dm_reg_name[] = {
+       [BQ27XXX_DM_DESIGN_CAPACITY] = "design-capacity",
+       [BQ27XXX_DM_DESIGN_ENERGY] = "design-energy",
+       [BQ27XXX_DM_TERMINATE_VOLTAGE] = "terminate-voltage",
+};
+
+static struct bq27xxx_dm_reg 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 */
Quoted 9 lines

drivers/power/supply/bq27xxx_battery.c static struct { · line 583

Quoted 3 lines
drivers/power/supply/bq27xxx_battery.c +3 -0
+       [BQ27425] = 0x04143672,
+//     [BQ27621] = 0x80008000,
+};

Maybe instead of defining dm_regs_* tables for many chips in this file, we should land them in a new header, to let the kernel builder define and/or pick one for his platform by setting a config option, e.g. config_bq27xxx_dm_type=421

Size isn't really an issue, we build drivers for parts we may not have, you should be able to add a part to a system and only change the DT. So supporting all these chips is okay.

Quoted 6 lines
Quoted 3 lines

OK but suppose kernel builders define a dm_regs{} when it's missing. Doing that in bq27xxx_battery.c isn't ideal.

Why would it be missing? All this stuff should be left defined always, if it is unused on a particular board that really cant be avoided.

Are you going to comb thru the manuals for all the supported parts, assemble dm_regs tables and test them? :-)

Quoted 11 lines
Quoted 11 lines
Quoted 11 lines
Quoted 3 lines

We could also pull in the dm update code if such an option is defined, tho it isn't that much code, ~230 lines plus whitespace.

We can have a config option to enable support for DM update, if it can be factored out in such a way. Otherwise if someone uses it, just include it for everyone, if they need it it's there, if they don't then no harm done.

Quoted 2 lines

Thoughts?

#39 lore raw

Re: [PATCH v8 7/9] power: bq27xxx_battery: Add power_supply_battery_info support

Quoted 118 lines · Liam Breck
Quoted 117 lines · Andrew F. Davis
Quoted 115 lines · Liam Breck
Quoted 107 lines · Andrew F. Davis
Quoted 96 lines · Liam Breck

Hi Andrew,

Quoted 93 lines · Liam Breck

From: Liam Breck <[email protected]>

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.

---
 drivers/power/supply/bq27xxx_battery.c | 458 ++++++++++++++++++++++++++++++++-
 1 file changed, 456 insertions(+), 2 deletions(-)
drivers/power/supply/bq27xxx_battery.c +70 -0
index 7475a5f..41d4ce7 100644
@@ -452,6 +508,81 @@ static struct {
 static DEFINE_MUTEX(bq27xxx_list_lock);
 static LIST_HEAD(bq27xxx_battery_devices);
 
+#define BQ27XXX_DM_SZ                  32
+
+#define BQ27XXX_MSLEEP(i) usleep_range((i)*1000, (i)*1000+500)
+
+struct bq27xxx_dm_reg {
+       u8 subclass_id;
+       u8 offset;
+       u8 bytes;
+       u16 min, max;
+};
+
+struct bq27xxx_dm_buf {
+       u8 class;
+       u8 block;
+       u8 a[BQ27XXX_DM_SZ];
+       bool full, updt;
+};
+
+#define BQ27XXX_DM_BUF(di, i) { \
+       .class = bq27xxx_dm_regs[(di)->chip][i].subclass_id, \
+       .block = bq27xxx_dm_regs[(di)->chip][i].offset / BQ27XXX_DM_SZ, \
+}
+
+static inline u16* bq27xxx_dm_buf_ptr(struct bq27xxx_dm_buf *buf,
+                                     struct bq27xxx_dm_reg *reg) {
+       if (buf->class == reg->subclass_id
+        && buf->block == reg->offset / BQ27XXX_DM_SZ)
+               return (u16*) (buf->a + reg->offset % BQ27XXX_DM_SZ);
+
+       return NULL;
+}
+
+enum bq27xxx_dm_reg_id {
+       BQ27XXX_DM_DESIGN_CAPACITY = 0,
+       BQ27XXX_DM_DESIGN_ENERGY,
+       BQ27XXX_DM_TERMINATE_VOLTAGE,
+};
+
+static const char* bq27xxx_dm_reg_name[] = {
+       [BQ27XXX_DM_DESIGN_CAPACITY] = "design-capacity",
+       [BQ27XXX_DM_DESIGN_ENERGY] = "design-energy",
+       [BQ27XXX_DM_TERMINATE_VOLTAGE] = "terminate-voltage",
+};
+
+static struct bq27xxx_dm_reg 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 */
Quoted 9 lines

drivers/power/supply/bq27xxx_battery.c static struct { · line 583

Quoted 3 lines
drivers/power/supply/bq27xxx_battery.c +3 -0
+       [BQ27425] = 0x04143672,
+//     [BQ27621] = 0x80008000,
+};

Maybe instead of defining dm_regs_* tables for many chips in this file, we should land them in a new header, to let the kernel builder define and/or pick one for his platform by setting a config option, e.g. config_bq27xxx_dm_type=421

Quoted 3 lines

Size isn't really an issue, we build drivers for parts we may not have, you should be able to add a part to a system and only change the DT. So supporting all these chips is okay.

OK but suppose kernel builders define a dm_regs{} when it's missing. Doing that in bq27xxx_battery.c isn't ideal.

Quoted 6 lines
Quoted 2 lines

Why would it be missing? All this stuff should be left defined always, if it is unused on a particular board that really cant be avoided.

Are you going to comb thru the manuals for all the supported parts, assemble dm_regs tables and test them? :-)

If I was adding a device, then yes, I would do it fully, even if it was a bit more of a pain at first :)

Quoted 11 lines
Quoted 11 lines
Quoted 11 lines
Quoted 11 lines
Quoted 3 lines

We could also pull in the dm update code if such an option is defined, tho it isn't that much code, ~230 lines plus whitespace.

We can have a config option to enable support for DM update, if it can be factored out in such a way. Otherwise if someone uses it, just include it for everyone, if they need it it's there, if they don't then no harm done.

Quoted 2 lines

Thoughts?

author
#40 lore raw

Re: [PATCH v8 7/9] power: bq27xxx_battery: Add power_supply_battery_info support

Quoted 122 lines · Andrew F. Davis
Quoted 121 lines · Liam Breck
Quoted 119 lines · Andrew F. Davis
Quoted 108 lines · Liam Breck
Quoted 107 lines · Andrew F. Davis
Quoted 105 lines · Liam Breck

Hi Andrew,

Quoted 86 lines · Liam Breck

From: Liam Breck <[email protected]>

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.

---
 drivers/power/supply/bq27xxx_battery.c | 458 ++++++++++++++++++++++++++++++++-
 1 file changed, 456 insertions(+), 2 deletions(-)
drivers/power/supply/bq27xxx_battery.c +63 -0
index 7475a5f..41d4ce7 100644
@@ -452,6 +508,81 @@ static struct {
 static DEFINE_MUTEX(bq27xxx_list_lock);
 static LIST_HEAD(bq27xxx_battery_devices);
 
+#define BQ27XXX_DM_SZ                  32
+
+#define BQ27XXX_MSLEEP(i) usleep_range((i)*1000, (i)*1000+500)
+
+struct bq27xxx_dm_reg {
+       u8 subclass_id;
+       u8 offset;
+       u8 bytes;
+       u16 min, max;
+};
+
+struct bq27xxx_dm_buf {
+       u8 class;
+       u8 block;
+       u8 a[BQ27XXX_DM_SZ];
+       bool full, updt;
+};
+
+#define BQ27XXX_DM_BUF(di, i) { \
+       .class = bq27xxx_dm_regs[(di)->chip][i].subclass_id, \
+       .block = bq27xxx_dm_regs[(di)->chip][i].offset / BQ27XXX_DM_SZ, \
+}
+
+static inline u16* bq27xxx_dm_buf_ptr(struct bq27xxx_dm_buf *buf,
+                                     struct bq27xxx_dm_reg *reg) {
+       if (buf->class == reg->subclass_id
+        && buf->block == reg->offset / BQ27XXX_DM_SZ)
+               return (u16*) (buf->a + reg->offset % BQ27XXX_DM_SZ);
+
+       return NULL;
+}
+
+enum bq27xxx_dm_reg_id {
+       BQ27XXX_DM_DESIGN_CAPACITY = 0,
+       BQ27XXX_DM_DESIGN_ENERGY,
+       BQ27XXX_DM_TERMINATE_VOLTAGE,
+};
+
+static const char* bq27xxx_dm_reg_name[] = {
+       [BQ27XXX_DM_DESIGN_CAPACITY] = "design-capacity",
+       [BQ27XXX_DM_DESIGN_ENERGY] = "design-energy",
+       [BQ27XXX_DM_TERMINATE_VOLTAGE] = "terminate-voltage",
+};
+
+static struct bq27xxx_dm_reg 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[] = {

drivers/power/supply/bq27xxx_battery.c static struct { · line 583

Quoted 10 lines
drivers/power/supply/bq27xxx_battery.c +10 -0
+       [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,
+};

Maybe instead of defining dm_regs_* tables for many chips in this file, we should land them in a new header, to let the kernel builder define and/or pick one for his platform by setting a config option, e.g. config_bq27xxx_dm_type=421

Quoted 7 lines
Quoted 3 lines

Size isn't really an issue, we build drivers for parts we may not have, you should be able to add a part to a system and only change the DT. So supporting all these chips is okay.

OK but suppose kernel builders define a dm_regs{} when it's missing. Doing that in bq27xxx_battery.c isn't ideal.

Why would it be missing? All this stuff should be left defined always, if it is unused on a particular board that really cant be avoided.

Quoted 6 lines
Quoted 3 lines

Are you going to comb thru the manuals for all the supported parts, assemble dm_regs tables and test them? :-)

If I was adding a device, then yes, I would do it fully, even if it was a bit more of a pain at first :)

By "device" you mean chip? We do not enable new chips in this patchset.

In v9, I support uniquely-defined chip IDs (500, 545, 421), which I need you to test. As there's 13 more chips to test, I think we should limit this patchset to those 3.

Quoted 11 lines
Quoted 11 lines
Quoted 11 lines
Quoted 11 lines
Quoted 11 lines
Quoted 3 lines

We could also pull in the dm update code if such an option is defined, tho it isn't that much code, ~230 lines plus whitespace.

We can have a config option to enable support for DM update, if it can be factored out in such a way. Otherwise if someone uses it, just include it for everyone, if they need it it's there, if they don't then no harm done.

Quoted 2 lines

Thoughts?

author
#41 lore raw

PATCH v8 8/9 power: bq27xxx_battery: Add print_dm_blocks() to log chip memory

In reply to Liam Breck

From: Liam Breck <[email protected]>

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

---
 drivers/power/supply/bq27xxx_battery.c | 70 ++++++++++++++++++++++++++++++++++
 1 file changed, 70 insertions(+)
drivers/power/supply/bq27xxx_battery.c +70 -0
index 41d4ce7..8ba0568 100644
@@ -703,6 +703,74 @@ 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_DM_INFO(f,o,v) dev_info(di->dev, "offset %d, " f "\n", o, v)
+
+static void bq27xxx_battery_print_dm_blocks(struct bq27xxx_device_info *di) {
+	enum { H1, H2, H4, I1, 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} },
+		},
+		c425_s104[][10] = {
+			{ { 0,I2},{ 2,I1},{ 3,I1},{ 4,I1},{99,0} },
+		},
+		c425_s105[][10] = {
+			{ { 0,F4},{ 4,I2},{99,0} },
+		};
+		/* add more subclass maps here */
+
+	struct dm_class { int id, blocks; struct dm_reg (*reg)[10]; }
+		c425[] = {
+			{ .id =  82, .blocks = 2, .reg = c425_s82  },
+			{ .id = 104, .blocks = 1, .reg = c425_s104 },
+			{ .id = 105, .blocks = 1, .reg = c425_s105 },
+			{ .reg = NULL }
+		};
+		/* 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; chip[c].reg != NULL; ++c) {
+		buf.class = chip[c].id;
+		dev_info(di->dev, "subclass %d registers...\n", chip[c].id);
+
+		for (b=0; b < chip[c].blocks; ++b) {
+			buf.block = b;
+			if (bq27xxx_battery_read_dm_block(di, &buf) < 0)
+				continue;
+
+			for (r=0; chip[c].reg[b][r].offset != 99; ++r) {
+				int o = chip[c].reg[b][r].offset;
+				u8* p = &buf.a[o % BQ27XXX_DM_SZ];
+
+				switch (chip[c].reg[b][r].type) {
+				case H1: BQ27XXX_DM_INFO("%02x", o,   *p); break;
+				case I1: BQ27XXX_DM_INFO("%d", o, (s8)*p); break;
+				case U1: BQ27XXX_DM_INFO("%u", o,     *p); break;
+				case H2: BQ27XXX_DM_INFO("%04x", o,
+						   be16_to_cpup((u16*)p)); break;
+				case I2: BQ27XXX_DM_INFO("%d", o,
+					      (s16)be16_to_cpup((u16*)p)); break;
+				case H4:
+				case F4: BQ27XXX_DM_INFO("%08x", o,
+						   be32_to_cpup((u32*)p)); break;
+				}
+			}
+		}
+	}
+}
+
 static void bq27xxx_battery_update_dm_block(struct bq27xxx_device_info *di,
 					    struct bq27xxx_dm_buf *buf,
 					    enum bq27xxx_dm_reg_id reg_id,
@@ -936,6 +1004,8 @@ void bq27xxx_battery_settings(struct bq27xxx_device_info *di)
 	bq27xxx_battery_set_config(di, &info);
 
 out:
+	/* bq27xxx_battery_print_dm_blocks(di); uncomment for debugging */
+	(void)bq27xxx_battery_print_dm_blocks; /* prevent compiler warning */
 	bq27xxx_battery_set_seal_state(di, true);
 }
 
Signature · 1 line
2.9.3
#42 lore raw

Re: [PATCH v8 8/9] power: bq27xxx_battery: Add print_dm_blocks() to log chip memory

Quoted 90 lines · Liam Breck

From: Liam Breck <[email protected]>

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

---
 drivers/power/supply/bq27xxx_battery.c | 70 ++++++++++++++++++++++++++++++++++
 1 file changed, 70 insertions(+)
drivers/power/supply/bq27xxx_battery.c +68 -0
index 41d4ce7..8ba0568 100644
@@ -703,6 +703,74 @@ 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_DM_INFO(f,o,v) dev_info(di->dev, "offset %d, " f "\n", o, v)
+
+static void bq27xxx_battery_print_dm_blocks(struct bq27xxx_device_info *di) {
+	enum { H1, H2, H4, I1, 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} },
+		},
+		c425_s104[][10] = {
+			{ { 0,I2},{ 2,I1},{ 3,I1},{ 4,I1},{99,0} },
+		},
+		c425_s105[][10] = {
+			{ { 0,F4},{ 4,I2},{99,0} },
+		};
+		/* add more subclass maps here */
+
+	struct dm_class { int id, blocks; struct dm_reg (*reg)[10]; }
+		c425[] = {
+			{ .id =  82, .blocks = 2, .reg = c425_s82  },
+			{ .id = 104, .blocks = 1, .reg = c425_s104 },
+			{ .id = 105, .blocks = 1, .reg = c425_s105 },
+			{ .reg = NULL }
+		};
+		/* 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; chip[c].reg != NULL; ++c) {
+		buf.class = chip[c].id;
+		dev_info(di->dev, "subclass %d registers...\n", chip[c].id);
+
+		for (b=0; b < chip[c].blocks; ++b) {
+			buf.block = b;
+			if (bq27xxx_battery_read_dm_block(di, &buf) < 0)
+				continue;
+
+			for (r=0; chip[c].reg[b][r].offset != 99; ++r) {
+				int o = chip[c].reg[b][r].offset;
+				u8* p = &buf.a[o % BQ27XXX_DM_SZ];
+
+				switch (chip[c].reg[b][r].type) {
+				case H1: BQ27XXX_DM_INFO("%02x", o,   *p); break;
+				case I1: BQ27XXX_DM_INFO("%d", o, (s8)*p); break;
+				case U1: BQ27XXX_DM_INFO("%u", o,     *p); break;
+				case H2: BQ27XXX_DM_INFO("%04x", o,
+						   be16_to_cpup((u16*)p)); break;
+				case I2: BQ27XXX_DM_INFO("%d", o,
+					      (s16)be16_to_cpup((u16*)p)); break;
+				case H4:
+				case F4: BQ27XXX_DM_INFO("%08x", o,
+						   be32_to_cpup((u32*)p)); break;
+				}
+			}
+		}
+	}
+}
+
 static void bq27xxx_battery_update_dm_block(struct bq27xxx_device_info *di,
 					    struct bq27xxx_dm_buf *buf,
 					    enum bq27xxx_dm_reg_id reg_id,

drivers/power/supply/bq27xxx_battery.c void bq27xxx_battery_settings(struct bq27xxx_device_info *di) · line 1008

Quoted 5 lines
drivers/power/supply/bq27xxx_battery.c +2 -0
@@ -936,6 +1004,8 @@ void bq27xxx_battery_settings(struct bq27xxx_device_info *di)
 	bq27xxx_battery_set_config(di, &info);
 
 out:
+	/* bq27xxx_battery_print_dm_blocks(di); uncomment for debugging */
+	(void)bq27xxx_battery_print_dm_blocks; /* prevent compiler warning */

__maybe_unused

Quoted 4 lines
drivers/power/supply/bq27xxx_battery.c +0 -0
 	bq27xxx_battery_set_seal_state(di, true);
 }
 
author
#43 lore raw

PATCH v8 9/9 power: bq27xxx_battery_i2c: Add I2C bulk read/write functions

In reply to Liam Breck

From: Matt Ranostay <[email protected]>

write(), read_bulk(), write_bulk() are required by bq27xxx_battery devicetree code.

---
 drivers/power/supply/bq27xxx_battery_i2c.c | 82 +++++++++++++++++++++++++++++-
 1 file changed, 81 insertions(+), 1 deletion(-)
drivers/power/supply/bq27xxx_battery_i2c.c +81 -1
index d7da535..0414573 100644
@@ -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)
Signature · 1 line
2.9.3