linux/drivers/power/bq24735-charger.c
<<
>>
Prefs
   1/*
   2 * Battery charger driver for TI BQ24735
   3 *
   4 * Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.
   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;
   9 *
  10 * This program is distributed in the hope that it will be useful, but WITHOUT
  11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  13 * more details.
  14 *
  15 * You should have received a copy of the GNU General Public License along
  16 * with this program; if not, write to the Free Software Foundation, Inc.,
  17 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  18 */
  19
  20#include <linux/err.h>
  21#include <linux/gpio.h>
  22#include <linux/i2c.h>
  23#include <linux/init.h>
  24#include <linux/interrupt.h>
  25#include <linux/kernel.h>
  26#include <linux/module.h>
  27#include <linux/of.h>
  28#include <linux/of_gpio.h>
  29#include <linux/power_supply.h>
  30#include <linux/slab.h>
  31
  32#include <linux/power/bq24735-charger.h>
  33
  34#define BQ24735_CHG_OPT                 0x12
  35#define BQ24735_CHG_OPT_CHARGE_DISABLE  (1 << 0)
  36#define BQ24735_CHG_OPT_AC_PRESENT      (1 << 4)
  37#define BQ24735_CHARGE_CURRENT          0x14
  38#define BQ24735_CHARGE_CURRENT_MASK     0x1fc0
  39#define BQ24735_CHARGE_VOLTAGE          0x15
  40#define BQ24735_CHARGE_VOLTAGE_MASK     0x7ff0
  41#define BQ24735_INPUT_CURRENT           0x3f
  42#define BQ24735_INPUT_CURRENT_MASK      0x1f80
  43#define BQ24735_MANUFACTURER_ID         0xfe
  44#define BQ24735_DEVICE_ID               0xff
  45
  46struct bq24735 {
  47        struct power_supply             *charger;
  48        struct power_supply_desc        charger_desc;
  49        struct i2c_client               *client;
  50        struct bq24735_platform         *pdata;
  51        struct mutex                    lock;
  52        bool                            charging;
  53};
  54
  55static inline struct bq24735 *to_bq24735(struct power_supply *psy)
  56{
  57        return power_supply_get_drvdata(psy);
  58}
  59
  60static enum power_supply_property bq24735_charger_properties[] = {
  61        POWER_SUPPLY_PROP_STATUS,
  62        POWER_SUPPLY_PROP_ONLINE,
  63};
  64
  65static int bq24735_charger_property_is_writeable(struct power_supply *psy,
  66                                                 enum power_supply_property psp)
  67{
  68        switch (psp) {
  69        case POWER_SUPPLY_PROP_STATUS:
  70                return 1;
  71        default:
  72                break;
  73        }
  74
  75        return 0;
  76}
  77
  78static inline int bq24735_write_word(struct i2c_client *client, u8 reg,
  79                                     u16 value)
  80{
  81        return i2c_smbus_write_word_data(client, reg, le16_to_cpu(value));
  82}
  83
  84static inline int bq24735_read_word(struct i2c_client *client, u8 reg)
  85{
  86        s32 ret = i2c_smbus_read_word_data(client, reg);
  87
  88        return ret < 0 ? ret : le16_to_cpu(ret);
  89}
  90
  91static int bq24735_update_word(struct i2c_client *client, u8 reg,
  92                               u16 mask, u16 value)
  93{
  94        unsigned int tmp;
  95        int ret;
  96
  97        ret = bq24735_read_word(client, reg);
  98        if (ret < 0)
  99                return ret;
 100
 101        tmp = ret & ~mask;
 102        tmp |= value & mask;
 103
 104        return bq24735_write_word(client, reg, tmp);
 105}
 106
 107static inline int bq24735_enable_charging(struct bq24735 *charger)
 108{
 109        if (charger->pdata->ext_control)
 110                return 0;
 111
 112        return bq24735_update_word(charger->client, BQ24735_CHG_OPT,
 113                                   BQ24735_CHG_OPT_CHARGE_DISABLE,
 114                                   ~BQ24735_CHG_OPT_CHARGE_DISABLE);
 115}
 116
 117static inline int bq24735_disable_charging(struct bq24735 *charger)
 118{
 119        if (charger->pdata->ext_control)
 120                return 0;
 121
 122        return bq24735_update_word(charger->client, BQ24735_CHG_OPT,
 123                                   BQ24735_CHG_OPT_CHARGE_DISABLE,
 124                                   BQ24735_CHG_OPT_CHARGE_DISABLE);
 125}
 126
 127static int bq24735_config_charger(struct bq24735 *charger)
 128{
 129        struct bq24735_platform *pdata = charger->pdata;
 130        int ret;
 131        u16 value;
 132
 133        if (pdata->ext_control)
 134                return 0;
 135
 136        if (pdata->charge_current) {
 137                value = pdata->charge_current & BQ24735_CHARGE_CURRENT_MASK;
 138
 139                ret = bq24735_write_word(charger->client,
 140                                         BQ24735_CHARGE_CURRENT, value);
 141                if (ret < 0) {
 142                        dev_err(&charger->client->dev,
 143                                "Failed to write charger current : %d\n",
 144                                ret);
 145                        return ret;
 146                }
 147        }
 148
 149        if (pdata->charge_voltage) {
 150                value = pdata->charge_voltage & BQ24735_CHARGE_VOLTAGE_MASK;
 151
 152                ret = bq24735_write_word(charger->client,
 153                                         BQ24735_CHARGE_VOLTAGE, value);
 154                if (ret < 0) {
 155                        dev_err(&charger->client->dev,
 156                                "Failed to write charger voltage : %d\n",
 157                                ret);
 158                        return ret;
 159                }
 160        }
 161
 162        if (pdata->input_current) {
 163                value = pdata->input_current & BQ24735_INPUT_CURRENT_MASK;
 164
 165                ret = bq24735_write_word(charger->client,
 166                                         BQ24735_INPUT_CURRENT, value);
 167                if (ret < 0) {
 168                        dev_err(&charger->client->dev,
 169                                "Failed to write input current : %d\n",
 170                                ret);
 171                        return ret;
 172                }
 173        }
 174
 175        return 0;
 176}
 177
 178static bool bq24735_charger_is_present(struct bq24735 *charger)
 179{
 180        struct bq24735_platform *pdata = charger->pdata;
 181        int ret;
 182
 183        if (pdata->status_gpio_valid) {
 184                ret = gpio_get_value_cansleep(pdata->status_gpio);
 185                return ret ^= pdata->status_gpio_active_low == 0;
 186        } else {
 187                int ac = 0;
 188
 189                ac = bq24735_read_word(charger->client, BQ24735_CHG_OPT);
 190                if (ac < 0) {
 191                        dev_err(&charger->client->dev,
 192                                "Failed to read charger options : %d\n",
 193                                ac);
 194                        return false;
 195                }
 196                return (ac & BQ24735_CHG_OPT_AC_PRESENT) ? true : false;
 197        }
 198
 199        return false;
 200}
 201
 202static int bq24735_charger_is_charging(struct bq24735 *charger)
 203{
 204        int ret = bq24735_read_word(charger->client, BQ24735_CHG_OPT);
 205
 206        if (ret < 0)
 207                return ret;
 208
 209        return !(ret & BQ24735_CHG_OPT_CHARGE_DISABLE);
 210}
 211
 212static irqreturn_t bq24735_charger_isr(int irq, void *devid)
 213{
 214        struct power_supply *psy = devid;
 215        struct bq24735 *charger = to_bq24735(psy);
 216
 217        mutex_lock(&charger->lock);
 218
 219        if (charger->charging && bq24735_charger_is_present(charger))
 220                bq24735_enable_charging(charger);
 221        else
 222                bq24735_disable_charging(charger);
 223
 224        mutex_unlock(&charger->lock);
 225
 226        power_supply_changed(psy);
 227
 228        return IRQ_HANDLED;
 229}
 230
 231static int bq24735_charger_get_property(struct power_supply *psy,
 232                                        enum power_supply_property psp,
 233                                        union power_supply_propval *val)
 234{
 235        struct bq24735 *charger = to_bq24735(psy);
 236
 237        switch (psp) {
 238        case POWER_SUPPLY_PROP_ONLINE:
 239                val->intval = bq24735_charger_is_present(charger) ? 1 : 0;
 240                break;
 241        case POWER_SUPPLY_PROP_STATUS:
 242                switch (bq24735_charger_is_charging(charger)) {
 243                case 1:
 244                        val->intval = POWER_SUPPLY_STATUS_CHARGING;
 245                        break;
 246                case 0:
 247                        val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
 248                        break;
 249                default:
 250                        val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
 251                        break;
 252                }
 253                break;
 254        default:
 255                return -EINVAL;
 256        }
 257
 258        return 0;
 259}
 260
 261static int bq24735_charger_set_property(struct power_supply *psy,
 262                                        enum power_supply_property psp,
 263                                        const union power_supply_propval *val)
 264{
 265        struct bq24735 *charger = to_bq24735(psy);
 266        int ret;
 267
 268        switch (psp) {
 269        case POWER_SUPPLY_PROP_STATUS:
 270                switch (val->intval) {
 271                case POWER_SUPPLY_STATUS_CHARGING:
 272                        mutex_lock(&charger->lock);
 273                        charger->charging = true;
 274                        ret = bq24735_enable_charging(charger);
 275                        mutex_unlock(&charger->lock);
 276                        if (ret)
 277                                return ret;
 278                        bq24735_config_charger(charger);
 279                        break;
 280                case POWER_SUPPLY_STATUS_DISCHARGING:
 281                case POWER_SUPPLY_STATUS_NOT_CHARGING:
 282                        mutex_lock(&charger->lock);
 283                        charger->charging = false;
 284                        ret = bq24735_disable_charging(charger);
 285                        mutex_unlock(&charger->lock);
 286                        if (ret)
 287                                return ret;
 288                        break;
 289                default:
 290                        return -EINVAL;
 291                }
 292                power_supply_changed(psy);
 293                break;
 294        default:
 295                return -EPERM;
 296        }
 297
 298        return 0;
 299}
 300
 301static struct bq24735_platform *bq24735_parse_dt_data(struct i2c_client *client)
 302{
 303        struct bq24735_platform *pdata;
 304        struct device_node *np = client->dev.of_node;
 305        u32 val;
 306        int ret;
 307        enum of_gpio_flags flags;
 308
 309        pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
 310        if (!pdata) {
 311                dev_err(&client->dev,
 312                        "Memory alloc for bq24735 pdata failed\n");
 313                return NULL;
 314        }
 315
 316        pdata->status_gpio = of_get_named_gpio_flags(np, "ti,ac-detect-gpios",
 317                                                     0, &flags);
 318
 319        if (flags & OF_GPIO_ACTIVE_LOW)
 320                pdata->status_gpio_active_low = 1;
 321
 322        ret = of_property_read_u32(np, "ti,charge-current", &val);
 323        if (!ret)
 324                pdata->charge_current = val;
 325
 326        ret = of_property_read_u32(np, "ti,charge-voltage", &val);
 327        if (!ret)
 328                pdata->charge_voltage = val;
 329
 330        ret = of_property_read_u32(np, "ti,input-current", &val);
 331        if (!ret)
 332                pdata->input_current = val;
 333
 334        pdata->ext_control = of_property_read_bool(np, "ti,external-control");
 335
 336        return pdata;
 337}
 338
 339static int bq24735_charger_probe(struct i2c_client *client,
 340                                 const struct i2c_device_id *id)
 341{
 342        int ret;
 343        struct bq24735 *charger;
 344        struct power_supply_desc *supply_desc;
 345        struct power_supply_config psy_cfg = {};
 346        char *name;
 347
 348        charger = devm_kzalloc(&client->dev, sizeof(*charger), GFP_KERNEL);
 349        if (!charger)
 350                return -ENOMEM;
 351
 352        mutex_init(&charger->lock);
 353        charger->charging = true;
 354        charger->pdata = client->dev.platform_data;
 355
 356        if (IS_ENABLED(CONFIG_OF) && !charger->pdata && client->dev.of_node)
 357                charger->pdata = bq24735_parse_dt_data(client);
 358
 359        if (!charger->pdata) {
 360                dev_err(&client->dev, "no platform data provided\n");
 361                return -EINVAL;
 362        }
 363
 364        name = (char *)charger->pdata->name;
 365        if (!name) {
 366                name = devm_kasprintf(&client->dev, GFP_KERNEL,
 367                                      "bq24735@%s",
 368                                      dev_name(&client->dev));
 369                if (!name) {
 370                        dev_err(&client->dev, "Failed to alloc device name\n");
 371                        return -ENOMEM;
 372                }
 373        }
 374
 375        charger->client = client;
 376
 377        supply_desc = &charger->charger_desc;
 378
 379        supply_desc->name = name;
 380        supply_desc->type = POWER_SUPPLY_TYPE_MAINS;
 381        supply_desc->properties = bq24735_charger_properties;
 382        supply_desc->num_properties = ARRAY_SIZE(bq24735_charger_properties);
 383        supply_desc->get_property = bq24735_charger_get_property;
 384        supply_desc->set_property = bq24735_charger_set_property;
 385        supply_desc->property_is_writeable =
 386                                bq24735_charger_property_is_writeable;
 387
 388        psy_cfg.supplied_to = charger->pdata->supplied_to;
 389        psy_cfg.num_supplicants = charger->pdata->num_supplicants;
 390        psy_cfg.of_node = client->dev.of_node;
 391        psy_cfg.drv_data = charger;
 392
 393        i2c_set_clientdata(client, charger);
 394
 395        if (gpio_is_valid(charger->pdata->status_gpio)) {
 396                ret = devm_gpio_request(&client->dev,
 397                                        charger->pdata->status_gpio,
 398                                        name);
 399                if (ret) {
 400                        dev_err(&client->dev,
 401                                "Failed GPIO request for GPIO %d: %d\n",
 402                                charger->pdata->status_gpio, ret);
 403                }
 404
 405                charger->pdata->status_gpio_valid = !ret;
 406        }
 407
 408        if (!charger->pdata->status_gpio_valid
 409            || bq24735_charger_is_present(charger)) {
 410                ret = bq24735_read_word(client, BQ24735_MANUFACTURER_ID);
 411                if (ret < 0) {
 412                        dev_err(&client->dev, "Failed to read manufacturer id : %d\n",
 413                                ret);
 414                        return ret;
 415                } else if (ret != 0x0040) {
 416                        dev_err(&client->dev,
 417                                "manufacturer id mismatch. 0x0040 != 0x%04x\n", ret);
 418                        return -ENODEV;
 419                }
 420
 421                ret = bq24735_read_word(client, BQ24735_DEVICE_ID);
 422                if (ret < 0) {
 423                        dev_err(&client->dev, "Failed to read device id : %d\n", ret);
 424                        return ret;
 425                } else if (ret != 0x000B) {
 426                        dev_err(&client->dev,
 427                                "device id mismatch. 0x000b != 0x%04x\n", ret);
 428                        return -ENODEV;
 429                }
 430        }
 431
 432        ret = bq24735_config_charger(charger);
 433        if (ret < 0) {
 434                dev_err(&client->dev, "failed in configuring charger");
 435                return ret;
 436        }
 437
 438        /* check for AC adapter presence */
 439        if (bq24735_charger_is_present(charger)) {
 440                ret = bq24735_enable_charging(charger);
 441                if (ret < 0) {
 442                        dev_err(&client->dev, "Failed to enable charging\n");
 443                        return ret;
 444                }
 445        }
 446
 447        charger->charger = devm_power_supply_register(&client->dev, supply_desc,
 448                                                      &psy_cfg);
 449        if (IS_ERR(charger->charger)) {
 450                ret = PTR_ERR(charger->charger);
 451                dev_err(&client->dev, "Failed to register power supply: %d\n",
 452                        ret);
 453                return ret;
 454        }
 455
 456        if (client->irq) {
 457                ret = devm_request_threaded_irq(&client->dev, client->irq,
 458                                                NULL, bq24735_charger_isr,
 459                                                IRQF_TRIGGER_RISING |
 460                                                IRQF_TRIGGER_FALLING |
 461                                                IRQF_ONESHOT,
 462                                                supply_desc->name,
 463                                                charger->charger);
 464                if (ret) {
 465                        dev_err(&client->dev,
 466                                "Unable to register IRQ %d err %d\n",
 467                                client->irq, ret);
 468                        return ret;
 469                }
 470        }
 471
 472        return 0;
 473}
 474
 475static const struct i2c_device_id bq24735_charger_id[] = {
 476        { "bq24735-charger", 0 },
 477        {}
 478};
 479MODULE_DEVICE_TABLE(i2c, bq24735_charger_id);
 480
 481static const struct of_device_id bq24735_match_ids[] = {
 482        { .compatible = "ti,bq24735", },
 483        { /* end */ }
 484};
 485MODULE_DEVICE_TABLE(of, bq24735_match_ids);
 486
 487static struct i2c_driver bq24735_charger_driver = {
 488        .driver = {
 489                .name = "bq24735-charger",
 490                .of_match_table = bq24735_match_ids,
 491        },
 492        .probe = bq24735_charger_probe,
 493        .id_table = bq24735_charger_id,
 494};
 495
 496module_i2c_driver(bq24735_charger_driver);
 497
 498MODULE_DESCRIPTION("bq24735 battery charging driver");
 499MODULE_AUTHOR("Darbha Sriharsha <dsriharsha@nvidia.com>");
 500MODULE_LICENSE("GPL v2");
 501
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.