| 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | // |
| 3 | // max17040_battery.c |
| 4 | // fuel-gauge systems for lithium-ion (Li+) batteries |
| 5 | // |
| 6 | // Copyright (C) 2009 Samsung Electronics |
| 7 | // Minkyu Kang <mk7.kang@samsung.com> |
| 8 | |
| 9 | #include <linux/module.h> |
| 10 | #include <linux/init.h> |
| 11 | #include <linux/platform_device.h> |
| 12 | #include <linux/mutex.h> |
| 13 | #include <linux/err.h> |
| 14 | #include <linux/i2c.h> |
| 15 | #include <linux/delay.h> |
| 16 | #include <linux/interrupt.h> |
| 17 | #include <linux/power_supply.h> |
| 18 | #include <linux/of.h> |
| 19 | #include <linux/regmap.h> |
| 20 | #include <linux/slab.h> |
| 21 | #include <linux/iio/consumer.h> |
| 22 | |
| 23 | #define MAX17040_VCELL 0x02 |
| 24 | #define MAX17040_SOC 0x04 |
| 25 | #define MAX17040_MODE 0x06 |
| 26 | #define MAX17040_VER 0x08 |
| 27 | #define MAX17040_CONFIG 0x0C |
| 28 | #define MAX17040_STATUS 0x1A |
| 29 | #define MAX17040_CMD 0xFE |
| 30 | |
| 31 | |
| 32 | #define MAX17040_DELAY 1000 |
| 33 | #define MAX17040_BATTERY_FULL 95 |
| 34 | #define MAX17040_RCOMP_DEFAULT 0x9700 |
| 35 | |
| 36 | #define MAX17040_ATHD_MASK 0x3f |
| 37 | #define MAX17040_ALSC_MASK 0x40 |
| 38 | #define MAX17040_ATHD_DEFAULT_POWER_UP 4 |
| 39 | #define MAX17040_STATUS_HD_MASK 0x1000 |
| 40 | #define MAX17040_STATUS_SC_MASK 0x2000 |
| 41 | #define MAX17040_CFG_RCOMP_MASK 0xff00 |
| 42 | |
| 43 | enum chip_id { |
| 44 | ID_MAX17040, |
| 45 | ID_MAX17041, |
| 46 | ID_MAX17043, |
| 47 | ID_MAX17044, |
| 48 | ID_MAX17048, |
| 49 | ID_MAX17049, |
| 50 | ID_MAX17058, |
| 51 | ID_MAX17059, |
| 52 | }; |
| 53 | |
| 54 | /* values that differ by chip_id */ |
| 55 | struct chip_data { |
| 56 | u16 reset_val; |
| 57 | u16 vcell_shift; |
| 58 | u16 vcell_mul; |
| 59 | u16 vcell_div; |
| 60 | u8 has_low_soc_alert; |
| 61 | u8 rcomp_bytes; |
| 62 | u8 has_soc_alert; |
| 63 | }; |
| 64 | |
| 65 | static struct chip_data max17040_family[] = { |
| 66 | [ID_MAX17040] = { |
| 67 | .reset_val = 0x0054, |
| 68 | .vcell_shift = 4, |
| 69 | .vcell_mul = 1250, |
| 70 | .vcell_div = 1, |
| 71 | .has_low_soc_alert = 0, |
| 72 | .rcomp_bytes = 2, |
| 73 | .has_soc_alert = 0, |
| 74 | }, |
| 75 | [ID_MAX17041] = { |
| 76 | .reset_val = 0x0054, |
| 77 | .vcell_shift = 4, |
| 78 | .vcell_mul = 2500, |
| 79 | .vcell_div = 1, |
| 80 | .has_low_soc_alert = 0, |
| 81 | .rcomp_bytes = 2, |
| 82 | .has_soc_alert = 0, |
| 83 | }, |
| 84 | [ID_MAX17043] = { |
| 85 | .reset_val = 0x0054, |
| 86 | .vcell_shift = 4, |
| 87 | .vcell_mul = 1250, |
| 88 | .vcell_div = 1, |
| 89 | .has_low_soc_alert = 1, |
| 90 | .rcomp_bytes = 1, |
| 91 | .has_soc_alert = 0, |
| 92 | }, |
| 93 | [ID_MAX17044] = { |
| 94 | .reset_val = 0x0054, |
| 95 | .vcell_shift = 4, |
| 96 | .vcell_mul = 2500, |
| 97 | .vcell_div = 1, |
| 98 | .has_low_soc_alert = 1, |
| 99 | .rcomp_bytes = 1, |
| 100 | .has_soc_alert = 0, |
| 101 | }, |
| 102 | [ID_MAX17048] = { |
| 103 | .reset_val = 0x5400, |
| 104 | .vcell_shift = 0, |
| 105 | .vcell_mul = 625, |
| 106 | .vcell_div = 8, |
| 107 | .has_low_soc_alert = 1, |
| 108 | .rcomp_bytes = 1, |
| 109 | .has_soc_alert = 1, |
| 110 | }, |
| 111 | [ID_MAX17049] = { |
| 112 | .reset_val = 0x5400, |
| 113 | .vcell_shift = 0, |
| 114 | .vcell_mul = 625, |
| 115 | .vcell_div = 4, |
| 116 | .has_low_soc_alert = 1, |
| 117 | .rcomp_bytes = 1, |
| 118 | .has_soc_alert = 1, |
| 119 | }, |
| 120 | [ID_MAX17058] = { |
| 121 | .reset_val = 0x5400, |
| 122 | .vcell_shift = 0, |
| 123 | .vcell_mul = 625, |
| 124 | .vcell_div = 8, |
| 125 | .has_low_soc_alert = 1, |
| 126 | .rcomp_bytes = 1, |
| 127 | .has_soc_alert = 0, |
| 128 | }, |
| 129 | [ID_MAX17059] = { |
| 130 | .reset_val = 0x5400, |
| 131 | .vcell_shift = 0, |
| 132 | .vcell_mul = 625, |
| 133 | .vcell_div = 4, |
| 134 | .has_low_soc_alert = 1, |
| 135 | .rcomp_bytes = 1, |
| 136 | .has_soc_alert = 0, |
| 137 | }, |
| 138 | }; |
| 139 | |
| 140 | struct max17040_chip { |
| 141 | struct i2c_client *client; |
| 142 | struct regmap *regmap; |
| 143 | struct delayed_work work; |
| 144 | struct power_supply *battery; |
| 145 | struct chip_data data; |
| 146 | struct iio_channel *channel_temp; |
| 147 | |
| 148 | /* battery capacity */ |
| 149 | int soc; |
| 150 | /* Low alert threshold from 32% to 1% of the State of Charge */ |
| 151 | u32 low_soc_alert; |
| 152 | /* some devices return twice the capacity */ |
| 153 | bool quirk_double_soc; |
| 154 | /* higher 8 bits for 17043+, 16 bits for 17040,41 */ |
| 155 | u16 rcomp; |
| 156 | }; |
| 157 | |
| 158 | static int max17040_reset(struct max17040_chip *chip) |
| 159 | { |
| 160 | return regmap_write(map: chip->regmap, MAX17040_CMD, val: chip->data.reset_val); |
| 161 | } |
| 162 | |
| 163 | static int max17040_set_low_soc_alert(struct max17040_chip *chip, u32 level) |
| 164 | { |
| 165 | level = 32 - level * (chip->quirk_double_soc ? 2 : 1); |
| 166 | return regmap_update_bits(map: chip->regmap, MAX17040_CONFIG, |
| 167 | MAX17040_ATHD_MASK, val: level); |
| 168 | } |
| 169 | |
| 170 | static int max17040_set_soc_alert(struct max17040_chip *chip, bool enable) |
| 171 | { |
| 172 | return regmap_update_bits(map: chip->regmap, MAX17040_CONFIG, |
| 173 | MAX17040_ALSC_MASK, val: enable ? MAX17040_ALSC_MASK : 0); |
| 174 | } |
| 175 | |
| 176 | static int max17040_set_rcomp(struct max17040_chip *chip, u16 rcomp) |
| 177 | { |
| 178 | u16 mask = chip->data.rcomp_bytes == 2 ? |
| 179 | 0xffff : MAX17040_CFG_RCOMP_MASK; |
| 180 | |
| 181 | return regmap_update_bits(map: chip->regmap, MAX17040_CONFIG, mask, val: rcomp); |
| 182 | } |
| 183 | |
| 184 | static int max17040_raw_vcell_to_uvolts(struct max17040_chip *chip, u16 vcell) |
| 185 | { |
| 186 | struct chip_data *d = &chip->data; |
| 187 | |
| 188 | return (vcell >> d->vcell_shift) * d->vcell_mul / d->vcell_div; |
| 189 | } |
| 190 | |
| 191 | |
| 192 | static int max17040_get_vcell(struct max17040_chip *chip) |
| 193 | { |
| 194 | u32 vcell; |
| 195 | |
| 196 | regmap_read(map: chip->regmap, MAX17040_VCELL, val: &vcell); |
| 197 | |
| 198 | return max17040_raw_vcell_to_uvolts(chip, vcell); |
| 199 | } |
| 200 | |
| 201 | static int max17040_get_soc(struct max17040_chip *chip) |
| 202 | { |
| 203 | u32 soc; |
| 204 | |
| 205 | regmap_read(map: chip->regmap, MAX17040_SOC, val: &soc); |
| 206 | |
| 207 | return soc >> (chip->quirk_double_soc ? 9 : 8); |
| 208 | } |
| 209 | |
| 210 | static int max17040_get_version(struct max17040_chip *chip) |
| 211 | { |
| 212 | int ret; |
| 213 | u32 version; |
| 214 | |
| 215 | ret = regmap_read(map: chip->regmap, MAX17040_VER, val: &version); |
| 216 | |
| 217 | return ret ? ret : version; |
| 218 | } |
| 219 | |
| 220 | static int max17040_get_online(struct max17040_chip *chip) |
| 221 | { |
| 222 | return 1; |
| 223 | } |
| 224 | |
| 225 | static int max17040_get_of_data(struct max17040_chip *chip) |
| 226 | { |
| 227 | struct device *dev = &chip->client->dev; |
| 228 | struct chip_data *data = &max17040_family[ |
| 229 | (uintptr_t) of_device_get_match_data(dev)]; |
| 230 | int rcomp_len; |
| 231 | u8 rcomp[2]; |
| 232 | |
| 233 | chip->quirk_double_soc = device_property_read_bool(dev, |
| 234 | propname: "maxim,double-soc" ); |
| 235 | |
| 236 | chip->low_soc_alert = MAX17040_ATHD_DEFAULT_POWER_UP; |
| 237 | device_property_read_u32(dev, |
| 238 | propname: "maxim,alert-low-soc-level" , |
| 239 | val: &chip->low_soc_alert); |
| 240 | |
| 241 | if (chip->low_soc_alert <= 0 || |
| 242 | chip->low_soc_alert > (chip->quirk_double_soc ? 16 : 32)) { |
| 243 | dev_err(dev, "maxim,alert-low-soc-level out of bounds\n" ); |
| 244 | return -EINVAL; |
| 245 | } |
| 246 | |
| 247 | rcomp_len = device_property_count_u8(dev, propname: "maxim,rcomp" ); |
| 248 | chip->rcomp = MAX17040_RCOMP_DEFAULT; |
| 249 | if (rcomp_len == data->rcomp_bytes) { |
| 250 | if (!device_property_read_u8_array(dev, propname: "maxim,rcomp" , |
| 251 | val: rcomp, nval: rcomp_len)) |
| 252 | chip->rcomp = rcomp_len == 2 ? rcomp[0] << 8 | rcomp[1] : |
| 253 | rcomp[0] << 8; |
| 254 | } else if (rcomp_len > 0) { |
| 255 | dev_err(dev, "maxim,rcomp has incorrect length\n" ); |
| 256 | return -EINVAL; |
| 257 | } |
| 258 | |
| 259 | return 0; |
| 260 | } |
| 261 | |
| 262 | static void max17040_check_changes(struct max17040_chip *chip) |
| 263 | { |
| 264 | chip->soc = max17040_get_soc(chip); |
| 265 | } |
| 266 | |
| 267 | static void max17040_queue_work(struct max17040_chip *chip) |
| 268 | { |
| 269 | queue_delayed_work(wq: system_power_efficient_wq, dwork: &chip->work, |
| 270 | MAX17040_DELAY); |
| 271 | } |
| 272 | |
| 273 | static void max17040_stop_work(void *data) |
| 274 | { |
| 275 | struct max17040_chip *chip = data; |
| 276 | |
| 277 | cancel_delayed_work_sync(dwork: &chip->work); |
| 278 | } |
| 279 | |
| 280 | static void max17040_work(struct work_struct *work) |
| 281 | { |
| 282 | struct max17040_chip *chip; |
| 283 | int last_soc; |
| 284 | |
| 285 | chip = container_of(work, struct max17040_chip, work.work); |
| 286 | |
| 287 | /* store SOC to check changes */ |
| 288 | last_soc = chip->soc; |
| 289 | max17040_check_changes(chip); |
| 290 | |
| 291 | /* check changes and send uevent */ |
| 292 | if (last_soc != chip->soc) |
| 293 | power_supply_changed(psy: chip->battery); |
| 294 | |
| 295 | max17040_queue_work(chip); |
| 296 | } |
| 297 | |
| 298 | /* Returns true if alert cause was SOC change, not low SOC */ |
| 299 | static bool max17040_handle_soc_alert(struct max17040_chip *chip) |
| 300 | { |
| 301 | bool ret = true; |
| 302 | u32 data; |
| 303 | |
| 304 | regmap_read(map: chip->regmap, MAX17040_STATUS, val: &data); |
| 305 | |
| 306 | if (data & MAX17040_STATUS_HD_MASK) { |
| 307 | // this alert was caused by low soc |
| 308 | ret = false; |
| 309 | } |
| 310 | if (data & MAX17040_STATUS_SC_MASK) { |
| 311 | // soc change bit -- deassert to mark as handled |
| 312 | regmap_write(map: chip->regmap, MAX17040_STATUS, |
| 313 | val: data & ~MAX17040_STATUS_SC_MASK); |
| 314 | } |
| 315 | |
| 316 | return ret; |
| 317 | } |
| 318 | |
| 319 | static irqreturn_t max17040_thread_handler(int id, void *dev) |
| 320 | { |
| 321 | struct max17040_chip *chip = dev; |
| 322 | |
| 323 | if (!(chip->data.has_soc_alert && max17040_handle_soc_alert(chip))) |
| 324 | dev_warn(&chip->client->dev, "IRQ: Alert battery low level\n" ); |
| 325 | |
| 326 | /* read registers */ |
| 327 | max17040_check_changes(chip); |
| 328 | |
| 329 | /* send uevent */ |
| 330 | power_supply_changed(psy: chip->battery); |
| 331 | |
| 332 | /* reset alert bit */ |
| 333 | max17040_set_low_soc_alert(chip, level: chip->low_soc_alert); |
| 334 | |
| 335 | return IRQ_HANDLED; |
| 336 | } |
| 337 | |
| 338 | static int max17040_enable_alert_irq(struct max17040_chip *chip) |
| 339 | { |
| 340 | struct i2c_client *client = chip->client; |
| 341 | int ret; |
| 342 | |
| 343 | ret = devm_request_threaded_irq(dev: &client->dev, irq: client->irq, NULL, |
| 344 | thread_fn: max17040_thread_handler, IRQF_ONESHOT, |
| 345 | devname: chip->battery->desc->name, dev_id: chip); |
| 346 | |
| 347 | return ret; |
| 348 | } |
| 349 | |
| 350 | static int max17040_prop_writeable(struct power_supply *psy, |
| 351 | enum power_supply_property psp) |
| 352 | { |
| 353 | switch (psp) { |
| 354 | case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: |
| 355 | return 1; |
| 356 | default: |
| 357 | return 0; |
| 358 | } |
| 359 | } |
| 360 | |
| 361 | static int max17040_set_property(struct power_supply *psy, |
| 362 | enum power_supply_property psp, |
| 363 | const union power_supply_propval *val) |
| 364 | { |
| 365 | struct max17040_chip *chip = power_supply_get_drvdata(psy); |
| 366 | int ret; |
| 367 | |
| 368 | switch (psp) { |
| 369 | case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: |
| 370 | /* alert threshold can be programmed from 1% up to 16/32% */ |
| 371 | if ((val->intval < 1) || |
| 372 | (val->intval > (chip->quirk_double_soc ? 16 : 32))) { |
| 373 | ret = -EINVAL; |
| 374 | break; |
| 375 | } |
| 376 | ret = max17040_set_low_soc_alert(chip, level: val->intval); |
| 377 | chip->low_soc_alert = val->intval; |
| 378 | break; |
| 379 | default: |
| 380 | ret = -EINVAL; |
| 381 | } |
| 382 | |
| 383 | return ret; |
| 384 | } |
| 385 | |
| 386 | static int max17040_get_property(struct power_supply *psy, |
| 387 | enum power_supply_property psp, |
| 388 | union power_supply_propval *val) |
| 389 | { |
| 390 | struct max17040_chip *chip = power_supply_get_drvdata(psy); |
| 391 | int ret; |
| 392 | |
| 393 | switch (psp) { |
| 394 | case POWER_SUPPLY_PROP_ONLINE: |
| 395 | case POWER_SUPPLY_PROP_PRESENT: |
| 396 | val->intval = max17040_get_online(chip); |
| 397 | break; |
| 398 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: |
| 399 | val->intval = max17040_get_vcell(chip); |
| 400 | break; |
| 401 | case POWER_SUPPLY_PROP_CAPACITY: |
| 402 | val->intval = max17040_get_soc(chip); |
| 403 | break; |
| 404 | case POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: |
| 405 | val->intval = chip->low_soc_alert; |
| 406 | break; |
| 407 | case POWER_SUPPLY_PROP_STATUS: |
| 408 | power_supply_get_property_from_supplier(psy, psp, val); |
| 409 | break; |
| 410 | case POWER_SUPPLY_PROP_TEMP: |
| 411 | if (!chip->channel_temp) |
| 412 | return -ENODATA; |
| 413 | |
| 414 | ret = iio_read_channel_processed(chan: chip->channel_temp, val: &val->intval); |
| 415 | if (ret) |
| 416 | return ret; |
| 417 | |
| 418 | val->intval /= 100; /* Convert from milli- to deci-degree */ |
| 419 | |
| 420 | break; |
| 421 | default: |
| 422 | return -EINVAL; |
| 423 | } |
| 424 | return 0; |
| 425 | } |
| 426 | |
| 427 | static const struct regmap_config max17040_regmap = { |
| 428 | .reg_bits = 8, |
| 429 | .reg_stride = 2, |
| 430 | .val_bits = 16, |
| 431 | .val_format_endian = REGMAP_ENDIAN_BIG, |
| 432 | }; |
| 433 | |
| 434 | static enum power_supply_property max17040_battery_props[] = { |
| 435 | POWER_SUPPLY_PROP_ONLINE, |
| 436 | POWER_SUPPLY_PROP_PRESENT, |
| 437 | POWER_SUPPLY_PROP_VOLTAGE_NOW, |
| 438 | POWER_SUPPLY_PROP_CAPACITY, |
| 439 | POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN, |
| 440 | POWER_SUPPLY_PROP_STATUS, |
| 441 | POWER_SUPPLY_PROP_TEMP, |
| 442 | }; |
| 443 | |
| 444 | static const struct power_supply_desc max17040_battery_desc = { |
| 445 | .name = "battery" , |
| 446 | .type = POWER_SUPPLY_TYPE_BATTERY, |
| 447 | .get_property = max17040_get_property, |
| 448 | .set_property = max17040_set_property, |
| 449 | .property_is_writeable = max17040_prop_writeable, |
| 450 | .properties = max17040_battery_props, |
| 451 | .num_properties = ARRAY_SIZE(max17040_battery_props), |
| 452 | }; |
| 453 | |
| 454 | static int max17040_probe(struct i2c_client *client) |
| 455 | { |
| 456 | const struct i2c_device_id *id = i2c_client_get_device_id(client); |
| 457 | struct i2c_adapter *adapter = client->adapter; |
| 458 | struct power_supply_config psy_cfg = {}; |
| 459 | struct max17040_chip *chip; |
| 460 | enum chip_id chip_id; |
| 461 | bool enable_irq = false; |
| 462 | int ret; |
| 463 | |
| 464 | if (!i2c_check_functionality(adap: adapter, I2C_FUNC_SMBUS_BYTE)) |
| 465 | return -EIO; |
| 466 | |
| 467 | chip = devm_kzalloc(dev: &client->dev, size: sizeof(*chip), GFP_KERNEL); |
| 468 | if (!chip) |
| 469 | return -ENOMEM; |
| 470 | |
| 471 | chip->client = client; |
| 472 | chip->regmap = devm_regmap_init_i2c(client, &max17040_regmap); |
| 473 | if (IS_ERR(ptr: chip->regmap)) |
| 474 | return PTR_ERR(ptr: chip->regmap); |
| 475 | chip_id = (enum chip_id) id->driver_data; |
| 476 | if (client->dev.of_node) { |
| 477 | ret = max17040_get_of_data(chip); |
| 478 | if (ret) |
| 479 | return ret; |
| 480 | chip_id = (uintptr_t)of_device_get_match_data(dev: &client->dev); |
| 481 | } |
| 482 | chip->data = max17040_family[chip_id]; |
| 483 | |
| 484 | i2c_set_clientdata(client, data: chip); |
| 485 | psy_cfg.drv_data = chip; |
| 486 | |
| 487 | /* Switch to devm_iio_channel_get_optional when available */ |
| 488 | chip->channel_temp = devm_iio_channel_get(dev: &client->dev, consumer_channel: "temp" ); |
| 489 | if (IS_ERR(ptr: chip->channel_temp)) { |
| 490 | ret = PTR_ERR(ptr: chip->channel_temp); |
| 491 | if (ret != -ENODEV) |
| 492 | return dev_err_probe(dev: &client->dev, err: PTR_ERR(ptr: chip->channel_temp), |
| 493 | fmt: "failed to get temp\n" ); |
| 494 | else |
| 495 | chip->channel_temp = NULL; |
| 496 | } |
| 497 | |
| 498 | chip->battery = devm_power_supply_register(parent: &client->dev, |
| 499 | desc: &max17040_battery_desc, cfg: &psy_cfg); |
| 500 | if (IS_ERR(ptr: chip->battery)) { |
| 501 | dev_err(&client->dev, "failed: power supply register\n" ); |
| 502 | return PTR_ERR(ptr: chip->battery); |
| 503 | } |
| 504 | |
| 505 | ret = max17040_get_version(chip); |
| 506 | if (ret < 0) |
| 507 | return ret; |
| 508 | dev_dbg(&chip->client->dev, "MAX17040 Fuel-Gauge Ver 0x%x\n" , ret); |
| 509 | |
| 510 | if (chip_id == ID_MAX17040 || chip_id == ID_MAX17041) |
| 511 | max17040_reset(chip); |
| 512 | |
| 513 | max17040_set_rcomp(chip, rcomp: chip->rcomp); |
| 514 | |
| 515 | /* check interrupt */ |
| 516 | if (client->irq && chip->data.has_low_soc_alert) { |
| 517 | ret = max17040_set_low_soc_alert(chip, level: chip->low_soc_alert); |
| 518 | if (ret) { |
| 519 | dev_err(&client->dev, |
| 520 | "Failed to set low SOC alert: err %d\n" , ret); |
| 521 | return ret; |
| 522 | } |
| 523 | |
| 524 | enable_irq = true; |
| 525 | } |
| 526 | |
| 527 | if (client->irq && chip->data.has_soc_alert) { |
| 528 | ret = max17040_set_soc_alert(chip, enable: 1); |
| 529 | if (ret) { |
| 530 | dev_err(&client->dev, |
| 531 | "Failed to set SOC alert: err %d\n" , ret); |
| 532 | return ret; |
| 533 | } |
| 534 | enable_irq = true; |
| 535 | } else { |
| 536 | /* soc alerts negate the need for polling */ |
| 537 | INIT_DEFERRABLE_WORK(&chip->work, max17040_work); |
| 538 | ret = devm_add_action(&client->dev, max17040_stop_work, chip); |
| 539 | if (ret) |
| 540 | return ret; |
| 541 | max17040_queue_work(chip); |
| 542 | } |
| 543 | |
| 544 | if (enable_irq) { |
| 545 | ret = max17040_enable_alert_irq(chip); |
| 546 | if (ret) { |
| 547 | client->irq = 0; |
| 548 | dev_warn(&client->dev, |
| 549 | "Failed to get IRQ err %d\n" , ret); |
| 550 | } |
| 551 | } |
| 552 | |
| 553 | return 0; |
| 554 | } |
| 555 | |
| 556 | #ifdef CONFIG_PM_SLEEP |
| 557 | |
| 558 | static int max17040_suspend(struct device *dev) |
| 559 | { |
| 560 | struct i2c_client *client = to_i2c_client(dev); |
| 561 | struct max17040_chip *chip = i2c_get_clientdata(client); |
| 562 | |
| 563 | if (client->irq && chip->data.has_soc_alert) |
| 564 | // disable soc alert to prevent wakeup |
| 565 | max17040_set_soc_alert(chip, enable: 0); |
| 566 | else |
| 567 | cancel_delayed_work(dwork: &chip->work); |
| 568 | |
| 569 | if (client->irq && device_may_wakeup(dev)) |
| 570 | enable_irq_wake(irq: client->irq); |
| 571 | |
| 572 | return 0; |
| 573 | } |
| 574 | |
| 575 | static int max17040_resume(struct device *dev) |
| 576 | { |
| 577 | struct i2c_client *client = to_i2c_client(dev); |
| 578 | struct max17040_chip *chip = i2c_get_clientdata(client); |
| 579 | |
| 580 | if (client->irq && device_may_wakeup(dev)) |
| 581 | disable_irq_wake(irq: client->irq); |
| 582 | |
| 583 | if (client->irq && chip->data.has_soc_alert) |
| 584 | max17040_set_soc_alert(chip, enable: 1); |
| 585 | else |
| 586 | max17040_queue_work(chip); |
| 587 | |
| 588 | return 0; |
| 589 | } |
| 590 | |
| 591 | static SIMPLE_DEV_PM_OPS(max17040_pm_ops, max17040_suspend, max17040_resume); |
| 592 | #define MAX17040_PM_OPS (&max17040_pm_ops) |
| 593 | |
| 594 | #else |
| 595 | |
| 596 | #define MAX17040_PM_OPS NULL |
| 597 | |
| 598 | #endif /* CONFIG_PM_SLEEP */ |
| 599 | |
| 600 | static const struct i2c_device_id max17040_id[] = { |
| 601 | { "max17040" , ID_MAX17040 }, |
| 602 | { "max17041" , ID_MAX17041 }, |
| 603 | { "max17043" , ID_MAX17043 }, |
| 604 | { "max77836-battery" , ID_MAX17043 }, |
| 605 | { "max17044" , ID_MAX17044 }, |
| 606 | { "max17048" , ID_MAX17048 }, |
| 607 | { "max17049" , ID_MAX17049 }, |
| 608 | { "max17058" , ID_MAX17058 }, |
| 609 | { "max17059" , ID_MAX17059 }, |
| 610 | { /* sentinel */ } |
| 611 | }; |
| 612 | MODULE_DEVICE_TABLE(i2c, max17040_id); |
| 613 | |
| 614 | static const struct of_device_id max17040_of_match[] = { |
| 615 | { .compatible = "maxim,max17040" , .data = (void *) ID_MAX17040 }, |
| 616 | { .compatible = "maxim,max17041" , .data = (void *) ID_MAX17041 }, |
| 617 | { .compatible = "maxim,max17043" , .data = (void *) ID_MAX17043 }, |
| 618 | { .compatible = "maxim,max77836-battery" , .data = (void *) ID_MAX17043 }, |
| 619 | { .compatible = "maxim,max17044" , .data = (void *) ID_MAX17044 }, |
| 620 | { .compatible = "maxim,max17048" , .data = (void *) ID_MAX17048 }, |
| 621 | { .compatible = "maxim,max17049" , .data = (void *) ID_MAX17049 }, |
| 622 | { .compatible = "maxim,max17058" , .data = (void *) ID_MAX17058 }, |
| 623 | { .compatible = "maxim,max17059" , .data = (void *) ID_MAX17059 }, |
| 624 | { /* sentinel */ }, |
| 625 | }; |
| 626 | MODULE_DEVICE_TABLE(of, max17040_of_match); |
| 627 | |
| 628 | static struct i2c_driver max17040_i2c_driver = { |
| 629 | .driver = { |
| 630 | .name = "max17040" , |
| 631 | .of_match_table = max17040_of_match, |
| 632 | .pm = MAX17040_PM_OPS, |
| 633 | }, |
| 634 | .probe = max17040_probe, |
| 635 | .id_table = max17040_id, |
| 636 | }; |
| 637 | module_i2c_driver(max17040_i2c_driver); |
| 638 | |
| 639 | MODULE_AUTHOR("Minkyu Kang <mk7.kang@samsung.com>" ); |
| 640 | MODULE_DESCRIPTION("MAX17040 Fuel Gauge" ); |
| 641 | MODULE_LICENSE("GPL" ); |
| 642 | |