linux/drivers/power/supply/qcom_smbb.c
<<
>>
Prefs
   1/* Copyright (c) 2014, Sony Mobile Communications Inc.
   2 *
   3 * This program is free software; you can redistribute it and/or modify
   4 * it under the terms of the GNU General Public License version 2 and
   5 * only version 2 as published by the Free Software Foundation.
   6 *
   7 * This program is distributed in the hope that it will be useful,
   8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
   9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  10 * GNU General Public License for more details.
  11 *
  12 * This driver is for the multi-block Switch-Mode Battery Charger and Boost
  13 * (SMBB) hardware, found in Qualcomm PM8941 PMICs.  The charger is an
  14 * integrated, single-cell lithium-ion battery charger.
  15 *
  16 * Sub-components:
  17 *  - Charger core
  18 *  - Buck
  19 *  - DC charge-path
  20 *  - USB charge-path
  21 *  - Battery interface
  22 *  - Boost (not implemented)
  23 *  - Misc
  24 *  - HF-Buck
  25 */
  26
  27#include <linux/errno.h>
  28#include <linux/interrupt.h>
  29#include <linux/kernel.h>
  30#include <linux/module.h>
  31#include <linux/mutex.h>
  32#include <linux/of.h>
  33#include <linux/platform_device.h>
  34#include <linux/power_supply.h>
  35#include <linux/regmap.h>
  36#include <linux/slab.h>
  37#include <linux/extcon.h>
  38
  39#define SMBB_CHG_VMAX           0x040
  40#define SMBB_CHG_VSAFE          0x041
  41#define SMBB_CHG_CFG            0x043
  42#define SMBB_CHG_IMAX           0x044
  43#define SMBB_CHG_ISAFE          0x045
  44#define SMBB_CHG_VIN_MIN        0x047
  45#define SMBB_CHG_CTRL           0x049
  46#define CTRL_EN                 BIT(7)
  47#define SMBB_CHG_VBAT_WEAK      0x052
  48#define SMBB_CHG_IBAT_TERM_CHG  0x05b
  49#define IBAT_TERM_CHG_IEOC      BIT(7)
  50#define IBAT_TERM_CHG_IEOC_BMS  BIT(7)
  51#define IBAT_TERM_CHG_IEOC_CHG  0
  52#define SMBB_CHG_VBAT_DET       0x05d
  53#define SMBB_CHG_TCHG_MAX_EN    0x060
  54#define TCHG_MAX_EN             BIT(7)
  55#define SMBB_CHG_WDOG_TIME      0x062
  56#define SMBB_CHG_WDOG_EN        0x065
  57#define WDOG_EN                 BIT(7)
  58
  59#define SMBB_BUCK_REG_MODE      0x174
  60#define BUCK_REG_MODE           BIT(0)
  61#define BUCK_REG_MODE_VBAT      BIT(0)
  62#define BUCK_REG_MODE_VSYS      0
  63
  64#define SMBB_BAT_PRES_STATUS    0x208
  65#define PRES_STATUS_BAT_PRES    BIT(7)
  66#define SMBB_BAT_TEMP_STATUS    0x209
  67#define TEMP_STATUS_OK          BIT(7)
  68#define TEMP_STATUS_HOT         BIT(6)
  69#define SMBB_BAT_BTC_CTRL       0x249
  70#define BTC_CTRL_COMP_EN        BIT(7)
  71#define BTC_CTRL_COLD_EXT       BIT(1)
  72#define BTC_CTRL_HOT_EXT_N      BIT(0)
  73
  74#define SMBB_USB_IMAX           0x344
  75#define SMBB_USB_ENUM_TIMER_STOP 0x34e
  76#define ENUM_TIMER_STOP         BIT(0)
  77#define SMBB_USB_SEC_ACCESS     0x3d0
  78#define SEC_ACCESS_MAGIC        0xa5
  79#define SMBB_USB_REV_BST        0x3ed
  80#define REV_BST_CHG_GONE        BIT(7)
  81
  82#define SMBB_DC_IMAX            0x444
  83
  84#define SMBB_MISC_REV2          0x601
  85#define SMBB_MISC_BOOT_DONE     0x642
  86#define BOOT_DONE               BIT(7)
  87
  88#define STATUS_USBIN_VALID      BIT(0) /* USB connection is valid */
  89#define STATUS_DCIN_VALID       BIT(1) /* DC connection is valid */
  90#define STATUS_BAT_HOT          BIT(2) /* Battery temp 1=Hot, 0=Cold */
  91#define STATUS_BAT_OK           BIT(3) /* Battery temp OK */
  92#define STATUS_BAT_PRESENT      BIT(4) /* Battery is present */
  93#define STATUS_CHG_DONE         BIT(5) /* Charge cycle is complete */
  94#define STATUS_CHG_TRKL         BIT(6) /* Trickle charging */
  95#define STATUS_CHG_FAST         BIT(7) /* Fast charging */
  96#define STATUS_CHG_GONE         BIT(8) /* No charger is connected */
  97
  98enum smbb_attr {
  99        ATTR_BAT_ISAFE,
 100        ATTR_BAT_IMAX,
 101        ATTR_USBIN_IMAX,
 102        ATTR_DCIN_IMAX,
 103        ATTR_BAT_VSAFE,
 104        ATTR_BAT_VMAX,
 105        ATTR_BAT_VMIN,
 106        ATTR_CHG_VDET,
 107        ATTR_VIN_MIN,
 108        _ATTR_CNT,
 109};
 110
 111struct smbb_charger {
 112        unsigned int revision;
 113        unsigned int addr;
 114        struct device *dev;
 115        struct extcon_dev *edev;
 116
 117        bool dc_disabled;
 118        bool jeita_ext_temp;
 119        unsigned long status;
 120        struct mutex statlock;
 121
 122        unsigned int attr[_ATTR_CNT];
 123
 124        struct power_supply *usb_psy;
 125        struct power_supply *dc_psy;
 126        struct power_supply *bat_psy;
 127        struct regmap *regmap;
 128};
 129
 130static const unsigned int smbb_usb_extcon_cable[] = {
 131        EXTCON_USB,
 132        EXTCON_NONE,
 133};
 134
 135static int smbb_vbat_weak_fn(unsigned int index)
 136{
 137        return 2100000 + index * 100000;
 138}
 139
 140static int smbb_vin_fn(unsigned int index)
 141{
 142        if (index > 42)
 143                return 5600000 + (index - 43) * 200000;
 144        return 3400000 + index * 50000;
 145}
 146
 147static int smbb_vmax_fn(unsigned int index)
 148{
 149        return 3240000 + index * 10000;
 150}
 151
 152static int smbb_vbat_det_fn(unsigned int index)
 153{
 154        return 3240000 + index * 20000;
 155}
 156
 157static int smbb_imax_fn(unsigned int index)
 158{
 159        if (index < 2)
 160                return 100000 + index * 50000;
 161        return index * 100000;
 162}
 163
 164static int smbb_bat_imax_fn(unsigned int index)
 165{
 166        return index * 50000;
 167}
 168
 169static unsigned int smbb_hw_lookup(unsigned int val, int (*fn)(unsigned int))
 170{
 171        unsigned int widx;
 172        unsigned int sel;
 173
 174        for (widx = sel = 0; (*fn)(widx) <= val; ++widx)
 175                sel = widx;
 176
 177        return sel;
 178}
 179
 180static const struct smbb_charger_attr {
 181        const char *name;
 182        unsigned int reg;
 183        unsigned int safe_reg;
 184        unsigned int max;
 185        unsigned int min;
 186        unsigned int fail_ok;
 187        int (*hw_fn)(unsigned int);
 188} smbb_charger_attrs[] = {
 189        [ATTR_BAT_ISAFE] = {
 190                .name = "qcom,fast-charge-safe-current",
 191                .reg = SMBB_CHG_ISAFE,
 192                .max = 3000000,
 193                .min = 200000,
 194                .hw_fn = smbb_bat_imax_fn,
 195                .fail_ok = 1,
 196        },
 197        [ATTR_BAT_IMAX] = {
 198                .name = "qcom,fast-charge-current-limit",
 199                .reg = SMBB_CHG_IMAX,
 200                .safe_reg = SMBB_CHG_ISAFE,
 201                .max = 3000000,
 202                .min = 200000,
 203                .hw_fn = smbb_bat_imax_fn,
 204        },
 205        [ATTR_DCIN_IMAX] = {
 206                .name = "qcom,dc-current-limit",
 207                .reg = SMBB_DC_IMAX,
 208                .max = 2500000,
 209                .min = 100000,
 210                .hw_fn = smbb_imax_fn,
 211        },
 212        [ATTR_BAT_VSAFE] = {
 213                .name = "qcom,fast-charge-safe-voltage",
 214                .reg = SMBB_CHG_VSAFE,
 215                .max = 5000000,
 216                .min = 3240000,
 217                .hw_fn = smbb_vmax_fn,
 218                .fail_ok = 1,
 219        },
 220        [ATTR_BAT_VMAX] = {
 221                .name = "qcom,fast-charge-high-threshold-voltage",
 222                .reg = SMBB_CHG_VMAX,
 223                .safe_reg = SMBB_CHG_VSAFE,
 224                .max = 5000000,
 225                .min = 3240000,
 226                .hw_fn = smbb_vmax_fn,
 227        },
 228        [ATTR_BAT_VMIN] = {
 229                .name = "qcom,fast-charge-low-threshold-voltage",
 230                .reg = SMBB_CHG_VBAT_WEAK,
 231                .max = 3600000,
 232                .min = 2100000,
 233                .hw_fn = smbb_vbat_weak_fn,
 234        },
 235        [ATTR_CHG_VDET] = {
 236                .name = "qcom,auto-recharge-threshold-voltage",
 237                .reg = SMBB_CHG_VBAT_DET,
 238                .max = 5000000,
 239                .min = 3240000,
 240                .hw_fn = smbb_vbat_det_fn,
 241        },
 242        [ATTR_VIN_MIN] = {
 243                .name = "qcom,minimum-input-voltage",
 244                .reg = SMBB_CHG_VIN_MIN,
 245                .max = 9600000,
 246                .min = 4200000,
 247                .hw_fn = smbb_vin_fn,
 248        },
 249        [ATTR_USBIN_IMAX] = {
 250                .name = "usb-charge-current-limit",
 251                .reg = SMBB_USB_IMAX,
 252                .max = 2500000,
 253                .min = 100000,
 254                .hw_fn = smbb_imax_fn,
 255        },
 256};
 257
 258static int smbb_charger_attr_write(struct smbb_charger *chg,
 259                enum smbb_attr which, unsigned int val)
 260{
 261        const struct smbb_charger_attr *prop;
 262        unsigned int wval;
 263        unsigned int out;
 264        int rc;
 265
 266        prop = &smbb_charger_attrs[which];
 267
 268        if (val > prop->max || val < prop->min) {
 269                dev_err(chg->dev, "value out of range for %s [%u:%u]\n",
 270                        prop->name, prop->min, prop->max);
 271                return -EINVAL;
 272        }
 273
 274        if (prop->safe_reg) {
 275                rc = regmap_read(chg->regmap,
 276                                chg->addr + prop->safe_reg, &wval);
 277                if (rc) {
 278                        dev_err(chg->dev,
 279                                "unable to read safe value for '%s'\n",
 280                                prop->name);
 281                        return rc;
 282                }
 283
 284                wval = prop->hw_fn(wval);
 285
 286                if (val > wval) {
 287                        dev_warn(chg->dev,
 288                                "%s above safe value, clamping at %u\n",
 289                                prop->name, wval);
 290                        val = wval;
 291                }
 292        }
 293
 294        wval = smbb_hw_lookup(val, prop->hw_fn);
 295
 296        rc = regmap_write(chg->regmap, chg->addr + prop->reg, wval);
 297        if (rc) {
 298                dev_err(chg->dev, "unable to update %s", prop->name);
 299                return rc;
 300        }
 301        out = prop->hw_fn(wval);
 302        if (out != val) {
 303                dev_warn(chg->dev,
 304                        "%s inaccurate, rounded to %u\n",
 305                        prop->name, out);
 306        }
 307
 308        dev_dbg(chg->dev, "%s <= %d\n", prop->name, out);
 309
 310        chg->attr[which] = out;
 311
 312        return 0;
 313}
 314
 315static int smbb_charger_attr_read(struct smbb_charger *chg,
 316                enum smbb_attr which)
 317{
 318        const struct smbb_charger_attr *prop;
 319        unsigned int val;
 320        int rc;
 321
 322        prop = &smbb_charger_attrs[which];
 323
 324        rc = regmap_read(chg->regmap, chg->addr + prop->reg, &val);
 325        if (rc) {
 326                dev_err(chg->dev, "failed to read %s\n", prop->name);
 327                return rc;
 328        }
 329        val = prop->hw_fn(val);
 330        dev_dbg(chg->dev, "%s => %d\n", prop->name, val);
 331
 332        chg->attr[which] = val;
 333
 334        return 0;
 335}
 336
 337static int smbb_charger_attr_parse(struct smbb_charger *chg,
 338                enum smbb_attr which)
 339{
 340        const struct smbb_charger_attr *prop;
 341        unsigned int val;
 342        int rc;
 343
 344        prop = &smbb_charger_attrs[which];
 345
 346        rc = of_property_read_u32(chg->dev->of_node, prop->name, &val);
 347        if (rc == 0) {
 348                rc = smbb_charger_attr_write(chg, which, val);
 349                if (!rc || !prop->fail_ok)
 350                        return rc;
 351        }
 352        return smbb_charger_attr_read(chg, which);
 353}
 354
 355static void smbb_set_line_flag(struct smbb_charger *chg, int irq, int flag)
 356{
 357        bool state;
 358        int ret;
 359
 360        ret = irq_get_irqchip_state(irq, IRQCHIP_STATE_LINE_LEVEL, &state);
 361        if (ret < 0) {
 362                dev_err(chg->dev, "failed to read irq line\n");
 363                return;
 364        }
 365
 366        mutex_lock(&chg->statlock);
 367        if (state)
 368                chg->status |= flag;
 369        else
 370                chg->status &= ~flag;
 371        mutex_unlock(&chg->statlock);
 372
 373        dev_dbg(chg->dev, "status = %03lx\n", chg->status);
 374}
 375
 376static irqreturn_t smbb_usb_valid_handler(int irq, void *_data)
 377{
 378        struct smbb_charger *chg = _data;
 379
 380        smbb_set_line_flag(chg, irq, STATUS_USBIN_VALID);
 381        extcon_set_cable_state_(chg->edev, EXTCON_USB,
 382                                chg->status & STATUS_USBIN_VALID);
 383        power_supply_changed(chg->usb_psy);
 384
 385        return IRQ_HANDLED;
 386}
 387
 388static irqreturn_t smbb_dc_valid_handler(int irq, void *_data)
 389{
 390        struct smbb_charger *chg = _data;
 391
 392        smbb_set_line_flag(chg, irq, STATUS_DCIN_VALID);
 393        if (!chg->dc_disabled)
 394                power_supply_changed(chg->dc_psy);
 395
 396        return IRQ_HANDLED;
 397}
 398
 399static irqreturn_t smbb_bat_temp_handler(int irq, void *_data)
 400{
 401        struct smbb_charger *chg = _data;
 402        unsigned int val;
 403        int rc;
 404
 405        rc = regmap_read(chg->regmap, chg->addr + SMBB_BAT_TEMP_STATUS, &val);
 406        if (rc)
 407                return IRQ_HANDLED;
 408
 409        mutex_lock(&chg->statlock);
 410        if (val & TEMP_STATUS_OK) {
 411                chg->status |= STATUS_BAT_OK;
 412        } else {
 413                chg->status &= ~STATUS_BAT_OK;
 414                if (val & TEMP_STATUS_HOT)
 415                        chg->status |= STATUS_BAT_HOT;
 416        }
 417        mutex_unlock(&chg->statlock);
 418
 419        power_supply_changed(chg->bat_psy);
 420        return IRQ_HANDLED;
 421}
 422
 423static irqreturn_t smbb_bat_present_handler(int irq, void *_data)
 424{
 425        struct smbb_charger *chg = _data;
 426
 427        smbb_set_line_flag(chg, irq, STATUS_BAT_PRESENT);
 428        power_supply_changed(chg->bat_psy);
 429
 430        return IRQ_HANDLED;
 431}
 432
 433static irqreturn_t smbb_chg_done_handler(int irq, void *_data)
 434{
 435        struct smbb_charger *chg = _data;
 436
 437        smbb_set_line_flag(chg, irq, STATUS_CHG_DONE);
 438        power_supply_changed(chg->bat_psy);
 439
 440        return IRQ_HANDLED;
 441}
 442
 443static irqreturn_t smbb_chg_gone_handler(int irq, void *_data)
 444{
 445        struct smbb_charger *chg = _data;
 446
 447        smbb_set_line_flag(chg, irq, STATUS_CHG_GONE);
 448        power_supply_changed(chg->bat_psy);
 449        power_supply_changed(chg->usb_psy);
 450        if (!chg->dc_disabled)
 451                power_supply_changed(chg->dc_psy);
 452
 453        return IRQ_HANDLED;
 454}
 455
 456static irqreturn_t smbb_chg_fast_handler(int irq, void *_data)
 457{
 458        struct smbb_charger *chg = _data;
 459
 460        smbb_set_line_flag(chg, irq, STATUS_CHG_FAST);
 461        power_supply_changed(chg->bat_psy);
 462
 463        return IRQ_HANDLED;
 464}
 465
 466static irqreturn_t smbb_chg_trkl_handler(int irq, void *_data)
 467{
 468        struct smbb_charger *chg = _data;
 469
 470        smbb_set_line_flag(chg, irq, STATUS_CHG_TRKL);
 471        power_supply_changed(chg->bat_psy);
 472
 473        return IRQ_HANDLED;
 474}
 475
 476static const struct smbb_irq {
 477        const char *name;
 478        irqreturn_t (*handler)(int, void *);
 479} smbb_charger_irqs[] = {
 480        { "chg-done", smbb_chg_done_handler },
 481        { "chg-fast", smbb_chg_fast_handler },
 482        { "chg-trkl", smbb_chg_trkl_handler },
 483        { "bat-temp-ok", smbb_bat_temp_handler },
 484        { "bat-present", smbb_bat_present_handler },
 485        { "chg-gone", smbb_chg_gone_handler },
 486        { "usb-valid", smbb_usb_valid_handler },
 487        { "dc-valid", smbb_dc_valid_handler },
 488};
 489
 490static int smbb_usbin_get_property(struct power_supply *psy,
 491                enum power_supply_property psp,
 492                union power_supply_propval *val)
 493{
 494        struct smbb_charger *chg = power_supply_get_drvdata(psy);
 495        int rc = 0;
 496
 497        switch (psp) {
 498        case POWER_SUPPLY_PROP_ONLINE:
 499                mutex_lock(&chg->statlock);
 500                val->intval = !(chg->status & STATUS_CHG_GONE) &&
 501                                (chg->status & STATUS_USBIN_VALID);
 502                mutex_unlock(&chg->statlock);
 503                break;
 504        case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
 505                val->intval = chg->attr[ATTR_USBIN_IMAX];
 506                break;
 507        case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX:
 508                val->intval = 2500000;
 509                break;
 510        default:
 511                rc = -EINVAL;
 512                break;
 513        }
 514
 515        return rc;
 516}
 517
 518static int smbb_usbin_set_property(struct power_supply *psy,
 519                enum power_supply_property psp,
 520                const union power_supply_propval *val)
 521{
 522        struct smbb_charger *chg = power_supply_get_drvdata(psy);
 523        int rc;
 524
 525        switch (psp) {
 526        case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
 527                rc = smbb_charger_attr_write(chg, ATTR_USBIN_IMAX,
 528                                val->intval);
 529                break;
 530        default:
 531                rc = -EINVAL;
 532                break;
 533        }
 534
 535        return rc;
 536}
 537
 538static int smbb_dcin_get_property(struct power_supply *psy,
 539                enum power_supply_property psp,
 540                union power_supply_propval *val)
 541{
 542        struct smbb_charger *chg = power_supply_get_drvdata(psy);
 543        int rc = 0;
 544
 545        switch (psp) {
 546        case POWER_SUPPLY_PROP_ONLINE:
 547                mutex_lock(&chg->statlock);
 548                val->intval = !(chg->status & STATUS_CHG_GONE) &&
 549                                (chg->status & STATUS_DCIN_VALID);
 550                mutex_unlock(&chg->statlock);
 551                break;
 552        case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
 553                val->intval = chg->attr[ATTR_DCIN_IMAX];
 554                break;
 555        case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX:
 556                val->intval = 2500000;
 557                break;
 558        default:
 559                rc = -EINVAL;
 560                break;
 561        }
 562
 563        return rc;
 564}
 565
 566static int smbb_dcin_set_property(struct power_supply *psy,
 567                enum power_supply_property psp,
 568                const union power_supply_propval *val)
 569{
 570        struct smbb_charger *chg = power_supply_get_drvdata(psy);
 571        int rc;
 572
 573        switch (psp) {
 574        case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
 575                rc = smbb_charger_attr_write(chg, ATTR_DCIN_IMAX,
 576                                val->intval);
 577                break;
 578        default:
 579                rc = -EINVAL;
 580                break;
 581        }
 582
 583        return rc;
 584}
 585
 586static int smbb_charger_writable_property(struct power_supply *psy,
 587                enum power_supply_property psp)
 588{
 589        return psp == POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT;
 590}
 591
 592static int smbb_battery_get_property(struct power_supply *psy,
 593                enum power_supply_property psp,
 594                union power_supply_propval *val)
 595{
 596        struct smbb_charger *chg = power_supply_get_drvdata(psy);
 597        unsigned long status;
 598        int rc = 0;
 599
 600        mutex_lock(&chg->statlock);
 601        status = chg->status;
 602        mutex_unlock(&chg->statlock);
 603
 604        switch (psp) {
 605        case POWER_SUPPLY_PROP_STATUS:
 606                if (status & STATUS_CHG_GONE)
 607                        val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
 608                else if (!(status & (STATUS_DCIN_VALID | STATUS_USBIN_VALID)))
 609                        val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
 610                else if (status & STATUS_CHG_DONE)
 611                        val->intval = POWER_SUPPLY_STATUS_FULL;
 612                else if (!(status & STATUS_BAT_OK))
 613                        val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
 614                else if (status & (STATUS_CHG_FAST | STATUS_CHG_TRKL))
 615                        val->intval = POWER_SUPPLY_STATUS_CHARGING;
 616                else /* everything is ok for charging, but we are not... */
 617                        val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
 618                break;
 619        case POWER_SUPPLY_PROP_HEALTH:
 620                if (status & STATUS_BAT_OK)
 621                        val->intval = POWER_SUPPLY_HEALTH_GOOD;
 622                else if (status & STATUS_BAT_HOT)
 623                        val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
 624                else
 625                        val->intval = POWER_SUPPLY_HEALTH_COLD;
 626                break;
 627        case POWER_SUPPLY_PROP_CHARGE_TYPE:
 628                if (status & STATUS_CHG_FAST)
 629                        val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
 630                else if (status & STATUS_CHG_TRKL)
 631                        val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
 632                else
 633                        val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
 634                break;
 635        case POWER_SUPPLY_PROP_PRESENT:
 636                val->intval = !!(status & STATUS_BAT_PRESENT);
 637                break;
 638        case POWER_SUPPLY_PROP_CURRENT_MAX:
 639                val->intval = chg->attr[ATTR_BAT_IMAX];
 640                break;
 641        case POWER_SUPPLY_PROP_VOLTAGE_MAX:
 642                val->intval = chg->attr[ATTR_BAT_VMAX];
 643                break;
 644        case POWER_SUPPLY_PROP_TECHNOLOGY:
 645                /* this charger is a single-cell lithium-ion battery charger
 646                * only.  If you hook up some other technology, there will be
 647                * fireworks.
 648                */
 649                val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
 650                break;
 651        case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
 652                val->intval = 3000000; /* single-cell li-ion low end */
 653                break;
 654        default:
 655                rc = -EINVAL;
 656                break;
 657        }
 658
 659        return rc;
 660}
 661
 662static int smbb_battery_set_property(struct power_supply *psy,
 663                enum power_supply_property psp,
 664                const union power_supply_propval *val)
 665{
 666        struct smbb_charger *chg = power_supply_get_drvdata(psy);
 667        int rc;
 668
 669        switch (psp) {
 670        case POWER_SUPPLY_PROP_CURRENT_MAX:
 671                rc = smbb_charger_attr_write(chg, ATTR_BAT_IMAX, val->intval);
 672                break;
 673        case POWER_SUPPLY_PROP_VOLTAGE_MAX:
 674                rc = smbb_charger_attr_write(chg, ATTR_BAT_VMAX, val->intval);
 675                break;
 676        default:
 677                rc = -EINVAL;
 678                break;
 679        }
 680
 681        return rc;
 682}
 683
 684static int smbb_battery_writable_property(struct power_supply *psy,
 685                enum power_supply_property psp)
 686{
 687        switch (psp) {
 688        case POWER_SUPPLY_PROP_CURRENT_MAX:
 689        case POWER_SUPPLY_PROP_VOLTAGE_MAX:
 690                return 1;
 691        default:
 692                return 0;
 693        }
 694}
 695
 696static enum power_supply_property smbb_charger_properties[] = {
 697        POWER_SUPPLY_PROP_ONLINE,
 698        POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
 699        POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,
 700};
 701
 702static enum power_supply_property smbb_battery_properties[] = {
 703        POWER_SUPPLY_PROP_STATUS,
 704        POWER_SUPPLY_PROP_HEALTH,
 705        POWER_SUPPLY_PROP_PRESENT,
 706        POWER_SUPPLY_PROP_CHARGE_TYPE,
 707        POWER_SUPPLY_PROP_CURRENT_MAX,
 708        POWER_SUPPLY_PROP_VOLTAGE_MAX,
 709        POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
 710        POWER_SUPPLY_PROP_TECHNOLOGY,
 711};
 712
 713static const struct reg_off_mask_default {
 714        unsigned int offset;
 715        unsigned int mask;
 716        unsigned int value;
 717        unsigned int rev_mask;
 718} smbb_charger_setup[] = {
 719        /* The bootloader is supposed to set this... make sure anyway. */
 720        { SMBB_MISC_BOOT_DONE, BOOT_DONE, BOOT_DONE },
 721
 722        /* Disable software timer */
 723        { SMBB_CHG_TCHG_MAX_EN, TCHG_MAX_EN, 0 },
 724
 725        /* Clear and disable watchdog */
 726        { SMBB_CHG_WDOG_TIME, 0xff, 160 },
 727        { SMBB_CHG_WDOG_EN, WDOG_EN, 0 },
 728
 729        /* Use charger based EoC detection */
 730        { SMBB_CHG_IBAT_TERM_CHG, IBAT_TERM_CHG_IEOC, IBAT_TERM_CHG_IEOC_CHG },
 731
 732        /* Disable GSM PA load adjustment.
 733        * The PA signal is incorrectly connected on v2.
 734        */
 735        { SMBB_CHG_CFG, 0xff, 0x00, BIT(3) },
 736
 737        /* Use VBAT (not VSYS) to compensate for IR drop during fast charging */
 738        { SMBB_BUCK_REG_MODE, BUCK_REG_MODE, BUCK_REG_MODE_VBAT },
 739
 740        /* Enable battery temperature comparators */
 741        { SMBB_BAT_BTC_CTRL, BTC_CTRL_COMP_EN, BTC_CTRL_COMP_EN },
 742
 743        /* Stop USB enumeration timer */
 744        { SMBB_USB_ENUM_TIMER_STOP, ENUM_TIMER_STOP, ENUM_TIMER_STOP },
 745
 746#if 0 /* FIXME supposedly only to disable hardware ARB termination */
 747        { SMBB_USB_SEC_ACCESS, SEC_ACCESS_MAGIC },
 748        { SMBB_USB_REV_BST, 0xff, REV_BST_CHG_GONE },
 749#endif
 750
 751        /* Stop USB enumeration timer, again */
 752        { SMBB_USB_ENUM_TIMER_STOP, ENUM_TIMER_STOP, ENUM_TIMER_STOP },
 753
 754        /* Enable charging */
 755        { SMBB_CHG_CTRL, CTRL_EN, CTRL_EN },
 756};
 757
 758static char *smbb_bif[] = { "smbb-bif" };
 759
 760static const struct power_supply_desc bat_psy_desc = {
 761        .name = "smbb-bif",
 762        .type = POWER_SUPPLY_TYPE_BATTERY,
 763        .properties = smbb_battery_properties,
 764        .num_properties = ARRAY_SIZE(smbb_battery_properties),
 765        .get_property = smbb_battery_get_property,
 766        .set_property = smbb_battery_set_property,
 767        .property_is_writeable = smbb_battery_writable_property,
 768};
 769
 770static const struct power_supply_desc usb_psy_desc = {
 771        .name = "smbb-usbin",
 772        .type = POWER_SUPPLY_TYPE_USB,
 773        .properties = smbb_charger_properties,
 774        .num_properties = ARRAY_SIZE(smbb_charger_properties),
 775        .get_property = smbb_usbin_get_property,
 776        .set_property = smbb_usbin_set_property,
 777        .property_is_writeable = smbb_charger_writable_property,
 778};
 779
 780static const struct power_supply_desc dc_psy_desc = {
 781        .name = "smbb-dcin",
 782        .type = POWER_SUPPLY_TYPE_MAINS,
 783        .properties = smbb_charger_properties,
 784        .num_properties = ARRAY_SIZE(smbb_charger_properties),
 785        .get_property = smbb_dcin_get_property,
 786        .set_property = smbb_dcin_set_property,
 787        .property_is_writeable = smbb_charger_writable_property,
 788};
 789
 790static int smbb_charger_probe(struct platform_device *pdev)
 791{
 792        struct power_supply_config bat_cfg = {};
 793        struct power_supply_config usb_cfg = {};
 794        struct power_supply_config dc_cfg = {};
 795        struct smbb_charger *chg;
 796        int rc, i;
 797
 798        chg = devm_kzalloc(&pdev->dev, sizeof(*chg), GFP_KERNEL);
 799        if (!chg)
 800                return -ENOMEM;
 801
 802        chg->dev = &pdev->dev;
 803        mutex_init(&chg->statlock);
 804
 805        chg->regmap = dev_get_regmap(pdev->dev.parent, NULL);
 806        if (!chg->regmap) {
 807                dev_err(&pdev->dev, "failed to locate regmap\n");
 808                return -ENODEV;
 809        }
 810
 811        rc = of_property_read_u32(pdev->dev.of_node, "reg", &chg->addr);
 812        if (rc) {
 813                dev_err(&pdev->dev, "missing or invalid 'reg' property\n");
 814                return rc;
 815        }
 816
 817        rc = regmap_read(chg->regmap, chg->addr + SMBB_MISC_REV2, &chg->revision);
 818        if (rc) {
 819                dev_err(&pdev->dev, "unable to read revision\n");
 820                return rc;
 821        }
 822
 823        chg->revision += 1;
 824        if (chg->revision != 2 && chg->revision != 3) {
 825                dev_err(&pdev->dev, "v1 hardware not supported\n");
 826                return -ENODEV;
 827        }
 828        dev_info(&pdev->dev, "Initializing SMBB rev %u", chg->revision);
 829
 830        chg->dc_disabled = of_property_read_bool(pdev->dev.of_node, "qcom,disable-dc");
 831
 832        for (i = 0; i < _ATTR_CNT; ++i) {
 833                rc = smbb_charger_attr_parse(chg, i);
 834                if (rc) {
 835                        dev_err(&pdev->dev, "failed to parse/apply settings\n");
 836                        return rc;
 837                }
 838        }
 839
 840        bat_cfg.drv_data = chg;
 841        bat_cfg.of_node = pdev->dev.of_node;
 842        chg->bat_psy = devm_power_supply_register(&pdev->dev,
 843                                                  &bat_psy_desc,
 844                                                  &bat_cfg);
 845        if (IS_ERR(chg->bat_psy)) {
 846                dev_err(&pdev->dev, "failed to register battery\n");
 847                return PTR_ERR(chg->bat_psy);
 848        }
 849
 850        usb_cfg.drv_data = chg;
 851        usb_cfg.supplied_to = smbb_bif;
 852        usb_cfg.num_supplicants = ARRAY_SIZE(smbb_bif);
 853        chg->usb_psy = devm_power_supply_register(&pdev->dev,
 854                                                  &usb_psy_desc,
 855                                                  &usb_cfg);
 856        if (IS_ERR(chg->usb_psy)) {
 857                dev_err(&pdev->dev, "failed to register USB power supply\n");
 858                return PTR_ERR(chg->usb_psy);
 859        }
 860
 861        chg->edev = devm_extcon_dev_allocate(&pdev->dev, smbb_usb_extcon_cable);
 862        if (IS_ERR(chg->edev)) {
 863                dev_err(&pdev->dev, "failed to allocate extcon device\n");
 864                return -ENOMEM;
 865        }
 866
 867        rc = devm_extcon_dev_register(&pdev->dev, chg->edev);
 868        if (rc < 0) {
 869                dev_err(&pdev->dev, "failed to register extcon device\n");
 870                return rc;
 871        }
 872
 873        if (!chg->dc_disabled) {
 874                dc_cfg.drv_data = chg;
 875                dc_cfg.supplied_to = smbb_bif;
 876                dc_cfg.num_supplicants = ARRAY_SIZE(smbb_bif);
 877                chg->dc_psy = devm_power_supply_register(&pdev->dev,
 878                                                         &dc_psy_desc,
 879                                                         &dc_cfg);
 880                if (IS_ERR(chg->dc_psy)) {
 881                        dev_err(&pdev->dev, "failed to register DC power supply\n");
 882                        return PTR_ERR(chg->dc_psy);
 883                }
 884        }
 885
 886        for (i = 0; i < ARRAY_SIZE(smbb_charger_irqs); ++i) {
 887                int irq;
 888
 889                irq = platform_get_irq_byname(pdev, smbb_charger_irqs[i].name);
 890                if (irq < 0) {
 891                        dev_err(&pdev->dev, "failed to get irq '%s'\n",
 892                                smbb_charger_irqs[i].name);
 893                        return irq;
 894                }
 895
 896                smbb_charger_irqs[i].handler(irq, chg);
 897
 898                rc = devm_request_threaded_irq(&pdev->dev, irq, NULL,
 899                                smbb_charger_irqs[i].handler, IRQF_ONESHOT,
 900                                smbb_charger_irqs[i].name, chg);
 901                if (rc) {
 902                        dev_err(&pdev->dev, "failed to request irq '%s'\n",
 903                                smbb_charger_irqs[i].name);
 904                        return rc;
 905                }
 906        }
 907
 908        chg->jeita_ext_temp = of_property_read_bool(pdev->dev.of_node,
 909                        "qcom,jeita-extended-temp-range");
 910
 911        /* Set temperature range to [35%:70%] or [25%:80%] accordingly */
 912        rc = regmap_update_bits(chg->regmap, chg->addr + SMBB_BAT_BTC_CTRL,
 913                        BTC_CTRL_COLD_EXT | BTC_CTRL_HOT_EXT_N,
 914                        chg->jeita_ext_temp ?
 915                                BTC_CTRL_COLD_EXT :
 916                                BTC_CTRL_HOT_EXT_N);
 917        if (rc) {
 918                dev_err(&pdev->dev,
 919                        "unable to set %s temperature range\n",
 920                        chg->jeita_ext_temp ? "JEITA extended" : "normal");
 921                return rc;
 922        }
 923
 924        for (i = 0; i < ARRAY_SIZE(smbb_charger_setup); ++i) {
 925                const struct reg_off_mask_default *r = &smbb_charger_setup[i];
 926
 927                if (r->rev_mask & BIT(chg->revision))
 928                        continue;
 929
 930                rc = regmap_update_bits(chg->regmap, chg->addr + r->offset,
 931                                r->mask, r->value);
 932                if (rc) {
 933                        dev_err(&pdev->dev,
 934                                "unable to initializing charging, bailing\n");
 935                        return rc;
 936                }
 937        }
 938
 939        platform_set_drvdata(pdev, chg);
 940
 941        return 0;
 942}
 943
 944static int smbb_charger_remove(struct platform_device *pdev)
 945{
 946        struct smbb_charger *chg;
 947
 948        chg = platform_get_drvdata(pdev);
 949
 950        regmap_update_bits(chg->regmap, chg->addr + SMBB_CHG_CTRL, CTRL_EN, 0);
 951
 952        return 0;
 953}
 954
 955static const struct of_device_id smbb_charger_id_table[] = {
 956        { .compatible = "qcom,pm8941-charger" },
 957        { }
 958};
 959MODULE_DEVICE_TABLE(of, smbb_charger_id_table);
 960
 961static struct platform_driver smbb_charger_driver = {
 962        .probe    = smbb_charger_probe,
 963        .remove  = smbb_charger_remove,
 964        .driver  = {
 965                .name   = "qcom-smbb",
 966                .of_match_table = smbb_charger_id_table,
 967        },
 968};
 969module_platform_driver(smbb_charger_driver);
 970
 971MODULE_DESCRIPTION("Qualcomm Switch-Mode Battery Charger and Boost driver");
 972MODULE_LICENSE("GPL v2");
 973