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