devicetree battery support and client bq27xxx_battery
Subsystems (4)
v7 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 v7:
bq27xxx_battery:
* support chips where terminate_voltage & design_* live in separate blocks * draft support for 421, 441, 621 chips * new patch to log chip memory fields * report bus I/O errors; return error code in bq27xxx_battery_i2c * verify checksum in read_dm_block() * use set_cfgupdate only if chip provides it, soft_reset on I/O error * block_data_control=0 only in write_dm_block() * note toxic code from TI bqtool in write_dm_block() * lots of functionally neutral polishing
Documentation/devicetree/.../battery.txt:
* mention power_supply_get_battery_info()
Changes in v6:
* Documentation/devictree/... fixes
* bq27xxx_battery: clarify names
* bq27xxx_battery: verify that selected registers are supported
* bq27xxx_battery: allocate NVM buffer on stack
* bq27xxx_battery_i2c: fix return code of bulk_read
Changes in v5:
* incorporate feedback into Documentation/devicetree/.../battery.txt
* use power_supply_prop_* names in devicetree and power_supply_battery_info
* default fields to -EINVAL in power_supply_battery_info
* power_supply_get_battery_info() always looks for "monitored-battery"
* power_supply_get_battery_info() emits a warning if !psy->of_node
* squash patches for power_supply_battery_info
* bq27xxx_battery: check power_supply_battery_info values
* bq27xxx_battery: note missing power_supply_prop_* features
* bq27xxx_battery: new patch for access methods
Changes in v4:
* add "fixed-battery" compatible field to be be more consistant with devicetree
Changes in v3:
* split i2c changes into respective patches
* add documentation for battery information for fuel gauge
* rebased documentation patches on change on the list
* abstracted the battery configuration for the state machine to an generic struct and platform data access function
Changes in v2:
* add documentation for uWh and uAh property units
* change devicetree entries to match new property units
Liam (5):
devicetree: power: Add battery.txt
devicetree: power: bq27xxx: Add monitored-battery documentation
power: power_supply: Add power_supply_battery_info and API
power: bq27xxx_battery: Add power_supply_battery_info support
power: bq27xxx_battery: Add print_dm() to log chip memory
Matt Ranostay (4):
devicetree: property-units: Add uWh and uAh units
power: bq27xxx_battery: Define access methods to write chip registers
power: bq27xxx_battery: Add BQ27425 chip id
power: bq27xxx_battery_i2c: Add I2C bulk read/write functions
.../devicetree/bindings/power/supply/battery.txt | 42 ++ .../devicetree/bindings/power/supply/bq27xxx.txt | 11 +- .../devicetree/bindings/property-units.txt | 2 + drivers/power/supply/bq27xxx_battery.c | 473 ++++++++++++++++++++- drivers/power/supply/bq27xxx_battery_i2c.c | 84 +++- drivers/power/supply/power_supply_core.c | 40 ++ include/linux/power/bq27xxx_battery.h | 6 +- include/linux/power_supply.h | 18 + 8 files changed, 669 insertions(+), 7 deletions(-) create mode 100644 Documentation/devicetree/bindings/power/supply/battery.txt
Signature · 1 line
2.9.3
A message that is not in the archive (sent off-list or not yet received): 20170221213008.30044-1-liam-RYWXG+zxWwBdeoIcmNTgJF6hYfS7NtTn@public.gmane.org
PATCH v7 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
@@ -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): 20170221213008.30044-2-liam-RYWXG+zxWwBdeoIcmNTgJF6hYfS7NtTn@public.gmane.org
Re: [PATCH v7 1/9] devicetree: power: Add battery.txt
Quoted 42 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.
Cc Rob Herring <[email protected]>Signed-off-by Matt Ranostay <[email protected]>Signed-off-by Liam Breck <[email protected]>--- .../devicetree/bindings/power/supply/battery.txt | 42 ++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 Documentation/devicetree/bindings/power/supply/battery.txtDocumentation/devicetree/bindings/power/supply/battery.txt new file +21
-0@@ -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
Documentation/devicetree/bindings/power/supply/battery.txt line 31
Quoted 10 lines
Documentation/devicetree/bindings/power/supply/battery.txt new file +10
-0+ +Example: + + bat: battery { + compatible = "fixed-battery"; + voltage-min-design-microvolt = <3200000>; + energy-full-design-microwatt-hours = <5290000>; + charge-full-design-microamp-hours = <1430000>; + }; +
Still not sure what a non-fixed-battery would be. I guess it would be a battery with an integrated fuel-gauge pre-programmed with this design info. So then the charger node would want to point to a fuel-gauge node, and the fuel-gauge can optionally point to a fixed-battery if that info is not pre-programmable.
The issues then is for chargers with an integrated fuel-gauge, or systems without a fuel-gauge, that may get a bit messy, so I have no issues with this current binding.
Documentation/devicetree/bindings/power/supply/battery.txt line 42
Quoted 12 lines
Documentation/devicetree/bindings/power/supply/battery.txt new file +11
-0+ charger: charger@11 { + .... + monitored-battery = <&bat>; + ... + }; + + fuel_gauge: fuel-gauge@22 { + .... + monitored-battery = <&bat>; + ... + };
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to [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): bd747560-8780-3294-86dd-f7f7f4de1736-l0cyMroinI0@public.gmane.org
Re: [PATCH v7 1/9] devicetree: power: Add battery.txt
Quoted 51 lines · Andrew F. Davis
Quoted 50 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.
Cc Rob Herring <[email protected]>Signed-off-by Matt Ranostay <[email protected]>Signed-off-by Liam Breck <[email protected]>--- .../devicetree/bindings/power/supply/battery.txt | 42 ++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 Documentation/devicetree/bindings/power/supply/battery.txtDocumentation/devicetree/bindings/power/supply/battery.txt new file +29
-0@@ -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>;
Quoted 8 lines
Documentation/devicetree/bindings/power/supply/battery.txt line 31
Quoted 2 lines
Documentation/devicetree/bindings/power/supply/battery.txt new file +2
-0+ }; +Still not sure what a non-fixed-battery would be. I guess it would be a battery with an integrated fuel-gauge pre-programmed with this design info. So then the charger node would want to point to a fuel-gauge node, and the fuel-gauge can optionally point to a fixed-battery if that info is not pre-programmable.
Sebastian wondered about a chain from charger thru gauge to battery, but there are battery characteristics the gauge won't know about, e.g. pre-charge and post-charge current, which is usually derived from battery capacity.
Quoted 16 lines
The issues then is for chargers with an integrated fuel-gauge, or systems without a fuel-gauge, that may get a bit messy, so I have no issues with this current binding.
Quoted 12 lines
Documentation/devicetree/bindings/power/supply/battery.txt new file +11
-0+ charger: charger@11 { + .... + monitored-battery = <&bat>; + ... + }; + + fuel_gauge: fuel-gauge@22 { + .... + monitored-battery = <&bat>; + ... + };
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
PATCH v7 3/9 devicetree: power: bq27xxx: Add monitored-battery documentation
In reply to a message not in the archiveFrom: 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
@@ -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
PATCH v7 2/9 devicetree: property-units: Add uWh and uAh units
In reply to Liam BreckFrom: 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
@@ -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 · 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
PATCH v7 4/9 power: power_supply: Add power_supply_battery_info and API
In reply to Liam BreckFrom: 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
@@ -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
@@ -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
PATCH v7 5/9 power: bq27xxx_battery: Define access methods to write chip registers
In reply to Liam BreckFrom: 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
@@ -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
PATCH v7 6/9 power: bq27xxx_battery: Add BQ27425 chip id
In reply to Liam BreckFrom: 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
@@ -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
@@ -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
@@ -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
Re: [PATCH v7 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.
Signed-off-by Matt Ranostay <[email protected]>Signed-off-by Liam Breck <[email protected]>
Looks inline with the datasheet,
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@@ -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@@ -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@@ -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 */ };/**
PATCH v7 7/9 power: bq27xxx_battery: Add power_supply_battery_info support
In reply to Liam BreckFrom: 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 | 399 ++++++++++++++++++++++++++++++++- 1 file changed, 397 insertions(+), 2 deletions(-)
drivers/power/supply/bq27xxx_battery.c +397 -2
@@ -51,7 +51,7 @@#include <linux/power/bq27xxx_battery.h> -#define DRIVER_VERSION "1.2.0" +#define DRIVER_VERSION "1.3.0"#define BQ27XXX_MANUFACTURER "Texas Instruments"
@@ -452,6 +452,99 @@ static struct {static DEFINE_MUTEX (bq27xxx_list_lock);static LIST_HEAD (bq27xxx_battery_devices); +/* writable registers */ +#define BQ27XXX_CONTROL 0x00 +#define BQ27XXX_DATA_CLASS 0x3E +#define BQ27XXX_DATA_BLOCK 0x3F +#define BQ27XXX_BLOCK_DATA 0x40 +#define BQ27XXX_BLOCK_DATA_CHECKSUM 0x60 +#define BQ27XXX_BLOCK_DATA_CONTROL 0x61 + +/* control register params */ +#define BQ27XXX_SEALED 0x20 +#define BQ27XXX_SET_CFGUPDATE 0x13 +#define BQ27XXX_SOFT_RESET 0x42 + +#define BQ27XXX_MSLEEP (i) usleep_range((i)*1000, (i)*1000+500) + +struct bq27xxx_dm_reg { +u8 subclass_id; +u8 offset; +u8 bytes; +u16 min, max; +}; + +struct bq27xxx_dm_buf { +u8 class; +u8 block; +u8 a[32 ]; +bool full, updt; +}; + +#define BQ27XXX_DM_BUF_PTR (b, r) \ + ( (u16*) &(b).a[ (r)->offset % sizeof (b).a ] ) + +#define BQ27XXX_DM_BUF (di, i) { \ + .class = bq27xxx_dm_regs[(di)->chip][i].subclass_id, \ + .block = bq27xxx_dm_regs[(di)->chip][i].offset / sizeof ((struct bq27xxx_dm_buf*)0)->a, \ +} + +static inline bool bq27xxx_dm_buf_set (struct bq27xxx_dm_buf *buf, +struct bq27xxx_dm_reg *reg) { +if (buf->class == reg->subclass_id + && buf->block == reg->offset /sizeof buf->a + && buf->full) +return false; + buf->class = reg->subclass_id; + buf->block = reg->offset /sizeof buf->a; + buf->full = buf->updt = false; +return true; +} + +enum bq27xxx_dm_reg_id { +BQ27XXX_DM_DESIGN_CAPACITY =0 , +BQ27XXX_DM_DESIGN_ENERGY , +BQ27XXX_DM_TERMINATE_VOLTAGE , +BQ27XXX_DM_END , +}; + +static const char * bq27xxx_dm_reg_name[] = { + [BQ27XXX_DM_DESIGN_CAPACITY ] ="design-capacity" , + [BQ27XXX_DM_DESIGN_ENERGY ] ="design-energy" , + [BQ27XXX_DM_TERMINATE_VOLTAGE ] ="terminate-voltage" , +}; + +static struct bq27xxx_dm_reg bq27425_dm_regs[] = { + [BQ27XXX_DM_DESIGN_CAPACITY ] = {82 ,12 ,2 ,0 ,32767 }, + [BQ27XXX_DM_DESIGN_ENERGY ] = {82 ,14 ,2 ,0 ,32767 }, + [BQ27XXX_DM_TERMINATE_VOLTAGE ] = {82 ,18 ,2 ,2800 ,3700 }, +}; + +static struct bq27xxx_dm_reg bq27421_dm_regs[] = {/* not tested */ + [BQ27XXX_DM_DESIGN_CAPACITY ] = {82 ,10 ,2 ,0 ,8000 }, + [BQ27XXX_DM_DESIGN_ENERGY ] = {82 ,12 ,2 ,0 ,32767 }, + [BQ27XXX_DM_TERMINATE_VOLTAGE ] = {82 ,16 ,2 ,2500 ,3700 }, +}; + +static struct bq27xxx_dm_reg bq27621_dm_regs[] = {/* not tested */ + [BQ27XXX_DM_DESIGN_CAPACITY ] = {82 ,3 ,2 ,0 ,8000 }, + [BQ27XXX_DM_DESIGN_ENERGY ] = {82 ,5 ,2 ,0 ,32767 }, + [BQ27XXX_DM_TERMINATE_VOLTAGE ] = {82 ,9 ,2 ,2500 ,3700 }, +}; + +static struct bq27xxx_dm_reg *bq27xxx_dm_regs[] = { + [BQ27421 ] = bq27421_dm_regs,/* and BQ27441 */ + [BQ27425 ] = bq27425_dm_regs, +/* [BQ27621] = */ bq27621_dm_regs, +}; + +static u32 bq27xxx_unseal_keys[] = { + [BQ27421 ] =0x80008000 ,/* and BQ27441 */ + [BQ27425 ] =0x04143672 , +/* [BQ27621] = */ 0x80008000 , +}; + +static int poll_interval_param_set (const char *val,const struct kernel_param *kp) {struct bq27xxx_device_info *di;
@@ -496,6 +589,297 @@ static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index,return di->bus.read (di, di->regs[reg_index], single); } +static int bq27xxx_battery_set_seal_state (struct bq27xxx_device_info *di, +bool state) +{ +u32 key = bq27xxx_unseal_keys[di->chip]; +int ret; + +if (state) { + ret = di->bus.write (di,BQ27XXX_CONTROL ,BQ27XXX_SEALED , false); +if (ret <0 ) + goto out; + }else { + ret = di->bus.write (di,BQ27XXX_CONTROL , (u16)(key >>16 ), false); +if (ret <0 ) + goto out; + + ret = di->bus.write (di,BQ27XXX_CONTROL , (u16 )key, false); +if (ret <0 ) + goto out; + } +return 0 ; + +out: +dev_err (di->dev,"bus error in %s: %d\n" , __func__, ret); +return ret; +} + +static u8 bq27xxx_battery_checksum (struct bq27xxx_dm_buf *buf) +{ +u16 sum =0 ; +int i; + +for (i =0 ; i <sizeof buf->a; i++) + sum += buf->a[i]; + sum &=0xff ; + +return 0xff - sum; +} + +static int bq27xxx_battery_read_dm_block (struct bq27xxx_device_info *di, +struct bq27xxx_dm_buf *buf) +{ +int ret; + + ret = di->bus.write (di,BQ27XXX_DATA_CLASS , buf->class, true); +if (ret <0 ) + goto out; + + ret = di->bus.write (di,BQ27XXX_DATA_BLOCK , buf->block, true); +if (ret <0 ) + goto out; + +BQ27XXX_MSLEEP (1 ); + + ret = di->bus.read_bulk (di,BQ27XXX_BLOCK_DATA , buf->a,sizeof buf->a); +if (ret <0 ) + goto out; + + ret = di->bus.read (di,BQ27XXX_BLOCK_DATA_CHECKSUM , true); +if (ret <0 ) + goto out; + +if ((u8 )ret !=bq27xxx_battery_checksum (buf)) { + ret = -EINVAL ; + goto out; + } + + buf->full = true; + buf->updt = false; +return 0 ; + +out: +dev_err (di->dev,"bus error in %s: %d\n" , __func__, ret); +return ret; +} + +static void bq27xxx_battery_print_config (struct bq27xxx_device_info *di) +{ +struct bq27xxx_dm_reg *reg = bq27xxx_dm_regs[di->chip]; +struct bq27xxx_dm_buf buf = { .class =0xFF }; +int i; + +for (i =0 ; i <BQ27XXX_DM_END ; i++, reg++) { +const char * str = bq27xxx_dm_reg_name[i]; + +if (bq27xxx_dm_buf_set (&buf, reg)) +if (bq27xxx_battery_read_dm_block (di, &buf) <0 ) +continue ; + +if (reg->bytes ==2 ) +dev_info (di->dev,"config register %s is %d\n" , str, +be16_to_cpup (BQ27XXX_DM_BUF_PTR (buf, reg))); +else +dev_warn (di->dev,"unsupported config register %s\n" , str); + } +} + +static void bq27xxx_battery_update_dm_block (struct bq27xxx_device_info *di, +struct bq27xxx_dm_buf *buf, +enum bq27xxx_dm_reg_id reg_id, +unsigned int val) +{ +struct bq27xxx_dm_reg *reg = &bq27xxx_dm_regs[di->chip][reg_id]; +u16 *prev; + +if (reg->bytes !=2 ) +return ; + + prev =BQ27XXX_DM_BUF_PTR (*buf, reg); +if (be16_to_cpup (prev) == val) +return ; + *prev =cpu_to_be16 (val); + +dev_info (di->dev,"update chip %d, class %u, block %u, offset %u, value %u\n" , + di->chip, buf->class, buf->block, reg->offset, val); + + buf->updt = true; +return ; +} + +static int bq27xxx_battery_write_dm_block (struct bq27xxx_device_info *di, +struct bq27xxx_dm_buf *buf) +{ +bool cfgup = di->chip ==BQ27425 || di->chip ==BQ27421 ;/* || BQ27441 || BQ27621 */ +int ret; + +if (cfgup) { + ret = di->bus.write (di,BQ27XXX_CONTROL ,BQ27XXX_SET_CFGUPDATE , false); +if (ret <0 ) + goto out1; + } + + ret = di->bus.write (di,BQ27XXX_BLOCK_DATA_CONTROL ,0 , true); +if (ret <0 ) + goto out2; + + ret = di->bus.write (di,BQ27XXX_DATA_CLASS , buf->class, true); +if (ret <0 ) + goto out2; + + ret = di->bus.write (di,BQ27XXX_DATA_BLOCK , buf->block, true); +if (ret <0 ) + goto out2; + +BQ27XXX_MSLEEP (1 ); + + ret = di->bus.write_bulk (di,BQ27XXX_BLOCK_DATA , buf->a,sizeof buf->a); +if (ret <0 ) + goto out2; + + ret = di->bus.write (di,BQ27XXX_BLOCK_DATA_CHECKSUM , +bq27xxx_battery_checksum (buf), true); +if (ret <0 ) + goto out2; + +/* THE FOLLOWING CODE IS TOXIC. DO NOT USE! +* If the 350ms delay is insufficient, NVM corruption results +* on the '425 chip, which could damage the chip. +* It was suggested in this TI tool: +* http://git.ti.com/bms-linux/bqtool/blobs/master/gauge.c#line328 +* +* BQ27XXX_MSLEEP(350); +* ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, buf->block, true); +* BQ27XXX_MSLEEP(1); +* ret = di->bus.read(di, BQ27XXX_BLOCK_DATA_CHECKSUM, true); +* if ((u8)ret != bq27xxx_battery_checksum(buf)) +* ret = -EINVAL; +*/ + +if (cfgup) { +BQ27XXX_MSLEEP (1 ); + ret = di->bus.write (di,BQ27XXX_CONTROL ,BQ27XXX_SOFT_RESET , false); +if (ret <0 ) + goto out1; + } + + buf->updt = false; +return 0 ; + +out2: +if (cfgup) + di->bus.write (di,BQ27XXX_CONTROL ,BQ27XXX_SOFT_RESET , false); +out1: +dev_err (di->dev,"bus error in %s: %d\n" , __func__, ret); +return ret; +} + +static void bq27xxx_battery_set_config (struct bq27xxx_device_info *di, +struct power_supply_battery_info *info) +{ +struct bq27xxx_dm_buf bd =BQ27XXX_DM_BUF (di,BQ27XXX_DM_DESIGN_ENERGY ); +struct bq27xxx_dm_buf bt =BQ27XXX_DM_BUF (di,BQ27XXX_DM_TERMINATE_VOLTAGE ); + +if (info->charge_full_design_uah != -EINVAL + && info->energy_full_design_uwh != -EINVAL ) { +bq27xxx_battery_read_dm_block (di, &bd); +if (bd.full) { +/* assume design energy & capacity are in same block */ +bq27xxx_battery_update_dm_block (di, &bd, +BQ27XXX_DM_DESIGN_CAPACITY , + info->charge_full_design_uah /1000 ); +bq27xxx_battery_update_dm_block (di, &bd, +BQ27XXX_DM_DESIGN_ENERGY , + info->energy_full_design_uwh /1000 ); + } + } + +if (info->voltage_min_design_uv != -EINVAL ) { +bool same = bd.full && bd.class == bt.class && bd.block == bt.block; +if (!same) +bq27xxx_battery_read_dm_block (di, &bt); +if (same ? bd.full : bt.full) +bq27xxx_battery_update_dm_block (di, same ? &bd : &bt, +BQ27XXX_DM_TERMINATE_VOLTAGE , + info->voltage_min_design_uv /1000 ); + } + +if (bd.updt) +bq27xxx_battery_write_dm_block (di, &bd); +if (bt.updt) +bq27xxx_battery_write_dm_block (di, &bt); +} + +void bq27xxx_battery_settings (struct bq27xxx_device_info *di) +{ +struct power_supply_battery_info info = {}; +unsigned int min, max; + +/* functions don't exist for writing data so abort */ +if (!di->bus.write || !di->bus.write_bulk) +return ; + +/* no settings to be set for this chipset so abort */ +if (!bq27xxx_dm_regs[di->chip]) +return ; + +if (bq27xxx_battery_set_seal_state (di, false) <0 ) +return ; + +if (power_supply_get_battery_info (di->bat, &info) <0 ) + goto out; + +if (info.energy_full_design_uwh != info.charge_full_design_uah) { +if (info.energy_full_design_uwh == -EINVAL ) +dev_warn (di->dev, +"missing battery:energy-full-design-microwatt-hours\n" ); +else if (info.charge_full_design_uah == -EINVAL ) +dev_warn (di->dev, +"missing battery:charge-full-design-microamp-hours\n" ); + } + +/* assume min == 0 */ + max = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_DESIGN_ENERGY ].max; +if (info.energy_full_design_uwh > max *1000 ) { +dev_err (di->dev, +"invalid battery:energy-full-design-microwatt-hours %d\n" , + info.energy_full_design_uwh); + info.energy_full_design_uwh = -EINVAL ; + } + +/* assume min == 0 */ + max = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_DESIGN_CAPACITY ].max; +if (info.charge_full_design_uah > max *1000 ) { +dev_err (di->dev, +"invalid battery:charge-full-design-microamp-hours %d\n" , + info.charge_full_design_uah); + info.charge_full_design_uah = -EINVAL ; + } + + min = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_TERMINATE_VOLTAGE ].min; + max = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_TERMINATE_VOLTAGE ].max; +if ((info.voltage_min_design_uv < min *1000 + || info.voltage_min_design_uv > max *1000 ) + && info.voltage_min_design_uv != -EINVAL ) { +dev_err (di->dev, +"invalid battery:voltage-min-design-microvolt %d\n" , + info.voltage_min_design_uv); + info.voltage_min_design_uv = -EINVAL ; + } + +if ((info.energy_full_design_uwh == -EINVAL + || info.charge_full_design_uah == -EINVAL ) + && info.voltage_min_design_uv == -EINVAL ) + goto out; + +bq27xxx_battery_set_config (di, &info); + +out: +bq27xxx_battery_print_config (di); +bq27xxx_battery_set_seal_state (di, true); +} +/* * Return the battery State-of-Charge * Or < 0 if something fails.
@@ -1006,6 +1390,13 @@ static int bq27xxx_battery_get_property(struct power_supply *psy,case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN : ret =bq27xxx_simple_value (di->charge_design_full, val);break ; +/* +* TODO: Implement these to make registers set from +* power_supply_battery_info visible in sysfs. +*/ +case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN : +case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN : +return -EINVAL ;case POWER_SUPPLY_PROP_CYCLE_COUNT : ret =bq27xxx_simple_value (di->cache.cycle_count, val);break ;
@@ -1039,7 +1430,10 @@ static void bq27xxx_external_power_changed(struct power_supply *psy)int bq27xxx_battery_setup (struct bq27xxx_device_info *di) {struct power_supply_desc *psy_desc; -struct power_supply_config psy_cfg = { .drv_data = di, }; +struct power_supply_config psy_cfg = { + .of_node = di->dev->of_node, + .drv_data = di, + };INIT_DELAYED_WORK (&di->work, bq27xxx_battery_poll);mutex_init (&di->lock);
@@ -1064,6 +1458,7 @@ int bq27xxx_battery_setup(struct bq27xxx_device_info *di)dev_info (di->dev,"support ver. %s enabled\n" ,DRIVER_VERSION ); +bq27xxx_battery_settings (di);bq27xxx_battery_update (di);mutex_lock (&bq27xxx_list_lock);
Signature · 1 line
2.9.3
Re: [PATCH v7 7/9] power: bq27xxx_battery: Add power_supply_battery_info support
Quoted 29 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.Signed-off-by Matt Ranostay <[email protected]>Signed-off-by Liam Breck <[email protected]>--- drivers/power/supply/bq27xxx_battery.c | 399 ++++++++++++++++++++++++++++++++- 1 file changed, 397 insertions(+), 2 deletions(-)drivers/power/supply/bq27xxx_battery.c +1
-1@@ -51,7 +51,7 @@#include <linux/power/bq27xxx_battery.h> -#define DRIVER_VERSION "1.2.0" +#define DRIVER_VERSION "1.3.0"#define BQ27XXX_MANUFACTURER "Texas Instruments"@@ -452,6 +452,99 @@ static struct {static DEFINE_MUTEX (bq27xxx_list_lock);
drivers/power/supply/bq27xxx_battery.c static struct { · line 462
Quoted 10 lines
drivers/power/supply/bq27xxx_battery.c +8
-0static LIST_HEAD (bq27xxx_battery_devices); +/* writable registers */ +#define BQ27XXX_CONTROL 0x00 +#define BQ27XXX_DATA_CLASS 0x3E +#define BQ27XXX_DATA_BLOCK 0x3F +#define BQ27XXX_BLOCK_DATA 0x40 +#define BQ27XXX_BLOCK_DATA_CHECKSUM 0x60 +#define BQ27XXX_BLOCK_DATA_CONTROL 0x61 +
These all belong in the bq27xxx_regs array, you can add new register types to the bq27xxx_reg_index enum.
drivers/power/supply/bq27xxx_battery.c static struct { · line 468
Quoted 6 lines
drivers/power/supply/bq27xxx_battery.c +6
-0+/* control register params */ +#define BQ27XXX_SEALED 0x20 +#define BQ27XXX_SET_CFGUPDATE 0x13 +#define BQ27XXX_SOFT_RESET 0x42 + +#define BQ27XXX_MSLEEP (i) usleep_range((i)*1000, (i)*1000+500)
why?
Quoted 8 lines
drivers/power/supply/bq27xxx_battery.c +8
-0+ +struct bq27xxx_dm_reg { +u8 subclass_id; +u8 offset; +u8 bytes; +u16 min, max; +}; +
drivers/power/supply/bq27xxx_battery.c static struct { · line 486
Quoted 10 lines
drivers/power/supply/bq27xxx_battery.c +10
-0+struct bq27xxx_dm_buf { +u8 class; +u8 block; +u8 a[32 ]; +bool full, updt; +}; + +#define BQ27XXX_DM_BUF_PTR (b, r) \ + ( (u16*) &(b).a[ (r)->offset % sizeof (b).a ] ) +
This could compete in the IOCCC, there has to be a more readable way to do this..
drivers/power/supply/bq27xxx_battery.c static struct { · line 496
Quoted 10 lines
drivers/power/supply/bq27xxx_battery.c +10
-0+#define BQ27XXX_DM_BUF (di, i) { \ + .class = bq27xxx_dm_regs[(di)->chip][i].subclass_id, \ + .block = bq27xxx_dm_regs[(di)->chip][i].offset / sizeof ((struct bq27xxx_dm_buf*)0)->a, \ +} + +static inline bool bq27xxx_dm_buf_set (struct bq27xxx_dm_buf *buf, +struct bq27xxx_dm_reg *reg) { +if (buf->class == reg->subclass_id + && buf->block == reg->offset /sizeof buf->a + && buf->full)
Some alignment issues here.
drivers/power/supply/bq27xxx_battery.c static struct { · line 500
Quoted 4 lines
drivers/power/supply/bq27xxx_battery.c +4
-0+return false; + buf->class = reg->subclass_id; + buf->block = reg->offset /sizeof buf->a; + buf->full = buf->updt = false;
newline before return
Quoted 34 lines
drivers/power/supply/bq27xxx_battery.c +34
-0+return true; +} + +enum bq27xxx_dm_reg_id { +BQ27XXX_DM_DESIGN_CAPACITY =0 , +BQ27XXX_DM_DESIGN_ENERGY , +BQ27XXX_DM_TERMINATE_VOLTAGE , +BQ27XXX_DM_END , +}; + +static const char * bq27xxx_dm_reg_name[] = { + [BQ27XXX_DM_DESIGN_CAPACITY ] ="design-capacity" , + [BQ27XXX_DM_DESIGN_ENERGY ] ="design-energy" , + [BQ27XXX_DM_TERMINATE_VOLTAGE ] ="terminate-voltage" , +}; + +static struct bq27xxx_dm_reg bq27425_dm_regs[] = { + [BQ27XXX_DM_DESIGN_CAPACITY ] = {82 ,12 ,2 ,0 ,32767 }, + [BQ27XXX_DM_DESIGN_ENERGY ] = {82 ,14 ,2 ,0 ,32767 }, + [BQ27XXX_DM_TERMINATE_VOLTAGE ] = {82 ,18 ,2 ,2800 ,3700 }, +}; + +static struct bq27xxx_dm_reg bq27421_dm_regs[] = {/* not tested */ + [BQ27XXX_DM_DESIGN_CAPACITY ] = {82 ,10 ,2 ,0 ,8000 }, + [BQ27XXX_DM_DESIGN_ENERGY ] = {82 ,12 ,2 ,0 ,32767 }, + [BQ27XXX_DM_TERMINATE_VOLTAGE ] = {82 ,16 ,2 ,2500 ,3700 }, +}; + +static struct bq27xxx_dm_reg bq27621_dm_regs[] = {/* not tested */ + [BQ27XXX_DM_DESIGN_CAPACITY ] = {82 ,3 ,2 ,0 ,8000 }, + [BQ27XXX_DM_DESIGN_ENERGY ] = {82 ,5 ,2 ,0 ,32767 }, + [BQ27XXX_DM_TERMINATE_VOLTAGE ] = {82 ,9 ,2 ,2500 ,3700 }, +}; +
drivers/power/supply/bq27xxx_battery.c static struct { · line 544
Quoted 10 lines
drivers/power/supply/bq27xxx_battery.c +10
-0+static struct bq27xxx_dm_reg *bq27xxx_dm_regs[] = { + [BQ27421 ] = bq27421_dm_regs,/* and BQ27441 */ + [BQ27425 ] = bq27425_dm_regs, +/* [BQ27621] = */ bq27621_dm_regs, +}; + +static u32 bq27xxx_unseal_keys[] = { + [BQ27421 ] =0x80008000 ,/* and BQ27441 */ + [BQ27425 ] =0x04143672 , +/* [BQ27621] = */ 0x80008000 ,
Why is this commented out.
Quoted 23 lines
drivers/power/supply/bq27xxx_battery.c +16
-0+}; + +static int poll_interval_param_set (const char *val,const struct kernel_param *kp) {struct bq27xxx_device_info *di;@@ -496,6 +589,297 @@ static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index,return di->bus.read (di, di->regs[reg_index], single); } +static int bq27xxx_battery_set_seal_state (struct bq27xxx_device_info *di, +bool state) +{ +u32 key = bq27xxx_unseal_keys[di->chip]; +int ret; + +if (state) { + ret = di->bus.write (di,BQ27XXX_CONTROL ,BQ27XXX_SEALED , false); +if (ret <0 ) + goto out; + }else { + ret = di->bus.write (di,BQ27XXX_CONTROL , (u16)(key >>16 ), false); +if (ret <0 )
drivers/power/supply/bq27xxx_battery.c static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index, · line 614
Quoted 10 lines
drivers/power/supply/bq27xxx_battery.c +10
-0+ goto out; + + ret = di->bus.write (di,BQ27XXX_CONTROL , (u16 )key, false); +if (ret <0 ) + goto out; + } +return 0 ; + +out: +dev_err (di->dev,"bus error in %s: %d\n" , __func__, ret);
This is not a debug statement, we don't need the function name, just say where it broke.
drivers/power/supply/bq27xxx_battery.c static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index, · line 623
Quoted 9 lines
drivers/power/supply/bq27xxx_battery.c +9
-0+return ret; +} + +static u8 bq27xxx_battery_checksum (struct bq27xxx_dm_buf *buf) +{ +u16 sum =0 ; +int i; + +for (i =0 ; i <sizeof buf->a; i++)
Linus would like a word with you:
https://lkml.org/lkml/2012/7/11/103
Quoted 73 lines
drivers/power/supply/bq27xxx_battery.c +73
-0+ sum += buf->a[i]; + sum &=0xff ; + +return 0xff - sum; +} + +static int bq27xxx_battery_read_dm_block (struct bq27xxx_device_info *di, +struct bq27xxx_dm_buf *buf) +{ +int ret; + + ret = di->bus.write (di,BQ27XXX_DATA_CLASS , buf->class, true); +if (ret <0 ) + goto out; + + ret = di->bus.write (di,BQ27XXX_DATA_BLOCK , buf->block, true); +if (ret <0 ) + goto out; + +BQ27XXX_MSLEEP (1 ); + + ret = di->bus.read_bulk (di,BQ27XXX_BLOCK_DATA , buf->a,sizeof buf->a); +if (ret <0 ) + goto out; + + ret = di->bus.read (di,BQ27XXX_BLOCK_DATA_CHECKSUM , true); +if (ret <0 ) + goto out; + +if ((u8 )ret !=bq27xxx_battery_checksum (buf)) { + ret = -EINVAL ; + goto out; + } + + buf->full = true; + buf->updt = false; +return 0 ; + +out: +dev_err (di->dev,"bus error in %s: %d\n" , __func__, ret); +return ret; +} + +static void bq27xxx_battery_print_config (struct bq27xxx_device_info *di) +{ +struct bq27xxx_dm_reg *reg = bq27xxx_dm_regs[di->chip]; +struct bq27xxx_dm_buf buf = { .class =0xFF }; +int i; + +for (i =0 ; i <BQ27XXX_DM_END ; i++, reg++) { +const char * str = bq27xxx_dm_reg_name[i]; + +if (bq27xxx_dm_buf_set (&buf, reg)) +if (bq27xxx_battery_read_dm_block (di, &buf) <0 ) +continue ; + +if (reg->bytes ==2 ) +dev_info (di->dev,"config register %s is %d\n" , str, +be16_to_cpup (BQ27XXX_DM_BUF_PTR (buf, reg))); +else +dev_warn (di->dev,"unsupported config register %s\n" , str); + } +} + +static void bq27xxx_battery_update_dm_block (struct bq27xxx_device_info *di, +struct bq27xxx_dm_buf *buf, +enum bq27xxx_dm_reg_id reg_id, +unsigned int val) +{ +struct bq27xxx_dm_reg *reg = &bq27xxx_dm_regs[di->chip][reg_id]; +u16 *prev; + +if (reg->bytes !=2 )
drivers/power/supply/bq27xxx_battery.c static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index, · line 706
Quoted 10 lines
drivers/power/supply/bq27xxx_battery.c +10
-0+return ; + + prev =BQ27XXX_DM_BUF_PTR (*buf, reg); +if (be16_to_cpup (prev) == val) +return ; + *prev =cpu_to_be16 (val); + +dev_info (di->dev,"update chip %d, class %u, block %u, offset %u, value %u\n" , + di->chip, buf->class, buf->block, reg->offset, val); +
This should probably be dev_debug, no normal user cares about all this detail.
Quoted 30 lines
drivers/power/supply/bq27xxx_battery.c +30
-0+ buf->updt = true; +return ; +} + +static int bq27xxx_battery_write_dm_block (struct bq27xxx_device_info *di, +struct bq27xxx_dm_buf *buf) +{ +bool cfgup = di->chip ==BQ27425 || di->chip ==BQ27421 ;/* || BQ27441 || BQ27621 */ +int ret; + +if (cfgup) { + ret = di->bus.write (di,BQ27XXX_CONTROL ,BQ27XXX_SET_CFGUPDATE , false); +if (ret <0 ) + goto out1; + } + + ret = di->bus.write (di,BQ27XXX_BLOCK_DATA_CONTROL ,0 , true); +if (ret <0 ) + goto out2; + + ret = di->bus.write (di,BQ27XXX_DATA_CLASS , buf->class, true); +if (ret <0 ) + goto out2; + + ret = di->bus.write (di,BQ27XXX_DATA_BLOCK , buf->block, true); +if (ret <0 ) + goto out2; + +BQ27XXX_MSLEEP (1 ); +
drivers/power/supply/bq27xxx_battery.c static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index, · line 746
Quoted 10 lines
drivers/power/supply/bq27xxx_battery.c +10
-0+ ret = di->bus.write_bulk (di,BQ27XXX_BLOCK_DATA , buf->a,sizeof buf->a); +if (ret <0 ) + goto out2; + + ret = di->bus.write (di,BQ27XXX_BLOCK_DATA_CHECKSUM , +bq27xxx_battery_checksum (buf), true); +if (ret <0 ) + goto out2; + +/* THE FOLLOWING CODE IS TOXIC. DO NOT USE!
Then why include it, what is this code supposed to do, and what do we lose without it? If it is really needed we can find a way to guarantee the delay length.
Quoted 174 lines
drivers/power/supply/bq27xxx_battery.c +148
-1+* If the 350ms delay is insufficient, NVM corruption results +* on the '425 chip, which could damage the chip. +* It was suggested in this TI tool: +* http://git.ti.com/bms-linux/bqtool/blobs/master/gauge.c#line328 +* +* BQ27XXX_MSLEEP(350); +* ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, buf->block, true); +* BQ27XXX_MSLEEP(1); +* ret = di->bus.read(di, BQ27XXX_BLOCK_DATA_CHECKSUM, true); +* if ((u8)ret != bq27xxx_battery_checksum(buf)) +* ret = -EINVAL; +*/ + +if (cfgup) { +BQ27XXX_MSLEEP (1 ); + ret = di->bus.write (di,BQ27XXX_CONTROL ,BQ27XXX_SOFT_RESET , false); +if (ret <0 ) + goto out1; + } + + buf->updt = false; +return 0 ; + +out2: +if (cfgup) + di->bus.write (di,BQ27XXX_CONTROL ,BQ27XXX_SOFT_RESET , false); +out1: +dev_err (di->dev,"bus error in %s: %d\n" , __func__, ret); +return ret; +} + +static void bq27xxx_battery_set_config (struct bq27xxx_device_info *di, +struct power_supply_battery_info *info) +{ +struct bq27xxx_dm_buf bd =BQ27XXX_DM_BUF (di,BQ27XXX_DM_DESIGN_ENERGY ); +struct bq27xxx_dm_buf bt =BQ27XXX_DM_BUF (di,BQ27XXX_DM_TERMINATE_VOLTAGE ); + +if (info->charge_full_design_uah != -EINVAL + && info->energy_full_design_uwh != -EINVAL ) { +bq27xxx_battery_read_dm_block (di, &bd); +if (bd.full) { +/* assume design energy & capacity are in same block */ +bq27xxx_battery_update_dm_block (di, &bd, +BQ27XXX_DM_DESIGN_CAPACITY , + info->charge_full_design_uah /1000 ); +bq27xxx_battery_update_dm_block (di, &bd, +BQ27XXX_DM_DESIGN_ENERGY , + info->energy_full_design_uwh /1000 ); + } + } + +if (info->voltage_min_design_uv != -EINVAL ) { +bool same = bd.full && bd.class == bt.class && bd.block == bt.block; +if (!same) +bq27xxx_battery_read_dm_block (di, &bt); +if (same ? bd.full : bt.full) +bq27xxx_battery_update_dm_block (di, same ? &bd : &bt, +BQ27XXX_DM_TERMINATE_VOLTAGE , + info->voltage_min_design_uv /1000 ); + } + +if (bd.updt) +bq27xxx_battery_write_dm_block (di, &bd); +if (bt.updt) +bq27xxx_battery_write_dm_block (di, &bt); +} + +void bq27xxx_battery_settings (struct bq27xxx_device_info *di) +{ +struct power_supply_battery_info info = {}; +unsigned int min, max; + +/* functions don't exist for writing data so abort */ +if (!di->bus.write || !di->bus.write_bulk) +return ; + +/* no settings to be set for this chipset so abort */ +if (!bq27xxx_dm_regs[di->chip]) +return ; + +if (bq27xxx_battery_set_seal_state (di, false) <0 ) +return ; + +if (power_supply_get_battery_info (di->bat, &info) <0 ) + goto out; + +if (info.energy_full_design_uwh != info.charge_full_design_uah) { +if (info.energy_full_design_uwh == -EINVAL ) +dev_warn (di->dev, +"missing battery:energy-full-design-microwatt-hours\n" ); +else if (info.charge_full_design_uah == -EINVAL ) +dev_warn (di->dev, +"missing battery:charge-full-design-microamp-hours\n" ); + } + +/* assume min == 0 */ + max = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_DESIGN_ENERGY ].max; +if (info.energy_full_design_uwh > max *1000 ) { +dev_err (di->dev, +"invalid battery:energy-full-design-microwatt-hours %d\n" , + info.energy_full_design_uwh); + info.energy_full_design_uwh = -EINVAL ; + } + +/* assume min == 0 */ + max = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_DESIGN_CAPACITY ].max; +if (info.charge_full_design_uah > max *1000 ) { +dev_err (di->dev, +"invalid battery:charge-full-design-microamp-hours %d\n" , + info.charge_full_design_uah); + info.charge_full_design_uah = -EINVAL ; + } + + min = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_TERMINATE_VOLTAGE ].min; + max = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_TERMINATE_VOLTAGE ].max; +if ((info.voltage_min_design_uv < min *1000 + || info.voltage_min_design_uv > max *1000 ) + && info.voltage_min_design_uv != -EINVAL ) { +dev_err (di->dev, +"invalid battery:voltage-min-design-microvolt %d\n" , + info.voltage_min_design_uv); + info.voltage_min_design_uv = -EINVAL ; + } + +if ((info.energy_full_design_uwh == -EINVAL + || info.charge_full_design_uah == -EINVAL ) + && info.voltage_min_design_uv == -EINVAL ) + goto out; + +bq27xxx_battery_set_config (di, &info); + +out: +bq27xxx_battery_print_config (di); +bq27xxx_battery_set_seal_state (di, true); +} +/* * Return the battery State-of-Charge * Or < 0 if something fails. @@ -1006,6 +1390,13 @@ static int bq27xxx_battery_get_property(struct power_supply *psy,case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN : ret =bq27xxx_simple_value (di->charge_design_full, val);break ; +/* +* TODO: Implement these to make registers set from +* power_supply_battery_info visible in sysfs. +*/ +case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN : +case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN : +return -EINVAL ;case POWER_SUPPLY_PROP_CYCLE_COUNT : ret =bq27xxx_simple_value (di->cache.cycle_count, val);break ;@@ -1039,7 +1430,10 @@ static void bq27xxx_external_power_changed(struct power_supply *psy)int bq27xxx_battery_setup (struct bq27xxx_device_info *di) {struct power_supply_desc *psy_desc; -struct power_supply_config psy_cfg = { .drv_data = di, }; +struct power_supply_config psy_cfg = { + .of_node = di->dev->of_node, + .drv_data = di, + };INIT_DELAYED_WORK (&di->work, bq27xxx_battery_poll);mutex_init (&di->lock);@@ -1064,6 +1458,7 @@ int bq27xxx_battery_setup(struct bq27xxx_device_info *di)dev_info (di->dev,"support ver. %s enabled\n" ,DRIVER_VERSION ); +bq27xxx_battery_settings (di);bq27xxx_battery_update (di);mutex_lock (&bq27xxx_list_lock);
Re: [PATCH v7 7/9] power: bq27xxx_battery: Add power_supply_battery_info support
Quoted 32 lines · Andrew F. Davis
Quoted 31 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.Signed-off-by Matt Ranostay <[email protected]>Signed-off-by Liam Breck <[email protected]>--- drivers/power/supply/bq27xxx_battery.c | 399 ++++++++++++++++++++++++++++++++- 1 file changed, 397 insertions(+), 2 deletions(-)drivers/power/supply/bq27xxx_battery.c +1
-1@@ -51,7 +51,7 @@#include <linux/power/bq27xxx_battery.h> -#define DRIVER_VERSION "1.2.0" +#define DRIVER_VERSION "1.3.0"#define BQ27XXX_MANUFACTURER "Texas Instruments"@@ -452,6 +452,99 @@ static struct {static DEFINE_MUTEX (bq27xxx_list_lock);static LIST_HEAD (bq27xxx_battery_devices);
Quoted 11 lines
drivers/power/supply/bq27xxx_battery.c static struct { · line 462
Quoted 8 lines
drivers/power/supply/bq27xxx_battery.c +8
-0+/* writable registers */ +#define BQ27XXX_CONTROL 0x00 +#define BQ27XXX_DATA_CLASS 0x3E +#define BQ27XXX_DATA_BLOCK 0x3F +#define BQ27XXX_BLOCK_DATA 0x40 +#define BQ27XXX_BLOCK_DATA_CHECKSUM 0x60 +#define BQ27XXX_BLOCK_DATA_CONTROL 0x61 +These all belong in the
bq27xxx_regsarray, you can add new register types to thebq27xxx_reg_indexenum.
They're the same on all chips. Let's dup this code 9+ times when we have cause.
Or make a default array of reg numbers and memcopy it onto each chip array, then alter as req'd per chip. But that's outside our scope.
Quoted 8 lines
drivers/power/supply/bq27xxx_battery.c static struct { · line 468
Quoted 6 lines
drivers/power/supply/bq27xxx_battery.c +6
-0+/* control register params */ +#define BQ27XXX_SEALED 0x20 +#define BQ27XXX_SET_CFGUPDATE 0x13 +#define BQ27XXX_SOFT_RESET 0x42 + +#define BQ27XXX_MSLEEP (i) usleep_range((i)*1000, (i)*1000+500)why?
I think this might vary on diff chips. Also I have a pending change which uses MSLEEP(20)
Quoted 13 lines
Quoted 13 lines
drivers/power/supply/bq27xxx_battery.c +13
-0+ +struct bq27xxx_dm_reg { +u8 subclass_id; +u8 offset; +u8 bytes; +u16 min, max; +}; + +struct bq27xxx_dm_buf { +u8 class; +u8 block; +u8 a[32 ]; +bool full, updt;
Quoted 8 lines
drivers/power/supply/bq27xxx_battery.c static struct { · line 486
Quoted 5 lines
drivers/power/supply/bq27xxx_battery.c +5
-0+}; + +#define BQ27XXX_DM_BUF_PTR (b, r) \ + ( (u16*) &(b).a[ (r)->offset % sizeof (b).a ] ) +This could compete in the IOCCC, there has to be a more readable way to do this..
Hence the macro. It didn't seem to merit a function, but I could write an inline fn.
Quoted 58 lines
drivers/power/supply/bq27xxx_battery.c static struct { · line 496
Quoted 10 lines
drivers/power/supply/bq27xxx_battery.c +10
-0+#define BQ27XXX_DM_BUF (di, i) { \ + .class = bq27xxx_dm_regs[(di)->chip][i].subclass_id, \ + .block = bq27xxx_dm_regs[(di)->chip][i].offset / sizeof ((struct bq27xxx_dm_buf*)0)->a, \ +} + +static inline bool bq27xxx_dm_buf_set (struct bq27xxx_dm_buf *buf, +struct bq27xxx_dm_reg *reg) { +if (buf->class == reg->subclass_id + && buf->block == reg->offset /sizeof buf->a + && buf->full)Some alignment issues here.
drivers/power/supply/bq27xxx_battery.c static struct { · line 500
Quoted 4 lines
drivers/power/supply/bq27xxx_battery.c +4
-0+return false; + buf->class = reg->subclass_id; + buf->block = reg->offset /sizeof buf->a; + buf->full = buf->updt = false;newline before return
Quoted 38 lines
drivers/power/supply/bq27xxx_battery.c +38
-0+return true; +} + +enum bq27xxx_dm_reg_id { +BQ27XXX_DM_DESIGN_CAPACITY =0 , +BQ27XXX_DM_DESIGN_ENERGY , +BQ27XXX_DM_TERMINATE_VOLTAGE , +BQ27XXX_DM_END , +}; + +static const char * bq27xxx_dm_reg_name[] = { + [BQ27XXX_DM_DESIGN_CAPACITY ] ="design-capacity" , + [BQ27XXX_DM_DESIGN_ENERGY ] ="design-energy" , + [BQ27XXX_DM_TERMINATE_VOLTAGE ] ="terminate-voltage" , +}; + +static struct bq27xxx_dm_reg bq27425_dm_regs[] = { + [BQ27XXX_DM_DESIGN_CAPACITY ] = {82 ,12 ,2 ,0 ,32767 }, + [BQ27XXX_DM_DESIGN_ENERGY ] = {82 ,14 ,2 ,0 ,32767 }, + [BQ27XXX_DM_TERMINATE_VOLTAGE ] = {82 ,18 ,2 ,2800 ,3700 }, +}; + +static struct bq27xxx_dm_reg bq27421_dm_regs[] = {/* not tested */ + [BQ27XXX_DM_DESIGN_CAPACITY ] = {82 ,10 ,2 ,0 ,8000 }, + [BQ27XXX_DM_DESIGN_ENERGY ] = {82 ,12 ,2 ,0 ,32767 }, + [BQ27XXX_DM_TERMINATE_VOLTAGE ] = {82 ,16 ,2 ,2500 ,3700 }, +}; + +static struct bq27xxx_dm_reg bq27621_dm_regs[] = {/* not tested */ + [BQ27XXX_DM_DESIGN_CAPACITY ] = {82 ,3 ,2 ,0 ,8000 }, + [BQ27XXX_DM_DESIGN_ENERGY ] = {82 ,5 ,2 ,0 ,32767 }, + [BQ27XXX_DM_TERMINATE_VOLTAGE ] = {82 ,9 ,2 ,2500 ,3700 }, +}; + +static struct bq27xxx_dm_reg *bq27xxx_dm_regs[] = { + [BQ27421 ] = bq27421_dm_regs,/* and BQ27441 */ + [BQ27425 ] = bq27425_dm_regs, +/* [BQ27621] = */ bq27621_dm_regs,
Quoted 8 lines
drivers/power/supply/bq27xxx_battery.c static struct { · line 544
Quoted 6 lines
drivers/power/supply/bq27xxx_battery.c +6
-0+}; + +static u32 bq27xxx_unseal_keys[] = { + [BQ27421 ] =0x80008000 ,/* and BQ27441 */ + [BQ27425 ] =0x04143672 , +/* [BQ27621] = */ 0x80008000 ,Why is this commented out.
The 621 id isn't defined yet. I didn't want to enable something I can't test.
Quoted 28 lines
Quoted 28 lines
drivers/power/supply/bq27xxx_battery.c +21
-0+}; + +static int poll_interval_param_set (const char *val,const struct kernel_param *kp) {struct bq27xxx_device_info *di;@@ -496,6 +589,297 @@ static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index,return di->bus.read (di, di->regs[reg_index], single); } +static int bq27xxx_battery_set_seal_state (struct bq27xxx_device_info *di, +bool state) +{ +u32 key = bq27xxx_unseal_keys[di->chip]; +int ret; + +if (state) { + ret = di->bus.write (di,BQ27XXX_CONTROL ,BQ27XXX_SEALED , false); +if (ret <0 ) + goto out; + }else { + ret = di->bus.write (di,BQ27XXX_CONTROL , (u16)(key >>16 ), false); +if (ret <0 ) + goto out; + + ret = di->bus.write (di,BQ27XXX_CONTROL , (u16 )key, false); +if (ret <0 ) + goto out;
Quoted 8 lines
drivers/power/supply/bq27xxx_battery.c static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index, · line 614
Quoted 5 lines
drivers/power/supply/bq27xxx_battery.c +5
-0+ } +return 0 ; + +out: +dev_err (di->dev,"bus error in %s: %d\n" , __func__, ret);This is not a debug statement, we don't need the function name, just say where it broke.
What's "where" if not fn name?
Quoted 91 lines
drivers/power/supply/bq27xxx_battery.c static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index, · line 623
Quoted 9 lines
drivers/power/supply/bq27xxx_battery.c +9
-0+return ret; +} + +static u8 bq27xxx_battery_checksum (struct bq27xxx_dm_buf *buf) +{ +u16 sum =0 ; +int i; + +for (i =0 ; i <sizeof buf->a; i++)Linus would like a word with you:
https://lkml.org/lkml/2012/7/11/103Quoted 78 lines
drivers/power/supply/bq27xxx_battery.c +78
-0+ sum += buf->a[i]; + sum &=0xff ; + +return 0xff - sum; +} + +static int bq27xxx_battery_read_dm_block (struct bq27xxx_device_info *di, +struct bq27xxx_dm_buf *buf) +{ +int ret; + + ret = di->bus.write (di,BQ27XXX_DATA_CLASS , buf->class, true); +if (ret <0 ) + goto out; + + ret = di->bus.write (di,BQ27XXX_DATA_BLOCK , buf->block, true); +if (ret <0 ) + goto out; + +BQ27XXX_MSLEEP (1 ); + + ret = di->bus.read_bulk (di,BQ27XXX_BLOCK_DATA , buf->a,sizeof buf->a); +if (ret <0 ) + goto out; + + ret = di->bus.read (di,BQ27XXX_BLOCK_DATA_CHECKSUM , true); +if (ret <0 ) + goto out; + +if ((u8 )ret !=bq27xxx_battery_checksum (buf)) { + ret = -EINVAL ; + goto out; + } + + buf->full = true; + buf->updt = false; +return 0 ; + +out: +dev_err (di->dev,"bus error in %s: %d\n" , __func__, ret); +return ret; +} + +static void bq27xxx_battery_print_config (struct bq27xxx_device_info *di) +{ +struct bq27xxx_dm_reg *reg = bq27xxx_dm_regs[di->chip]; +struct bq27xxx_dm_buf buf = { .class =0xFF }; +int i; + +for (i =0 ; i <BQ27XXX_DM_END ; i++, reg++) { +const char * str = bq27xxx_dm_reg_name[i]; + +if (bq27xxx_dm_buf_set (&buf, reg)) +if (bq27xxx_battery_read_dm_block (di, &buf) <0 ) +continue ; + +if (reg->bytes ==2 ) +dev_info (di->dev,"config register %s is %d\n" , str, +be16_to_cpup (BQ27XXX_DM_BUF_PTR (buf, reg))); +else +dev_warn (di->dev,"unsupported config register %s\n" , str); + } +} + +static void bq27xxx_battery_update_dm_block (struct bq27xxx_device_info *di, +struct bq27xxx_dm_buf *buf, +enum bq27xxx_dm_reg_id reg_id, +unsigned int val) +{ +struct bq27xxx_dm_reg *reg = &bq27xxx_dm_regs[di->chip][reg_id]; +u16 *prev; + +if (reg->bytes !=2 ) +return ; + + prev =BQ27XXX_DM_BUF_PTR (*buf, reg); +if (be16_to_cpup (prev) == val) +return ;
Quoted 8 lines
drivers/power/supply/bq27xxx_battery.c static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index, · line 706
Quoted 5 lines
drivers/power/supply/bq27xxx_battery.c +5
-0+ *prev =cpu_to_be16 (val); + +dev_info (di->dev,"update chip %d, class %u, block %u, offset %u, value %u\n" , + di->chip, buf->class, buf->block, reg->offset, val); +This should probably be
dev_debug, no normal user cares about all this detail.
It's only on change to NVM, which a hw developer (like me) wants to know about. I plan to remove the print_config routine so this is not redundant.
Quoted 36 lines
Quoted 36 lines
drivers/power/supply/bq27xxx_battery.c +36
-0+ buf->updt = true; +return ; +} + +static int bq27xxx_battery_write_dm_block (struct bq27xxx_device_info *di, +struct bq27xxx_dm_buf *buf) +{ +bool cfgup = di->chip ==BQ27425 || di->chip ==BQ27421 ;/* || BQ27441 || BQ27621 */ +int ret; + +if (cfgup) { + ret = di->bus.write (di,BQ27XXX_CONTROL ,BQ27XXX_SET_CFGUPDATE , false); +if (ret <0 ) + goto out1; + } + + ret = di->bus.write (di,BQ27XXX_BLOCK_DATA_CONTROL ,0 , true); +if (ret <0 ) + goto out2; + + ret = di->bus.write (di,BQ27XXX_DATA_CLASS , buf->class, true); +if (ret <0 ) + goto out2; + + ret = di->bus.write (di,BQ27XXX_DATA_BLOCK , buf->block, true); +if (ret <0 ) + goto out2; + +BQ27XXX_MSLEEP (1 ); + + ret = di->bus.write_bulk (di,BQ27XXX_BLOCK_DATA , buf->a,sizeof buf->a); +if (ret <0 ) + goto out2; + + ret = di->bus.write (di,BQ27XXX_BLOCK_DATA_CHECKSUM , +bq27xxx_battery_checksum (buf), true);
Quoted 8 lines
drivers/power/supply/bq27xxx_battery.c static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index, · line 746
Quoted 4 lines
drivers/power/supply/bq27xxx_battery.c +4
-0+if (ret <0 ) + goto out2; + +/* THE FOLLOWING CODE IS TOXIC. DO NOT USE! Then why include it, what is this code supposed to do, and what do we lose without it? If it is really needed we can find a way to guarantee the delay length.
This triggers a chip bug, and future devs need to know to avoid it. I was tempted by bqtool into using it for the extra error check, and now my gauge isn't working, tho I have only reset one NVM block back to factory defaults. Bqtool is easy to find, if poorly written.
Quoted 174 lines
Quoted 174 lines
drivers/power/supply/bq27xxx_battery.c +148
-1+* If the 350ms delay is insufficient, NVM corruption results +* on the '425 chip, which could damage the chip. +* It was suggested in this TI tool: +* http://git.ti.com/bms-linux/bqtool/blobs/master/gauge.c#line328 +* +* BQ27XXX_MSLEEP(350); +* ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, buf->block, true); +* BQ27XXX_MSLEEP(1); +* ret = di->bus.read(di, BQ27XXX_BLOCK_DATA_CHECKSUM, true); +* if ((u8)ret != bq27xxx_battery_checksum(buf)) +* ret = -EINVAL; +*/ + +if (cfgup) { +BQ27XXX_MSLEEP (1 ); + ret = di->bus.write (di,BQ27XXX_CONTROL ,BQ27XXX_SOFT_RESET , false); +if (ret <0 ) + goto out1; + } + + buf->updt = false; +return 0 ; + +out2: +if (cfgup) + di->bus.write (di,BQ27XXX_CONTROL ,BQ27XXX_SOFT_RESET , false); +out1: +dev_err (di->dev,"bus error in %s: %d\n" , __func__, ret); +return ret; +} + +static void bq27xxx_battery_set_config (struct bq27xxx_device_info *di, +struct power_supply_battery_info *info) +{ +struct bq27xxx_dm_buf bd =BQ27XXX_DM_BUF (di,BQ27XXX_DM_DESIGN_ENERGY ); +struct bq27xxx_dm_buf bt =BQ27XXX_DM_BUF (di,BQ27XXX_DM_TERMINATE_VOLTAGE ); + +if (info->charge_full_design_uah != -EINVAL + && info->energy_full_design_uwh != -EINVAL ) { +bq27xxx_battery_read_dm_block (di, &bd); +if (bd.full) { +/* assume design energy & capacity are in same block */ +bq27xxx_battery_update_dm_block (di, &bd, +BQ27XXX_DM_DESIGN_CAPACITY , + info->charge_full_design_uah /1000 ); +bq27xxx_battery_update_dm_block (di, &bd, +BQ27XXX_DM_DESIGN_ENERGY , + info->energy_full_design_uwh /1000 ); + } + } + +if (info->voltage_min_design_uv != -EINVAL ) { +bool same = bd.full && bd.class == bt.class && bd.block == bt.block; +if (!same) +bq27xxx_battery_read_dm_block (di, &bt); +if (same ? bd.full : bt.full) +bq27xxx_battery_update_dm_block (di, same ? &bd : &bt, +BQ27XXX_DM_TERMINATE_VOLTAGE , + info->voltage_min_design_uv /1000 ); + } + +if (bd.updt) +bq27xxx_battery_write_dm_block (di, &bd); +if (bt.updt) +bq27xxx_battery_write_dm_block (di, &bt); +} + +void bq27xxx_battery_settings (struct bq27xxx_device_info *di) +{ +struct power_supply_battery_info info = {}; +unsigned int min, max; + +/* functions don't exist for writing data so abort */ +if (!di->bus.write || !di->bus.write_bulk) +return ; + +/* no settings to be set for this chipset so abort */ +if (!bq27xxx_dm_regs[di->chip]) +return ; + +if (bq27xxx_battery_set_seal_state (di, false) <0 ) +return ; + +if (power_supply_get_battery_info (di->bat, &info) <0 ) + goto out; + +if (info.energy_full_design_uwh != info.charge_full_design_uah) { +if (info.energy_full_design_uwh == -EINVAL ) +dev_warn (di->dev, +"missing battery:energy-full-design-microwatt-hours\n" ); +else if (info.charge_full_design_uah == -EINVAL ) +dev_warn (di->dev, +"missing battery:charge-full-design-microamp-hours\n" ); + } + +/* assume min == 0 */ + max = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_DESIGN_ENERGY ].max; +if (info.energy_full_design_uwh > max *1000 ) { +dev_err (di->dev, +"invalid battery:energy-full-design-microwatt-hours %d\n" , + info.energy_full_design_uwh); + info.energy_full_design_uwh = -EINVAL ; + } + +/* assume min == 0 */ + max = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_DESIGN_CAPACITY ].max; +if (info.charge_full_design_uah > max *1000 ) { +dev_err (di->dev, +"invalid battery:charge-full-design-microamp-hours %d\n" , + info.charge_full_design_uah); + info.charge_full_design_uah = -EINVAL ; + } + + min = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_TERMINATE_VOLTAGE ].min; + max = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_TERMINATE_VOLTAGE ].max; +if ((info.voltage_min_design_uv < min *1000 + || info.voltage_min_design_uv > max *1000 ) + && info.voltage_min_design_uv != -EINVAL ) { +dev_err (di->dev, +"invalid battery:voltage-min-design-microvolt %d\n" , + info.voltage_min_design_uv); + info.voltage_min_design_uv = -EINVAL ; + } + +if ((info.energy_full_design_uwh == -EINVAL + || info.charge_full_design_uah == -EINVAL ) + && info.voltage_min_design_uv == -EINVAL ) + goto out; + +bq27xxx_battery_set_config (di, &info); + +out: +bq27xxx_battery_print_config (di); +bq27xxx_battery_set_seal_state (di, true); +} +/* * Return the battery State-of-Charge * Or < 0 if something fails. @@ -1006,6 +1390,13 @@ static int bq27xxx_battery_get_property(struct power_supply *psy,case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN : ret =bq27xxx_simple_value (di->charge_design_full, val);break ; +/* +* TODO: Implement these to make registers set from +* power_supply_battery_info visible in sysfs. +*/ +case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN : +case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN : +return -EINVAL ;case POWER_SUPPLY_PROP_CYCLE_COUNT : ret =bq27xxx_simple_value (di->cache.cycle_count, val);break ;@@ -1039,7 +1430,10 @@ static void bq27xxx_external_power_changed(struct power_supply *psy)int bq27xxx_battery_setup (struct bq27xxx_device_info *di) {struct power_supply_desc *psy_desc; -struct power_supply_config psy_cfg = { .drv_data = di, }; +struct power_supply_config psy_cfg = { + .of_node = di->dev->of_node, + .drv_data = di, + };INIT_DELAYED_WORK (&di->work, bq27xxx_battery_poll);mutex_init (&di->lock);@@ -1064,6 +1458,7 @@ int bq27xxx_battery_setup(struct bq27xxx_device_info *di)dev_info (di->dev,"support ver. %s enabled\n" ,DRIVER_VERSION ); +bq27xxx_battery_settings (di);bq27xxx_battery_update (di);mutex_lock (&bq27xxx_list_lock);
Re: [PATCH v7 7/9] power: bq27xxx_battery: Add power_supply_battery_info support
Quoted 45 lines · Liam Breck
Quoted 32 lines · Andrew F. Davis
Quoted 31 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.Signed-off-by Matt Ranostay <[email protected]>Signed-off-by Liam Breck <[email protected]>--- drivers/power/supply/bq27xxx_battery.c | 399 ++++++++++++++++++++++++++++++++- 1 file changed, 397 insertions(+), 2 deletions(-)drivers/power/supply/bq27xxx_battery.c +1
-1@@ -51,7 +51,7 @@#include <linux/power/bq27xxx_battery.h> -#define DRIVER_VERSION "1.2.0" +#define DRIVER_VERSION "1.3.0"#define BQ27XXX_MANUFACTURER "Texas Instruments"@@ -452,6 +452,99 @@ static struct {static DEFINE_MUTEX (bq27xxx_list_lock);static LIST_HEAD (bq27xxx_battery_devices);Quoted 11 lines
drivers/power/supply/bq27xxx_battery.c static struct { · line 462
Quoted 8 lines
drivers/power/supply/bq27xxx_battery.c +8
-0+/* writable registers */ +#define BQ27XXX_CONTROL 0x00 +#define BQ27XXX_DATA_CLASS 0x3E +#define BQ27XXX_DATA_BLOCK 0x3F +#define BQ27XXX_BLOCK_DATA 0x40 +#define BQ27XXX_BLOCK_DATA_CHECKSUM 0x60 +#define BQ27XXX_BLOCK_DATA_CONTROL 0x61 +These all belong in the
bq27xxx_regsarray, you can add new register types to thebq27xxx_reg_indexenum.
Quoted 2 lines
They're the same on all chips. Let's dup this code 9+ times when we have cause.
They are not the same on all chips, some chips don't have these registers at all. Plus, BQ27XXX_CONTROL is the same for all chips, and we already have it in all the chips bq27xxx_regs.
Quoted 12 lines
Or make a default array of reg numbers and memcopy it onto each chip array, then alter as req'd per chip. But that's outside our scope.
Quoted 8 lines
drivers/power/supply/bq27xxx_battery.c static struct { · line 468
Quoted 6 lines
drivers/power/supply/bq27xxx_battery.c +6
-0+/* control register params */ +#define BQ27XXX_SEALED 0x20 +#define BQ27XXX_SET_CFGUPDATE 0x13 +#define BQ27XXX_SOFT_RESET 0x42 + +#define BQ27XXX_MSLEEP (i) usleep_range((i)*1000, (i)*1000+500)why?
Quoted 3 lines
I think this might vary on diff chips. Also I have a pending change which uses MSLEEP(20)
If they are different we should delay for the largest needed time, a range means some may not get enough delay, we would always be targeting the lowest common denominator chip anyway.
Quoted 22 lines
Quoted 13 lines
Quoted 13 lines
drivers/power/supply/bq27xxx_battery.c +13
-0+ +struct bq27xxx_dm_reg { +u8 subclass_id; +u8 offset; +u8 bytes; +u16 min, max; +}; + +struct bq27xxx_dm_buf { +u8 class; +u8 block; +u8 a[32 ]; +bool full, updt;Quoted 8 lines
drivers/power/supply/bq27xxx_battery.c static struct { · line 486
Quoted 5 lines
drivers/power/supply/bq27xxx_battery.c +5
-0+}; + +#define BQ27XXX_DM_BUF_PTR (b, r) \ + ( (u16*) &(b).a[ (r)->offset % sizeof (b).a ] ) +This could compete in the IOCCC, there has to be a more readable way to do this..
Quoted 3 lines
Hence the macro. It didn't seem to merit a function, but I could write an inline fn.
That would be nice. With C, the whole kernel could be one long line, but just because you can doesn't mean you should, lets keep this readable for all of our sake :)
Quoted 68 lines
Quoted 59 lines
drivers/power/supply/bq27xxx_battery.c static struct { · line 496
Quoted 11 lines
drivers/power/supply/bq27xxx_battery.c +10
-0+#define BQ27XXX_DM_BUF (di, i) { \ + .class = bq27xxx_dm_regs[(di)->chip][i].subclass_id, \ + .block = bq27xxx_dm_regs[(di)->chip][i].offset / sizeof ((struct bq27xxx_dm_buf*)0)->a, \ +} + +static inline bool bq27xxx_dm_buf_set (struct bq27xxx_dm_buf *buf, +struct bq27xxx_dm_reg *reg) { +if (buf->class == reg->subclass_id + && buf->block == reg->offset /sizeof buf->a + && buf->full)Some alignment issues here.
drivers/power/supply/bq27xxx_battery.c static struct { · line 500
Quoted 4 lines
drivers/power/supply/bq27xxx_battery.c +4
-0+return false; + buf->class = reg->subclass_id; + buf->block = reg->offset /sizeof buf->a; + buf->full = buf->updt = false;newline before return
Quoted 38 lines
drivers/power/supply/bq27xxx_battery.c +38
-0+return true; +} + +enum bq27xxx_dm_reg_id { +BQ27XXX_DM_DESIGN_CAPACITY =0 , +BQ27XXX_DM_DESIGN_ENERGY , +BQ27XXX_DM_TERMINATE_VOLTAGE , +BQ27XXX_DM_END , +}; + +static const char * bq27xxx_dm_reg_name[] = { + [BQ27XXX_DM_DESIGN_CAPACITY ] ="design-capacity" , + [BQ27XXX_DM_DESIGN_ENERGY ] ="design-energy" , + [BQ27XXX_DM_TERMINATE_VOLTAGE ] ="terminate-voltage" , +}; + +static struct bq27xxx_dm_reg bq27425_dm_regs[] = { + [BQ27XXX_DM_DESIGN_CAPACITY ] = {82 ,12 ,2 ,0 ,32767 }, + [BQ27XXX_DM_DESIGN_ENERGY ] = {82 ,14 ,2 ,0 ,32767 }, + [BQ27XXX_DM_TERMINATE_VOLTAGE ] = {82 ,18 ,2 ,2800 ,3700 }, +}; + +static struct bq27xxx_dm_reg bq27421_dm_regs[] = {/* not tested */ + [BQ27XXX_DM_DESIGN_CAPACITY ] = {82 ,10 ,2 ,0 ,8000 }, + [BQ27XXX_DM_DESIGN_ENERGY ] = {82 ,12 ,2 ,0 ,32767 }, + [BQ27XXX_DM_TERMINATE_VOLTAGE ] = {82 ,16 ,2 ,2500 ,3700 }, +}; + +static struct bq27xxx_dm_reg bq27621_dm_regs[] = {/* not tested */ + [BQ27XXX_DM_DESIGN_CAPACITY ] = {82 ,3 ,2 ,0 ,8000 }, + [BQ27XXX_DM_DESIGN_ENERGY ] = {82 ,5 ,2 ,0 ,32767 }, + [BQ27XXX_DM_TERMINATE_VOLTAGE ] = {82 ,9 ,2 ,2500 ,3700 }, +}; + +static struct bq27xxx_dm_reg *bq27xxx_dm_regs[] = { + [BQ27421 ] = bq27421_dm_regs,/* and BQ27441 */ + [BQ27425 ] = bq27425_dm_regs, +/* [BQ27621] = */ bq27621_dm_regs,Quoted 8 lines
drivers/power/supply/bq27xxx_battery.c static struct { · line 544
Quoted 6 lines
drivers/power/supply/bq27xxx_battery.c +6
-0+}; + +static u32 bq27xxx_unseal_keys[] = { + [BQ27421 ] =0x80008000 ,/* and BQ27441 */ + [BQ27425 ] =0x04143672 , +/* [BQ27621] = */ 0x80008000 ,Why is this commented out.
Quoted 2 lines
The 621 id isn't defined yet. I didn't want to enable something I can't test.
Yes, but you are only commenting out the index, it still is added to the list. You should wait to add that line when the rest of BQ27621 support is added.
Quoted 37 lines
Quoted 28 lines
Quoted 28 lines
drivers/power/supply/bq27xxx_battery.c +21
-0+}; + +static int poll_interval_param_set (const char *val,const struct kernel_param *kp) {struct bq27xxx_device_info *di;@@ -496,6 +589,297 @@ static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index,return di->bus.read (di, di->regs[reg_index], single); } +static int bq27xxx_battery_set_seal_state (struct bq27xxx_device_info *di, +bool state) +{ +u32 key = bq27xxx_unseal_keys[di->chip]; +int ret; + +if (state) { + ret = di->bus.write (di,BQ27XXX_CONTROL ,BQ27XXX_SEALED , false); +if (ret <0 ) + goto out; + }else { + ret = di->bus.write (di,BQ27XXX_CONTROL , (u16)(key >>16 ), false); +if (ret <0 ) + goto out; + + ret = di->bus.write (di,BQ27XXX_CONTROL , (u16 )key, false); +if (ret <0 ) + goto out;Quoted 8 lines
drivers/power/supply/bq27xxx_battery.c static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index, · line 614
Quoted 5 lines
drivers/power/supply/bq27xxx_battery.c +5
-0+ } +return 0 ; + +out: +dev_err (di->dev,"bus error in %s: %d\n" , __func__, ret);This is not a debug statement, we don't need the function name, just say where it broke.
Quoted 2 lines
What's "where" if not fn name?
For example:
"Bus error while setting seal state"
The actually function name is unimportant, and the user doesn't care.
Quoted 100 lines
Quoted 91 lines
drivers/power/supply/bq27xxx_battery.c static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index, · line 623
Quoted 9 lines
drivers/power/supply/bq27xxx_battery.c +9
-0+return ret; +} + +static u8 bq27xxx_battery_checksum (struct bq27xxx_dm_buf *buf) +{ +u16 sum =0 ; +int i; + +for (i =0 ; i <sizeof buf->a; i++)Linus would like a word with you:
https://lkml.org/lkml/2012/7/11/103Quoted 78 lines
drivers/power/supply/bq27xxx_battery.c +78
-0+ sum += buf->a[i]; + sum &=0xff ; + +return 0xff - sum; +} + +static int bq27xxx_battery_read_dm_block (struct bq27xxx_device_info *di, +struct bq27xxx_dm_buf *buf) +{ +int ret; + + ret = di->bus.write (di,BQ27XXX_DATA_CLASS , buf->class, true); +if (ret <0 ) + goto out; + + ret = di->bus.write (di,BQ27XXX_DATA_BLOCK , buf->block, true); +if (ret <0 ) + goto out; + +BQ27XXX_MSLEEP (1 ); + + ret = di->bus.read_bulk (di,BQ27XXX_BLOCK_DATA , buf->a,sizeof buf->a); +if (ret <0 ) + goto out; + + ret = di->bus.read (di,BQ27XXX_BLOCK_DATA_CHECKSUM , true); +if (ret <0 ) + goto out; + +if ((u8 )ret !=bq27xxx_battery_checksum (buf)) { + ret = -EINVAL ; + goto out; + } + + buf->full = true; + buf->updt = false; +return 0 ; + +out: +dev_err (di->dev,"bus error in %s: %d\n" , __func__, ret); +return ret; +} + +static void bq27xxx_battery_print_config (struct bq27xxx_device_info *di) +{ +struct bq27xxx_dm_reg *reg = bq27xxx_dm_regs[di->chip]; +struct bq27xxx_dm_buf buf = { .class =0xFF }; +int i; + +for (i =0 ; i <BQ27XXX_DM_END ; i++, reg++) { +const char * str = bq27xxx_dm_reg_name[i]; + +if (bq27xxx_dm_buf_set (&buf, reg)) +if (bq27xxx_battery_read_dm_block (di, &buf) <0 ) +continue ; + +if (reg->bytes ==2 ) +dev_info (di->dev,"config register %s is %d\n" , str, +be16_to_cpup (BQ27XXX_DM_BUF_PTR (buf, reg))); +else +dev_warn (di->dev,"unsupported config register %s\n" , str); + } +} + +static void bq27xxx_battery_update_dm_block (struct bq27xxx_device_info *di, +struct bq27xxx_dm_buf *buf, +enum bq27xxx_dm_reg_id reg_id, +unsigned int val) +{ +struct bq27xxx_dm_reg *reg = &bq27xxx_dm_regs[di->chip][reg_id]; +u16 *prev; + +if (reg->bytes !=2 ) +return ; + + prev =BQ27XXX_DM_BUF_PTR (*buf, reg); +if (be16_to_cpup (prev) == val) +return ;Quoted 8 lines
drivers/power/supply/bq27xxx_battery.c static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index, · line 706
Quoted 5 lines
drivers/power/supply/bq27xxx_battery.c +5
-0+ *prev =cpu_to_be16 (val); + +dev_info (di->dev,"update chip %d, class %u, block %u, offset %u, value %u\n" , + di->chip, buf->class, buf->block, reg->offset, val); +This should probably be
dev_debug, no normal user cares about all this detail.
Quoted 4 lines
It's only on change to NVM, which a hw developer (like me) wants to know about. I plan to remove the
print_configroutine so this is not redundant.
HW developers should then have debug enabled for drivers they care about. For everyone else this is just kernel log clutter.
Quoted 45 lines
Quoted 36 lines
Quoted 36 lines
drivers/power/supply/bq27xxx_battery.c +36
-0+ buf->updt = true; +return ; +} + +static int bq27xxx_battery_write_dm_block (struct bq27xxx_device_info *di, +struct bq27xxx_dm_buf *buf) +{ +bool cfgup = di->chip ==BQ27425 || di->chip ==BQ27421 ;/* || BQ27441 || BQ27621 */ +int ret; + +if (cfgup) { + ret = di->bus.write (di,BQ27XXX_CONTROL ,BQ27XXX_SET_CFGUPDATE , false); +if (ret <0 ) + goto out1; + } + + ret = di->bus.write (di,BQ27XXX_BLOCK_DATA_CONTROL ,0 , true); +if (ret <0 ) + goto out2; + + ret = di->bus.write (di,BQ27XXX_DATA_CLASS , buf->class, true); +if (ret <0 ) + goto out2; + + ret = di->bus.write (di,BQ27XXX_DATA_BLOCK , buf->block, true); +if (ret <0 ) + goto out2; + +BQ27XXX_MSLEEP (1 ); + + ret = di->bus.write_bulk (di,BQ27XXX_BLOCK_DATA , buf->a,sizeof buf->a); +if (ret <0 ) + goto out2; + + ret = di->bus.write (di,BQ27XXX_BLOCK_DATA_CHECKSUM , +bq27xxx_battery_checksum (buf), true);Quoted 8 lines
drivers/power/supply/bq27xxx_battery.c static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index, · line 746
Quoted 4 lines
drivers/power/supply/bq27xxx_battery.c +4
-0+if (ret <0 ) + goto out2; + +/* THE FOLLOWING CODE IS TOXIC. DO NOT USE! Then why include it, what is this code supposed to do, and what do we lose without it? If it is really needed we can find a way to guarantee the delay length.
Quoted 5 lines
This triggers a chip bug, and future devs need to know to avoid it. I was tempted by bqtool into using it for the extra error check, and now my gauge isn't working, tho I have only reset one NVM block back to factory defaults. Bqtool is easy to find, if poorly written.
Just the warning would be enough then, we don't gain anything by writing out the exact code to break your device in the comment also.
Quoted 174 lines
Quoted 174 lines
Quoted 174 lines
drivers/power/supply/bq27xxx_battery.c +148
-1+* If the 350ms delay is insufficient, NVM corruption results +* on the '425 chip, which could damage the chip. +* It was suggested in this TI tool: +* http://git.ti.com/bms-linux/bqtool/blobs/master/gauge.c#line328 +* +* BQ27XXX_MSLEEP(350); +* ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, buf->block, true); +* BQ27XXX_MSLEEP(1); +* ret = di->bus.read(di, BQ27XXX_BLOCK_DATA_CHECKSUM, true); +* if ((u8)ret != bq27xxx_battery_checksum(buf)) +* ret = -EINVAL; +*/ + +if (cfgup) { +BQ27XXX_MSLEEP (1 ); + ret = di->bus.write (di,BQ27XXX_CONTROL ,BQ27XXX_SOFT_RESET , false); +if (ret <0 ) + goto out1; + } + + buf->updt = false; +return 0 ; + +out2: +if (cfgup) + di->bus.write (di,BQ27XXX_CONTROL ,BQ27XXX_SOFT_RESET , false); +out1: +dev_err (di->dev,"bus error in %s: %d\n" , __func__, ret); +return ret; +} + +static void bq27xxx_battery_set_config (struct bq27xxx_device_info *di, +struct power_supply_battery_info *info) +{ +struct bq27xxx_dm_buf bd =BQ27XXX_DM_BUF (di,BQ27XXX_DM_DESIGN_ENERGY ); +struct bq27xxx_dm_buf bt =BQ27XXX_DM_BUF (di,BQ27XXX_DM_TERMINATE_VOLTAGE ); + +if (info->charge_full_design_uah != -EINVAL + && info->energy_full_design_uwh != -EINVAL ) { +bq27xxx_battery_read_dm_block (di, &bd); +if (bd.full) { +/* assume design energy & capacity are in same block */ +bq27xxx_battery_update_dm_block (di, &bd, +BQ27XXX_DM_DESIGN_CAPACITY , + info->charge_full_design_uah /1000 ); +bq27xxx_battery_update_dm_block (di, &bd, +BQ27XXX_DM_DESIGN_ENERGY , + info->energy_full_design_uwh /1000 ); + } + } + +if (info->voltage_min_design_uv != -EINVAL ) { +bool same = bd.full && bd.class == bt.class && bd.block == bt.block; +if (!same) +bq27xxx_battery_read_dm_block (di, &bt); +if (same ? bd.full : bt.full) +bq27xxx_battery_update_dm_block (di, same ? &bd : &bt, +BQ27XXX_DM_TERMINATE_VOLTAGE , + info->voltage_min_design_uv /1000 ); + } + +if (bd.updt) +bq27xxx_battery_write_dm_block (di, &bd); +if (bt.updt) +bq27xxx_battery_write_dm_block (di, &bt); +} + +void bq27xxx_battery_settings (struct bq27xxx_device_info *di) +{ +struct power_supply_battery_info info = {}; +unsigned int min, max; + +/* functions don't exist for writing data so abort */ +if (!di->bus.write || !di->bus.write_bulk) +return ; + +/* no settings to be set for this chipset so abort */ +if (!bq27xxx_dm_regs[di->chip]) +return ; + +if (bq27xxx_battery_set_seal_state (di, false) <0 ) +return ; + +if (power_supply_get_battery_info (di->bat, &info) <0 ) + goto out; + +if (info.energy_full_design_uwh != info.charge_full_design_uah) { +if (info.energy_full_design_uwh == -EINVAL ) +dev_warn (di->dev, +"missing battery:energy-full-design-microwatt-hours\n" ); +else if (info.charge_full_design_uah == -EINVAL ) +dev_warn (di->dev, +"missing battery:charge-full-design-microamp-hours\n" ); + } + +/* assume min == 0 */ + max = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_DESIGN_ENERGY ].max; +if (info.energy_full_design_uwh > max *1000 ) { +dev_err (di->dev, +"invalid battery:energy-full-design-microwatt-hours %d\n" , + info.energy_full_design_uwh); + info.energy_full_design_uwh = -EINVAL ; + } + +/* assume min == 0 */ + max = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_DESIGN_CAPACITY ].max; +if (info.charge_full_design_uah > max *1000 ) { +dev_err (di->dev, +"invalid battery:charge-full-design-microamp-hours %d\n" , + info.charge_full_design_uah); + info.charge_full_design_uah = -EINVAL ; + } + + min = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_TERMINATE_VOLTAGE ].min; + max = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_TERMINATE_VOLTAGE ].max; +if ((info.voltage_min_design_uv < min *1000 + || info.voltage_min_design_uv > max *1000 ) + && info.voltage_min_design_uv != -EINVAL ) { +dev_err (di->dev, +"invalid battery:voltage-min-design-microvolt %d\n" , + info.voltage_min_design_uv); + info.voltage_min_design_uv = -EINVAL ; + } + +if ((info.energy_full_design_uwh == -EINVAL + || info.charge_full_design_uah == -EINVAL ) + && info.voltage_min_design_uv == -EINVAL ) + goto out; + +bq27xxx_battery_set_config (di, &info); + +out: +bq27xxx_battery_print_config (di); +bq27xxx_battery_set_seal_state (di, true); +} +/* * Return the battery State-of-Charge * Or < 0 if something fails. @@ -1006,6 +1390,13 @@ static int bq27xxx_battery_get_property(struct power_supply *psy,case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN : ret =bq27xxx_simple_value (di->charge_design_full, val);break ; +/* +* TODO: Implement these to make registers set from +* power_supply_battery_info visible in sysfs. +*/ +case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN : +case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN : +return -EINVAL ;case POWER_SUPPLY_PROP_CYCLE_COUNT : ret =bq27xxx_simple_value (di->cache.cycle_count, val);break ;@@ -1039,7 +1430,10 @@ static void bq27xxx_external_power_changed(struct power_supply *psy)int bq27xxx_battery_setup (struct bq27xxx_device_info *di) {struct power_supply_desc *psy_desc; -struct power_supply_config psy_cfg = { .drv_data = di, }; +struct power_supply_config psy_cfg = { + .of_node = di->dev->of_node, + .drv_data = di, + };INIT_DELAYED_WORK (&di->work, bq27xxx_battery_poll);mutex_init (&di->lock);@@ -1064,6 +1458,7 @@ int bq27xxx_battery_setup(struct bq27xxx_device_info *di)dev_info (di->dev,"support ver. %s enabled\n" ,DRIVER_VERSION ); +bq27xxx_battery_settings (di);bq27xxx_battery_update (di);mutex_lock (&bq27xxx_list_lock);
Re: [PATCH v7 7/9] power: bq27xxx_battery: Add power_supply_battery_info support
Quoted 46 lines · Andrew F. Davis
Quoted 45 lines · Liam Breck
Quoted 43 lines · Andrew F. Davis
Quoted 29 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.Signed-off-by Matt Ranostay <[email protected]>Signed-off-by Liam Breck <[email protected]>--- drivers/power/supply/bq27xxx_battery.c | 399 ++++++++++++++++++++++++++++++++- 1 file changed, 397 insertions(+), 2 deletions(-)drivers/power/supply/bq27xxx_battery.c +1
-1@@ -51,7 +51,7 @@#include <linux/power/bq27xxx_battery.h> -#define DRIVER_VERSION "1.2.0" +#define DRIVER_VERSION "1.3.0"#define BQ27XXX_MANUFACTURER "Texas Instruments"@@ -452,6 +452,99 @@ static struct {static DEFINE_MUTEX (bq27xxx_list_lock);drivers/power/supply/bq27xxx_battery.c static struct { · line 462
Quoted 10 lines
drivers/power/supply/bq27xxx_battery.c +8
-0static LIST_HEAD (bq27xxx_battery_devices); +/* writable registers */ +#define BQ27XXX_CONTROL 0x00 +#define BQ27XXX_DATA_CLASS 0x3E +#define BQ27XXX_DATA_BLOCK 0x3F +#define BQ27XXX_BLOCK_DATA 0x40 +#define BQ27XXX_BLOCK_DATA_CHECKSUM 0x60 +#define BQ27XXX_BLOCK_DATA_CONTROL 0x61 +These all belong in the
bq27xxx_regsarray, you can add new register types to thebq27xxx_reg_indexenum.
Quoted 6 lines
Quoted 2 lines
They're the same on all chips. Let's dup this code 9+ times when we have cause.
They are not the same on all chips, some chips don't have these registers at all. Plus,
BQ27XXX_CONTROLis the same for all chips, and we already have it in all the chipsbq27xxx_regs.
Datasheets? Do those chips even support this feature set? Bqtool doesn't appear to support any other memory update scheme.
Quoted 12 lines
Quoted 12 lines
Or make a default array of reg numbers and memcopy it onto each chip array, then alter as req'd per chip. But that's outside our scope.
Quoted 8 lines
drivers/power/supply/bq27xxx_battery.c static struct { · line 468
Quoted 6 lines
drivers/power/supply/bq27xxx_battery.c +6
-0+/* control register params */ +#define BQ27XXX_SEALED 0x20 +#define BQ27XXX_SET_CFGUPDATE 0x13 +#define BQ27XXX_SOFT_RESET 0x42 + +#define BQ27XXX_MSLEEP (i) usleep_range((i)*1000, (i)*1000+500)why?
Quoted 7 lines
Quoted 3 lines
I think this might vary on diff chips. Also I have a pending change which uses MSLEEP(20)
If they are different we should delay for the largest needed time, a range means some may not get enough delay, we would always be targeting the lowest common denominator chip anyway.
That will be a task for someone else who has hw to test :-)
Quoted 487 lines
Quoted 22 lines
Quoted 13 lines
Quoted 13 lines
drivers/power/supply/bq27xxx_battery.c +13
-0+ +struct bq27xxx_dm_reg { +u8 subclass_id; +u8 offset; +u8 bytes; +u16 min, max; +}; + +struct bq27xxx_dm_buf { +u8 class; +u8 block; +u8 a[32 ]; +bool full, updt;Quoted 8 lines
drivers/power/supply/bq27xxx_battery.c static struct { · line 486
Quoted 5 lines
drivers/power/supply/bq27xxx_battery.c +5
-0+}; + +#define BQ27XXX_DM_BUF_PTR (b, r) \ + ( (u16*) &(b).a[ (r)->offset % sizeof (b).a ] ) +This could compete in the IOCCC, there has to be a more readable way to do this..
Quoted 3 lines
Hence the macro. It didn't seem to merit a function, but I could write an inline fn.
That would be nice. With C, the whole kernel could be one long line, but just because you can doesn't mean you should, lets keep this readable for all of our sake :)
Quoted 68 lines
Quoted 59 lines
drivers/power/supply/bq27xxx_battery.c static struct { · line 496
Quoted 11 lines
drivers/power/supply/bq27xxx_battery.c +10
-0+#define BQ27XXX_DM_BUF (di, i) { \ + .class = bq27xxx_dm_regs[(di)->chip][i].subclass_id, \ + .block = bq27xxx_dm_regs[(di)->chip][i].offset / sizeof ((struct bq27xxx_dm_buf*)0)->a, \ +} + +static inline bool bq27xxx_dm_buf_set (struct bq27xxx_dm_buf *buf, +struct bq27xxx_dm_reg *reg) { +if (buf->class == reg->subclass_id + && buf->block == reg->offset /sizeof buf->a + && buf->full)Some alignment issues here.
drivers/power/supply/bq27xxx_battery.c static struct { · line 500
Quoted 4 lines
drivers/power/supply/bq27xxx_battery.c +4
-0+return false; + buf->class = reg->subclass_id; + buf->block = reg->offset /sizeof buf->a; + buf->full = buf->updt = false;newline before return
Quoted 38 lines
drivers/power/supply/bq27xxx_battery.c +38
-0+return true; +} + +enum bq27xxx_dm_reg_id { +BQ27XXX_DM_DESIGN_CAPACITY =0 , +BQ27XXX_DM_DESIGN_ENERGY , +BQ27XXX_DM_TERMINATE_VOLTAGE , +BQ27XXX_DM_END , +}; + +static const char * bq27xxx_dm_reg_name[] = { + [BQ27XXX_DM_DESIGN_CAPACITY ] ="design-capacity" , + [BQ27XXX_DM_DESIGN_ENERGY ] ="design-energy" , + [BQ27XXX_DM_TERMINATE_VOLTAGE ] ="terminate-voltage" , +}; + +static struct bq27xxx_dm_reg bq27425_dm_regs[] = { + [BQ27XXX_DM_DESIGN_CAPACITY ] = {82 ,12 ,2 ,0 ,32767 }, + [BQ27XXX_DM_DESIGN_ENERGY ] = {82 ,14 ,2 ,0 ,32767 }, + [BQ27XXX_DM_TERMINATE_VOLTAGE ] = {82 ,18 ,2 ,2800 ,3700 }, +}; + +static struct bq27xxx_dm_reg bq27421_dm_regs[] = {/* not tested */ + [BQ27XXX_DM_DESIGN_CAPACITY ] = {82 ,10 ,2 ,0 ,8000 }, + [BQ27XXX_DM_DESIGN_ENERGY ] = {82 ,12 ,2 ,0 ,32767 }, + [BQ27XXX_DM_TERMINATE_VOLTAGE ] = {82 ,16 ,2 ,2500 ,3700 }, +}; + +static struct bq27xxx_dm_reg bq27621_dm_regs[] = {/* not tested */ + [BQ27XXX_DM_DESIGN_CAPACITY ] = {82 ,3 ,2 ,0 ,8000 }, + [BQ27XXX_DM_DESIGN_ENERGY ] = {82 ,5 ,2 ,0 ,32767 }, + [BQ27XXX_DM_TERMINATE_VOLTAGE ] = {82 ,9 ,2 ,2500 ,3700 }, +}; + +static struct bq27xxx_dm_reg *bq27xxx_dm_regs[] = { + [BQ27421 ] = bq27421_dm_regs,/* and BQ27441 */ + [BQ27425 ] = bq27425_dm_regs, +/* [BQ27621] = */ bq27621_dm_regs,Quoted 8 lines
drivers/power/supply/bq27xxx_battery.c static struct { · line 544
Quoted 6 lines
drivers/power/supply/bq27xxx_battery.c +6
-0+}; + +static u32 bq27xxx_unseal_keys[] = { + [BQ27421 ] =0x80008000 ,/* and BQ27441 */ + [BQ27425 ] =0x04143672 , +/* [BQ27621] = */ 0x80008000 ,Why is this commented out.
Quoted 2 lines
The 621 id isn't defined yet. I didn't want to enable something I can't test.
Yes, but you are only commenting out the index, it still is added to the list. You should wait to add that line when the rest of BQ27621 support is added.
Quoted 37 lines
Quoted 28 lines
Quoted 28 lines
drivers/power/supply/bq27xxx_battery.c +21
-0+}; + +static int poll_interval_param_set (const char *val,const struct kernel_param *kp) {struct bq27xxx_device_info *di;@@ -496,6 +589,297 @@ static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index,return di->bus.read (di, di->regs[reg_index], single); } +static int bq27xxx_battery_set_seal_state (struct bq27xxx_device_info *di, +bool state) +{ +u32 key = bq27xxx_unseal_keys[di->chip]; +int ret; + +if (state) { + ret = di->bus.write (di,BQ27XXX_CONTROL ,BQ27XXX_SEALED , false); +if (ret <0 ) + goto out; + }else { + ret = di->bus.write (di,BQ27XXX_CONTROL , (u16)(key >>16 ), false); +if (ret <0 ) + goto out; + + ret = di->bus.write (di,BQ27XXX_CONTROL , (u16 )key, false); +if (ret <0 ) + goto out;Quoted 8 lines
drivers/power/supply/bq27xxx_battery.c static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index, · line 614
Quoted 5 lines
drivers/power/supply/bq27xxx_battery.c +5
-0+ } +return 0 ; + +out: +dev_err (di->dev,"bus error in %s: %d\n" , __func__, ret);This is not a debug statement, we don't need the function name, just say where it broke.
Quoted 2 lines
What's "where" if not fn name?
For example:
"Bus error while setting seal state"
The actually function name is unimportant, and the user doesn't care.
Quoted 100 lines
Quoted 91 lines
drivers/power/supply/bq27xxx_battery.c static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index, · line 623
Quoted 9 lines
drivers/power/supply/bq27xxx_battery.c +9
-0+return ret; +} + +static u8 bq27xxx_battery_checksum (struct bq27xxx_dm_buf *buf) +{ +u16 sum =0 ; +int i; + +for (i =0 ; i <sizeof buf->a; i++)Linus would like a word with you:
https://lkml.org/lkml/2012/7/11/103Quoted 78 lines
drivers/power/supply/bq27xxx_battery.c +78
-0+ sum += buf->a[i]; + sum &=0xff ; + +return 0xff - sum; +} + +static int bq27xxx_battery_read_dm_block (struct bq27xxx_device_info *di, +struct bq27xxx_dm_buf *buf) +{ +int ret; + + ret = di->bus.write (di,BQ27XXX_DATA_CLASS , buf->class, true); +if (ret <0 ) + goto out; + + ret = di->bus.write (di,BQ27XXX_DATA_BLOCK , buf->block, true); +if (ret <0 ) + goto out; + +BQ27XXX_MSLEEP (1 ); + + ret = di->bus.read_bulk (di,BQ27XXX_BLOCK_DATA , buf->a,sizeof buf->a); +if (ret <0 ) + goto out; + + ret = di->bus.read (di,BQ27XXX_BLOCK_DATA_CHECKSUM , true); +if (ret <0 ) + goto out; + +if ((u8 )ret !=bq27xxx_battery_checksum (buf)) { + ret = -EINVAL ; + goto out; + } + + buf->full = true; + buf->updt = false; +return 0 ; + +out: +dev_err (di->dev,"bus error in %s: %d\n" , __func__, ret); +return ret; +} + +static void bq27xxx_battery_print_config (struct bq27xxx_device_info *di) +{ +struct bq27xxx_dm_reg *reg = bq27xxx_dm_regs[di->chip]; +struct bq27xxx_dm_buf buf = { .class =0xFF }; +int i; + +for (i =0 ; i <BQ27XXX_DM_END ; i++, reg++) { +const char * str = bq27xxx_dm_reg_name[i]; + +if (bq27xxx_dm_buf_set (&buf, reg)) +if (bq27xxx_battery_read_dm_block (di, &buf) <0 ) +continue ; + +if (reg->bytes ==2 ) +dev_info (di->dev,"config register %s is %d\n" , str, +be16_to_cpup (BQ27XXX_DM_BUF_PTR (buf, reg))); +else +dev_warn (di->dev,"unsupported config register %s\n" , str); + } +} + +static void bq27xxx_battery_update_dm_block (struct bq27xxx_device_info *di, +struct bq27xxx_dm_buf *buf, +enum bq27xxx_dm_reg_id reg_id, +unsigned int val) +{ +struct bq27xxx_dm_reg *reg = &bq27xxx_dm_regs[di->chip][reg_id]; +u16 *prev; + +if (reg->bytes !=2 ) +return ; + + prev =BQ27XXX_DM_BUF_PTR (*buf, reg); +if (be16_to_cpup (prev) == val) +return ;Quoted 8 lines
drivers/power/supply/bq27xxx_battery.c static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index, · line 706
Quoted 5 lines
drivers/power/supply/bq27xxx_battery.c +5
-0+ *prev =cpu_to_be16 (val); + +dev_info (di->dev,"update chip %d, class %u, block %u, offset %u, value %u\n" , + di->chip, buf->class, buf->block, reg->offset, val); +This should probably be
dev_debug, no normal user cares about all this detail.Quoted 4 lines
It's only on change to NVM, which a hw developer (like me) wants to know about. I plan to remove the
print_configroutine so this is not redundant.HW developers should then have debug enabled for drivers they care about. For everyone else this is just kernel log clutter.
Quoted 45 lines
Quoted 36 lines
Quoted 36 lines
drivers/power/supply/bq27xxx_battery.c +36
-0+ buf->updt = true; +return ; +} + +static int bq27xxx_battery_write_dm_block (struct bq27xxx_device_info *di, +struct bq27xxx_dm_buf *buf) +{ +bool cfgup = di->chip ==BQ27425 || di->chip ==BQ27421 ;/* || BQ27441 || BQ27621 */ +int ret; + +if (cfgup) { + ret = di->bus.write (di,BQ27XXX_CONTROL ,BQ27XXX_SET_CFGUPDATE , false); +if (ret <0 ) + goto out1; + } + + ret = di->bus.write (di,BQ27XXX_BLOCK_DATA_CONTROL ,0 , true); +if (ret <0 ) + goto out2; + + ret = di->bus.write (di,BQ27XXX_DATA_CLASS , buf->class, true); +if (ret <0 ) + goto out2; + + ret = di->bus.write (di,BQ27XXX_DATA_BLOCK , buf->block, true); +if (ret <0 ) + goto out2; + +BQ27XXX_MSLEEP (1 ); + + ret = di->bus.write_bulk (di,BQ27XXX_BLOCK_DATA , buf->a,sizeof buf->a); +if (ret <0 ) + goto out2; + + ret = di->bus.write (di,BQ27XXX_BLOCK_DATA_CHECKSUM , +bq27xxx_battery_checksum (buf), true);Quoted 8 lines
drivers/power/supply/bq27xxx_battery.c static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index, · line 746
Quoted 4 lines
drivers/power/supply/bq27xxx_battery.c +4
-0+if (ret <0 ) + goto out2; + +/* THE FOLLOWING CODE IS TOXIC. DO NOT USE! Then why include it, what is this code supposed to do, and what do we lose without it? If it is really needed we can find a way to guarantee the delay length.
Quoted 5 lines
This triggers a chip bug, and future devs need to know to avoid it. I was tempted by bqtool into using it for the extra error check, and now my gauge isn't working, tho I have only reset one NVM block back to factory defaults. Bqtool is easy to find, if poorly written.
Just the warning would be enough then, we don't gain anything by writing out the exact code to break your device in the comment also.
Quoted 174 lines
Quoted 174 lines
Quoted 174 lines
drivers/power/supply/bq27xxx_battery.c +148
-1+* If the 350ms delay is insufficient, NVM corruption results +* on the '425 chip, which could damage the chip. +* It was suggested in this TI tool: +* http://git.ti.com/bms-linux/bqtool/blobs/master/gauge.c#line328 +* +* BQ27XXX_MSLEEP(350); +* ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, buf->block, true); +* BQ27XXX_MSLEEP(1); +* ret = di->bus.read(di, BQ27XXX_BLOCK_DATA_CHECKSUM, true); +* if ((u8)ret != bq27xxx_battery_checksum(buf)) +* ret = -EINVAL; +*/ + +if (cfgup) { +BQ27XXX_MSLEEP (1 ); + ret = di->bus.write (di,BQ27XXX_CONTROL ,BQ27XXX_SOFT_RESET , false); +if (ret <0 ) + goto out1; + } + + buf->updt = false; +return 0 ; + +out2: +if (cfgup) + di->bus.write (di,BQ27XXX_CONTROL ,BQ27XXX_SOFT_RESET , false); +out1: +dev_err (di->dev,"bus error in %s: %d\n" , __func__, ret); +return ret; +} + +static void bq27xxx_battery_set_config (struct bq27xxx_device_info *di, +struct power_supply_battery_info *info) +{ +struct bq27xxx_dm_buf bd =BQ27XXX_DM_BUF (di,BQ27XXX_DM_DESIGN_ENERGY ); +struct bq27xxx_dm_buf bt =BQ27XXX_DM_BUF (di,BQ27XXX_DM_TERMINATE_VOLTAGE ); + +if (info->charge_full_design_uah != -EINVAL + && info->energy_full_design_uwh != -EINVAL ) { +bq27xxx_battery_read_dm_block (di, &bd); +if (bd.full) { +/* assume design energy & capacity are in same block */ +bq27xxx_battery_update_dm_block (di, &bd, +BQ27XXX_DM_DESIGN_CAPACITY , + info->charge_full_design_uah /1000 ); +bq27xxx_battery_update_dm_block (di, &bd, +BQ27XXX_DM_DESIGN_ENERGY , + info->energy_full_design_uwh /1000 ); + } + } + +if (info->voltage_min_design_uv != -EINVAL ) { +bool same = bd.full && bd.class == bt.class && bd.block == bt.block; +if (!same) +bq27xxx_battery_read_dm_block (di, &bt); +if (same ? bd.full : bt.full) +bq27xxx_battery_update_dm_block (di, same ? &bd : &bt, +BQ27XXX_DM_TERMINATE_VOLTAGE , + info->voltage_min_design_uv /1000 ); + } + +if (bd.updt) +bq27xxx_battery_write_dm_block (di, &bd); +if (bt.updt) +bq27xxx_battery_write_dm_block (di, &bt); +} + +void bq27xxx_battery_settings (struct bq27xxx_device_info *di) +{ +struct power_supply_battery_info info = {}; +unsigned int min, max; + +/* functions don't exist for writing data so abort */ +if (!di->bus.write || !di->bus.write_bulk) +return ; + +/* no settings to be set for this chipset so abort */ +if (!bq27xxx_dm_regs[di->chip]) +return ; + +if (bq27xxx_battery_set_seal_state (di, false) <0 ) +return ; + +if (power_supply_get_battery_info (di->bat, &info) <0 ) + goto out; + +if (info.energy_full_design_uwh != info.charge_full_design_uah) { +if (info.energy_full_design_uwh == -EINVAL ) +dev_warn (di->dev, +"missing battery:energy-full-design-microwatt-hours\n" ); +else if (info.charge_full_design_uah == -EINVAL ) +dev_warn (di->dev, +"missing battery:charge-full-design-microamp-hours\n" ); + } + +/* assume min == 0 */ + max = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_DESIGN_ENERGY ].max; +if (info.energy_full_design_uwh > max *1000 ) { +dev_err (di->dev, +"invalid battery:energy-full-design-microwatt-hours %d\n" , + info.energy_full_design_uwh); + info.energy_full_design_uwh = -EINVAL ; + } + +/* assume min == 0 */ + max = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_DESIGN_CAPACITY ].max; +if (info.charge_full_design_uah > max *1000 ) { +dev_err (di->dev, +"invalid battery:charge-full-design-microamp-hours %d\n" , + info.charge_full_design_uah); + info.charge_full_design_uah = -EINVAL ; + } + + min = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_TERMINATE_VOLTAGE ].min; + max = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_TERMINATE_VOLTAGE ].max; +if ((info.voltage_min_design_uv < min *1000 + || info.voltage_min_design_uv > max *1000 ) + && info.voltage_min_design_uv != -EINVAL ) { +dev_err (di->dev, +"invalid battery:voltage-min-design-microvolt %d\n" , + info.voltage_min_design_uv); + info.voltage_min_design_uv = -EINVAL ; + } + +if ((info.energy_full_design_uwh == -EINVAL + || info.charge_full_design_uah == -EINVAL ) + && info.voltage_min_design_uv == -EINVAL ) + goto out; + +bq27xxx_battery_set_config (di, &info); + +out: +bq27xxx_battery_print_config (di); +bq27xxx_battery_set_seal_state (di, true); +} +/* * Return the battery State-of-Charge * Or < 0 if something fails. @@ -1006,6 +1390,13 @@ static int bq27xxx_battery_get_property(struct power_supply *psy,case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN : ret =bq27xxx_simple_value (di->charge_design_full, val);break ; +/* +* TODO: Implement these to make registers set from +* power_supply_battery_info visible in sysfs. +*/ +case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN : +case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN : +return -EINVAL ;case POWER_SUPPLY_PROP_CYCLE_COUNT : ret =bq27xxx_simple_value (di->cache.cycle_count, val);break ;@@ -1039,7 +1430,10 @@ static void bq27xxx_external_power_changed(struct power_supply *psy)int bq27xxx_battery_setup (struct bq27xxx_device_info *di) {struct power_supply_desc *psy_desc; -struct power_supply_config psy_cfg = { .drv_data = di, }; +struct power_supply_config psy_cfg = { + .of_node = di->dev->of_node, + .drv_data = di, + };INIT_DELAYED_WORK (&di->work, bq27xxx_battery_poll);mutex_init (&di->lock);@@ -1064,6 +1458,7 @@ int bq27xxx_battery_setup(struct bq27xxx_device_info *di)dev_info (di->dev,"support ver. %s enabled\n" ,DRIVER_VERSION ); +bq27xxx_battery_settings (di);bq27xxx_battery_update (di);mutex_lock (&bq27xxx_list_lock);
Re: [PATCH v7 7/9] power: bq27xxx_battery: Add power_supply_battery_info support
Quoted 50 lines · Liam Breck
Quoted 49 lines · Andrew F. Davis
Quoted 47 lines · Liam Breck
Quoted 32 lines · Andrew F. Davis
Quoted 31 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.Signed-off-by Matt Ranostay <[email protected]>Signed-off-by Liam Breck <[email protected]>--- drivers/power/supply/bq27xxx_battery.c | 399 ++++++++++++++++++++++++++++++++- 1 file changed, 397 insertions(+), 2 deletions(-)drivers/power/supply/bq27xxx_battery.c +1
-1@@ -51,7 +51,7 @@#include <linux/power/bq27xxx_battery.h> -#define DRIVER_VERSION "1.2.0" +#define DRIVER_VERSION "1.3.0"#define BQ27XXX_MANUFACTURER "Texas Instruments"@@ -452,6 +452,99 @@ static struct {static DEFINE_MUTEX (bq27xxx_list_lock);static LIST_HEAD (bq27xxx_battery_devices);Quoted 11 lines
drivers/power/supply/bq27xxx_battery.c static struct { · line 462
Quoted 8 lines
drivers/power/supply/bq27xxx_battery.c +8
-0+/* writable registers */ +#define BQ27XXX_CONTROL 0x00 +#define BQ27XXX_DATA_CLASS 0x3E +#define BQ27XXX_DATA_BLOCK 0x3F +#define BQ27XXX_BLOCK_DATA 0x40 +#define BQ27XXX_BLOCK_DATA_CHECKSUM 0x60 +#define BQ27XXX_BLOCK_DATA_CONTROL 0x61 +These all belong in the
bq27xxx_regsarray, you can add new register types to thebq27xxx_reg_indexenum.They're the same on all chips. Let's dup this code 9+ times when we have cause.
Quoted 7 lines
Quoted 3 lines
They are not the same on all chips, some chips don't have these registers at all. Plus,
BQ27XXX_CONTROLis the same for all chips, and we already have it in all the chipsbq27xxx_regs.Datasheets? Do those chips even support this feature set? Bqtool doesn't appear to support any other memory update scheme.
Many chips don't, which is why we don't want these registers being global defines, you can have the chips without these registers as INVALID_REG_ADDR
Quoted 16 lines
Quoted 16 lines
Quoted 15 lines
Or make a default array of reg numbers and memcopy it onto each chip array, then alter as req'd per chip. But that's outside our scope.
Quoted 8 lines
drivers/power/supply/bq27xxx_battery.c static struct { · line 468
Quoted 6 lines
drivers/power/supply/bq27xxx_battery.c +6
-0+/* control register params */ +#define BQ27XXX_SEALED 0x20 +#define BQ27XXX_SET_CFGUPDATE 0x13 +#define BQ27XXX_SOFT_RESET 0x42 + +#define BQ27XXX_MSLEEP (i) usleep_range((i)*1000, (i)*1000+500)why?
I think this might vary on diff chips. Also I have a pending change which uses MSLEEP(20)
Quoted 6 lines
Quoted 3 lines
If they are different we should delay for the largest needed time, a range means some may not get enough delay, we would always be targeting the lowest common denominator chip anyway.
That will be a task for someone else who has hw to test :-)
Right, so pick a delay that works on the device you are adding, as other devices are added they can be tested and new values can be chosen.
Quoted 487 lines
Quoted 487 lines
Quoted 22 lines
Quoted 13 lines
Quoted 13 lines
drivers/power/supply/bq27xxx_battery.c +13
-0+ +struct bq27xxx_dm_reg { +u8 subclass_id; +u8 offset; +u8 bytes; +u16 min, max; +}; + +struct bq27xxx_dm_buf { +u8 class; +u8 block; +u8 a[32 ]; +bool full, updt;Quoted 8 lines
drivers/power/supply/bq27xxx_battery.c static struct { · line 486
Quoted 5 lines
drivers/power/supply/bq27xxx_battery.c +5
-0+}; + +#define BQ27XXX_DM_BUF_PTR (b, r) \ + ( (u16*) &(b).a[ (r)->offset % sizeof (b).a ] ) +This could compete in the IOCCC, there has to be a more readable way to do this..
Quoted 3 lines
Hence the macro. It didn't seem to merit a function, but I could write an inline fn.
That would be nice. With C, the whole kernel could be one long line, but just because you can doesn't mean you should, lets keep this readable for all of our sake :)
Quoted 68 lines
Quoted 59 lines
drivers/power/supply/bq27xxx_battery.c static struct { · line 496
Quoted 11 lines
drivers/power/supply/bq27xxx_battery.c +10
-0+#define BQ27XXX_DM_BUF (di, i) { \ + .class = bq27xxx_dm_regs[(di)->chip][i].subclass_id, \ + .block = bq27xxx_dm_regs[(di)->chip][i].offset / sizeof ((struct bq27xxx_dm_buf*)0)->a, \ +} + +static inline bool bq27xxx_dm_buf_set (struct bq27xxx_dm_buf *buf, +struct bq27xxx_dm_reg *reg) { +if (buf->class == reg->subclass_id + && buf->block == reg->offset /sizeof buf->a + && buf->full)Some alignment issues here.
drivers/power/supply/bq27xxx_battery.c static struct { · line 500
Quoted 4 lines
drivers/power/supply/bq27xxx_battery.c +4
-0+return false; + buf->class = reg->subclass_id; + buf->block = reg->offset /sizeof buf->a; + buf->full = buf->updt = false;newline before return
Quoted 38 lines
drivers/power/supply/bq27xxx_battery.c +38
-0+return true; +} + +enum bq27xxx_dm_reg_id { +BQ27XXX_DM_DESIGN_CAPACITY =0 , +BQ27XXX_DM_DESIGN_ENERGY , +BQ27XXX_DM_TERMINATE_VOLTAGE , +BQ27XXX_DM_END , +}; + +static const char * bq27xxx_dm_reg_name[] = { + [BQ27XXX_DM_DESIGN_CAPACITY ] ="design-capacity" , + [BQ27XXX_DM_DESIGN_ENERGY ] ="design-energy" , + [BQ27XXX_DM_TERMINATE_VOLTAGE ] ="terminate-voltage" , +}; + +static struct bq27xxx_dm_reg bq27425_dm_regs[] = { + [BQ27XXX_DM_DESIGN_CAPACITY ] = {82 ,12 ,2 ,0 ,32767 }, + [BQ27XXX_DM_DESIGN_ENERGY ] = {82 ,14 ,2 ,0 ,32767 }, + [BQ27XXX_DM_TERMINATE_VOLTAGE ] = {82 ,18 ,2 ,2800 ,3700 }, +}; + +static struct bq27xxx_dm_reg bq27421_dm_regs[] = {/* not tested */ + [BQ27XXX_DM_DESIGN_CAPACITY ] = {82 ,10 ,2 ,0 ,8000 }, + [BQ27XXX_DM_DESIGN_ENERGY ] = {82 ,12 ,2 ,0 ,32767 }, + [BQ27XXX_DM_TERMINATE_VOLTAGE ] = {82 ,16 ,2 ,2500 ,3700 }, +}; + +static struct bq27xxx_dm_reg bq27621_dm_regs[] = {/* not tested */ + [BQ27XXX_DM_DESIGN_CAPACITY ] = {82 ,3 ,2 ,0 ,8000 }, + [BQ27XXX_DM_DESIGN_ENERGY ] = {82 ,5 ,2 ,0 ,32767 }, + [BQ27XXX_DM_TERMINATE_VOLTAGE ] = {82 ,9 ,2 ,2500 ,3700 }, +}; + +static struct bq27xxx_dm_reg *bq27xxx_dm_regs[] = { + [BQ27421 ] = bq27421_dm_regs,/* and BQ27441 */ + [BQ27425 ] = bq27425_dm_regs, +/* [BQ27621] = */ bq27621_dm_regs,Quoted 8 lines
drivers/power/supply/bq27xxx_battery.c static struct { · line 544
Quoted 6 lines
drivers/power/supply/bq27xxx_battery.c +6
-0+}; + +static u32 bq27xxx_unseal_keys[] = { + [BQ27421 ] =0x80008000 ,/* and BQ27441 */ + [BQ27425 ] =0x04143672 , +/* [BQ27621] = */ 0x80008000 ,Why is this commented out.
Quoted 2 lines
The 621 id isn't defined yet. I didn't want to enable something I can't test.
Yes, but you are only commenting out the index, it still is added to the list. You should wait to add that line when the rest of BQ27621 support is added.
Quoted 37 lines
Quoted 28 lines
Quoted 28 lines
drivers/power/supply/bq27xxx_battery.c +21
-0+}; + +static int poll_interval_param_set (const char *val,const struct kernel_param *kp) {struct bq27xxx_device_info *di;@@ -496,6 +589,297 @@ static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index,return di->bus.read (di, di->regs[reg_index], single); } +static int bq27xxx_battery_set_seal_state (struct bq27xxx_device_info *di, +bool state) +{ +u32 key = bq27xxx_unseal_keys[di->chip]; +int ret; + +if (state) { + ret = di->bus.write (di,BQ27XXX_CONTROL ,BQ27XXX_SEALED , false); +if (ret <0 ) + goto out; + }else { + ret = di->bus.write (di,BQ27XXX_CONTROL , (u16)(key >>16 ), false); +if (ret <0 ) + goto out; + + ret = di->bus.write (di,BQ27XXX_CONTROL , (u16 )key, false); +if (ret <0 ) + goto out;Quoted 8 lines
drivers/power/supply/bq27xxx_battery.c static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index, · line 614
Quoted 5 lines
drivers/power/supply/bq27xxx_battery.c +5
-0+ } +return 0 ; + +out: +dev_err (di->dev,"bus error in %s: %d\n" , __func__, ret);This is not a debug statement, we don't need the function name, just say where it broke.
Quoted 2 lines
What's "where" if not fn name?
For example:
"Bus error while setting seal state"
The actually function name is unimportant, and the user doesn't care.
Quoted 100 lines
Quoted 91 lines
drivers/power/supply/bq27xxx_battery.c static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index, · line 623
Quoted 9 lines
drivers/power/supply/bq27xxx_battery.c +9
-0+return ret; +} + +static u8 bq27xxx_battery_checksum (struct bq27xxx_dm_buf *buf) +{ +u16 sum =0 ; +int i; + +for (i =0 ; i <sizeof buf->a; i++)Linus would like a word with you:
https://lkml.org/lkml/2012/7/11/103Quoted 78 lines
drivers/power/supply/bq27xxx_battery.c +78
-0+ sum += buf->a[i]; + sum &=0xff ; + +return 0xff - sum; +} + +static int bq27xxx_battery_read_dm_block (struct bq27xxx_device_info *di, +struct bq27xxx_dm_buf *buf) +{ +int ret; + + ret = di->bus.write (di,BQ27XXX_DATA_CLASS , buf->class, true); +if (ret <0 ) + goto out; + + ret = di->bus.write (di,BQ27XXX_DATA_BLOCK , buf->block, true); +if (ret <0 ) + goto out; + +BQ27XXX_MSLEEP (1 ); + + ret = di->bus.read_bulk (di,BQ27XXX_BLOCK_DATA , buf->a,sizeof buf->a); +if (ret <0 ) + goto out; + + ret = di->bus.read (di,BQ27XXX_BLOCK_DATA_CHECKSUM , true); +if (ret <0 ) + goto out; + +if ((u8 )ret !=bq27xxx_battery_checksum (buf)) { + ret = -EINVAL ; + goto out; + } + + buf->full = true; + buf->updt = false; +return 0 ; + +out: +dev_err (di->dev,"bus error in %s: %d\n" , __func__, ret); +return ret; +} + +static void bq27xxx_battery_print_config (struct bq27xxx_device_info *di) +{ +struct bq27xxx_dm_reg *reg = bq27xxx_dm_regs[di->chip]; +struct bq27xxx_dm_buf buf = { .class =0xFF }; +int i; + +for (i =0 ; i <BQ27XXX_DM_END ; i++, reg++) { +const char * str = bq27xxx_dm_reg_name[i]; + +if (bq27xxx_dm_buf_set (&buf, reg)) +if (bq27xxx_battery_read_dm_block (di, &buf) <0 ) +continue ; + +if (reg->bytes ==2 ) +dev_info (di->dev,"config register %s is %d\n" , str, +be16_to_cpup (BQ27XXX_DM_BUF_PTR (buf, reg))); +else +dev_warn (di->dev,"unsupported config register %s\n" , str); + } +} + +static void bq27xxx_battery_update_dm_block (struct bq27xxx_device_info *di, +struct bq27xxx_dm_buf *buf, +enum bq27xxx_dm_reg_id reg_id, +unsigned int val) +{ +struct bq27xxx_dm_reg *reg = &bq27xxx_dm_regs[di->chip][reg_id]; +u16 *prev; + +if (reg->bytes !=2 ) +return ; + + prev =BQ27XXX_DM_BUF_PTR (*buf, reg); +if (be16_to_cpup (prev) == val) +return ;Quoted 8 lines
drivers/power/supply/bq27xxx_battery.c static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index, · line 706
Quoted 5 lines
drivers/power/supply/bq27xxx_battery.c +5
-0+ *prev =cpu_to_be16 (val); + +dev_info (di->dev,"update chip %d, class %u, block %u, offset %u, value %u\n" , + di->chip, buf->class, buf->block, reg->offset, val); +This should probably be
dev_debug, no normal user cares about all this detail.Quoted 4 lines
It's only on change to NVM, which a hw developer (like me) wants to know about. I plan to remove the
print_configroutine so this is not redundant.HW developers should then have debug enabled for drivers they care about. For everyone else this is just kernel log clutter.
Quoted 45 lines
Quoted 36 lines
Quoted 36 lines
drivers/power/supply/bq27xxx_battery.c +36
-0+ buf->updt = true; +return ; +} + +static int bq27xxx_battery_write_dm_block (struct bq27xxx_device_info *di, +struct bq27xxx_dm_buf *buf) +{ +bool cfgup = di->chip ==BQ27425 || di->chip ==BQ27421 ;/* || BQ27441 || BQ27621 */ +int ret; + +if (cfgup) { + ret = di->bus.write (di,BQ27XXX_CONTROL ,BQ27XXX_SET_CFGUPDATE , false); +if (ret <0 ) + goto out1; + } + + ret = di->bus.write (di,BQ27XXX_BLOCK_DATA_CONTROL ,0 , true); +if (ret <0 ) + goto out2; + + ret = di->bus.write (di,BQ27XXX_DATA_CLASS , buf->class, true); +if (ret <0 ) + goto out2; + + ret = di->bus.write (di,BQ27XXX_DATA_BLOCK , buf->block, true); +if (ret <0 ) + goto out2; + +BQ27XXX_MSLEEP (1 ); + + ret = di->bus.write_bulk (di,BQ27XXX_BLOCK_DATA , buf->a,sizeof buf->a); +if (ret <0 ) + goto out2; + + ret = di->bus.write (di,BQ27XXX_BLOCK_DATA_CHECKSUM , +bq27xxx_battery_checksum (buf), true);Quoted 8 lines
drivers/power/supply/bq27xxx_battery.c static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index, · line 746
Quoted 4 lines
drivers/power/supply/bq27xxx_battery.c +4
-0+if (ret <0 ) + goto out2; + +/* THE FOLLOWING CODE IS TOXIC. DO NOT USE! Then why include it, what is this code supposed to do, and what do we lose without it? If it is really needed we can find a way to guarantee the delay length.
Quoted 5 lines
This triggers a chip bug, and future devs need to know to avoid it. I was tempted by bqtool into using it for the extra error check, and now my gauge isn't working, tho I have only reset one NVM block back to factory defaults. Bqtool is easy to find, if poorly written.
Just the warning would be enough then, we don't gain anything by writing out the exact code to break your device in the comment also.
Quoted 174 lines
Quoted 174 lines
Quoted 174 lines
drivers/power/supply/bq27xxx_battery.c +148
-1+* If the 350ms delay is insufficient, NVM corruption results +* on the '425 chip, which could damage the chip. +* It was suggested in this TI tool: +* http://git.ti.com/bms-linux/bqtool/blobs/master/gauge.c#line328 +* +* BQ27XXX_MSLEEP(350); +* ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, buf->block, true); +* BQ27XXX_MSLEEP(1); +* ret = di->bus.read(di, BQ27XXX_BLOCK_DATA_CHECKSUM, true); +* if ((u8)ret != bq27xxx_battery_checksum(buf)) +* ret = -EINVAL; +*/ + +if (cfgup) { +BQ27XXX_MSLEEP (1 ); + ret = di->bus.write (di,BQ27XXX_CONTROL ,BQ27XXX_SOFT_RESET , false); +if (ret <0 ) + goto out1; + } + + buf->updt = false; +return 0 ; + +out2: +if (cfgup) + di->bus.write (di,BQ27XXX_CONTROL ,BQ27XXX_SOFT_RESET , false); +out1: +dev_err (di->dev,"bus error in %s: %d\n" , __func__, ret); +return ret; +} + +static void bq27xxx_battery_set_config (struct bq27xxx_device_info *di, +struct power_supply_battery_info *info) +{ +struct bq27xxx_dm_buf bd =BQ27XXX_DM_BUF (di,BQ27XXX_DM_DESIGN_ENERGY ); +struct bq27xxx_dm_buf bt =BQ27XXX_DM_BUF (di,BQ27XXX_DM_TERMINATE_VOLTAGE ); + +if (info->charge_full_design_uah != -EINVAL + && info->energy_full_design_uwh != -EINVAL ) { +bq27xxx_battery_read_dm_block (di, &bd); +if (bd.full) { +/* assume design energy & capacity are in same block */ +bq27xxx_battery_update_dm_block (di, &bd, +BQ27XXX_DM_DESIGN_CAPACITY , + info->charge_full_design_uah /1000 ); +bq27xxx_battery_update_dm_block (di, &bd, +BQ27XXX_DM_DESIGN_ENERGY , + info->energy_full_design_uwh /1000 ); + } + } + +if (info->voltage_min_design_uv != -EINVAL ) { +bool same = bd.full && bd.class == bt.class && bd.block == bt.block; +if (!same) +bq27xxx_battery_read_dm_block (di, &bt); +if (same ? bd.full : bt.full) +bq27xxx_battery_update_dm_block (di, same ? &bd : &bt, +BQ27XXX_DM_TERMINATE_VOLTAGE , + info->voltage_min_design_uv /1000 ); + } + +if (bd.updt) +bq27xxx_battery_write_dm_block (di, &bd); +if (bt.updt) +bq27xxx_battery_write_dm_block (di, &bt); +} + +void bq27xxx_battery_settings (struct bq27xxx_device_info *di) +{ +struct power_supply_battery_info info = {}; +unsigned int min, max; + +/* functions don't exist for writing data so abort */ +if (!di->bus.write || !di->bus.write_bulk) +return ; + +/* no settings to be set for this chipset so abort */ +if (!bq27xxx_dm_regs[di->chip]) +return ; + +if (bq27xxx_battery_set_seal_state (di, false) <0 ) +return ; + +if (power_supply_get_battery_info (di->bat, &info) <0 ) + goto out; + +if (info.energy_full_design_uwh != info.charge_full_design_uah) { +if (info.energy_full_design_uwh == -EINVAL ) +dev_warn (di->dev, +"missing battery:energy-full-design-microwatt-hours\n" ); +else if (info.charge_full_design_uah == -EINVAL ) +dev_warn (di->dev, +"missing battery:charge-full-design-microamp-hours\n" ); + } + +/* assume min == 0 */ + max = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_DESIGN_ENERGY ].max; +if (info.energy_full_design_uwh > max *1000 ) { +dev_err (di->dev, +"invalid battery:energy-full-design-microwatt-hours %d\n" , + info.energy_full_design_uwh); + info.energy_full_design_uwh = -EINVAL ; + } + +/* assume min == 0 */ + max = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_DESIGN_CAPACITY ].max; +if (info.charge_full_design_uah > max *1000 ) { +dev_err (di->dev, +"invalid battery:charge-full-design-microamp-hours %d\n" , + info.charge_full_design_uah); + info.charge_full_design_uah = -EINVAL ; + } + + min = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_TERMINATE_VOLTAGE ].min; + max = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_TERMINATE_VOLTAGE ].max; +if ((info.voltage_min_design_uv < min *1000 + || info.voltage_min_design_uv > max *1000 ) + && info.voltage_min_design_uv != -EINVAL ) { +dev_err (di->dev, +"invalid battery:voltage-min-design-microvolt %d\n" , + info.voltage_min_design_uv); + info.voltage_min_design_uv = -EINVAL ; + } + +if ((info.energy_full_design_uwh == -EINVAL + || info.charge_full_design_uah == -EINVAL ) + && info.voltage_min_design_uv == -EINVAL ) + goto out; + +bq27xxx_battery_set_config (di, &info); + +out: +bq27xxx_battery_print_config (di); +bq27xxx_battery_set_seal_state (di, true); +} +/* * Return the battery State-of-Charge * Or < 0 if something fails. @@ -1006,6 +1390,13 @@ static int bq27xxx_battery_get_property(struct power_supply *psy,case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN : ret =bq27xxx_simple_value (di->charge_design_full, val);break ; +/* +* TODO: Implement these to make registers set from +* power_supply_battery_info visible in sysfs. +*/ +case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN : +case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN : +return -EINVAL ;case POWER_SUPPLY_PROP_CYCLE_COUNT : ret =bq27xxx_simple_value (di->cache.cycle_count, val);break ;@@ -1039,7 +1430,10 @@ static void bq27xxx_external_power_changed(struct power_supply *psy)int bq27xxx_battery_setup (struct bq27xxx_device_info *di) {struct power_supply_desc *psy_desc; -struct power_supply_config psy_cfg = { .drv_data = di, }; +struct power_supply_config psy_cfg = { + .of_node = di->dev->of_node, + .drv_data = di, + };INIT_DELAYED_WORK (&di->work, bq27xxx_battery_poll);mutex_init (&di->lock);@@ -1064,6 +1458,7 @@ int bq27xxx_battery_setup(struct bq27xxx_device_info *di)dev_info (di->dev,"support ver. %s enabled\n" ,DRIVER_VERSION ); +bq27xxx_battery_settings (di);bq27xxx_battery_update (di);mutex_lock (&bq27xxx_list_lock);
Re: [PATCH v7 7/9] power: bq27xxx_battery: Add power_supply_battery_info support
Quoted 55 lines · Andrew F. Davis
Quoted 54 lines · Liam Breck
Quoted 52 lines · Andrew F. Davis
Quoted 45 lines · Liam Breck
Quoted 32 lines · Andrew F. Davis
Quoted 31 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.Signed-off-by Matt Ranostay <[email protected]>Signed-off-by Liam Breck <[email protected]>--- drivers/power/supply/bq27xxx_battery.c | 399 ++++++++++++++++++++++++++++++++- 1 file changed, 397 insertions(+), 2 deletions(-)drivers/power/supply/bq27xxx_battery.c +1
-1@@ -51,7 +51,7 @@#include <linux/power/bq27xxx_battery.h> -#define DRIVER_VERSION "1.2.0" +#define DRIVER_VERSION "1.3.0"#define BQ27XXX_MANUFACTURER "Texas Instruments"@@ -452,6 +452,99 @@ static struct {static DEFINE_MUTEX (bq27xxx_list_lock);static LIST_HEAD (bq27xxx_battery_devices);Quoted 11 lines
drivers/power/supply/bq27xxx_battery.c static struct { · line 462
Quoted 8 lines
drivers/power/supply/bq27xxx_battery.c +8
-0+/* writable registers */ +#define BQ27XXX_CONTROL 0x00 +#define BQ27XXX_DATA_CLASS 0x3E +#define BQ27XXX_DATA_BLOCK 0x3F +#define BQ27XXX_BLOCK_DATA 0x40 +#define BQ27XXX_BLOCK_DATA_CHECKSUM 0x60 +#define BQ27XXX_BLOCK_DATA_CONTROL 0x61 +These all belong in the
bq27xxx_regsarray, you can add new register types to thebq27xxx_reg_indexenum.Quoted 2 lines
They're the same on all chips. Let's dup this code 9+ times when we have cause.
They are not the same on all chips, some chips don't have these registers at all. Plus,
BQ27XXX_CONTROLis the same for all chips, and we already have it in all the chipsbq27xxx_regs.
Quoted 7 lines
Quoted 3 lines
Datasheets? Do those chips even support this feature set? Bqtool doesn't appear to support any other memory update scheme.
Many chips don't, which is why we don't want these registers being global defines, you can have the chips without these registers as
INVALID_REG_ADDR
Chips that don't support memory update won't have a bq27xxx_dm_regs list; we look for that at the start of this code path and do nothing if it's null for di->chip. All chips that do support memory update use the same command registers, so there's no reason to dup those.
Also you can delete REG_CTRL as it's not used by the existing code :-)
Quoted 513 lines
Quoted 16 lines
Quoted 16 lines
Quoted 15 lines
Or make a default array of reg numbers and memcopy it onto each chip array, then alter as req'd per chip. But that's outside our scope.
Quoted 8 lines
drivers/power/supply/bq27xxx_battery.c static struct { · line 468
Quoted 6 lines
drivers/power/supply/bq27xxx_battery.c +6
-0+/* control register params */ +#define BQ27XXX_SEALED 0x20 +#define BQ27XXX_SET_CFGUPDATE 0x13 +#define BQ27XXX_SOFT_RESET 0x42 + +#define BQ27XXX_MSLEEP (i) usleep_range((i)*1000, (i)*1000+500)why?
I think this might vary on diff chips. Also I have a pending change which uses MSLEEP(20)
Quoted 6 lines
Quoted 3 lines
If they are different we should delay for the largest needed time, a range means some may not get enough delay, we would always be targeting the lowest common denominator chip anyway.
That will be a task for someone else who has hw to test :-)
Right, so pick a delay that works on the device you are adding, as other devices are added they can be tested and new values can be chosen.
Quoted 487 lines
Quoted 487 lines
Quoted 22 lines
Quoted 13 lines
Quoted 13 lines
drivers/power/supply/bq27xxx_battery.c +13
-0+ +struct bq27xxx_dm_reg { +u8 subclass_id; +u8 offset; +u8 bytes; +u16 min, max; +}; + +struct bq27xxx_dm_buf { +u8 class; +u8 block; +u8 a[32 ]; +bool full, updt;Quoted 8 lines
drivers/power/supply/bq27xxx_battery.c static struct { · line 486
Quoted 5 lines
drivers/power/supply/bq27xxx_battery.c +5
-0+}; + +#define BQ27XXX_DM_BUF_PTR (b, r) \ + ( (u16*) &(b).a[ (r)->offset % sizeof (b).a ] ) +This could compete in the IOCCC, there has to be a more readable way to do this..
Quoted 3 lines
Hence the macro. It didn't seem to merit a function, but I could write an inline fn.
That would be nice. With C, the whole kernel could be one long line, but just because you can doesn't mean you should, lets keep this readable for all of our sake :)
Quoted 68 lines
Quoted 59 lines
drivers/power/supply/bq27xxx_battery.c static struct { · line 496
Quoted 11 lines
drivers/power/supply/bq27xxx_battery.c +10
-0+#define BQ27XXX_DM_BUF (di, i) { \ + .class = bq27xxx_dm_regs[(di)->chip][i].subclass_id, \ + .block = bq27xxx_dm_regs[(di)->chip][i].offset / sizeof ((struct bq27xxx_dm_buf*)0)->a, \ +} + +static inline bool bq27xxx_dm_buf_set (struct bq27xxx_dm_buf *buf, +struct bq27xxx_dm_reg *reg) { +if (buf->class == reg->subclass_id + && buf->block == reg->offset /sizeof buf->a + && buf->full)Some alignment issues here.
drivers/power/supply/bq27xxx_battery.c static struct { · line 500
Quoted 4 lines
drivers/power/supply/bq27xxx_battery.c +4
-0+return false; + buf->class = reg->subclass_id; + buf->block = reg->offset /sizeof buf->a; + buf->full = buf->updt = false;newline before return
Quoted 38 lines
drivers/power/supply/bq27xxx_battery.c +38
-0+return true; +} + +enum bq27xxx_dm_reg_id { +BQ27XXX_DM_DESIGN_CAPACITY =0 , +BQ27XXX_DM_DESIGN_ENERGY , +BQ27XXX_DM_TERMINATE_VOLTAGE , +BQ27XXX_DM_END , +}; + +static const char * bq27xxx_dm_reg_name[] = { + [BQ27XXX_DM_DESIGN_CAPACITY ] ="design-capacity" , + [BQ27XXX_DM_DESIGN_ENERGY ] ="design-energy" , + [BQ27XXX_DM_TERMINATE_VOLTAGE ] ="terminate-voltage" , +}; + +static struct bq27xxx_dm_reg bq27425_dm_regs[] = { + [BQ27XXX_DM_DESIGN_CAPACITY ] = {82 ,12 ,2 ,0 ,32767 }, + [BQ27XXX_DM_DESIGN_ENERGY ] = {82 ,14 ,2 ,0 ,32767 }, + [BQ27XXX_DM_TERMINATE_VOLTAGE ] = {82 ,18 ,2 ,2800 ,3700 }, +}; + +static struct bq27xxx_dm_reg bq27421_dm_regs[] = {/* not tested */ + [BQ27XXX_DM_DESIGN_CAPACITY ] = {82 ,10 ,2 ,0 ,8000 }, + [BQ27XXX_DM_DESIGN_ENERGY ] = {82 ,12 ,2 ,0 ,32767 }, + [BQ27XXX_DM_TERMINATE_VOLTAGE ] = {82 ,16 ,2 ,2500 ,3700 }, +}; + +static struct bq27xxx_dm_reg bq27621_dm_regs[] = {/* not tested */ + [BQ27XXX_DM_DESIGN_CAPACITY ] = {82 ,3 ,2 ,0 ,8000 }, + [BQ27XXX_DM_DESIGN_ENERGY ] = {82 ,5 ,2 ,0 ,32767 }, + [BQ27XXX_DM_TERMINATE_VOLTAGE ] = {82 ,9 ,2 ,2500 ,3700 }, +}; + +static struct bq27xxx_dm_reg *bq27xxx_dm_regs[] = { + [BQ27421 ] = bq27421_dm_regs,/* and BQ27441 */ + [BQ27425 ] = bq27425_dm_regs, +/* [BQ27621] = */ bq27621_dm_regs,Quoted 8 lines
drivers/power/supply/bq27xxx_battery.c static struct { · line 544
Quoted 6 lines
drivers/power/supply/bq27xxx_battery.c +6
-0+}; + +static u32 bq27xxx_unseal_keys[] = { + [BQ27421 ] =0x80008000 ,/* and BQ27441 */ + [BQ27425 ] =0x04143672 , +/* [BQ27621] = */ 0x80008000 ,Why is this commented out.
Quoted 2 lines
The 621 id isn't defined yet. I didn't want to enable something I can't test.
Yes, but you are only commenting out the index, it still is added to the list. You should wait to add that line when the rest of BQ27621 support is added.
Quoted 37 lines
Quoted 28 lines
Quoted 28 lines
drivers/power/supply/bq27xxx_battery.c +21
-0+}; + +static int poll_interval_param_set (const char *val,const struct kernel_param *kp) {struct bq27xxx_device_info *di;@@ -496,6 +589,297 @@ static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index,return di->bus.read (di, di->regs[reg_index], single); } +static int bq27xxx_battery_set_seal_state (struct bq27xxx_device_info *di, +bool state) +{ +u32 key = bq27xxx_unseal_keys[di->chip]; +int ret; + +if (state) { + ret = di->bus.write (di,BQ27XXX_CONTROL ,BQ27XXX_SEALED , false); +if (ret <0 ) + goto out; + }else { + ret = di->bus.write (di,BQ27XXX_CONTROL , (u16)(key >>16 ), false); +if (ret <0 ) + goto out; + + ret = di->bus.write (di,BQ27XXX_CONTROL , (u16 )key, false); +if (ret <0 ) + goto out;Quoted 8 lines
drivers/power/supply/bq27xxx_battery.c static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index, · line 614
Quoted 5 lines
drivers/power/supply/bq27xxx_battery.c +5
-0+ } +return 0 ; + +out: +dev_err (di->dev,"bus error in %s: %d\n" , __func__, ret);This is not a debug statement, we don't need the function name, just say where it broke.
Quoted 2 lines
What's "where" if not fn name?
For example:
"Bus error while setting seal state"
The actually function name is unimportant, and the user doesn't care.
Quoted 100 lines
Quoted 91 lines
drivers/power/supply/bq27xxx_battery.c static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index, · line 623
Quoted 9 lines
drivers/power/supply/bq27xxx_battery.c +9
-0+return ret; +} + +static u8 bq27xxx_battery_checksum (struct bq27xxx_dm_buf *buf) +{ +u16 sum =0 ; +int i; + +for (i =0 ; i <sizeof buf->a; i++)Linus would like a word with you:
https://lkml.org/lkml/2012/7/11/103Quoted 78 lines
drivers/power/supply/bq27xxx_battery.c +78
-0+ sum += buf->a[i]; + sum &=0xff ; + +return 0xff - sum; +} + +static int bq27xxx_battery_read_dm_block (struct bq27xxx_device_info *di, +struct bq27xxx_dm_buf *buf) +{ +int ret; + + ret = di->bus.write (di,BQ27XXX_DATA_CLASS , buf->class, true); +if (ret <0 ) + goto out; + + ret = di->bus.write (di,BQ27XXX_DATA_BLOCK , buf->block, true); +if (ret <0 ) + goto out; + +BQ27XXX_MSLEEP (1 ); + + ret = di->bus.read_bulk (di,BQ27XXX_BLOCK_DATA , buf->a,sizeof buf->a); +if (ret <0 ) + goto out; + + ret = di->bus.read (di,BQ27XXX_BLOCK_DATA_CHECKSUM , true); +if (ret <0 ) + goto out; + +if ((u8 )ret !=bq27xxx_battery_checksum (buf)) { + ret = -EINVAL ; + goto out; + } + + buf->full = true; + buf->updt = false; +return 0 ; + +out: +dev_err (di->dev,"bus error in %s: %d\n" , __func__, ret); +return ret; +} + +static void bq27xxx_battery_print_config (struct bq27xxx_device_info *di) +{ +struct bq27xxx_dm_reg *reg = bq27xxx_dm_regs[di->chip]; +struct bq27xxx_dm_buf buf = { .class =0xFF }; +int i; + +for (i =0 ; i <BQ27XXX_DM_END ; i++, reg++) { +const char * str = bq27xxx_dm_reg_name[i]; + +if (bq27xxx_dm_buf_set (&buf, reg)) +if (bq27xxx_battery_read_dm_block (di, &buf) <0 ) +continue ; + +if (reg->bytes ==2 ) +dev_info (di->dev,"config register %s is %d\n" , str, +be16_to_cpup (BQ27XXX_DM_BUF_PTR (buf, reg))); +else +dev_warn (di->dev,"unsupported config register %s\n" , str); + } +} + +static void bq27xxx_battery_update_dm_block (struct bq27xxx_device_info *di, +struct bq27xxx_dm_buf *buf, +enum bq27xxx_dm_reg_id reg_id, +unsigned int val) +{ +struct bq27xxx_dm_reg *reg = &bq27xxx_dm_regs[di->chip][reg_id]; +u16 *prev; + +if (reg->bytes !=2 ) +return ; + + prev =BQ27XXX_DM_BUF_PTR (*buf, reg); +if (be16_to_cpup (prev) == val) +return ;Quoted 8 lines
drivers/power/supply/bq27xxx_battery.c static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index, · line 706
Quoted 5 lines
drivers/power/supply/bq27xxx_battery.c +5
-0+ *prev =cpu_to_be16 (val); + +dev_info (di->dev,"update chip %d, class %u, block %u, offset %u, value %u\n" , + di->chip, buf->class, buf->block, reg->offset, val); +This should probably be
dev_debug, no normal user cares about all this detail.Quoted 4 lines
It's only on change to NVM, which a hw developer (like me) wants to know about. I plan to remove the
print_configroutine so this is not redundant.HW developers should then have debug enabled for drivers they care about. For everyone else this is just kernel log clutter.
Quoted 45 lines
Quoted 36 lines
Quoted 36 lines
drivers/power/supply/bq27xxx_battery.c +36
-0+ buf->updt = true; +return ; +} + +static int bq27xxx_battery_write_dm_block (struct bq27xxx_device_info *di, +struct bq27xxx_dm_buf *buf) +{ +bool cfgup = di->chip ==BQ27425 || di->chip ==BQ27421 ;/* || BQ27441 || BQ27621 */ +int ret; + +if (cfgup) { + ret = di->bus.write (di,BQ27XXX_CONTROL ,BQ27XXX_SET_CFGUPDATE , false); +if (ret <0 ) + goto out1; + } + + ret = di->bus.write (di,BQ27XXX_BLOCK_DATA_CONTROL ,0 , true); +if (ret <0 ) + goto out2; + + ret = di->bus.write (di,BQ27XXX_DATA_CLASS , buf->class, true); +if (ret <0 ) + goto out2; + + ret = di->bus.write (di,BQ27XXX_DATA_BLOCK , buf->block, true); +if (ret <0 ) + goto out2; + +BQ27XXX_MSLEEP (1 ); + + ret = di->bus.write_bulk (di,BQ27XXX_BLOCK_DATA , buf->a,sizeof buf->a); +if (ret <0 ) + goto out2; + + ret = di->bus.write (di,BQ27XXX_BLOCK_DATA_CHECKSUM , +bq27xxx_battery_checksum (buf), true);Quoted 8 lines
drivers/power/supply/bq27xxx_battery.c static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index, · line 746
Quoted 4 lines
drivers/power/supply/bq27xxx_battery.c +4
-0+if (ret <0 ) + goto out2; + +/* THE FOLLOWING CODE IS TOXIC. DO NOT USE! Then why include it, what is this code supposed to do, and what do we lose without it? If it is really needed we can find a way to guarantee the delay length.
Quoted 5 lines
This triggers a chip bug, and future devs need to know to avoid it. I was tempted by bqtool into using it for the extra error check, and now my gauge isn't working, tho I have only reset one NVM block back to factory defaults. Bqtool is easy to find, if poorly written.
Just the warning would be enough then, we don't gain anything by writing out the exact code to break your device in the comment also.
Quoted 174 lines
Quoted 174 lines
Quoted 174 lines
drivers/power/supply/bq27xxx_battery.c +148
-1+* If the 350ms delay is insufficient, NVM corruption results +* on the '425 chip, which could damage the chip. +* It was suggested in this TI tool: +* http://git.ti.com/bms-linux/bqtool/blobs/master/gauge.c#line328 +* +* BQ27XXX_MSLEEP(350); +* ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, buf->block, true); +* BQ27XXX_MSLEEP(1); +* ret = di->bus.read(di, BQ27XXX_BLOCK_DATA_CHECKSUM, true); +* if ((u8)ret != bq27xxx_battery_checksum(buf)) +* ret = -EINVAL; +*/ + +if (cfgup) { +BQ27XXX_MSLEEP (1 ); + ret = di->bus.write (di,BQ27XXX_CONTROL ,BQ27XXX_SOFT_RESET , false); +if (ret <0 ) + goto out1; + } + + buf->updt = false; +return 0 ; + +out2: +if (cfgup) + di->bus.write (di,BQ27XXX_CONTROL ,BQ27XXX_SOFT_RESET , false); +out1: +dev_err (di->dev,"bus error in %s: %d\n" , __func__, ret); +return ret; +} + +static void bq27xxx_battery_set_config (struct bq27xxx_device_info *di, +struct power_supply_battery_info *info) +{ +struct bq27xxx_dm_buf bd =BQ27XXX_DM_BUF (di,BQ27XXX_DM_DESIGN_ENERGY ); +struct bq27xxx_dm_buf bt =BQ27XXX_DM_BUF (di,BQ27XXX_DM_TERMINATE_VOLTAGE ); + +if (info->charge_full_design_uah != -EINVAL + && info->energy_full_design_uwh != -EINVAL ) { +bq27xxx_battery_read_dm_block (di, &bd); +if (bd.full) { +/* assume design energy & capacity are in same block */ +bq27xxx_battery_update_dm_block (di, &bd, +BQ27XXX_DM_DESIGN_CAPACITY , + info->charge_full_design_uah /1000 ); +bq27xxx_battery_update_dm_block (di, &bd, +BQ27XXX_DM_DESIGN_ENERGY , + info->energy_full_design_uwh /1000 ); + } + } + +if (info->voltage_min_design_uv != -EINVAL ) { +bool same = bd.full && bd.class == bt.class && bd.block == bt.block; +if (!same) +bq27xxx_battery_read_dm_block (di, &bt); +if (same ? bd.full : bt.full) +bq27xxx_battery_update_dm_block (di, same ? &bd : &bt, +BQ27XXX_DM_TERMINATE_VOLTAGE , + info->voltage_min_design_uv /1000 ); + } + +if (bd.updt) +bq27xxx_battery_write_dm_block (di, &bd); +if (bt.updt) +bq27xxx_battery_write_dm_block (di, &bt); +} + +void bq27xxx_battery_settings (struct bq27xxx_device_info *di) +{ +struct power_supply_battery_info info = {}; +unsigned int min, max; + +/* functions don't exist for writing data so abort */ +if (!di->bus.write || !di->bus.write_bulk) +return ; + +/* no settings to be set for this chipset so abort */ +if (!bq27xxx_dm_regs[di->chip]) +return ; + +if (bq27xxx_battery_set_seal_state (di, false) <0 ) +return ; + +if (power_supply_get_battery_info (di->bat, &info) <0 ) + goto out; + +if (info.energy_full_design_uwh != info.charge_full_design_uah) { +if (info.energy_full_design_uwh == -EINVAL ) +dev_warn (di->dev, +"missing battery:energy-full-design-microwatt-hours\n" ); +else if (info.charge_full_design_uah == -EINVAL ) +dev_warn (di->dev, +"missing battery:charge-full-design-microamp-hours\n" ); + } + +/* assume min == 0 */ + max = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_DESIGN_ENERGY ].max; +if (info.energy_full_design_uwh > max *1000 ) { +dev_err (di->dev, +"invalid battery:energy-full-design-microwatt-hours %d\n" , + info.energy_full_design_uwh); + info.energy_full_design_uwh = -EINVAL ; + } + +/* assume min == 0 */ + max = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_DESIGN_CAPACITY ].max; +if (info.charge_full_design_uah > max *1000 ) { +dev_err (di->dev, +"invalid battery:charge-full-design-microamp-hours %d\n" , + info.charge_full_design_uah); + info.charge_full_design_uah = -EINVAL ; + } + + min = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_TERMINATE_VOLTAGE ].min; + max = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_TERMINATE_VOLTAGE ].max; +if ((info.voltage_min_design_uv < min *1000 + || info.voltage_min_design_uv > max *1000 ) + && info.voltage_min_design_uv != -EINVAL ) { +dev_err (di->dev, +"invalid battery:voltage-min-design-microvolt %d\n" , + info.voltage_min_design_uv); + info.voltage_min_design_uv = -EINVAL ; + } + +if ((info.energy_full_design_uwh == -EINVAL + || info.charge_full_design_uah == -EINVAL ) + && info.voltage_min_design_uv == -EINVAL ) + goto out; + +bq27xxx_battery_set_config (di, &info); + +out: +bq27xxx_battery_print_config (di); +bq27xxx_battery_set_seal_state (di, true); +} +/* * Return the battery State-of-Charge * Or < 0 if something fails. @@ -1006,6 +1390,13 @@ static int bq27xxx_battery_get_property(struct power_supply *psy,case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN : ret =bq27xxx_simple_value (di->charge_design_full, val);break ; +/* +* TODO: Implement these to make registers set from +* power_supply_battery_info visible in sysfs. +*/ +case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN : +case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN : +return -EINVAL ;case POWER_SUPPLY_PROP_CYCLE_COUNT : ret =bq27xxx_simple_value (di->cache.cycle_count, val);break ;@@ -1039,7 +1430,10 @@ static void bq27xxx_external_power_changed(struct power_supply *psy)int bq27xxx_battery_setup (struct bq27xxx_device_info *di) {struct power_supply_desc *psy_desc; -struct power_supply_config psy_cfg = { .drv_data = di, }; +struct power_supply_config psy_cfg = { + .of_node = di->dev->of_node, + .drv_data = di, + };INIT_DELAYED_WORK (&di->work, bq27xxx_battery_poll);mutex_init (&di->lock);@@ -1064,6 +1458,7 @@ int bq27xxx_battery_setup(struct bq27xxx_device_info *di)dev_info (di->dev,"support ver. %s enabled\n" ,DRIVER_VERSION ); +bq27xxx_battery_settings (di);bq27xxx_battery_update (di);mutex_lock (&bq27xxx_list_lock);
Re: [PATCH v7 7/9] power: bq27xxx_battery: Add power_supply_battery_info support
Quoted 60 lines · Liam Breck
Quoted 59 lines · Andrew F. Davis
Quoted 57 lines · Liam Breck
Quoted 46 lines · Andrew F. Davis
Quoted 45 lines · Liam Breck
Quoted 43 lines · Andrew F. Davis
Quoted 29 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.Signed-off-by Matt Ranostay <[email protected]>Signed-off-by Liam Breck <[email protected]>--- drivers/power/supply/bq27xxx_battery.c | 399 ++++++++++++++++++++++++++++++++- 1 file changed, 397 insertions(+), 2 deletions(-)drivers/power/supply/bq27xxx_battery.c +1
-1@@ -51,7 +51,7 @@#include <linux/power/bq27xxx_battery.h> -#define DRIVER_VERSION "1.2.0" +#define DRIVER_VERSION "1.3.0"#define BQ27XXX_MANUFACTURER "Texas Instruments"@@ -452,6 +452,99 @@ static struct {static DEFINE_MUTEX (bq27xxx_list_lock);drivers/power/supply/bq27xxx_battery.c static struct { · line 462
Quoted 10 lines
drivers/power/supply/bq27xxx_battery.c +8
-0static LIST_HEAD (bq27xxx_battery_devices); +/* writable registers */ +#define BQ27XXX_CONTROL 0x00 +#define BQ27XXX_DATA_CLASS 0x3E +#define BQ27XXX_DATA_BLOCK 0x3F +#define BQ27XXX_BLOCK_DATA 0x40 +#define BQ27XXX_BLOCK_DATA_CHECKSUM 0x60 +#define BQ27XXX_BLOCK_DATA_CONTROL 0x61 +These all belong in the
bq27xxx_regsarray, you can add new register types to thebq27xxx_reg_indexenum.Quoted 6 lines
Quoted 2 lines
They're the same on all chips. Let's dup this code 9+ times when we have cause.
They are not the same on all chips, some chips don't have these registers at all. Plus,
BQ27XXX_CONTROLis the same for all chips, and we already have it in all the chipsbq27xxx_regs.Datasheets? Do those chips even support this feature set? Bqtool doesn't appear to support any other memory update scheme.
Quoted 9 lines
Quoted 3 lines
Many chips don't, which is why we don't want these registers being global defines, you can have the chips without these registers as
INVALID_REG_ADDRChips that don't support memory update won't have a
bq27xxx_dm_regslist; we look for that at the start of this code path and do nothing if it's null fordi->chip. All chips that do support memory update use the same command registers, so there's no reason to dup those.
Someday they might use a different value, we don't lose anything by putting these device registers in the table with the other device registers. I would rather duplicate some lines if it makes the code more uniform and easier to follow, again this is not a contest, we write code for ourselves, let the compiler organize/optimize things.
Quoted 2 lines
Also you can delete
REG_CTRLas it's not used by the existing code :-)
True, but as before, I just like having the complete register set in a nice simple table, as opposed to having to add and change things all over if/when we may need that register.
Being a static table, I wonder if a sufficiently smart compiler could see that it is not used and optimize the value out for us...
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
Or make a default array of reg numbers and memcopy it onto each chip array, then alter as req'd per chip. But that's outside our scope.
Quoted 7 lines
Quoted 6 lines
drivers/power/supply/bq27xxx_battery.c +6
-0+/* control register params */ +#define BQ27XXX_SEALED 0x20 +#define BQ27XXX_SET_CFGUPDATE 0x13 +#define BQ27XXX_SOFT_RESET 0x42 + +#define BQ27XXX_MSLEEP (i) usleep_range((i)*1000, (i)*1000+500)
Re: [PATCH v7 7/9] power: bq27xxx_battery: Add power_supply_battery_info support
Quoted 71 lines · Andrew F. Davis
Quoted 60 lines · Liam Breck
Quoted 59 lines · Andrew F. Davis
Quoted 57 lines · Liam Breck
Quoted 46 lines · Andrew F. Davis
Quoted 45 lines · Liam Breck
Quoted 43 lines · Andrew F. Davis
Quoted 29 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.Signed-off-by Matt Ranostay <[email protected]>Signed-off-by Liam Breck <[email protected]>--- drivers/power/supply/bq27xxx_battery.c | 399 ++++++++++++++++++++++++++++++++- 1 file changed, 397 insertions(+), 2 deletions(-)drivers/power/supply/bq27xxx_battery.c +1
-1@@ -51,7 +51,7 @@#include <linux/power/bq27xxx_battery.h> -#define DRIVER_VERSION "1.2.0" +#define DRIVER_VERSION "1.3.0"#define BQ27XXX_MANUFACTURER "Texas Instruments"@@ -452,6 +452,99 @@ static struct {static DEFINE_MUTEX (bq27xxx_list_lock);drivers/power/supply/bq27xxx_battery.c static struct { · line 462
Quoted 10 lines
drivers/power/supply/bq27xxx_battery.c +8
-0static LIST_HEAD (bq27xxx_battery_devices); +/* writable registers */ +#define BQ27XXX_CONTROL 0x00 +#define BQ27XXX_DATA_CLASS 0x3E +#define BQ27XXX_DATA_BLOCK 0x3F +#define BQ27XXX_BLOCK_DATA 0x40 +#define BQ27XXX_BLOCK_DATA_CHECKSUM 0x60 +#define BQ27XXX_BLOCK_DATA_CONTROL 0x61 +These all belong in the
bq27xxx_regsarray, you can add new register types to thebq27xxx_reg_indexenum.Quoted 6 lines
Quoted 2 lines
They're the same on all chips. Let's dup this code 9+ times when we have cause.
They are not the same on all chips, some chips don't have these registers at all. Plus,
BQ27XXX_CONTROLis the same for all chips, and we already have it in all the chipsbq27xxx_regs.Datasheets? Do those chips even support this feature set? Bqtool doesn't appear to support any other memory update scheme.
Quoted 9 lines
Quoted 3 lines
Many chips don't, which is why we don't want these registers being global defines, you can have the chips without these registers as
INVALID_REG_ADDRChips that don't support memory update won't have a
bq27xxx_dm_regslist; we look for that at the start of this code path and do nothing if it's null fordi->chip. All chips that do support memory update use the same command registers, so there's no reason to dup those.
Quoted 5 lines
Someday they might use a different value, we don't lose anything by putting these device registers in the table with the other device registers. I would rather duplicate some lines if it makes the code more uniform and easier to follow, again this is not a contest, we write code for ourselves, let the compiler organize/optimize things.
I prefer the principle that every line you write must have a purpose in the current codebase, vs a future one. But if you are adamant, I capitulate.
Quoted 9 lines
Quoted 2 lines
Also you can delete
REG_CTRLas it's not used by the existing code :-)True, but as before, I just like having the complete register set in a nice simple table, as opposed to having to add and change things all over if/when we may need that register.
Being a static table, I wonder if a sufficiently smart compiler could see that it is not used and optimize the value out for us...
No :-p All the compiler can do for unused data is issue a warning. And it can't do so for array elements.
You really should define those tables in a function which memcpy's the right one onto di.regs.
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
Or make a default array of reg numbers and memcopy it onto each chip array, then alter as req'd per chip. But that's outside our scope.
Quoted 7 lines
Quoted 6 lines
drivers/power/supply/bq27xxx_battery.c +6
-0+/* control register params */ +#define BQ27XXX_SEALED 0x20 +#define BQ27XXX_SET_CFGUPDATE 0x13 +#define BQ27XXX_SOFT_RESET 0x42 + +#define BQ27XXX_MSLEEP (i) usleep_range((i)*1000, (i)*1000+500)
Re: [PATCH v7 7/9] power: bq27xxx_battery: Add power_supply_battery_info support
Quoted 78 lines · Liam Breck
Quoted 71 lines · Andrew F. Davis
Quoted 60 lines · Liam Breck
Quoted 59 lines · Andrew F. Davis
Quoted 57 lines · Liam Breck
Quoted 46 lines · Andrew F. Davis
Quoted 45 lines · Liam Breck
Quoted 43 lines · Andrew F. Davis
Quoted 29 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.Signed-off-by Matt Ranostay <[email protected]>Signed-off-by Liam Breck <[email protected]>--- drivers/power/supply/bq27xxx_battery.c | 399 ++++++++++++++++++++++++++++++++- 1 file changed, 397 insertions(+), 2 deletions(-)drivers/power/supply/bq27xxx_battery.c +1
-1@@ -51,7 +51,7 @@#include <linux/power/bq27xxx_battery.h> -#define DRIVER_VERSION "1.2.0" +#define DRIVER_VERSION "1.3.0"#define BQ27XXX_MANUFACTURER "Texas Instruments"@@ -452,6 +452,99 @@ static struct {static DEFINE_MUTEX (bq27xxx_list_lock);drivers/power/supply/bq27xxx_battery.c static struct { · line 462
Quoted 10 lines
drivers/power/supply/bq27xxx_battery.c +8
-0static LIST_HEAD (bq27xxx_battery_devices); +/* writable registers */ +#define BQ27XXX_CONTROL 0x00 +#define BQ27XXX_DATA_CLASS 0x3E +#define BQ27XXX_DATA_BLOCK 0x3F +#define BQ27XXX_BLOCK_DATA 0x40 +#define BQ27XXX_BLOCK_DATA_CHECKSUM 0x60 +#define BQ27XXX_BLOCK_DATA_CONTROL 0x61 +These all belong in the
bq27xxx_regsarray, you can add new register types to thebq27xxx_reg_indexenum.Quoted 6 lines
Quoted 2 lines
They're the same on all chips. Let's dup this code 9+ times when we have cause.
They are not the same on all chips, some chips don't have these registers at all. Plus,
BQ27XXX_CONTROLis the same for all chips, and we already have it in all the chipsbq27xxx_regs.Datasheets? Do those chips even support this feature set? Bqtool doesn't appear to support any other memory update scheme.
Quoted 9 lines
Quoted 3 lines
Many chips don't, which is why we don't want these registers being global defines, you can have the chips without these registers as
INVALID_REG_ADDRChips that don't support memory update won't have a
bq27xxx_dm_regslist; we look for that at the start of this code path and do nothing if it's null fordi->chip. All chips that do support memory update use the same command registers, so there's no reason to dup those.Quoted 5 lines
Someday they might use a different value, we don't lose anything by putting these device registers in the table with the other device registers. I would rather duplicate some lines if it makes the code more uniform and easier to follow, again this is not a contest, we write code for ourselves, let the compiler organize/optimize things.
Quoted 4 lines
I prefer the principle that every line you write must have a purpose in the current codebase, vs a future one. But if you are adamant, I capitulate.
Normally I would agree, but for listing registers on a device we would have a register map that looks like swiss cheese.
Quoted 7 lines
Quoted 7 lines
Quoted 2 lines
Also you can delete
REG_CTRLas it's not used by the existing code :-)True, but as before, I just like having the complete register set in a nice simple table, as opposed to having to add and change things all over if/when we may need that register.
Quoted 6 lines
Quoted 2 lines
Being a static table, I wonder if a sufficiently smart compiler could see that it is not used and optimize the value out for us...
No :-p All the compiler can do for unused data is issue a warning. And it can't do so for array elements.
I said "sufficiently smart compiler", it could find that no code within the tables context accesses it outside of the set indexes, it could then even remove the tables altogether and replace accesses with immediate values. Not practical, but not impossible, I think.. :)
Quoted 3 lines
You really should define those tables in a function which memcpy's the right one onto di.regs.
What would that do for us? We still need to store the tables, it just removes one level of pointers.
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
Or make a default array of reg numbers and memcopy it onto each chip array, then alter as req'd per chip. But that's outside our scope.
Quoted 7 lines
Quoted 6 lines
drivers/power/supply/bq27xxx_battery.c +6
-0+/* control register params */ +#define BQ27XXX_SEALED 0x20 +#define BQ27XXX_SET_CFGUPDATE 0x13 +#define BQ27XXX_SOFT_RESET 0x42 + +#define BQ27XXX_MSLEEP (i) usleep_range((i)*1000, (i)*1000+500)
Re: [PATCH v7 7/9] power: bq27xxx_battery: Add power_supply_battery_info support
Quoted 79 lines · Andrew F. Davis
Quoted 78 lines · Liam Breck
Quoted 76 lines · Andrew F. Davis
Quoted 60 lines · Liam Breck
Quoted 59 lines · Andrew F. Davis
Quoted 57 lines · Liam Breck
Quoted 46 lines · Andrew F. Davis
Quoted 45 lines · Liam Breck
Quoted 43 lines · Andrew F. Davis
Quoted 29 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.Signed-off-by Matt Ranostay <[email protected]>Signed-off-by Liam Breck <[email protected]>--- drivers/power/supply/bq27xxx_battery.c | 399 ++++++++++++++++++++++++++++++++- 1 file changed, 397 insertions(+), 2 deletions(-)drivers/power/supply/bq27xxx_battery.c +1
-1@@ -51,7 +51,7 @@#include <linux/power/bq27xxx_battery.h> -#define DRIVER_VERSION "1.2.0" +#define DRIVER_VERSION "1.3.0"#define BQ27XXX_MANUFACTURER "Texas Instruments"@@ -452,6 +452,99 @@ static struct {static DEFINE_MUTEX (bq27xxx_list_lock);drivers/power/supply/bq27xxx_battery.c static struct { · line 462
Quoted 10 lines
drivers/power/supply/bq27xxx_battery.c +8
-0static LIST_HEAD (bq27xxx_battery_devices); +/* writable registers */ +#define BQ27XXX_CONTROL 0x00 +#define BQ27XXX_DATA_CLASS 0x3E +#define BQ27XXX_DATA_BLOCK 0x3F +#define BQ27XXX_BLOCK_DATA 0x40 +#define BQ27XXX_BLOCK_DATA_CHECKSUM 0x60 +#define BQ27XXX_BLOCK_DATA_CONTROL 0x61 +These all belong in the
bq27xxx_regsarray, you can add new register types to thebq27xxx_reg_indexenum.Quoted 6 lines
Quoted 2 lines
They're the same on all chips. Let's dup this code 9+ times when we have cause.
They are not the same on all chips, some chips don't have these registers at all. Plus,
BQ27XXX_CONTROLis the same for all chips, and we already have it in all the chipsbq27xxx_regs.Datasheets? Do those chips even support this feature set? Bqtool doesn't appear to support any other memory update scheme.
Quoted 9 lines
Quoted 3 lines
Many chips don't, which is why we don't want these registers being global defines, you can have the chips without these registers as
INVALID_REG_ADDRChips that don't support memory update won't have a
bq27xxx_dm_regslist; we look for that at the start of this code path and do nothing if it's null fordi->chip. All chips that do support memory update use the same command registers, so there's no reason to dup those.Someday they might use a different value, we don't lose anything by putting these device registers in the table with the other device registers. I would rather duplicate some lines if it makes the code more uniform and easier to follow, again this is not a contest, we write code for ourselves, let the compiler organize/optimize things.
Quoted 7 lines
Quoted 4 lines
I prefer the principle that every line you write must have a purpose in the current codebase, vs a future one. But if you are adamant, I capitulate.
Normally I would agree, but for listing registers on a device we would have a register map that looks like swiss cheese.
Was that adamant? Keep the readable registers in tables; make the writable ones universal since they're all the same.
Quoted 10 lines
Quoted 10 lines
Quoted 9 lines
Quoted 2 lines
Also you can delete
REG_CTRLas it's not used by the existing code :-)True, but as before, I just like having the complete register set in a nice simple table, as opposed to having to add and change things all over if/when we may need that register.
Being a static table, I wonder if a sufficiently smart compiler could see that it is not used and optimize the value out for us...
Quoted 8 lines
Quoted 3 lines
No :-p All the compiler can do for unused data is issue a warning. And it can't do so for array elements.
I said "sufficiently smart compiler", it could find that no code within the tables context accesses it outside of the set indexes, it could then even remove the tables altogether and replace accesses with immediate values. Not practical, but not impossible, I think.. :)
I'm not so interested in hypothetical compilers and chips. There are reasons why compilers don't optimize away data.
Quoted 5 lines
Quoted 2 lines
You really should define those tables in a function which memcpy's the right one onto di.regs.
What would that do for us? We still need to store the tables, it just removes one level of pointers.
It would store 1 table for the life of the kernel, vs 9+
Re: [PATCH v7 7/9] power: bq27xxx_battery: Add power_supply_battery_info support
Quoted 113 lines · Liam Breck
Quoted 79 lines · Andrew F. Davis
Quoted 78 lines · Liam Breck
Quoted 76 lines · Andrew F. Davis
Quoted 60 lines · Liam Breck
Quoted 59 lines · Andrew F. Davis
Quoted 57 lines · Liam Breck
Quoted 46 lines · Andrew F. Davis
Quoted 45 lines · Liam Breck
Quoted 43 lines · Andrew F. Davis
Quoted 29 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.Signed-off-by Matt Ranostay <[email protected]>Signed-off-by Liam Breck <[email protected]>--- drivers/power/supply/bq27xxx_battery.c | 399 ++++++++++++++++++++++++++++++++- 1 file changed, 397 insertions(+), 2 deletions(-)drivers/power/supply/bq27xxx_battery.c +1
-1@@ -51,7 +51,7 @@#include <linux/power/bq27xxx_battery.h> -#define DRIVER_VERSION "1.2.0" +#define DRIVER_VERSION "1.3.0"#define BQ27XXX_MANUFACTURER "Texas Instruments"@@ -452,6 +452,99 @@ static struct {static DEFINE_MUTEX (bq27xxx_list_lock);drivers/power/supply/bq27xxx_battery.c static struct { · line 462
Quoted 10 lines
drivers/power/supply/bq27xxx_battery.c +8
-0static LIST_HEAD (bq27xxx_battery_devices); +/* writable registers */ +#define BQ27XXX_CONTROL 0x00 +#define BQ27XXX_DATA_CLASS 0x3E +#define BQ27XXX_DATA_BLOCK 0x3F +#define BQ27XXX_BLOCK_DATA 0x40 +#define BQ27XXX_BLOCK_DATA_CHECKSUM 0x60 +#define BQ27XXX_BLOCK_DATA_CONTROL 0x61 +These all belong in the
bq27xxx_regsarray, you can add new register types to thebq27xxx_reg_indexenum.Quoted 6 lines
Quoted 2 lines
They're the same on all chips. Let's dup this code 9+ times when we have cause.
They are not the same on all chips, some chips don't have these registers at all. Plus,
BQ27XXX_CONTROLis the same for all chips, and we already have it in all the chipsbq27xxx_regs.Datasheets? Do those chips even support this feature set? Bqtool doesn't appear to support any other memory update scheme.
Quoted 9 lines
Quoted 3 lines
Many chips don't, which is why we don't want these registers being global defines, you can have the chips without these registers as
INVALID_REG_ADDRChips that don't support memory update won't have a
bq27xxx_dm_regslist; we look for that at the start of this code path and do nothing if it's null fordi->chip. All chips that do support memory update use the same command registers, so there's no reason to dup those.Someday they might use a different value, we don't lose anything by putting these device registers in the table with the other device registers. I would rather duplicate some lines if it makes the code more uniform and easier to follow, again this is not a contest, we write code for ourselves, let the compiler organize/optimize things.
Quoted 7 lines
Quoted 4 lines
I prefer the principle that every line you write must have a purpose in the current codebase, vs a future one. But if you are adamant, I capitulate.
Normally I would agree, but for listing registers on a device we would have a register map that looks like swiss cheese.
Was that adamant? Keep the readable registers in tables; make the writable ones universal since they're all the same.
Quoted 10 lines
Quoted 10 lines
Quoted 9 lines
Quoted 2 lines
Also you can delete
REG_CTRLas it's not used by the existing code :-)True, but as before, I just like having the complete register set in a nice simple table, as opposed to having to add and change things all over if/when we may need that register.
Being a static table, I wonder if a sufficiently smart compiler could see that it is not used and optimize the value out for us...
Quoted 8 lines
Quoted 3 lines
No :-p All the compiler can do for unused data is issue a warning. And it can't do so for array elements.
I said "sufficiently smart compiler", it could find that no code within the tables context accesses it outside of the set indexes, it could then even remove the tables altogether and replace accesses with immediate values. Not practical, but not impossible, I think.. :)
I'm not so interested in hypothetical compilers and chips. There are reasons why compilers don't optimize away data.
Quoted 8 lines
Quoted 5 lines
Quoted 2 lines
You really should define those tables in a function which memcpy's the right one onto di.regs.
What would that do for us? We still need to store the tables, it just removes one level of pointers.
It would store 1 table for the life of the kernel, vs 9+
Helping the compiler optimize away a couple bytes is much less important than readability and maintainability of the code base, IMHO.
Re: [PATCH v7 7/9] power: bq27xxx_battery: Add power_supply_battery_info support
Quoted 117 lines · Andrew F. Davis
Quoted 116 lines · Liam Breck
Quoted 79 lines · Andrew F. Davis
Quoted 78 lines · Liam Breck
Quoted 76 lines · Andrew F. Davis
Quoted 60 lines · Liam Breck
Quoted 59 lines · Andrew F. Davis
Quoted 57 lines · Liam Breck
Quoted 46 lines · Andrew F. Davis
Quoted 45 lines · Liam Breck
Quoted 43 lines · Andrew F. Davis
Quoted 29 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.Signed-off-by Matt Ranostay <[email protected]>Signed-off-by Liam Breck <[email protected]>--- drivers/power/supply/bq27xxx_battery.c | 399 ++++++++++++++++++++++++++++++++- 1 file changed, 397 insertions(+), 2 deletions(-)drivers/power/supply/bq27xxx_battery.c +1
-1@@ -51,7 +51,7 @@#include <linux/power/bq27xxx_battery.h> -#define DRIVER_VERSION "1.2.0" +#define DRIVER_VERSION "1.3.0"#define BQ27XXX_MANUFACTURER "Texas Instruments"@@ -452,6 +452,99 @@ static struct {static DEFINE_MUTEX (bq27xxx_list_lock);drivers/power/supply/bq27xxx_battery.c static struct { · line 462
Quoted 10 lines
drivers/power/supply/bq27xxx_battery.c +8
-0static LIST_HEAD (bq27xxx_battery_devices); +/* writable registers */ +#define BQ27XXX_CONTROL 0x00 +#define BQ27XXX_DATA_CLASS 0x3E +#define BQ27XXX_DATA_BLOCK 0x3F +#define BQ27XXX_BLOCK_DATA 0x40 +#define BQ27XXX_BLOCK_DATA_CHECKSUM 0x60 +#define BQ27XXX_BLOCK_DATA_CONTROL 0x61 +These all belong in the
bq27xxx_regsarray, you can add new register types to thebq27xxx_reg_indexenum.Quoted 6 lines
Quoted 2 lines
They're the same on all chips. Let's dup this code 9+ times when we have cause.
They are not the same on all chips, some chips don't have these registers at all. Plus,
BQ27XXX_CONTROLis the same for all chips, and we already have it in all the chipsbq27xxx_regs.Datasheets? Do those chips even support this feature set? Bqtool doesn't appear to support any other memory update scheme.
Quoted 9 lines
Quoted 3 lines
Many chips don't, which is why we don't want these registers being global defines, you can have the chips without these registers as
INVALID_REG_ADDRChips that don't support memory update won't have a
bq27xxx_dm_regslist; we look for that at the start of this code path and do nothing if it's null fordi->chip. All chips that do support memory update use the same command registers, so there's no reason to dup those.Someday they might use a different value, we don't lose anything by putting these device registers in the table with the other device registers. I would rather duplicate some lines if it makes the code more uniform and easier to follow, again this is not a contest, we write code for ourselves, let the compiler organize/optimize things.
Quoted 7 lines
Quoted 4 lines
I prefer the principle that every line you write must have a purpose in the current codebase, vs a future one. But if you are adamant, I capitulate.
Normally I would agree, but for listing registers on a device we would have a register map that looks like swiss cheese.
Was that adamant? Keep the readable registers in tables; make the writable ones universal since they're all the same.
Quoted 10 lines
Quoted 10 lines
Quoted 9 lines
Quoted 2 lines
Also you can delete
REG_CTRLas it's not used by the existing code :-)True, but as before, I just like having the complete register set in a nice simple table, as opposed to having to add and change things all over if/when we may need that register.
Being a static table, I wonder if a sufficiently smart compiler could see that it is not used and optimize the value out for us...
Quoted 8 lines
Quoted 3 lines
No :-p All the compiler can do for unused data is issue a warning. And it can't do so for array elements.
I said "sufficiently smart compiler", it could find that no code within the tables context accesses it outside of the set indexes, it could then even remove the tables altogether and replace accesses with immediate values. Not practical, but not impossible, I think.. :)
I'm not so interested in hypothetical compilers and chips. There are reasons why compilers don't optimize away data.
Quoted 3 lines
Quoted 2 lines
You really should define those tables in a function which memcpy's the right one onto di.regs.
Quoted 8 lines
Quoted 5 lines
Quoted 2 lines
What would that do for us? We still need to store the tables, it just removes one level of pointers.
It would store 1 table for the life of the kernel, vs 9+
Helping the compiler optimize away a couple bytes is much less important than readability and maintainability of the code base, IMHO.
It's 176 bytes (8x22) after adding the new registers in this patch. And more again when more chip IDs are added.
Not a burden for most environments...
Re: [PATCH v7 7/9] power: bq27xxx_battery: Add power_supply_battery_info support
Quoted 124 lines · Liam Breck
Quoted 123 lines · Andrew F. Davis
Quoted 121 lines · Liam Breck
Quoted 79 lines · Andrew F. Davis
Quoted 78 lines · Liam Breck
Quoted 76 lines · Andrew F. Davis
Quoted 60 lines · Liam Breck
Quoted 59 lines · Andrew F. Davis
Quoted 57 lines · Liam Breck
Quoted 46 lines · Andrew F. Davis
Quoted 45 lines · Liam Breck
Quoted 43 lines · Andrew F. Davis
Quoted 39 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.Signed-off-by: Matt Ranostay <[email protected]>
Signed-off-by: Liam Breck <[email protected]>
---
drivers/power/supply/bq27xxx_battery.c | 399 ++++++++++++++++++++++++++++++++-
1 file changed, 397 insertions(+), 2 deletions(-)diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c
index 7475a5f..8085d4a 100644
--- a/drivers/power/supply/bq27xxx_battery.c
+++ b/drivers/power/supply/bq27xxx_battery.c
@@ -51,7 +51,7 @@#include <linux/power/bq27xxx_battery.h>
-#define DRIVER_VERSION "1.2.0"
+#define DRIVER_VERSION "1.3.0"#define BQ27XXX_MANUFACTURER "Texas Instruments"
@@ -452,6 +452,99 @@ static struct {
static DEFINE_MUTEX(bq27xxx_list_lock);
static LIST_HEAD(bq27xxx_battery_devices);+/* writable registers */
+#define BQ27XXX_CONTROL 0x00
+#define BQ27XXX_DATA_CLASS 0x3E
+#define BQ27XXX_DATA_BLOCK 0x3F
+#define BQ27XXX_BLOCK_DATA 0x40
+#define BQ27XXX_BLOCK_DATA_CHECKSUM 0x60
+#define BQ27XXX_BLOCK_DATA_CONTROL 0x61
+These all belong in the
bq27xxx_regsarray, you can add new register types to thebq27xxx_reg_indexenum.Quoted 6 lines
Quoted 2 lines
They're the same on all chips. Let's dup this code 9+ times when we have cause.
They are not the same on all chips, some chips don't have these registers at all. Plus,
BQ27XXX_CONTROLis the same for all chips, and we already have it in all the chipsbq27xxx_regs.Datasheets? Do those chips even support this feature set? Bqtool doesn't appear to support any other memory update scheme.
Quoted 9 lines
Quoted 3 lines
Many chips don't, which is why we don't want these registers being global defines, you can have the chips without these registers as
INVALID_REG_ADDRChips that don't support memory update won't have a
bq27xxx_dm_regslist; we look for that at the start of this code path and do nothing if it's null fordi->chip. All chips that do support memory update use the same command registers, so there's no reason to dup those.Someday they might use a different value, we don't lose anything by putting these device registers in the table with the other device registers. I would rather duplicate some lines if it makes the code more uniform and easier to follow, again this is not a contest, we write code for ourselves, let the compiler organize/optimize things.
Quoted 7 lines
Quoted 4 lines
I prefer the principle that every line you write must have a purpose in the current codebase, vs a future one. But if you are adamant, I capitulate.
Normally I would agree, but for listing registers on a device we would have a register map that looks like swiss cheese.
Was that adamant? Keep the readable registers in tables; make the writable ones universal since they're all the same.
Quoted 10 lines
Quoted 10 lines
Quoted 9 lines
Quoted 2 lines
Also you can delete
REG_CTRLas it's not used by the existing code :-)True, but as before, I just like having the complete register set in a nice simple table, as opposed to having to add and change things all over if/when we may need that register.
Being a static table, I wonder if a sufficiently smart compiler could see that it is not used and optimize the value out for us...
Quoted 8 lines
Quoted 3 lines
No :-p All the compiler can do for unused data is issue a warning. And it can't do so for array elements.
I said "sufficiently smart compiler", it could find that no code within the tables context accesses it outside of the set indexes, it could then even remove the tables altogether and replace accesses with immediate values. Not practical, but not impossible, I think.. :)
I'm not so interested in hypothetical compilers and chips. There are reasons why compilers don't optimize away data.
Quoted 5 lines
Quoted 2 lines
You really should define those tables in a function which memcpy's the right one onto di.regs.
What would that do for us? We still need to store the tables, it just removes one level of pointers.
It would store 1 table for the life of the kernel, vs 9+
Quoted 8 lines
Quoted 2 lines
Helping the compiler optimize away a couple bytes is much less important than readability and maintainability of the code base, IMHO.
It's 176 bytes (8x22) after adding the new registers in this patch. And more again when more chip IDs are added.
Not a burden for most environments...
If it is really a huge annoyance for you, moving these registers into a separate bq27xxx_dm_regs style array may also be acceptable, I'm just not a fan of #define some registers when we have other registers in tables. Should be all one way or all the other, how you organize the tables isn't too big of a concern as long as there is some logic to it.
Andrew
Re: [PATCH v7 7/9] power: bq27xxx_battery: Add power_supply_battery_info support
In reply to Andrew F. DavisDid you have a chance to test the patchset against hardware?
If you don't have a board with a 425 chip, I can send you the dm_regs tables for whatever parts you have to hand...
Quoted 498 lines · Andrew F. Davis
Quoted 29 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.Signed-off-by Matt Ranostay <[email protected]>Signed-off-by Liam Breck <[email protected]>--- drivers/power/supply/bq27xxx_battery.c | 399 ++++++++++++++++++++++++++++++++- 1 file changed, 397 insertions(+), 2 deletions(-)drivers/power/supply/bq27xxx_battery.c +1
-1@@ -51,7 +51,7 @@#include <linux/power/bq27xxx_battery.h> -#define DRIVER_VERSION "1.2.0" +#define DRIVER_VERSION "1.3.0"#define BQ27XXX_MANUFACTURER "Texas Instruments"@@ -452,6 +452,99 @@ static struct {static DEFINE_MUTEX (bq27xxx_list_lock);drivers/power/supply/bq27xxx_battery.c static struct { · line 462
Quoted 10 lines
drivers/power/supply/bq27xxx_battery.c +8
-0static LIST_HEAD (bq27xxx_battery_devices); +/* writable registers */ +#define BQ27XXX_CONTROL 0x00 +#define BQ27XXX_DATA_CLASS 0x3E +#define BQ27XXX_DATA_BLOCK 0x3F +#define BQ27XXX_BLOCK_DATA 0x40 +#define BQ27XXX_BLOCK_DATA_CHECKSUM 0x60 +#define BQ27XXX_BLOCK_DATA_CONTROL 0x61 +These all belong in the
bq27xxx_regsarray, you can add new register types to thebq27xxx_reg_indexenum.drivers/power/supply/bq27xxx_battery.c static struct { · line 468
Quoted 6 lines
drivers/power/supply/bq27xxx_battery.c +6
-0+/* control register params */ +#define BQ27XXX_SEALED 0x20 +#define BQ27XXX_SET_CFGUPDATE 0x13 +#define BQ27XXX_SOFT_RESET 0x42 + +#define BQ27XXX_MSLEEP (i) usleep_range((i)*1000, (i)*1000+500)why?
Quoted 8 lines
drivers/power/supply/bq27xxx_battery.c +8
-0+ +struct bq27xxx_dm_reg { +u8 subclass_id; +u8 offset; +u8 bytes; +u16 min, max; +}; +drivers/power/supply/bq27xxx_battery.c static struct { · line 486
Quoted 10 lines
drivers/power/supply/bq27xxx_battery.c +10
-0+struct bq27xxx_dm_buf { +u8 class; +u8 block; +u8 a[32 ]; +bool full, updt; +}; + +#define BQ27XXX_DM_BUF_PTR (b, r) \ + ( (u16*) &(b).a[ (r)->offset % sizeof (b).a ] ) +This could compete in the IOCCC, there has to be a more readable way to do this..
drivers/power/supply/bq27xxx_battery.c static struct { · line 496
Quoted 10 lines
drivers/power/supply/bq27xxx_battery.c +10
-0+#define BQ27XXX_DM_BUF (di, i) { \ + .class = bq27xxx_dm_regs[(di)->chip][i].subclass_id, \ + .block = bq27xxx_dm_regs[(di)->chip][i].offset / sizeof ((struct bq27xxx_dm_buf*)0)->a, \ +} + +static inline bool bq27xxx_dm_buf_set (struct bq27xxx_dm_buf *buf, +struct bq27xxx_dm_reg *reg) { +if (buf->class == reg->subclass_id + && buf->block == reg->offset /sizeof buf->a + && buf->full)Some alignment issues here.
drivers/power/supply/bq27xxx_battery.c static struct { · line 500
Quoted 4 lines
drivers/power/supply/bq27xxx_battery.c +4
-0+return false; + buf->class = reg->subclass_id; + buf->block = reg->offset /sizeof buf->a; + buf->full = buf->updt = false;newline before return
Quoted 34 lines
drivers/power/supply/bq27xxx_battery.c +34
-0+return true; +} + +enum bq27xxx_dm_reg_id { +BQ27XXX_DM_DESIGN_CAPACITY =0 , +BQ27XXX_DM_DESIGN_ENERGY , +BQ27XXX_DM_TERMINATE_VOLTAGE , +BQ27XXX_DM_END , +}; + +static const char * bq27xxx_dm_reg_name[] = { + [BQ27XXX_DM_DESIGN_CAPACITY ] ="design-capacity" , + [BQ27XXX_DM_DESIGN_ENERGY ] ="design-energy" , + [BQ27XXX_DM_TERMINATE_VOLTAGE ] ="terminate-voltage" , +}; + +static struct bq27xxx_dm_reg bq27425_dm_regs[] = { + [BQ27XXX_DM_DESIGN_CAPACITY ] = {82 ,12 ,2 ,0 ,32767 }, + [BQ27XXX_DM_DESIGN_ENERGY ] = {82 ,14 ,2 ,0 ,32767 }, + [BQ27XXX_DM_TERMINATE_VOLTAGE ] = {82 ,18 ,2 ,2800 ,3700 }, +}; + +static struct bq27xxx_dm_reg bq27421_dm_regs[] = {/* not tested */ + [BQ27XXX_DM_DESIGN_CAPACITY ] = {82 ,10 ,2 ,0 ,8000 }, + [BQ27XXX_DM_DESIGN_ENERGY ] = {82 ,12 ,2 ,0 ,32767 }, + [BQ27XXX_DM_TERMINATE_VOLTAGE ] = {82 ,16 ,2 ,2500 ,3700 }, +}; + +static struct bq27xxx_dm_reg bq27621_dm_regs[] = {/* not tested */ + [BQ27XXX_DM_DESIGN_CAPACITY ] = {82 ,3 ,2 ,0 ,8000 }, + [BQ27XXX_DM_DESIGN_ENERGY ] = {82 ,5 ,2 ,0 ,32767 }, + [BQ27XXX_DM_TERMINATE_VOLTAGE ] = {82 ,9 ,2 ,2500 ,3700 }, +}; +drivers/power/supply/bq27xxx_battery.c static struct { · line 544
Quoted 10 lines
drivers/power/supply/bq27xxx_battery.c +10
-0+static struct bq27xxx_dm_reg *bq27xxx_dm_regs[] = { + [BQ27421 ] = bq27421_dm_regs,/* and BQ27441 */ + [BQ27425 ] = bq27425_dm_regs, +/* [BQ27621] = */ bq27621_dm_regs, +}; + +static u32 bq27xxx_unseal_keys[] = { + [BQ27421 ] =0x80008000 ,/* and BQ27441 */ + [BQ27425 ] =0x04143672 , +/* [BQ27621] = */ 0x80008000 ,Why is this commented out.
Quoted 23 lines
drivers/power/supply/bq27xxx_battery.c +16
-0+}; + +static int poll_interval_param_set (const char *val,const struct kernel_param *kp) {struct bq27xxx_device_info *di;@@ -496,6 +589,297 @@ static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index,return di->bus.read (di, di->regs[reg_index], single); } +static int bq27xxx_battery_set_seal_state (struct bq27xxx_device_info *di, +bool state) +{ +u32 key = bq27xxx_unseal_keys[di->chip]; +int ret; + +if (state) { + ret = di->bus.write (di,BQ27XXX_CONTROL ,BQ27XXX_SEALED , false); +if (ret <0 ) + goto out; + }else { + ret = di->bus.write (di,BQ27XXX_CONTROL , (u16)(key >>16 ), false); +if (ret <0 )drivers/power/supply/bq27xxx_battery.c static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index, · line 614
Quoted 10 lines
drivers/power/supply/bq27xxx_battery.c +10
-0+ goto out; + + ret = di->bus.write (di,BQ27XXX_CONTROL , (u16 )key, false); +if (ret <0 ) + goto out; + } +return 0 ; + +out: +dev_err (di->dev,"bus error in %s: %d\n" , __func__, ret);This is not a debug statement, we don't need the function name, just say where it broke.
drivers/power/supply/bq27xxx_battery.c static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index, · line 623
Quoted 9 lines
drivers/power/supply/bq27xxx_battery.c +9
-0+return ret; +} + +static u8 bq27xxx_battery_checksum (struct bq27xxx_dm_buf *buf) +{ +u16 sum =0 ; +int i; + +for (i =0 ; i <sizeof buf->a; i++)Linus would like a word with you:
https://lkml.org/lkml/2012/7/11/103Quoted 73 lines
drivers/power/supply/bq27xxx_battery.c +73
-0+ sum += buf->a[i]; + sum &=0xff ; + +return 0xff - sum; +} + +static int bq27xxx_battery_read_dm_block (struct bq27xxx_device_info *di, +struct bq27xxx_dm_buf *buf) +{ +int ret; + + ret = di->bus.write (di,BQ27XXX_DATA_CLASS , buf->class, true); +if (ret <0 ) + goto out; + + ret = di->bus.write (di,BQ27XXX_DATA_BLOCK , buf->block, true); +if (ret <0 ) + goto out; + +BQ27XXX_MSLEEP (1 ); + + ret = di->bus.read_bulk (di,BQ27XXX_BLOCK_DATA , buf->a,sizeof buf->a); +if (ret <0 ) + goto out; + + ret = di->bus.read (di,BQ27XXX_BLOCK_DATA_CHECKSUM , true); +if (ret <0 ) + goto out; + +if ((u8 )ret !=bq27xxx_battery_checksum (buf)) { + ret = -EINVAL ; + goto out; + } + + buf->full = true; + buf->updt = false; +return 0 ; + +out: +dev_err (di->dev,"bus error in %s: %d\n" , __func__, ret); +return ret; +} + +static void bq27xxx_battery_print_config (struct bq27xxx_device_info *di) +{ +struct bq27xxx_dm_reg *reg = bq27xxx_dm_regs[di->chip]; +struct bq27xxx_dm_buf buf = { .class =0xFF }; +int i; + +for (i =0 ; i <BQ27XXX_DM_END ; i++, reg++) { +const char * str = bq27xxx_dm_reg_name[i]; + +if (bq27xxx_dm_buf_set (&buf, reg)) +if (bq27xxx_battery_read_dm_block (di, &buf) <0 ) +continue ; + +if (reg->bytes ==2 ) +dev_info (di->dev,"config register %s is %d\n" , str, +be16_to_cpup (BQ27XXX_DM_BUF_PTR (buf, reg))); +else +dev_warn (di->dev,"unsupported config register %s\n" , str); + } +} + +static void bq27xxx_battery_update_dm_block (struct bq27xxx_device_info *di, +struct bq27xxx_dm_buf *buf, +enum bq27xxx_dm_reg_id reg_id, +unsigned int val) +{ +struct bq27xxx_dm_reg *reg = &bq27xxx_dm_regs[di->chip][reg_id]; +u16 *prev; + +if (reg->bytes !=2 )drivers/power/supply/bq27xxx_battery.c static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index, · line 706
Quoted 10 lines
drivers/power/supply/bq27xxx_battery.c +10
-0+return ; + + prev =BQ27XXX_DM_BUF_PTR (*buf, reg); +if (be16_to_cpup (prev) == val) +return ; + *prev =cpu_to_be16 (val); + +dev_info (di->dev,"update chip %d, class %u, block %u, offset %u, value %u\n" , + di->chip, buf->class, buf->block, reg->offset, val); +This should probably be
dev_debug, no normal user cares about all this detail.Quoted 30 lines
drivers/power/supply/bq27xxx_battery.c +30
-0+ buf->updt = true; +return ; +} + +static int bq27xxx_battery_write_dm_block (struct bq27xxx_device_info *di, +struct bq27xxx_dm_buf *buf) +{ +bool cfgup = di->chip ==BQ27425 || di->chip ==BQ27421 ;/* || BQ27441 || BQ27621 */ +int ret; + +if (cfgup) { + ret = di->bus.write (di,BQ27XXX_CONTROL ,BQ27XXX_SET_CFGUPDATE , false); +if (ret <0 ) + goto out1; + } + + ret = di->bus.write (di,BQ27XXX_BLOCK_DATA_CONTROL ,0 , true); +if (ret <0 ) + goto out2; + + ret = di->bus.write (di,BQ27XXX_DATA_CLASS , buf->class, true); +if (ret <0 ) + goto out2; + + ret = di->bus.write (di,BQ27XXX_DATA_BLOCK , buf->block, true); +if (ret <0 ) + goto out2; + +BQ27XXX_MSLEEP (1 ); +drivers/power/supply/bq27xxx_battery.c static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index, · line 746
Quoted 10 lines
drivers/power/supply/bq27xxx_battery.c +10
-0+ ret = di->bus.write_bulk (di,BQ27XXX_BLOCK_DATA , buf->a,sizeof buf->a); +if (ret <0 ) + goto out2; + + ret = di->bus.write (di,BQ27XXX_BLOCK_DATA_CHECKSUM , +bq27xxx_battery_checksum (buf), true); +if (ret <0 ) + goto out2; + +/* THE FOLLOWING CODE IS TOXIC. DO NOT USE! Then why include it, what is this code supposed to do, and what do we lose without it? If it is really needed we can find a way to guarantee the delay length.
Quoted 174 lines
drivers/power/supply/bq27xxx_battery.c +148
-1+* If the 350ms delay is insufficient, NVM corruption results +* on the '425 chip, which could damage the chip. +* It was suggested in this TI tool: +* http://git.ti.com/bms-linux/bqtool/blobs/master/gauge.c#line328 +* +* BQ27XXX_MSLEEP(350); +* ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, buf->block, true); +* BQ27XXX_MSLEEP(1); +* ret = di->bus.read(di, BQ27XXX_BLOCK_DATA_CHECKSUM, true); +* if ((u8)ret != bq27xxx_battery_checksum(buf)) +* ret = -EINVAL; +*/ + +if (cfgup) { +BQ27XXX_MSLEEP (1 ); + ret = di->bus.write (di,BQ27XXX_CONTROL ,BQ27XXX_SOFT_RESET , false); +if (ret <0 ) + goto out1; + } + + buf->updt = false; +return 0 ; + +out2: +if (cfgup) + di->bus.write (di,BQ27XXX_CONTROL ,BQ27XXX_SOFT_RESET , false); +out1: +dev_err (di->dev,"bus error in %s: %d\n" , __func__, ret); +return ret; +} + +static void bq27xxx_battery_set_config (struct bq27xxx_device_info *di, +struct power_supply_battery_info *info) +{ +struct bq27xxx_dm_buf bd =BQ27XXX_DM_BUF (di,BQ27XXX_DM_DESIGN_ENERGY ); +struct bq27xxx_dm_buf bt =BQ27XXX_DM_BUF (di,BQ27XXX_DM_TERMINATE_VOLTAGE ); + +if (info->charge_full_design_uah != -EINVAL + && info->energy_full_design_uwh != -EINVAL ) { +bq27xxx_battery_read_dm_block (di, &bd); +if (bd.full) { +/* assume design energy & capacity are in same block */ +bq27xxx_battery_update_dm_block (di, &bd, +BQ27XXX_DM_DESIGN_CAPACITY , + info->charge_full_design_uah /1000 ); +bq27xxx_battery_update_dm_block (di, &bd, +BQ27XXX_DM_DESIGN_ENERGY , + info->energy_full_design_uwh /1000 ); + } + } + +if (info->voltage_min_design_uv != -EINVAL ) { +bool same = bd.full && bd.class == bt.class && bd.block == bt.block; +if (!same) +bq27xxx_battery_read_dm_block (di, &bt); +if (same ? bd.full : bt.full) +bq27xxx_battery_update_dm_block (di, same ? &bd : &bt, +BQ27XXX_DM_TERMINATE_VOLTAGE , + info->voltage_min_design_uv /1000 ); + } + +if (bd.updt) +bq27xxx_battery_write_dm_block (di, &bd); +if (bt.updt) +bq27xxx_battery_write_dm_block (di, &bt); +} + +void bq27xxx_battery_settings (struct bq27xxx_device_info *di) +{ +struct power_supply_battery_info info = {}; +unsigned int min, max; + +/* functions don't exist for writing data so abort */ +if (!di->bus.write || !di->bus.write_bulk) +return ; + +/* no settings to be set for this chipset so abort */ +if (!bq27xxx_dm_regs[di->chip]) +return ; + +if (bq27xxx_battery_set_seal_state (di, false) <0 ) +return ; + +if (power_supply_get_battery_info (di->bat, &info) <0 ) + goto out; + +if (info.energy_full_design_uwh != info.charge_full_design_uah) { +if (info.energy_full_design_uwh == -EINVAL ) +dev_warn (di->dev, +"missing battery:energy-full-design-microwatt-hours\n" ); +else if (info.charge_full_design_uah == -EINVAL ) +dev_warn (di->dev, +"missing battery:charge-full-design-microamp-hours\n" ); + } + +/* assume min == 0 */ + max = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_DESIGN_ENERGY ].max; +if (info.energy_full_design_uwh > max *1000 ) { +dev_err (di->dev, +"invalid battery:energy-full-design-microwatt-hours %d\n" , + info.energy_full_design_uwh); + info.energy_full_design_uwh = -EINVAL ; + } + +/* assume min == 0 */ + max = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_DESIGN_CAPACITY ].max; +if (info.charge_full_design_uah > max *1000 ) { +dev_err (di->dev, +"invalid battery:charge-full-design-microamp-hours %d\n" , + info.charge_full_design_uah); + info.charge_full_design_uah = -EINVAL ; + } + + min = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_TERMINATE_VOLTAGE ].min; + max = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_TERMINATE_VOLTAGE ].max; +if ((info.voltage_min_design_uv < min *1000 + || info.voltage_min_design_uv > max *1000 ) + && info.voltage_min_design_uv != -EINVAL ) { +dev_err (di->dev, +"invalid battery:voltage-min-design-microvolt %d\n" , + info.voltage_min_design_uv); + info.voltage_min_design_uv = -EINVAL ; + } + +if ((info.energy_full_design_uwh == -EINVAL + || info.charge_full_design_uah == -EINVAL ) + && info.voltage_min_design_uv == -EINVAL ) + goto out; + +bq27xxx_battery_set_config (di, &info); + +out: +bq27xxx_battery_print_config (di); +bq27xxx_battery_set_seal_state (di, true); +} +/* * Return the battery State-of-Charge * Or < 0 if something fails. @@ -1006,6 +1390,13 @@ static int bq27xxx_battery_get_property(struct power_supply *psy,case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN : ret =bq27xxx_simple_value (di->charge_design_full, val);break ; +/* +* TODO: Implement these to make registers set from +* power_supply_battery_info visible in sysfs. +*/ +case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN : +case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN : +return -EINVAL ;case POWER_SUPPLY_PROP_CYCLE_COUNT : ret =bq27xxx_simple_value (di->cache.cycle_count, val);break ;@@ -1039,7 +1430,10 @@ static void bq27xxx_external_power_changed(struct power_supply *psy)int bq27xxx_battery_setup (struct bq27xxx_device_info *di) {struct power_supply_desc *psy_desc; -struct power_supply_config psy_cfg = { .drv_data = di, }; +struct power_supply_config psy_cfg = { + .of_node = di->dev->of_node, + .drv_data = di, + };INIT_DELAYED_WORK (&di->work, bq27xxx_battery_poll);mutex_init (&di->lock);@@ -1064,6 +1458,7 @@ int bq27xxx_battery_setup(struct bq27xxx_device_info *di)dev_info (di->dev,"support ver. %s enabled\n" ,DRIVER_VERSION ); +bq27xxx_battery_settings (di);bq27xxx_battery_update (di);mutex_lock (&bq27xxx_list_lock);
Re: [PATCH v7 7/9] power: bq27xxx_battery: Add power_supply_battery_info support
I can get ahold of any BQ27xxx part, I'm holding off on testing until the comments start to get sorted out, that way I don't have to test again after all these changes.
Quoted 505 lines · Liam Breck
Did you have a chance to test the patchset against hardware?
If you don't have a board with a 425 chip, I can send you the
dm_regstables for whatever parts you have to hand...Quoted 498 lines · Andrew F. Davis
Quoted 29 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.Signed-off-by Matt Ranostay <[email protected]>Signed-off-by Liam Breck <[email protected]>--- drivers/power/supply/bq27xxx_battery.c | 399 ++++++++++++++++++++++++++++++++- 1 file changed, 397 insertions(+), 2 deletions(-)drivers/power/supply/bq27xxx_battery.c +1
-1@@ -51,7 +51,7 @@#include <linux/power/bq27xxx_battery.h> -#define DRIVER_VERSION "1.2.0" +#define DRIVER_VERSION "1.3.0"#define BQ27XXX_MANUFACTURER "Texas Instruments"@@ -452,6 +452,99 @@ static struct {static DEFINE_MUTEX (bq27xxx_list_lock);drivers/power/supply/bq27xxx_battery.c static struct { · line 462
Quoted 10 lines
drivers/power/supply/bq27xxx_battery.c +8
-0static LIST_HEAD (bq27xxx_battery_devices); +/* writable registers */ +#define BQ27XXX_CONTROL 0x00 +#define BQ27XXX_DATA_CLASS 0x3E +#define BQ27XXX_DATA_BLOCK 0x3F +#define BQ27XXX_BLOCK_DATA 0x40 +#define BQ27XXX_BLOCK_DATA_CHECKSUM 0x60 +#define BQ27XXX_BLOCK_DATA_CONTROL 0x61 +These all belong in the
bq27xxx_regsarray, you can add new register types to thebq27xxx_reg_indexenum.drivers/power/supply/bq27xxx_battery.c static struct { · line 468
Quoted 6 lines
drivers/power/supply/bq27xxx_battery.c +6
-0+/* control register params */ +#define BQ27XXX_SEALED 0x20 +#define BQ27XXX_SET_CFGUPDATE 0x13 +#define BQ27XXX_SOFT_RESET 0x42 + +#define BQ27XXX_MSLEEP (i) usleep_range((i)*1000, (i)*1000+500)why?
Quoted 8 lines
drivers/power/supply/bq27xxx_battery.c +8
-0+ +struct bq27xxx_dm_reg { +u8 subclass_id; +u8 offset; +u8 bytes; +u16 min, max; +}; +drivers/power/supply/bq27xxx_battery.c static struct { · line 486
Quoted 10 lines
drivers/power/supply/bq27xxx_battery.c +10
-0+struct bq27xxx_dm_buf { +u8 class; +u8 block; +u8 a[32 ]; +bool full, updt; +}; + +#define BQ27XXX_DM_BUF_PTR (b, r) \ + ( (u16*) &(b).a[ (r)->offset % sizeof (b).a ] ) +This could compete in the IOCCC, there has to be a more readable way to do this..
drivers/power/supply/bq27xxx_battery.c static struct { · line 496
Quoted 10 lines
drivers/power/supply/bq27xxx_battery.c +10
-0+#define BQ27XXX_DM_BUF (di, i) { \ + .class = bq27xxx_dm_regs[(di)->chip][i].subclass_id, \ + .block = bq27xxx_dm_regs[(di)->chip][i].offset / sizeof ((struct bq27xxx_dm_buf*)0)->a, \ +} + +static inline bool bq27xxx_dm_buf_set (struct bq27xxx_dm_buf *buf, +struct bq27xxx_dm_reg *reg) { +if (buf->class == reg->subclass_id + && buf->block == reg->offset /sizeof buf->a + && buf->full)Some alignment issues here.
drivers/power/supply/bq27xxx_battery.c static struct { · line 500
Quoted 4 lines
drivers/power/supply/bq27xxx_battery.c +4
-0+return false; + buf->class = reg->subclass_id; + buf->block = reg->offset /sizeof buf->a; + buf->full = buf->updt = false;newline before return
Quoted 34 lines
drivers/power/supply/bq27xxx_battery.c +34
-0+return true; +} + +enum bq27xxx_dm_reg_id { +BQ27XXX_DM_DESIGN_CAPACITY =0 , +BQ27XXX_DM_DESIGN_ENERGY , +BQ27XXX_DM_TERMINATE_VOLTAGE , +BQ27XXX_DM_END , +}; + +static const char * bq27xxx_dm_reg_name[] = { + [BQ27XXX_DM_DESIGN_CAPACITY ] ="design-capacity" , + [BQ27XXX_DM_DESIGN_ENERGY ] ="design-energy" , + [BQ27XXX_DM_TERMINATE_VOLTAGE ] ="terminate-voltage" , +}; + +static struct bq27xxx_dm_reg bq27425_dm_regs[] = { + [BQ27XXX_DM_DESIGN_CAPACITY ] = {82 ,12 ,2 ,0 ,32767 }, + [BQ27XXX_DM_DESIGN_ENERGY ] = {82 ,14 ,2 ,0 ,32767 }, + [BQ27XXX_DM_TERMINATE_VOLTAGE ] = {82 ,18 ,2 ,2800 ,3700 }, +}; + +static struct bq27xxx_dm_reg bq27421_dm_regs[] = {/* not tested */ + [BQ27XXX_DM_DESIGN_CAPACITY ] = {82 ,10 ,2 ,0 ,8000 }, + [BQ27XXX_DM_DESIGN_ENERGY ] = {82 ,12 ,2 ,0 ,32767 }, + [BQ27XXX_DM_TERMINATE_VOLTAGE ] = {82 ,16 ,2 ,2500 ,3700 }, +}; + +static struct bq27xxx_dm_reg bq27621_dm_regs[] = {/* not tested */ + [BQ27XXX_DM_DESIGN_CAPACITY ] = {82 ,3 ,2 ,0 ,8000 }, + [BQ27XXX_DM_DESIGN_ENERGY ] = {82 ,5 ,2 ,0 ,32767 }, + [BQ27XXX_DM_TERMINATE_VOLTAGE ] = {82 ,9 ,2 ,2500 ,3700 }, +}; +drivers/power/supply/bq27xxx_battery.c static struct { · line 544
Quoted 10 lines
drivers/power/supply/bq27xxx_battery.c +10
-0+static struct bq27xxx_dm_reg *bq27xxx_dm_regs[] = { + [BQ27421 ] = bq27421_dm_regs,/* and BQ27441 */ + [BQ27425 ] = bq27425_dm_regs, +/* [BQ27621] = */ bq27621_dm_regs, +}; + +static u32 bq27xxx_unseal_keys[] = { + [BQ27421 ] =0x80008000 ,/* and BQ27441 */ + [BQ27425 ] =0x04143672 , +/* [BQ27621] = */ 0x80008000 ,Why is this commented out.
Quoted 23 lines
drivers/power/supply/bq27xxx_battery.c +16
-0+}; + +static int poll_interval_param_set (const char *val,const struct kernel_param *kp) {struct bq27xxx_device_info *di;@@ -496,6 +589,297 @@ static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index,return di->bus.read (di, di->regs[reg_index], single); } +static int bq27xxx_battery_set_seal_state (struct bq27xxx_device_info *di, +bool state) +{ +u32 key = bq27xxx_unseal_keys[di->chip]; +int ret; + +if (state) { + ret = di->bus.write (di,BQ27XXX_CONTROL ,BQ27XXX_SEALED , false); +if (ret <0 ) + goto out; + }else { + ret = di->bus.write (di,BQ27XXX_CONTROL , (u16)(key >>16 ), false); +if (ret <0 )drivers/power/supply/bq27xxx_battery.c static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index, · line 614
Quoted 10 lines
drivers/power/supply/bq27xxx_battery.c +10
-0+ goto out; + + ret = di->bus.write (di,BQ27XXX_CONTROL , (u16 )key, false); +if (ret <0 ) + goto out; + } +return 0 ; + +out: +dev_err (di->dev,"bus error in %s: %d\n" , __func__, ret);This is not a debug statement, we don't need the function name, just say where it broke.
drivers/power/supply/bq27xxx_battery.c static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index, · line 623
Quoted 9 lines
drivers/power/supply/bq27xxx_battery.c +9
-0+return ret; +} + +static u8 bq27xxx_battery_checksum (struct bq27xxx_dm_buf *buf) +{ +u16 sum =0 ; +int i; + +for (i =0 ; i <sizeof buf->a; i++)Linus would like a word with you:
https://lkml.org/lkml/2012/7/11/103Quoted 73 lines
drivers/power/supply/bq27xxx_battery.c +73
-0+ sum += buf->a[i]; + sum &=0xff ; + +return 0xff - sum; +} + +static int bq27xxx_battery_read_dm_block (struct bq27xxx_device_info *di, +struct bq27xxx_dm_buf *buf) +{ +int ret; + + ret = di->bus.write (di,BQ27XXX_DATA_CLASS , buf->class, true); +if (ret <0 ) + goto out; + + ret = di->bus.write (di,BQ27XXX_DATA_BLOCK , buf->block, true); +if (ret <0 ) + goto out; + +BQ27XXX_MSLEEP (1 ); + + ret = di->bus.read_bulk (di,BQ27XXX_BLOCK_DATA , buf->a,sizeof buf->a); +if (ret <0 ) + goto out; + + ret = di->bus.read (di,BQ27XXX_BLOCK_DATA_CHECKSUM , true); +if (ret <0 ) + goto out; + +if ((u8 )ret !=bq27xxx_battery_checksum (buf)) { + ret = -EINVAL ; + goto out; + } + + buf->full = true; + buf->updt = false; +return 0 ; + +out: +dev_err (di->dev,"bus error in %s: %d\n" , __func__, ret); +return ret; +} + +static void bq27xxx_battery_print_config (struct bq27xxx_device_info *di) +{ +struct bq27xxx_dm_reg *reg = bq27xxx_dm_regs[di->chip]; +struct bq27xxx_dm_buf buf = { .class =0xFF }; +int i; + +for (i =0 ; i <BQ27XXX_DM_END ; i++, reg++) { +const char * str = bq27xxx_dm_reg_name[i]; + +if (bq27xxx_dm_buf_set (&buf, reg)) +if (bq27xxx_battery_read_dm_block (di, &buf) <0 ) +continue ; + +if (reg->bytes ==2 ) +dev_info (di->dev,"config register %s is %d\n" , str, +be16_to_cpup (BQ27XXX_DM_BUF_PTR (buf, reg))); +else +dev_warn (di->dev,"unsupported config register %s\n" , str); + } +} + +static void bq27xxx_battery_update_dm_block (struct bq27xxx_device_info *di, +struct bq27xxx_dm_buf *buf, +enum bq27xxx_dm_reg_id reg_id, +unsigned int val) +{ +struct bq27xxx_dm_reg *reg = &bq27xxx_dm_regs[di->chip][reg_id]; +u16 *prev; + +if (reg->bytes !=2 )drivers/power/supply/bq27xxx_battery.c static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index, · line 706
Quoted 10 lines
drivers/power/supply/bq27xxx_battery.c +10
-0+return ; + + prev =BQ27XXX_DM_BUF_PTR (*buf, reg); +if (be16_to_cpup (prev) == val) +return ; + *prev =cpu_to_be16 (val); + +dev_info (di->dev,"update chip %d, class %u, block %u, offset %u, value %u\n" , + di->chip, buf->class, buf->block, reg->offset, val); +This should probably be
dev_debug, no normal user cares about all this detail.Quoted 30 lines
drivers/power/supply/bq27xxx_battery.c +30
-0+ buf->updt = true; +return ; +} + +static int bq27xxx_battery_write_dm_block (struct bq27xxx_device_info *di, +struct bq27xxx_dm_buf *buf) +{ +bool cfgup = di->chip ==BQ27425 || di->chip ==BQ27421 ;/* || BQ27441 || BQ27621 */ +int ret; + +if (cfgup) { + ret = di->bus.write (di,BQ27XXX_CONTROL ,BQ27XXX_SET_CFGUPDATE , false); +if (ret <0 ) + goto out1; + } + + ret = di->bus.write (di,BQ27XXX_BLOCK_DATA_CONTROL ,0 , true); +if (ret <0 ) + goto out2; + + ret = di->bus.write (di,BQ27XXX_DATA_CLASS , buf->class, true); +if (ret <0 ) + goto out2; + + ret = di->bus.write (di,BQ27XXX_DATA_BLOCK , buf->block, true); +if (ret <0 ) + goto out2; + +BQ27XXX_MSLEEP (1 ); +drivers/power/supply/bq27xxx_battery.c static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index, · line 746
Quoted 10 lines
drivers/power/supply/bq27xxx_battery.c +10
-0+ ret = di->bus.write_bulk (di,BQ27XXX_BLOCK_DATA , buf->a,sizeof buf->a); +if (ret <0 ) + goto out2; + + ret = di->bus.write (di,BQ27XXX_BLOCK_DATA_CHECKSUM , +bq27xxx_battery_checksum (buf), true); +if (ret <0 ) + goto out2; + +/* THE FOLLOWING CODE IS TOXIC. DO NOT USE! Then why include it, what is this code supposed to do, and what do we lose without it? If it is really needed we can find a way to guarantee the delay length.
Quoted 174 lines
drivers/power/supply/bq27xxx_battery.c +148
-1+* If the 350ms delay is insufficient, NVM corruption results +* on the '425 chip, which could damage the chip. +* It was suggested in this TI tool: +* http://git.ti.com/bms-linux/bqtool/blobs/master/gauge.c#line328 +* +* BQ27XXX_MSLEEP(350); +* ret = di->bus.write(di, BQ27XXX_DATA_BLOCK, buf->block, true); +* BQ27XXX_MSLEEP(1); +* ret = di->bus.read(di, BQ27XXX_BLOCK_DATA_CHECKSUM, true); +* if ((u8)ret != bq27xxx_battery_checksum(buf)) +* ret = -EINVAL; +*/ + +if (cfgup) { +BQ27XXX_MSLEEP (1 ); + ret = di->bus.write (di,BQ27XXX_CONTROL ,BQ27XXX_SOFT_RESET , false); +if (ret <0 ) + goto out1; + } + + buf->updt = false; +return 0 ; + +out2: +if (cfgup) + di->bus.write (di,BQ27XXX_CONTROL ,BQ27XXX_SOFT_RESET , false); +out1: +dev_err (di->dev,"bus error in %s: %d\n" , __func__, ret); +return ret; +} + +static void bq27xxx_battery_set_config (struct bq27xxx_device_info *di, +struct power_supply_battery_info *info) +{ +struct bq27xxx_dm_buf bd =BQ27XXX_DM_BUF (di,BQ27XXX_DM_DESIGN_ENERGY ); +struct bq27xxx_dm_buf bt =BQ27XXX_DM_BUF (di,BQ27XXX_DM_TERMINATE_VOLTAGE ); + +if (info->charge_full_design_uah != -EINVAL + && info->energy_full_design_uwh != -EINVAL ) { +bq27xxx_battery_read_dm_block (di, &bd); +if (bd.full) { +/* assume design energy & capacity are in same block */ +bq27xxx_battery_update_dm_block (di, &bd, +BQ27XXX_DM_DESIGN_CAPACITY , + info->charge_full_design_uah /1000 ); +bq27xxx_battery_update_dm_block (di, &bd, +BQ27XXX_DM_DESIGN_ENERGY , + info->energy_full_design_uwh /1000 ); + } + } + +if (info->voltage_min_design_uv != -EINVAL ) { +bool same = bd.full && bd.class == bt.class && bd.block == bt.block; +if (!same) +bq27xxx_battery_read_dm_block (di, &bt); +if (same ? bd.full : bt.full) +bq27xxx_battery_update_dm_block (di, same ? &bd : &bt, +BQ27XXX_DM_TERMINATE_VOLTAGE , + info->voltage_min_design_uv /1000 ); + } + +if (bd.updt) +bq27xxx_battery_write_dm_block (di, &bd); +if (bt.updt) +bq27xxx_battery_write_dm_block (di, &bt); +} + +void bq27xxx_battery_settings (struct bq27xxx_device_info *di) +{ +struct power_supply_battery_info info = {}; +unsigned int min, max; + +/* functions don't exist for writing data so abort */ +if (!di->bus.write || !di->bus.write_bulk) +return ; + +/* no settings to be set for this chipset so abort */ +if (!bq27xxx_dm_regs[di->chip]) +return ; + +if (bq27xxx_battery_set_seal_state (di, false) <0 ) +return ; + +if (power_supply_get_battery_info (di->bat, &info) <0 ) + goto out; + +if (info.energy_full_design_uwh != info.charge_full_design_uah) { +if (info.energy_full_design_uwh == -EINVAL ) +dev_warn (di->dev, +"missing battery:energy-full-design-microwatt-hours\n" ); +else if (info.charge_full_design_uah == -EINVAL ) +dev_warn (di->dev, +"missing battery:charge-full-design-microamp-hours\n" ); + } + +/* assume min == 0 */ + max = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_DESIGN_ENERGY ].max; +if (info.energy_full_design_uwh > max *1000 ) { +dev_err (di->dev, +"invalid battery:energy-full-design-microwatt-hours %d\n" , + info.energy_full_design_uwh); + info.energy_full_design_uwh = -EINVAL ; + } + +/* assume min == 0 */ + max = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_DESIGN_CAPACITY ].max; +if (info.charge_full_design_uah > max *1000 ) { +dev_err (di->dev, +"invalid battery:charge-full-design-microamp-hours %d\n" , + info.charge_full_design_uah); + info.charge_full_design_uah = -EINVAL ; + } + + min = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_TERMINATE_VOLTAGE ].min; + max = bq27xxx_dm_regs[di->chip][BQ27XXX_DM_TERMINATE_VOLTAGE ].max; +if ((info.voltage_min_design_uv < min *1000 + || info.voltage_min_design_uv > max *1000 ) + && info.voltage_min_design_uv != -EINVAL ) { +dev_err (di->dev, +"invalid battery:voltage-min-design-microvolt %d\n" , + info.voltage_min_design_uv); + info.voltage_min_design_uv = -EINVAL ; + } + +if ((info.energy_full_design_uwh == -EINVAL + || info.charge_full_design_uah == -EINVAL ) + && info.voltage_min_design_uv == -EINVAL ) + goto out; + +bq27xxx_battery_set_config (di, &info); + +out: +bq27xxx_battery_print_config (di); +bq27xxx_battery_set_seal_state (di, true); +} +/* * Return the battery State-of-Charge * Or < 0 if something fails. @@ -1006,6 +1390,13 @@ static int bq27xxx_battery_get_property(struct power_supply *psy,case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN : ret =bq27xxx_simple_value (di->charge_design_full, val);break ; +/* +* TODO: Implement these to make registers set from +* power_supply_battery_info visible in sysfs. +*/ +case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN : +case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN : +return -EINVAL ;case POWER_SUPPLY_PROP_CYCLE_COUNT : ret =bq27xxx_simple_value (di->cache.cycle_count, val);break ;@@ -1039,7 +1430,10 @@ static void bq27xxx_external_power_changed(struct power_supply *psy)int bq27xxx_battery_setup (struct bq27xxx_device_info *di) {struct power_supply_desc *psy_desc; -struct power_supply_config psy_cfg = { .drv_data = di, }; +struct power_supply_config psy_cfg = { + .of_node = di->dev->of_node, + .drv_data = di, + };INIT_DELAYED_WORK (&di->work, bq27xxx_battery_poll);mutex_init (&di->lock);@@ -1064,6 +1458,7 @@ int bq27xxx_battery_setup(struct bq27xxx_device_info *di)dev_info (di->dev,"support ver. %s enabled\n" ,DRIVER_VERSION ); +bq27xxx_battery_settings (di);bq27xxx_battery_update (di);mutex_lock (&bq27xxx_list_lock);
PATCH v7 8/9 power: bq27xxx_battery: Add print_dm() to log chip memory
In reply to Liam BreckFrom: Liam Breck <[email protected]>
Enable listing of chip RAM and/or NVM at startup, for debugging use. Intially supports BQ27425 State NVM, subclass 82.
Support for other chips may be added.
--- drivers/power/supply/bq27xxx_battery.c | 51 ++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+)
drivers/power/supply/bq27xxx_battery.c +51 -0
@@ -664,6 +664,55 @@ static int bq27xxx_battery_read_dm_block(struct bq27xxx_device_info *di,return ret; } +/* Translate f4 floating point values to/from hexadecimal: +* perl -e 'printf("%08x\n", unpack("I", pack("f", 3.93e-4 )))' # 39ce0b91 +* perl -e 'printf("%f\n" , unpack("f", pack("I", 0x39ce0b91)))' # 0.000393 +*/ + +#define BQ27XXX_REG (f,o,v) dev_info(di->dev, "offset %d, " f "\n", o, v) + +static void bq27xxx_battery_print_dm (struct bq27xxx_device_info *di) { +enum { h1, h2, i2, u1, f4 }; + +struct dm_reg {int offset, type; } +/* each array row describes one data block */ + c425_s82[][10 ] = { + { {2 ,h1},{3 ,i2},{5 ,h2},{12 ,i2},{14 ,i2},{18 ,i2},{22 ,i2},{29 ,u1},{30 ,i2},{99 ,0 } }, + { {32 ,i2},{34 ,i2},{36 ,i2},{38 ,u1},{39 ,u1},{40 ,f4},{99 ,0 } }, + }; +/* add more subclass maps here */ + +struct dm_class {int id, len;struct dm_reg (*reg)[10 ]; } + c425[] = { + { .id =82 , .len =2 , .reg = c425_s82 }, + }; +/* add more chip maps here */ + +struct dm_class *chip = c425;/* set to your chip */ + +struct bq27xxx_dm_buf buf = { }; +int c, b, r; +for (c=0 ; c <1 ; ++c) { + buf.class = chip[c].id; +dev_info (di->dev,"subclass %d registers...\n" , chip[c].id); +for (b=0 ; b < chip[c].len; ++b) { + buf.block = b; +bq27xxx_battery_read_dm_block (di, &buf); +for (r=0 ; chip[c].reg[b][r].offset !=99 ; ++r) { +int o = chip[c].reg[b][r].offset; +u8 * p = &buf.a[o %sizeof buf.a]; +switch (chip[c].reg[b][r].type) { +case h1:BQ27XXX_REG ("%02x" , o, *p);break ; +case h2:BQ27XXX_REG ("%04x" , o,be16_to_cpup ((u16 *)p));break ; +case i2:BQ27XXX_REG ("%d" , o, (s16 )be16_to_cpup ((u16 *)p));break ; +case u1:BQ27XXX_REG ("%u" , o, *p);break ; +case f4:BQ27XXX_REG ("%08x" , o,be32_to_cpup ((u32 *)p));break ; + } + } + } + } +} +static void bq27xxx_battery_print_config (struct bq27xxx_device_info *di) {struct bq27xxx_dm_reg *reg = bq27xxx_dm_regs[di->chip];
@@ -683,6 +732,8 @@ static void bq27xxx_battery_print_config(struct bq27xxx_device_info *di)else dev_warn (di->dev,"unsupported config register %s\n" , str); } +/* bq27xxx_battery_print_dm(di); uncomment for debugging */ + (void )bq27xxx_battery_print_dm;/* prevent compiler warning */ } static void bq27xxx_battery_update_dm_block(struct bq27xxx_device_info *di,
Signature · 1 line
2.9.3
Re: [PATCH v7 8/9] power: bq27xxx_battery: Add print_dm() to log chip memory
Quoted 2 lines · Liam Breck
From: Liam Breck <[email protected]>
Do you prefer kernel@ or liam@, you should probably pick one and use it uniformly.
This patch is nice for debugging, but not upstreamable, it is full of checkpatch and formatting issues, it should probably just be dropped from this series.
Quoted 79 lines
Enable listing of chip RAM and/or NVM at startup, for debugging use. Intially supports BQ27425 State NVM, subclass 82.
Support for other chips may be added.Signed-off-by Liam Breck <[email protected]>--- drivers/power/supply/bq27xxx_battery.c | 51 ++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+)drivers/power/supply/bq27xxx_battery.c +51
-0@@ -664,6 +664,55 @@ static int bq27xxx_battery_read_dm_block(struct bq27xxx_device_info *di,return ret; } +/* Translate f4 floating point values to/from hexadecimal: +* perl -e 'printf("%08x\n", unpack("I", pack("f", 3.93e-4 )))' # 39ce0b91 +* perl -e 'printf("%f\n" , unpack("f", pack("I", 0x39ce0b91)))' # 0.000393 +*/ + +#define BQ27XXX_REG (f,o,v) dev_info(di->dev, "offset %d, " f "\n", o, v) + +static void bq27xxx_battery_print_dm (struct bq27xxx_device_info *di) { +enum { h1, h2, i2, u1, f4 }; + +struct dm_reg {int offset, type; } +/* each array row describes one data block */ + c425_s82[][10 ] = { + { {2 ,h1},{3 ,i2},{5 ,h2},{12 ,i2},{14 ,i2},{18 ,i2},{22 ,i2},{29 ,u1},{30 ,i2},{99 ,0 } }, + { {32 ,i2},{34 ,i2},{36 ,i2},{38 ,u1},{39 ,u1},{40 ,f4},{99 ,0 } }, + }; +/* add more subclass maps here */ + +struct dm_class {int id, len;struct dm_reg (*reg)[10 ]; } + c425[] = { + { .id =82 , .len =2 , .reg = c425_s82 }, + }; +/* add more chip maps here */ + +struct dm_class *chip = c425;/* set to your chip */ + +struct bq27xxx_dm_buf buf = { }; +int c, b, r; +for (c=0 ; c <1 ; ++c) { + buf.class = chip[c].id; +dev_info (di->dev,"subclass %d registers...\n" , chip[c].id); +for (b=0 ; b < chip[c].len; ++b) { + buf.block = b; +bq27xxx_battery_read_dm_block (di, &buf); +for (r=0 ; chip[c].reg[b][r].offset !=99 ; ++r) { +int o = chip[c].reg[b][r].offset; +u8 * p = &buf.a[o %sizeof buf.a]; +switch (chip[c].reg[b][r].type) { +case h1:BQ27XXX_REG ("%02x" , o, *p);break ; +case h2:BQ27XXX_REG ("%04x" , o,be16_to_cpup ((u16 *)p));break ; +case i2:BQ27XXX_REG ("%d" , o, (s16 )be16_to_cpup ((u16 *)p));break ; +case u1:BQ27XXX_REG ("%u" , o, *p);break ; +case f4:BQ27XXX_REG ("%08x" , o,be32_to_cpup ((u32 *)p));break ; + } + } + } + } +} +static void bq27xxx_battery_print_config (struct bq27xxx_device_info *di) {struct bq27xxx_dm_reg *reg = bq27xxx_dm_regs[di->chip];@@ -683,6 +732,8 @@ static void bq27xxx_battery_print_config(struct bq27xxx_device_info *di)else dev_warn (di->dev,"unsupported config register %s\n" , str); } +/* bq27xxx_battery_print_dm(di); uncomment for debugging */ + (void )bq27xxx_battery_print_dm;/* prevent compiler warning */ } static void bq27xxx_battery_update_dm_block(struct bq27xxx_device_info *di,
Re: [PATCH v7 8/9] power: bq27xxx_battery: Add print_dm() to log chip memory
Quoted 10 lines · Andrew F. Davis
Quoted 2 lines · Liam Breck
From: Liam Breck <[email protected]>
Do you prefer kernel@ or liam@, you should probably pick one and use it uniformly.
This patch is nice for debugging, but not upstreamable, it is full of checkpatch and formatting issues, it should probably just be dropped from this series.
Your hw developer customers will want this.
It's up to you whether every line of code in bq27xxx must fit the format guidelines. I can adjust the format. Or leave it in commented out?
Quoted 79 lines
Quoted 79 lines
Enable listing of chip RAM and/or NVM at startup, for debugging use. Intially supports BQ27425 State NVM, subclass 82.
Support for other chips may be added.Signed-off-by Liam Breck <[email protected]>--- drivers/power/supply/bq27xxx_battery.c | 51 ++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+)drivers/power/supply/bq27xxx_battery.c +51
-0@@ -664,6 +664,55 @@ static int bq27xxx_battery_read_dm_block(struct bq27xxx_device_info *di,return ret; } +/* Translate f4 floating point values to/from hexadecimal: +* perl -e 'printf("%08x\n", unpack("I", pack("f", 3.93e-4 )))' # 39ce0b91 +* perl -e 'printf("%f\n" , unpack("f", pack("I", 0x39ce0b91)))' # 0.000393 +*/ + +#define BQ27XXX_REG (f,o,v) dev_info(di->dev, "offset %d, " f "\n", o, v) + +static void bq27xxx_battery_print_dm (struct bq27xxx_device_info *di) { +enum { h1, h2, i2, u1, f4 }; + +struct dm_reg {int offset, type; } +/* each array row describes one data block */ + c425_s82[][10 ] = { + { {2 ,h1},{3 ,i2},{5 ,h2},{12 ,i2},{14 ,i2},{18 ,i2},{22 ,i2},{29 ,u1},{30 ,i2},{99 ,0 } }, + { {32 ,i2},{34 ,i2},{36 ,i2},{38 ,u1},{39 ,u1},{40 ,f4},{99 ,0 } }, + }; +/* add more subclass maps here */ + +struct dm_class {int id, len;struct dm_reg (*reg)[10 ]; } + c425[] = { + { .id =82 , .len =2 , .reg = c425_s82 }, + }; +/* add more chip maps here */ + +struct dm_class *chip = c425;/* set to your chip */ + +struct bq27xxx_dm_buf buf = { }; +int c, b, r; +for (c=0 ; c <1 ; ++c) { + buf.class = chip[c].id; +dev_info (di->dev,"subclass %d registers...\n" , chip[c].id); +for (b=0 ; b < chip[c].len; ++b) { + buf.block = b; +bq27xxx_battery_read_dm_block (di, &buf); +for (r=0 ; chip[c].reg[b][r].offset !=99 ; ++r) { +int o = chip[c].reg[b][r].offset; +u8 * p = &buf.a[o %sizeof buf.a]; +switch (chip[c].reg[b][r].type) { +case h1:BQ27XXX_REG ("%02x" , o, *p);break ; +case h2:BQ27XXX_REG ("%04x" , o,be16_to_cpup ((u16 *)p));break ; +case i2:BQ27XXX_REG ("%d" , o, (s16 )be16_to_cpup ((u16 *)p));break ; +case u1:BQ27XXX_REG ("%u" , o, *p);break ; +case f4:BQ27XXX_REG ("%08x" , o,be32_to_cpup ((u32 *)p));break ; + } + } + } + } +} +static void bq27xxx_battery_print_config (struct bq27xxx_device_info *di) {struct bq27xxx_dm_reg *reg = bq27xxx_dm_regs[di->chip];@@ -683,6 +732,8 @@ static void bq27xxx_battery_print_config(struct bq27xxx_device_info *di)else dev_warn (di->dev,"unsupported config register %s\n" , str); } +/* bq27xxx_battery_print_dm(di); uncomment for debugging */ + (void )bq27xxx_battery_print_dm;/* prevent compiler warning */ } static void bq27xxx_battery_update_dm_block(struct bq27xxx_device_info *di,
Re: [PATCH v7 8/9] power: bq27xxx_battery: Add print_dm() to log chip memory
Quoted 5 lines · Liam Breck
Quoted 4 lines · Andrew F. Davis
Quoted 2 lines · Liam Breck
From: Liam Breck <[email protected]>
Quoted 9 lines
Quoted 6 lines
Do you prefer kernel@ or liam@, you should probably pick one and use it uniformly.
This patch is nice for debugging, but not upstreamable, it is full of checkpatch and formatting issues, it should probably just be dropped from this series.
Your hw developer customers will want this.
The kernel is not for hw developers, we have our evil vendor trees for this stuff. That said, I have no problem with this here, but all the print outs should at least dev_debug.
Quoted 4 lines
It's up to you whether every line of code in bq27xxx must fit the format guidelines. I can adjust the format. Or leave it in commented out?
It's not really up to me, Sebastian has the real final call, I would just cleanup the format.
Quoted 79 lines
Quoted 79 lines
Quoted 79 lines
Enable listing of chip RAM and/or NVM at startup, for debugging use. Intially supports BQ27425 State NVM, subclass 82.
Support for other chips may be added.Signed-off-by Liam Breck <[email protected]>--- drivers/power/supply/bq27xxx_battery.c | 51 ++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+)drivers/power/supply/bq27xxx_battery.c +51
-0@@ -664,6 +664,55 @@ static int bq27xxx_battery_read_dm_block(struct bq27xxx_device_info *di,return ret; } +/* Translate f4 floating point values to/from hexadecimal: +* perl -e 'printf("%08x\n", unpack("I", pack("f", 3.93e-4 )))' # 39ce0b91 +* perl -e 'printf("%f\n" , unpack("f", pack("I", 0x39ce0b91)))' # 0.000393 +*/ + +#define BQ27XXX_REG (f,o,v) dev_info(di->dev, "offset %d, " f "\n", o, v) + +static void bq27xxx_battery_print_dm (struct bq27xxx_device_info *di) { +enum { h1, h2, i2, u1, f4 }; + +struct dm_reg {int offset, type; } +/* each array row describes one data block */ + c425_s82[][10 ] = { + { {2 ,h1},{3 ,i2},{5 ,h2},{12 ,i2},{14 ,i2},{18 ,i2},{22 ,i2},{29 ,u1},{30 ,i2},{99 ,0 } }, + { {32 ,i2},{34 ,i2},{36 ,i2},{38 ,u1},{39 ,u1},{40 ,f4},{99 ,0 } }, + }; +/* add more subclass maps here */ + +struct dm_class {int id, len;struct dm_reg (*reg)[10 ]; } + c425[] = { + { .id =82 , .len =2 , .reg = c425_s82 }, + }; +/* add more chip maps here */ + +struct dm_class *chip = c425;/* set to your chip */ + +struct bq27xxx_dm_buf buf = { }; +int c, b, r; +for (c=0 ; c <1 ; ++c) { + buf.class = chip[c].id; +dev_info (di->dev,"subclass %d registers...\n" , chip[c].id); +for (b=0 ; b < chip[c].len; ++b) { + buf.block = b; +bq27xxx_battery_read_dm_block (di, &buf); +for (r=0 ; chip[c].reg[b][r].offset !=99 ; ++r) { +int o = chip[c].reg[b][r].offset; +u8 * p = &buf.a[o %sizeof buf.a]; +switch (chip[c].reg[b][r].type) { +case h1:BQ27XXX_REG ("%02x" , o, *p);break ; +case h2:BQ27XXX_REG ("%04x" , o,be16_to_cpup ((u16 *)p));break ; +case i2:BQ27XXX_REG ("%d" , o, (s16 )be16_to_cpup ((u16 *)p));break ; +case u1:BQ27XXX_REG ("%u" , o, *p);break ; +case f4:BQ27XXX_REG ("%08x" , o,be32_to_cpup ((u32 *)p));break ; + } + } + } + } +} +static void bq27xxx_battery_print_config (struct bq27xxx_device_info *di) {struct bq27xxx_dm_reg *reg = bq27xxx_dm_regs[di->chip];@@ -683,6 +732,8 @@ static void bq27xxx_battery_print_config(struct bq27xxx_device_info *di)else dev_warn (di->dev,"unsupported config register %s\n" , str); } +/* bq27xxx_battery_print_dm(di); uncomment for debugging */ + (void )bq27xxx_battery_print_dm;/* prevent compiler warning */ } static void bq27xxx_battery_update_dm_block(struct bq27xxx_device_info *di,
PATCH v7 9/9 power: bq27xxx_battery_i2c: Add I2C bulk read/write functions
In reply to Liam BreckFrom: 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
@@ -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
Hi Andrew,
I have a new rev ready,with support for bq27500 & 545 & 421. How soon could you test it? If this week, I'll post ASAP...
Quoted 83 lines · Liam Breck
Overview:
* new devicetree battery node specifies static battery data
* fuel gauge and charger nodes shall use monitored-battery=<&battery_node>
* newpower_supply_get_battery_info()reads battery data from devicetree
* new structpower_supply_battery_infoprovides 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-microvoltChanges 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:
* mentionpower_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 ofbulk_readChanges in v5:
* incorporate feedback intoDocumentation/devicetree/.../battery.txt
* use power_supply_prop_* names in devicetree andpower_supply_battery_info
* default fields to-EINVALinpower_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 forpower_supply_battery_info
*bq27xxx_battery: checkpower_supply_battery_infovalues
*bq27xxx_battery: note missing power_supply_prop_* features
*bq27xxx_battery: new patch for access methodsChanges in v4:
* add "fixed-battery" compatible field to be be more consistant with devicetreeChanges 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 functionChanges in v2:
* add documentation for uWh and uAh property units
* change devicetree entries to match new property unitsLiam (5):
devicetree: power: Add battery.txt
devicetree: power: bq27xxx: Add monitored-battery documentation
power:power_supply: Addpower_supply_battery_infoand API
power:bq27xxx_battery: Addpower_supply_battery_infosupport
power:bq27xxx_battery: Addprint_dm()to log chip memoryMatt Ranostay (4):
devicetree: property-units: Add uWh and uAh units
power:bq27xxx_battery: Define access methods to write chip registers
power:bq27xxx_battery: Add BQ27425 chip id
power:bq27xxx_battery_i2c: Add I2C bulk read/write functions.../devicetree/bindings/power/supply/battery.txt | 42 ++ .../devicetree/bindings/power/supply/bq27xxx.txt | 11 +- .../devicetree/bindings/property-units.txt | 2 + drivers/power/supply/bq27xxx_battery.c | 473 ++++++++++++++++++++- drivers/power/supply/bq27xxx_battery_i2c.c | 84 +++- drivers/power/supply/power_supply_core.c | 40 ++ include/linux/power/bq27xxx_battery.h | 6 +- include/linux/power_supply.h | 18 + 8 files changed, 669 insertions(+), 7 deletions(-) create mode 100644 Documentation/devicetree/bindings/power/supply/battery.txt--
2.9.3
Quoted 5 lines · Liam Breck
Hi Andrew,
I have a new rev ready,with support for bq27500 & 545 & 421. How soon could you test it? If this week, I'll post ASAP...
Probably no time this week, so take your time. Although it never hurts to post.
Andrew
Quoted 85 lines · Liam Breck
Quoted 83 lines
Overview:
* new devicetree battery node specifies static battery data
* fuel gauge and charger nodes shall use monitored-battery=<&battery_node>
* newpower_supply_get_battery_info()reads battery data from devicetree
* new structpower_supply_battery_infoprovides 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-microvoltChanges 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:
* mentionpower_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 ofbulk_readChanges in v5:
* incorporate feedback intoDocumentation/devicetree/.../battery.txt
* use power_supply_prop_* names in devicetree andpower_supply_battery_info
* default fields to-EINVALinpower_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 forpower_supply_battery_info
*bq27xxx_battery: checkpower_supply_battery_infovalues
*bq27xxx_battery: note missing power_supply_prop_* features
*bq27xxx_battery: new patch for access methodsChanges in v4:
* add "fixed-battery" compatible field to be be more consistant with devicetreeChanges 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 functionChanges in v2:
* add documentation for uWh and uAh property units
* change devicetree entries to match new property unitsLiam (5):
devicetree: power: Add battery.txt
devicetree: power: bq27xxx: Add monitored-battery documentation
power:power_supply: Addpower_supply_battery_infoand API
power:bq27xxx_battery: Addpower_supply_battery_infosupport
power:bq27xxx_battery: Addprint_dm()to log chip memoryMatt Ranostay (4):
devicetree: property-units: Add uWh and uAh units
power:bq27xxx_battery: Define access methods to write chip registers
power:bq27xxx_battery: Add BQ27425 chip id
power:bq27xxx_battery_i2c: Add I2C bulk read/write functions.../devicetree/bindings/power/supply/battery.txt | 42 ++ .../devicetree/bindings/power/supply/bq27xxx.txt | 11 +- .../devicetree/bindings/property-units.txt | 2 + drivers/power/supply/bq27xxx_battery.c | 473 ++++++++++++++++++++- drivers/power/supply/bq27xxx_battery_i2c.c | 84 +++- drivers/power/supply/power_supply_core.c | 40 ++ include/linux/power/bq27xxx_battery.h | 6 +- include/linux/power_supply.h | 18 + 8 files changed, 669 insertions(+), 7 deletions(-) create mode 100644 Documentation/devicetree/bindings/power/supply/battery.txt--
2.9.3