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 = ® |
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(adap: client->adapter, msgs: msg, ARRAY_SIZE(msg)); |
52 | if (ret < 0) |
53 | return ret; |
54 | |
55 | if (!single) |
56 | ret = get_unaligned_le16(p: 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(val: value, p: &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(adap: client->adapter, msgs: &msg, num: 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, command: reg, length: len, values: 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(adap: client->adapter, msgs: &msg, num: 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 | { |
141 | const struct i2c_device_id *id = i2c_client_get_device_id(client); |
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, ptr: client, start: 0, end: 0, GFP_KERNEL); |
150 | mutex_unlock(lock: &battery_mutex); |
151 | if (num < 0) |
152 | return num; |
153 | |
154 | name = devm_kasprintf(dev: &client->dev, GFP_KERNEL, fmt: "%s-%d" , id->name, num); |
155 | if (!name) |
156 | goto err_mem; |
157 | |
158 | di = devm_kzalloc(dev: &client->dev, size: 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(dwork: &di->work, delay: 60 * HZ); |
178 | |
179 | i2c_set_clientdata(client, data: di); |
180 | |
181 | if (client->irq) { |
182 | ret = request_threaded_irq(irq: client->irq, |
183 | NULL, thread_fn: bq27xxx_battery_irq_handler_thread, |
184 | IRQF_ONESHOT, |
185 | name: di->name, dev: 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, id: num); |
203 | mutex_unlock(lock: &battery_mutex); |
204 | |
205 | return ret; |
206 | } |
207 | |
208 | static void bq27xxx_battery_i2c_remove(struct i2c_client *client) |
209 | { |
210 | struct bq27xxx_device_info *di = i2c_get_clientdata(client); |
211 | |
212 | free_irq(client->irq, di); |
213 | bq27xxx_battery_teardown(di); |
214 | |
215 | mutex_lock(&battery_mutex); |
216 | idr_remove(&battery_id, id: di->id); |
217 | mutex_unlock(lock: &battery_mutex); |
218 | } |
219 | |
220 | static const struct i2c_device_id bq27xxx_i2c_id_table[] = { |
221 | { "bq27200" , BQ27000 }, |
222 | { "bq27210" , BQ27010 }, |
223 | { "bq27500" , BQ2750X }, |
224 | { "bq27510" , BQ2751X }, |
225 | { "bq27520" , BQ2752X }, |
226 | { "bq27500-1" , BQ27500 }, |
227 | { "bq27510g1" , BQ27510G1 }, |
228 | { "bq27510g2" , BQ27510G2 }, |
229 | { "bq27510g3" , BQ27510G3 }, |
230 | { "bq27520g1" , BQ27520G1 }, |
231 | { "bq27520g2" , BQ27520G2 }, |
232 | { "bq27520g3" , BQ27520G3 }, |
233 | { "bq27520g4" , BQ27520G4 }, |
234 | { "bq27521" , BQ27521 }, |
235 | { "bq27530" , BQ27530 }, |
236 | { "bq27531" , BQ27531 }, |
237 | { "bq27541" , BQ27541 }, |
238 | { "bq27542" , BQ27542 }, |
239 | { "bq27546" , BQ27546 }, |
240 | { "bq27742" , BQ27742 }, |
241 | { "bq27545" , BQ27545 }, |
242 | { "bq27411" , BQ27411 }, |
243 | { "bq27421" , BQ27421 }, |
244 | { "bq27425" , BQ27425 }, |
245 | { "bq27426" , BQ27426 }, |
246 | { "bq27441" , BQ27441 }, |
247 | { "bq27621" , BQ27621 }, |
248 | { "bq27z561" , BQ27Z561 }, |
249 | { "bq28z610" , BQ28Z610 }, |
250 | { "bq34z100" , BQ34Z100 }, |
251 | { "bq78z100" , BQ78Z100 }, |
252 | {}, |
253 | }; |
254 | MODULE_DEVICE_TABLE(i2c, bq27xxx_i2c_id_table); |
255 | |
256 | #ifdef CONFIG_OF |
257 | static const struct of_device_id bq27xxx_battery_i2c_of_match_table[] = { |
258 | { .compatible = "ti,bq27200" }, |
259 | { .compatible = "ti,bq27210" }, |
260 | { .compatible = "ti,bq27500" }, |
261 | { .compatible = "ti,bq27510" }, |
262 | { .compatible = "ti,bq27520" }, |
263 | { .compatible = "ti,bq27500-1" }, |
264 | { .compatible = "ti,bq27510g1" }, |
265 | { .compatible = "ti,bq27510g2" }, |
266 | { .compatible = "ti,bq27510g3" }, |
267 | { .compatible = "ti,bq27520g1" }, |
268 | { .compatible = "ti,bq27520g2" }, |
269 | { .compatible = "ti,bq27520g3" }, |
270 | { .compatible = "ti,bq27520g4" }, |
271 | { .compatible = "ti,bq27521" }, |
272 | { .compatible = "ti,bq27530" }, |
273 | { .compatible = "ti,bq27531" }, |
274 | { .compatible = "ti,bq27541" }, |
275 | { .compatible = "ti,bq27542" }, |
276 | { .compatible = "ti,bq27546" }, |
277 | { .compatible = "ti,bq27742" }, |
278 | { .compatible = "ti,bq27545" }, |
279 | { .compatible = "ti,bq27411" }, |
280 | { .compatible = "ti,bq27421" }, |
281 | { .compatible = "ti,bq27425" }, |
282 | { .compatible = "ti,bq27426" }, |
283 | { .compatible = "ti,bq27441" }, |
284 | { .compatible = "ti,bq27621" }, |
285 | { .compatible = "ti,bq27z561" }, |
286 | { .compatible = "ti,bq28z610" }, |
287 | { .compatible = "ti,bq34z100" }, |
288 | { .compatible = "ti,bq78z100" }, |
289 | {}, |
290 | }; |
291 | MODULE_DEVICE_TABLE(of, bq27xxx_battery_i2c_of_match_table); |
292 | #endif |
293 | |
294 | static struct i2c_driver bq27xxx_battery_i2c_driver = { |
295 | .driver = { |
296 | .name = "bq27xxx-battery" , |
297 | .of_match_table = of_match_ptr(bq27xxx_battery_i2c_of_match_table), |
298 | }, |
299 | .probe = bq27xxx_battery_i2c_probe, |
300 | .remove = bq27xxx_battery_i2c_remove, |
301 | .id_table = bq27xxx_i2c_id_table, |
302 | }; |
303 | module_i2c_driver(bq27xxx_battery_i2c_driver); |
304 | |
305 | MODULE_AUTHOR("Andrew F. Davis <afd@ti.com>" ); |
306 | MODULE_DESCRIPTION("BQ27xxx battery monitor i2c driver" ); |
307 | MODULE_LICENSE("GPL" ); |
308 | |