[RFC 5/5] power: supply: bq27xxx: Add support for ACPI enumeration

From: Hans de Goede
Date: Sun Oct 31 2021 - 12:25:19 EST


Some x86/ACPI devices with a bq27xxx fuel-gauge do not use the standard
ACPI battery API for battery monitoring. Instead they have an ACPI
device describing the actual fuel-gauge IC and expect the OS to have
a native driver for the fuel-gauge.

Add support for such ACPI enumerated bq27xxx fuel-gauges.

To get the fuel-gauge to work properly on ACPI devs 3 changes are needed:

1. Add an acpi_match_table and set di->chip based on this, note the
new "if (id) check also fixes a NULL pointer deref when someone tries to
manually bind the driver from sysfs.

2. When the charger IC has external power applied or removed, the psy-core
should call bq27xxx_external_power_changed(). This requires a valid
consumer<->supplier link between the charger and the fuel-gauge
power-supplies. For ACPI enumerated fuel-gauges this link is missing.

The charger-ICs used on these devices already contain a special
power_supply_config.supplied_to default set to "main-battery".
This commit makes use of this by setting the power-supply's name to
"main-battery" when enumerated by ACPI, to establish the missing
consumer<->supplier link.

3. Sometimes the irqflags in the ACPI tables are no good, override them.

Signed-off-by: Hans de Goede <hdegoede@xxxxxxxxxx>
---
drivers/power/supply/bq27xxx_battery_i2c.c | 38 ++++++++++++++++++----
1 file changed, 32 insertions(+), 6 deletions(-)

diff --git a/drivers/power/supply/bq27xxx_battery_i2c.c b/drivers/power/supply/bq27xxx_battery_i2c.c
index 0a1b922389e1..c3fe1e4b08bd 100644
--- a/drivers/power/supply/bq27xxx_battery_i2c.c
+++ b/drivers/power/supply/bq27xxx_battery_i2c.c
@@ -6,6 +6,7 @@
* Andrew F. Davis <afd@xxxxxx>
*/

+#include <linux/acpi.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/module.h>
@@ -139,8 +140,10 @@ static int bq27xxx_battery_i2c_bulk_write(struct bq27xxx_device_info *di,
static int bq27xxx_battery_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
+ const struct acpi_device_id *acpi_id = NULL;
struct device *dev = &client->dev;
struct bq27xxx_device_info *di;
+ unsigned long irqflags = 0;
int ret;
char *name;
int num;
@@ -152,17 +155,31 @@ static int bq27xxx_battery_i2c_probe(struct i2c_client *client,
if (num < 0)
return num;

- name = devm_kasprintf(dev, GFP_KERNEL, "%s-%d", id->name, num);
- if (!name)
- goto err_mem;
-
di = devm_kzalloc(dev, sizeof(*di), GFP_KERNEL);
if (!di)
goto err_mem;

+ if (id) {
+ di->chip = id->driver_data;
+ } else {
+ acpi_id = acpi_match_device(dev->driver->acpi_match_table, dev);
+ if (!acpi_id)
+ return -ENODEV;
+
+ irqflags = IRQF_TRIGGER_RISING;
+ di->chip = acpi_id->driver_data;
+ }
+
+ if (acpi_id && num == 0) {
+ name = "main-battery";
+ } else {
+ name = devm_kasprintf(dev, GFP_KERNEL, "%s-%d", id->name, num);
+ if (!name)
+ goto err_mem;
+ }
+
di->id = num;
di->dev = dev;
- di->chip = id->driver_data;
di->name = name;

di->bus.read = bq27xxx_battery_i2c_read;
@@ -182,7 +199,7 @@ static int bq27xxx_battery_i2c_probe(struct i2c_client *client,
if (client->irq) {
ret = devm_request_threaded_irq(dev, client->irq,
NULL, bq27xxx_battery_irq_handler_thread,
- IRQF_ONESHOT,
+ irqflags | IRQF_ONESHOT,
di->name, di);
if (ret) {
dev_err(dev, "Unable to register IRQ %d error %d\n",
@@ -292,10 +309,19 @@ static const struct of_device_id bq27xxx_battery_i2c_of_match_table[] = {
MODULE_DEVICE_TABLE(of, bq27xxx_battery_i2c_of_match_table);
#endif

+#ifdef CONFIG_ACPI
+static const struct acpi_device_id bq27xxx_acpi_match[] = {
+ { "TXN27520", BQ2752X },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, bq27xxx_acpi_match);
+#endif
+
static struct i2c_driver bq27xxx_battery_i2c_driver = {
.driver = {
.name = "bq27xxx-battery",
.of_match_table = of_match_ptr(bq27xxx_battery_i2c_of_match_table),
+ .acpi_match_table = ACPI_PTR(bq27xxx_acpi_match),
},
.probe = bq27xxx_battery_i2c_probe,
.remove = bq27xxx_battery_i2c_remove,
--
2.31.1