Commit | Line | Data |
---|---|---|
a419b4fd AT |
1 | /* |
2 | * DA9150 Fuel-Gauge Driver | |
3 | * | |
4 | * Copyright (c) 2015 Dialog Semiconductor | |
5 | * | |
6 | * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com> | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify it | |
9 | * under the terms of the GNU General Public License as published by the | |
10 | * Free Software Foundation; either version 2 of the License, or (at your | |
11 | * option) any later version. | |
12 | */ | |
13 | ||
14 | #include <linux/kernel.h> | |
15 | #include <linux/module.h> | |
16 | #include <linux/platform_device.h> | |
17 | #include <linux/of.h> | |
18 | #include <linux/of_platform.h> | |
19 | #include <linux/slab.h> | |
20 | #include <linux/interrupt.h> | |
21 | #include <linux/delay.h> | |
22 | #include <linux/power_supply.h> | |
23 | #include <linux/list.h> | |
24 | #include <asm/div64.h> | |
25 | #include <linux/mfd/da9150/core.h> | |
26 | #include <linux/mfd/da9150/registers.h> | |
27 | ||
28 | /* Core2Wire */ | |
29 | #define DA9150_QIF_READ (0x0 << 7) | |
30 | #define DA9150_QIF_WRITE (0x1 << 7) | |
31 | #define DA9150_QIF_CODE_MASK 0x7F | |
32 | ||
33 | #define DA9150_QIF_BYTE_SIZE 8 | |
34 | #define DA9150_QIF_BYTE_MASK 0xFF | |
35 | #define DA9150_QIF_SHORT_SIZE 2 | |
36 | #define DA9150_QIF_LONG_SIZE 4 | |
37 | ||
38 | /* QIF Codes */ | |
39 | #define DA9150_QIF_UAVG 6 | |
40 | #define DA9150_QIF_UAVG_SIZE DA9150_QIF_LONG_SIZE | |
41 | #define DA9150_QIF_IAVG 8 | |
42 | #define DA9150_QIF_IAVG_SIZE DA9150_QIF_LONG_SIZE | |
43 | #define DA9150_QIF_NTCAVG 12 | |
44 | #define DA9150_QIF_NTCAVG_SIZE DA9150_QIF_LONG_SIZE | |
45 | #define DA9150_QIF_SHUNT_VAL 36 | |
46 | #define DA9150_QIF_SHUNT_VAL_SIZE DA9150_QIF_SHORT_SIZE | |
47 | #define DA9150_QIF_SD_GAIN 38 | |
48 | #define DA9150_QIF_SD_GAIN_SIZE DA9150_QIF_LONG_SIZE | |
49 | #define DA9150_QIF_FCC_MAH 40 | |
50 | #define DA9150_QIF_FCC_MAH_SIZE DA9150_QIF_SHORT_SIZE | |
51 | #define DA9150_QIF_SOC_PCT 43 | |
52 | #define DA9150_QIF_SOC_PCT_SIZE DA9150_QIF_SHORT_SIZE | |
53 | #define DA9150_QIF_CHARGE_LIMIT 44 | |
54 | #define DA9150_QIF_CHARGE_LIMIT_SIZE DA9150_QIF_SHORT_SIZE | |
55 | #define DA9150_QIF_DISCHARGE_LIMIT 45 | |
56 | #define DA9150_QIF_DISCHARGE_LIMIT_SIZE DA9150_QIF_SHORT_SIZE | |
57 | #define DA9150_QIF_FW_MAIN_VER 118 | |
58 | #define DA9150_QIF_FW_MAIN_VER_SIZE DA9150_QIF_SHORT_SIZE | |
59 | #define DA9150_QIF_E_FG_STATUS 126 | |
60 | #define DA9150_QIF_E_FG_STATUS_SIZE DA9150_QIF_SHORT_SIZE | |
61 | #define DA9150_QIF_SYNC 127 | |
62 | #define DA9150_QIF_SYNC_SIZE DA9150_QIF_SHORT_SIZE | |
63 | #define DA9150_QIF_MAX_CODES 128 | |
64 | ||
65 | /* QIF Sync Timeout */ | |
66 | #define DA9150_QIF_SYNC_TIMEOUT 1000 | |
67 | #define DA9150_QIF_SYNC_RETRIES 10 | |
68 | ||
69 | /* QIF E_FG_STATUS */ | |
70 | #define DA9150_FG_IRQ_LOW_SOC_MASK (1 << 0) | |
71 | #define DA9150_FG_IRQ_HIGH_SOC_MASK (1 << 1) | |
72 | #define DA9150_FG_IRQ_SOC_MASK \ | |
73 | (DA9150_FG_IRQ_LOW_SOC_MASK | DA9150_FG_IRQ_HIGH_SOC_MASK) | |
74 | ||
75 | /* Private data */ | |
76 | struct da9150_fg { | |
77 | struct da9150 *da9150; | |
78 | struct device *dev; | |
79 | ||
80 | struct mutex io_lock; | |
81 | ||
82 | struct power_supply *battery; | |
83 | struct delayed_work work; | |
84 | u32 interval; | |
85 | ||
86 | int warn_soc; | |
87 | int crit_soc; | |
88 | int soc; | |
89 | }; | |
90 | ||
91 | /* Battery Properties */ | |
92 | static u32 da9150_fg_read_attr(struct da9150_fg *fg, u8 code, u8 size) | |
93 | ||
94 | { | |
95 | u8 buf[size]; | |
96 | u8 read_addr; | |
97 | u32 res = 0; | |
98 | int i; | |
99 | ||
100 | /* Set QIF code (READ mode) */ | |
101 | read_addr = (code & DA9150_QIF_CODE_MASK) | DA9150_QIF_READ; | |
102 | ||
103 | da9150_read_qif(fg->da9150, read_addr, size, buf); | |
104 | for (i = 0; i < size; ++i) | |
105 | res |= (buf[i] << (i * DA9150_QIF_BYTE_SIZE)); | |
106 | ||
107 | return res; | |
108 | } | |
109 | ||
110 | static void da9150_fg_write_attr(struct da9150_fg *fg, u8 code, u8 size, | |
111 | u32 val) | |
112 | ||
113 | { | |
114 | u8 buf[size]; | |
115 | u8 write_addr; | |
116 | int i; | |
117 | ||
118 | /* Set QIF code (WRITE mode) */ | |
119 | write_addr = (code & DA9150_QIF_CODE_MASK) | DA9150_QIF_WRITE; | |
120 | ||
121 | for (i = 0; i < size; ++i) { | |
122 | buf[i] = (val >> (i * DA9150_QIF_BYTE_SIZE)) & | |
123 | DA9150_QIF_BYTE_MASK; | |
124 | } | |
125 | da9150_write_qif(fg->da9150, write_addr, size, buf); | |
126 | } | |
127 | ||
128 | /* Trigger QIF Sync to update QIF readable data */ | |
129 | static void da9150_fg_read_sync_start(struct da9150_fg *fg) | |
130 | { | |
131 | int i = 0; | |
132 | u32 res = 0; | |
133 | ||
134 | mutex_lock(&fg->io_lock); | |
135 | ||
136 | /* Check if QIF sync already requested, and write to sync if not */ | |
137 | res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC, | |
138 | DA9150_QIF_SYNC_SIZE); | |
139 | if (res > 0) | |
140 | da9150_fg_write_attr(fg, DA9150_QIF_SYNC, | |
141 | DA9150_QIF_SYNC_SIZE, 0); | |
142 | ||
143 | /* Wait for sync to complete */ | |
144 | res = 0; | |
145 | while ((res == 0) && (i++ < DA9150_QIF_SYNC_RETRIES)) { | |
146 | usleep_range(DA9150_QIF_SYNC_TIMEOUT, | |
147 | DA9150_QIF_SYNC_TIMEOUT * 2); | |
148 | res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC, | |
149 | DA9150_QIF_SYNC_SIZE); | |
150 | } | |
151 | ||
152 | /* Check if sync completed */ | |
153 | if (res == 0) | |
154 | dev_err(fg->dev, "Failed to perform QIF read sync!\n"); | |
155 | } | |
156 | ||
157 | /* | |
158 | * Should always be called after QIF sync read has been performed, and all | |
159 | * attributes required have been accessed. | |
160 | */ | |
161 | static inline void da9150_fg_read_sync_end(struct da9150_fg *fg) | |
162 | { | |
163 | mutex_unlock(&fg->io_lock); | |
164 | } | |
165 | ||
166 | /* Sync read of single QIF attribute */ | |
167 | static u32 da9150_fg_read_attr_sync(struct da9150_fg *fg, u8 code, u8 size) | |
168 | { | |
169 | u32 val; | |
170 | ||
171 | da9150_fg_read_sync_start(fg); | |
172 | val = da9150_fg_read_attr(fg, code, size); | |
173 | da9150_fg_read_sync_end(fg); | |
174 | ||
175 | return val; | |
176 | } | |
177 | ||
178 | /* Wait for QIF Sync, write QIF data and wait for ack */ | |
179 | static void da9150_fg_write_attr_sync(struct da9150_fg *fg, u8 code, u8 size, | |
180 | u32 val) | |
181 | { | |
182 | int i = 0; | |
183 | u32 res = 0, sync_val; | |
184 | ||
185 | mutex_lock(&fg->io_lock); | |
186 | ||
187 | /* Check if QIF sync already requested */ | |
188 | res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC, | |
189 | DA9150_QIF_SYNC_SIZE); | |
190 | ||
191 | /* Wait for an existing sync to complete */ | |
192 | while ((res == 0) && (i++ < DA9150_QIF_SYNC_RETRIES)) { | |
193 | usleep_range(DA9150_QIF_SYNC_TIMEOUT, | |
194 | DA9150_QIF_SYNC_TIMEOUT * 2); | |
195 | res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC, | |
196 | DA9150_QIF_SYNC_SIZE); | |
197 | } | |
198 | ||
199 | if (res == 0) { | |
200 | dev_err(fg->dev, "Timeout waiting for existing QIF sync!\n"); | |
201 | mutex_unlock(&fg->io_lock); | |
202 | return; | |
203 | } | |
204 | ||
205 | /* Write value for QIF code */ | |
206 | da9150_fg_write_attr(fg, code, size, val); | |
207 | ||
208 | /* Wait for write acknowledgment */ | |
209 | i = 0; | |
210 | sync_val = res; | |
211 | while ((res == sync_val) && (i++ < DA9150_QIF_SYNC_RETRIES)) { | |
212 | usleep_range(DA9150_QIF_SYNC_TIMEOUT, | |
213 | DA9150_QIF_SYNC_TIMEOUT * 2); | |
214 | res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC, | |
215 | DA9150_QIF_SYNC_SIZE); | |
216 | } | |
217 | ||
218 | mutex_unlock(&fg->io_lock); | |
219 | ||
220 | /* Check write was actually successful */ | |
221 | if (res != (sync_val + 1)) | |
222 | dev_err(fg->dev, "Error performing QIF sync write for code %d\n", | |
223 | code); | |
224 | } | |
225 | ||
226 | /* Power Supply attributes */ | |
227 | static int da9150_fg_capacity(struct da9150_fg *fg, | |
228 | union power_supply_propval *val) | |
229 | { | |
230 | val->intval = da9150_fg_read_attr_sync(fg, DA9150_QIF_SOC_PCT, | |
231 | DA9150_QIF_SOC_PCT_SIZE); | |
232 | ||
233 | if (val->intval > 100) | |
234 | val->intval = 100; | |
235 | ||
236 | return 0; | |
237 | } | |
238 | ||
239 | static int da9150_fg_current_avg(struct da9150_fg *fg, | |
240 | union power_supply_propval *val) | |
241 | { | |
242 | u32 iavg, sd_gain, shunt_val; | |
243 | u64 div, res; | |
244 | ||
245 | da9150_fg_read_sync_start(fg); | |
246 | iavg = da9150_fg_read_attr(fg, DA9150_QIF_IAVG, | |
247 | DA9150_QIF_IAVG_SIZE); | |
248 | shunt_val = da9150_fg_read_attr(fg, DA9150_QIF_SHUNT_VAL, | |
249 | DA9150_QIF_SHUNT_VAL_SIZE); | |
250 | sd_gain = da9150_fg_read_attr(fg, DA9150_QIF_SD_GAIN, | |
251 | DA9150_QIF_SD_GAIN_SIZE); | |
252 | da9150_fg_read_sync_end(fg); | |
253 | ||
254 | div = (u64) (sd_gain * shunt_val * 65536ULL); | |
255 | do_div(div, 1000000); | |
256 | res = (u64) (iavg * 1000000ULL); | |
257 | do_div(res, div); | |
258 | ||
259 | val->intval = (int) res; | |
260 | ||
261 | return 0; | |
262 | } | |
263 | ||
264 | static int da9150_fg_voltage_avg(struct da9150_fg *fg, | |
265 | union power_supply_propval *val) | |
266 | { | |
267 | u64 res; | |
268 | ||
269 | val->intval = da9150_fg_read_attr_sync(fg, DA9150_QIF_UAVG, | |
270 | DA9150_QIF_UAVG_SIZE); | |
271 | ||
272 | res = (u64) (val->intval * 186ULL); | |
273 | do_div(res, 10000); | |
274 | val->intval = (int) res; | |
275 | ||
276 | return 0; | |
277 | } | |
278 | ||
279 | static int da9150_fg_charge_full(struct da9150_fg *fg, | |
280 | union power_supply_propval *val) | |
281 | { | |
282 | val->intval = da9150_fg_read_attr_sync(fg, DA9150_QIF_FCC_MAH, | |
283 | DA9150_QIF_FCC_MAH_SIZE); | |
284 | ||
285 | val->intval = val->intval * 1000; | |
286 | ||
287 | return 0; | |
288 | } | |
289 | ||
290 | /* | |
291 | * Temperature reading from device is only valid if battery/system provides | |
292 | * valid NTC to associated pin of DA9150 chip. | |
293 | */ | |
294 | static int da9150_fg_temp(struct da9150_fg *fg, | |
295 | union power_supply_propval *val) | |
296 | { | |
297 | val->intval = da9150_fg_read_attr_sync(fg, DA9150_QIF_NTCAVG, | |
298 | DA9150_QIF_NTCAVG_SIZE); | |
299 | ||
300 | val->intval = (val->intval * 10) / 1048576; | |
301 | ||
302 | return 0; | |
303 | } | |
304 | ||
305 | static enum power_supply_property da9150_fg_props[] = { | |
306 | POWER_SUPPLY_PROP_CAPACITY, | |
307 | POWER_SUPPLY_PROP_CURRENT_AVG, | |
308 | POWER_SUPPLY_PROP_VOLTAGE_AVG, | |
309 | POWER_SUPPLY_PROP_CHARGE_FULL, | |
310 | POWER_SUPPLY_PROP_TEMP, | |
311 | }; | |
312 | ||
313 | static int da9150_fg_get_prop(struct power_supply *psy, | |
314 | enum power_supply_property psp, | |
315 | union power_supply_propval *val) | |
316 | { | |
317 | struct da9150_fg *fg = dev_get_drvdata(psy->dev.parent); | |
318 | int ret; | |
319 | ||
320 | switch (psp) { | |
321 | case POWER_SUPPLY_PROP_CAPACITY: | |
322 | ret = da9150_fg_capacity(fg, val); | |
323 | break; | |
324 | case POWER_SUPPLY_PROP_CURRENT_AVG: | |
325 | ret = da9150_fg_current_avg(fg, val); | |
326 | break; | |
327 | case POWER_SUPPLY_PROP_VOLTAGE_AVG: | |
328 | ret = da9150_fg_voltage_avg(fg, val); | |
329 | break; | |
330 | case POWER_SUPPLY_PROP_CHARGE_FULL: | |
331 | ret = da9150_fg_charge_full(fg, val); | |
332 | break; | |
333 | case POWER_SUPPLY_PROP_TEMP: | |
334 | ret = da9150_fg_temp(fg, val); | |
335 | break; | |
336 | default: | |
337 | ret = -EINVAL; | |
338 | break; | |
339 | } | |
340 | ||
341 | return ret; | |
342 | } | |
343 | ||
344 | /* Repeated SOC check */ | |
345 | static bool da9150_fg_soc_changed(struct da9150_fg *fg) | |
346 | { | |
347 | union power_supply_propval val; | |
348 | ||
349 | da9150_fg_capacity(fg, &val); | |
350 | if (val.intval != fg->soc) { | |
351 | fg->soc = val.intval; | |
352 | return true; | |
353 | } | |
354 | ||
355 | return false; | |
356 | } | |
357 | ||
358 | static void da9150_fg_work(struct work_struct *work) | |
359 | { | |
360 | struct da9150_fg *fg = container_of(work, struct da9150_fg, work.work); | |
361 | ||
362 | /* Report if SOC has changed */ | |
363 | if (da9150_fg_soc_changed(fg)) | |
364 | power_supply_changed(fg->battery); | |
365 | ||
366 | schedule_delayed_work(&fg->work, msecs_to_jiffies(fg->interval)); | |
367 | } | |
368 | ||
369 | /* SOC level event configuration */ | |
370 | static void da9150_fg_soc_event_config(struct da9150_fg *fg) | |
371 | { | |
372 | int soc; | |
373 | ||
374 | soc = da9150_fg_read_attr_sync(fg, DA9150_QIF_SOC_PCT, | |
375 | DA9150_QIF_SOC_PCT_SIZE); | |
376 | ||
377 | if (soc > fg->warn_soc) { | |
378 | /* If SOC > warn level, set discharge warn level event */ | |
379 | da9150_fg_write_attr_sync(fg, DA9150_QIF_DISCHARGE_LIMIT, | |
380 | DA9150_QIF_DISCHARGE_LIMIT_SIZE, | |
381 | fg->warn_soc + 1); | |
382 | } else if ((soc <= fg->warn_soc) && (soc > fg->crit_soc)) { | |
383 | /* | |
384 | * If SOC <= warn level, set discharge crit level event, | |
385 | * and set charge warn level event. | |
386 | */ | |
387 | da9150_fg_write_attr_sync(fg, DA9150_QIF_DISCHARGE_LIMIT, | |
388 | DA9150_QIF_DISCHARGE_LIMIT_SIZE, | |
389 | fg->crit_soc + 1); | |
390 | ||
391 | da9150_fg_write_attr_sync(fg, DA9150_QIF_CHARGE_LIMIT, | |
392 | DA9150_QIF_CHARGE_LIMIT_SIZE, | |
393 | fg->warn_soc); | |
394 | } else if (soc <= fg->crit_soc) { | |
395 | /* If SOC <= crit level, set charge crit level event */ | |
396 | da9150_fg_write_attr_sync(fg, DA9150_QIF_CHARGE_LIMIT, | |
397 | DA9150_QIF_CHARGE_LIMIT_SIZE, | |
398 | fg->crit_soc); | |
399 | } | |
400 | } | |
401 | ||
402 | static irqreturn_t da9150_fg_irq(int irq, void *data) | |
403 | { | |
404 | struct da9150_fg *fg = data; | |
405 | u32 e_fg_status; | |
406 | ||
407 | /* Read FG IRQ status info */ | |
408 | e_fg_status = da9150_fg_read_attr(fg, DA9150_QIF_E_FG_STATUS, | |
409 | DA9150_QIF_E_FG_STATUS_SIZE); | |
410 | ||
411 | /* Handle warning/critical threhold events */ | |
412 | if (e_fg_status & DA9150_FG_IRQ_SOC_MASK) | |
413 | da9150_fg_soc_event_config(fg); | |
414 | ||
415 | /* Clear any FG IRQs */ | |
416 | da9150_fg_write_attr(fg, DA9150_QIF_E_FG_STATUS, | |
417 | DA9150_QIF_E_FG_STATUS_SIZE, e_fg_status); | |
418 | ||
419 | return IRQ_HANDLED; | |
420 | } | |
421 | ||
422 | static struct da9150_fg_pdata *da9150_fg_dt_pdata(struct device *dev) | |
423 | { | |
424 | struct device_node *fg_node = dev->of_node; | |
425 | struct da9150_fg_pdata *pdata; | |
426 | ||
427 | pdata = devm_kzalloc(dev, sizeof(struct da9150_fg_pdata), GFP_KERNEL); | |
428 | if (!pdata) | |
429 | return NULL; | |
430 | ||
431 | of_property_read_u32(fg_node, "dlg,update-interval", | |
432 | &pdata->update_interval); | |
433 | of_property_read_u8(fg_node, "dlg,warn-soc-level", | |
434 | &pdata->warn_soc_lvl); | |
435 | of_property_read_u8(fg_node, "dlg,crit-soc-level", | |
436 | &pdata->crit_soc_lvl); | |
437 | ||
438 | return pdata; | |
439 | } | |
440 | ||
441 | static const struct power_supply_desc fg_desc = { | |
442 | .name = "da9150-fg", | |
443 | .type = POWER_SUPPLY_TYPE_BATTERY, | |
444 | .properties = da9150_fg_props, | |
445 | .num_properties = ARRAY_SIZE(da9150_fg_props), | |
446 | .get_property = da9150_fg_get_prop, | |
447 | }; | |
448 | ||
449 | static int da9150_fg_probe(struct platform_device *pdev) | |
450 | { | |
451 | struct device *dev = &pdev->dev; | |
452 | struct da9150 *da9150 = dev_get_drvdata(dev->parent); | |
453 | struct da9150_fg_pdata *fg_pdata = dev_get_platdata(dev); | |
454 | struct da9150_fg *fg; | |
455 | int ver, irq, ret = 0; | |
456 | ||
457 | fg = devm_kzalloc(dev, sizeof(*fg), GFP_KERNEL); | |
458 | if (fg == NULL) | |
459 | return -ENOMEM; | |
460 | ||
461 | platform_set_drvdata(pdev, fg); | |
462 | fg->da9150 = da9150; | |
463 | fg->dev = dev; | |
464 | ||
465 | mutex_init(&fg->io_lock); | |
466 | ||
467 | /* Enable QIF */ | |
468 | da9150_set_bits(da9150, DA9150_CORE2WIRE_CTRL_A, DA9150_FG_QIF_EN_MASK, | |
469 | DA9150_FG_QIF_EN_MASK); | |
470 | ||
471 | fg->battery = devm_power_supply_register(dev, &fg_desc, NULL); | |
472 | if (IS_ERR(fg->battery)) { | |
473 | ret = PTR_ERR(fg->battery); | |
474 | return ret; | |
475 | } | |
476 | ||
477 | ver = da9150_fg_read_attr(fg, DA9150_QIF_FW_MAIN_VER, | |
478 | DA9150_QIF_FW_MAIN_VER_SIZE); | |
479 | dev_info(dev, "Version: 0x%x\n", ver); | |
480 | ||
481 | /* Handle DT data if provided */ | |
482 | if (dev->of_node) { | |
483 | fg_pdata = da9150_fg_dt_pdata(dev); | |
484 | dev->platform_data = fg_pdata; | |
485 | } | |
486 | ||
487 | /* Handle any pdata provided */ | |
488 | if (fg_pdata) { | |
489 | fg->interval = fg_pdata->update_interval; | |
490 | ||
491 | if (fg_pdata->warn_soc_lvl > 100) | |
492 | dev_warn(dev, "Invalid SOC warning level provided, Ignoring"); | |
493 | else | |
494 | fg->warn_soc = fg_pdata->warn_soc_lvl; | |
495 | ||
496 | if ((fg_pdata->crit_soc_lvl > 100) || | |
497 | (fg_pdata->crit_soc_lvl >= fg_pdata->warn_soc_lvl)) | |
498 | dev_warn(dev, "Invalid SOC critical level provided, Ignoring"); | |
499 | else | |
500 | fg->crit_soc = fg_pdata->crit_soc_lvl; | |
501 | ||
502 | ||
503 | } | |
504 | ||
505 | /* Configure initial SOC level events */ | |
506 | da9150_fg_soc_event_config(fg); | |
507 | ||
508 | /* | |
509 | * If an interval period has been provided then setup repeating | |
510 | * work for reporting data updates. | |
511 | */ | |
512 | if (fg->interval) { | |
513 | INIT_DELAYED_WORK(&fg->work, da9150_fg_work); | |
514 | schedule_delayed_work(&fg->work, | |
515 | msecs_to_jiffies(fg->interval)); | |
516 | } | |
517 | ||
518 | /* Register IRQ */ | |
519 | irq = platform_get_irq_byname(pdev, "FG"); | |
520 | if (irq < 0) { | |
521 | dev_err(dev, "Failed to get IRQ FG: %d\n", irq); | |
522 | ret = irq; | |
523 | goto irq_fail; | |
524 | } | |
525 | ||
526 | ret = devm_request_threaded_irq(dev, irq, NULL, da9150_fg_irq, | |
527 | IRQF_ONESHOT, "FG", fg); | |
528 | if (ret) { | |
529 | dev_err(dev, "Failed to request IRQ %d: %d\n", irq, ret); | |
530 | goto irq_fail; | |
531 | } | |
532 | ||
533 | return 0; | |
534 | ||
535 | irq_fail: | |
536 | if (fg->interval) | |
537 | cancel_delayed_work(&fg->work); | |
538 | ||
539 | return ret; | |
540 | } | |
541 | ||
542 | static int da9150_fg_remove(struct platform_device *pdev) | |
543 | { | |
544 | struct da9150_fg *fg = platform_get_drvdata(pdev); | |
545 | ||
546 | if (fg->interval) | |
547 | cancel_delayed_work(&fg->work); | |
548 | ||
549 | return 0; | |
550 | } | |
551 | ||
552 | static int da9150_fg_resume(struct platform_device *pdev) | |
553 | { | |
554 | struct da9150_fg *fg = platform_get_drvdata(pdev); | |
555 | ||
556 | /* | |
557 | * Trigger SOC check to happen now so as to indicate any value change | |
558 | * since last check before suspend. | |
559 | */ | |
560 | if (fg->interval) | |
561 | flush_delayed_work(&fg->work); | |
562 | ||
563 | return 0; | |
564 | } | |
565 | ||
566 | static struct platform_driver da9150_fg_driver = { | |
567 | .driver = { | |
568 | .name = "da9150-fuel-gauge", | |
569 | }, | |
570 | .probe = da9150_fg_probe, | |
571 | .remove = da9150_fg_remove, | |
572 | .resume = da9150_fg_resume, | |
573 | }; | |
574 | ||
575 | module_platform_driver(da9150_fg_driver); | |
576 | ||
577 | MODULE_DESCRIPTION("Fuel-Gauge Driver for DA9150"); | |
578 | MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>"); | |
579 | MODULE_LICENSE("GPL"); |