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