bq20z75: Add optional battery detect gpio

Adding support for an optional gpio for battery detection. This is
passed in through the i2c platform data. It also accepts another
field, battery_detect_present to signify the gpio state which means
the battery is present, either 0 (low) or 1 (high).

Signed-off-by: Rhyland Klein <rklein@nvidia.com>
Signed-off-by: Anton Vorontsov <cbouatmailru@gmail.com>

authored by Rhyland Klein and committed by Anton Vorontsov bb879101 b5db7cde

+166 -31
+129 -31
drivers/power/bq20z75.c
··· 25 25 #include <linux/power_supply.h> 26 26 #include <linux/i2c.h> 27 27 #include <linux/slab.h> 28 + #include <linux/interrupt.h> 29 + #include <linux/gpio.h> 30 + 31 + #include <linux/power/bq20z75.h> 28 32 29 33 enum { 30 34 REG_MANUFACTURER_DATA, ··· 145 141 }; 146 142 147 143 struct bq20z75_info { 148 - struct i2c_client *client; 149 - struct power_supply power_supply; 144 + struct i2c_client *client; 145 + struct power_supply power_supply; 146 + struct bq20z75_platform_data *pdata; 147 + bool is_present; 148 + bool gpio_detect; 149 + bool enable_detection; 150 + int irq; 150 151 }; 151 152 152 153 static int bq20z75_read_word_data(struct i2c_client *client, u8 address) ··· 188 179 union power_supply_propval *val) 189 180 { 190 181 s32 ret; 182 + struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); 183 + 184 + if (psp == POWER_SUPPLY_PROP_PRESENT && 185 + bq20z75_device->gpio_detect) { 186 + ret = gpio_get_value( 187 + bq20z75_device->pdata->battery_detect); 188 + if (ret == bq20z75_device->pdata->battery_detect_present) 189 + val->intval = 1; 190 + else 191 + val->intval = 0; 192 + return ret; 193 + } 191 194 192 195 /* Write to ManufacturerAccess with 193 196 * ManufacturerAccess command and then ··· 213 192 214 193 ret = bq20z75_read_word_data(client, 215 194 bq20z75_data[REG_MANUFACTURER_DATA].addr); 216 - if (ret < 0) 195 + if (ret < 0) { 196 + if (psp == POWER_SUPPLY_PROP_PRESENT) 197 + val->intval = 0; /* battery removed */ 217 198 return ret; 199 + } 218 200 219 201 if (ret < bq20z75_data[REG_MANUFACTURER_DATA].min_value || 220 202 ret > bq20z75_data[REG_MANUFACTURER_DATA].max_value) { ··· 421 397 enum power_supply_property psp, 422 398 union power_supply_propval *val) 423 399 { 424 - int ps_index; 425 - int ret; 400 + int ret = 0; 426 401 struct bq20z75_info *bq20z75_device = container_of(psy, 427 402 struct bq20z75_info, power_supply); 428 403 struct i2c_client *client = bq20z75_device->client; ··· 430 407 case POWER_SUPPLY_PROP_PRESENT: 431 408 case POWER_SUPPLY_PROP_HEALTH: 432 409 ret = bq20z75_get_battery_presence_and_health(client, psp, val); 433 - if (ret) 434 - return ret; 435 410 break; 436 411 437 412 case POWER_SUPPLY_PROP_TECHNOLOGY: ··· 443 422 case POWER_SUPPLY_PROP_CHARGE_FULL: 444 423 case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: 445 424 case POWER_SUPPLY_PROP_CAPACITY: 446 - ps_index = bq20z75_get_property_index(client, psp); 447 - if (ps_index < 0) 448 - return ps_index; 425 + ret = bq20z75_get_property_index(client, psp); 426 + if (ret < 0) 427 + break; 449 428 450 - ret = bq20z75_get_battery_capacity(client, ps_index, psp, val); 451 - if (ret) 452 - return ret; 453 - 429 + ret = bq20z75_get_battery_capacity(client, ret, psp, val); 454 430 break; 455 431 456 432 case POWER_SUPPLY_PROP_SERIAL_NUMBER: 457 433 ret = bq20z75_get_battery_serial_number(client, val); 458 - if (ret) 459 - return ret; 460 434 break; 461 435 462 436 case POWER_SUPPLY_PROP_STATUS: ··· 462 446 case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: 463 447 case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: 464 448 case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: 465 - ps_index = bq20z75_get_property_index(client, psp); 466 - if (ps_index < 0) 467 - return ps_index; 449 + ret = bq20z75_get_property_index(client, psp); 450 + if (ret < 0) 451 + break; 468 452 469 - ret = bq20z75_get_battery_property(client, ps_index, psp, val); 470 - if (ret) 471 - return ret; 472 - 453 + ret = bq20z75_get_battery_property(client, ret, psp, val); 473 454 break; 474 455 475 456 default: ··· 475 462 return -EINVAL; 476 463 } 477 464 478 - /* Convert units to match requirements for power supply class */ 479 - bq20z75_unit_adjustment(client, psp, val); 465 + if (!bq20z75_device->enable_detection) 466 + goto done; 467 + 468 + if (!bq20z75_device->gpio_detect && 469 + bq20z75_device->is_present != (ret >= 0)) { 470 + bq20z75_device->is_present = (ret >= 0); 471 + power_supply_changed(&bq20z75_device->power_supply); 472 + } 473 + 474 + done: 475 + if (!ret) { 476 + /* Convert units to match requirements for power supply class */ 477 + bq20z75_unit_adjustment(client, psp, val); 478 + } 480 479 481 480 dev_dbg(&client->dev, 482 481 "%s: property = %d, value = %d\n", __func__, psp, val->intval); 483 482 484 - return 0; 483 + return ret; 485 484 } 486 485 487 - static int bq20z75_probe(struct i2c_client *client, 486 + static irqreturn_t bq20z75_irq(int irq, void *devid) 487 + { 488 + struct power_supply *battery = devid; 489 + 490 + power_supply_changed(battery); 491 + 492 + return IRQ_HANDLED; 493 + } 494 + 495 + static int __devinit bq20z75_probe(struct i2c_client *client, 488 496 const struct i2c_device_id *id) 489 497 { 490 498 struct bq20z75_info *bq20z75_device; 499 + struct bq20z75_platform_data *pdata = client->dev.platform_data; 491 500 int rc; 501 + int irq; 492 502 493 503 bq20z75_device = kzalloc(sizeof(struct bq20z75_info), GFP_KERNEL); 494 504 if (!bq20z75_device) 495 505 return -ENOMEM; 496 506 497 507 bq20z75_device->client = client; 508 + bq20z75_device->enable_detection = false; 509 + bq20z75_device->gpio_detect = false; 498 510 bq20z75_device->power_supply.name = "battery"; 499 511 bq20z75_device->power_supply.type = POWER_SUPPLY_TYPE_BATTERY; 500 512 bq20z75_device->power_supply.properties = bq20z75_properties; ··· 527 489 ARRAY_SIZE(bq20z75_properties); 528 490 bq20z75_device->power_supply.get_property = bq20z75_get_property; 529 491 492 + if (pdata) { 493 + bq20z75_device->gpio_detect = 494 + gpio_is_valid(pdata->battery_detect); 495 + bq20z75_device->pdata = pdata; 496 + } 497 + 530 498 i2c_set_clientdata(client, bq20z75_device); 499 + 500 + if (!bq20z75_device->gpio_detect) 501 + goto skip_gpio; 502 + 503 + rc = gpio_request(pdata->battery_detect, dev_name(&client->dev)); 504 + if (rc) { 505 + dev_warn(&client->dev, "Failed to request gpio: %d\n", rc); 506 + bq20z75_device->gpio_detect = false; 507 + goto skip_gpio; 508 + } 509 + 510 + rc = gpio_direction_input(pdata->battery_detect); 511 + if (rc) { 512 + dev_warn(&client->dev, "Failed to get gpio as input: %d\n", rc); 513 + gpio_free(pdata->battery_detect); 514 + bq20z75_device->gpio_detect = false; 515 + goto skip_gpio; 516 + } 517 + 518 + irq = gpio_to_irq(pdata->battery_detect); 519 + if (irq <= 0) { 520 + dev_warn(&client->dev, "Failed to get gpio as irq: %d\n", irq); 521 + gpio_free(pdata->battery_detect); 522 + bq20z75_device->gpio_detect = false; 523 + goto skip_gpio; 524 + } 525 + 526 + rc = request_irq(irq, bq20z75_irq, 527 + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 528 + dev_name(&client->dev), &bq20z75_device->power_supply); 529 + if (rc) { 530 + dev_warn(&client->dev, "Failed to request irq: %d\n", rc); 531 + gpio_free(pdata->battery_detect); 532 + bq20z75_device->gpio_detect = false; 533 + goto skip_gpio; 534 + } 535 + 536 + bq20z75_device->irq = irq; 537 + 538 + skip_gpio: 531 539 532 540 rc = power_supply_register(&client->dev, &bq20z75_device->power_supply); 533 541 if (rc) { 534 542 dev_err(&client->dev, 535 543 "%s: Failed to register power supply\n", __func__); 536 - kfree(bq20z75_device); 537 - return rc; 544 + goto exit_psupply; 538 545 } 539 546 540 547 dev_info(&client->dev, 541 548 "%s: battery gas gauge device registered\n", client->name); 542 549 543 550 return 0; 551 + 552 + exit_psupply: 553 + if (bq20z75_device->irq) 554 + free_irq(bq20z75_device->irq, &bq20z75_device->power_supply); 555 + if (bq20z75_device->gpio_detect) 556 + gpio_free(pdata->battery_detect); 557 + 558 + kfree(bq20z75_device); 559 + 560 + return rc; 544 561 } 545 562 546 - static int bq20z75_remove(struct i2c_client *client) 563 + static int __devexit bq20z75_remove(struct i2c_client *client) 547 564 { 548 565 struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client); 566 + 567 + if (bq20z75_device->irq) 568 + free_irq(bq20z75_device->irq, &bq20z75_device->power_supply); 569 + if (bq20z75_device->gpio_detect) 570 + gpio_free(bq20z75_device->pdata->battery_detect); 549 571 550 572 power_supply_unregister(&bq20z75_device->power_supply); 551 573 kfree(bq20z75_device); ··· 642 544 643 545 static struct i2c_driver bq20z75_battery_driver = { 644 546 .probe = bq20z75_probe, 645 - .remove = bq20z75_remove, 547 + .remove = __devexit_p(bq20z75_remove), 646 548 .suspend = bq20z75_suspend, 647 549 .resume = bq20z75_resume, 648 550 .id_table = bq20z75_id,
+37
include/linux/power/bq20z75.h
··· 1 + /* 2 + * Gas Gauge driver for TI's BQ20Z75 3 + * 4 + * Copyright (c) 2010, NVIDIA Corporation. 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License as published by 8 + * the Free Software Foundation; either version 2 of the License, or 9 + * (at your option) any later version. 10 + * 11 + * This program is distributed in the hope that it will be useful, but WITHOUT 12 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 14 + * more details. 15 + * 16 + * You should have received a copy of the GNU General Public License along 17 + * with this program; if not, write to the Free Software Foundation, Inc., 18 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19 + */ 20 + 21 + #ifndef __LINUX_POWER_BQ20Z75_H_ 22 + #define __LINUX_POWER_BQ20Z75_H_ 23 + 24 + #include <linux/power_supply.h> 25 + #include <linux/types.h> 26 + 27 + /** 28 + * struct bq20z75_platform_data - platform data for bq20z75 devices 29 + * @battery_detect: GPIO which is used to detect battery presence 30 + * @battery_detect_present: gpio state when battery is present (0 / 1) 31 + */ 32 + struct bq20z75_platform_data { 33 + int battery_detect; 34 + int battery_detect_present; 35 + }; 36 + 37 + #endif