summaryrefslogtreecommitdiff
path: root/drivers/power/supply/bq27xxx_battery_i2c.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/power/supply/bq27xxx_battery_i2c.c')
-rw-r--r--drivers/power/supply/bq27xxx_battery_i2c.c94
1 files changed, 51 insertions, 43 deletions
diff --git a/drivers/power/supply/bq27xxx_battery_i2c.c b/drivers/power/supply/bq27xxx_battery_i2c.c
index 2677c38a8a42..868e95f0887e 100644
--- a/drivers/power/supply/bq27xxx_battery_i2c.c
+++ b/drivers/power/supply/bq27xxx_battery_i2c.c
@@ -1,28 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* BQ27xxx battery monitor I2C driver
*
- * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
+ * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com/
* Andrew F. Davis <afd@ti.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
+#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/module.h>
-#include <asm/unaligned.h>
+#include <linux/unaligned.h>
#include <linux/power/bq27xxx_battery.h>
-static DEFINE_IDR(battery_id);
-static DEFINE_MUTEX(battery_mutex);
+static DEFINE_IDA(battery_id);
static irqreturn_t bq27xxx_battery_irq_handler_thread(int irq, void *data)
{
@@ -40,6 +32,7 @@ static int bq27xxx_battery_i2c_read(struct bq27xxx_device_info *di, u8 reg,
struct i2c_msg msg[2];
u8 data[2];
int ret;
+ int retry = 0;
if (!client->adapter)
return -ENODEV;
@@ -56,7 +49,16 @@ static int bq27xxx_battery_i2c_read(struct bq27xxx_device_info *di, u8 reg,
else
msg[1].len = 2;
- ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
+ do {
+ ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
+ if (ret == -EBUSY && ++retry < 3) {
+ /* sleep 10 milliseconds when busy */
+ usleep_range(10000, 11000);
+ continue;
+ }
+ break;
+ } while (1);
+
if (ret < 0)
return ret;
@@ -144,30 +146,39 @@ static int bq27xxx_battery_i2c_bulk_write(struct bq27xxx_device_info *di,
return 0;
}
-static int bq27xxx_battery_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+static void bq27xxx_battery_i2c_devm_ida_free(void *data)
+{
+ int num = (long)data;
+
+ ida_free(&battery_id, num);
+}
+
+static int bq27xxx_battery_i2c_probe(struct i2c_client *client)
{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
struct bq27xxx_device_info *di;
int ret;
char *name;
- int num;
+ long num;
/* Get new ID for the new battery device */
- mutex_lock(&battery_mutex);
- num = idr_alloc(&battery_id, client, 0, 0, GFP_KERNEL);
- mutex_unlock(&battery_mutex);
+ num = ida_alloc(&battery_id, GFP_KERNEL);
if (num < 0)
return num;
+ ret = devm_add_action_or_reset(&client->dev,
+ bq27xxx_battery_i2c_devm_ida_free,
+ (void *)num);
+ if (ret)
+ return ret;
- name = devm_kasprintf(&client->dev, GFP_KERNEL, "%s-%d", id->name, num);
+ name = devm_kasprintf(&client->dev, GFP_KERNEL, "%s-%ld", id->name, num);
if (!name)
- goto err_mem;
+ return -ENOMEM;
di = devm_kzalloc(&client->dev, sizeof(*di), GFP_KERNEL);
if (!di)
- goto err_mem;
+ return -ENOMEM;
- di->id = num;
di->dev = &client->dev;
di->chip = id->driver_data;
di->name = name;
@@ -179,7 +190,7 @@ static int bq27xxx_battery_i2c_probe(struct i2c_client *client,
ret = bq27xxx_battery_setup(di);
if (ret)
- goto err_failed;
+ return ret;
/* Schedule a polling after about 1 min */
schedule_delayed_work(&di->work, 60 * HZ);
@@ -187,7 +198,7 @@ static int bq27xxx_battery_i2c_probe(struct i2c_client *client,
i2c_set_clientdata(client, di);
if (client->irq) {
- ret = devm_request_threaded_irq(&client->dev, client->irq,
+ ret = request_threaded_irq(client->irq,
NULL, bq27xxx_battery_irq_handler_thread,
IRQF_ONESHOT,
di->name, di);
@@ -195,34 +206,22 @@ static int bq27xxx_battery_i2c_probe(struct i2c_client *client,
dev_err(&client->dev,
"Unable to register IRQ %d error %d\n",
client->irq, ret);
+ bq27xxx_battery_teardown(di);
return ret;
}
}
return 0;
-
-err_mem:
- ret = -ENOMEM;
-
-err_failed:
- mutex_lock(&battery_mutex);
- idr_remove(&battery_id, num);
- mutex_unlock(&battery_mutex);
-
- return ret;
}
-static int bq27xxx_battery_i2c_remove(struct i2c_client *client)
+static void bq27xxx_battery_i2c_remove(struct i2c_client *client)
{
struct bq27xxx_device_info *di = i2c_get_clientdata(client);
- bq27xxx_battery_teardown(di);
-
- mutex_lock(&battery_mutex);
- idr_remove(&battery_id, di->id);
- mutex_unlock(&battery_mutex);
+ if (client->irq)
+ free_irq(client->irq, di);
- return 0;
+ bq27xxx_battery_teardown(di);
}
static const struct i2c_device_id bq27xxx_i2c_id_table[] = {
@@ -253,6 +252,10 @@ static const struct i2c_device_id bq27xxx_i2c_id_table[] = {
{ "bq27426", BQ27426 },
{ "bq27441", BQ27441 },
{ "bq27621", BQ27621 },
+ { "bq27z561", BQ27Z561 },
+ { "bq28z610", BQ28Z610 },
+ { "bq34z100", BQ34Z100 },
+ { "bq78z100", BQ78Z100 },
{},
};
MODULE_DEVICE_TABLE(i2c, bq27xxx_i2c_id_table);
@@ -286,6 +289,10 @@ static const struct of_device_id bq27xxx_battery_i2c_of_match_table[] = {
{ .compatible = "ti,bq27426" },
{ .compatible = "ti,bq27441" },
{ .compatible = "ti,bq27621" },
+ { .compatible = "ti,bq27z561" },
+ { .compatible = "ti,bq28z610" },
+ { .compatible = "ti,bq34z100" },
+ { .compatible = "ti,bq78z100" },
{},
};
MODULE_DEVICE_TABLE(of, bq27xxx_battery_i2c_of_match_table);
@@ -295,6 +302,7 @@ static struct i2c_driver bq27xxx_battery_i2c_driver = {
.driver = {
.name = "bq27xxx-battery",
.of_match_table = of_match_ptr(bq27xxx_battery_i2c_of_match_table),
+ .pm = &bq27xxx_battery_battery_pm_ops,
},
.probe = bq27xxx_battery_i2c_probe,
.remove = bq27xxx_battery_i2c_remove,