xref: /linux/drivers/power/supply/bq27xxx_battery_i2c.c (revision 24bce201d79807b668bf9d9e0aca801c5c0d5f78)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * BQ27xxx battery monitor I2C driver
4  *
5  * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com/
6  *	Andrew F. Davis <afd@ti.com>
7  */
8 
9 #include <linux/i2c.h>
10 #include <linux/interrupt.h>
11 #include <linux/module.h>
12 #include <asm/unaligned.h>
13 
14 #include <linux/power/bq27xxx_battery.h>
15 
16 static DEFINE_IDR(battery_id);
17 static DEFINE_MUTEX(battery_mutex);
18 
19 static irqreturn_t bq27xxx_battery_irq_handler_thread(int irq, void *data)
20 {
21 	struct bq27xxx_device_info *di = data;
22 
23 	bq27xxx_battery_update(di);
24 
25 	return IRQ_HANDLED;
26 }
27 
28 static int bq27xxx_battery_i2c_read(struct bq27xxx_device_info *di, u8 reg,
29 				    bool single)
30 {
31 	struct i2c_client *client = to_i2c_client(di->dev);
32 	struct i2c_msg msg[2];
33 	u8 data[2];
34 	int ret;
35 
36 	if (!client->adapter)
37 		return -ENODEV;
38 
39 	msg[0].addr = client->addr;
40 	msg[0].flags = 0;
41 	msg[0].buf = &reg;
42 	msg[0].len = sizeof(reg);
43 	msg[1].addr = client->addr;
44 	msg[1].flags = I2C_M_RD;
45 	msg[1].buf = data;
46 	if (single)
47 		msg[1].len = 1;
48 	else
49 		msg[1].len = 2;
50 
51 	ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
52 	if (ret < 0)
53 		return ret;
54 
55 	if (!single)
56 		ret = get_unaligned_le16(data);
57 	else
58 		ret = data[0];
59 
60 	return ret;
61 }
62 
63 static int bq27xxx_battery_i2c_write(struct bq27xxx_device_info *di, u8 reg,
64 				     int value, bool single)
65 {
66 	struct i2c_client *client = to_i2c_client(di->dev);
67 	struct i2c_msg msg;
68 	u8 data[4];
69 	int ret;
70 
71 	if (!client->adapter)
72 		return -ENODEV;
73 
74 	data[0] = reg;
75 	if (single) {
76 		data[1] = (u8) value;
77 		msg.len = 2;
78 	} else {
79 		put_unaligned_le16(value, &data[1]);
80 		msg.len = 3;
81 	}
82 
83 	msg.buf = data;
84 	msg.addr = client->addr;
85 	msg.flags = 0;
86 
87 	ret = i2c_transfer(client->adapter, &msg, 1);
88 	if (ret < 0)
89 		return ret;
90 	if (ret != 1)
91 		return -EINVAL;
92 	return 0;
93 }
94 
95 static int bq27xxx_battery_i2c_bulk_read(struct bq27xxx_device_info *di, u8 reg,
96 					 u8 *data, int len)
97 {
98 	struct i2c_client *client = to_i2c_client(di->dev);
99 	int ret;
100 
101 	if (!client->adapter)
102 		return -ENODEV;
103 
104 	ret = i2c_smbus_read_i2c_block_data(client, reg, len, data);
105 	if (ret < 0)
106 		return ret;
107 	if (ret != len)
108 		return -EINVAL;
109 	return 0;
110 }
111 
112 static int bq27xxx_battery_i2c_bulk_write(struct bq27xxx_device_info *di,
113 					  u8 reg, u8 *data, int len)
114 {
115 	struct i2c_client *client = to_i2c_client(di->dev);
116 	struct i2c_msg msg;
117 	u8 buf[33];
118 	int ret;
119 
120 	if (!client->adapter)
121 		return -ENODEV;
122 
123 	buf[0] = reg;
124 	memcpy(&buf[1], data, len);
125 
126 	msg.buf = buf;
127 	msg.addr = client->addr;
128 	msg.flags = 0;
129 	msg.len = len + 1;
130 
131 	ret = i2c_transfer(client->adapter, &msg, 1);
132 	if (ret < 0)
133 		return ret;
134 	if (ret != 1)
135 		return -EINVAL;
136 	return 0;
137 }
138 
139 static int bq27xxx_battery_i2c_probe(struct i2c_client *client,
140 				     const struct i2c_device_id *id)
141 {
142 	struct bq27xxx_device_info *di;
143 	int ret;
144 	char *name;
145 	int num;
146 
147 	/* Get new ID for the new battery device */
148 	mutex_lock(&battery_mutex);
149 	num = idr_alloc(&battery_id, client, 0, 0, GFP_KERNEL);
150 	mutex_unlock(&battery_mutex);
151 	if (num < 0)
152 		return num;
153 
154 	name = devm_kasprintf(&client->dev, GFP_KERNEL, "%s-%d", id->name, num);
155 	if (!name)
156 		goto err_mem;
157 
158 	di = devm_kzalloc(&client->dev, sizeof(*di), GFP_KERNEL);
159 	if (!di)
160 		goto err_mem;
161 
162 	di->id = num;
163 	di->dev = &client->dev;
164 	di->chip = id->driver_data;
165 	di->name = name;
166 
167 	di->bus.read = bq27xxx_battery_i2c_read;
168 	di->bus.write = bq27xxx_battery_i2c_write;
169 	di->bus.read_bulk = bq27xxx_battery_i2c_bulk_read;
170 	di->bus.write_bulk = bq27xxx_battery_i2c_bulk_write;
171 
172 	ret = bq27xxx_battery_setup(di);
173 	if (ret)
174 		goto err_failed;
175 
176 	/* Schedule a polling after about 1 min */
177 	schedule_delayed_work(&di->work, 60 * HZ);
178 
179 	i2c_set_clientdata(client, di);
180 
181 	if (client->irq) {
182 		ret = devm_request_threaded_irq(&client->dev, client->irq,
183 				NULL, bq27xxx_battery_irq_handler_thread,
184 				IRQF_ONESHOT,
185 				di->name, di);
186 		if (ret) {
187 			dev_err(&client->dev,
188 				"Unable to register IRQ %d error %d\n",
189 				client->irq, ret);
190 			bq27xxx_battery_teardown(di);
191 			goto err_failed;
192 		}
193 	}
194 
195 	return 0;
196 
197 err_mem:
198 	ret = -ENOMEM;
199 
200 err_failed:
201 	mutex_lock(&battery_mutex);
202 	idr_remove(&battery_id, num);
203 	mutex_unlock(&battery_mutex);
204 
205 	return ret;
206 }
207 
208 static int bq27xxx_battery_i2c_remove(struct i2c_client *client)
209 {
210 	struct bq27xxx_device_info *di = i2c_get_clientdata(client);
211 
212 	bq27xxx_battery_teardown(di);
213 
214 	mutex_lock(&battery_mutex);
215 	idr_remove(&battery_id, di->id);
216 	mutex_unlock(&battery_mutex);
217 
218 	return 0;
219 }
220 
221 static const struct i2c_device_id bq27xxx_i2c_id_table[] = {
222 	{ "bq27200", BQ27000 },
223 	{ "bq27210", BQ27010 },
224 	{ "bq27500", BQ2750X },
225 	{ "bq27510", BQ2751X },
226 	{ "bq27520", BQ2752X },
227 	{ "bq27500-1", BQ27500 },
228 	{ "bq27510g1", BQ27510G1 },
229 	{ "bq27510g2", BQ27510G2 },
230 	{ "bq27510g3", BQ27510G3 },
231 	{ "bq27520g1", BQ27520G1 },
232 	{ "bq27520g2", BQ27520G2 },
233 	{ "bq27520g3", BQ27520G3 },
234 	{ "bq27520g4", BQ27520G4 },
235 	{ "bq27521", BQ27521 },
236 	{ "bq27530", BQ27530 },
237 	{ "bq27531", BQ27531 },
238 	{ "bq27541", BQ27541 },
239 	{ "bq27542", BQ27542 },
240 	{ "bq27546", BQ27546 },
241 	{ "bq27742", BQ27742 },
242 	{ "bq27545", BQ27545 },
243 	{ "bq27411", BQ27411 },
244 	{ "bq27421", BQ27421 },
245 	{ "bq27425", BQ27425 },
246 	{ "bq27426", BQ27426 },
247 	{ "bq27441", BQ27441 },
248 	{ "bq27621", BQ27621 },
249 	{ "bq27z561", BQ27Z561 },
250 	{ "bq28z610", BQ28Z610 },
251 	{ "bq34z100", BQ34Z100 },
252 	{ "bq78z100", BQ78Z100 },
253 	{},
254 };
255 MODULE_DEVICE_TABLE(i2c, bq27xxx_i2c_id_table);
256 
257 #ifdef CONFIG_OF
258 static const struct of_device_id bq27xxx_battery_i2c_of_match_table[] = {
259 	{ .compatible = "ti,bq27200" },
260 	{ .compatible = "ti,bq27210" },
261 	{ .compatible = "ti,bq27500" },
262 	{ .compatible = "ti,bq27510" },
263 	{ .compatible = "ti,bq27520" },
264 	{ .compatible = "ti,bq27500-1" },
265 	{ .compatible = "ti,bq27510g1" },
266 	{ .compatible = "ti,bq27510g2" },
267 	{ .compatible = "ti,bq27510g3" },
268 	{ .compatible = "ti,bq27520g1" },
269 	{ .compatible = "ti,bq27520g2" },
270 	{ .compatible = "ti,bq27520g3" },
271 	{ .compatible = "ti,bq27520g4" },
272 	{ .compatible = "ti,bq27521" },
273 	{ .compatible = "ti,bq27530" },
274 	{ .compatible = "ti,bq27531" },
275 	{ .compatible = "ti,bq27541" },
276 	{ .compatible = "ti,bq27542" },
277 	{ .compatible = "ti,bq27546" },
278 	{ .compatible = "ti,bq27742" },
279 	{ .compatible = "ti,bq27545" },
280 	{ .compatible = "ti,bq27411" },
281 	{ .compatible = "ti,bq27421" },
282 	{ .compatible = "ti,bq27425" },
283 	{ .compatible = "ti,bq27426" },
284 	{ .compatible = "ti,bq27441" },
285 	{ .compatible = "ti,bq27621" },
286 	{ .compatible = "ti,bq27z561" },
287 	{ .compatible = "ti,bq28z610" },
288 	{ .compatible = "ti,bq34z100" },
289 	{ .compatible = "ti,bq78z100" },
290 	{},
291 };
292 MODULE_DEVICE_TABLE(of, bq27xxx_battery_i2c_of_match_table);
293 #endif
294 
295 static struct i2c_driver bq27xxx_battery_i2c_driver = {
296 	.driver = {
297 		.name = "bq27xxx-battery",
298 		.of_match_table = of_match_ptr(bq27xxx_battery_i2c_of_match_table),
299 	},
300 	.probe = bq27xxx_battery_i2c_probe,
301 	.remove = bq27xxx_battery_i2c_remove,
302 	.id_table = bq27xxx_i2c_id_table,
303 };
304 module_i2c_driver(bq27xxx_battery_i2c_driver);
305 
306 MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>");
307 MODULE_DESCRIPTION("BQ27xxx battery monitor i2c driver");
308 MODULE_LICENSE("GPL");
309