Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

power: Add support for DA9150 Fuel-Gauge

This adds power supply driver support for the Fuel-Gauge part of
the DA9150 combined Charger and Fuel-Gauge device.

Signed-off-by: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>
Acked-by: Sebastian Reichel <sre@kernel.org>
Signed-off-by: Lee Jones <lee.jones@linaro.org>

authored by

Adam Thomson and committed by
Lee Jones
a419b4fd daaab943

+590
+10
drivers/power/Kconfig
··· 204 204 This driver can also be built as a module. If so, the module will be 205 205 called da9150-charger. 206 206 207 + config BATTERY_DA9150 208 + tristate "Dialog Semiconductor DA9150 Fuel Gauge support" 209 + depends on MFD_DA9150 210 + help 211 + Say Y here to enable support for the Fuel-Gauge unit of the DA9150 212 + Integrated Charger & Fuel-Gauge IC 213 + 214 + This driver can also be built as a module. If so, the module will be 215 + called da9150-fg. 216 + 207 217 config AXP288_CHARGER 208 218 tristate "X-Powers AXP288 Charger" 209 219 depends on MFD_AXP20X && EXTCON_AXP288
+1
drivers/power/Makefile
··· 33 33 obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o 34 34 obj-$(CONFIG_BATTERY_DA9052) += da9052-battery.o 35 35 obj-$(CONFIG_CHARGER_DA9150) += da9150-charger.o 36 + obj-$(CONFIG_BATTERY_DA9150) += da9150-fg.o 36 37 obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o 37 38 obj-$(CONFIG_BATTERY_MAX17042) += max17042_battery.o 38 39 obj-$(CONFIG_BATTERY_Z2) += z2_battery.o
+579
drivers/power/da9150-fg.c
··· 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");