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